From a536b3a2f865972bf85289990733e28bc565f5d8 Mon Sep 17 00:00:00 2001 From: Florianboux Date: Fri, 7 Nov 2014 16:14:25 +0100 Subject: [PATCH 001/152] =?UTF-8?q?Rajout=20de=20=C2=AB=20width:=20auto=20?= =?UTF-8?q?=C2=BB=20pour=20corriger=20la=20bande=20blanche=20sur=20la=20ho?= =?UTF-8?q?me?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/scss/_wide.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/scss/_wide.scss b/assets/scss/_wide.scss index e282234d24..3dcb6e5876 100644 --- a/assets/scss/_wide.scss +++ b/assets/scss/_wide.scss @@ -346,6 +346,7 @@ blockquote { margin: 0 0 0 300px; + width: auto; } } From a95c82bae526880e0bae5e69b1b192c9fe37fa96 Mon Sep 17 00:00:00 2001 From: firm1 Date: Wed, 12 Nov 2014 09:57:57 +0100 Subject: [PATCH 002/152] nouvelles fixtures automatiques --- zds/forum/factories.py | 1 + zds/member/factories.py | 15 +- zds/utils/management/__init__.py | 0 zds/utils/management/commands/__init__.py | 0 .../management/commands/load_fixtures.py | 191 ++++++++++++++++++ 5 files changed, 200 insertions(+), 7 deletions(-) create mode 100644 zds/utils/management/__init__.py create mode 100644 zds/utils/management/commands/__init__.py create mode 100644 zds/utils/management/commands/load_fixtures.py diff --git a/zds/forum/factories.py b/zds/forum/factories.py index 7ec2f295af..4b0a1ddb63 100644 --- a/zds/forum/factories.py +++ b/zds/forum/factories.py @@ -47,6 +47,7 @@ def _prepare(cls, create, **kwargs): post = super(PostFactory, cls)._prepare(create, **kwargs) topic = kwargs.pop('topic', None) if topic: + post.save() topic.last_message = post topic.save() return post diff --git a/zds/member/factories.py b/zds/member/factories.py index 609968a267..5a0b80fe60 100644 --- a/zds/member/factories.py +++ b/zds/member/factories.py @@ -1,6 +1,6 @@ # coding: utf-8 -from django.contrib.auth.models import User, Permission +from django.contrib.auth.models import User, Permission, Group import factory from zds.member.models import Profile @@ -50,13 +50,14 @@ def _prepare(cls, create, **kwargs): user.set_password(password) if create: user.save() - perms = Permission.objects.filter(codename__startswith='change_').all() + group_staff = Group.objects.filter(name="staff").first() + if group_staff is None: + group_staff = Group(name="staff") + group_staff.save() - user.user_permissions = list(perms) - user.user_permissions.add( - Permission.objects.get( - codename='moderation')) - user.user_permissions.add(Permission.objects.get(codename='show_ip')) + perms = Permission.objects.filter(codename__startswith='change_').all() + group_staff.permissions = perms + user.groups.add(group_staff) user.save() return user diff --git a/zds/utils/management/__init__.py b/zds/utils/management/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/zds/utils/management/commands/__init__.py b/zds/utils/management/commands/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/zds/utils/management/commands/load_fixtures.py b/zds/utils/management/commands/load_fixtures.py new file mode 100644 index 0000000000..6c05251a26 --- /dev/null +++ b/zds/utils/management/commands/load_fixtures.py @@ -0,0 +1,191 @@ +# coding: utf-8 + +import random +from django.core.management.base import BaseCommand +from random import randint +from faker import Factory +from zds.utils.templatetags.emarkdown import emarkdown + +from zds.forum.factories import CategoryFactory, ForumFactory, TopicFactory, PostFactory +from zds.tutorial.factories import BigTutorialFactory, PartFactory, ChapterFactory +from zds.gallery.factories import GalleryFactory, UserGalleryFactory, ImageFactory +from zds.member.factories import UserFactory, StaffFactory, StaffProfileFactory, ProfileFactory +from zds.utils.models import Tag +from zds.utils import slugify +from django.db import transaction + + +@transaction.atomic +class Command(BaseCommand): + args = '[low|medium|high]' + help = 'Load fixtures for ZdS' + + def handle(self, *args, **options): + if len(args) > 0: + if args[0] == "low": + size = 1 + elif args[0] == "medium": + size = 2 + elif args[0] == "high": + size = 3 + else: + size = 2 + else: + size = 2 + fake = Factory.create(locale="fr_FR") + + """ + Load members + """ + profiles = [] + nb_users = size * 10 + self.stdout.write(u"Nombres de membres à créer : {}".format(nb_users)) + for i in range(0, nb_users): + profile = ProfileFactory() + profile.user.set_password(profile.user.username) + profile.user.first_name = fake.first_name() + profile.user.last_name = fake.last_name() + profile.user.email = fake.free_email() + profile.user.save() + profile.site = fake.url() + profile.biography = fake.text(max_nb_chars=200) + profile.last_ip_address = fake.ipv4() + profile.save() + profiles.append(profile) + + """ + Load staff + """ + profile_staffs = [] + nb_staffs = size * 10 + self.stdout.write(u"Nombres de staffs à créer : {}".format(nb_staffs)) + for i in range(0, nb_staffs): + profile = StaffProfileFactory() + profile.user.first_name = fake.first_name() + profile.user.last_name = fake.last_name() + profile.user.email = fake.free_email() + profile.user.save() + profile.site = fake.url() + profile.biography = fake.paragraph() + profile.sign = fake.text(max_nb_chars=80) + profile.last_ip_address = fake.ipv6() + profile.save() + profile_staffs.append(profile) + + """ + Load galleries + """ + galleries = [] + nb_galleries = size * 3 + nb_images = size * 5 + self.stdout.write(u"Nombres de galéries à créer par utilisateur: {}".format(nb_galleries)) + self.stdout.write(u"Nombres d'images à créer par gallerie: {}".format(nb_images)) + for i in range(0, nb_users): + for j in range(0, nb_galleries): + gal = GalleryFactory(title=fake.text(max_nb_chars=80), subtitle=fake.text(max_nb_chars=200)) + ug = UserGalleryFactory(user=profiles[i].user, gallery=gal) + for k in range(0, nb_images): + img = ImageFactory(gallery=gal) + + """ + Load categories + """ + categories = [] + nb_categories = size * 2 + self.stdout.write(u"Nombres de catégories de forum à créer : {}".format(nb_categories)) + for i in range(0, nb_users): + cat = CategoryFactory(position=i + 1) + cat.title = fake.text(max_nb_chars=20) + cat.save() + categories.append(cat) + + """ + Load forums + """ + forums = [] + nb_forums = size * 8 + self.stdout.write(u"Nombres de Forums à créer : {}".format(nb_forums)) + for i in range(0, nb_forums): + forum = ForumFactory(category=categories[i % nb_categories], + position_in_category=(i / nb_categories) + 1) + forum.title = fake.text(max_nb_chars=20) + forum.subtitle = fake.sentence(nb_words=15, variable_nb_words=True) + forum.save() + forums.append(forum) + + """ + Load tags + """ + tags = [] + nb_tags = size * 50 + self.stdout.write(u"Nombres de Tags de forum à créer : {}".format(nb_tags)) + for i in range(0, nb_tags): + title = fake.word() + t = Tag(title=title, slug=slugify(title)) + + """ + Load topics + """ + topics = [] + nb_topics = size * 20 + self.stdout.write(u"Nombres de Topics à créer : {}".format(nb_topics)) + for i in range(0, nb_topics): + # load one staff for ten member + if i % 10 == 0: + topic = TopicFactory(forum=forums[i % nb_forums], author=profiles[i % nb_users].user) + topic.is_sticky = True + else: + topic = TopicFactory(forum=forums[i % nb_forums], author=profile_staffs[i % nb_staffs].user) + nb_rand_tags = random.randint(0, 5) + for k in range(0, nb_tags): + topic.tags.add(random.randint(1, nb_tags)) + topic.title = fake.text(max_nb_chars=80) + topic.subtitle = fake.text(max_nb_chars=200) + topic.save() + topics.append(topic) + + """ + Load posts + """ + posts = [] + nb_avg_posts_in_topic = size * 10 + self.stdout.write(u"Nombres de messages à poster en moyenne dans un sujet : {}".format(nb_avg_posts_in_topic)) + for i in range(0, nb_topics): + nb = randint(0, nb_avg_posts_in_topic * 2) + for j in range(0, nb): + if j == 0: + post = PostFactory(topic=topics[i], author=topics[i].author, position=1) + else: + post = PostFactory(topic=topics[i], author=profiles[j % nb_users].user, position=j+1) + post.text = fake.paragraph(nb_sentences=5, variable_nb_sentences=True) + post.text_html = emarkdown(post.text) + post.save() + posts.append(post) + + """ + Load tutorials + """ + tutorials = [] + parts = [] + chapters = [] + + nb_tutos = size * 5 + nb_avg_parts_in_tuto = size * 1 + nb_avg_chapters_in_tuto = size * 1 + self.stdout.write(u"Nombres de big tutoriels à créer : {}".format(nb_tutos)) + for i in range(0, nb_tutos): + tuto = BigTutorialFactory(title=fake.text(max_nb_chars=200), + description=fake.sentence(nb_words=15, variable_nb_words=True)) + tuto.authors.add(profiles[i % nb_users].user) + tutorials.append(tuto) + nb_part = randint(0, nb_avg_parts_in_tuto * 2) + for j in range(0, nb_part): + parts.append(PartFactory(tutorial=tutorials[i], + position_in_tutorial=j, + title=fake.text(max_nb_chars=200))) + nb_chap = randint(0, nb_avg_chapters_in_tuto * 2) + for k in range(0, nb_chap): + chapters.append(ChapterFactory(part=parts[j], + position_in_part=k, + position_in_tutorial=j * k, + title=fake.text(max_nb_chars=200))) \ No newline at end of file From 5d5aa3055770638d5a7039f091f574b9a65289cf Mon Sep 17 00:00:00 2001 From: firm1 Date: Mon, 17 Nov 2014 13:55:54 +0100 Subject: [PATCH 003/152] factorisation --- .../management/commands/load_fixtures.py | 374 ++++++++++-------- 1 file changed, 209 insertions(+), 165 deletions(-) diff --git a/zds/utils/management/commands/load_fixtures.py b/zds/utils/management/commands/load_fixtures.py index 6c05251a26..1fcfa301fc 100644 --- a/zds/utils/management/commands/load_fixtures.py +++ b/zds/utils/management/commands/load_fixtures.py @@ -9,183 +9,227 @@ from zds.forum.factories import CategoryFactory, ForumFactory, TopicFactory, PostFactory from zds.tutorial.factories import BigTutorialFactory, PartFactory, ChapterFactory from zds.gallery.factories import GalleryFactory, UserGalleryFactory, ImageFactory -from zds.member.factories import UserFactory, StaffFactory, StaffProfileFactory, ProfileFactory +from zds.member.factories import StaffProfileFactory, ProfileFactory +from django.contrib.auth.models import User +from zds.member.models import Profile +from zds.forum.models import Category, Forum, Topic, Post from zds.utils.models import Tag from zds.utils import slugify from django.db import transaction +def load_member(cli, size, fake): + """ + Load members + """ + nb_users = size * 10 + cli.stdout.write(u"Nombres de membres à créer : {}".format(nb_users)) + for i in range(0, nb_users): + profile = ProfileFactory() + profile.user.set_password(profile.user.username) + profile.user.first_name = fake.first_name() + profile.user.last_name = fake.last_name() + profile.user.email = fake.free_email() + profile.user.save() + profile.site = fake.url() + profile.biography = fake.text(max_nb_chars=200) + profile.last_ip_address = fake.ipv4() + profile.save() + + +def load_staff(cli, size, fake): + """ + Load staff + """ + nb_staffs = size * 10 + cli.stdout.write(u"Nombres de staffs à créer : {}".format(nb_staffs)) + for i in range(0, nb_staffs): + profile = StaffProfileFactory() + profile.user.first_name = fake.first_name() + profile.user.last_name = fake.last_name() + profile.user.email = fake.free_email() + profile.user.save() + profile.site = fake.url() + profile.biography = fake.paragraph() + profile.sign = fake.text(max_nb_chars=80) + profile.last_ip_address = fake.ipv6() + profile.save() + + +def load_gallery(cli, size, fake): + """ + Load galleries + """ + nb_galleries = size * 3 + nb_images = size * 5 + cli.stdout.write(u"Nombres de galéries à créer par utilisateur: {}".format(nb_galleries)) + cli.stdout.write(u"Nombres d'images à créer par gallerie: {}".format(nb_images)) + nb_users = User.objects.count() + profiles = list(Profile.objects.all()) + for i in range(0, nb_users): + for j in range(0, nb_galleries): + gal = GalleryFactory(title=fake.text(max_nb_chars=80), subtitle=fake.text(max_nb_chars=200)) + ug = UserGalleryFactory(user=profiles[i].user, gallery=gal) + for k in range(0, nb_images): + img = ImageFactory(gallery=gal) + + +def load_categories_forum(cli, size, fake): + """ + Load categories + """ + nb_categories = size * 2 + cli.stdout.write(u"Nombres de catégories de forum à créer : {}".format(nb_categories)) + nb_users = User.objects.count() + for i in range(0, nb_users): + cat = CategoryFactory(position=i + 1) + cat.title = fake.text(max_nb_chars=20) + cat.save() + + +def load_forums(cli, size, fake): + """ + Load forums + """ + nb_forums = size * 8 + cli.stdout.write(u"Nombres de Forums à créer : {}".format(nb_forums)) + nb_categories = Category.objects.count() + categories = list(Category.objects.all()) + for i in range(0, nb_forums): + forum = ForumFactory(category=categories[i % nb_categories], + position_in_category=(i / nb_categories) + 1) + forum.title = fake.text(max_nb_chars=20) + forum.subtitle = fake.sentence(nb_words=15, variable_nb_words=True) + forum.save() + + +def load_tags(cli, size, fake): + """ + Load tags + """ + nb_tags = size * 50 + cli.stdout.write(u"Nombres de Tags de forum à créer : {}".format(nb_tags)) + for i in range(0, nb_tags): + title = fake.word() + t = Tag(title=title, slug=slugify(title)) + + +def load_topics(cli, size, fake): + """ + Load topics + """ + nb_topics = size * 20 + cli.stdout.write(u"Nombres de Topics à créer : {}".format(nb_topics)) + nb_forums = Forum.objects.count() + forums = list(Forum.objects.all()) + nb_users = User.objects.count() + profiles = list(Profile.objects.all()) + nb_tags = Tag.objects.count() + for i in range(0, nb_topics): + topic = TopicFactory(forum=forums[i % nb_forums], author=profiles[i % nb_users].user) + topic.is_sticky = True + nb_rand_tags = random.randint(0, 5) + for k in range(0, nb_rand_tags): + topic.tags.add(random.randint(1, nb_tags)) + topic.title = fake.text(max_nb_chars=80) + topic.subtitle = fake.text(max_nb_chars=200) + topic.save() + + +def load_posts(cli, size, fake): + """ + Load posts + """ + nb_avg_posts_in_topic = size * 10 + cli.stdout.write(u"Nombres de messages à poster en moyenne dans un sujet : {}".format(nb_avg_posts_in_topic)) + nb_topics = Topic.objects.count() + topics = list(Topic.objects.all()) + nb_users = User.objects.count() + profiles = list(Profile.objects.all()) + for i in range(0, nb_topics): + nb = randint(0, nb_avg_posts_in_topic * 2) + for j in range(0, nb): + if j == 0: + post = PostFactory(topic=topics[i], author=topics[i].author, position=1) + else: + post = PostFactory(topic=topics[i], author=profiles[j % nb_users].user, position=j+1) + post.text = fake.paragraph(nb_sentences=5, variable_nb_sentences=True) + post.text_html = emarkdown(post.text) + post.save() + + +def load_tutorials(cli, size, fake): + """ + Load tutorials + """ + tutorials = [] + parts = [] + chapters = [] + + nb_tutos = size * 5 + nb_avg_parts_in_tuto = size * 1 + nb_avg_chapters_in_tuto = size * 1 + cli.stdout.write(u"Nombres de big tutoriels à créer : {}".format(nb_tutos)) + nb_users = User.objects.count() + profiles = list(Profile.objects.all()) + for i in range(0, nb_tutos): + tuto = BigTutorialFactory(title=fake.text(max_nb_chars=200), + description=fake.sentence(nb_words=15, variable_nb_words=True)) + tuto.authors.add(profiles[i % nb_users].user) + tutorials.append(tuto) + nb_part = randint(0, nb_avg_parts_in_tuto * 2) + for j in range(0, nb_part): + parts.append(PartFactory(tutorial=tutorials[i], + position_in_tutorial=j, + title=fake.text(max_nb_chars=200))) + nb_chap = randint(0, nb_avg_chapters_in_tuto * 2) + for k in range(0, nb_chap): + chapters.append(ChapterFactory(part=parts[j], + position_in_part=k, + position_in_tutorial=j * k, + title=fake.text(max_nb_chars=200))) + @transaction.atomic class Command(BaseCommand): args = '[low|medium|high]' help = 'Load fixtures for ZdS' def handle(self, *args, **options): - if len(args) > 0: - if args[0] == "low": - size = 1 - elif args[0] == "medium": - size = 2 - elif args[0] == "high": - size = 3 + default_size = "low" + default_module = ["member", "article", "gallery", "tutorial"] + for arg in args: + ps = arg.split("=") + if len(ps) < 2: + continue else: - size = 2 - else: + if ps[0] in ["size", "sizes", "taille", "level"]: + default_size = ps[1].split(",")[0] + elif ps[0] in ["type", "types"]: + default_module = ps[1].split(",") + + if default_size == "low": + size = 1 + elif default_size == "medium": size = 2 + elif default_size == "high": + size = 3 + else: + size = 1 fake = Factory.create(locale="fr_FR") - """ - Load members - """ - profiles = [] - nb_users = size * 10 - self.stdout.write(u"Nombres de membres à créer : {}".format(nb_users)) - for i in range(0, nb_users): - profile = ProfileFactory() - profile.user.set_password(profile.user.username) - profile.user.first_name = fake.first_name() - profile.user.last_name = fake.last_name() - profile.user.email = fake.free_email() - profile.user.save() - profile.site = fake.url() - profile.biography = fake.text(max_nb_chars=200) - profile.last_ip_address = fake.ipv4() - profile.save() - profiles.append(profile) - - """ - Load staff - """ - profile_staffs = [] - nb_staffs = size * 10 - self.stdout.write(u"Nombres de staffs à créer : {}".format(nb_staffs)) - for i in range(0, nb_staffs): - profile = StaffProfileFactory() - profile.user.first_name = fake.first_name() - profile.user.last_name = fake.last_name() - profile.user.email = fake.free_email() - profile.user.save() - profile.site = fake.url() - profile.biography = fake.paragraph() - profile.sign = fake.text(max_nb_chars=80) - profile.last_ip_address = fake.ipv6() - profile.save() - profile_staffs.append(profile) - - """ - Load galleries - """ - galleries = [] - nb_galleries = size * 3 - nb_images = size * 5 - self.stdout.write(u"Nombres de galéries à créer par utilisateur: {}".format(nb_galleries)) - self.stdout.write(u"Nombres d'images à créer par gallerie: {}".format(nb_images)) - for i in range(0, nb_users): - for j in range(0, nb_galleries): - gal = GalleryFactory(title=fake.text(max_nb_chars=80), subtitle=fake.text(max_nb_chars=200)) - ug = UserGalleryFactory(user=profiles[i].user, gallery=gal) - for k in range(0, nb_images): - img = ImageFactory(gallery=gal) - - """ - Load categories - """ - categories = [] - nb_categories = size * 2 - self.stdout.write(u"Nombres de catégories de forum à créer : {}".format(nb_categories)) - for i in range(0, nb_users): - cat = CategoryFactory(position=i + 1) - cat.title = fake.text(max_nb_chars=20) - cat.save() - categories.append(cat) - - """ - Load forums - """ - forums = [] - nb_forums = size * 8 - self.stdout.write(u"Nombres de Forums à créer : {}".format(nb_forums)) - for i in range(0, nb_forums): - forum = ForumFactory(category=categories[i % nb_categories], - position_in_category=(i / nb_categories) + 1) - forum.title = fake.text(max_nb_chars=20) - forum.subtitle = fake.sentence(nb_words=15, variable_nb_words=True) - forum.save() - forums.append(forum) - - """ - Load tags - """ - tags = [] - nb_tags = size * 50 - self.stdout.write(u"Nombres de Tags de forum à créer : {}".format(nb_tags)) - for i in range(0, nb_tags): - title = fake.word() - t = Tag(title=title, slug=slugify(title)) - - """ - Load topics - """ - topics = [] - nb_topics = size * 20 - self.stdout.write(u"Nombres de Topics à créer : {}".format(nb_topics)) - for i in range(0, nb_topics): - # load one staff for ten member - if i % 10 == 0: - topic = TopicFactory(forum=forums[i % nb_forums], author=profiles[i % nb_users].user) - topic.is_sticky = True - else: - topic = TopicFactory(forum=forums[i % nb_forums], author=profile_staffs[i % nb_staffs].user) - nb_rand_tags = random.randint(0, 5) - for k in range(0, nb_tags): - topic.tags.add(random.randint(1, nb_tags)) - topic.title = fake.text(max_nb_chars=80) - topic.subtitle = fake.text(max_nb_chars=200) - topic.save() - topics.append(topic) - - """ - Load posts - """ - posts = [] - nb_avg_posts_in_topic = size * 10 - self.stdout.write(u"Nombres de messages à poster en moyenne dans un sujet : {}".format(nb_avg_posts_in_topic)) - for i in range(0, nb_topics): - nb = randint(0, nb_avg_posts_in_topic * 2) - for j in range(0, nb): - if j == 0: - post = PostFactory(topic=topics[i], author=topics[i].author, position=1) - else: - post = PostFactory(topic=topics[i], author=profiles[j % nb_users].user, position=j+1) - post.text = fake.paragraph(nb_sentences=5, variable_nb_sentences=True) - post.text_html = emarkdown(post.text) - post.save() - posts.append(post) - - """ - Load tutorials - """ - tutorials = [] - parts = [] - chapters = [] - - nb_tutos = size * 5 - nb_avg_parts_in_tuto = size * 1 - nb_avg_chapters_in_tuto = size * 1 - self.stdout.write(u"Nombres de big tutoriels à créer : {}".format(nb_tutos)) - for i in range(0, nb_tutos): - tuto = BigTutorialFactory(title=fake.text(max_nb_chars=200), - description=fake.sentence(nb_words=15, variable_nb_words=True)) - tuto.authors.add(profiles[i % nb_users].user) - tutorials.append(tuto) - nb_part = randint(0, nb_avg_parts_in_tuto * 2) - for j in range(0, nb_part): - parts.append(PartFactory(tutorial=tutorials[i], - position_in_tutorial=j, - title=fake.text(max_nb_chars=200))) - nb_chap = randint(0, nb_avg_chapters_in_tuto * 2) - for k in range(0, nb_chap): - chapters.append(ChapterFactory(part=parts[j], - position_in_part=k, - position_in_tutorial=j * k, - title=fake.text(max_nb_chars=200))) \ No newline at end of file + if "member" in default_module: + load_member(self, size, fake) + if "staff" in default_module: + load_staff(self, size, fake) + if "gallery" in default_module: + load_gallery(self, size, fake) + if "category_forum" in default_module: + load_categories_forum(self, size, fake) + if "forum" in default_module: + load_forums(self, size, fake) + if "tag" in default_module: + load_tags(self, size, fake) + if "topic" in default_module: + load_topics(self, size, fake) + if "post" in default_module: + load_posts(self, size, fake) From 72e897797fe0d3a62b1ba416e203e2dd6f6f0a2f Mon Sep 17 00:00:00 2001 From: firm1 Date: Mon, 17 Nov 2014 18:26:13 +0100 Subject: [PATCH 004/152] Update load_fixtures.py --- .../management/commands/load_fixtures.py | 61 ++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/zds/utils/management/commands/load_fixtures.py b/zds/utils/management/commands/load_fixtures.py index 1fcfa301fc..78a1abc6c4 100644 --- a/zds/utils/management/commands/load_fixtures.py +++ b/zds/utils/management/commands/load_fixtures.py @@ -158,6 +158,44 @@ def load_posts(cli, size, fake): post.save() +def load_comment_article(cli, size, fake): + """ + Load article's comments + """ + nb_avg_posts = size * 20 + cli.stdout.write(u"Nombres de messages à poster en moyenne dans un article : {}".format(nb_avg_posts)) + nb_articles = Article.objects.filter(sha_public__isnull=False).count() + articles = list(Article.objects.filter(sha_public__isnull=False).all()) + nb_users = User.objects.count() + profiles = list(Profile.objects.all()) + for i in range(0, nb_articles): + nb = randint(0, nb_avg_posts * 2) + for j in range(0, nb): + post = ReactionFactory(article=articles[i], author=profiles[j % nb_users].user, position=j+1) + post.text = fake.paragraph(nb_sentences=5, variable_nb_sentences=True) + post.text_html = emarkdown(post.text) + post.save() + + +def load_comment_tutorial(cli, size, fake): + """ + Load tutorial's comments + """ + nb_avg_posts = size * 20 + cli.stdout.write(u"Nombres de messages à poster en moyenne dans un tutoriel : {}".format(nb_avg_posts)) + nb_tutorials = Tutorial.objects.filter(sha_public__isnull=False).count() + tutorials = list(Tutorial.objects.filter(sha_public__isnull=False).all()) + nb_users = User.objects.count() + profiles = list(Profile.objects.all()) + for i in range(0, nb_tutorials): + nb = randint(0, nb_avg_posts * 2) + for j in range(0, nb): + post = NoteFactory(tutorial=tutorials[i], author=profiles[j % nb_tutorials].user, position=j+1) + post.text = fake.paragraph(nb_sentences=5, variable_nb_sentences=True) + post.text_html = emarkdown(post.text) + post.save() + + def load_tutorials(cli, size, fake): """ Load tutorials @@ -166,7 +204,7 @@ def load_tutorials(cli, size, fake): parts = [] chapters = [] - nb_tutos = size * 5 + nb_tutos = size * 10 nb_avg_parts_in_tuto = size * 1 nb_avg_chapters_in_tuto = size * 1 cli.stdout.write(u"Nombres de big tutoriels à créer : {}".format(nb_tutos)) @@ -189,6 +227,17 @@ def load_tutorials(cli, size, fake): position_in_tutorial=j * k, title=fake.text(max_nb_chars=200))) + percent_tutos_validation_without_validator = 0.1 + percent_tutos_validation_with_validator = 0.1 + percent_tutos_in_beta = 0.1 + percent_tutos_public = 0.3 + tutorials_count = len(tutorials) + cli.stdout.write(u"Nombres de big tutoriels sans validateurs : {}".format(str(int(tutorials_count * percent_tutos_validation_without_validator)))) + cli.stdout.write(u"Nombres de big tutoriels réservé en validations : {}".format(str(int(tutorials_count * percent_tutos_validation_with_validator)))) + cli.stdout.write(u"Nombres de big tutoriels en beta : {}".format(str(int(tutorials_count * percent_tutos_in_beta)))) + cli.stdout.write(u"Nombres de big tutoriels publiés : {}".format(str(int(tutorials_count * percent_tutos_public)))) + # code validation + @transaction.atomic class Command(BaseCommand): args = '[low|medium|high]' @@ -196,7 +245,7 @@ class Command(BaseCommand): def handle(self, *args, **options): default_size = "low" - default_module = ["member", "article", "gallery", "tutorial"] + default_module = ["member", "staff", "category_forum", "forum", "tag", "topic", "post", "article", "note", "gallery", "tutorial", "reaction"] for arg in args: ps = arg.split("=") if len(ps) < 2: @@ -233,3 +282,11 @@ def handle(self, *args, **options): load_topics(self, size, fake) if "post" in default_module: load_posts(self, size, fake) + if "article" in default_module: + load_articles(self, size, fake) + if "tutorial" in default_module: + load_tutorials(self, size, fake) + if "reaction" in default_module: + load_comment_article(self, size, fake) + if "note" in default_module: + load_comment_tutorial(self, size, fake) From 1c276f1b0f641ad3be3950e51d3e49d76c22d721 Mon Sep 17 00:00:00 2001 From: firm1 Date: Tue, 18 Nov 2014 22:29:59 +0100 Subject: [PATCH 005/152] =?UTF-8?q?chargement=20de=20toutes=20les=20donn?= =?UTF-8?q?=C3=A9es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zds/article/factories.py | 2 +- zds/tutorial/factories.py | 24 +- .../management/commands/load_fixtures.py | 512 +++++++++++++++--- 3 files changed, 441 insertions(+), 97 deletions(-) diff --git a/zds/article/factories.py b/zds/article/factories.py index e2e1ec92c3..269c7237d1 100644 --- a/zds/article/factories.py +++ b/zds/article/factories.py @@ -61,7 +61,7 @@ def _prepare(cls, create, **kwargs): return reaction -class VaidationFactory(factory.DjangoModelFactory): +class ValidationFactory(factory.DjangoModelFactory): FACTORY_FOR = Validation diff --git a/zds/tutorial/factories.py b/zds/tutorial/factories.py index b680fbd63d..01b416dc32 100644 --- a/zds/tutorial/factories.py +++ b/zds/tutorial/factories.py @@ -14,18 +14,18 @@ from zds.utils.tutorials import export_tutorial content = ( - u'Ceci est un contenu de tutoriel utile et à tester un peu partout' - u'Ce contenu ira aussi bien dans les introductions, que dans les conclusions et les extraits ' - u'le gros intéret étant qu\'il renferme des images pour tester l\'execution coté pandoc ' - u'Exemple d\'image ![Ma pepite souris](http://blog.science-infuse.fr/public/souris.jpg)' - u'\nExemple d\'image ![Image inexistante](http://blog.science-infuse.fr/public/inv_souris.jpg)' - u'\nExemple de gif ![](http://corigif.free.fr/oiseau/img/oiseau_004.gif)' - u'\nExemple de gif inexistant ![](http://corigif.free.fr/oiseau/img/ironman.gif)' + u'Ceci est un contenu de tutoriel utile et à tester un peu partout\n\n ' + u'Ce contenu ira aussi bien dans les introductions, que dans les conclusions et les extraits \n\n ' + u'le gros intéret étant qu\'il renferme des images pour tester l\'execution coté pandoc \n\n ' + u'Exemple d\'image ![Ma pepite souris](http://blog.science-infuse.fr/public/souris.jpg)\n\n ' + u'\nExemple d\'image ![Image inexistante](http://blog.science-infuse.fr/public/inv_souris.jpg)\n\n ' + u'\nExemple de gif ![](http://corigif.free.fr/oiseau/img/oiseau_004.gif)\n\n ' + u'\nExemple de gif inexistant ![](http://corigif.free.fr/oiseau/img/ironman.gif)\n\n ' u'Une image de type wikipedia qui fait tomber des tests ![](https://s.qwant.com/thumbr/?u=http%3A%2' - u'F%2Fwww.blogoergosum.com%2Fwp-content%2Fuploads%2F2010%2F02%2Fwikipedia-logo.jpg&h=338&w=600)' - u'Image dont le serveur n\'existe pas ![](http://unknown.image.zds)' - u'\n Attention les tests ne doivent pas crasher ' - u'qu\'un sujet abandonné !') + u'F%2Fwww.blogoergosum.com%2Fwp-content%2Fuploads%2F2010%2F02%2Fwikipedia-logo.jpg&h=338&w=600)\n\n ' + u'Image dont le serveur n\'existe pas ![](http://unknown.image.zds)\n\n ' + u'\n Attention les tests ne doivent pas crasher \n\n \n\n \n\n ' + u'qu\'un sujet abandonné !\n\n ') content_light = u'Un contenu light pour quand ce n\'est pas vraiment ça qui est testé' @@ -324,7 +324,7 @@ class SubCategoryFactory(factory.DjangoModelFactory): slug = factory.Sequence(lambda n: 'sous-categorie-{0}'.format(n)) -class VaidationFactory(factory.DjangoModelFactory): +class ValidationFactory(factory.DjangoModelFactory): FACTORY_FOR = Validation diff --git a/zds/utils/management/commands/load_fixtures.py b/zds/utils/management/commands/load_fixtures.py index 78a1abc6c4..796e4c5ba7 100644 --- a/zds/utils/management/commands/load_fixtures.py +++ b/zds/utils/management/commands/load_fixtures.py @@ -1,31 +1,63 @@ # coding: utf-8 import random +from datetime import datetime from django.core.management.base import BaseCommand from random import randint from faker import Factory from zds.utils.templatetags.emarkdown import emarkdown from zds.forum.factories import CategoryFactory, ForumFactory, TopicFactory, PostFactory -from zds.tutorial.factories import BigTutorialFactory, PartFactory, ChapterFactory +from zds.tutorial.factories import BigTutorialFactory, PartFactory, ChapterFactory, NoteFactory, MiniTutorialFactory,\ + ExtractFactory +from zds.article.factories import ReactionFactory, ArticleFactory from zds.gallery.factories import GalleryFactory, UserGalleryFactory, ImageFactory from zds.member.factories import StaffProfileFactory, ProfileFactory -from django.contrib.auth.models import User +from django.contrib.auth.models import User, Permission from zds.member.models import Profile +from zds.article.models import Article, Reaction, Validation as AValidation +from zds.tutorial.models import Tutorial, Note, Validation as TValidation +from zds.tutorial.views import mep as mep_tuto +from zds.article.views import mep as mep_art from zds.forum.models import Category, Forum, Topic, Post -from zds.utils.models import Tag +from zds.utils.models import Tag, Category, CategorySubCategory, SubCategory from zds.utils import slugify +from zds import settings from django.db import transaction +import time -def load_member(cli, size, fake): +def load_member(cli, size, fake, root): """ Load members """ nb_users = size * 10 cli.stdout.write(u"Nombres de membres à créer : {}".format(nb_users)) + tps1 = time.time() + cpt = 1 + # member in settings + users_set = ["admin", + settings.ZDS_APP["member"]["external_account"], + settings.ZDS_APP["member"]["anonymous_account"]] + for u in users_set: + us = Profile.objects.filter(user__username=u).first() + if us is None: + profile = ProfileFactory(user__username=u) + profile.user.set_password(u) + profile.user.first_name = u + profile.user.email = fake.free_email() + if u == "admin": + profile.user.is_superuser = True + profile.user.save() + profile.site = fake.url() + profile.biography = fake.text(max_nb_chars=200) + profile.last_ip_address = fake.ipv4() + profile.save() + for i in range(0, nb_users): - profile = ProfileFactory() + while Profile.objects.filter(user__username="{}{}".format(root, cpt)).count() > 0: + cpt += 1 + profile = ProfileFactory(user__username="{}{}".format(root, cpt)) profile.user.set_password(profile.user.username) profile.user.first_name = fake.first_name() profile.user.last_name = fake.last_name() @@ -35,16 +67,23 @@ def load_member(cli, size, fake): profile.biography = fake.text(max_nb_chars=200) profile.last_ip_address = fake.ipv4() profile.save() + cpt += 1 + tps2 = time.time() + cli.stdout.write(u"Fait en {} sec".format(tps2 - tps1)) -def load_staff(cli, size, fake): +def load_staff(cli, size, fake, root): """ Load staff """ - nb_staffs = size * 10 + nb_staffs = size * 3 cli.stdout.write(u"Nombres de staffs à créer : {}".format(nb_staffs)) + tps1 = time.time() + cpt = 1 for i in range(0, nb_staffs): - profile = StaffProfileFactory() + while Profile.objects.filter(user__username="{}staff{}".format(root, cpt)).count() > 0: + cpt += 1 + profile = StaffProfileFactory(user__username="{}staff{}".format(root, cpt)) profile.user.first_name = fake.first_name() profile.user.last_name = fake.last_name() profile.user.email = fake.free_email() @@ -54,6 +93,9 @@ def load_staff(cli, size, fake): profile.sign = fake.text(max_nb_chars=80) profile.last_ip_address = fake.ipv6() profile.save() + cpt += 1 + tps2 = time.time() + cli.stdout.write(u"Fait en {} sec".format(tps2 - tps1)) def load_gallery(cli, size, fake): @@ -64,14 +106,20 @@ def load_gallery(cli, size, fake): nb_images = size * 5 cli.stdout.write(u"Nombres de galéries à créer par utilisateur: {}".format(nb_galleries)) cli.stdout.write(u"Nombres d'images à créer par gallerie: {}".format(nb_images)) + tps1 = time.time() nb_users = User.objects.count() - profiles = list(Profile.objects.all()) - for i in range(0, nb_users): - for j in range(0, nb_galleries): - gal = GalleryFactory(title=fake.text(max_nb_chars=80), subtitle=fake.text(max_nb_chars=200)) - ug = UserGalleryFactory(user=profiles[i].user, gallery=gal) - for k in range(0, nb_images): - img = ImageFactory(gallery=gal) + if nb_users == 0: + cli.stdout.write(u"Il n'y a aucun membre actuellement. Vous devez rajouter les membres dans vos fixtures") + else: + profiles = list(Profile.objects.all()) + for i in range(0, nb_users): + for j in range(0, nb_galleries): + gal = GalleryFactory(title=fake.text(max_nb_chars=80), subtitle=fake.text(max_nb_chars=200)) + ug = UserGalleryFactory(user=profiles[i].user, gallery=gal) + for k in range(0, nb_images): + img = ImageFactory(gallery=gal) + tps2 = time.time() + cli.stdout.write(u"Fait en {} sec".format(tps2 - tps1)) def load_categories_forum(cli, size, fake): @@ -80,11 +128,13 @@ def load_categories_forum(cli, size, fake): """ nb_categories = size * 2 cli.stdout.write(u"Nombres de catégories de forum à créer : {}".format(nb_categories)) - nb_users = User.objects.count() - for i in range(0, nb_users): + tps1 = time.time() + for i in range(0, nb_categories): cat = CategoryFactory(position=i + 1) - cat.title = fake.text(max_nb_chars=20) + cat.title = fake.word() cat.save() + tps2 = time.time() + cli.stdout.write(u"Fait en {} sec".format(tps2 - tps1)) def load_forums(cli, size, fake): @@ -93,14 +143,21 @@ def load_forums(cli, size, fake): """ nb_forums = size * 8 cli.stdout.write(u"Nombres de Forums à créer : {}".format(nb_forums)) + tps1 = time.time() nb_categories = Category.objects.count() - categories = list(Category.objects.all()) - for i in range(0, nb_forums): - forum = ForumFactory(category=categories[i % nb_categories], - position_in_category=(i / nb_categories) + 1) - forum.title = fake.text(max_nb_chars=20) - forum.subtitle = fake.sentence(nb_words=15, variable_nb_words=True) - forum.save() + if nb_categories == 0: + cli.stdout.write(u"Il n'y a aucune catgorie actuellement. Vous devez rajouter les categories " + u"de forum dans vos fixtures") + else: + categories = list(Category.objects.all()) + for i in range(0, nb_forums): + forum = ForumFactory(category=categories[i % nb_categories], + position_in_category=(i / nb_categories) + 1) + forum.title = fake.word() + forum.subtitle = fake.sentence(nb_words=15, variable_nb_words=True) + forum.save() + tps2 = time.time() + cli.stdout.write(u"Fait en {} sec".format(tps2 - tps1)) def load_tags(cli, size, fake): @@ -109,9 +166,13 @@ def load_tags(cli, size, fake): """ nb_tags = size * 50 cli.stdout.write(u"Nombres de Tags de forum à créer : {}".format(nb_tags)) + tps1 = time.time() for i in range(0, nb_tags): title = fake.word() t = Tag(title=title, slug=slugify(title)) + t.save() + tps2 = time.time() + cli.stdout.write(u"Fait en {} sec".format(tps2 - tps1)) def load_topics(cli, size, fake): @@ -120,20 +181,37 @@ def load_topics(cli, size, fake): """ nb_topics = size * 20 cli.stdout.write(u"Nombres de Topics à créer : {}".format(nb_topics)) + tps1 = time.time() nb_forums = Forum.objects.count() - forums = list(Forum.objects.all()) - nb_users = User.objects.count() - profiles = list(Profile.objects.all()) - nb_tags = Tag.objects.count() - for i in range(0, nb_topics): - topic = TopicFactory(forum=forums[i % nb_forums], author=profiles[i % nb_users].user) - topic.is_sticky = True - nb_rand_tags = random.randint(0, 5) - for k in range(0, nb_rand_tags): - topic.tags.add(random.randint(1, nb_tags)) - topic.title = fake.text(max_nb_chars=80) - topic.subtitle = fake.text(max_nb_chars=200) - topic.save() + if nb_forums == 0: + cli.stdout.write(u"Il n'y a aucun forum actuellement. Vous devez rajouter les forums dans vos fixtures") + else: + forums = list(Forum.objects.all()) + nb_users = User.objects.count() + if nb_users == 0: + cli.stdout.write(u"Il n'y a aucun membre actuellement. Vous devez rajouter les membres dans vos fixtures") + else: + profiles = list(Profile.objects.all()) + nb_tags = Tag.objects.count() + if nb_tags == 0: + cli.stdout.write(u"Il n'y a aucun tag actuellement. Vous devez rajouter les tags dans vos fixtures") + else: + for i in range(0, nb_topics): + topic = TopicFactory(forum=forums[i % nb_forums], author=profiles[i % nb_users].user) + if i % 5 == 0: + topic.is_solved = True + if i % 10 == 0: + topic.is_locked = True + if i % 15 == 0: + topic.is_sticky = True + nb_rand_tags = random.randint(0, 5) + for k in range(0, nb_rand_tags): + topic.tags.add(random.randint(1, nb_tags)) + topic.title = fake.text(max_nb_chars=80) + topic.subtitle = fake.text(max_nb_chars=200) + topic.save() + tps2 = time.time() + cli.stdout.write(u"Fait en {} sec".format(tps2 - tps1)) def load_posts(cli, size, fake): @@ -142,20 +220,66 @@ def load_posts(cli, size, fake): """ nb_avg_posts_in_topic = size * 10 cli.stdout.write(u"Nombres de messages à poster en moyenne dans un sujet : {}".format(nb_avg_posts_in_topic)) + tps1 = time.time() nb_topics = Topic.objects.count() - topics = list(Topic.objects.all()) - nb_users = User.objects.count() - profiles = list(Profile.objects.all()) - for i in range(0, nb_topics): - nb = randint(0, nb_avg_posts_in_topic * 2) - for j in range(0, nb): - if j == 0: - post = PostFactory(topic=topics[i], author=topics[i].author, position=1) - else: - post = PostFactory(topic=topics[i], author=profiles[j % nb_users].user, position=j+1) - post.text = fake.paragraph(nb_sentences=5, variable_nb_sentences=True) - post.text_html = emarkdown(post.text) - post.save() + if nb_topics == 0: + cli.stdout.write(u"Il n'y a aucun topic actuellement. Vous devez rajouter les topics dans vos fixtures") + else: + topics = list(Topic.objects.all()) + nb_users = User.objects.count() + if nb_users == 0: + cli.stdout.write(u"Il n'y a aucun membre actuellement. Vous devez rajouter les membres dans vos fixtures") + else: + profiles = list(Profile.objects.all()) + for i in range(0, nb_topics): + nb = randint(0, nb_avg_posts_in_topic * 2) + for j in range(0, nb): + if j == 0: + post = PostFactory(topic=topics[i], author=topics[i].author, position=1) + else: + post = PostFactory(topic=topics[i], author=profiles[j % nb_users].user, position=j+1) + post.text = fake.paragraph(nb_sentences=5, variable_nb_sentences=True) + post.text_html = emarkdown(post.text) + if int(nb * 0.3) > 0: + if j % int(nb * 0.3) == 0: + post.is_useful = True + post.save() + tps2 = time.time() + cli.stdout.write(u"Fait en {} sec".format(tps2 - tps1)) + + +def load_categories_content(cli, size, fake): + """ + Load categories and subcategories for tutorial and article + """ + categories = [] + sub_categories = [] + nb_categories = size * 5 + nb_sub_categories = size * 10 + cli.stdout.write(u"Nombres de catégories de contenus à créer : {}".format(nb_categories)) + cli.stdout.write(u"Nombres de sous-catégories de contenus à créer : {}".format(nb_sub_categories)) + tps1 = time.time() + for i in range(0, nb_categories): + ttl = fake.word() + cat = Category(title=ttl, + description=fake.sentence(nb_words=15, variable_nb_words=True), + slug=slugify(ttl)) + cat.save() + categories.append(cat) + for i in range(0, nb_sub_categories): + ttl = fake.word() + cat = SubCategory(title=ttl, + subtitle=fake.sentence(nb_words=5, variable_nb_words=True), + slug=slugify(ttl)) + cat.save() + sub_categories.append(cat) + + for i in range(0, nb_sub_categories): + h = CategorySubCategory(category=categories[i % nb_categories], subcategory=sub_categories[i], is_main=True) + h.save() + + tps2 = time.time() + cli.stdout.write(u"Fait en {} sec".format(tps2 - tps1)) def load_comment_article(cli, size, fake): @@ -164,6 +288,7 @@ def load_comment_article(cli, size, fake): """ nb_avg_posts = size * 20 cli.stdout.write(u"Nombres de messages à poster en moyenne dans un article : {}".format(nb_avg_posts)) + tps1 = time.time() nb_articles = Article.objects.filter(sha_public__isnull=False).count() articles = list(Article.objects.filter(sha_public__isnull=False).all()) nb_users = User.objects.count() @@ -175,6 +300,8 @@ def load_comment_article(cli, size, fake): post.text = fake.paragraph(nb_sentences=5, variable_nb_sentences=True) post.text_html = emarkdown(post.text) post.save() + tps2 = time.time() + cli.stdout.write(u"Fait en {} sec".format(tps2 - tps1)) def load_comment_tutorial(cli, size, fake): @@ -183,6 +310,7 @@ def load_comment_tutorial(cli, size, fake): """ nb_avg_posts = size * 20 cli.stdout.write(u"Nombres de messages à poster en moyenne dans un tutoriel : {}".format(nb_avg_posts)) + tps1 = time.time() nb_tutorials = Tutorial.objects.filter(sha_public__isnull=False).count() tutorials = list(Tutorial.objects.filter(sha_public__isnull=False).all()) nb_users = User.objects.count() @@ -194,58 +322,270 @@ def load_comment_tutorial(cli, size, fake): post.text = fake.paragraph(nb_sentences=5, variable_nb_sentences=True) post.text_html = emarkdown(post.text) post.save() + tps2 = time.time() + cli.stdout.write(u"Fait en {} sec".format(tps2 - tps1)) def load_tutorials(cli, size, fake): """ Load tutorials """ + tutorials = [] parts = [] chapters = [] nb_tutos = size * 10 + percent_tutos_validation_in_validation = 0.4 + percent_tutos_validation_with_validator = 0.2 + percent_tutos_public = 0.3 nb_avg_parts_in_tuto = size * 1 nb_avg_chapters_in_tuto = size * 1 + nb_avg_extracts_in_tuto = size * 1 cli.stdout.write(u"Nombres de big tutoriels à créer : {}".format(nb_tutos)) + cli.stdout.write(u"Nombres de big tutoriels en validations : {}" + .format(str(int(nb_tutos * percent_tutos_validation_in_validation)))) + cli.stdout.write(u"Nombres de big tutoriels réservé en validations : {}" + .format(str(int(nb_tutos * percent_tutos_validation_with_validator)))) + cli.stdout.write(u"Nombres de big tutoriels publiés : {}" + .format(str(int(nb_tutos * percent_tutos_public)))) + cli.stdout.write(u"Nombres de mini tutoriels à créer : {}".format(nb_tutos)) + cli.stdout.write(u"Nombres de mini tutoriels en validations : {}" + .format(str(int(nb_tutos * percent_tutos_validation_in_validation)))) + cli.stdout.write(u"Nombres de mini tutoriels réservé en validations : {}" + .format(str(int(nb_tutos * percent_tutos_validation_with_validator)))) + cli.stdout.write(u"Nombres de mini tutoriels publiés : {}" + .format(str(int(nb_tutos * percent_tutos_public)))) + tps1 = time.time() nb_users = User.objects.count() - profiles = list(Profile.objects.all()) - for i in range(0, nb_tutos): - tuto = BigTutorialFactory(title=fake.text(max_nb_chars=200), - description=fake.sentence(nb_words=15, variable_nb_words=True)) - tuto.authors.add(profiles[i % nb_users].user) - tutorials.append(tuto) - nb_part = randint(0, nb_avg_parts_in_tuto * 2) - for j in range(0, nb_part): - parts.append(PartFactory(tutorial=tutorials[i], - position_in_tutorial=j, - title=fake.text(max_nb_chars=200))) - nb_chap = randint(0, nb_avg_chapters_in_tuto * 2) - for k in range(0, nb_chap): - chapters.append(ChapterFactory(part=parts[j], - position_in_part=k, - position_in_tutorial=j * k, - title=fake.text(max_nb_chars=200))) - - percent_tutos_validation_without_validator = 0.1 - percent_tutos_validation_with_validator = 0.1 - percent_tutos_in_beta = 0.1 - percent_tutos_public = 0.3 - tutorials_count = len(tutorials) - cli.stdout.write(u"Nombres de big tutoriels sans validateurs : {}".format(str(int(tutorials_count * percent_tutos_validation_without_validator)))) - cli.stdout.write(u"Nombres de big tutoriels réservé en validations : {}".format(str(int(tutorials_count * percent_tutos_validation_with_validator)))) - cli.stdout.write(u"Nombres de big tutoriels en beta : {}".format(str(int(tutorials_count * percent_tutos_in_beta)))) - cli.stdout.write(u"Nombres de big tutoriels publiés : {}".format(str(int(tutorials_count * percent_tutos_public)))) - # code validation + if nb_users == 0: + cli.stdout.write(u"Il n'y a aucun membre actuellement. Vous devez rajouter les membre dans vos fixtures") + else: + profiles = list(Profile.objects.all()) + nb_sub_categories = SubCategory.objects.count() + if nb_sub_categories == 0: + cli.stdout.write(u"Il n'y a aucune catégories actuellement." + u"Vous devez rajouter les membre dans vos fixtures") + else: + sub_categories = list(SubCategory.objects.all()) + perms = list(Permission.objects.filter(codename__startswith='change_').all()) + staffs = list(User.objects.filter(groups__permissions__in=perms).all()) + nb_staffs = len(staffs) + if nb_staffs == 0: + cli.stdout.write(u"Il n'y a aucun staff actuellement. Vous devez rajouter les staffs dans vos fixtures") + else: + # big tutorials + for i in range(0, nb_tutos): + tuto = BigTutorialFactory(title=fake.text(max_nb_chars=200), + description=fake.sentence(nb_words=15, variable_nb_words=True)) + tuto.authors.add(profiles[i % nb_users].user) + tuto.subcategory.add(sub_categories[random.randint(0, nb_sub_categories-1)]) + tutorials.append(tuto) + nb_part = randint(0, nb_avg_parts_in_tuto * 2) + for j in range(0, nb_part): + parts.append(PartFactory(tutorial=tutorials[i], + position_in_tutorial=j, + title=fake.text(max_nb_chars=200))) + nb_chap = randint(0, nb_avg_chapters_in_tuto * 2) + for k in range(0, nb_chap): + chapters.append(ChapterFactory(part=parts[j], + position_in_part=k, + position_in_tutorial=j * k, + title=fake.text(max_nb_chars=200))) + nb_ext = randint(0, nb_avg_extracts_in_tuto * 2) + for l in range(0, nb_ext): + ExtractFactory(chapter=chapters[k], + position_in_chapter=l, + title=fake.text(max_nb_chars=200)) + if i < int(nb_tutos * percent_tutos_validation_with_validator): + validator = staffs[random.randint(0, nb_staffs-1)] + v = TValidation(tutorial=tuto, + version=tuto.sha_draft, + date_proposition=datetime.now(), + date_reserve=datetime.now(), + validator=validator, + status="PENDING_V") + v.save() + tuto.sha_validation = tuto.sha_draft + tuto.save() + elif i < int(nb_tutos * (percent_tutos_validation_in_validation + + percent_tutos_validation_with_validator)): + v = TValidation(tutorial=tuto, + version=tuto.sha_draft, + date_proposition=datetime.now()) + v.save() + tuto.sha_validation = tuto.sha_draft + tuto.save() + elif i < int(nb_tutos * (percent_tutos_validation_in_validation + + percent_tutos_validation_with_validator + + percent_tutos_public)): + mep_tuto(tuto, tuto.sha_draft) + v = TValidation(tutorial=tuto, + version=tuto.sha_draft, + date_proposition=datetime.now(), + date_reserve=datetime.now(), + validator=validator, + status="ACCEPT", + comment_validator=fake.text(max_nb_chars=200), + date_validation=datetime.now()) + v.save() + tuto.sha_public = tuto.sha_draft + tuto.save() + # Mini tutorials + for i in range(0, nb_tutos): + tuto = MiniTutorialFactory(title=fake.text(max_nb_chars=200), + description=fake.sentence(nb_words=15, variable_nb_words=True)) + tuto.authors.add(profiles[i % nb_users].user) + tuto.subcategory.add(sub_categories[random.randint(0, nb_sub_categories-1)]) + tutorials.append(tuto) + chap = ChapterFactory(tutorial=tutorials[j]) + nb_ext = randint(0, nb_avg_extracts_in_tuto * 2) + for l in range(0, nb_ext): + ExtractFactory(chapter=chap, + position_in_chapter=l, + title=fake.text(max_nb_chars=200)) + if i < int(nb_tutos * percent_tutos_validation_with_validator): + validator = staffs[random.randint(0, nb_staffs-1)] + v = TValidation(tutorial=tuto, + version=tuto.sha_draft, + date_proposition=datetime.now(), + date_reserve=datetime.now(), + validator=validator, + status="PENDING_V") + v.save() + tuto.sha_validation = tuto.sha_draft + tuto.save() + elif i < int(nb_tutos * (percent_tutos_validation_in_validation + + percent_tutos_validation_with_validator)): + v = TValidation(tutorial=tuto, + version=tuto.sha_draft, + date_proposition=datetime.now()) + v.save() + tuto.sha_validation = tuto.sha_draft + tuto.save() + elif i < int(nb_tutos * (percent_tutos_validation_in_validation + + percent_tutos_validation_with_validator + + percent_tutos_public)): + mep_tuto(tuto, tuto.sha_draft) + v = TValidation(tutorial=tuto, + version=tuto.sha_draft, + date_proposition=datetime.now(), + date_reserve=datetime.now(), + validator=validator, + status="ACCEPT", + comment_validator=fake.text(max_nb_chars=200), + date_validation=datetime.now()) + v.save() + tuto.sha_public = tuto.sha_draft + tuto.save() + tps2 = time.time() + cli.stdout.write(u"Fait en {} sec".format(tps2 - tps1)) + + +def load_articles(cli, size, fake): + """ + Load articles + """ + + articles = [] + + nb_arts = size * 10 + percent_arts_validation_in_validation = 0.4 + percent_arts_validation_with_validator = 0.2 + percent_arts_public = 0.3 + cli.stdout.write(u"Nombres d'articles à créer : {}".format(nb_arts)) + cli.stdout.write(u"Nombres d'articles en validations : {}" + .format(str(int(nb_arts * percent_arts_validation_in_validation)))) + cli.stdout.write(u"Nombres d'articles réservé en validations : {}" + .format(str(int(nb_arts * percent_arts_validation_with_validator)))) + cli.stdout.write(u"Nombres d'articles publiés : {}" + .format(str(int(nb_arts * percent_arts_public)))) + tps1 = time.time() + nb_users = User.objects.count() + if nb_users == 0: + cli.stdout.write(u"Il n'y a aucun membre actuellement. Vous devez rajouter les membre dans vos fixtures") + else: + nb_sub_categories = SubCategory.objects.count() + if nb_sub_categories == 0: + cli.stdout.write(u"Il n'y a aucune catégories actuellement." + u"Vous devez rajouter les membre dans vos fixtures") + else: + sub_categories = list(SubCategory.objects.all()) + profiles = list(Profile.objects.all()) + perms = list(Permission.objects.filter(codename__startswith='change_').all()) + staffs = list(User.objects.filter(groups__permissions__in=perms).all()) + nb_staffs = len(staffs) + if nb_staffs == 0: + cli.stdout.write(u"Il n'y a aucun staff actuellement. Vous devez rajouter les staffs dans vos fixtures") + else: + for i in range(0, nb_arts): + art = ArticleFactory(title=fake.text(max_nb_chars=200), + description=fake.sentence(nb_words=15, variable_nb_words=True)) + art.authors.add(profiles[i % nb_users].user) + art.subcategory.add(sub_categories[random.randint(0, nb_sub_categories-1)]) + articles.append(art) + + if i < int(nb_arts * percent_arts_validation_with_validator): + validator = staffs[random.randint(0, nb_staffs-1)] + v = AValidation(article=art, + version=art.sha_draft, + date_proposition=datetime.now(), + date_reserve=datetime.now(), + validator=validator, + status="PENDING_V") + v.save() + art.sha_validation = art.sha_draft + art.save() + elif i < int(nb_arts * (percent_arts_validation_in_validation + + percent_arts_validation_with_validator)): + v = AValidation(article=art, + version=art.sha_draft, + date_proposition=datetime.now()) + v.save() + art.sha_validation = art.sha_draft + art.save() + elif i < int(nb_arts * (percent_arts_validation_in_validation + + percent_arts_validation_with_validator + + percent_arts_public)): + mep_art(art, art.sha_draft) + v = AValidation(article=art, + version=art.sha_draft, + date_proposition=datetime.now(), + date_reserve=datetime.now(), + validator=validator, + status="ACCEPT", + comment_validator=fake.text(max_nb_chars=200), + date_validation=datetime.now()) + v.save() + art.sha_public = art.sha_draft + art.pubdate = datetime.now() + art.save() + tps2 = time.time() + cli.stdout.write(u"Fait en {} sec".format(tps2 - tps1)) + @transaction.atomic class Command(BaseCommand): args = '[low|medium|high]' help = 'Load fixtures for ZdS' + # python manage.py load_fixtures size=low module=staff racine=user def handle(self, *args, **options): default_size = "low" - default_module = ["member", "staff", "category_forum", "forum", "tag", "topic", "post", "article", "note", "gallery", "tutorial", "reaction"] + default_root = "user" + default_module = ["member", + "staff", + "category_forum", + "category_content", + "forum", + "tag", + "topic", + "post", + "article", + "note", + "gallery", + "tutorial", + "reaction"] for arg in args: ps = arg.split("=") if len(ps) < 2: @@ -255,6 +595,8 @@ def handle(self, *args, **options): default_size = ps[1].split(",")[0] elif ps[0] in ["type", "types"]: default_module = ps[1].split(",") + elif ps[0] in ["racine"]: + default_root = ps[1].split(",")[0] if default_size == "low": size = 1 @@ -267,9 +609,9 @@ def handle(self, *args, **options): fake = Factory.create(locale="fr_FR") if "member" in default_module: - load_member(self, size, fake) + load_member(self, size, fake, default_root) if "staff" in default_module: - load_staff(self, size, fake) + load_staff(self, size, fake, default_root) if "gallery" in default_module: load_gallery(self, size, fake) if "category_forum" in default_module: @@ -282,11 +624,13 @@ def handle(self, *args, **options): load_topics(self, size, fake) if "post" in default_module: load_posts(self, size, fake) + if "category_content" in default_module: + load_categories_content(self, size, fake) if "article" in default_module: load_articles(self, size, fake) - if "tutorial" in default_module: - load_tutorials(self, size, fake) if "reaction" in default_module: load_comment_article(self, size, fake) + if "tutorial" in default_module: + load_tutorials(self, size, fake) if "note" in default_module: load_comment_tutorial(self, size, fake) From ce9a38fa5f1eb91e3f4b41ab45b17c4faaa0f969 Mon Sep 17 00:00:00 2001 From: firm1 Date: Tue, 18 Nov 2014 22:46:03 +0100 Subject: [PATCH 006/152] corrige pep8 --- zds/utils/management/commands/load_fixtures.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/zds/utils/management/commands/load_fixtures.py b/zds/utils/management/commands/load_fixtures.py index 796e4c5ba7..a2546c3d9d 100644 --- a/zds/utils/management/commands/load_fixtures.py +++ b/zds/utils/management/commands/load_fixtures.py @@ -15,11 +15,11 @@ from zds.member.factories import StaffProfileFactory, ProfileFactory from django.contrib.auth.models import User, Permission from zds.member.models import Profile -from zds.article.models import Article, Reaction, Validation as AValidation -from zds.tutorial.models import Tutorial, Note, Validation as TValidation +from zds.article.models import Article, Validation as AValidation +from zds.tutorial.models import Tutorial, Validation as TValidation from zds.tutorial.views import mep as mep_tuto from zds.article.views import mep as mep_art -from zds.forum.models import Category, Forum, Topic, Post +from zds.forum.models import Forum, Topic from zds.utils.models import Tag, Category, CategorySubCategory, SubCategory from zds.utils import slugify from zds import settings @@ -115,9 +115,9 @@ def load_gallery(cli, size, fake): for i in range(0, nb_users): for j in range(0, nb_galleries): gal = GalleryFactory(title=fake.text(max_nb_chars=80), subtitle=fake.text(max_nb_chars=200)) - ug = UserGalleryFactory(user=profiles[i].user, gallery=gal) + UserGalleryFactory(user=profiles[i].user, gallery=gal) for k in range(0, nb_images): - img = ImageFactory(gallery=gal) + ImageFactory(gallery=gal) tps2 = time.time() cli.stdout.write(u"Fait en {} sec".format(tps2 - tps1)) @@ -318,7 +318,7 @@ def load_comment_tutorial(cli, size, fake): for i in range(0, nb_tutorials): nb = randint(0, nb_avg_posts * 2) for j in range(0, nb): - post = NoteFactory(tutorial=tutorials[i], author=profiles[j % nb_tutorials].user, position=j+1) + post = NoteFactory(tutorial=tutorials[i], author=profiles[j % nb_users].user, position=j+1) post.text = fake.paragraph(nb_sentences=5, variable_nb_sentences=True) post.text_html = emarkdown(post.text) post.save() @@ -566,7 +566,7 @@ def load_articles(cli, size, fake): @transaction.atomic class Command(BaseCommand): - args = '[low|medium|high]' + args = 'size=[low|medium|high] type=member,staff,gallery,category_forum,category_content' help = 'Load fixtures for ZdS' # python manage.py load_fixtures size=low module=staff racine=user From 80aafb3373731b17187f992c664f733b4933576b Mon Sep 17 00:00:00 2001 From: firm1 Date: Tue, 18 Nov 2014 22:59:14 +0100 Subject: [PATCH 007/152] rajout des licences --- zds/utils/management/commands/load_fixtures.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/zds/utils/management/commands/load_fixtures.py b/zds/utils/management/commands/load_fixtures.py index a2546c3d9d..39e19cfb49 100644 --- a/zds/utils/management/commands/load_fixtures.py +++ b/zds/utils/management/commands/load_fixtures.py @@ -20,7 +20,7 @@ from zds.tutorial.views import mep as mep_tuto from zds.article.views import mep as mep_art from zds.forum.models import Forum, Topic -from zds.utils.models import Tag, Category, CategorySubCategory, SubCategory +from zds.utils.models import Tag, Category, CategorySubCategory, SubCategory, Licence from zds.utils import slugify from zds import settings from django.db import transaction @@ -252,6 +252,13 @@ def load_categories_content(cli, size, fake): """ Load categories and subcategories for tutorial and article """ + + lics = ["CB-BY", "CC-BY-ND", "CC-BY-ND-SA", "CC-BY-SA", "CC", "CC-BY-IO", "Tout-Droits"] + for lic in lics: + ex = Licence.objects.filter(code=lic) + if ex is None: + l = Licence(code=lic, title=lic, description="") + l.save() categories = [] sub_categories = [] nb_categories = size * 5 From 1ffd16beba81b448fe7a05a3611325ce54b12da6 Mon Sep 17 00:00:00 2001 From: firm1 Date: Wed, 19 Nov 2014 01:27:31 +0100 Subject: [PATCH 008/152] =?UTF-8?q?ajuste=20les=20cat=C3=A9gories?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zds/utils/management/commands/load_fixtures.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/zds/utils/management/commands/load_fixtures.py b/zds/utils/management/commands/load_fixtures.py index 39e19cfb49..ea4beaef14 100644 --- a/zds/utils/management/commands/load_fixtures.py +++ b/zds/utils/management/commands/load_fixtures.py @@ -19,8 +19,8 @@ from zds.tutorial.models import Tutorial, Validation as TValidation from zds.tutorial.views import mep as mep_tuto from zds.article.views import mep as mep_art -from zds.forum.models import Forum, Topic -from zds.utils.models import Tag, Category, CategorySubCategory, SubCategory, Licence +from zds.forum.models import Forum, Topic, Category as FCategory +from zds.utils.models import Tag, Category as TCategory, CategorySubCategory, SubCategory, Licence from zds.utils import slugify from zds import settings from django.db import transaction @@ -48,6 +48,7 @@ def load_member(cli, size, fake, root): profile.user.email = fake.free_email() if u == "admin": profile.user.is_superuser = True + profile.user.is_staff = True profile.user.save() profile.site = fake.url() profile.biography = fake.text(max_nb_chars=200) @@ -144,12 +145,12 @@ def load_forums(cli, size, fake): nb_forums = size * 8 cli.stdout.write(u"Nombres de Forums à créer : {}".format(nb_forums)) tps1 = time.time() - nb_categories = Category.objects.count() + nb_categories = FCategory.objects.count() if nb_categories == 0: cli.stdout.write(u"Il n'y a aucune catgorie actuellement. Vous devez rajouter les categories " u"de forum dans vos fixtures") else: - categories = list(Category.objects.all()) + categories = list(FCategory.objects.all()) for i in range(0, nb_forums): forum = ForumFactory(category=categories[i % nb_categories], position_in_category=(i / nb_categories) + 1) @@ -268,9 +269,9 @@ def load_categories_content(cli, size, fake): tps1 = time.time() for i in range(0, nb_categories): ttl = fake.word() - cat = Category(title=ttl, - description=fake.sentence(nb_words=15, variable_nb_words=True), - slug=slugify(ttl)) + cat = TCategory(title=ttl, + description=fake.sentence(nb_words=15, variable_nb_words=True), + slug=slugify(ttl)) cat.save() categories.append(cat) for i in range(0, nb_sub_categories): From 5d5eac63371a0a667198fe805acf4dc19218460c Mon Sep 17 00:00:00 2001 From: Christophe G Date: Wed, 19 Nov 2014 19:41:31 +0100 Subject: [PATCH 009/152] Add jsfiddle support --- requirements.txt | 2 +- zds/utils/templatetags/emarkdown.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index 9beb37321b..84c7c7f279 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,7 +18,7 @@ factory-boy==2.4.1 pygeoip==0.3.2 pillow==2.6.1 https://github.com/zestedesavoir/GitPython/archive/0.3.2-RC2.zip -https://github.com/zestedesavoir/Python-ZMarkdown/archive/2.4.1-zds.10.zip +https://github.com/zestedesavoir/Python-ZMarkdown/archive/2.4.1-zds.11.zip easy-thumbnails==2.2 # Development tools diff --git a/zds/utils/templatetags/emarkdown.py b/zds/utils/templatetags/emarkdown.py index 0273941cee..e4d50fee20 100644 --- a/zds/utils/templatetags/emarkdown.py +++ b/zds/utils/templatetags/emarkdown.py @@ -20,14 +20,14 @@ u"Veuillez rapporter le bug." -def get_markdown_instance(inline=False): +def get_markdown_instance(inline=False, js_support=False): """ Provide a pre-configured markdown parser. :param bool inline: If `True`, configure parser to parse only inline content. :return: A ZMarkdown parser. """ - zdsext = ZdsExtension({"inline": inline, "emoticons": smileys}) + zdsext = ZdsExtension({"inline": inline, "emoticons": smileys, "js_support": js_support}) # Generate parser md = markdown.Markdown(extensions=(zdsext,), safe_mode = 'escape', @@ -53,7 +53,7 @@ def get_markdown_instance(inline=False): return md -def render_markdown(text, inline=False): +def render_markdown(text, inline=False, js_support=False): """ Render a markdown text to html. @@ -62,7 +62,7 @@ def render_markdown(text, inline=False): :return: Equivalent html string. :rtype: str """ - return get_markdown_instance(inline=inline).convert(text).encode('utf-8').strip() + return get_markdown_instance(inline=inline, js_support=js_support).convert(text).encode('utf-8').strip() @register.filter(needs_autoescape=False) @@ -91,7 +91,7 @@ def emarkdown_inline(text): """ try: - return mark_safe(render_markdown(text, inline=True)) + return mark_safe(render_markdown(text, inline=True, js_support=False)) except: return mark_safe(u'

