Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
vincentporte committed Sep 18, 2024
1 parent 96e1989 commit d3d1421
Show file tree
Hide file tree
Showing 48 changed files with 1,229 additions and 262 deletions.
10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,13 @@ index:
INSTANCE_NUMBER=0 \
POSTGRESQL_ADDON_URI=$(POSTGRESQL_ADDON_URI) \
clevercloud/rebuild_index.sh

# DB

.PHONY: resetdb
resetdb:
dropdb --if-exists $(POSTGRESQL_ADDON_DB)
createdb $(POSTGRESQL_ADDON_DB)
python manage.py migrate
python manage.py configure_bucket
python manage.py populate
4 changes: 3 additions & 1 deletion clevercloud/collect_weekly_matomo_forum_stats.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ fi
# $APP_HOME is set by default by clever cloud.
cd $APP_HOME

python manage.py collect_matomo_forum_stats
# reactivate after documentation migration
# python manage.py collect_matomo_forum_stats
echo "Collecting weekly matomo stats for the forum is disabled for now."
2 changes: 2 additions & 0 deletions config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from django.urls import include, path, re_path
from machina.core.loading import get_class

from lacommunaute.documentation import urls as documentation_urls
from lacommunaute.event import urls as event_urls
from lacommunaute.forum import urls as forum_extension_urls
from lacommunaute.forum_conversation import urls as forum_conversation_extension_urls
Expand All @@ -30,6 +31,7 @@
path("inclusion_connect/", include(inclusion_connect_urls)),
# www.
path("", include(pages_urls)),
path("documentation/", include(documentation_urls)),
path("members/", include(forum_member_urls)),
path("", include(forum_conversation_extension_urls)),
path("", include(forum_extension_urls)),
Expand Down
38 changes: 38 additions & 0 deletions lacommunaute/documentation/abstract_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from django.conf import settings
from django.db import models
from django.utils.encoding import force_str
from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _
from machina.models.fields import MarkupTextField
from storages.backends.s3boto3 import S3Boto3Storage

from lacommunaute.utils.validators import validate_image_size


class AbstractDatedModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

class Meta:
abstract = True


class AbstractPublication(AbstractDatedModel):
name = models.CharField(max_length=100, verbose_name=_("Name"))
slug = models.SlugField(max_length=255, verbose_name=_("Slug"), unique=True)

description = MarkupTextField(verbose_name=_("Description"), null=True, blank=True)
short_description = models.CharField(
max_length=400, blank=True, null=True, verbose_name="Description courte (SEO)"
)
image = models.ImageField(
storage=S3Boto3Storage(bucket_name=settings.AWS_STORAGE_BUCKET_NAME, file_overwrite=False),
validators=[validate_image_size],
)

class Meta:
abstract = True

def save(self, *args, **kwargs):
self.slug = slugify(force_str(self.name), allow_unicode=True)
super().save(*args, **kwargs)
40 changes: 40 additions & 0 deletions lacommunaute/documentation/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from django.contrib import admin

from lacommunaute.documentation.models import Category, Document, DocumentRating


class DocumentInlines(admin.TabularInline):
model = Document
extra = 0
fields = ("name", "short_description")
readonly_fields = ("name", "short_description")

def has_delete_permission(self, request, obj=None):
return False

def has_add_permission(self, request, obj=None):
return False


@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ("name",)
search_fields = ("name",)
fields = ("name", "short_description", "description", "image")
inlines = [DocumentInlines]


@admin.register(Document)
class DocumentAdmin(admin.ModelAdmin):
list_display = ("name", "category")
list_filter = ("category",)
search_fields = ("name",)
fields = ("name", "short_description", "description", "image")


@admin.register(DocumentRating)
class DocumentRatingAdmin(admin.ModelAdmin):
list_display = ("document", "rating", "created_at")
list_filter = ("document",)
list_display_links = ("rating",)
raw_id_fields = ("document", "user")
22 changes: 22 additions & 0 deletions lacommunaute/documentation/factories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import factory
from faker import Faker

from lacommunaute.documentation.models import Category


faker = Faker()


class CategoryFactory(factory.django.DjangoModelFactory):
name = factory.Faker("name")
description = factory.Faker("sentence", nb_words=100)
short_description = factory.Faker("sentence", nb_words=10)
image = factory.django.ImageField(filename="banner.jpg")

class Meta:
model = Category

class Params:
for_snapshot = factory.Trait(
name="Test Category", description="Test description", short_description="Test description"
)
96 changes: 96 additions & 0 deletions lacommunaute/documentation/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import re

from django import forms
from django.conf import settings
from django.forms import CharField, CheckboxSelectMultiple, ModelMultipleChoiceField
from taggit.models import Tag

from lacommunaute.documentation.models import Category, Document
from lacommunaute.partner.models import Partner


def wrap_iframe_in_div_tag(text):
# iframe tags must be wrapped in a div tag to be displayed correctly
# add div tag if not present

iframe_regex = r"((<div>)?<iframe.*?</iframe>(</div>)?)"

for match, starts_with, ends_with in re.findall(iframe_regex, text, re.DOTALL):

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High documentation

This
regular expression
that depends on a
user-provided value
may run slow on strings starting with '<iframe' and with many repetitions of '<iframe'.
if not starts_with and not ends_with:
text = text.replace(match, f"<div>{match}</div>")

return text


class DocumentationFormMixin:
name = forms.CharField(required=True, label="Titre")
short_description = forms.CharField(
widget=forms.Textarea(attrs={"rows": 3}),
max_length=400,
required=True,
label="Sous-titre (400 caractères pour le SEO)",
)
description = forms.CharField(
widget=forms.Textarea(attrs={"rows": 20}), required=False, label="Contenu (markdown autorisé)"
)
image = forms.ImageField(
required=False,
label="Banniere de couverture, format 1200 x 630 pixels recommandé",
widget=forms.FileInput(attrs={"accept": settings.SUPPORTED_IMAGE_FILE_TYPES.keys()}),
)

def save(self, commit=True):
instance = super().save(commit=False)
instance.description = wrap_iframe_in_div_tag(self.cleaned_data.get("description"))

if commit:
instance.save()
return instance

class CategoryForm(forms.ModelForm, DocumentationFormMixin):
class Meta:
model = Category
fields = ["name", "short_description", "description", "image"]


class DocumentForm(forms.ModelForm, DocumentationFormMixin):
certified = forms.BooleanField(required=False, label="Certifiée par la communauté de l'inclusion")
partner = forms.ModelChoiceField(
label="Sélectionner un partenaire",
queryset=Partner.objects.all(),
required=False,
)
category = forms.ModelChoiceField(
label="Sélectionner une catégorie documentaire",
queryset=Category.objects.all(),
required=False,
)
tags = ModelMultipleChoiceField(
label="Sélectionner un ou plusieurs tags",
queryset=Tag.objects.all(),
widget=CheckboxSelectMultiple,
required=False,
)
new_tags = CharField(required=False, label="Ajouter un tag ou plusieurs tags (séparés par des virgules)")

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance.pk:
self.fields["tags"].initial = self.instance.tags.all()

def save(self, commit=True):
instance = super().save(commit=False)

if commit:
instance.save()
instance.tags.set(self.cleaned_data["tags"])
(
instance.tags.add(*[tag.strip() for tag in self.cleaned_data["new_tags"].split(",")])
if self.cleaned_data.get("new_tags")
else None
)
return instance

class Meta:
model = Document
fields = ["name", "short_description", "description", "image", "certified", "partner", "category"]
Loading

0 comments on commit d3d1421

Please sign in to comment.