-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
96e1989
commit d3d1421
Showing
48 changed files
with
1,229 additions
and
262 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 Error loading related location Loading user-provided value Error loading related location Loading |
||
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"] |
Oops, something went wrong.