{}

'.format(__MD_ERROR_PARSING)) From 2df5cb2d67d8a1afdf5e0f41ebe43a87e43cf74b Mon Sep 17 00:00:00 2001 From: firm1 Date: Thu, 20 Nov 2014 13:26:43 +0100 Subject: [PATCH 010/152] choix d'utilisation de js dans le contenu --- templates/article/member/view.html | 2 +- templates/tutorial/includes/chapter.part.html | 12 +- templates/tutorial/part/view.html | 4 +- templates/tutorial/tutorial/view.html | 4 +- ...0004_auto__add_field_article_js_support.py | 139 +++++++++++++ zds/article/models.py | 1 + zds/article/views.py | 14 +- ...005_auto__add_field_tutorial_js_support.py | 195 ++++++++++++++++++ zds/tutorial/models.py | 1 + zds/tutorial/views.py | 26 ++- zds/utils/templatetags/emarkdown.py | 7 +- 11 files changed, 387 insertions(+), 18 deletions(-) create mode 100644 zds/article/migrations/0004_auto__add_field_article_js_support.py create mode 100644 zds/tutorial/migrations/0005_auto__add_field_tutorial_js_support.py diff --git a/templates/article/member/view.html b/templates/article/member/view.html index 647b91125f..db72976e43 100644 --- a/templates/article/member/view.html +++ b/templates/article/member/view.html @@ -145,6 +145,6 @@

