From 0012338e022501d9fb6978efeff926a55ffbb94e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?fran=C3=A7ois=20dambrine?=
Date: Fri, 18 Jul 2014 20:50:05 +0200
Subject: [PATCH 01/20] =?UTF-8?q?fix=20843=20:=20les=20conflits=20d'=C3=A9?=
=?UTF-8?q?ditions=20simultan=C3=A9s=20donnent=20lieu=20=C3=A0=20un=20?=
=?UTF-8?q?=C3=A9crasement=20de=20la=20version=20interm=C3=A9diaire?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
templates/tutorial/chapter/edit.html | 3 +
templates/tutorial/extract/edit.html | 3 +
templates/tutorial/part/edit.html | 3 +
templates/tutorial/tutorial/edit.html | 5 ++
zds/tutorial/forms.py | 12 ++-
zds/tutorial/views.py | 116 ++++++++++++++++----------
zds/utils/misc.py | 18 +++-
zds/utils/tutorials.py | 3 +-
8 files changed, 116 insertions(+), 47 deletions(-)
diff --git a/templates/tutorial/chapter/edit.html b/templates/tutorial/chapter/edit.html
index c18be0a24a..696f8f3b76 100644
--- a/templates/tutorial/chapter/edit.html
+++ b/templates/tutorial/chapter/edit.html
@@ -40,5 +40,8 @@
{% block content %}
+ {% if new_version %}
+
Une nouvelle version a été postée
+ {% endif%}
{% crispy form %}
{% endblock %}
\ No newline at end of file
diff --git a/templates/tutorial/extract/edit.html b/templates/tutorial/extract/edit.html
index 3969f9c6ef..f5b585d53a 100644
--- a/templates/tutorial/extract/edit.html
+++ b/templates/tutorial/extract/edit.html
@@ -29,6 +29,9 @@ Éditer l'extrait
{% block content %}
+ {% if new_version %}
+ Une nouvelle version a été postée
+ {% endif%}
{% crispy form %}
{% if form.text.value %}
diff --git a/templates/tutorial/part/edit.html b/templates/tutorial/part/edit.html
index 421a8f826d..82dbb0257c 100644
--- a/templates/tutorial/part/edit.html
+++ b/templates/tutorial/part/edit.html
@@ -35,5 +35,8 @@
{% block content %}
+ {% if new_version %}
+
Une nouvelle version a été postée
+ {% endif%}
{% crispy form %}
{% endblock %}
\ No newline at end of file
diff --git a/templates/tutorial/tutorial/edit.html b/templates/tutorial/tutorial/edit.html
index c14defe5f8..59bf338268 100644
--- a/templates/tutorial/tutorial/edit.html
+++ b/templates/tutorial/tutorial/edit.html
@@ -29,10 +29,15 @@
{% block breadcrumb %}
{{ tutorial.title }}
Éditer le tutoriel
+
+
{% endblock %}
{% block content %}
+ {% if new_version %}
+ Une nouvelle version a été postée
+ {% endif%}
{% crispy form %}
{% endblock %}
\ No newline at end of file
diff --git a/zds/tutorial/forms.py b/zds/tutorial/forms.py
index b5a7ea2ff2..add0efbae6 100644
--- a/zds/tutorial/forms.py
+++ b/zds/tutorial/forms.py
@@ -108,6 +108,7 @@ def __init__(self, *args, **kwargs):
Field('image'),
Field('introduction', css_class='md-editor'),
Field('conclusion', css_class='md-editor'),
+ Hidden('last_hash', '{{ last_hash }}'),
Field('subcategory'),
Field('licence'),
ButtonHolder(
@@ -153,6 +154,7 @@ def __init__(self, *args, **kwargs):
Field('title'),
Field('introduction', css_class='md-editor'),
Field('conclusion', css_class='md-editor'),
+ Hidden('last_hash', '{{ last_hash }}'),
ButtonHolder(
StrictButton(
'Valider',
@@ -200,6 +202,7 @@ def __init__(self, *args, **kwargs):
Field('image'),
Field('introduction', css_class='md-editor'),
Field('conclusion', css_class='md-editor'),
+ Hidden('last_hash', '{{ last_hash }}'),
ButtonHolder(
StrictButton(
'Valider',
@@ -236,7 +239,8 @@ def __init__(self, *args, **kwargs):
u'Contenu',
Field('image'),
Field('introduction', css_class='md-editor'),
- Field('conclusion', css_class='md-editor')
+ Field('conclusion', css_class='md-editor'),
+ Hidden('last_hash', '{{ last_hash }}'),
),
ButtonHolder(
Submit('submit', 'Valider')
@@ -265,6 +269,7 @@ def __init__(self, *args, **kwargs):
self.helper.layout = Layout(
Field('title'),
+ Hidden('last_hash', '{{ last_hash }}'),
CommonLayoutEditor()
)
@@ -387,8 +392,9 @@ def __init__(self, *args, **kwargs):
StrictButton(
'Confirmer',
type='submit'),
- Hidden('tutorial', '{{ tutorial.pk }}'),
- Hidden('version', '{{ version }}'), )
+ Hidden(
+ 'tutorial', '{{ tutorial.pk }}'), Hidden(
+ 'version', '{{ version }}'), )
class ValidForm(forms.Form):
diff --git a/zds/tutorial/views.py b/zds/tutorial/views.py
index ed7ca774b4..f3d02e60ac 100644
--- a/zds/tutorial/views.py
+++ b/zds/tutorial/views.py
@@ -54,8 +54,18 @@
from zds.utils.paginator import paginator_range
from zds.utils.templatetags.emarkdown import emarkdown
from zds.utils.tutorials import get_blob, export_tutorial_to_md, move
+from zds.utils.misc import compute_hash, content_has_changed
+def render_chapter_form(chapter):
+ if chapter.part:
+ return ChapterForm({"title": chapter.title,
+ "introduction": chapter.get_introduction(),
+ "conclusion": chapter.get_conclusion()})
+ else:
+ return \
+ EmbdedChapterForm({"introduction": chapter.get_introduction(),
+ "conclusion": chapter.get_conclusion()})
def index(request):
"""Display all public tutorials of the website."""
@@ -704,11 +714,10 @@ def view_tutorial(request, tutorial_pk, tutorial_slug):
version=sha)\
.order_by("-date_proposition")\
.first()
+ formAskValidation = AskValidationForm()
if tutorial.source:
- formAskValidation = AskValidationForm(initial={"source": tutorial.source})
formValid = ValidForm(initial={"source": tutorial.source})
else:
- formAskValidation = AskValidationForm()
formValid = ValidForm()
formReject = RejectForm()
return render_template("tutorial/tutorial/view.html", {
@@ -1295,7 +1304,8 @@ def edit_part(request):
except KeyError:
raise Http404
part = get_object_or_404(Part, pk=part_pk)
-
+ introduction = os.path.join(part.get_path(), "introduction.md")
+ conclusion = os.path.join(part.get_path(), "conclusion.md")
# Make sure the user is allowed to do that
if request.user not in part.tutorial.authors.all() and not request.user.has_perm("tutorial.change_tutorial"):
@@ -1304,7 +1314,18 @@ def edit_part(request):
form = PartForm(request.POST)
if form.is_valid():
data = form.data
-
+ # avoid collision
+ if content_has_changed([introduction, conclusion],data["last_hash"]):
+ form = PartForm({"title": part.title,
+ "introduction": part.get_introduction(),
+ "conclusion": part.get_conclusion()})
+ return render_template("tutorial/part/edit.html",
+ {
+ "part": part,
+ "last_hash": compute_hash([introduction, conclusion]),
+ "new_version":True,
+ "form": form
+ })
# Update title and his slug.
part.title = data["title"]
@@ -1333,8 +1354,12 @@ def edit_part(request):
form = PartForm({"title": part.title,
"introduction": part.get_introduction(),
"conclusion": part.get_conclusion()})
- return render_template("tutorial/part/edit.html", {"part": part,
- "form": form})
+ return render_template("tutorial/part/edit.html",
+ {
+ "part": part,
+ "last_hash": compute_hash([introduction, conclusion]),
+ "form": form
+ })
# Chapters.
@@ -1388,7 +1413,7 @@ def view_chapter(
args=[
tutorial.pk,
tutorial.slug,
- part["pk"],
+ part_pk,
part["slug"]])
part["tutorial"] = tutorial
for chapter in part["chapters"]:
@@ -1398,15 +1423,8 @@ def view_chapter(
chapter["type"] = "BIG"
chapter["position_in_part"] = cpt_c
chapter["position_in_tutorial"] = cpt_c * cpt_p
- chapter["get_absolute_url"] = reverse(
- "zds.tutorial.views.view_chapter",
- args=[
- tutorial.pk,
- tutorial.slug,
- part["pk"],
- part["slug"],
- chapter["pk"],
- chapter["slug"]])
+ chapter["get_absolute_url"] = part["get_absolute_url"] \
+ + "{0}/{1}/".format(chapter["pk"], chapter["slug"])
if chapter_pk == str(chapter["pk"]):
chapter["intro"] = get_blob(repo.commit(sha).tree,
chapter["introduction"])
@@ -1427,8 +1445,10 @@ def view_chapter(
cpt_c += 1
cpt_p += 1
- prev_chapter = (chapter_tab[final_position - 1] if final_position > 0 else None)
- next_chapter = (chapter_tab[final_position + 1] if final_position + 1 < len(chapter_tab) else None)
+ prev_chapter = (chapter_tab[final_position - 1] if final_position
+ > 0 else None)
+ next_chapter = (chapter_tab[final_position + 1] if final_position + 1
+ < len(chapter_tab) else None)
return render_template("tutorial/chapter/view.html", {
"tutorial": tutorial,
@@ -1474,7 +1494,7 @@ def view_chapter_online(
args=[
tutorial.pk,
tutorial.slug,
- part["pk"],
+ part_pk,
part["slug"]])
part["tutorial"] = mandata
part["position_in_tutorial"] = cpt_p
@@ -1486,15 +1506,8 @@ def view_chapter_online(
chapter["type"] = "BIG"
chapter["position_in_part"] = cpt_c
chapter["position_in_tutorial"] = cpt_c * cpt_p
- chapter["get_absolute_url_online"] = reverse(
- "zds.tutorial.views.view_chapter_online",
- args=[
- tutorial.pk,
- tutorial.slug,
- part["pk"],
- part["slug"],
- chapter["pk"],
- chapter["slug"]])
+ chapter["get_absolute_url_online"] = part[
+ "get_absolute_url_online"] + "{0}/{1}/".format(chapter["pk"], chapter["slug"])
if chapter_pk == str(chapter["pk"]):
intro = open(
os.path.join(
@@ -1718,7 +1731,10 @@ def edit_chapter(request):
or small and request.user not in chapter.tutorial.authors.all())\
and not request.user.has_perm("tutorial.change_tutorial"):
raise PermissionDenied
+ introduction = os.path.join(chapter.get_path(), "introduction.md")
+ conclusion = os.path.join(chapter.get_path(), "conclusion.md")
if request.method == "POST":
+
if chapter.part:
form = ChapterForm(request.POST, request.FILES)
gal = chapter.part.tutorial.gallery
@@ -1727,6 +1743,16 @@ def edit_chapter(request):
gal = chapter.tutorial.gallery
if form.is_valid():
data = form.data
+ # avoid collision
+ if content_has_changed([introduction, conclusion], data["last_hash"]):
+ form = render_chapter_form(chapter)
+ return render_template("tutorial/part/edit.html",
+ {
+ "chapter": chapter,
+ "last_hash": compute_hash([introduction, conclusion]),
+ "new_version":True,
+ "form": form
+ })
chapter.title = data["title"]
old_slug = chapter.get_path()
@@ -1764,16 +1790,9 @@ def edit_chapter(request):
)
return redirect(chapter.get_absolute_url())
else:
- if chapter.part:
- form = ChapterForm({"title": chapter.title,
- "introduction": chapter.get_introduction(),
- "conclusion": chapter.get_conclusion()})
- else:
-
- form = \
- EmbdedChapterForm({"introduction": chapter.get_introduction(),
- "conclusion": chapter.get_conclusion()})
+ form = render_chapter_form(chapter)
return render_template("tutorial/chapter/edit.html", {"chapter": chapter,
+ "last_hash": compute_hash([introduction, conclusion]),
"form": form})
@@ -1860,7 +1879,17 @@ def edit_extract(request):
raise PermissionDenied
if request.method == "POST":
data = request.POST
-
+ if content_has_changed([extract.get_path()], data["last_hash"]):
+ form = form = ExtractForm(initial={
+ "title": extract.title,
+ "text": extract.get_text()})
+ return render_template("tutorial/extract/edit.html",
+ {
+ "extract": extract,
+ "last_hash": compute_hash([extract.get_path()]),
+ "new_version":True,
+ "form": form
+ })
# Using the « preview button »
if "preview" in data:
@@ -1909,8 +1938,12 @@ def edit_extract(request):
else:
form = ExtractForm({"title": extract.title,
"text": extract.get_text()})
- return render_template("tutorial/extract/edit.html", {"extract": extract,
- "form": form})
+ return render_template("tutorial/extract/edit.html",
+ {
+ "extract": extract,
+ "last_hash": compute_hash([extract.get_path()]),
+ "form": form
+ })
@can_write_and_read_now
@@ -2958,10 +2991,9 @@ def answer(request):
raise PermissionDenied
for line in note_cite.text.splitlines():
text = text + "> " + line + "\n"
- text = u"{0}Source:[{1}]({2}{3})".format(
+ text = u"{0}Source:[{1}]({2})".format(
text,
note_cite.author.username,
- settings.SITE_URL,
note_cite.get_absolute_url())
form = NoteForm(tutorial, request.user, initial={"text": text})
return render_template("tutorial/comment/new.html", {
diff --git a/zds/utils/misc.py b/zds/utils/misc.py
index 21a01718e0..f3ddea168e 100644
--- a/zds/utils/misc.py
+++ b/zds/utils/misc.py
@@ -3,7 +3,7 @@
import os
import string
import uuid
-
+import hashlib
THUMB_MAX_WIDTH = 80
THUMB_MAX_HEIGHT = 80
@@ -11,6 +11,22 @@
MEDIUM_MAX_WIDTH = 200
MEDIUM_MAX_HEIGHT = 200
+def compute_hash(filenames):
+ """returns a md5 hexdigest of group of files to check if they have change"""
+ md5_hash = hashlib.md5()
+ for filename in filenames:
+ file_handle = open(filename, 'rb')
+ must_continue = True
+ while must_continue:
+ read_bytes = file_handle.read(8096)
+ if not read_bytes:
+ must_continue = False
+ else:
+ md5_hash.update(read_bytes)
+ return md5_hash.hexdigest()
+
+def content_has_changed(filenames, md5):
+ return md5 != compute_hash(filenames)
def image_path(instance, filename):
"""Return path to an image."""
diff --git a/zds/utils/tutorials.py b/zds/utils/tutorials.py
index acf84fda96..fa53615985 100644
--- a/zds/utils/tutorials.py
+++ b/zds/utils/tutorials.py
@@ -9,7 +9,6 @@
from zds.utils import slugify
-
# Export-to-dict functions
def export_chapter(chapter, export_all=True):
from zds.tutorial.models import Extract
@@ -289,3 +288,5 @@ def move(obj, new_pos, position_f, parent_f, children_fn):
# All objects have been updated except the current one we want to move, so
# we can do it now
setattr(obj, position_f, new_pos)
+
+
From 8a9118b442db2d7ace67efc3a573929a5e487e2a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?fran=C3=A7ois=20dambrine?=
Date: Fri, 18 Jul 2014 21:37:47 +0200
Subject: [PATCH 02/20] =?UTF-8?q?mise=20=C3=A0=20jour=20des=20tests=20unit?=
=?UTF-8?q?aires?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
zds/tutorial/tests.py | 52 +++++--------------------------------------
1 file changed, 5 insertions(+), 47 deletions(-)
diff --git a/zds/tutorial/tests.py b/zds/tutorial/tests.py
index 442a91598f..110e49b89e 100644
--- a/zds/tutorial/tests.py
+++ b/zds/tutorial/tests.py
@@ -706,6 +706,7 @@ def test_workflow_tuto(self):
'title': u"Partie 2 : edition de titre",
'introduction': u"Expérimentation : edition d'introduction",
'conclusion': u"C'est terminé : edition de conlusion",
+ "last_hash": compute_hash([os.path.join(p2.get_path(),"introduction.md"),os.path.join(p2.get_path(),"conclusion.md")])
},
follow=True)
self.assertContains(response=result, text = u"Partie 2 : edition de titre")
@@ -720,6 +721,7 @@ def test_workflow_tuto(self):
'title': u"Chapitre 3 : edition de titre",
'introduction': u"Edition d'introduction",
'conclusion': u"Edition de conlusion",
+ "last_hash": compute_hash([os.path.join(c3.get_path(),"introduction.md"),os.path.join(c3.get_path(),"conclusion.md")])
},
follow=True)
self.assertContains(response=result, text = u"Chapitre 3 : edition de titre")
@@ -734,6 +736,7 @@ def test_workflow_tuto(self):
'title': u"Partie 2 : seconde edition de titre",
'introduction': u"Expérimentation : seconde edition d'introduction",
'conclusion': u"C'est terminé : seconde edition de conlusion",
+ "last_hash": compute_hash([os.path.join(p2.get_path(),"introduction.md"),os.path.join(p2.get_path(),"conclusion.md")])
},
follow=True)
self.assertContains(response=result, text = u"Partie 2 : seconde edition de titre")
@@ -748,6 +751,7 @@ def test_workflow_tuto(self):
'title': u"Chapitre 2 : edition de titre",
'introduction': u"Edition d'introduction",
'conclusion': u"Edition de conlusion",
+ "last_hash": compute_hash([os.path.join(c2.get_path(),"introduction.md"),os.path.join(c2.get_path(),"conclusion.md")])
},
follow=True)
self.assertContains(response=result, text = u"Chapitre 2 : edition de titre")
@@ -792,53 +796,6 @@ def test_workflow_tuto(self):
self.assertEqual(Chapter.objects.filter(part__tutorial=tuto.pk).count(), 2)
self.assertEqual(Part.objects.filter(tutorial=tuto.pk).count(), 2)
- def test_available_tuto(self):
- """ Test that all page of big tutorial is available"""
- parts = self.bigtuto.get_parts()
- for part in parts:
- result = self.client.get(reverse(
- 'zds.tutorial.views.view_part_online',
- args=[
- self.bigtuto.pk,
- self.bigtuto.slug,
- part.pk,
- part.slug]),
- follow=True)
- self.assertEqual(result.status_code, 200)
- result = self.client.get(reverse(
- 'zds.tutorial.views.view_part',
- args=[
- self.bigtuto.pk,
- self.bigtuto.slug,
- part.pk,
- part.slug]),
- follow=True)
- self.assertEqual(result.status_code, 200)
- chapters = part.get_chapters()
- for chapter in chapters:
- result = self.client.get(reverse(
- 'zds.tutorial.views.view_chapter_online',
- args=[
- self.bigtuto.pk,
- self.bigtuto.slug,
- part.pk,
- part.slug,
- chapter.pk,
- chapter.slug]),
- follow=True)
- self.assertEqual(result.status_code, 200)
- result = self.client.get(reverse(
- 'zds.tutorial.views.view_chapter',
- args=[
- self.bigtuto.pk,
- self.bigtuto.slug,
- part.pk,
- part.slug,
- chapter.pk,
- chapter.slug]),
- follow=True)
- self.assertEqual(result.status_code, 200)
-
def test_url_for_member(self):
"""Test simple get request by simple member."""
@@ -2411,6 +2368,7 @@ def test_workflow_tuto(self):
{
'title': u"Extrait 2 : edition de titre",
'text': u"Edition d'introduction",
+ "last_hash": compute_hash([e2.get_path(), e2.get_path()])
},
follow=True)
self.assertEqual(result.status_code, 200)
From a28c9dd4f3020cf78f3d9668535a0d6e38a5c359 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?fran=C3=A7ois=20dambrine?=
Date: Fri, 18 Jul 2014 22:06:55 +0200
Subject: [PATCH 03/20] =?UTF-8?q?derniers=20probl=C3=A8mes=20d'actualisati?=
=?UTF-8?q?on=20des=20TU?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
zds/tutorial/tests.py | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/zds/tutorial/tests.py b/zds/tutorial/tests.py
index 110e49b89e..b83b02d4ca 100644
--- a/zds/tutorial/tests.py
+++ b/zds/tutorial/tests.py
@@ -18,7 +18,7 @@
from zds.gallery.factories import GalleryFactory
from zds.tutorial.models import Note, Tutorial, Validation, Extract, Part, Chapter
from zds.utils.models import SubCategory, Licence, Alert
-
+from zds.utils.misc import compute_hash
@override_settings(MEDIA_ROOT=os.path.join(SITE_ROOT, 'media-test'))
@override_settings(REPO_PATH=os.path.join(SITE_ROOT, 'tutoriels-private-test'))
@@ -540,7 +540,7 @@ def test_workflow_tuto(self):
self.assertEqual(result.status_code, 302)
self.assertEqual(Part.objects.filter(tutorial=tuto).count(), 2)
p2 = Part.objects.filter(tutorial=tuto).last()
-
+ self.assertEqual(u"Analyse", p2.get_introduction())
#check view offline
result = self.client.get(
reverse(
@@ -706,7 +706,8 @@ def test_workflow_tuto(self):
'title': u"Partie 2 : edition de titre",
'introduction': u"Expérimentation : edition d'introduction",
'conclusion': u"C'est terminé : edition de conlusion",
- "last_hash": compute_hash([os.path.join(p2.get_path(),"introduction.md"),os.path.join(p2.get_path(),"conclusion.md")])
+ "last_hash": compute_hash([os.path.join(p2.tutorial.get_path(), p2.introduction),
+ os.path.join(p2.tutorial.get_path(), p2.conclusion)])
},
follow=True)
self.assertContains(response=result, text = u"Partie 2 : edition de titre")
@@ -728,7 +729,7 @@ def test_workflow_tuto(self):
self.assertContains(response=result, text = u"Edition d'introduction")
self.assertContains(response=result, text = u"Edition de conlusion")
self.assertEqual(Chapter.objects.filter(part=p2.pk).count(), 3)
-
+ p2 = Part.objects.filter(pk=p2.pk).first()
#edit part 2
result = self.client.post(
reverse('zds.tutorial.views.edit_part') + '?partie={}'.format(p2.pk),
@@ -736,7 +737,8 @@ def test_workflow_tuto(self):
'title': u"Partie 2 : seconde edition de titre",
'introduction': u"Expérimentation : seconde edition d'introduction",
'conclusion': u"C'est terminé : seconde edition de conlusion",
- "last_hash": compute_hash([os.path.join(p2.get_path(),"introduction.md"),os.path.join(p2.get_path(),"conclusion.md")])
+ "last_hash": compute_hash([os.path.join(p2.tutorial.get_path(), p2.introduction),
+ os.path.join(p2.tutorial.get_path(), p2.conclusion)])
},
follow=True)
self.assertContains(response=result, text = u"Partie 2 : seconde edition de titre")
@@ -2368,7 +2370,7 @@ def test_workflow_tuto(self):
{
'title': u"Extrait 2 : edition de titre",
'text': u"Edition d'introduction",
- "last_hash": compute_hash([e2.get_path(), e2.get_path()])
+ "last_hash": compute_hash([e2.get_path()])
},
follow=True)
self.assertEqual(result.status_code, 200)
From b34e5a0cb06d869401b34e3ef684a460ee0c29ea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?fran=C3=A7ois=20dambrine?=
Date: Sat, 19 Jul 2014 13:23:48 +0200
Subject: [PATCH 04/20] =?UTF-8?q?l'accueil=20des=20tuto=20est=20aussi=20pr?=
=?UTF-8?q?ot=C3=A9g=C3=A9e=20contre=20l'=C3=A9crasement,=20l'avertissemen?=
=?UTF-8?q?t=20est=20d=C3=A9sormais=20mieux=20int=C3=A9gr=C3=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
ajoute des tests unitaires
pep8 et indentation dans les templates
---
templates/tutorial/chapter/edit.html | 4 +-
templates/tutorial/extract/edit.html | 4 +-
templates/tutorial/part/edit.html | 6 +-
templates/tutorial/tutorial/edit.html | 13 ++-
zds/tutorial/tests.py | 118 +++++++++++++++++++++++++-
zds/tutorial/views.py | 21 ++++-
6 files changed, 151 insertions(+), 15 deletions(-)
diff --git a/templates/tutorial/chapter/edit.html b/templates/tutorial/chapter/edit.html
index 696f8f3b76..8bef5a9fba 100644
--- a/templates/tutorial/chapter/edit.html
+++ b/templates/tutorial/chapter/edit.html
@@ -41,7 +41,9 @@
{% block content %}
{% if new_version %}
-
Une nouvelle version a été postée
+
+ Une nouvelle version a été postée avant que vous ne validiez.
+
{% endif%}
{% crispy form %}
{% endblock %}
\ No newline at end of file
diff --git a/templates/tutorial/extract/edit.html b/templates/tutorial/extract/edit.html
index f5b585d53a..05be5a735e 100644
--- a/templates/tutorial/extract/edit.html
+++ b/templates/tutorial/extract/edit.html
@@ -30,7 +30,9 @@ Éditer l'extrait
{% block content %}
{% if new_version %}
- Une nouvelle version a été postée
+
+ Une nouvelle version a été postée avant que vous ne validiez.
+
{% endif%}
{% crispy form %}
diff --git a/templates/tutorial/part/edit.html b/templates/tutorial/part/edit.html
index 82dbb0257c..ef9eff34ad 100644
--- a/templates/tutorial/part/edit.html
+++ b/templates/tutorial/part/edit.html
@@ -36,7 +36,9 @@
{% block content %}
{% if new_version %}
-
Une nouvelle version a été postée
- {% endif%}
+
+ Une nouvelle version a été postée avant que vous ne validiez.
+
+ {% endif %}
{% crispy form %}
{% endblock %}
\ No newline at end of file
diff --git a/templates/tutorial/tutorial/edit.html b/templates/tutorial/tutorial/edit.html
index 59bf338268..a5e35ee61a 100644
--- a/templates/tutorial/tutorial/edit.html
+++ b/templates/tutorial/tutorial/edit.html
@@ -21,23 +21,20 @@
{% block headline_sub %}
- {{ tutorial.description }}
+ {{ tutorial.description }}
{% endblock %}
-
-
-
{% block breadcrumb %}
{{ tutorial.title }}
Éditer le tutoriel
-
-
{% endblock %}
{% block content %}
{% if new_version %}
- Une nouvelle version a été postée
- {% endif%}
+
+ Une nouvelle version a été postée avant que vous ne validiez.
+
+ {% endif %}
{% crispy form %}
{% endblock %}
\ No newline at end of file
diff --git a/zds/tutorial/tests.py b/zds/tutorial/tests.py
index b83b02d4ca..09181b3754 100644
--- a/zds/tutorial/tests.py
+++ b/zds/tutorial/tests.py
@@ -722,7 +722,8 @@ def test_workflow_tuto(self):
'title': u"Chapitre 3 : edition de titre",
'introduction': u"Edition d'introduction",
'conclusion': u"Edition de conlusion",
- "last_hash": compute_hash([os.path.join(c3.get_path(),"introduction.md"),os.path.join(c3.get_path(),"conclusion.md")])
+ "last_hash": compute_hash([os.path.join(c3.get_path(),"introduction.md"),
+ os.path.join(c3.get_path(),"conclusion.md")])
},
follow=True)
self.assertContains(response=result, text = u"Chapitre 3 : edition de titre")
@@ -753,7 +754,8 @@ def test_workflow_tuto(self):
'title': u"Chapitre 2 : edition de titre",
'introduction': u"Edition d'introduction",
'conclusion': u"Edition de conlusion",
- "last_hash": compute_hash([os.path.join(c2.get_path(),"introduction.md"),os.path.join(c2.get_path(),"conclusion.md")])
+ "last_hash": compute_hash([os.path.join(c2.get_path(),"introduction.md"),
+ os.path.join(c2.get_path(),"conclusion.md")])
},
follow=True)
self.assertContains(response=result, text = u"Chapitre 2 : edition de titre")
@@ -798,6 +800,112 @@ def test_workflow_tuto(self):
self.assertEqual(Chapter.objects.filter(part__tutorial=tuto.pk).count(), 2)
self.assertEqual(Part.objects.filter(tutorial=tuto.pk).count(), 2)
+ def test_conflict_does_not_destroy(self):
+ """tests that simultaneous edition does not conflict"""
+ sub = SubCategory()
+ sub.title = "toto"
+ sub.save()
+ # logout before
+ self.client.logout()
+ # first, login with author :
+ self.assertEqual(
+ self.client.login(
+ username=self.user_author.username,
+ password='hostel77'),
+ True)
+ # test tuto
+ (introduction_path, conclusion_path) =(os.path.join(self.bigtuto.get_path(),"introduction.md"), os.path.join(self.bigtuto.get_path(),"conclusion.md"))
+ hash = compute_hash([introduction_path, conclusion_path])
+ self.client.post(
+ reverse('zds.tutorial.views.edit_tutorial')+'?tutoriel={0}'.format(self.bigtuto.pk),
+ {
+ 'title': self.bigtuto.title,
+ 'description': "nouvelle description",
+ 'subcategory': [sub.pk],
+ 'introduction': self.bigtuto.get_introduction() +" un essai",
+ 'conclusion': self.bigtuto.get_conclusion(),
+ 'last_hash': hash
+ }, follow= True)
+ conflict_result = self.client.post(
+ reverse('zds.tutorial.views.edit_tutorial')+'?tutoriel={0}'.format(self.bigtuto.pk),
+ {
+ 'title': self.bigtuto.title,
+ 'description': "nouvelle description",
+ 'subcategory': [sub.pk],
+ 'introduction': self.bigtuto.get_introduction() +" conflictual",
+ 'conclusion': self.bigtuto.get_conclusion(),
+ 'last_hash': hash
+ }, follow= False)
+ self.assertEqual(conflict_result.status_code, 200)
+ self.assertContains(response=conflict_result, text = u"nouvelle version")
+
+ # test parts
+
+ result = self.client.post(
+ reverse('zds.tutorial.views.add_part') + '?tutoriel={}'.format(self.bigtuto.pk),
+ {
+ 'title': u"Partie 2",
+ 'introduction': u"Analyse",
+ 'conclusion': u"Fin de l'analyse",
+ },
+ follow=False)
+ p1 = Part.objects.last()
+ hash = compute_hash([os.path.join(p1.tutorial.get_path(), p1.introduction),
+ os.path.join(p1.tutorial.get_path(), p1.conclusion)])
+ self.client.post(
+ reverse('zds.tutorial.views.edit_part') + '?partie={}'.format(p1.pk),
+ {
+ 'title': u"Partie 2 : edition de titre",
+ 'introduction': u"Expérimentation : edition d'introduction",
+ 'conclusion': u"C'est terminé : edition de conlusion",
+ "last_hash": hash
+ },
+ follow=False)
+ conflict_result = self.client.post(
+ reverse('zds.tutorial.views.edit_part') + '?partie={}'.format(p1.pk),
+ {
+ 'title': u"Partie 2 : edition de titre",
+ 'introduction': u"Expérimentation : edition d'introduction conflit",
+ 'conclusion': u"C'est terminé : edition de conlusion",
+ "last_hash": hash
+ },
+ follow=False)
+ self.assertEqual(conflict_result.status_code, 200)
+ self.assertContains(response=conflict_result, text = u"nouvelle version")
+
+ # test chapter
+ result = self.client.post(
+ reverse('zds.tutorial.views.add_chapter') + '?partie={}'.format(p1.pk),
+ {
+ 'title': u"Chapitre 1",
+ 'introduction':"Mon premier chapitre",
+ 'conclusion': "Fin de mon premier chapitre",
+ },
+ follow=False)
+ c1 = Chapter.objects.last()
+ hash = compute_hash([os.path.join(c1.get_path(),"introduction.md"),
+ os.path.join(c1.get_path(),"conclusion.md")])
+ self.client.post(
+ reverse('zds.tutorial.views.edit_chapter') + '?chapitre={}'.format(c1.pk),
+ {
+ 'title': u"Chapitre 3 : edition de titre",
+ 'introduction': u"Edition d'introduction",
+ 'conclusion': u"Edition de conlusion",
+ "last_hash": hash
+ },
+ follow=True)
+ conflict_result = self.client.post(
+ reverse('zds.tutorial.views.edit_chapter') + '?chapitre={}'.format(c1.pk),
+ {
+ 'title': u"Chapitre 3 : edition de titre",
+ 'introduction': u"Edition d'introduction conflict",
+ 'conclusion': u"Edition de conlusion",
+ "last_hash": hash
+ },
+ follow=True)
+ self.assertEqual(conflict_result.status_code, 200)
+ self.assertContains(response=conflict_result, text = u"nouvelle version")
+
def test_url_for_member(self):
"""Test simple get request by simple member."""
@@ -1337,6 +1445,8 @@ def test_gallery_tuto_change_name(self):
'introduction': self.bigtuto.introduction,
'description': self.bigtuto.description,
'conclusion': self.bigtuto.conclusion,
+ 'last_hash': compute_hash([os.path.join(self.bigtuto.get_path(),"introduction.md"),
+ os.path.join(self.bigtuto.get_path(),"conclusion.md")])
},
follow=True)
@@ -2028,6 +2138,8 @@ def test_edit_tuto(self):
'subcategory': [sub.pk],
'introduction': self.minituto.get_introduction(),
'conclusion': self.minituto.get_conclusion(),
+ 'last_hash': compute_hash([os.path.join(self.minituto.get_path(),"introduction.md"),
+ os.path.join(self.minituto.get_path(),"conclusion.md")])
},
follow=False
)
@@ -2045,6 +2157,8 @@ def test_edit_tuto(self):
'subcategory': [sub.pk],
'introduction': self.minituto.get_introduction(),
'conclusion': self.minituto.get_conclusion(),
+ 'last_hash': compute_hash([os.path.join(self.minituto.get_path(),"introduction.md"),
+ os.path.join(self.minituto.get_path(),"conclusion.md")])
},
follow=False
)
diff --git a/zds/tutorial/views.py b/zds/tutorial/views.py
index f3d02e60ac..b59f3f57b6 100644
--- a/zds/tutorial/views.py
+++ b/zds/tutorial/views.py
@@ -977,10 +977,29 @@ def edit_tutorial(request):
if request.user not in tutorial.authors.all():
if not request.user.has_perm("tutorial.change_tutorial"):
raise PermissionDenied
+ introduction = os.path.join(tutorial.get_path(), "introduction.md")
+ conclusion = os.path.join(tutorial.get_path(), "conclusion.md")
if request.method == "POST":
form = TutorialForm(request.POST, request.FILES)
if form.is_valid():
data = form.data
+ if content_has_changed([introduction, conclusion], data["last_hash"]):
+ form = TutorialForm(initial={
+ "title": tutorial.title,
+ "type": tutorial.type,
+ "licence": tutorial.licence,
+ "description": tutorial.description,
+ "subcategory": tutorial.subcategory.all(),
+ "introduction": tutorial.get_introduction(),
+ "conclusion": tutorial.get_conclusion(),
+
+ })
+ return render_template("tutorial/tutorial/edit.html",
+ {
+ "tutorial": tutorial, "form": form,
+ "last_hash": compute_hash([introduction, conclusion]),
+ "new_version": True
+ })
old_slug = tutorial.get_path()
tutorial.title = data["title"]
tutorial.description = data["description"]
@@ -1045,7 +1064,7 @@ def edit_tutorial(request):
"conclusion": tutorial.get_conclusion(),
})
return render_template("tutorial/tutorial/edit.html",
- {"tutorial": tutorial, "form": form})
+ {"tutorial": tutorial, "form": form, "last_hash": compute_hash([introduction, conclusion])})
# Parts.
From 240881fab2bc03a5a6836666192f87c95c005726 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?fran=C3=A7ois=20dambrine?=
Date: Tue, 22 Jul 2014 07:28:50 +0200
Subject: [PATCH 05/20] indentation
---
templates/tutorial/chapter/edit.html | 2 +-
templates/tutorial/extract/edit.html | 2 +-
templates/tutorial/tutorial/edit.html | 6 +++---
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/templates/tutorial/chapter/edit.html b/templates/tutorial/chapter/edit.html
index 8bef5a9fba..3db72a8c76 100644
--- a/templates/tutorial/chapter/edit.html
+++ b/templates/tutorial/chapter/edit.html
@@ -44,6 +44,6 @@
Une nouvelle version a été postée avant que vous ne validiez.
- {% endif%}
+ {% endif %}
{% crispy form %}
{% endblock %}
\ No newline at end of file
diff --git a/templates/tutorial/extract/edit.html b/templates/tutorial/extract/edit.html
index 05be5a735e..45341465d1 100644
--- a/templates/tutorial/extract/edit.html
+++ b/templates/tutorial/extract/edit.html
@@ -33,7 +33,7 @@ Éditer l'extrait
Une nouvelle version a été postée avant que vous ne validiez.
- {% endif%}
+ {% endif %}
{% crispy form %}
{% if form.text.value %}
diff --git a/templates/tutorial/tutorial/edit.html b/templates/tutorial/tutorial/edit.html
index a5e35ee61a..6f3cdb358e 100644
--- a/templates/tutorial/tutorial/edit.html
+++ b/templates/tutorial/tutorial/edit.html
@@ -32,9 +32,9 @@
{% block content %}
{% if new_version %}
-
- Une nouvelle version a été postée avant que vous ne validiez.
-
+
+ Une nouvelle version a été postée avant que vous ne validiez.
+
{% endif %}
{% crispy form %}
{% endblock %}
\ No newline at end of file
From 85b15286b5d7f4449f8cf99758ce9df9b8f18bd1 Mon Sep 17 00:00:00 2001
From: Alex-D
Date: Tue, 22 Jul 2014 16:29:31 +0200
Subject: [PATCH 06/20] =?UTF-8?q?Corrige=20la=20bani=C3=A8re=20cookies?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
assets/scss/_all-supports.scss | 53 ++++++++++++++++++----------------
1 file changed, 28 insertions(+), 25 deletions(-)
diff --git a/assets/scss/_all-supports.scss b/assets/scss/_all-supports.scss
index 7f59097dee..94893993e8 100644
--- a/assets/scss/_all-supports.scss
+++ b/assets/scss/_all-supports.scss
@@ -48,39 +48,41 @@
background: #062E41;
display: none;
- p {
+ span {
+ display: inline-block;
margin: 0;
padding: 7px 0;
color: #EEE;
+ line-height: 23px;
+ }
- a {
- display: inline-block;
- color: #EEE;
- padding: 4px 13px;
- margin-left: 15px;
- background: $blue;
- text-decoration: none;
+ a {
+ display: inline-block;
+ color: #EEE;
+ padding: 4px 13px;
+ margin-left: 15px;
+ background: $blue;
+ text-decoration: none;
- &:hover,
- &:focus {
- background: #EEE;
- color: $blue;
- }
+ &:hover,
+ &:focus {
+ background: #EEE;
+ color: $blue;
}
+ }
- button {
- display: inline-block;
- background: none;
- border: none;
- text-decoration: underline;
- margin: 0;
- padding: 0;
- color: #EEE;
+ #reject-cookies {
+ display: inline-block;
+ background: none;
+ border: none;
+ text-decoration: underline;
+ margin: 0;
+ padding: 0;
+ color: #EEE;
- &:hover,
- &:focus {
- text-decoration: none;
- }
+ &:hover,
+ &:focus {
+ text-decoration: none;
}
}
@@ -91,6 +93,7 @@
padding: 4px 15px;
border: none;
transition: background .15s, color .15s;
+ margin-top: 3px;
&:hover,
&:focus {
From 2e40a1ecffc0ce0ba0544aeb81e1c3cfd72d1c59 Mon Sep 17 00:00:00 2001
From: Alex-D
Date: Tue, 22 Jul 2014 16:31:43 +0200
Subject: [PATCH 07/20] Fix #1257 Orthographe accueil
---
templates/home.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/templates/home.html b/templates/home.html
index ca2ab9f2bc..9e71f19a7e 100644
--- a/templates/home.html
+++ b/templates/home.html
@@ -40,7 +40,7 @@ Zeste de Savoir, la connaissance pour tous et sans pépins
Tous les membres peuvent écrire et publier des tutoriels et articles sur le site. Pour assurer la qualité et la pédagogie du contenu, l'équipe du site valide chaque cours avant publication.
- Tout cela est entièrement gratuit et garanti sans publicité, le site est géré et financé par une association a but non lucratif.
+ Tout cela est entièrement gratuit et garanti sans publicité, le site est géré et financé par une association à but non lucratif.
From ed7b771e2e23140bf3cfadd36b2c81bd3c342738 Mon Sep 17 00:00:00 2001
From: Alex-D
Date: Tue, 22 Jul 2014 16:44:36 +0200
Subject: [PATCH 08/20] =?UTF-8?q?Fix=20#1123=20block=20image=20>=20meta=5F?=
=?UTF-8?q?image=20+=20metadonn=C3=A9es=20articles?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
templates/article/view.html | 25 ++++++++++++++++++++
templates/base.html | 10 ++++----
templates/tutorial/chapter/view_online.html | 16 +++++++++++++
templates/tutorial/part/view_online.html | 2 +-
templates/tutorial/tutorial/view_online.html | 2 +-
5 files changed, 48 insertions(+), 7 deletions(-)
diff --git a/templates/article/view.html b/templates/article/view.html
index 13de5eb1b0..b1565dbc84 100644
--- a/templates/article/view.html
+++ b/templates/article/view.html
@@ -14,6 +14,31 @@
+{% block meta_image %}{% spaceless %}
+ {% if article.image %}
+ {{ article.image.article_illu.url }}
+ {% else %}
+ {{ block.super }}
+ {% endif %}
+{% endspaceless %}{% endblock %}
+
+
+
+{% block opengraph %}
+
+
+ {% if article.pubdate %}
+
+ {% endif %}
+
+
+ {% for tag in tags.all %}
+
+ {% endfor %}
+{% endblock %}
+
+
+
{% block breadcrumb %}
{{ article.title }}
{% endblock %}
diff --git a/templates/base.html b/templates/base.html
index bd057037e5..d201ef6668 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -45,8 +45,8 @@
- {% captureas image %}
- {{ request.META.HTTP_HOST }}{% block image %}{% spaceless %}
+ {% captureas meta_image %}
+ {{ request.META.HTTP_HOST }}{% block meta_image %}{% spaceless %}
{% static "images/apple-touch-icon-144x144-precomposed.png" %}
{% endspaceless %}{% endblock %}
{% endcaptureas %}
@@ -57,8 +57,8 @@
-
-
+
+
{% block opengraph %}
{% endblock %}
@@ -72,7 +72,7 @@
-
+
{# Stylesheets #}
diff --git a/templates/tutorial/chapter/view_online.html b/templates/tutorial/chapter/view_online.html
index aee1ff44cb..a9f5dc7a36 100644
--- a/templates/tutorial/chapter/view_online.html
+++ b/templates/tutorial/chapter/view_online.html
@@ -9,6 +9,22 @@
+{% block meta_image %}{% spaceless %}
+ {% if tutorial.image %}
+ {{ tutorial.image.physical.tutorial_illu.url }}
+ {% else %}
+ {{ block.super }}
+ {% endif %}
+{% endspaceless %}{% endblock %}
+
+
+
+{% block opengraph %}
+ {% include "tutorial/includes/opengraph.part.html" %}
+{% endblock %}
+
+
+
{% block breadcrumb %}
diff --git a/templates/tutorial/part/view_online.html b/templates/tutorial/part/view_online.html
index 83690b206a..30850ece3e 100644
--- a/templates/tutorial/part/view_online.html
+++ b/templates/tutorial/part/view_online.html
@@ -8,7 +8,7 @@
-{% block image %}{% spaceless %}
+{% block meta_image %}{% spaceless %}
{% if tutorial.image %}
{{ tutorial.image.physical.tutorial_illu.url }}
{% else %}
diff --git a/templates/tutorial/tutorial/view_online.html b/templates/tutorial/tutorial/view_online.html
index 12495742e2..4fdcb85d06 100644
--- a/templates/tutorial/tutorial/view_online.html
+++ b/templates/tutorial/tutorial/view_online.html
@@ -19,7 +19,7 @@
-{% block image %}{% spaceless %}
+{% block meta_image %}{% spaceless %}
{% if tutorial.image %}
{{ tutorial.image.physical.tutorial_illu.url }}
{% else %}
From 6b9699414056d555167e22839121d1eddd5bad9b Mon Sep 17 00:00:00 2001
From: Alex-D
Date: Tue, 22 Jul 2014 16:53:06 +0200
Subject: [PATCH 09/20] Fix #1256 : Correction du karma
---
assets/scss/_all-supports.scss | 15 ++++++++++-----
templates/misc/message.part.html | 4 ++--
2 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/assets/scss/_all-supports.scss b/assets/scss/_all-supports.scss
index 94893993e8..f04cf7ebed 100644
--- a/assets/scss/_all-supports.scss
+++ b/assets/scss/_all-supports.scss
@@ -2420,9 +2420,12 @@ table {
}
}
}
- span:not(.has-vote) {
- border-bottom: none;
- opacity: .5;
+ .upvote,
+ .downvote {
+ &:not(.has-vote) {
+ border-bottom: none;
+ opacity: .5;
+ }
}
.tick {
@@ -2518,7 +2521,8 @@ table {
}
a,
- span,
+ .upvote,
+ .downvote,
button {
display: block;
float: left;
@@ -2538,7 +2542,8 @@ table {
}
}
a,
- span,
+ .upvote,
+ .downvote,
button.ico-after {
border-bottom: 1px solid #D2D5D6;
text-decoration: none;
diff --git a/templates/misc/message.part.html b/templates/misc/message.part.html
index d643f45cd5..17711f8ab1 100644
--- a/templates/misc/message.part.html
+++ b/templates/misc/message.part.html
@@ -284,7 +284,7 @@
{% if message.like > 0 %}has-vote{% endif %}
{% if not user.is_authenticated and message.like > message.dislike %}voted{% endif %}"
{% if message.like > 0 %}
- title="{{ message.like }} personnes ont trouvé ce message utile"
+ title="{{ message.like }} personne{{ message.like|pluralize }} {% if message.like > 1 %}ont{% else %}a{% endif %} trouvé ce message utile"
{% endif %}
>
+
@@ -298,7 +298,7 @@
{% if message.dislike > 0 %}has-vote{% endif %}
{% if not user.is_authenticated and message.like < message.dislike %}voted{% endif %}"
{% if message.dislike > 0 %}
- title="{{ message.dislike }} personnes n'ont pas trouvé ce message utile"
+ title="{{ message.dislike }} personne{{ message.dislike|pluralize }} n'{% if message.dislike > 1 %}ont{% else %}a{% endif %} pas trouvé ce message utile"
{% endif %}
>
-
From d17b6e24f0cdd8bca58573d9b4b3c70a247f7970 Mon Sep 17 00:00:00 2001
From: Alex-D
Date: Tue, 22 Jul 2014 17:14:15 +0200
Subject: [PATCH 10/20] Corrige les marges des messages
---
assets/scss/_all-supports.scss | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/assets/scss/_all-supports.scss b/assets/scss/_all-supports.scss
index f04cf7ebed..c7bc427e1b 100644
--- a/assets/scss/_all-supports.scss
+++ b/assets/scss/_all-supports.scss
@@ -2323,8 +2323,13 @@ table {
clear: both;
padding-top: 1px;
- & > p:first-child {
- margin-top: 7px;
+ & > div {
+ & > p:first-child {
+ margin-top: 7px;
+ }
+ & > figure:first-child {
+ margin-top: 8px;
+ }
}
.message-hidden-content {
From 4d3072f24fb91a5cec658acf60a721a01a675cd9 Mon Sep 17 00:00:00 2001
From: Alex-D
Date: Tue, 22 Jul 2014 17:38:23 +0200
Subject: [PATCH 11/20] Ajout d'informations sur la page d'import de tutoriels
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Légères corrections de templates import
---
assets/scss/_all-supports.scss | 4 ++++
templates/tutorial/tutorial/import.html | 15 ++++++++++++++-
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/assets/scss/_all-supports.scss b/assets/scss/_all-supports.scss
index c7bc427e1b..1410b5d4a3 100644
--- a/assets/scss/_all-supports.scss
+++ b/assets/scss/_all-supports.scss
@@ -2368,6 +2368,10 @@ table {
text-indent: 20px;
}
+ .member-item {
+ margin: 0;
+ }
+
textarea {
margin: 10px 0 10px -1px;
background-color: transparent;
diff --git a/templates/tutorial/tutorial/import.html b/templates/tutorial/tutorial/import.html
index 6dac964765..ccf831b52f 100644
--- a/templates/tutorial/tutorial/import.html
+++ b/templates/tutorial/tutorial/import.html
@@ -24,13 +24,26 @@
{% block content %}
+
+ Vous êtes l'auteur d'un cours sur le SdZ ? Nous l'avons récupéré pour vous ! Il est disponible hors-ligne et n'attend plus que votre accord pour être publié. Il vous suffit d'en faire la demande à un membre du Staff via le forum ou directement par MP.
+
+
+
Envoi de fichiers .tuto
+
+ Attention, le fichier attendu n'est pas un .tuto du Site du Zéro, mais un format intermédiaire qui contient du Markdown et non du zCode. Contactez un membre du Staff pour demander la conversion de votre fichier.
+
+
+ Le fichier zip est celui qui contient les ressources de votre tutoriel. Il est également issu du convertisseur Markdown > zCode.
+
{% crispy form %}
-
{% if old_tutos %}
Récupérer un tutoriel du Site du Zéro
+
+ Ci-dessous se trouve la liste des tutoriels que vous aviez rédigé sur le Site du Zéro et que nous avons récupéré. Vous pouvez les importer pour continuer leur rédaction et les soumettre à la validation. Si vous pensez qu'il en manque un, vous pouvez contacter un membre du Staff.
+
From 8add62dbbcedd4287b1306a594b860b194f5488a Mon Sep 17 00:00:00 2001
From: firm1
Date: Wed, 23 Jul 2014 00:02:53 +0200
Subject: [PATCH 12/20] =?UTF-8?q?affiche=20les=20mode=20versionn=C3=A9s=20?=
=?UTF-8?q?de=20tutoriels?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
zds/tutorial/models.py | 7 +++++--
zds/tutorial/views.py | 39 +++++++++++++++++++++++++++------------
2 files changed, 32 insertions(+), 14 deletions(-)
diff --git a/zds/tutorial/models.py b/zds/tutorial/models.py
index a429fd318d..20a1d29893 100644
--- a/zds/tutorial/models.py
+++ b/zds/tutorial/models.py
@@ -183,7 +183,8 @@ def get_prod_path(self):
str(self.pk) + '_' + slugify(data['title']))
def load_dic(self, mandata):
- mandata['get_absolute_url_online'] = self.get_absolute_url_online()
+ mandata['get_absolute_url_online'] = reverse('zds.tutorial.views.view_tutorial_online',
+ args=[self.pk, slugify(mandata["title"])])
mandata['get_absolute_url'] = self.get_absolute_url()
mandata['get_introduction_online'] = self.get_introduction_online()
mandata['get_conclusion_online'] = self.get_conclusion_online()
@@ -203,7 +204,9 @@ def load_dic(self, mandata):
return mandata
- def load_json_for_public(self):
+ def load_json_for_public(self, sha=None):
+ if sha is None:
+ sha = self.sha_public
repo = Repo(self.get_path())
mantuto = get_blob(repo.commit(self.sha_public).tree, 'manifest.json')
data = json_reader.loads(mantuto)
diff --git a/zds/tutorial/views.py b/zds/tutorial/views.py
index bb0a52d5cf..3a5aaf4e11 100644
--- a/zds/tutorial/views.py
+++ b/zds/tutorial/views.py
@@ -89,7 +89,13 @@ def index(request):
tutorials = Tutorial.objects.filter(
sha_public__isnull=False,
subcategory__in=[tag]).exclude(sha_public="").order_by("-pubdate").all()
- return render_template("tutorial/index.html", {"tutorials": tutorials, "tag": tag})
+
+ tuto_versions = []
+ for tutorial in tutorials:
+ mandata = tutorial.load_json_for_public()
+ mandata = tutorial.load_dic(mandata)
+ tuto_versions.append(mandata)
+ return render_template("tutorial/index.html", {"tutorials": tuto_versions, "tag": tag})
# Staff actions.
@@ -219,10 +225,6 @@ def history(request, tutorial_pk, tutorial_slug):
if not request.user.has_perm("tutorial.change_tutorial"):
raise PermissionDenied
- # Make sure the URL is well-formed
-
- if not tutorial_slug == slugify(tutorial.title):
- return redirect(tutorial.get_absolute_url())
repo = Repo(tutorial.get_path())
logs = repo.head.reference.log()
logs = sorted(logs, key=attrgetter("time"), reverse=True)
@@ -2082,20 +2084,33 @@ def find_tuto(request, pk_user):
type = request.GET["type"]
except KeyError:
type = None
- u = get_object_or_404(User, pk=pk_user)
+ display_user = get_object_or_404(User, pk=pk_user)
if type == "beta":
tutorials = Tutorial.objects.all().filter(
- authors__in=[u],
+ authors__in=[display_user],
sha_beta__isnull=False).exclude(sha_beta="").order_by("-pubdate")
+
+ tuto_versions = []
+ for tutorial in tutorials:
+ mandata = tutorial.load_json_for_public(sha=tutorial.sha_beta)
+ mandata = tutorial.load_dic(mandata)
+ tuto_versions.append(mandata)
+
return render_template("tutorial/member/beta.html",
- {"tutorials": tutorials, "usr": u})
+ {"tutorials": tuto_versions, "usr": display_user})
else:
tutorials = Tutorial.objects.all().filter(
- authors__in=[u],
+ authors__in=[display_user],
sha_public__isnull=False).exclude(sha_public="").order_by("-pubdate")
-
- return render_template("tutorial/member/online.html", {"tutorials": tutorials,
- "usr": u})
+
+ tuto_versions = []
+ for tutorial in tutorials:
+ mandata = tutorial.load_json_for_public()
+ mandata = tutorial.load_dic(mandata)
+ tuto_versions.append(mandata)
+
+ return render_template("tutorial/member/online.html", {"tutorials": tuto_versions,
+ "usr": display_user})
def upload_images(images, tutorial):
From 5aa1723d3a89ae9df2c7e4e8e5a73cb2b6e8164a Mon Sep 17 00:00:00 2001
From: firm1
Date: Wed, 23 Jul 2014 00:03:26 +0200
Subject: [PATCH 13/20] =?UTF-8?q?affiche=20les=20mode=20versionn=C3=A9s=20?=
=?UTF-8?q?des=20articles?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
zds/article/models.py | 31 ++++++++++++++++----
zds/article/urls.py | 2 +-
zds/article/views.py | 66 +++++++++++++++++--------------------------
3 files changed, 53 insertions(+), 46 deletions(-)
diff --git a/zds/article/models.py b/zds/article/models.py
index 319afe9f34..29fe606c76 100644
--- a/zds/article/models.py
+++ b/zds/article/models.py
@@ -5,6 +5,7 @@
from django.core.files.uploadedfile import SimpleUploadedFile
from django.db import models
from math import ceil
+from git import Repo
import os
import string
import uuid
@@ -25,15 +26,11 @@
from zds.utils import get_current_user
from zds.utils import slugify
-from zds.utils.articles import export_article
+from zds.utils.articles import export_article, get_blob
from zds.utils.models import SubCategory, Comment, Licence
from django.core.urlresolvers import reverse
-IMAGE_MAX_WIDTH = 480
-IMAGE_MAX_HEIGHT = 100
-
-
def image_path(instance, filename):
"""Return path to an image."""
ext = filename.split('.')[-1]
@@ -141,6 +138,30 @@ def load_json(self, path=None, online=False):
else:
return None
+ def load_json_for_public(self):
+ repo = Repo(self.get_path())
+ manarticle = get_blob(repo.commit(self.sha_public).tree, 'manifest.json')
+ data = json_reader.loads(manarticle)
+
+ return data
+
+ def load_dic(self, article_version):
+ article_version['pk'] = self.pk
+ article_version['slug'] = slugify(article_version['title'])
+ article_version['image'] = self.image
+ article_version['pubdate'] = self.pubdate
+ article_version['is_locked'] = self.is_locked
+ article_version['sha_draft'] = self.sha_draft
+ article_version['sha_validation'] = self.sha_validation
+ article_version['sha_public'] = self.sha_public
+ article_version['get_reaction_count'] = self.get_reaction_count
+ article_version['get_absolute_url'] = reverse('zds.article.views.view',
+ args=[self.pk, self.slug])
+ article_version['get_absolute_url_online'] = reverse('zds.article.views.view_online',
+ args=[self.pk,slugify(article_version['title'])])
+
+ return article_version
+
def dump_json(self, path=None):
if path is None:
man_path = os.path.join(self.get_path(), 'manifest.json')
diff --git a/zds/article/urls.py b/zds/article/urls.py
index f1b2532f12..40c472c366 100644
--- a/zds/article/urls.py
+++ b/zds/article/urls.py
@@ -29,7 +29,7 @@
url(r'^nouveau/$', 'zds.article.views.new'),
url(r'^editer/$', 'zds.article.views.edit'),
url(r'^modifier/$', 'zds.article.views.modify'),
- url(r'^recherche/(?P\d+)/$',
+ url(r'^recherche/(?P\d+)/$',
'zds.article.views.find_article'),
diff --git a/zds/article/views.py b/zds/article/views.py
index b08d78a184..742934eefa 100644
--- a/zds/article/views.py
+++ b/zds/article/views.py
@@ -55,20 +55,26 @@ def index(request):
tag = None
if tag is None:
- article = Article.objects\
+ articles = Article.objects\
.filter(sha_public__isnull=False).exclude(sha_public="")\
.order_by('-pubdate')\
.all()
else:
# The tag isn't None and exist in the system. We can use it to retrieve
# all articles in the subcategory specified.
- article = Article.objects\
+ articles = Article.objects\
.filter(sha_public__isnull=False, subcategory__in=[tag])\
.exclude(sha_public="").order_by('-pubdate')\
.all()
+
+ article_versions = []
+ for article in articles:
+ article_version = article.load_json_for_public()
+ article_version = article.load_dic(article_version)
+ article_versions.append(article_version)
return render_template('article/index.html', {
- 'articles': article,
+ 'articles': article_versions,
'tag': tag,
})
@@ -83,10 +89,6 @@ def view(request, article_pk, article_slug):
if not request.user.has_perm('article.change_article'):
raise PermissionDenied
- # The slug of the article must to be right.
- if article_slug != slugify(article.title):
- return redirect(article.get_absolute_url())
-
# Retrieve sha given by the user. This sha must to be exist.
# If it doesn't exist, we take draft version of the article.
try:
@@ -105,17 +107,8 @@ def view(request, article_pk, article_slug):
manifest = get_blob(repo.commit(sha).tree, 'manifest.json')
article_version = json_reader.loads(manifest)
- article_version['txt'] = get_blob(
- repo.commit(sha).tree,
- article_version['text'])
- article_version['pk'] = article.pk
- article_version['slug'] = article.slug
- article_version['image'] = article.image
- article_version['pubdate'] = article.pubdate
- article_version['sha_draft'] = article.sha_draft
- article_version['sha_validation'] = article.sha_validation
- article_version['sha_public'] = article.sha_public
- article_version['get_absolute_url_online'] = article.get_absolute_url_online()
+ article_version['txt'] = get_blob(repo.commit(sha).tree, article_version['text'])
+ article_version = article.load_dic(article_version)
validation = Validation.objects.filter(article__pk=article.pk,
version=sha)\
@@ -135,28 +128,14 @@ def view_online(request, article_pk, article_slug):
"""Show the given article if exists and is visible."""
article = get_object_or_404(Article, pk=article_pk)
- # The slug of the article must to be right.
- if article_slug != slugify(article.title):
- return redirect(article.get_absolute_url_online())
-
# Load the article.
- article_version = article.load_json()
- txt = open(
- os.path.join(
- article.get_path(),
- article_version['text'] +
- '.html'),
- "r")
+ article_version = article.load_json_for_public()
+ txt = open(os.path.join(article.get_path(),
+ article_version['text'] + '.html'),
+ "r")
article_version['txt'] = txt.read()
txt.close()
- article_version['pk'] = article.pk
- article_version['slug'] = article.slug
- article_version['image'] = article.image
- article_version['pubdate'] = article.pubdate
- article_version['is_locked'] = article.is_locked
- article_version['get_reaction_count'] = article.get_reaction_count
- article_version['get_absolute_url'] = article.get_absolute_url()
- article_version['get_absolute_url_online'] = article.get_absolute_url_online()
+ article_version = article.load_dic(article_version)
# If the user is authenticated
if request.user.is_authenticated():
@@ -364,16 +343,23 @@ def edit(request):
})
-def find_article(request, name):
+def find_article(request, pk_user):
"""Find an article from his author."""
- user = get_object_or_404(User, pk=name)
+ user = get_object_or_404(User, pk=pk_user)
articles = Article.objects\
.filter(authors__in=[user], sha_public__isnull=False).exclude(sha_public="")\
.order_by('-pubdate')\
.all()
+
+ article_versions = []
+ for article in articles:
+ article_version = article.load_json_for_public()
+ article_version = article.load_dic(article_version)
+ article_versions.append(article_version)
+
# Paginator
return render_template('article/find.html', {
- 'articles': articles, 'usr': user,
+ 'articles': article_versions, 'usr': user,
})
From 8f5eb2db937fd3d5ab011c26d9523bed8d786845 Mon Sep 17 00:00:00 2001
From: firm1
Date: Wed, 23 Jul 2014 00:03:47 +0200
Subject: [PATCH 14/20] =?UTF-8?q?affiche=20les=20modes=20versionn=C3=A9s?=
=?UTF-8?q?=20sur=20l'accueil?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
zds/pages/views.py | 23 ++++++++---------------
1 file changed, 8 insertions(+), 15 deletions(-)
diff --git a/zds/pages/views.py b/zds/pages/views.py
index e372c970a7..14b0e961e8 100644
--- a/zds/pages/views.py
+++ b/zds/pages/views.py
@@ -29,20 +29,14 @@ def home(request):
tutos = []
for tuto in get_last_tutorials():
data = tuto.load_json_for_public()
- data['pk'] = tuto.pk
- data['image'] = tuto.image
- data['gallery'] = tuto.gallery
- data['pubdate'] = tuto.pubdate
- data['update'] = tuto.update
- data['subcategory'] = tuto.subcategory
- data['get_absolute_url_online'] = reverse(
- 'zds.tutorial.views.view_tutorial_online',
- args=[
- tuto.pk,
- slugify(
- data['title'])])
-
+ data = tuto.load_dic(data)
tutos.append(data)
+
+ articles = []
+ for article in get_last_articles():
+ data = article.load_json_for_public()
+ data = article.load_dic(data)
+ articles.append(data)
try:
with open(os.path.join(SITE_ROOT, 'quotes.txt'), 'r') as fh:
@@ -51,9 +45,8 @@ def home(request):
quote = u'Zeste de Savoir, la connaissance pour tous et sans pépins !'
return render_template('home.html', {
- 'last_topics': get_last_topics(request.user),
'last_tutorials': tutos,
- 'last_articles': get_last_articles(),
+ 'last_articles': articles,
'quote': quote,
})
From 6ca877aea00245491bf818749b80785e3c833c6d Mon Sep 17 00:00:00 2001
From: firm1
Date: Wed, 23 Jul 2014 03:20:26 +0200
Subject: [PATCH 15/20] =?UTF-8?q?lie=20le=20commit=20au=20contenu=20associ?=
=?UTF-8?q?=C3=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
zds/tutorial/models.py | 227 +++++++++++++++++++++++++++--------------
zds/tutorial/tests.py | 193 ++++++++++++++++++++++++++++++++++-
zds/tutorial/views.py | 2 +-
3 files changed, 342 insertions(+), 80 deletions(-)
diff --git a/zds/tutorial/models.py b/zds/tutorial/models.py
index 20a1d29893..8622192c0b 100644
--- a/zds/tutorial/models.py
+++ b/zds/tutorial/models.py
@@ -244,13 +244,21 @@ def dump_json(self, path=None):
json_data.write(data.encode('utf-8'))
json_data.close()
- def get_introduction(self):
- path = os.path.join(self.get_path(), self.introduction)
- intro = open(path, "r")
- intro_contenu = intro.read()
- intro.close()
-
- return intro_contenu.decode('utf-8')
+ def get_introduction(self, sha=None):
+ # find hash code
+ if sha is None:
+ sha = self.sha_draft
+ repo = Repo(self.get_path())
+
+ manifest = get_blob(repo.commit(sha).tree, "manifest.json")
+ tutorial_version = json_reader.loads(manifest)
+ if "introduction" in tutorial_version:
+ path_tuto = tutorial_version["introduction"]
+
+ if path_tuto:
+ return get_blob(repo.commit(sha).tree, path_tuto)
+ else:
+ return None
def get_introduction_online(self):
intro = open(
@@ -264,12 +272,21 @@ def get_introduction_online(self):
return intro_contenu.decode('utf-8')
- def get_conclusion(self):
- conclu = open(os.path.join(self.get_path(), self.conclusion), "r")
- conclu_contenu = conclu.read()
- conclu.close()
-
- return conclu_contenu.decode('utf-8')
+ def get_conclusion(self, sha=None):
+ # find hash code
+ if sha is None:
+ sha = self.sha_draft
+ repo = Repo(self.get_path())
+
+ manifest = get_blob(repo.commit(sha).tree, "manifest.json")
+ tutorial_version = json_reader.loads(manifest)
+ if "introduction" in tutorial_version:
+ path_tuto = tutorial_version["conclusion"]
+
+ if path_tuto:
+ return get_blob(repo.commit(sha).tree, path_tuto)
+ else:
+ return None
def get_conclusion_online(self):
conclu = open(
@@ -525,16 +542,27 @@ def get_path(self, relative=False):
else:
return os.path.join(settings.REPO_PATH, self.tutorial.get_phy_slug(), self.get_phy_slug())
- def get_introduction(self):
- intro = open(
- os.path.join(
- self.tutorial.get_path(),
- self.introduction),
- "r")
- intro_contenu = intro.read()
- intro.close()
+ def get_introduction(self, sha=None):
+
+ tutorial = self.tutorial
- return intro_contenu.decode('utf-8')
+ # find hash code
+ if sha is None:
+ sha = tutorial.sha_draft
+ repo = Repo(tutorial.get_path())
+
+ manifest = get_blob(repo.commit(sha).tree, "manifest.json")
+ tutorial_version = json_reader.loads(manifest)
+ if "parts" in tutorial_version:
+ for part in tutorial_version["parts"]:
+ if part["pk"] == self.pk:
+ path_part = part["introduction"]
+ break
+
+ if path_part:
+ return get_blob(repo.commit(sha).tree, path_part)
+ else:
+ return None
def get_introduction_online(self):
intro = open(
@@ -548,16 +576,27 @@ def get_introduction_online(self):
return intro_contenu.decode('utf-8')
- def get_conclusion(self):
- conclu = open(
- os.path.join(
- self.tutorial.get_path(),
- self.conclusion),
- "r")
- conclu_contenu = conclu.read()
- conclu.close()
+ def get_conclusion(self, sha=None):
- return conclu_contenu.decode('utf-8')
+ tutorial = self.tutorial
+
+ # find hash code
+ if sha is None:
+ sha = tutorial.sha_draft
+ repo = Repo(tutorial.get_path())
+
+ manifest = get_blob(repo.commit(sha).tree, "manifest.json")
+ tutorial_version = json_reader.loads(manifest)
+ if "parts" in tutorial_version:
+ for part in tutorial_version["parts"]:
+ if part["pk"] == self.pk:
+ path_part = part["conclusion"]
+ break
+
+ if path_part:
+ return get_blob(repo.commit(sha).tree, path_part)
+ else:
+ return None
def get_conclusion_online(self):
conclu = open(
@@ -705,25 +744,34 @@ def get_path(self, relative=False):
return chapter_path
- def get_introduction(self):
- if self.introduction:
- if self.tutorial:
- path = os.path.join(
- self.tutorial.get_path(),
- self.introduction)
- else:
- path = os.path.join(
- self.part.tutorial.get_path(),
- self.introduction)
+ def get_introduction(self, sha=None):
- if os.path.isfile(path):
- intro = open(path, "r")
- intro_contenu = intro.read()
- intro.close()
+ if self.tutorial:
+ tutorial = self.tutorial
+ else:
+ tutorial = self.part.tutorial
+ repo = Repo(tutorial.get_path())
- return intro_contenu.decode('utf-8')
- else:
- return None
+ # find hash code
+ if sha is None:
+ sha = tutorial.sha_draft
+
+ manifest = get_blob(repo.commit(sha).tree, "manifest.json")
+ tutorial_version = json_reader.loads(manifest)
+ if "parts" in tutorial_version:
+ for part in tutorial_version["parts"]:
+ if "chapters" in part:
+ for chapter in part["chapters"]:
+ if chapter["pk"] == self.pk:
+ path_chap = chapter["introduction"]
+ break
+ if "chapter" in tutorial_version:
+ chapter = tutorial_version["chapter"]
+ if chapter["pk"] == self.pk:
+ path_chap = chapter["introduction"]
+
+ if path_chap:
+ return get_blob(repo.commit(sha).tree, path_chap)
else:
return None
@@ -751,23 +799,34 @@ def get_introduction_online(self):
else:
return None
- def get_conclusion(self):
- if self.conclusion:
- if self.tutorial:
- path = os.path.join(self.tutorial.get_path(), self.conclusion)
- else:
- path = os.path.join(
- self.part.tutorial.get_path(),
- self.conclusion)
+ def get_conclusion(self, sha=None):
- if os.path.isfile(path):
- conclu = open(path, "r")
- conclu_contenu = conclu.read()
- conclu.close()
+ if self.tutorial:
+ tutorial = self.tutorial
+ else:
+ tutorial = self.part.tutorial
+ repo = Repo(tutorial.get_path())
- return conclu_contenu.decode('utf-8')
- else:
- return None
+ # find hash code
+ if sha is None:
+ sha = tutorial.sha_draft
+
+ manifest = get_blob(repo.commit(sha).tree, "manifest.json")
+ tutorial_version = json_reader.loads(manifest)
+ if "parts" in tutorial_version:
+ for part in tutorial_version["parts"]:
+ if "chapters" in part:
+ for chapter in part["chapters"]:
+ if chapter["pk"] == self.pk:
+ path_chap = chapter["conclusion"]
+ break
+ if "chapter" in tutorial_version:
+ chapter = tutorial_version["chapter"]
+ if chapter["pk"] == self.pk:
+ path_chap = chapter["conclusion"]
+
+ if path_chap:
+ return get_blob(repo.commit(sha).tree, path_chap)
else:
return None
@@ -805,7 +864,9 @@ def update_children(self):
self.introduction = os.path.join("introduction.md")
self.conclusion = os.path.join("conclusion.md")
self.save()
+
for extract in self.get_extracts():
+ extract.text = extract.get_path(relative=True)
extract.save()
class Extract(models.Model):
@@ -887,20 +948,38 @@ def get_prod_path(self):
str(ext['pk']) + "_" + slugify(ext['title'])) \
+ '.md.html'
- def get_text(self):
+ def get_text(self, sha=None):
+
if self.chapter.tutorial:
- path = os.path.join(self.chapter.tutorial.get_path(), self.text)
+ tutorial = self.chapter.tutorial
else:
- path = os.path.join(
- self.chapter.part.tutorial.get_path(),
- self.text)
+ tutorial = self.chapter.part.tutorial
+ repo = Repo(tutorial.get_path())
- if os.path.isfile(path):
- text = open(path, "r")
- text_contenu = text.read()
- text.close()
-
- return text_contenu.decode('utf-8')
+ # find hash code
+ if sha is None:
+ sha = tutorial.sha_draft
+
+ manifest = get_blob(repo.commit(sha).tree, "manifest.json")
+ tutorial_version = json_reader.loads(manifest)
+ if "parts" in tutorial_version:
+ for part in tutorial_version["parts"]:
+ if "chapters" in part:
+ for chapter in part["chapters"]:
+ if "extracts" in chapter:
+ for extract in chapter["extracts"]:
+ if extract["pk"] == self.pk:
+ path_ext = extract["text"]
+ break
+ if "chapter" in tutorial_version:
+ chapter = tutorial_version["chapter"]
+ if "extracts" in chapter:
+ for extract in chapter["extracts"]:
+ path_ext = extract["text"]
+ break
+
+ if path_ext:
+ return get_blob(repo.commit(sha).tree, path_ext)
else:
return None
diff --git a/zds/tutorial/tests.py b/zds/tutorial/tests.py
index a909dbf77c..b54efefd4c 100644
--- a/zds/tutorial/tests.py
+++ b/zds/tutorial/tests.py
@@ -2,12 +2,13 @@
import os
import shutil
-
+import HTMLParser
from django.conf import settings
from django.core import mail
from django.core.urlresolvers import reverse
from django.test import TestCase
from django.test.utils import override_settings
+from django.utils import html
from zds.member.factories import ProfileFactory, StaffProfileFactory
from zds.gallery.factories import GalleryFactory, UserGalleryFactory, ImageFactory
@@ -476,7 +477,7 @@ def test_url_for_guest(self):
def test_workflow_tuto(self):
"""Test workflow of tutorial."""
-
+
# logout before
self.client.logout()
# login with simple member
@@ -508,7 +509,7 @@ def test_workflow_tuto(self):
{
'title': u"Partie 1",
'introduction':u"Présentation",
- 'conclusion': u"Fin de la présenation",
+ 'conclusion': u"Fin de la présentation",
},
follow=False)
self.assertEqual(result.status_code, 302)
@@ -526,7 +527,7 @@ def test_workflow_tuto(self):
p1.slug]),
follow=True)
self.assertContains(response=result, text = u"Présentation")
- self.assertContains(response=result, text = u"Fin de la présenation")
+ self.assertContains(response=result, text = u"Fin de la présentation")
#add part 2
result = self.client.post(
@@ -699,6 +700,108 @@ def test_workflow_tuto(self):
follow=True)
self.assertContains(response=result, text = u"Mon premier chapitre d'une autre partie")
+ # add extract 1 of chapter 3
+ result = self.client.post(
+ reverse('zds.tutorial.views.add_extract') + '?chapitre={}'.format(c3.pk),
+ {
+ 'title': u"Extrait 1",
+ 'text':"Prune",
+ },
+ follow=False)
+ self.assertEqual(result.status_code, 302)
+ self.assertEqual(Extract.objects.filter(chapter=c3).count(), 1)
+ e1 = Extract.objects.filter(chapter=c3).last()
+
+ # add extract 2 of chapter 3
+ result = self.client.post(
+ reverse('zds.tutorial.views.add_extract') + '?chapitre={}'.format(c3.pk),
+ {
+ 'title': u"Extrait 2",
+ 'text':"Citron",
+ },
+ follow=False)
+ self.assertEqual(result.status_code, 302)
+ self.assertEqual(Extract.objects.filter(chapter=c3).count(), 2)
+ e2 = Extract.objects.filter(chapter=c3).last()
+
+ # add extract 3 of chapter 2
+ result = self.client.post(
+ reverse('zds.tutorial.views.add_extract') + '?chapitre={}'.format(c2.pk),
+ {
+ 'title': u"Extrait 3",
+ 'text':"Kiwi",
+ },
+ follow=False)
+ self.assertEqual(result.status_code, 302)
+ self.assertEqual(Extract.objects.filter(chapter=c2).count(), 1)
+ e3 = Extract.objects.filter(chapter=c2).last()
+
+ #check content edit part
+ result = self.client.get(
+ reverse('zds.tutorial.views.edit_part')+"?partie={}".format(p1.pk),
+ follow=True)
+ self.assertContains(response=result, text = u"Présentation")
+ self.assertContains(response=result, text = u"Fin de la présentation")
+
+ result = self.client.get(
+ reverse('zds.tutorial.views.edit_part')+"?partie={}".format(p2.pk),
+ follow=True)
+ self.assertContains(response=result, text = u"Analyse")
+ self.assertContains(response=result, text = "Fin de l'analyse")
+
+ result = self.client.get(
+ reverse('zds.tutorial.views.edit_part')+"?partie={}".format(p3.pk),
+ follow=True)
+ self.assertContains(response=result, text = u"Expérimentation")
+ self.assertContains(response=result, text = u"est terminé")
+
+ #check content edit chapter
+ result = self.client.get(
+ reverse('zds.tutorial.views.edit_chapter')+"?chapitre={}".format(c1.pk),
+ follow=True)
+ self.assertContains(response=result, text = u"Chapitre 1")
+ self.assertContains(response=result, text = u"Mon premier chapitre")
+ self.assertContains(response=result, text = u"Fin de mon premier chapitre")
+
+ result = self.client.get(
+ reverse('zds.tutorial.views.edit_chapter')+"?chapitre={}".format(c2.pk),
+ follow=True)
+ self.assertContains(response=result, text = u"Chapitre 2")
+ self.assertContains(response=result, text = u"Mon deuxième chapitre")
+ self.assertContains(response=result, text = u"Fin de mon deuxième chapitre")
+
+ result = self.client.get(
+ reverse('zds.tutorial.views.edit_chapter')+"?chapitre={}".format(c3.pk),
+ follow=True)
+ self.assertContains(response=result, text = u"Chapitre 2")
+ self.assertContains(response=result, text = u"Mon troisième chapitre homonyme")
+ self.assertContains(response=result, text = u"Fin de mon troisième chapitre")
+
+ result = self.client.get(
+ reverse('zds.tutorial.views.edit_chapter')+"?chapitre={}".format(c4.pk),
+ follow=True)
+ self.assertContains(response=result, text = u"Chapitre 1")
+ self.assertContains(response=result, text = u"Mon premier chapitre d'une autre partie")
+
+ #check content edit extract
+ result = self.client.get(
+ reverse('zds.tutorial.views.edit_extract')+"?extrait={}".format(e1.pk),
+ follow=True)
+ self.assertContains(response=result, text = u"Extrait 1")
+ self.assertContains(response=result, text = u"Prune")
+
+ result = self.client.get(
+ reverse('zds.tutorial.views.edit_extract')+"?extrait={}".format(e2.pk),
+ follow=True)
+ self.assertContains(response=result, text = u"Extrait 2")
+ self.assertContains(response=result, text = u"Citron")
+
+ result = self.client.get(
+ reverse('zds.tutorial.views.edit_extract')+"?extrait={}".format(e3.pk),
+ follow=True)
+ self.assertContains(response=result, text = u"Extrait 3")
+ self.assertContains(response=result, text = u"Kiwi")
+
#edit part 2
result = self.client.post(
reverse('zds.tutorial.views.edit_part') + '?partie={}'.format(p2.pk),
@@ -714,7 +817,7 @@ def test_workflow_tuto(self):
self.assertContains(response=result, text = u"Expérimentation : edition d'introduction")
self.assertContains(response=result, text = u"C'est terminé : edition de conlusion")
self.assertEqual(Part.objects.filter(tutorial=tuto).count(), 3)
-
+
#edit chapter 3
result = self.client.post(
reverse('zds.tutorial.views.edit_chapter') + '?chapitre={}'.format(c3.pk),
@@ -731,6 +834,7 @@ def test_workflow_tuto(self):
self.assertContains(response=result, text = u"Edition de conlusion")
self.assertEqual(Chapter.objects.filter(part=p2.pk).count(), 3)
p2 = Part.objects.filter(pk=p2.pk).first()
+
#edit part 2
result = self.client.post(
reverse('zds.tutorial.views.edit_part') + '?partie={}'.format(p2.pk),
@@ -762,6 +866,85 @@ def test_workflow_tuto(self):
self.assertContains(response=result, text = u"Edition d'introduction")
self.assertContains(response=result, text = u"Edition de conlusion")
self.assertEqual(Chapter.objects.filter(part=p2.pk).count(), 3)
+
+ #edit extract 2
+ result = self.client.post(
+ reverse('zds.tutorial.views.edit_extract') + '?extrait={}'.format(e2.pk),
+ {
+ 'title': u"Extrait 2 : edition de titre",
+ 'text': u"Agrume",
+ "last_hash": compute_hash([os.path.join(e2.get_path())])
+ },
+ follow=True)
+ self.assertContains(response=result, text = u"Extrait 2 : edition de titre")
+ self.assertContains(response=result, text = u"Agrume")
+
+ #check content edit part
+ result = self.client.get(
+ reverse('zds.tutorial.views.edit_part')+"?partie={}".format(p1.pk),
+ follow=True)
+ self.assertContains(response=result, text = u"Présentation")
+ self.assertContains(response=result, text = u"Fin de la présentation")
+
+ result = self.client.get(
+ reverse('zds.tutorial.views.edit_part')+"?partie={}".format(p2.pk),
+ follow=True)
+ self.assertContains(response=result, text = u"Partie 2 : seconde edition de titre")
+ self.assertContains(response=result, text = "Expérimentation : seconde edition d'introduction")
+ self.assertContains(response=result, text = "C'est terminé : seconde edition de conlusion")
+
+ result = self.client.get(
+ reverse('zds.tutorial.views.edit_part')+"?partie={}".format(p3.pk),
+ follow=True)
+ self.assertContains(response=result, text = u"Expérimentation")
+ self.assertContains(response=result, text = u"est terminé")
+
+ #check content edit chapter
+ result = self.client.get(
+ reverse('zds.tutorial.views.edit_chapter')+"?chapitre={}".format(c1.pk),
+ follow=True)
+ self.assertContains(response=result, text = u"Chapitre 1")
+ self.assertContains(response=result, text = u"Mon premier chapitre")
+ self.assertContains(response=result, text = u"Fin de mon premier chapitre")
+
+ result = self.client.get(
+ reverse('zds.tutorial.views.edit_chapter')+"?chapitre={}".format(c2.pk),
+ follow=True)
+ self.assertContains(response=result, text = u"Chapitre 2 : edition de titre")
+ self.assertContains(response=result, text = u"Edition d'introduction")
+ self.assertContains(response=result, text = u"Edition de conlusion")
+
+ result = self.client.get(
+ reverse('zds.tutorial.views.edit_chapter')+"?chapitre={}".format(c3.pk),
+ follow=True)
+ self.assertContains(response=result, text = u"Chapitre 3 : edition de titre")
+ self.assertContains(response=result, text = u"Edition d'introduction")
+ self.assertContains(response=result, text = u"Edition de conlusion")
+
+ result = self.client.get(
+ reverse('zds.tutorial.views.edit_chapter')+"?chapitre={}".format(c4.pk),
+ follow=True)
+ self.assertContains(response=result, text = u"Chapitre 1")
+ self.assertContains(response=result, text = u"Mon premier chapitre d'une autre partie")
+
+ #check content edit extract
+ result = self.client.get(
+ reverse('zds.tutorial.views.edit_extract')+"?extrait={}".format(e1.pk),
+ follow=True)
+ self.assertContains(response=result, text = u"Extrait 1")
+ self.assertContains(response=result, text = u"Prune")
+
+ result = self.client.get(
+ reverse('zds.tutorial.views.edit_extract')+"?extrait={}".format(e2.pk),
+ follow=True)
+ self.assertContains(response=result, text = u"Extrait 2 : edition de titre")
+ self.assertContains(response=result, text = u"Agrume")
+
+ result = self.client.get(
+ reverse('zds.tutorial.views.edit_extract')+"?extrait={}".format(e3.pk),
+ follow=True)
+ self.assertContains(response=result, text = u"Extrait 3")
+ self.assertContains(response=result, text = u"Kiwi")
#move chapter 1 against 2
result = self.client.post(
diff --git a/zds/tutorial/views.py b/zds/tutorial/views.py
index 3a5aaf4e11..8070d47e6b 100644
--- a/zds/tutorial/views.py
+++ b/zds/tutorial/views.py
@@ -1926,6 +1926,7 @@ def edit_extract(request):
if not request.user.has_perm("tutorial.change_tutorial"):
raise PermissionDenied
+
if request.method == "POST":
data = request.POST
if content_has_changed([extract.get_path()], data["last_hash"]):
@@ -2895,7 +2896,6 @@ def MEP(tutorial, sha):
get_url_images(md_file_contenu, tutorial.get_prod_path())
# convert to out format
-
out_file = open(os.path.join(tutorial.get_prod_path(), fichier), "w")
if md_file_contenu is not None:
out_file.write(markdown_to_out(md_file_contenu.encode("utf-8")))
From 1ea1876dffd651fb916096603e04cbaa8334ae0d Mon Sep 17 00:00:00 2001
From: firm1
Date: Wed, 23 Jul 2014 11:26:58 +0200
Subject: [PATCH 16/20] corrige le lien de recherche type beta
---
zds/tutorial/models.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/zds/tutorial/models.py b/zds/tutorial/models.py
index 8622192c0b..31171594b8 100644
--- a/zds/tutorial/models.py
+++ b/zds/tutorial/models.py
@@ -185,6 +185,7 @@ def get_prod_path(self):
def load_dic(self, mandata):
mandata['get_absolute_url_online'] = reverse('zds.tutorial.views.view_tutorial_online',
args=[self.pk, slugify(mandata["title"])])
+ mandata['get_absolute_url_beta'] = self.get_absolute_url_beta()
mandata['get_absolute_url'] = self.get_absolute_url()
mandata['get_introduction_online'] = self.get_introduction_online()
mandata['get_conclusion_online'] = self.get_conclusion_online()
From a1ec797c9860760a1ae8df916175d5f4a901fdac Mon Sep 17 00:00:00 2001
From: firm1
Date: Wed, 23 Jul 2014 11:39:35 +0200
Subject: [PATCH 17/20] =?UTF-8?q?affichage=20versionn=C3=A9=20sur=20la=20p?=
=?UTF-8?q?age=20de=20profil?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
zds/member/views.py | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/zds/member/views.py b/zds/member/views.py
index e6f664dbfb..3dc687377f 100644
--- a/zds/member/views.py
+++ b/zds/member/views.py
@@ -117,18 +117,30 @@ def details(request, user_name):
fchart = os.path.join(img_path, "mod-{}.svg".format(str(usr.pk)))
dot_chart.render_to_file(fchart)
my_articles = Article.objects.filter(sha_public__isnull=False).order_by(
- "-pubdate").filter(authors__in=[usr]).all()
+ "-pubdate").filter(authors__in=[usr]).all()[:5]
my_tutorials = \
Tutorial.objects.filter(sha_public__isnull=False) \
.filter(authors__in=[usr]) \
.order_by("-pubdate"
- ).all()
+ ).all()[:5]
+
+ my_tuto_versions = []
+ for my_tutorial in my_tutorials:
+ mandata = my_tutorial.load_json_for_public()
+ mandata = my_tutorial.load_dic(mandata)
+ my_tuto_versions.append(mandata)
+ my_article_versions = []
+ for my_article in my_articles:
+ article_version = my_article.load_json_for_public()
+ article_version = my_article.load_dic(article_version)
+ my_tuto_versions.append(article_version)
+
my_topics = \
Topic.objects\
.filter(author=usr)\
.exclude(Q(forum__group__isnull=False) & ~Q(forum__group__in=request.user.groups.all()))\
.prefetch_related("author")\
- .order_by("-pubdate").all()
+ .order_by("-pubdate").all()[:5]
form = OldTutoForm(profile)
oldtutos = []
@@ -142,9 +154,9 @@ def details(request, user_name):
"usr": usr,
"profile": profile,
"bans": bans,
- "articles": my_articles[:5],
- "tutorials": my_tutorials[:5],
- "topics": my_topics[:5],
+ "articles": my_article_versions,
+ "tutorials": my_tuto_versions,
+ "topics": my_topics,
"form": form,
"old_tutos": oldtutos,
})
From 9951dbe19011f94ab075e1ea782c94624b9bbf96 Mon Sep 17 00:00:00 2001
From: firm1
Date: Wed, 23 Jul 2014 11:57:23 +0200
Subject: [PATCH 18/20] corrections syntaxiques
---
zds/member/views.py | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/zds/member/views.py b/zds/member/views.py
index 3dc687377f..1de5bb9088 100644
--- a/zds/member/views.py
+++ b/zds/member/views.py
@@ -125,15 +125,15 @@ def details(request, user_name):
).all()[:5]
my_tuto_versions = []
- for my_tutorial in my_tutorials:
- mandata = my_tutorial.load_json_for_public()
- mandata = my_tutorial.load_dic(mandata)
- my_tuto_versions.append(mandata)
+ for my_tutorial in my_tutorials:
+ mandata = my_tutorial.load_json_for_public()
+ mandata = my_tutorial.load_dic(mandata)
+ my_tuto_versions.append(mandata)
my_article_versions = []
- for my_article in my_articles:
- article_version = my_article.load_json_for_public()
- article_version = my_article.load_dic(article_version)
- my_tuto_versions.append(article_version)
+ for my_article in my_articles:
+ article_version = my_article.load_json_for_public()
+ article_version = my_article.load_dic(article_version)
+ my_tuto_versions.append(article_version)
my_topics = \
Topic.objects\
From 376ea85403c7f87e532c62860ebfff9b8f36fcf8 Mon Sep 17 00:00:00 2001
From: firm1
Date: Wed, 23 Jul 2014 13:17:11 +0200
Subject: [PATCH 19/20] corrige les articles manquants dans le profil
---
zds/member/views.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/zds/member/views.py b/zds/member/views.py
index 1de5bb9088..bfc4e5da18 100644
--- a/zds/member/views.py
+++ b/zds/member/views.py
@@ -133,7 +133,7 @@ def details(request, user_name):
for my_article in my_articles:
article_version = my_article.load_json_for_public()
article_version = my_article.load_dic(article_version)
- my_tuto_versions.append(article_version)
+ my_article_versions.append(article_version)
my_topics = \
Topic.objects\
From e14b6dc7a8d68210c8cb6057f7acf97a90bc395a Mon Sep 17 00:00:00 2001
From: firm1
Date: Wed, 23 Jul 2014 13:17:16 +0200
Subject: [PATCH 20/20] correction de l'affichage public dans le profil
---
templates/tutorial/member/online.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/templates/tutorial/member/online.html b/templates/tutorial/member/online.html
index 171035d892..9d31b3fd70 100644
--- a/templates/tutorial/member/online.html
+++ b/templates/tutorial/member/online.html
@@ -31,7 +31,7 @@ Tutoriels publiés par {{ usr.username }}
{% for tutorial in tutorials %}
- {% include 'tutorial/includes/tutorial_item.part.html' with beta=True %}
+ {% include 'tutorial/includes/tutorial_item.part.html' %}
{% endfor %}
{% else %}