{% block content %}
- {{ article.txt|emarkdown }} + {{ article.txt|emarkdown:is_js }}
{% endblock %} diff --git a/templates/tutorial/includes/chapter.part.html b/templates/tutorial/includes/chapter.part.html index e4db36f9f5..9e04c25623 100644 --- a/templates/tutorial/includes/chapter.part.html +++ b/templates/tutorial/includes/chapter.part.html @@ -5,15 +5,15 @@ {% with extracts=chapter.extracts %} {% if not chapter.type = 'MINI' %} - {% if chapter.intro and chapter.intro != "None" %} - {{ chapter.intro|emarkdown }} + {% if chapter.intro and chapter.intro != None %} + {{ chapter.intro|emarkdown:is_js }} {% elif not tutorial.is_beta %}

{% trans "Il n'y a pas d'introduction" %}.

{% endif %} {% endif %} - +
{% if not extracts %} @@ -93,7 +93,7 @@

{% endif %} {% if extract.txt %} - {{ extract.txt|emarkdown }} + {{ extract.txt|emarkdown:is_js }} {% else %}

{% trans "Cet extrait est vide" %}. @@ -104,8 +104,8 @@


{% if not chapter.type = 'MINI' %} - {% if chapter.conclu and chapter.conclu != "None" %} - {{ chapter.conclu|emarkdown }} + {% if chapter.conclu and chapter.conclu != None %} + {{ chapter.conclu|emarkdown:is_js }} {% elif not tutorial.is_beta %}

{% trans "Il n'y a pas de conclusion" %}. diff --git a/templates/tutorial/part/view.html b/templates/tutorial/part/view.html index b33d027905..b74cb303f9 100644 --- a/templates/tutorial/part/view.html +++ b/templates/tutorial/part/view.html @@ -44,7 +44,7 @@

{% block content %} {% if part.intro and part.intro != "None" %} - {{ part.intro|emarkdown }} + {{ part.intro|emarkdown:is_js }} {% elif not tutorial.is_beta %}

{% trans "Il n'y a pas d'introduction" %}. @@ -91,7 +91,7 @@


{% if part.conclu and part.conclu != "None" %} - {{ part.conclu|emarkdown }} + {{ part.conclu|emarkdown:is_js }} {% elif not tutorial.is_beta %}

{% trans "Il n'y a pas de conclusion" %}. diff --git a/templates/tutorial/tutorial/view.html b/templates/tutorial/tutorial/view.html index 94668c83c7..f2eb7919a8 100644 --- a/templates/tutorial/tutorial/view.html +++ b/templates/tutorial/tutorial/view.html @@ -102,7 +102,7 @@

{% block content %} {% if tutorial.get_introduction and tutorial.get_introduction != "None" %} - {{ tutorial.get_introduction|emarkdown }} + {{ tutorial.get_introduction|emarkdown:is_js }} {% elif not tutorial.is_beta %}

{% trans "Il n'y a pas d'introduction" %}. @@ -136,7 +136,7 @@

{% endif %} {% if tutorial.get_conclusion and tutorial.get_conclusion != "None" %} - {{ tutorial.get_conclusion|emarkdown }} + {{ tutorial.get_conclusion|emarkdown:is_js }} {% elif not tutorial.is_beta %}

{% trans "Il n'y a pas de conclusion" %}. diff --git a/zds/article/migrations/0004_auto__add_field_article_js_support.py b/zds/article/migrations/0004_auto__add_field_article_js_support.py new file mode 100644 index 0000000000..0f8d0b7c21 --- /dev/null +++ b/zds/article/migrations/0004_auto__add_field_article_js_support.py @@ -0,0 +1,139 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Article.js_support' + db.add_column(u'article_article', 'js_support', + self.gf('django.db.models.fields.BooleanField')(default=False), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Article.js_support' + db.delete_column(u'article_article', 'js_support') + + + models = { + u'article.article': { + 'Meta': {'object_name': 'Article'}, + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'db_index': 'True', 'symmetrical': 'False'}), + 'create_at': ('django.db.models.fields.DateTimeField', [], {}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'is_locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_visible': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), + 'js_support': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_reaction': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'last_reaction'", 'null': 'True', 'to': u"orm['article.Reaction']"}), + 'licence': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['utils.Licence']", 'null': 'True', 'blank': 'True'}), + 'pubdate': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'sha_draft': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '80', 'null': 'True', 'blank': 'True'}), + 'sha_public': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '80', 'null': 'True', 'blank': 'True'}), + 'sha_validation': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '80', 'null': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '80'}), + 'subcategory': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['utils.SubCategory']", 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'text': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '80'}), + 'update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + u'article.articleread': { + 'Meta': {'object_name': 'ArticleRead'}, + 'article': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['article.Article']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'reaction': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['article.Reaction']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reactions_read'", 'to': u"orm['auth.User']"}) + }, + u'article.reaction': { + 'Meta': {'object_name': 'Reaction', '_ormbases': [u'utils.Comment']}, + 'article': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['article.Article']"}), + u'comment_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['utils.Comment']", 'unique': 'True', 'primary_key': 'True'}) + }, + u'article.validation': { + 'Meta': {'object_name': 'Validation'}, + 'article': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['article.Article']", 'null': 'True', 'blank': 'True'}), + 'comment_authors': ('django.db.models.fields.TextField', [], {}), + 'comment_validator': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'date_proposition': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'date_reserve': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_validation': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PENDING'", 'max_length': '10'}), + 'validator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'articles_author_validations'", 'null': 'True', 'to': u"orm['auth.User']"}), + 'version': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '80', 'null': 'True', 'blank': 'True'}) + }, + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'utils.comment': { + 'Meta': {'object_name': 'Comment'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'comments'", 'to': u"orm['auth.User']"}), + 'dislike': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'editor': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comments-editor'", 'null': 'True', 'to': u"orm['auth.User']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_address': ('django.db.models.fields.CharField', [], {'max_length': '39'}), + 'is_visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'like': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'position': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'pubdate': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'text_hidden': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '80'}), + 'text_html': ('django.db.models.fields.TextField', [], {}), + 'update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + u'utils.licence': { + 'Meta': {'object_name': 'Licence'}, + 'code': ('django.db.models.fields.CharField', [], {'max_length': '20'}), + 'description': ('django.db.models.fields.TextField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '80'}) + }, + u'utils.subcategory': { + 'Meta': {'object_name': 'SubCategory'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '80'}), + 'subtitle': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '80'}) + } + } + + complete_apps = ['article'] \ No newline at end of file diff --git a/zds/article/models.py b/zds/article/models.py index ea276b228a..d3f097f7c6 100644 --- a/zds/article/models.py +++ b/zds/article/models.py @@ -84,6 +84,7 @@ class Meta: related_name='last_reaction', verbose_name='Derniere réaction') is_locked = models.BooleanField('Est verrouillé', default=False) + js_support = models.BooleanField('Support du Javascript', default=False) licence = models.ForeignKey(Licence, verbose_name='Licence', diff --git a/zds/article/views.py b/zds/article/views.py index 12b3463672..eddc5b282e 100644 --- a/zds/article/views.py +++ b/zds/article/views.py @@ -117,12 +117,18 @@ def view(request, article_pk, article_slug): .order_by("-date_proposition")\ .first() + if article.js_support: + is_js = "js" + else: + is_js = "" + return render_template('article/member/view.html', { 'article': article_version, 'authors': article.authors, 'tags': article.subcategory, 'version': sha, - 'validation': validation + 'validation': validation, + 'is_js': is_js }) @@ -956,7 +962,11 @@ def mep(article, sha): article_version['text'] + '.html'), "w") - html_file.write(emarkdown(md_file_contenu)) + if article.js_support: + is_js = "js" + else: + is_js = "" + html_file.write(emarkdown(md_file_contenu, is_js)) html_file.close() diff --git a/zds/tutorial/migrations/0005_auto__add_field_tutorial_js_support.py b/zds/tutorial/migrations/0005_auto__add_field_tutorial_js_support.py new file mode 100644 index 0000000000..20a5216f02 --- /dev/null +++ b/zds/tutorial/migrations/0005_auto__add_field_tutorial_js_support.py @@ -0,0 +1,195 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Tutorial.js_support' + db.add_column(u'tutorial_tutorial', 'js_support', + self.gf('django.db.models.fields.BooleanField')(default=False), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Tutorial.js_support' + db.delete_column(u'tutorial_tutorial', 'js_support') + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'gallery.gallery': { + 'Meta': {'object_name': 'Gallery'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'pubdate': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '80'}), + 'subtitle': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '80'}), + 'update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + u'gallery.image': { + 'Meta': {'object_name': 'Image'}, + 'gallery': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gallery.Gallery']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'legend': ('django.db.models.fields.CharField', [], {'max_length': '80', 'null': 'True', 'blank': 'True'}), + 'physical': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}), + 'pubdate': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '80'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '80', 'null': 'True', 'blank': 'True'}), + 'update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + u'tutorial.chapter': { + 'Meta': {'object_name': 'Chapter'}, + 'conclusion': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gallery.Image']", 'null': 'True', 'blank': 'True'}), + 'introduction': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'part': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['tutorial.Part']", 'null': 'True', 'blank': 'True'}), + 'position_in_part': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'position_in_tutorial': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '80'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '80', 'blank': 'True'}), + 'tutorial': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['tutorial.Tutorial']", 'null': 'True', 'blank': 'True'}) + }, + u'tutorial.extract': { + 'Meta': {'object_name': 'Extract'}, + 'chapter': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['tutorial.Chapter']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'position_in_chapter': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'text': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '80'}) + }, + u'tutorial.note': { + 'Meta': {'object_name': 'Note', '_ormbases': [u'utils.Comment']}, + u'comment_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['utils.Comment']", 'unique': 'True', 'primary_key': 'True'}), + 'tutorial': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['tutorial.Tutorial']"}) + }, + u'tutorial.part': { + 'Meta': {'object_name': 'Part'}, + 'conclusion': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'introduction': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'position_in_tutorial': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '80'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '80'}), + 'tutorial': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['tutorial.Tutorial']"}) + }, + u'tutorial.tutorial': { + 'Meta': {'object_name': 'Tutorial'}, + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'db_index': 'True', 'symmetrical': 'False'}), + 'conclusion': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'create_at': ('django.db.models.fields.DateTimeField', [], {}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'gallery': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gallery.Gallery']", 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['gallery.Image']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}), + 'images': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'introduction': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'is_locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'js_support': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_note': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'last_note'", 'null': 'True', 'to': u"orm['tutorial.Note']"}), + 'licence': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['utils.Licence']", 'null': 'True', 'blank': 'True'}), + 'pubdate': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'sha_beta': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '80', 'null': 'True', 'blank': 'True'}), + 'sha_draft': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '80', 'null': 'True', 'blank': 'True'}), + 'sha_public': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '80', 'null': 'True', 'blank': 'True'}), + 'sha_validation': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '80', 'null': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '80'}), + 'source': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'subcategory': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['utils.SubCategory']", 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '80'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '10', 'db_index': 'True'}), + 'update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + u'tutorial.tutorialread': { + 'Meta': {'object_name': 'TutorialRead'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'note': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['tutorial.Note']"}), + 'tutorial': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['tutorial.Tutorial']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tuto_notes_read'", 'to': u"orm['auth.User']"}) + }, + u'tutorial.validation': { + 'Meta': {'object_name': 'Validation'}, + 'comment_authors': ('django.db.models.fields.TextField', [], {}), + 'comment_validator': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'date_proposition': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'date_reserve': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'date_validation': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'PENDING'", 'max_length': '10'}), + 'tutorial': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['tutorial.Tutorial']", 'null': 'True', 'blank': 'True'}), + 'validator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'author_validations'", 'null': 'True', 'to': u"orm['auth.User']"}), + 'version': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '80', 'null': 'True', 'blank': 'True'}) + }, + u'utils.comment': { + 'Meta': {'object_name': 'Comment'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'comments'", 'to': u"orm['auth.User']"}), + 'dislike': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'editor': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comments-editor'", 'null': 'True', 'to': u"orm['auth.User']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_address': ('django.db.models.fields.CharField', [], {'max_length': '39'}), + 'is_visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'like': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'position': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'pubdate': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}), + 'text': ('django.db.models.fields.TextField', [], {}), + 'text_hidden': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '80'}), + 'text_html': ('django.db.models.fields.TextField', [], {}), + 'update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}) + }, + u'utils.licence': { + 'Meta': {'object_name': 'Licence'}, + 'code': ('django.db.models.fields.CharField', [], {'max_length': '20'}), + 'description': ('django.db.models.fields.TextField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '80'}) + }, + u'utils.subcategory': { + 'Meta': {'object_name': 'SubCategory'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '80'}), + 'subtitle': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '80'}) + } + } + + complete_apps = ['tutorial'] \ No newline at end of file diff --git a/zds/tutorial/models.py b/zds/tutorial/models.py index caf53055f8..ea23a58e04 100644 --- a/zds/tutorial/models.py +++ b/zds/tutorial/models.py @@ -109,6 +109,7 @@ class Meta: related_name='last_note', verbose_name='Derniere note') is_locked = models.BooleanField('Est verrouillé', default=False) + js_support = models.BooleanField('Support du Javascript', default=False) def __unicode__(self): return self.title diff --git a/zds/tutorial/views.py b/zds/tutorial/views.py index 0fd9dfe6f5..7eab38fc67 100644 --- a/zds/tutorial/views.py +++ b/zds/tutorial/views.py @@ -887,6 +887,11 @@ def view_tutorial(request, tutorial_pk, tutorial_slug): form_ask_validation = AskValidationForm() form_valid = ValidForm() form_reject = RejectForm() + + if tutorial.js_support: + is_js = "js" + else: + is_js = "" return render_template("tutorial/tutorial/view.html", { "tutorial": mandata, "chapter": chapter, @@ -896,6 +901,7 @@ def view_tutorial(request, tutorial_pk, tutorial_slug): "formAskValidation": form_ask_validation, "formValid": form_valid, "formReject": form_reject, + "is_js": is_js }) @@ -1325,10 +1331,16 @@ def view_part( if not find: raise Http404 + if tutorial.js_support: + is_js = "js" + else: + is_js = "" + return render_template("tutorial/part/view.html", {"tutorial": mandata, "part": final_part, - "version": sha}) + "version": sha, + "is_js": is_js}) def view_part_online( @@ -1685,12 +1697,18 @@ def view_chapter( next_chapter = (chapter_tab[final_position + 1] if final_position + 1 < len(chapter_tab) else None) + if tutorial.js_support: + is_js = "js" + else: + is_js = "" + return render_template("tutorial/chapter/view.html", { "tutorial": mandata, "chapter": final_chapter, "prev": prev_chapter, "next": next_chapter, "version": sha, + "is_js": is_js }) @@ -3169,8 +3187,12 @@ def mep(tutorial, sha): target = u"\\\\?\{0}".format(target) html_file = open(target, "w") + if tutorial.js_support: + is_js = "js" + else: + is_js = "" if md_file_contenu is not None: - html_file.write(emarkdown(md_file_contenu)) + html_file.write(emarkdown(md_file_contenu, is_js)) html_file.close() # load markdown out diff --git a/zds/utils/templatetags/emarkdown.py b/zds/utils/templatetags/emarkdown.py index e4d50fee20..b6bc0718fd 100644 --- a/zds/utils/templatetags/emarkdown.py +++ b/zds/utils/templatetags/emarkdown.py @@ -66,7 +66,7 @@ def render_markdown(text, inline=False, js_support=False): @register.filter(needs_autoescape=False) -def emarkdown(text): +def emarkdown(text, js=""): """ Filter markdown text and render it to html. @@ -74,8 +74,9 @@ def emarkdown(text): :return: Equivalent html string. :rtype: str """ + is_js = (js == "js") try: - return mark_safe(render_markdown(text, inline=False)) + return mark_safe(render_markdown(text, inline=False, js_support=is_js)) except: return mark_safe(u'

{}

'.format(__MD_ERROR_PARSING)) @@ -91,7 +92,7 @@ def emarkdown_inline(text): """ try: - return mark_safe(render_markdown(text, inline=True, js_support=False)) + return mark_safe(render_markdown(text, inline=True)) except: return mark_safe(u'

{}

'.format(__MD_ERROR_PARSING)) From c9ce1c44c50a13a2cac33cc503784548d48a7a2a Mon Sep 17 00:00:00 2001 From: firm1 Date: Thu, 20 Nov 2014 18:55:17 +0100 Subject: [PATCH 011/152] =?UTF-8?q?am=C3=A9lioration=20de=20l'ihm=20pour?= =?UTF-8?q?=20staffs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../includes/sidebar_actions.part.html | 10 +++++++ templates/tutorial/tutorial/view.html | 8 ++++++ zds/article/forms.py | 26 ++++++++++++++++- zds/article/urls.py | 2 ++ zds/article/views.py | 28 ++++++++++++++++--- zds/tutorial/forms.py | 23 +++++++++++++++ zds/tutorial/urls.py | 4 ++- zds/tutorial/views.py | 20 ++++++++++++- 8 files changed, 114 insertions(+), 7 deletions(-) diff --git a/templates/article/includes/sidebar_actions.part.html b/templates/article/includes/sidebar_actions.part.html index 34102acef0..1e124b94d7 100644 --- a/templates/article/includes/sidebar_actions.part.html +++ b/templates/article/includes/sidebar_actions.part.html @@ -1,4 +1,6 @@ {% load i18n %} +{% load crispy_forms_tags %} + {% if user in authors.all or perms.article.change_article %} {% if article.sha_draft = version %} @@ -118,6 +120,14 @@

{% trans "Validation" %}

{% endif %}
+
  • + + JSFiddle + +
  • + {% if not article.sha_validation = None %} {% if validation.is_pending %} diff --git a/templates/tutorial/tutorial/view.html b/templates/tutorial/tutorial/view.html index f2eb7919a8..0624a3ebbf 100644 --- a/templates/tutorial/tutorial/view.html +++ b/templates/tutorial/tutorial/view.html @@ -362,6 +362,14 @@

    {% trans "Validation" %}

    {% endif %} +
  • + + JSFiddle + +
  • + {% if tutorial.on_line %}
  • diff --git a/zds/article/forms.py b/zds/article/forms.py index b52aa91511..f385ac8e09 100644 --- a/zds/article/forms.py +++ b/zds/article/forms.py @@ -2,8 +2,9 @@ from django.conf import settings +from crispy_forms.bootstrap import StrictButton from crispy_forms.helper import FormHelper -from crispy_forms.layout import Layout, Field, Hidden +from crispy_forms.layout import Layout, Field, Hidden, ButtonHolder from django import forms from django.core.urlresolvers import reverse @@ -172,3 +173,26 @@ def clean(self): u'caractères').format(settings.ZDS_APP['forum']['max_post_length'])]) return cleaned_data + + +class ActivJsForm(forms.Form): + + js_support = forms.BooleanField( + label='Cocher pour activer JSFiddle', + required=False, + initial=True + ) + + def __init__(self, *args, **kwargs): + super(ActivJsForm, self).__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.form_action = reverse('zds.article.views.activ_js') + self.helper.form_method = 'post' + + self.helper.layout = Layout( + Field('js_support'), + ButtonHolder( + StrictButton( + _(u'Valider'), + type='submit'),), + Hidden('article', '{{ article.pk }}'), ) \ No newline at end of file diff --git a/zds/article/urls.py b/zds/article/urls.py index 40c472c366..6363472b5e 100644 --- a/zds/article/urls.py +++ b/zds/article/urls.py @@ -45,6 +45,8 @@ 'zds.article.views.reservation'), url(r'^validation/historique/(?P\d+)/$', 'zds.article.views.history_validation'), + url(r'^activation_js/$', + 'zds.article.views.activ_js'), # Reactions url(r'^message/editer/$', diff --git a/zds/article/views.py b/zds/article/views.py index eddc5b282e..86df05dc36 100644 --- a/zds/article/views.py +++ b/zds/article/views.py @@ -43,7 +43,7 @@ from zds.utils.tutorials import get_sep, get_text_is_empty from zds.utils.templatetags.emarkdown import emarkdown -from .forms import ArticleForm, ReactionForm +from .forms import ArticleForm, ReactionForm, ActivJsForm from .models import Article, get_prev_article, get_next_article, Validation, \ Reaction, never_read, mark_read @@ -121,6 +121,7 @@ def view(request, article_pk, article_slug): is_js = "js" else: is_js = "" + form_js = ActivJsForm(initial={"js_support": article.js_support}) return render_template('article/member/view.html', { 'article': article_version, @@ -128,7 +129,8 @@ def view(request, article_pk, article_slug): 'tags': article.subcategory, 'version': sha, 'validation': validation, - 'is_js': is_js + 'is_js': is_js, + 'formJs': form_js, }) @@ -328,10 +330,12 @@ def edit(request): 'subcategory': request.POST.getlist('subcategory'), 'licence': licence }) + form_js = ActivJsForm(initial={"js_support": article.js_support}) return render_template('article/member/edit.html', { 'article': article, 'text': request.POST['text'], - 'form': form + 'form': form, + 'formJs': form_js }) form = ArticleForm(request.POST, request.FILES) @@ -387,8 +391,9 @@ def edit(request): 'licence': licence }) + form_js = ActivJsForm(initial={"js_support": article.js_support}) return render_template('article/member/edit.html', { - 'article': article, 'form': form + 'article': article, 'form': form, 'formJs': form_js }) @@ -1123,6 +1128,21 @@ def solve_alert(request): return redirect(reaction.get_absolute_url()) +@login_required +@require_POST +def activ_js(request): + + # only for staff + + if not request.user.has_perm("tutorial.change_tutorial"): + raise PermissionDenied + article = get_object_or_404(Article, pk=request.POST["article"]) + article.js_support = not (article.js_support) + article.save() + + return redirect(article.get_absolute_url()) + + @can_write_and_read_now @login_required def edit_reaction(request): diff --git a/zds/tutorial/forms.py b/zds/tutorial/forms.py index ab6c84ebd4..4db422cbb7 100644 --- a/zds/tutorial/forms.py +++ b/zds/tutorial/forms.py @@ -582,3 +582,26 @@ def __init__(self, *args, **kwargs): type='submit'),), Hidden('tutorial', '{{ tutorial.pk }}'), Hidden('version', '{{ version }}'), ) + + +class ActivJsForm(forms.Form): + + js_support = forms.BooleanField( + label='Cocher pour activer JSFiddle', + required=False, + initial=True + ) + + def __init__(self, *args, **kwargs): + super(ActivJsForm, self).__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.form_action = reverse('zds.tutorial.views.activ_js') + self.helper.form_method = 'post' + + self.helper.layout = Layout( + Field('js_support'), + ButtonHolder( + StrictButton( + _(u'Valider'), + type='submit'),), + Hidden('tutorial', '{{ tutorial.pk }}'), ) diff --git a/zds/tutorial/urls.py b/zds/tutorial/urls.py index c1b6736880..4e2c04b231 100644 --- a/zds/tutorial/urls.py +++ b/zds/tutorial/urls.py @@ -102,7 +102,8 @@ 'zds.tutorial.views.invalid_tutorial'), url(r'^validation/historique/(?P\d+)/$', 'zds.tutorial.views.history_validation'), - + url(r'^activation_js/$', + 'zds.tutorial.views.activ_js'), # Reactions url(r'^message/editer/$', 'zds.tutorial.views.edit_note'), @@ -115,3 +116,4 @@ url(r'^resolution_alerte/$', 'zds.tutorial.views.solve_alert'), ) + diff --git a/zds/tutorial/views.py b/zds/tutorial/views.py index 7eab38fc67..313ac6a32f 100644 --- a/zds/tutorial/views.py +++ b/zds/tutorial/views.py @@ -41,7 +41,7 @@ from lxml import etree from forms import TutorialForm, PartForm, ChapterForm, EmbdedChapterForm, \ - ExtractForm, ImportForm, ImportArchiveForm, NoteForm, AskValidationForm, ValidForm, RejectForm + ExtractForm, ImportForm, ImportArchiveForm, NoteForm, AskValidationForm, ValidForm, RejectForm, ActivJsForm from models import Tutorial, Part, Chapter, Extract, Validation, never_read, \ mark_read, Note from zds.gallery.models import Gallery, UserGallery, Image @@ -880,6 +880,8 @@ def view_tutorial(request, tutorial_pk, tutorial_slug): validation = Validation.objects.filter(tutorial__pk=tutorial.pk)\ .order_by("-date_proposition")\ .first() + form_js = ActivJsForm(initial={"js_support": tutorial.js_support}) + if tutorial.source: form_ask_validation = AskValidationForm(initial={"source": tutorial.source}) form_valid = ValidForm(initial={"source": tutorial.source}) @@ -899,6 +901,7 @@ def view_tutorial(request, tutorial_pk, tutorial_slug): "version": sha, "validation": validation, "formAskValidation": form_ask_validation, + "formJs": form_js, "formValid": form_valid, "formReject": form_reject, "is_js": is_js @@ -3397,6 +3400,21 @@ def solve_alert(request): return redirect(note.get_absolute_url()) +@login_required +@require_POST +def activ_js(request): + + # only for staff + + if not request.user.has_perm("tutorial.change_tutorial"): + raise PermissionDenied + tutorial = get_object_or_404(Tutorial, pk=request.POST["tutorial"]) + tutorial.js_support = not (tutorial.js_support) + tutorial.save() + + return redirect(tutorial.get_absolute_url()) + + @can_write_and_read_now @login_required def edit_note(request): From 528cbcf1e0c64205d68ec6ed74799d5f5d57f08a Mon Sep 17 00:00:00 2001 From: firm1 Date: Thu, 20 Nov 2014 18:57:13 +0100 Subject: [PATCH 012/152] correction pep8 --- zds/article/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zds/article/forms.py b/zds/article/forms.py index f385ac8e09..108254894c 100644 --- a/zds/article/forms.py +++ b/zds/article/forms.py @@ -195,4 +195,4 @@ def __init__(self, *args, **kwargs): StrictButton( _(u'Valider'), type='submit'),), - Hidden('article', '{{ article.pk }}'), ) \ No newline at end of file + Hidden('article', '{{ article.pk }}'), ) From fbd0407241ae579a16f706f651e2c1601de55d78 Mon Sep 17 00:00:00 2001 From: firm1 Date: Fri, 21 Nov 2014 19:50:51 +0100 Subject: [PATCH 013/152] prise en compte de la case --- zds/article/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zds/article/views.py b/zds/article/views.py index 86df05dc36..fb1d7ce86b 100644 --- a/zds/article/views.py +++ b/zds/article/views.py @@ -1137,7 +1137,7 @@ def activ_js(request): if not request.user.has_perm("tutorial.change_tutorial"): raise PermissionDenied article = get_object_or_404(Article, pk=request.POST["article"]) - article.js_support = not (article.js_support) + article.js_support = "js_support" in request.POST article.save() return redirect(article.get_absolute_url()) From 5ba2c86a6d447115f30abc72a507c31077c9db77 Mon Sep 17 00:00:00 2001 From: firm1 Date: Fri, 21 Nov 2014 19:51:26 +0100 Subject: [PATCH 014/152] prise en compte de la case --- zds/tutorial/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zds/tutorial/views.py b/zds/tutorial/views.py index 313ac6a32f..a8d5310358 100644 --- a/zds/tutorial/views.py +++ b/zds/tutorial/views.py @@ -3409,7 +3409,7 @@ def activ_js(request): if not request.user.has_perm("tutorial.change_tutorial"): raise PermissionDenied tutorial = get_object_or_404(Tutorial, pk=request.POST["tutorial"]) - tutorial.js_support = not (tutorial.js_support) + tutorial.js_support = "js_support" in request.POST tutorial.save() return redirect(tutorial.get_absolute_url()) From 172940d0a461f81cf1c334b085efb3849405804d Mon Sep 17 00:00:00 2001 From: Laville Augustin Date: Fri, 21 Nov 2014 22:35:21 +0100 Subject: [PATCH 015/152] =?UTF-8?q?Fix=20#1764=20:=20D=C3=A9calage=20sur?= =?UTF-8?q?=20la=20pages=20de=20tous=20les=20forums=20sur=20mobile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/scss/_all-supports.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/scss/_all-supports.scss b/assets/scss/_all-supports.scss index d6ae7b3b8f..af50a4cb2e 100644 --- a/assets/scss/_all-supports.scss +++ b/assets/scss/_all-supports.scss @@ -1797,7 +1797,7 @@ .forum-list { .group-title { - width: 100%; + max-width: 100%; margin-top: 30px !important; clear: both; border-bottom: 1px solid #CCC; @@ -3234,4 +3234,4 @@ form.topic-message { .screen, .wide { display: none; -} \ No newline at end of file +} From 79267a0e2d091a3fee74802adfd21bc4cc0bed69 Mon Sep 17 00:00:00 2001 From: Florianboux Date: Sun, 16 Nov 2014 20:44:00 +0100 Subject: [PATCH 016/152] =?UTF-8?q?=C3=89toffement=20de=20la=20doc=20sur?= =?UTF-8?q?=20les=20forums?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/sphinx/source/forum/forum.rst | 33 ++++++++++++++++++ .../source/forum/images/deplacement.png | Bin 0 -> 11573 bytes .../source/forum/images/modo_forum_sujet.png | Bin 0 -> 6194 bytes .../source/forum/images/modo_message.png | Bin 0 -> 4028 bytes doc/sphinx/source/forum/images/post_it.png | Bin 0 -> 15732 bytes .../source/forum/images/sujet_ferme.png | Bin 0 -> 3427 bytes 6 files changed, 33 insertions(+) create mode 100644 doc/sphinx/source/forum/images/deplacement.png create mode 100644 doc/sphinx/source/forum/images/modo_forum_sujet.png create mode 100644 doc/sphinx/source/forum/images/modo_message.png create mode 100644 doc/sphinx/source/forum/images/post_it.png create mode 100644 doc/sphinx/source/forum/images/sujet_ferme.png diff --git a/doc/sphinx/source/forum/forum.rst b/doc/sphinx/source/forum/forum.rst index c238505599..52072ab271 100644 --- a/doc/sphinx/source/forum/forum.rst +++ b/doc/sphinx/source/forum/forum.rst @@ -11,12 +11,45 @@ Le découpage des forums La modération des forums ======================== +Sur Zeste de Savoir, une modération des forums, autant sur les sujets, que sur les messages inclus, est faite par les membres possèdant un certain rang. Cette dernière, permet d'éviter tout débordement ou autre. + La modération des sujets ------------------------ +<<<<<<< HEAD +======= +Tout d'abord, il y a une posibilité de faire de la modération par sujet. Ici, cette modération s'effectue grâce aux liens se trouvant dans *sidebar* (zone se trouvant sur le côté gauche de la page). + ..figure:: images/modo_forum_sujet.png + :align: center + +Nous retrouvons ici, trois items : + +- **Fermer le sujet** : ici, le but est de fermer le sujet. Ce qui empêchera quiconque de poster dedans. Un cadena, apparaîtra aussi à côté du sujet sur la liste des sujets de la catégorie, et, l'encart suivant fera alors son apparition sur le sujet en lui-même : + + ..figure:: images/sujet_ferme.png + :align: center + +- **Marquer en post-it** : le sujet sera mis en post-it sur la catégorie dans laquelle il se trouve. Ce qui fait qu'il surpassera tout les autres sujets et cela, même si une réponse plus récente vient d'être posté dans un autre sujet. Il ne **pourra jamais se retrouver en-dessous des autres**. Lors de la mise en post-it, une épingle apparaît à côté du sujet dans la catégorie où il se trouve. + + ..figure:: images/post_it.png + :align: center + +- **Déplacer le sujet** : cet item permet de faire un déplacement de sujet, au cas-où un membre se serait trompé en postant. Cela évite qu'il ait à recréer un sujet et donc un doublon de celui-ci. Le déplacement, se fait via une modale : + + ..figure:: images/deplacement.png + :align: center + +>>>>>>> Étoffement de la doc sur les forums La modération des messages -------------------------- +Il est aussi possible d'effectuer la modération plus finement, en ciblant des messages en particulier dans des sujets. Cela se fait grâce aux différents liens qui se trouvent sur les messages : + + ..figure:: images/modo_message.png + :align: center + + + Les filtres sur les sujets ========================== diff --git a/doc/sphinx/source/forum/images/deplacement.png b/doc/sphinx/source/forum/images/deplacement.png new file mode 100644 index 0000000000000000000000000000000000000000..512309700d35d14544bd2938a102f1b35d01ed42 GIT binary patch literal 11573 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.BG-WwCR7$vkRJ1pZ$U&A778-gEy#s_p)p!MNW-c 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 literal 0 HcmV?d00001 diff --git a/doc/sphinx/source/forum/images/sujet_ferme.png b/doc/sphinx/source/forum/images/sujet_ferme.png new file mode 100644 index 0000000000000000000000000000000000000000..ed59bf83453e3235267181a563b6ac7d0c481e24 GIT binary patch literal 3427 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

    @T*g0SA$uaaMaAxYV+aa z`?tu<{WpJw3z?qcZwnTn|S&HTej?V%ADudqQ}UsyuJ-|;Oq(u_(N1p zrVVKsu!(4ZB{d8F(bMO7nadH2z&GD=zyDPtPW+O$^jG7O+N!^=r>ll4@-=KJZGKzC z#n|q8_)cDwOj9c)g{L~f^U%{IbRX45p=&0Gn~K!ALQQHqOf6r z95x=qr5>ATatJ)Ov93(Ve3m3^Oih^B@OT`g)H2qcCQ+F8xW98Yi#3?4}dLh$QU!dxnluB1v%9u{W#2=PWFwaCB zOol&sH2<4!3sx&^qnEU!JCW9O=*+rTKnHE1+2U;mL51P`3CRD6bxwP zcRiL>i*a429-2-t8s;CAWd8E%&AI;gyGLJ{OPu1bJ3&Xbh4I@x`TwDye?|j?e^+iF wUy=k%9eotPElmH18vmJG|5e8SUjs$^zr@77IOnAW+GgN*@rrGo&9w*r20?+WjQ{`u literal 0 HcmV?d00001 From a53aa846728a938a6ee6db433c80068f90aa5693 Mon Sep 17 00:00:00 2001 From: Florianboux Date: Sun, 16 Nov 2014 20:52:05 +0100 Subject: [PATCH 017/152] Correction sur les images --- doc/sphinx/source/forum/forum.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/sphinx/source/forum/forum.rst b/doc/sphinx/source/forum/forum.rst index 52072ab271..3bd15017e5 100644 --- a/doc/sphinx/source/forum/forum.rst +++ b/doc/sphinx/source/forum/forum.rst @@ -26,18 +26,18 @@ Nous retrouvons ici, trois items : - **Fermer le sujet** : ici, le but est de fermer le sujet. Ce qui empêchera quiconque de poster dedans. Un cadena, apparaîtra aussi à côté du sujet sur la liste des sujets de la catégorie, et, l'encart suivant fera alors son apparition sur le sujet en lui-même : - ..figure:: images/sujet_ferme.png - :align: center + .. figure:: images/sujet_ferme.png + :align: center - **Marquer en post-it** : le sujet sera mis en post-it sur la catégorie dans laquelle il se trouve. Ce qui fait qu'il surpassera tout les autres sujets et cela, même si une réponse plus récente vient d'être posté dans un autre sujet. Il ne **pourra jamais se retrouver en-dessous des autres**. Lors de la mise en post-it, une épingle apparaît à côté du sujet dans la catégorie où il se trouve. - ..figure:: images/post_it.png - :align: center + .. figure:: images/post_it.png + :align: center - **Déplacer le sujet** : cet item permet de faire un déplacement de sujet, au cas-où un membre se serait trompé en postant. Cela évite qu'il ait à recréer un sujet et donc un doublon de celui-ci. Le déplacement, se fait via une modale : - ..figure:: images/deplacement.png - :align: center + .. figure:: images/deplacement.png + :align: center >>>>>>> Étoffement de la doc sur les forums La modération des messages @@ -45,8 +45,8 @@ La modération des messages Il est aussi possible d'effectuer la modération plus finement, en ciblant des messages en particulier dans des sujets. Cela se fait grâce aux différents liens qui se trouvent sur les messages : - ..figure:: images/modo_message.png - :align: center + .. figure:: images/modo_message.png + :align: center From da5da35e7e49a03959de735e2615b1737948c88d Mon Sep 17 00:00:00 2001 From: firm1 Date: Fri, 21 Nov 2014 19:50:51 +0100 Subject: [PATCH 018/152] prise en compte de la case --- zds/article/views.py | 2 +- zds/tutorial/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/zds/article/views.py b/zds/article/views.py index 86df05dc36..fb1d7ce86b 100644 --- a/zds/article/views.py +++ b/zds/article/views.py @@ -1137,7 +1137,7 @@ def activ_js(request): if not request.user.has_perm("tutorial.change_tutorial"): raise PermissionDenied article = get_object_or_404(Article, pk=request.POST["article"]) - article.js_support = not (article.js_support) + article.js_support = "js_support" in request.POST article.save() return redirect(article.get_absolute_url()) diff --git a/zds/tutorial/views.py b/zds/tutorial/views.py index 313ac6a32f..a8d5310358 100644 --- a/zds/tutorial/views.py +++ b/zds/tutorial/views.py @@ -3409,7 +3409,7 @@ def activ_js(request): if not request.user.has_perm("tutorial.change_tutorial"): raise PermissionDenied tutorial = get_object_or_404(Tutorial, pk=request.POST["tutorial"]) - tutorial.js_support = not (tutorial.js_support) + tutorial.js_support = "js_support" in request.POST tutorial.save() return redirect(tutorial.get_absolute_url()) From 9a30a866d041f3c9d44790a6e0372458da16df97 Mon Sep 17 00:00:00 2001 From: Florianboux Date: Sat, 22 Nov 2014 15:35:29 +0100 Subject: [PATCH 019/152] =?UTF-8?q?R=C3=A9daction=20de=20la=20doc=20forum?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/sphinx/source/forum/forum.rst | 29 ++++++++++++------ doc/sphinx/source/forum/images/edit_modo.png | Bin 0 -> 10053 bytes .../source/forum/images/message_masque.png | Bin 0 -> 7211 bytes 3 files changed, 20 insertions(+), 9 deletions(-) create mode 100644 doc/sphinx/source/forum/images/edit_modo.png create mode 100644 doc/sphinx/source/forum/images/message_masque.png diff --git a/doc/sphinx/source/forum/forum.rst b/doc/sphinx/source/forum/forum.rst index 3bd15017e5..cffcd3be95 100644 --- a/doc/sphinx/source/forum/forum.rst +++ b/doc/sphinx/source/forum/forum.rst @@ -16,39 +16,50 @@ Sur Zeste de Savoir, une modération des forums, autant sur les sujets, que sur La modération des sujets ------------------------ -<<<<<<< HEAD -======= Tout d'abord, il y a une posibilité de faire de la modération par sujet. Ici, cette modération s'effectue grâce aux liens se trouvant dans *sidebar* (zone se trouvant sur le côté gauche de la page). - ..figure:: images/modo_forum_sujet.png - :align: center + + .. figure:: images/modo_forum_sujet.png + :align: center Nous retrouvons ici, trois items : - **Fermer le sujet** : ici, le but est de fermer le sujet. Ce qui empêchera quiconque de poster dedans. Un cadena, apparaîtra aussi à côté du sujet sur la liste des sujets de la catégorie, et, l'encart suivant fera alors son apparition sur le sujet en lui-même : .. figure:: images/sujet_ferme.png - :align: center + :align: center - **Marquer en post-it** : le sujet sera mis en post-it sur la catégorie dans laquelle il se trouve. Ce qui fait qu'il surpassera tout les autres sujets et cela, même si une réponse plus récente vient d'être posté dans un autre sujet. Il ne **pourra jamais se retrouver en-dessous des autres**. Lors de la mise en post-it, une épingle apparaît à côté du sujet dans la catégorie où il se trouve. .. figure:: images/post_it.png - :align: center + :align: center - **Déplacer le sujet** : cet item permet de faire un déplacement de sujet, au cas-où un membre se serait trompé en postant. Cela évite qu'il ait à recréer un sujet et donc un doublon de celui-ci. Le déplacement, se fait via une modale : .. figure:: images/deplacement.png - :align: center + :align: center ->>>>>>> Étoffement de la doc sur les forums La modération des messages -------------------------- Il est aussi possible d'effectuer la modération plus finement, en ciblant des messages en particulier dans des sujets. Cela se fait grâce aux différents liens qui se trouvent sur les messages : .. figure:: images/modo_message.png - :align: center + :align: center + +Ici, les différents choses que vous pouvez faire, sont : + +- **Masquer** : cela, rend la lecture du message impossible par les autres membres. Mais l'emplacement du message reste présent, comme le montre la capture ci-dessous. Vous pouvez également contaster sur cette capture, que vous avez, avec un certain rang, la possibilité de démasquer le message. Avec ce dernier, vous avez la possiblité de masquer tout les messages des forums. Mais un membre, peut masquer de lui-même, ces propres messages. + + .. figure:: images/message_masque.png + :align: center + +- **Éditer** : en cliquant ici, vous accèderez à l'interface d'édition des messages. En tant que modérateur, vous pouvez éditer tout les messages des forums, dès lors, cette encart apparaît : + .. figure:: images/edit_modo.png + :align: center +- **Signaler** : cela permet d'envoyer une demande d'intervention à l'équipe modératrice du site. Ce bouton, n'est pas un résultant de votr rang, tout les membres le possèdes. +- **Citer** : permet de citer un message lors de la rédaction d'une réponse. Ce bouton, n'est pas un résultant de votre rang. Les filtres sur les sujets ========================== diff --git a/doc/sphinx/source/forum/images/edit_modo.png b/doc/sphinx/source/forum/images/edit_modo.png new file mode 100644 index 0000000000000000000000000000000000000000..872cc86a21c32112b1a46168e10c88ac67c3f96b GIT binary patch literal 10053 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 literal 0 HcmV?d00001 diff --git a/doc/sphinx/source/forum/images/message_masque.png b/doc/sphinx/source/forum/images/message_masque.png new file mode 100644 index 0000000000000000000000000000000000000000..b36de30021f31aebac2ae0bfa9991bc2fe0c99e7 GIT binary patch literal 7211 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 literal 0 HcmV?d00001 From 3affe8ab9fef1d95bbbc2eb70d38d9fa7e67c94e Mon Sep 17 00:00:00 2001 From: Laville Augustin Date: Sat, 22 Nov 2014 17:46:13 +0100 Subject: [PATCH 020/152] =?UTF-8?q?Fix=20#1787=20:=20Mauvais=20lien=20de?= =?UTF-8?q?=20citation=20lorsqu'on=20est=20d=C3=A9j=C3=A0=20en=20train=20d?= =?UTF-8?q?e=20citer=20quelqu'un?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/tutorial/comment/new.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/tutorial/comment/new.html b/templates/tutorial/comment/new.html index be7c919ff9..ad89850bc5 100644 --- a/templates/tutorial/comment/new.html +++ b/templates/tutorial/comment/new.html @@ -52,7 +52,7 @@

    {{ tutorial.description }}

    {% endcaptureas %} {% captureas cite_link %} - {% url "zds.tutorial.views.answer" %}?tutorial={{ topic.pk }}&cite={{ message.pk }} + {% url "zds.tutorial.views.answer" %}?tutorial={{ tutorial.pk }}&cite={{ message.pk }} {% endcaptureas %} {% captureas upvote_link %} @@ -71,4 +71,4 @@

    {{ tutorial.description }}

    {% endfor %} -{% endblock %} \ No newline at end of file +{% endblock %} From 2d863769162c45014fdf20dab1974a14c486dd42 Mon Sep 17 00:00:00 2001 From: Alex-D Date: Thu, 9 Oct 2014 20:00:06 +0200 Subject: [PATCH 021/152] Submit bleu + bleu footer = bleu header --- assets/scss/_all-supports.scss | 6 +++--- assets/scss/_form.scss | 6 +++--- assets/scss/_wide.scss | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/assets/scss/_all-supports.scss b/assets/scss/_all-supports.scss index d6ae7b3b8f..345ee7608f 100644 --- a/assets/scss/_all-supports.scss +++ b/assets/scss/_all-supports.scss @@ -3043,9 +3043,9 @@ form.topic-message { padding: 20px 0; } .page-footer { - background: darken($primary, 10%); - height: 50px; - line-height: 50px; + background: $primary; + height: 40px; + line-height: 40px; border-top: 3px solid $secondary; font-size: 14px; font-size: 1.4rem; diff --git a/assets/scss/_form.scss b/assets/scss/_form.scss index 29b8bb8239..80626ce173 100644 --- a/assets/scss/_form.scss +++ b/assets/scss/_form.scss @@ -33,7 +33,7 @@ } - + label { display: block; color: #555; @@ -143,13 +143,13 @@ [type=submit], .btn-submit { color: #FFF; - background: $upvote; + background: $primary; &:not([disabled]):hover, &:not([disabled]):focus, &:not(.disabled):hover, &:not(.disabled):focus { - background: lighten($upvote, 5%); + background: lighten($upvote, 7%); } &.disabled.submitted { diff --git a/assets/scss/_wide.scss b/assets/scss/_wide.scss index e282234d24..db4f8cf0ed 100644 --- a/assets/scss/_wide.scss +++ b/assets/scss/_wide.scss @@ -15,7 +15,7 @@ .main-container { - min-height: calc(100% - 146px); + min-height: calc(100% - 136px); } From b5621923d518989124d6bb793b3db3fd2208b2d0 Mon Sep 17 00:00:00 2001 From: Alex-D Date: Thu, 9 Oct 2014 20:09:38 +0200 Subject: [PATCH 022/152] =?UTF-8?q?Am=C3=A9liore=20la=20zone=20de=20recher?= =?UTF-8?q?che=20du=20header?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/scss/_all-supports.scss | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/assets/scss/_all-supports.scss b/assets/scss/_all-supports.scss index 345ee7608f..18956c7096 100644 --- a/assets/scss/_all-supports.scss +++ b/assets/scss/_all-supports.scss @@ -530,8 +530,10 @@ &:hover, &:focus { - outline: none; - background-color: rgba(255, 255, 255, .75); + background-color: #FFF; + } + &:focus { + outline-color: $secondary; } } input { @@ -563,7 +565,6 @@ } } .search-more { - // TODO : mettre l'icône en background plutôt que tout ça display: block; float: left; height: 40px; @@ -574,13 +575,15 @@ font-weight: bold; text-decoration: none; font-size: 24px; - background: rgba(255, 255, 255, 1); - color: #084561; + background: #FFF; + color: $primary; transition: background $transition-duration ease; &:hover, &:focus { - background: rgba(255, 255, 255, .7); + background: $secondary; + color: #FFF; + outline: none; } } } From 1584de4fa95139a634e60ca6b33138e4919140ee Mon Sep 17 00:00:00 2001 From: Alex-D Date: Sat, 25 Oct 2014 03:36:48 +0200 Subject: [PATCH 023/152] Utilisation totale des variables dans Sass --- assets/scss/_all-supports.scss | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/assets/scss/_all-supports.scss b/assets/scss/_all-supports.scss index 18956c7096..610e35ad6b 100644 --- a/assets/scss/_all-supports.scss +++ b/assets/scss/_all-supports.scss @@ -30,7 +30,7 @@ &:hover, &:focus { - color: #084561; + color: $primary; background-color: #fff; text-decoration: none; } @@ -105,7 +105,7 @@ .header-container header { - background: #084561; + background: $primary; border-bottom: 3px solid $secondary; a, @@ -440,7 +440,7 @@ &:nth-child(2n+1) { &, form button { - background-color: #084561; + background-color: $primary; } } } @@ -993,7 +993,7 @@ a { position: relative; - color: #084561; + color: $primary; transition: all $transition-duration ease; &:hover, @@ -1224,7 +1224,7 @@ font-size: 2.2rem; line-height: 38px; line-height: 3.8rem; - color: #084561; + color: $primary; font-weight: normal; border-bottom: 1px solid #F8AD32; margin-top: 0; @@ -1483,7 +1483,7 @@ &:hover, &:focus { background: #DDD; - color: #084561; + color: $primary; } } @@ -1516,7 +1516,7 @@ display: block; text-align: center; text-decoration: none; - color: #084561; + color: $primary; min-width: 45px; height: 40px; line-height: 40px; From 3b309e6cd6c4a5915f0190a035fb63eb66efe774 Mon Sep 17 00:00:00 2001 From: Alex-D Date: Sat, 25 Oct 2014 03:58:58 +0200 Subject: [PATCH 024/152] Remet le bouton submit en vert --- assets/scss/_form.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/scss/_form.scss b/assets/scss/_form.scss index 80626ce173..da8453d250 100644 --- a/assets/scss/_form.scss +++ b/assets/scss/_form.scss @@ -143,7 +143,7 @@ [type=submit], .btn-submit { color: #FFF; - background: $primary; + background: $upvote; &:not([disabled]):hover, &:not([disabled]):focus, From 6bf8c13a18a2c762fc09670b12cff22d21ad778a Mon Sep 17 00:00:00 2001 From: Eskimon Date: Sun, 23 Nov 2014 12:24:05 +0100 Subject: [PATCH 025/152] Icone pour le form support js --- .../article/includes/sidebar_actions.part.html | 14 ++++++++++---- templates/tutorial/tutorial/view.html | 12 +++++++++--- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/templates/article/includes/sidebar_actions.part.html b/templates/article/includes/sidebar_actions.part.html index 1e124b94d7..8cbd8f35fe 100644 --- a/templates/article/includes/sidebar_actions.part.html +++ b/templates/article/includes/sidebar_actions.part.html @@ -112,7 +112,7 @@

    {% trans "Validation" %}

    - {% trans "Envoyer un MP" %} + {% trans "Envoyer un MP" %} {% if authors.all|length > 1 %} {% trans "aux auteurs" %} {% else %} @@ -121,9 +121,15 @@

    {% trans "Validation" %}

  • - - JSFiddle - + {% if is_js %} + + {% trans "Désactiver JSFiddle" %} + + {% else %} + + {% trans "Activer JSFiddle" %} + + {% endif %}
  • From 31def4f1afbb8154c4ecd2f15fe62180f0fb4c5a Mon Sep 17 00:00:00 2001 From: firm1 Date: Mon, 1 Dec 2014 17:55:51 +0100 Subject: [PATCH 079/152] exclure le forum beta des sitemaps --- zds/urls.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zds/urls.py b/zds/urls.py index f4b4d99a93..df322177ab 100644 --- a/zds/urls.py +++ b/zds/urls.py @@ -55,12 +55,12 @@ def location(self, article): priority=0.7 ), 'forums': GenericSitemap( - {'queryset': Forum.objects.filter(group__isnull=True)}, + {'queryset': Forum.objects.filter(group__isnull=True).exclude(pk=settings.ZDS_APP['forum']['beta_forum_id'])}, changefreq='yearly', priority=0.7 ), 'topics': GenericSitemap( - {'queryset': Topic.objects.filter(is_locked=False, forum__group__isnull=True), 'date_field': 'pubdate'}, + {'queryset': Topic.objects.filter(is_locked=False, forum__group__isnull=True).exclude(pk=settings.ZDS_APP['forum']['beta_forum_id']), 'date_field': 'pubdate'}, changefreq='hourly', priority=0.7 ), From d80fdba4d576ec00c9dc6d28ce3fd96c75651de0 Mon Sep 17 00:00:00 2001 From: Situphen Date: Tue, 2 Dec 2014 19:20:51 +0100 Subject: [PATCH 080/152] Hotfix pour la PR #1788 --- zds/forum/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zds/forum/views.py b/zds/forum/views.py index 813f6a87e7..e3a969d883 100644 --- a/zds/forum/views.py +++ b/zds/forum/views.py @@ -669,7 +669,7 @@ def edit_post(request): # check if the form is valid form = TopicForm(request.POST) if not form.is_valid() and g_topic: - return render_template("forum/post/edit.html", { + return render(request, "forum/post/edit.html", { "post": post, "topic": post.topic, "text": post.text, From 1e29b96aa66b79ac7fdec14a6b80fd5dcf9deb7d Mon Sep 17 00:00:00 2001 From: Situphen Date: Wed, 3 Dec 2014 19:00:54 +0100 Subject: [PATCH 081/152] Raccourci la date d'un message du forum sur mobile --- assets/scss/_mobile-tablet.scss | 4 ++++ assets/scss/_wide.scss | 4 ++++ templates/misc/message.part.html | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/assets/scss/_mobile-tablet.scss b/assets/scss/_mobile-tablet.scss index 714d6dae1b..35dafc9f5d 100644 --- a/assets/scss/_mobile-tablet.scss +++ b/assets/scss/_mobile-tablet.scss @@ -566,6 +566,10 @@ .date { float: right; + + .long-date { + display: none; + } } } diff --git a/assets/scss/_wide.scss b/assets/scss/_wide.scss index cee4b478ad..73c53f2439 100644 --- a/assets/scss/_wide.scss +++ b/assets/scss/_wide.scss @@ -496,6 +496,10 @@ white-space: nowrap; overflow: hidden; } + + .message-metadata .date .short-date { + display: none; + } } } diff --git a/templates/misc/message.part.html b/templates/misc/message.part.html index 064c2d386b..75f6cd698c 100644 --- a/templates/misc/message.part.html +++ b/templates/misc/message.part.html @@ -148,7 +148,12 @@ From 3c30ea76a10b847459511f296a8abe2e38860da0 Mon Sep 17 00:00:00 2001 From: firm1 Date: Wed, 3 Dec 2014 23:27:02 +0100 Subject: [PATCH 082/152] Update urls.py --- zds/urls.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zds/urls.py b/zds/urls.py index df322177ab..aa2fff8fa2 100644 --- a/zds/urls.py +++ b/zds/urls.py @@ -60,7 +60,9 @@ def location(self, article): priority=0.7 ), 'topics': GenericSitemap( - {'queryset': Topic.objects.filter(is_locked=False, forum__group__isnull=True).exclude(pk=settings.ZDS_APP['forum']['beta_forum_id']), 'date_field': 'pubdate'}, + {'queryset': Topic.objects.filter(is_locked=False, + forum__group__isnull=True).exclude(forum__pk=settings.ZDS_APP['forum']['beta_forum_id']), + 'date_field': 'pubdate'}, changefreq='hourly', priority=0.7 ), From f687a7844ea0cdc9353e9c028bdd3e5bf8a98d94 Mon Sep 17 00:00:00 2001 From: Pedro Emanuel de Castro Faria Salgado Date: Tue, 2 Dec 2014 22:16:43 -0700 Subject: [PATCH 083/152] dependances pour developement devrait etre sur un fichier different (requirements-dev.txt). ajouter requirements-dev.txt sur .travis.yml. correction anglais sur requirements.txt. mise a jour des instructions d'installation (requirements-dev.txt). --- .travis.yml | 2 +- README.md | 2 +- doc/sphinx/source/install/install-linux.rst | 2 +- doc/sphinx/source/install/install-os-x.rst | 2 +- doc/sphinx/source/install/install-windows.rst | 2 +- requirements-dev.txt | 5 +++++ requirements.txt | 9 +-------- 7 files changed, 11 insertions(+), 13 deletions(-) create mode 100644 requirements-dev.txt diff --git a/.travis.yml b/.travis.yml index db94f8e6bd..40342afae8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,7 +55,7 @@ install: - sudo apt-get --reinstall install -qq language-pack-fr # Python dependencies - - travis_retry pip install -r requirements.txt + - travis_retry pip install -r requirements.txt -r requirements-dev.txt - travis_retry pip install coveralls - travis_retry pip install MySQL-python diff --git a/README.md b/README.md index 37d76211a3..98082a391a 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Elles sont reportées essentiellement dans le [bugtraker](https://github.com/zes Après avoir mis à jour votre dépot, vous devez executer les commandes suivantes (depuis la racine de votre projet) pour mettre à jour les dépendances. ```console -pip install --upgrade -r requirements.txt +pip install --upgrade -r requirements.txt -r requirements-dev.txt python manage.py migrate ``` diff --git a/doc/sphinx/source/install/install-linux.rst b/doc/sphinx/source/install/install-linux.rst index f308735dbd..284ae9bb08 100644 --- a/doc/sphinx/source/install/install-linux.rst +++ b/doc/sphinx/source/install/install-linux.rst @@ -67,7 +67,7 @@ Une fois dans votre environnement python (``source ../bin/activate`` si vous uti .. sourcecode:: bash - pip install --upgrade -r requirements.txt + pip install --upgrade -r requirements.txt -r requirements-dev.txt python manage.py syncdb python manage.py migrate python manage.py runserver diff --git a/doc/sphinx/source/install/install-os-x.rst b/doc/sphinx/source/install/install-os-x.rst index ee6d311f8e..4969d7ea8f 100644 --- a/doc/sphinx/source/install/install-os-x.rst +++ b/doc/sphinx/source/install/install-os-x.rst @@ -66,7 +66,7 @@ Installation de toutes les dépendances .. sourcecode:: bash - pip install --upgrade -r requirements.txt + pip install --upgrade -r requirements.txt -r requirements-dev.txt npm install gulp build diff --git a/doc/sphinx/source/install/install-windows.rst b/doc/sphinx/source/install/install-windows.rst index 38f3d53e46..bcc6e9387e 100644 --- a/doc/sphinx/source/install/install-windows.rst +++ b/doc/sphinx/source/install/install-windows.rst @@ -40,7 +40,7 @@ Suite de l'installation - Dans la console PowerShell via l'environnement zdsenv installez les dépendances. - ``easy_install lxml`` - - ``pip install -r requirements.txt`` + - ``pip install -r requirements.txt -r requirements-dev.txt`` - ``python manage.py syncdb`` - ``python manage.py migrate`` - ``python manage.py runserver`` diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000000..18eb7007b0 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,5 @@ +PyYAML==3.11 +django-debug-toolbar==1.2.2 +flake8==2.2.5 +autopep8==1.0.4 +sphinx==1.2.3 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 076007b156..6ac89bdd60 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -# Implicit dependencies (facutlative dependencies of dependencies) +# Implicit dependencies (optional dependencies of dependencies) pysolr==3.2.0 pygments==2.0.1 python-social-auth==0.1.26 @@ -20,10 +20,3 @@ pillow==2.6.1 https://github.com/zestedesavoir/GitPython/archive/0.3.2-RC2.zip https://github.com/zestedesavoir/Python-ZMarkdown/archive/2.4.1-zds.11.zip easy-thumbnails==2.2 - -# Development tools -PyYAML==3.11 -django-debug-toolbar==1.2.2 -flake8==2.2.5 -autopep8==1.0.4 -sphinx==1.2.3 From 681cf5f70f7f236637292ae04fb6bb3e5c2ade03 Mon Sep 17 00:00:00 2001 From: firm1 Date: Thu, 4 Dec 2014 17:46:45 +0100 Subject: [PATCH 084/152] =?UTF-8?q?initialisation=20des=20param=C3=A8tres?= =?UTF-8?q?=20pandoc=20pour=20pdf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit variabilisation de l'export pdf variabilisation du générateur correction du bug --- zds/settings.py | 1 + zds/tutorial/views.py | 7 +------ zds/utils/management/commands/pdf_generator.py | 9 ++------- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/zds/settings.py b/zds/settings.py index 24fc5abde0..8067caba88 100644 --- a/zds/settings.py +++ b/zds/settings.py @@ -254,6 +254,7 @@ SERVE = False PANDOC_LOC = '' +PANDOC_PDF_PARAM = "--latex-engine=xelatex --template=../../assets/tex/template.tex -s -S -N --toc -V documentclass=scrbook -V lang=francais -V mainfont=Merriweather -V monofont=\"Andale Mono\" -V fontsize=12pt -V geometry:margin=1in " # LOG PATH FOR PANDOC LOGGING PANDOC_LOG = './pandoc.log' PANDOC_LOG_STATE = False diff --git a/zds/tutorial/views.py b/zds/tutorial/views.py index 1a225e9bd9..e242d149b0 100644 --- a/zds/tutorial/views.py +++ b/zds/tutorial/views.py @@ -3218,12 +3218,7 @@ def mep(tutorial, sha): + os.path.join(prod_path, tutorial.slug) + ".md -o " + os.path.join(prod_path, tutorial.slug) + ".html" + pandoc_debug_str) - os.system(settings.PANDOC_LOC + "pandoc " + "--latex-engine=xelatex " - + "--template=../../assets/tex/template.tex " + "-s " + "-S " - + "-N " + "--toc " + "-V documentclass=scrbook " - + "-V lang=francais " + "-V mainfont=Merriweather " - + "-V monofont=\"Andale Mono\" " + "-V fontsize=12pt " - + "-V geometry:margin=1in " + os.system(settings.PANDOC_LOC + "pandoc " + settings.PANDOC_PDF_PARAM + " " + os.path.join(prod_path, tutorial.slug) + ".md " + "-o " + os.path.join(prod_path, tutorial.slug) + ".pdf" + pandoc_debug_str) diff --git a/zds/utils/management/commands/pdf_generator.py b/zds/utils/management/commands/pdf_generator.py index fa01de3078..0110f8bf92 100644 --- a/zds/utils/management/commands/pdf_generator.py +++ b/zds/utils/management/commands/pdf_generator.py @@ -35,13 +35,8 @@ def handle(self, *args, **options): for tutorial in tutorials: prod_path = tutorial.get_prod_path(tutorial.sha_public) - os.system("cd "+prod_path + " && " + settings.PANDOC_LOC + "pandoc " + "--latex-engine=xelatex " - + "--template=../../assets/tex/template.tex " + "-s " + "-S " - + "-N " + "--toc " + "-V documentclass=scrbook " - + "-V lang=francais " + "-V mainfont=Merriweather " - + "-V monofont=\"Andale Mono\" " + "-V fontsize=12pt " - + "-V geometry:margin=1in " + os.system("cd "+prod_path + " && " + settings.PANDOC_LOC + "pandoc " + settings.PANDOC_PDF_PARAM + " " + os.path.join(prod_path, tutorial.slug) + ".md " + "-o " + os.path.join(prod_path, tutorial.slug) + ".pdf" + pandoc_debug_str) - self.stdout.write(u"----> {} : OK".format(tutorial.title)) + self.stdout.write(u"----> {}".format(tutorial.title)) From 15b2d33ea2f3ed264117e3ad95c3abdd8a17828c Mon Sep 17 00:00:00 2001 From: Pedro Emanuel de Castro Faria Salgado Date: Thu, 4 Dec 2014 21:24:23 -0700 Subject: [PATCH 085/152] #1846 correction des try/except:. mp/views.py : nouvelle condition except pour le cas ou la conversation n'existe pas. --- zds/article/models.py | 4 ++-- zds/article/views.py | 4 ++-- zds/forum/views.py | 24 ++++++++++++------------ zds/gallery/views.py | 2 ++ zds/member/views.py | 2 +- zds/mp/views.py | 11 +++++++++-- zds/pages/views.py | 2 +- zds/tutorial/models.py | 4 ++-- zds/tutorial/views.py | 12 +++++++----- zds/utils/context_processor.py | 2 +- zds/utils/tutorials.py | 8 ++++---- 11 files changed, 43 insertions(+), 32 deletions(-) diff --git a/zds/article/models.py b/zds/article/models.py index 7579894aaa..2bccbb1a3f 100644 --- a/zds/article/models.py +++ b/zds/article/models.py @@ -13,10 +13,10 @@ try: import ujson as json_reader -except: +except ImportError: try: import simplejson as json_reader - except: + except ImportError: import json as json_reader import json as json_writer diff --git a/zds/article/views.py b/zds/article/views.py index 9d4cc39446..21d2c10c2f 100644 --- a/zds/article/views.py +++ b/zds/article/views.py @@ -4,10 +4,10 @@ from operator import attrgetter try: import ujson as json_reader -except: +except ImportError: try: import simplejson as json_reader - except: + except ImportError: import json as json_reader import json as json_writer diff --git a/zds/forum/views.py b/zds/forum/views.py index e3a969d883..40f4797406 100644 --- a/zds/forum/views.py +++ b/zds/forum/views.py @@ -143,7 +143,7 @@ def topic(request, topic_pk, topic_slug): if "page" in request.GET: try: page_nbr = int(request.GET["page"]) - except: + except (KeyError, ValueError): # problem in variable format raise Http404 else: @@ -226,7 +226,7 @@ def new(request): try: forum_pk = request.GET["forum"] - except: + except KeyError: # problem in variable format raise Http404 forum = get_object_or_404(Forum, pk=forum_pk) @@ -341,7 +341,7 @@ def move_topic(request): raise PermissionDenied try: topic_pk = request.GET["sujet"] - except: + except KeyError: # problem in variable format raise Http404 forum = get_object_or_404(Forum, pk=request.POST["forum"]) @@ -372,13 +372,13 @@ def edit(request): try: topic_pk = request.POST["topic"] - except: + except KeyError: # problem in variable format raise Http404 if "page" in request.POST: try: page = int(request.POST["page"]) - except: + except (KeyError, ValueError): # problem in variable format raise Http404 else: @@ -413,7 +413,7 @@ def edit(request): if "move" in data: try: forum_pk = int(request.POST["move_target"]) - except: + except (KeyError, ValueError): # problem in variable format raise Http404 forum = get_object_or_404(Forum, pk=forum_pk) @@ -437,7 +437,7 @@ def answer(request): try: topic_pk = request.GET["sujet"] - except: + except KeyError: # problem in variable format raise Http404 @@ -591,7 +591,7 @@ def edit_post(request): try: post_pk = request.GET["message"] - except: + except KeyError: # problem in variable format raise Http404 post = get_object_or_404(Post, pk=post_pk) @@ -727,7 +727,7 @@ def useful_post(request): try: post_pk = request.GET["message"] - except: + except KeyError: # problem in variable format raise Http404 post = get_object_or_404(Post, pk=post_pk) @@ -754,7 +754,7 @@ def unread_post(request): try: post_pk = request.GET["message"] - except: + except KeyError: # problem in variable format raise Http404 post = get_object_or_404(Post, pk=post_pk) @@ -791,7 +791,7 @@ def like_post(request): try: post_pk = request.GET["message"] - except: + except KeyError: # problem in variable format raise Http404 resp = {} @@ -839,7 +839,7 @@ def dislike_post(request): try: post_pk = request.GET["message"] - except: + except KeyError: # problem in variable format raise Http404 resp = {} diff --git a/zds/gallery/views.py b/zds/gallery/views.py index 9e941e576d..170485d590 100644 --- a/zds/gallery/views.py +++ b/zds/gallery/views.py @@ -269,6 +269,8 @@ def delete_image(request): try: img = Image.objects.get(pk=request.POST["image"], gallery=gal) img.delete() + except KeyError: + pass except: pass elif "delete_multi" in request.POST: diff --git a/zds/member/views.py b/zds/member/views.py index a1081532fa..cfd9b25d09 100644 --- a/zds/member/views.py +++ b/zds/member/views.py @@ -1111,7 +1111,7 @@ def modify_karma(request): note.comment = request.POST["warning"] try: note.value = int(request.POST["points"]) - except: + except (KeyError, ValueError): note.value = 0 note.save() diff --git a/zds/mp/views.py b/zds/mp/views.py index 12e41a5ce1..81b52db402 100644 --- a/zds/mp/views.py +++ b/zds/mp/views.py @@ -488,10 +488,14 @@ def leave(request): @require_POST @transaction.atomic def add_participant(request): - ptopic = get_object_or_404(PrivateTopic, pk=request.POST['topic_pk']) + try: + ptopic = get_object_or_404(PrivateTopic, pk=request.POST['topic_pk']) + except KeyError: + messages.warning( + request, _(u'La conversation que vous avez essayé d\'utiliser n\'existe pas.')) # check if user is the author of topic - if not ptopic.author == request.user: + if ptopic is not None and not ptopic.author == request.user: raise PermissionDenied try: @@ -509,6 +513,9 @@ def add_participant(request): messages.success( request, _(u'Le membre a bien été ajouté à la conversation.')) + except KeyError: + messages.warning( + request, _(u'Le membre que vous avez essayé d\'ajouter n\'existe pas.')) except: messages.warning( request, _(u'Le membre que vous avez essayé d\'ajouter n\'existe pas.')) diff --git a/zds/pages/views.py b/zds/pages/views.py index aa904f171d..fff7004529 100644 --- a/zds/pages/views.py +++ b/zds/pages/views.py @@ -41,7 +41,7 @@ def home(request): try: with open(os.path.join(SITE_ROOT, 'quotes.txt'), 'r') as fh: quote = random.choice(fh.readlines()) - except: + except IOError: quote = settings.ZDS_APP['site']['slogan'] return render(request, 'home.html', { diff --git a/zds/tutorial/models.py b/zds/tutorial/models.py index 8809df03db..7b3c5a129b 100644 --- a/zds/tutorial/models.py +++ b/zds/tutorial/models.py @@ -4,10 +4,10 @@ import shutil try: import ujson as json_reader -except: +except ImportError: try: import simplejson as json_reader - except: + except ImportError: import json as json_reader import json as json_writer diff --git a/zds/tutorial/views.py b/zds/tutorial/views.py index dc63a941e3..209fbe01b3 100644 --- a/zds/tutorial/views.py +++ b/zds/tutorial/views.py @@ -8,10 +8,10 @@ from urlparse import urlparse, parse_qs try: import ujson as json_reader -except: +except ImportError: try: import simplejson as json_reader - except: + except ImportError: import json as json_reader import json as json_writer @@ -2350,7 +2350,7 @@ def upload_images(images, tutorial): except IOError: try: os.makedirs(ph_temp) - except: + except OSError: pass zfile.close() return mapping @@ -3139,8 +3139,9 @@ def mep(tutorial, sha): if os.path.isdir(del_path): try: shutil.rmtree(del_path) - except: + except OSError: shutil.rmtree(u"\\\\?\{0}".format(del_path)) + # WARNING: this can throw another OSError shutil.copytree(tutorial.get_path(), prod_path) repo.head.reset(commit=sha, index=True, working_tree=True) @@ -3238,8 +3239,9 @@ def un_mep(tutorial): if os.path.isdir(del_path): try: shutil.rmtree(del_path) - except: + except OSError: shutil.rmtree(u"\\\\?\{0}".format(del_path)) + # WARNING: this can throw another OSError @can_write_and_read_now diff --git a/zds/utils/context_processor.py b/zds/utils/context_processor.py index 565c7ce062..cf860396c4 100644 --- a/zds/utils/context_processor.py +++ b/zds/utils/context_processor.py @@ -15,7 +15,7 @@ def get_git_version(): commit = repo.head.commit.hexsha v = u'{0}/{1}'.format(branch, commit[:7]) return {'name': v, 'url': u'{}/tree/{}'.format(settings.ZDS_APP['site']['repository'], commit)} - except: + except (KeyError, TypeError): return {'name': '', 'url': ''} diff --git a/zds/utils/tutorials.py b/zds/utils/tutorials.py index 3dfc505a66..3260f992ab 100644 --- a/zds/utils/tutorials.py +++ b/zds/utils/tutorials.py @@ -101,7 +101,7 @@ def get_blob(tree, chemin): if os.path.abspath(bl.path) == os.path.abspath(chemin): data = bl.data_stream.read() return data.decode('utf-8') - except: + except (OSError, IOError): return "" if len(tree.trees) > 0: for tr in tree.trees: @@ -435,10 +435,10 @@ def import_archive(request): import os try: import ujson as json_reader - except: + except ImportError: try: import simplejson as json_reader - except: + except ImportError: import json as json_reader archive = request.FILES["file"] @@ -501,7 +501,7 @@ def import_archive(request): except IOError: try: os.makedirs(ph_dest) - except: + except OSError: pass zfile.close() From f34d8394402176ab21bdc4cb97ce14030cdbf9d1 Mon Sep 17 00:00:00 2001 From: Situphen Date: Thu, 4 Dec 2014 19:09:18 +0100 Subject: [PATCH 086/152] =?UTF-8?q?Met=20des=20liens=20de=20partage=20pour?= =?UTF-8?q?=20les=20r=C3=A9seaux=20sociaux?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/locale/en/LC_MESSAGES/django.po | 373 ++++++++++--------- templates/article/view.html | 2 + templates/misc/social_buttons.part.html | 35 ++ templates/tutorial/chapter/view_online.html | 2 + templates/tutorial/part/view_online.html | 2 + templates/tutorial/tutorial/view_online.html | 2 + 6 files changed, 236 insertions(+), 180 deletions(-) create mode 100644 templates/misc/social_buttons.part.html diff --git a/conf/locale/en/LC_MESSAGES/django.po b/conf/locale/en/LC_MESSAGES/django.po index 0457efbd46..0215a776ad 100644 --- a/conf/locale/en/LC_MESSAGES/django.po +++ b/conf/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-11-29 13:50+0100\n" +"POT-Creation-Date: 2014-12-07 17:48+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -444,7 +444,7 @@ msgstr "" #: templates/member/profile.html.py:456 templates/member/profile.html:471 #: templates/member/profile.html.py:489 templates/member/profile.html:510 #: templates/member/profile.html.py:525 templates/member/profile.html:541 -#: templates/misc/message.part.html:66 templates/misc/message.part.html:129 +#: templates/misc/message.part.html:73 templates/misc/message.part.html:136 #: templates/misc/validation.part.html:40 templates/mp/index.html:104 #: templates/mp/topic/index.html:126 templates/tutorial/chapter/view.html:168 #: templates/tutorial/includes/chapter.part.html:46 @@ -458,7 +458,7 @@ msgstr "" #: templates/tutorial/tutorial/view.html:391 #: templates/tutorial/tutorial/view.html:442 #: templates/tutorial/tutorial/view.html:472 -#: templates/tutorial/tutorial/view_online.html:194 zds/tutorial/forms.py:509 +#: templates/tutorial/tutorial/view_online.html:194 zds/tutorial/forms.py:516 msgid "Confirmer" msgstr "" @@ -497,7 +497,7 @@ msgstr "" #: templates/tutorial/member/index.html:11 #: templates/tutorial/member/index.html:31 #: templates/tutorial/member/index.html:51 -#: templates/tutorial/member/index.html:122 zds/tutorial/views.py:490 +#: templates/tutorial/member/index.html:122 zds/tutorial/views.py:489 msgid "En validation" msgstr "" @@ -508,7 +508,7 @@ msgstr "" #: templates/article/includes/sidebar_actions.part.html:7 #: templates/article/member/edit.html:26 templates/article/member/edit.html:33 -#: templates/misc/message.part.html:77 +#: templates/misc/message.part.html:84 #: templates/tutorial/includes/chapter.part.html:90 #: templates/tutorial/tutorial/view.html:163 msgid "Éditer" @@ -626,12 +626,12 @@ msgid "Annuler la réservation" msgstr "" #: templates/article/includes/sidebar_actions.part.html:159 -#: zds/tutorial/forms.py:552 +#: zds/tutorial/forms.py:559 msgid "Publier" msgstr "" #: templates/article/includes/sidebar_actions.part.html:170 -#: templates/tutorial/tutorial/view.html:424 zds/tutorial/forms.py:581 +#: templates/tutorial/tutorial/view.html:424 zds/tutorial/forms.py:588 msgid "Rejeter" msgstr "" @@ -1102,7 +1102,8 @@ msgstr "" msgid "Ne plus suivre" msgstr "" -#: templates/forum/base.html:119 templates/mp/index.html:36 +#: templates/forum/base.html:119 templates/misc/message.part.html:49 +#: templates/mp/index.html:36 msgid "Non-lu" msgstr "" @@ -1113,8 +1114,8 @@ msgstr "" #: templates/forum/base.html:133 #: templates/forum/includes/topic_row.part.html:78 -#: templates/member/profile.html:82 templates/misc/message.part.html:182 -#: templates/misc/message.part.html:194 templates/mp/index.html:72 +#: templates/member/profile.html:82 templates/misc/message.part.html:189 +#: templates/misc/message.part.html:201 templates/mp/index.html:72 msgid "par" msgstr "" @@ -1170,7 +1171,7 @@ msgstr "" msgid "Sujets sans réponse" msgstr "" -#: templates/forum/category/forum.html:69 templates/forum/topic/index.html:127 +#: templates/forum/category/forum.html:69 templates/forum/topic/index.html:131 #: templates/forum/topic/new.html:9 templates/forum/topic/new.html.py:15 #: templates/forum/topic/new.html:31 msgid "Nouveau sujet" @@ -1292,52 +1293,52 @@ msgstr "" msgid "L'auteur de ce sujet a trouvé une solution à son problème" msgstr "" -#: templates/forum/topic/index.html:145 +#: templates/forum/topic/index.html:149 msgid "Marquer comme non résolu" msgstr "" -#: templates/forum/topic/index.html:147 +#: templates/forum/topic/index.html:151 msgid "Marquer comme résolu" msgstr "" -#: templates/forum/topic/index.html:164 +#: templates/forum/topic/index.html:168 msgid "Ne plus suivre ce sujet" msgstr "" -#: templates/forum/topic/index.html:166 +#: templates/forum/topic/index.html:170 msgid "Suivre ce sujet" msgstr "" -#: templates/forum/topic/index.html:181 +#: templates/forum/topic/index.html:185 msgid "Ne plus être notifié par courriel" msgstr "" -#: templates/forum/topic/index.html:183 +#: templates/forum/topic/index.html:187 msgid "Être notifié par courriel" msgstr "" -#: templates/forum/topic/index.html:195 templates/member/profile.html:420 +#: templates/forum/topic/index.html:199 templates/member/profile.html:420 #: templates/member/profile.html.py:421 msgid "Modération" msgstr "" -#: templates/forum/topic/index.html:207 +#: templates/forum/topic/index.html:211 msgid "Ouvrir le sujet" msgstr "" -#: templates/forum/topic/index.html:209 +#: templates/forum/topic/index.html:213 msgid "Fermer le sujet" msgstr "" -#: templates/forum/topic/index.html:225 +#: templates/forum/topic/index.html:229 msgid "Enlever du post-it" msgstr "" -#: templates/forum/topic/index.html:227 +#: templates/forum/topic/index.html:231 msgid "Marquer en post-it" msgstr "" -#: templates/forum/topic/index.html:234 +#: templates/forum/topic/index.html:238 msgid "Déplacer le sujet" msgstr "" @@ -2027,45 +2028,45 @@ msgstr "" msgid "Auteur du sujet" msgstr "" -#: templates/misc/message.part.html:50 +#: templates/misc/message.part.html:57 msgid "Masquer" msgstr "" -#: templates/misc/message.part.html:56 +#: templates/misc/message.part.html:63 msgid "Pour quelle raison souhaitez vous masquer ce message" msgstr "" -#: templates/misc/message.part.html:61 +#: templates/misc/message.part.html:68 msgid "" "Attention, en masquant ce message, vous ne pourrez plus l'afficher vous " "même. Êtes vous certains de vouloir le faire" msgstr "" -#: templates/misc/message.part.html:86 templates/misc/message.part.html:96 +#: templates/misc/message.part.html:93 templates/misc/message.part.html:103 msgid "Signaler" msgstr "" -#: templates/misc/message.part.html:91 +#: templates/misc/message.part.html:98 msgid "Pour quelle raison signalez-vous ce message" msgstr "" -#: templates/misc/message.part.html:93 +#: templates/misc/message.part.html:100 msgid "Minimum 3 caractères pour signaler" msgstr "" -#: templates/misc/message.part.html:106 +#: templates/misc/message.part.html:113 msgid "Citer" msgstr "" -#: templates/misc/message.part.html:113 +#: templates/misc/message.part.html:120 msgid "Voir" msgstr "" -#: templates/misc/message.part.html:118 +#: templates/misc/message.part.html:125 msgid "Démasquer" msgstr "" -#: templates/misc/message.part.html:123 +#: templates/misc/message.part.html:130 #, python-format msgid "" "\n" @@ -2074,27 +2075,27 @@ msgid "" " " msgstr "" -#: templates/misc/message.part.html:152 +#: templates/misc/message.part.html:159 msgid "Reprise du dernier message de la page précédente" msgstr "" -#: templates/misc/message.part.html:157 +#: templates/misc/message.part.html:164 msgid "Cette réponse a aidé l'auteur du sujet" msgstr "" -#: templates/misc/message.part.html:173 +#: templates/misc/message.part.html:180 msgid "Masqué par" msgstr "" -#: templates/misc/message.part.html:180 +#: templates/misc/message.part.html:187 msgid "Édité" msgstr "" -#: templates/misc/message.part.html:198 +#: templates/misc/message.part.html:205 msgid "Résoudre" msgstr "" -#: templates/misc/message.part.html:204 +#: templates/misc/message.part.html:211 msgid "Résoudre l'alerte" msgstr "" @@ -2157,6 +2158,15 @@ msgstr "" msgid "Prévisualisation de votre message" msgstr "" +#: templates/misc/social_buttons.part.html:3 +#: templates/misc/social_buttons.part.html:4 +msgid "Partager" +msgstr "" + +#: templates/misc/social_buttons.part.html:31 +msgid "Envoyer par mail" +msgstr "" + #: templates/misc/validation.part.html:19 msgid "Réservé par" msgstr "" @@ -3665,16 +3675,16 @@ msgstr "" msgid "Description" msgstr "" -#: zds/article/forms.py:39 zds/tutorial/forms.py:310 +#: zds/article/forms.py:39 zds/tutorial/forms.py:317 msgid "Texte" msgstr "" -#: zds/article/forms.py:42 zds/article/forms.py:126 zds/forum/forms.py:39 +#: zds/article/forms.py:42 zds/article/forms.py:133 zds/forum/forms.py:39 #: zds/forum/forms.py:106 zds/mp/forms.py:45 zds/mp/forms.py:102 -#: zds/tutorial/forms.py:62 zds/tutorial/forms.py:72 zds/tutorial/forms.py:147 -#: zds/tutorial/forms.py:157 zds/tutorial/forms.py:210 -#: zds/tutorial/forms.py:220 zds/tutorial/forms.py:314 -#: zds/tutorial/forms.py:423 +#: zds/tutorial/forms.py:62 zds/tutorial/forms.py:72 zds/tutorial/forms.py:154 +#: zds/tutorial/forms.py:164 zds/tutorial/forms.py:217 +#: zds/tutorial/forms.py:227 zds/tutorial/forms.py:321 +#: zds/tutorial/forms.py:430 msgid "Votre message au format Markdown." msgstr "" @@ -3688,45 +3698,48 @@ msgid "" "pas à en demander une nouvelle lors de la validation !" msgstr "" -#: zds/article/forms.py:61 zds/tutorial/forms.py:95 -msgid "Licence de votre publication" +#: zds/article/forms.py:62 zds/tutorial/forms.py:96 +#, python-brace-format +msgid "" +"Licence de votre publication (En savoir plus sur " +"les licences et {2})" msgstr "" -#: zds/article/forms.py:68 zds/tutorial/forms.py:102 zds/tutorial/forms.py:163 -#: zds/tutorial/forms.py:226 zds/tutorial/forms.py:276 -#: zds/tutorial/forms.py:320 +#: zds/article/forms.py:75 zds/tutorial/forms.py:109 zds/tutorial/forms.py:170 +#: zds/tutorial/forms.py:233 zds/tutorial/forms.py:283 +#: zds/tutorial/forms.py:327 msgid "Message de suivi" msgstr "" -#: zds/article/forms.py:73 zds/tutorial/forms.py:107 zds/tutorial/forms.py:168 -#: zds/tutorial/forms.py:231 zds/tutorial/forms.py:281 -#: zds/tutorial/forms.py:325 +#: zds/article/forms.py:80 zds/tutorial/forms.py:114 zds/tutorial/forms.py:175 +#: zds/tutorial/forms.py:238 zds/tutorial/forms.py:288 +#: zds/tutorial/forms.py:332 msgid "Un résumé de vos ajouts et modifications" msgstr "" -#: zds/article/forms.py:148 zds/forum/forms.py:125 zds/tutorial/forms.py:445 +#: zds/article/forms.py:155 zds/forum/forms.py:125 zds/tutorial/forms.py:452 msgid "" "Vous venez de poster. Merci de patienter au moins 15 minutes entre deux " "messages consécutifs afin de limiter le flood." msgstr "" -#: zds/article/forms.py:155 +#: zds/article/forms.py:162 msgid "Cet article est verrouillé." msgstr "" -#: zds/article/forms.py:166 zds/forum/forms.py:144 zds/tutorial/forms.py:463 +#: zds/article/forms.py:173 zds/forum/forms.py:144 zds/tutorial/forms.py:470 msgid "Vous devez écrire une réponse !" msgstr "" -#: zds/article/forms.py:172 zds/forum/forms.py:95 zds/forum/forms.py:150 -#: zds/tutorial/forms.py:469 +#: zds/article/forms.py:179 zds/forum/forms.py:95 zds/forum/forms.py:150 +#: zds/tutorial/forms.py:476 #, python-brace-format msgid "Ce message est trop long, il ne doit pas dépasser {0} caractères" msgstr "" -#: zds/article/forms.py:196 zds/forum/forms.py:174 zds/member/forms.py:601 -#: zds/pages/forms.py:66 zds/tutorial/forms.py:187 zds/tutorial/forms.py:251 -#: zds/tutorial/forms.py:301 zds/tutorial/forms.py:605 +#: zds/article/forms.py:203 zds/forum/forms.py:174 zds/member/forms.py:601 +#: zds/pages/forms.py:66 zds/tutorial/forms.py:194 zds/tutorial/forms.py:258 +#: zds/tutorial/forms.py:308 zds/tutorial/forms.py:612 msgid "Valider" msgstr "" @@ -3809,13 +3822,13 @@ msgstr "" msgid "Utiliser comme avatar" msgstr "" -#: zds/gallery/views.py:113 +#: zds/gallery/views.py:112 msgid "" "La galerie '{}' ne peut pas être supprimée car elle est liée à un tutoriel " "existant." msgstr "" -#: zds/gallery/views.py:208 zds/gallery/views.py:364 +#: zds/gallery/views.py:207 zds/gallery/views.py:364 msgid "" "Votre image est beaucoup trop lourde, réduisez sa taille à moins de {} Kio avant de l'envoyer." @@ -3976,70 +3989,70 @@ msgstr "" msgid "Compte actif" msgstr "" -#: zds/member/views.py:112 +#: zds/member/views.py:111 msgid "# Le tutoriel présenté par ce topic n'existe plus." msgstr "" -#: zds/member/views.py:198 +#: zds/member/views.py:197 msgid "Messages postés par période" msgstr "" -#: zds/member/views.py:282 +#: zds/member/views.py:281 msgid "Lecture Seule" msgstr "" -#: zds/member/views.py:284 +#: zds/member/views.py:283 msgid "" "Vous ne pouvez plus poster dans les forums, ni dans les commentaires " "d'articles et de tutoriels." msgstr "" -#: zds/member/views.py:287 +#: zds/member/views.py:286 msgid "Lecture Seule Temporaire" msgstr "" -#: zds/member/views.py:293 +#: zds/member/views.py:292 #, python-brace-format msgid "" "Vous ne pouvez plus poster dans les forums, ni dans les commentaires " "d'articles et de tutoriels pendant {0} jours." msgstr "" -#: zds/member/views.py:297 +#: zds/member/views.py:296 msgid "Ban Temporaire" msgstr "" -#: zds/member/views.py:303 +#: zds/member/views.py:302 msgid "Vous ne pouvez plus vous connecter sur {} pendant {} jours." msgstr "" -#: zds/member/views.py:309 +#: zds/member/views.py:308 msgid "Ban définitif" msgstr "" -#: zds/member/views.py:312 +#: zds/member/views.py:311 msgid "vous ne pouvez plus vous connecter sur {}." msgstr "" -#: zds/member/views.py:316 +#: zds/member/views.py:315 msgid "Autorisation d'écrire" msgstr "" -#: zds/member/views.py:319 +#: zds/member/views.py:318 msgid "" "Vous pouvez désormais poster sur les forums, dans les commentaires " "d'articles et tutoriels." msgstr "" -#: zds/member/views.py:322 +#: zds/member/views.py:321 msgid "Autorisation de se connecter" msgstr "" -#: zds/member/views.py:325 +#: zds/member/views.py:324 msgid "vous pouvez désormais vous connecter sur le site." msgstr "" -#: zds/member/views.py:332 +#: zds/member/views.py:331 #, python-brace-format msgid "" "Bonjour **{0}**,\n" @@ -4056,7 +4069,7 @@ msgid "" "Cordialement, L'équipe {4}." msgstr "" -#: zds/member/views.py:345 +#: zds/member/views.py:344 #, python-brace-format msgid "" "Bonjour **{0}**,\n" @@ -4072,51 +4085,51 @@ msgid "" "Cordialement, L'équipe {5}." msgstr "" -#: zds/member/views.py:362 +#: zds/member/views.py:361 msgid "Sanction" msgstr "" -#: zds/member/views.py:456 zds/member/views.py:505 +#: zds/member/views.py:455 zds/member/views.py:504 msgid "Le profil a correctement été mis à jour." msgstr "" -#: zds/member/views.py:502 zds/member/views.py:539 zds/member/views.py:563 +#: zds/member/views.py:501 zds/member/views.py:538 zds/member/views.py:562 msgid "Une erreur est survenue." msgstr "" -#: zds/member/views.py:541 +#: zds/member/views.py:540 msgid "L'avatar a correctement été mis à jour." msgstr "" -#: zds/member/views.py:559 +#: zds/member/views.py:558 msgid "Le mot de passe a bien été modifié." msgstr "" -#: zds/member/views.py:633 +#: zds/member/views.py:632 msgid "" "Vous n'êtes pas autorisé à vous connecter sur le site, vous avez été banni " "par un modérateur." msgstr "" -#: zds/member/views.py:638 +#: zds/member/views.py:637 msgid "" "Vous n'avez pas encore activé votre compte, vous devez le faire pour pouvoir " "vous connecter sur le site. Regardez dans vos mails : {}." msgstr "" -#: zds/member/views.py:644 +#: zds/member/views.py:643 msgid "Les identifiants fournis ne sont pas valides." msgstr "" -#: zds/member/views.py:700 zds/member/views.py:879 +#: zds/member/views.py:699 zds/member/views.py:878 msgid "{} - Confirmation d'inscription" msgstr "" -#: zds/member/views.py:741 +#: zds/member/views.py:740 msgid "{} - Mot de passe oublié" msgstr "" -#: zds/member/views.py:814 +#: zds/member/views.py:813 #, python-brace-format msgid "" "Bonjour **{0}**,\n" @@ -4146,64 +4159,64 @@ msgid "" "Clem'" msgstr "" -#: zds/member/views.py:849 +#: zds/member/views.py:848 msgid "Bienvenue sur {}" msgstr "" -#: zds/member/views.py:850 +#: zds/member/views.py:849 msgid "Le manuel du nouveau membre" msgstr "" -#: zds/member/views.py:940 +#: zds/member/views.py:939 #, python-brace-format msgid "Le tutoriel a bien été lié au membre {0}." msgstr "" -#: zds/member/views.py:972 +#: zds/member/views.py:971 #, python-brace-format msgid "Le tutoriel a bien été retiré au membre {0}." msgstr "" -#: zds/member/views.py:1000 +#: zds/member/views.py:999 #, python-brace-format msgid "{0} appartient maintenant au groupe {1}." msgstr "" -#: zds/member/views.py:1005 +#: zds/member/views.py:1004 #, python-brace-format msgid "{0} n'appartient maintenant plus au groupe {1}." msgstr "" -#: zds/member/views.py:1018 +#: zds/member/views.py:1017 #, python-brace-format msgid "{0} n'appartient (plus ?) à aucun groupe." msgstr "" -#: zds/member/views.py:1024 +#: zds/member/views.py:1023 #, python-brace-format msgid "{0} est maintenant super-utilisateur." msgstr "" -#: zds/member/views.py:1028 +#: zds/member/views.py:1027 msgid "Un super-utilisateur ne peux pas se retirer des super-utilisateurs." msgstr "" -#: zds/member/views.py:1032 +#: zds/member/views.py:1031 #, python-brace-format msgid "{0} n'est maintenant plus super-utilisateur." msgstr "" -#: zds/member/views.py:1037 +#: zds/member/views.py:1036 #, python-brace-format msgid "{0} est maintenant activé." msgstr "" -#: zds/member/views.py:1041 +#: zds/member/views.py:1040 #, python-brace-format msgid "{0} est désactivé." msgstr "" -#: zds/member/views.py:1048 +#: zds/member/views.py:1047 #, python-brace-format msgid "" "Bonjour {0},\n" @@ -4211,23 +4224,23 @@ msgid "" "Un administrateur vient de modifier les groupes auxquels vous appartenez. \n" msgstr "" -#: zds/member/views.py:1052 +#: zds/member/views.py:1051 msgid "" "Voici la liste des groupes dont vous faites dorénavant partie :\n" "\n" msgstr "" -#: zds/member/views.py:1056 +#: zds/member/views.py:1055 msgid "* Vous ne faites partie d'aucun groupe" msgstr "" -#: zds/member/views.py:1059 +#: zds/member/views.py:1058 msgid "" "Vous avez aussi rejoint le rang des super utilisateurs. N'oubliez pas, un " "grand pouvoir entraine de grandes responsabiltiés !" msgstr "" -#: zds/member/views.py:1064 +#: zds/member/views.py:1063 msgid "Modification des groupes" msgstr "" @@ -4307,13 +4320,13 @@ msgstr "" msgid "Sélectionnez le logo du tutoriel (max. {} Ko)" msgstr "" -#: zds/tutorial/forms.py:58 zds/tutorial/forms.py:143 -#: zds/tutorial/forms.py:206 +#: zds/tutorial/forms.py:58 zds/tutorial/forms.py:150 +#: zds/tutorial/forms.py:213 msgid "Introduction" msgstr "" -#: zds/tutorial/forms.py:68 zds/tutorial/forms.py:153 -#: zds/tutorial/forms.py:216 +#: zds/tutorial/forms.py:68 zds/tutorial/forms.py:160 +#: zds/tutorial/forms.py:223 msgid "Conclusion" msgstr "" @@ -4323,93 +4336,93 @@ msgid "" "pas à en demander une nouvelle lors de la validation !" msgstr "" -#: zds/tutorial/forms.py:190 zds/tutorial/forms.py:254 +#: zds/tutorial/forms.py:197 zds/tutorial/forms.py:261 msgid "Ajouter et continuer" msgstr "" -#: zds/tutorial/forms.py:200 +#: zds/tutorial/forms.py:207 #, python-brace-format msgid "Selectionnez le logo du tutoriel (max. {0} Ko)" msgstr "" -#: zds/tutorial/forms.py:267 +#: zds/tutorial/forms.py:274 msgid "Sélectionnez une image" msgstr "" -#: zds/tutorial/forms.py:293 +#: zds/tutorial/forms.py:300 msgid "Contenu" msgstr "" -#: zds/tutorial/forms.py:346 +#: zds/tutorial/forms.py:353 msgid "Sélectionnez le tutoriel à importer" msgstr "" -#: zds/tutorial/forms.py:350 +#: zds/tutorial/forms.py:357 msgid "Fichier zip contenant les images du tutoriel" msgstr "" -#: zds/tutorial/forms.py:362 +#: zds/tutorial/forms.py:369 msgid "Importer le .tuto" msgstr "" -#: zds/tutorial/forms.py:377 +#: zds/tutorial/forms.py:384 msgid "Le fichier doit être au format .tuto" msgstr "" -#: zds/tutorial/forms.py:384 +#: zds/tutorial/forms.py:391 msgid "Le fichier doit être au format .zip" msgstr "" -#: zds/tutorial/forms.py:391 +#: zds/tutorial/forms.py:398 msgid "Sélectionnez l'archive de votre tutoriel" msgstr "" -#: zds/tutorial/forms.py:396 +#: zds/tutorial/forms.py:403 msgid "Tutoriel vers lequel vous souhaitez importer votre archive" msgstr "" -#: zds/tutorial/forms.py:411 +#: zds/tutorial/forms.py:418 msgid "Importer l'archive" msgstr "" -#: zds/tutorial/forms.py:452 +#: zds/tutorial/forms.py:459 msgid "Ce tutoriel est verrouillé." msgstr "" -#: zds/tutorial/forms.py:484 +#: zds/tutorial/forms.py:491 msgid "Commentaire pour votre demande." msgstr "" -#: zds/tutorial/forms.py:494 zds/tutorial/forms.py:537 +#: zds/tutorial/forms.py:501 zds/tutorial/forms.py:544 msgid "URL de la version originale" msgstr "" -#: zds/tutorial/forms.py:522 +#: zds/tutorial/forms.py:529 msgid "Commentaire de publication." msgstr "" -#: zds/tutorial/forms.py:528 +#: zds/tutorial/forms.py:535 msgid "Version majeure ?" msgstr "" -#: zds/tutorial/forms.py:565 +#: zds/tutorial/forms.py:572 msgid "Commentaire de rejet." msgstr "" -#: zds/tutorial/views.py:188 +#: zds/tutorial/views.py:187 msgid "Le tutoriel n'est plus sous réserve." msgstr "" -#: zds/tutorial/views.py:196 +#: zds/tutorial/views.py:195 #, python-brace-format msgid "Le tutoriel a bien été réservé par {0}." msgstr "" -#: zds/tutorial/views.py:298 +#: zds/tutorial/views.py:297 msgid "Le tutoriel a bien été refusé." msgstr "" -#: zds/tutorial/views.py:302 +#: zds/tutorial/views.py:301 #, python-brace-format msgid "" "Désolé, le zeste **{0}** n'a malheureusement pas passé l’étape de " @@ -4423,20 +4436,20 @@ msgid "" "demander plus de détail si tout cela te semble injuste ou manque de clarté." msgstr "" -#: zds/tutorial/views.py:318 +#: zds/tutorial/views.py:317 #, python-brace-format msgid "Refus de Validation : {0}" msgstr "" -#: zds/tutorial/views.py:328 +#: zds/tutorial/views.py:327 msgid "Vous devez avoir réservé ce tutoriel pour pouvoir le refuser." msgstr "" -#: zds/tutorial/views.py:370 +#: zds/tutorial/views.py:369 msgid "Le tutoriel a bien été validé." msgstr "" -#: zds/tutorial/views.py:375 +#: zds/tutorial/views.py:374 #, python-brace-format msgid "" "Félicitations ! Le zeste [{0}]({1}) a été publié par [{2}]({3}) ! Les " @@ -4446,20 +4459,20 @@ msgid "" "abandonné !" msgstr "" -#: zds/tutorial/views.py:390 +#: zds/tutorial/views.py:389 #, python-brace-format msgid "Publication : {0}" msgstr "" -#: zds/tutorial/views.py:400 +#: zds/tutorial/views.py:399 msgid "Vous devez avoir réservé ce tutoriel pour pouvoir le valider." msgstr "" -#: zds/tutorial/views.py:431 +#: zds/tutorial/views.py:430 msgid "Le tutoriel a bien été dépublié." msgstr "" -#: zds/tutorial/views.py:481 +#: zds/tutorial/views.py:480 #, python-brace-format msgid "" "Bonjour {0},Le tutoriel *{1}* que tu as réservé a été mis à jour en zone de " @@ -4469,30 +4482,30 @@ msgid "" "> Merci" msgstr "" -#: zds/tutorial/views.py:489 +#: zds/tutorial/views.py:488 #, python-brace-format msgid "Mise à jour de tuto : {0}" msgstr "" -#: zds/tutorial/views.py:499 +#: zds/tutorial/views.py:498 msgid "Votre demande de validation a été envoyée à l'équipe." msgstr "" -#: zds/tutorial/views.py:539 +#: zds/tutorial/views.py:538 #, python-brace-format msgid "Le tutoriel {0} a bien été supprimé." msgstr "" -#: zds/tutorial/views.py:556 +#: zds/tutorial/views.py:555 msgid "Vous ne faites plus partie des rédacteurs de ce tutoriel." msgstr "" -#: zds/tutorial/views.py:591 +#: zds/tutorial/views.py:590 #, python-brace-format msgid "L'auteur {0} a bien été ajouté à la rédaction du tutoriel." msgstr "" -#: zds/tutorial/views.py:597 +#: zds/tutorial/views.py:596 #, python-brace-format msgid "" "Bonjour **{0}**,\n" @@ -4504,17 +4517,17 @@ msgid "" "Tu peux maintenant commencer à rédiger !" msgstr "" -#: zds/tutorial/views.py:611 +#: zds/tutorial/views.py:610 #, python-brace-format msgid "Ajout en tant qu'auteur : {0}" msgstr "" -#: zds/tutorial/views.py:643 +#: zds/tutorial/views.py:642 #, python-brace-format msgid "L'auteur {0} a bien été retiré du tutoriel." msgstr "" -#: zds/tutorial/views.py:649 +#: zds/tutorial/views.py:648 #, python-brace-format msgid "" "Bonjour **{0}**,\n" @@ -4523,12 +4536,12 @@ msgid "" "pas publié, tu ne pourra plus y accéder.\n" msgstr "" -#: zds/tutorial/views.py:660 +#: zds/tutorial/views.py:659 #, python-brace-format msgid "Suppression des auteurs : {0}" msgstr "" -#: zds/tutorial/views.py:675 +#: zds/tutorial/views.py:674 #, python-brace-format msgid "" "Bonjour à tous,\n" @@ -4547,12 +4560,12 @@ msgid "" "Merci d'avance pour votre aide" msgstr "" -#: zds/tutorial/views.py:690 +#: zds/tutorial/views.py:689 #, python-brace-format msgid "[beta][tutoriel]{0}" msgstr "" -#: zds/tutorial/views.py:698 +#: zds/tutorial/views.py:697 msgid "" "Bonjour {},\n" "\n" @@ -4564,12 +4577,12 @@ msgid "" "est accessible [ici]({})" msgstr "" -#: zds/tutorial/views.py:710 +#: zds/tutorial/views.py:709 #, python-brace-format msgid "Tutoriel en beta : {0}" msgstr "" -#: zds/tutorial/views.py:717 +#: zds/tutorial/views.py:716 #, python-brace-format msgid "" "Bonjour,\n" @@ -4583,15 +4596,15 @@ msgid "" "Merci pour vos relectures" msgstr "" -#: zds/tutorial/views.py:726 +#: zds/tutorial/views.py:725 msgid "La BETA sur ce tutoriel est bien activée." msgstr "" -#: zds/tutorial/views.py:728 +#: zds/tutorial/views.py:727 msgid "La BETA sur ce tutoriel n'a malheureusement pas pu être activée." msgstr "" -#: zds/tutorial/views.py:737 +#: zds/tutorial/views.py:736 #, python-brace-format msgid "" "Bonjour à tous,\n" @@ -4610,7 +4623,7 @@ msgid "" "Merci d'avance pour votre aide" msgstr "" -#: zds/tutorial/views.py:759 +#: zds/tutorial/views.py:758 #, python-brace-format msgid "" "Bonjour, !\n" @@ -4624,22 +4637,22 @@ msgid "" "Merci pour vos relectures" msgstr "" -#: zds/tutorial/views.py:767 +#: zds/tutorial/views.py:766 msgid "La BETA sur ce tutoriel a bien été mise à jour." msgstr "" -#: zds/tutorial/views.py:769 +#: zds/tutorial/views.py:768 msgid "La BETA sur ce tutoriel n'a malheureusement pas pu être mise à jour." msgstr "" -#: zds/tutorial/views.py:777 +#: zds/tutorial/views.py:776 msgid "" "Désactivation de la beta du tutoriel **{}**\n" "\n" "Pour plus d'informations envoyez moi un message privé." msgstr "" -#: zds/tutorial/views.py:781 +#: zds/tutorial/views.py:780 msgid "La BETA sur ce tutoriel a bien été désactivée." msgstr "" @@ -4685,55 +4698,55 @@ msgstr "" msgid "Déplacement de l'extrait {}" msgstr "" -#: zds/tutorial/views.py:2673 +#: zds/tutorial/views.py:2674 msgid "Modification du tutoriel : «{}» {} {}" msgstr "" -#: zds/tutorial/views.py:2680 +#: zds/tutorial/views.py:2681 msgid "Création du tutoriel «{}» {} {}" msgstr "" -#: zds/tutorial/views.py:2727 +#: zds/tutorial/views.py:2728 msgid "Suppresion de la partie : «{}»" msgstr "" -#: zds/tutorial/views.py:2733 +#: zds/tutorial/views.py:2734 msgid "Modification de la partie «{}» {} {}" msgstr "" -#: zds/tutorial/views.py:2738 +#: zds/tutorial/views.py:2739 msgid "Création de la partie «{}» {} {}" msgstr "" -#: zds/tutorial/views.py:2792 +#: zds/tutorial/views.py:2793 msgid "Suppresion du chapitre : «{}»" msgstr "" -#: zds/tutorial/views.py:2798 +#: zds/tutorial/views.py:2799 msgid "Modification du tutoriel «{}» {} {}" msgstr "" -#: zds/tutorial/views.py:2801 +#: zds/tutorial/views.py:2802 msgid "Modification du chapitre «{}» {} {}" msgstr "" -#: zds/tutorial/views.py:2806 +#: zds/tutorial/views.py:2807 msgid "Création du chapitre «{}» {} {}" msgstr "" -#: zds/tutorial/views.py:2871 +#: zds/tutorial/views.py:2872 msgid "Suppression de l'extrait : «{}»" msgstr "" -#: zds/tutorial/views.py:2879 +#: zds/tutorial/views.py:2880 msgid "Mise à jour de l'extrait «{}» {} {}" msgstr "" -#: zds/tutorial/views.py:2882 +#: zds/tutorial/views.py:2883 msgid "Création de l'extrait «{}» {} {}" msgstr "" -#: zds/tutorial/views.py:3382 +#: zds/tutorial/views.py:3383 #, python-brace-format msgid "" "Bonjour {0},Vous recevez ce message car vous avez signalé le message de *{1}" @@ -4745,16 +4758,16 @@ msgid "" "Toute l'équipe de la modération vous remercie !" msgstr "" -#: zds/tutorial/views.py:3396 +#: zds/tutorial/views.py:3397 #, python-brace-format msgid "Résolution d'alerte : {0}" msgstr "" -#: zds/tutorial/views.py:3402 +#: zds/tutorial/views.py:3403 msgid "L'alerte a bien été résolue." msgstr "" -#: zds/tutorial/views.py:3445 +#: zds/tutorial/views.py:3446 msgid "" "Vous éditez ce message en tant que modérateur (auteur : {}). Soyez encore " "plus prudent lors de l'édition de celui-ci !" diff --git a/templates/article/view.html b/templates/article/view.html index 0117d70cce..650dd6abb7 100644 --- a/templates/article/view.html +++ b/templates/article/view.html @@ -240,4 +240,6 @@

    {% trans "Télécharger" %}

    + + {% include "misc/social_buttons.part.html" with link=article.get_absolute_url_online text=article.title %} {% endblock %} diff --git a/templates/misc/social_buttons.part.html b/templates/misc/social_buttons.part.html new file mode 100644 index 0000000000..97ff25f913 --- /dev/null +++ b/templates/misc/social_buttons.part.html @@ -0,0 +1,35 @@ +{% load i18n %} + + \ No newline at end of file diff --git a/templates/tutorial/chapter/view_online.html b/templates/tutorial/chapter/view_online.html index 4439b4a080..03fd9ec1e7 100644 --- a/templates/tutorial/chapter/view_online.html +++ b/templates/tutorial/chapter/view_online.html @@ -90,4 +90,6 @@

    {% blocktrans %}Administration{% endblocktrans %}< {% endif %} {% include "tutorial/includes/summary.part.html" with online=True tutorial=chapter.part.tutorial chapter_current=chapter %} + + {% include "misc/social_buttons.part.html" with link=chapter.part.tutorial.get_absolute_url_online text=chapter.part.tutorial.title %} {% endblock %} \ No newline at end of file diff --git a/templates/tutorial/part/view_online.html b/templates/tutorial/part/view_online.html index 752dbffa87..6ccc3fd876 100644 --- a/templates/tutorial/part/view_online.html +++ b/templates/tutorial/part/view_online.html @@ -75,4 +75,6 @@

    {% blocktrans %}Administration{% endblocktrans %}< {% endif %} {% include "tutorial/includes/summary.part.html" with online=True tutorial=part.tutorial parts=part.tutorial.get_parts %} + + {% include "misc/social_buttons.part.html" with link=part.tutorial.get_absolute_url_online text=part.tutorial.title %} {% endblock %} \ No newline at end of file diff --git a/templates/tutorial/tutorial/view_online.html b/templates/tutorial/tutorial/view_online.html index 20dba28e39..d63ee31784 100644 --- a/templates/tutorial/tutorial/view_online.html +++ b/templates/tutorial/tutorial/view_online.html @@ -198,4 +198,6 @@

    {% blocktrans %}Administration{% endblocktrans %}< {% endif %} + + {% include "misc/social_buttons.part.html" with link=tutorial.get_absolute_url_online text=tutorial.title %} {% endblock %} \ No newline at end of file From 11c3bf2bcf51177ea171d1660a6ba115382e6536 Mon Sep 17 00:00:00 2001 From: firm1 Date: Mon, 8 Dec 2014 00:49:37 +0100 Subject: [PATCH 087/152] ajaxification des actions sur un topic --- assets/js/action-ajax.js | 153 +++++++++++++++++++++++++++++++ assets/scss/_all-supports.scss | 5 + templates/forum/topic/index.html | 11 +-- zds/forum/views.py | 55 +++++++---- zds/utils/forms.py | 30 +++--- 5 files changed, 214 insertions(+), 40 deletions(-) create mode 100644 assets/js/action-ajax.js diff --git a/assets/js/action-ajax.js b/assets/js/action-ajax.js new file mode 100644 index 0000000000..6bac2a372a --- /dev/null +++ b/assets/js/action-ajax.js @@ -0,0 +1,153 @@ +/* ===== Zeste de Savoir ==================================================== + Manage action button AJAX requests + ========================================================================== */ + +(function($, undefined){ + "use strict"; + + $(".sidebar").on("click", ".follow", function(e){ + var $act = $(this), + $form = $(this).parents("form:first"), + $email = $(this).parents("li:first").next().find(".email"); + + var csrfmiddlewaretoken = $form.find("input[name=csrfmiddlewaretoken]").val(), + topic = $form.find("input[name=topic]").val(), + follow = $form.find("input[name=follow]").val(), + page = $form.find("input[name=page]").val(); + + $.ajax({ + url: $form.attr("action"), + type: "POST", + dataType: "json", + data: { + "csrfmiddlewaretoken": csrfmiddlewaretoken, + "topic": topic, + "follow": follow, + "page": page + }, + success: function(data){ + if(data.follow){ + $act.removeClass("blue").addClass("yellow").text("Ne plus suivre ce sujet"); + $form.find("input[name=follow]").val(1); + } else { + $act.removeClass("yellow").addClass("blue").text("Suivre ce sujet"); + $email.removeClass("blue").text("Être notifié par courriel"); + $form.find("input[name=follow]").val(0); + $email.parents("form:first").find("input[name=email]").val(0); + } + } + }); + + e.stopPropagation(); + e.preventDefault(); + }); + $(".sidebar").on("click", ".email", function(e){ + var $act = $(this), + $follow = $(this).parents("li:first").prev().find(".follow"), + $form = $(this).parents("form:first"); + + var csrfmiddlewaretoken = $form.find("input[name=csrfmiddlewaretoken]").val(), + topic = $form.find("input[name=topic]").val(), + email = $form.find("input[name=email]").val(), + page = $form.find("input[name=page]").val(); + + $.ajax({ + url: $form.attr("action"), + type: "POST", + dataType: "json", + data: { + "csrfmiddlewaretoken": csrfmiddlewaretoken, + "topic": topic, + "email": email, + "page": page + }, + success: function(data){ + if(data.email){ + $act.addClass("blue").text("Ne plus être notifié par courriel"); + $follow.removeClass("blue").addClass("yellow").text("Ne plus suivre ce sujet"); + $form.find("input[name=email]").val(1); + $follow.parents("form:first").find("input[name=follow]").val(1); + } else { + $act.removeClass("blue").text("Être notifié par courriel"); + $form.find("input[name=email]").val(0); + } + } + }); + e.stopPropagation(); + e.preventDefault(); + }); + $(".sidebar").on("click", ".solve", function(e){ + var $act = $(this), + $form = $(this).parents("form:first"); + + var csrfmiddlewaretoken = $form.find("input[name=csrfmiddlewaretoken]").val(), + topic = $form.find("input[name=topic]").val(), + solved = $form.find("input[name=solved]").val(), + page = $form.find("input[name=page]").val(); + + $.ajax({ + url: $form.attr("action"), + type: "POST", + dataType: "json", + data: { + "csrfmiddlewaretoken": csrfmiddlewaretoken, + "topic": topic, + "solved": solved, + "page": page + }, + success: function(data){ + if(data.solved){ + $act.removeClass("green").addClass("blue").text("Marquer comme non résolu"); + $form.find("input[name=solved]").val(1); + $(".alert-box").removeClass("empty", 1000); + } else { + $act.removeClass("blue").addClass("green").text("Marquer comme résolu"); + $form.find("input[name=solved]").val(0); + $(".alert-box").addClass("empty", 1000); + } + } + }); + e.stopPropagation(); + e.preventDefault(); + }); + + $(".message-actions").on("click", ".cite", function(e){ + var $act = $(this), + $editor = $(".md-editor"); + + $.ajax({ + url: $act.attr("href"), + dataType: "json", + success: function(data){ + $editor.append(data.text+"\n\n\n"); + } + }); + e.stopPropagation(); + e.preventDefault(); + }); + $(".message-bottom").on("click", ".btn-grey", function(e){ + var $form = $(this).parents("form:first"); + var csrfmiddlewaretoken = $form.find("input[name=csrfmiddlewaretoken]").val(), + text = $form.find("textarea[name=text]").val(), + lastPost = $form.find("input[name=last_post]").val(); + + $.ajax({ + url: $form.attr("action"), + type: "POST", + dataType: "json", + data: { + "csrfmiddlewaretoken": csrfmiddlewaretoken, + "text": text, + "last_post": lastPost, + "preview": "Apercu" + }, + success: function(data){ + $(".previsualisation").remove(); + var $prev = $("

    Prévisualisation de votre message

    "+data.text+"
    "); + $prev.insertAfter($form); + } + }); + e.stopPropagation(); + e.preventDefault(); + }); +})(jQuery); diff --git a/assets/scss/_all-supports.scss b/assets/scss/_all-supports.scss index 19c895f033..4ab3bedb6c 100644 --- a/assets/scss/_all-supports.scss +++ b/assets/scss/_all-supports.scss @@ -702,6 +702,11 @@ } } } + +.empty { + display: none; +} + .content-wrapper .alert-box { margin: 0 0 20px; diff --git a/templates/forum/topic/index.html b/templates/forum/topic/index.html index 372da952d8..6cda5058ec 100644 --- a/templates/forum/topic/index.html +++ b/templates/forum/topic/index.html @@ -63,11 +63,10 @@ {% include "misc/pagination.part.html" with position="top" %} - {% if topic.is_solved %} -
    +
    {% trans "L'auteur de ce sujet a trouvé une solution à son problème" %}.
    - {% endif %} + {% for message in posts %} {% captureas edit_link %} @@ -144,7 +143,7 @@ {% csrf_token %} -