From 4890fcf0e1b269498b81ec2612e12c3de8be1ab9 Mon Sep 17 00:00:00 2001 From: Sli Date: Tue, 25 Feb 2025 18:08:16 +0100 Subject: [PATCH] Rename news moderate to publish --- com/api.py | 26 ++++---- com/calendar.py | 6 +- com/forms.py | 10 ++-- ...ove_news_is_moderated_news_is_published.py | 16 +++++ com/models.py | 16 ++--- com/schemas.py | 4 +- .../com/components/ics-calendar-index.ts | 32 +++++----- .../com/components/moderation-alert-index.ts | 16 ++--- com/templates/com/macros.jinja | 22 +++---- com/templates/com/news_admin_list.jinja | 16 ++--- com/templates/com/news_detail.jinja | 4 +- com/templates/com/news_edit.jinja | 6 +- com/templates/com/news_list.jinja | 20 +++---- com/tests/test_api.py | 18 +++--- com/tests/test_models.py | 2 +- com/tests/test_views.py | 8 +-- com/views.py | 6 +- core/management/commands/populate.py | 13 ++-- core/migrations/0044_alter_userban_options.py | 16 +++++ locale/fr/LC_MESSAGES/django.po | 59 ++++++++++--------- locale/fr/LC_MESSAGES/djangojs.po | 23 ++++---- 21 files changed, 186 insertions(+), 153 deletions(-) create mode 100644 com/migrations/0009_remove_news_is_moderated_news_is_published.py create mode 100644 core/migrations/0044_alter_userban_options.py diff --git a/com/api.py b/com/api.py index a8cedc6f..99186f36 100644 --- a/com/api.py +++ b/com/api.py @@ -40,13 +40,13 @@ class CalendarController(ControllerBase): return send_raw_file(IcsCalendar.get_internal()) @route.get( - "/unmoderated.ics", + "/unpublished.ics", permissions=[IsAuthenticated], - url_name="calendar_unmoderated", + url_name="calendar_unpublished", ) - def calendar_unmoderated(self): + def calendar_unpublished(self): return HttpResponse( - IcsCalendar.get_unmoderated(self.context.request.user), + IcsCalendar.get_unpublished(self.context.request.user), content_type="text/calendar", ) @@ -54,26 +54,26 @@ class CalendarController(ControllerBase): @api_controller("/news") class NewsController(ControllerBase): @route.patch( - "/{int:news_id}/moderate", + "/{int:news_id}/publish", permissions=[HasPerm("com.moderate_news")], url_name="moderate_news", ) - def moderate_news(self, news_id: int): + def publish_news(self, news_id: int): news = self.get_object_or_exception(News, id=news_id) - if not news.is_moderated: - news.is_moderated = True + if not news.is_published: + news.is_published = True news.moderator = self.context.request.user news.save() @route.patch( - "/{int:news_id}/remove", + "/{int:news_id}/unpublish", permissions=[HasPerm("com.moderate_news")], - url_name="remove_news", + url_name="unpublish_news", ) - def remove_news(self, news_id: int): + def unpublish_news(self, news_id: int): news = self.get_object_or_exception(News, id=news_id) - if news.is_moderated: - news.is_moderated = False + if news.is_published: + news.is_published = False news.moderator = self.context.request.user news.save() diff --git a/com/calendar.py b/com/calendar.py index cf35a522..1c95a2b3 100644 --- a/com/calendar.py +++ b/com/calendar.py @@ -63,7 +63,7 @@ class IcsCalendar: _ = f.write( cls.ics_from_queryset( NewsDate.objects.filter( - news__is_moderated=True, + news__is_published=True, end_date__gte=timezone.now() - (relativedelta(months=6)), ) ) @@ -71,10 +71,10 @@ class IcsCalendar: return cls._INTERNAL_CALENDAR @classmethod - def get_unmoderated(cls, user: User) -> bytes: + def get_unpublished(cls, user: User) -> bytes: return cls.ics_from_queryset( NewsDate.objects.viewable_by(user).filter( - news__is_moderated=False, + news__is_published=False, end_date__gte=timezone.now() - (relativedelta(months=6)), ), ) diff --git a/com/forms.py b/com/forms.py index 471e6632..8b81a3f9 100644 --- a/com/forms.py +++ b/com/forms.py @@ -147,8 +147,8 @@ class NewsForm(forms.ModelForm): "content": MarkdownInput, } - auto_moderate = forms.BooleanField( - label=_("Automoderation"), + auto_publish = forms.BooleanField( + label=_("Auto publication"), widget=CheckboxInput(attrs={"class": "switch"}), required=False, ) @@ -182,12 +182,12 @@ class NewsForm(forms.ModelForm): def save(self, commit: bool = True): # noqa FBT001 self.instance.author = self.author if (self.author.is_com_admin or self.author.is_root) and ( - self.cleaned_data.get("auto_moderate") is True + self.cleaned_data.get("auto_publish") is True ): - self.instance.is_moderated = True + self.instance.is_published = True self.instance.moderator = self.author else: - self.instance.is_moderated = False + self.instance.is_published = False created_news = super().save(commit=commit) self.date_form.save(commit=commit, news=created_news) return created_news diff --git a/com/migrations/0009_remove_news_is_moderated_news_is_published.py b/com/migrations/0009_remove_news_is_moderated_news_is_published.py new file mode 100644 index 00000000..dd633820 --- /dev/null +++ b/com/migrations/0009_remove_news_is_moderated_news_is_published.py @@ -0,0 +1,16 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [("com", "0008_alter_news_options_alter_newsdate_options_and_more")] + + operations = [ + migrations.RenameField( + model_name="news", old_name="is_moderated", new_name="is_published" + ), + migrations.AlterField( + model_name="news", + name="is_published", + field=models.BooleanField(default=False, verbose_name="is published"), + ), + ] diff --git a/com/models.py b/com/models.py index 229d83c8..2b3a76c4 100644 --- a/com/models.py +++ b/com/models.py @@ -56,7 +56,7 @@ class Sith(models.Model): class NewsQuerySet(models.QuerySet): def moderated(self) -> Self: - return self.filter(is_moderated=True) + return self.filter(is_published=True) def viewable_by(self, user: User) -> Self: """Filter news that the given user can view. @@ -68,7 +68,7 @@ class NewsQuerySet(models.QuerySet): """ if user.has_perm("com.view_unmoderated_news"): return self - q_filter = Q(is_moderated=True) + q_filter = Q(is_published=True) if user.is_authenticated: q_filter |= Q(author_id=user.id) return self.filter(q_filter) @@ -104,7 +104,7 @@ class News(models.Model): verbose_name=_("author"), on_delete=models.PROTECT, ) - is_moderated = models.BooleanField(_("is moderated"), default=False) + is_published = models.BooleanField(_("is published"), default=False) moderator = models.ForeignKey( User, related_name="moderated_news", @@ -127,7 +127,7 @@ class News(models.Model): def save(self, *args, **kwargs): super().save(*args, **kwargs) - if self.is_moderated: + if self.is_published: return for user in User.objects.filter( groups__id__in=[settings.SITH_GROUP_COM_ADMIN_ID] @@ -154,7 +154,7 @@ class News(models.Model): def can_be_viewed_by(self, user: User): return ( - self.is_moderated + self.is_published or user.has_perm("com.view_unmoderated_news") or (user.is_authenticated and self.author_id == user.id) ) @@ -162,7 +162,7 @@ class News(models.Model): def news_notification_callback(notif): count = News.objects.filter( - dates__start_date__gt=timezone.now(), is_moderated=False + dates__start_date__gt=timezone.now(), is_published=False ).count() if count: notif.viewed = False @@ -182,7 +182,7 @@ class NewsDateQuerySet(models.QuerySet): """ if user.has_perm("com.view_unmoderated_news"): return self - q_filter = Q(news__is_moderated=True) + q_filter = Q(news__is_published=True) if user.is_authenticated: q_filter |= Q(news__author_id=user.id) return self.filter(q_filter) @@ -337,7 +337,7 @@ class Screen(models.Model): def active_posters(self): now = timezone.now() - return self.posters.filter(is_moderated=True, date_begin__lte=now).filter( + return self.posters.filter(d=True, date_begin__lte=now).filter( Q(date_end__isnull=True) | Q(date_end__gte=now) ) diff --git a/com/schemas.py b/com/schemas.py index 967076ad..93ee5315 100644 --- a/com/schemas.py +++ b/com/schemas.py @@ -15,14 +15,14 @@ class NewsDateFilterSchema(FilterSchema): after: datetime | None = Field(None, q="start_date__gt") club_id: int | None = Field(None, q="news__club_id") news_id: int | None = None - is_moderated: bool | None = Field(None, q="news__is_moderated") + is_published: bool | None = Field(None, q="news__is_published") title: str | None = Field(None, q="news__title__icontains") class NewsSchema(ModelSchema): class Meta: model = News - fields = ["id", "title", "summary", "is_moderated"] + fields = ["id", "title", "summary", "is_published"] club: ClubProfileSchema url: str diff --git a/com/static/bundled/com/components/ics-calendar-index.ts b/com/static/bundled/com/components/ics-calendar-index.ts index 036d6b2d..0b4976b0 100644 --- a/com/static/bundled/com/components/ics-calendar-index.ts +++ b/com/static/bundled/com/components/ics-calendar-index.ts @@ -10,10 +10,10 @@ import listPlugin from "@fullcalendar/list"; import { calendarCalendarExternal, calendarCalendarInternal, - calendarCalendarUnmoderated, + calendarCalendarUnpublished, newsDeleteNews, - newsModerateNews, - newsRemoveNews, + newsPublishNews, + newsUnpublishNews, } from "#openapi"; @registerComponent("ics-calendar") @@ -89,15 +89,15 @@ export class IcsCalendar extends inheritHtmlElement("div") { this.calendar.refetchEvents(); } - async moderateNews(id: number) { - await newsModerateNews({ + async publishNews(id: number) { + await newsPublishNews({ path: { // biome-ignore lint/style/useNamingConvention: python API news_id: id, }, }); this.dispatchEvent( - new CustomEvent("calendar-moderate", { + new CustomEvent("calendar-publish", { bubbles: true, detail: { id: id, @@ -107,15 +107,15 @@ export class IcsCalendar extends inheritHtmlElement("div") { await this.refreshEvents(); } - async removeNews(id: number) { - await newsRemoveNews({ + async unpublishNews(id: number) { + await newsUnpublishNews({ path: { // biome-ignore lint/style/useNamingConvention: python API news_id: id, }, }); this.dispatchEvent( - new CustomEvent("calendar-remove", { + new CustomEvent("calendar-unpublish", { bubbles: true, detail: { id: id, @@ -157,10 +157,10 @@ export class IcsCalendar extends inheritHtmlElement("div") { className: "external", }, { - url: `${await makeUrl(calendarCalendarUnmoderated)}${cacheInvalidate}`, + url: `${await makeUrl(calendarCalendarUnpublished)}${cacheInvalidate}`, format: "ics", color: "red", - className: "unmoderated", + className: "unpublished", }, ]; } @@ -233,20 +233,20 @@ export class IcsCalendar extends inheritHtmlElement("div") { const newsId = this.getNewsId(event); const div = document.createElement("div"); if (this.canModerate) { - if (event.source.internalEventSource.ui.classNames.includes("unmoderated")) { + if (event.source.internalEventSource.ui.classNames.includes("unpublished")) { const button = document.createElement("button"); - button.innerHTML = `${gettext("Moderate")}`; + button.innerHTML = `${gettext("Publish")}`; button.setAttribute("class", "btn btn-green"); button.onclick = () => { - this.moderateNews(newsId); + this.publishNews(newsId); }; div.appendChild(button); } else { const button = document.createElement("button"); - button.innerHTML = `${gettext("Remove")}`; + button.innerHTML = `${gettext("Unpublish")}`; button.setAttribute("class", "btn btn-orange"); button.onclick = () => { - this.removeNews(newsId); + this.unpublishNews(newsId); }; div.appendChild(button); } diff --git a/com/static/bundled/com/components/moderation-alert-index.ts b/com/static/bundled/com/components/moderation-alert-index.ts index f4ecc4d1..1c9274dd 100644 --- a/com/static/bundled/com/components/moderation-alert-index.ts +++ b/com/static/bundled/com/components/moderation-alert-index.ts @@ -1,5 +1,5 @@ import { exportToHtml } from "#core:utils/globals"; -import { newsDeleteNews, newsFetchNewsDates, newsModerateNews } from "#openapi"; +import { newsDeleteNews, newsFetchNewsDates, newsPublishNews } from "#openapi"; // This will be used in jinja templates, // so we cannot use real enums as those are purely an abstraction of Typescript @@ -7,7 +7,7 @@ const AlertState = { // biome-ignore lint/style/useNamingConvention: this feels more like an enum PENDING: 1, // biome-ignore lint/style/useNamingConvention: this feels more like an enum - MODERATED: 2, + PUBLISHED: 2, // biome-ignore lint/style/useNamingConvention: this feels more like an enum DELETED: 3, }; @@ -19,11 +19,11 @@ document.addEventListener("alpine:init", () => { newsId: newsId as number, loading: false, - async moderateNews() { + async publishNews() { this.loading = true; // biome-ignore lint/style/useNamingConvention: api is snake case - await newsModerateNews({ path: { news_id: this.newsId } }); - this.state = AlertState.MODERATED; + await newsPublishNews({ path: { news_id: this.newsId } }); + this.state = AlertState.PUBLISHED; this.$dispatch("news-moderated", { newsId: this.newsId, state: this.state }); this.loading = false; }, @@ -54,7 +54,7 @@ document.addEventListener("alpine:init", () => { * Query the server to know the number of news dates that would be moderated * if this one is moderated. */ - async nbToModerate(): Promise { + async nbToPublish(): Promise { // What we want here is the count attribute of the response. // We don't care about the actual results, // so we ask for the minimum page size possible. @@ -69,8 +69,8 @@ document.addEventListener("alpine:init", () => { return interpolate( gettext( "This event will take place every week for %s weeks. " + - "If you moderate or delete this event, " + - "it will also be moderated (or deleted) for the following weeks.", + "If you publish or delete this event, " + + "it will also be published (or deleted) for the following weeks.", ), [nbEvents], ); diff --git a/com/templates/com/macros.jinja b/com/templates/com/macros.jinja index dbff9188..f3713717 100644 --- a/com/templates/com/macros.jinja +++ b/com/templates/com/macros.jinja @@ -1,6 +1,6 @@ {% macro news_moderation_alert(news, user, alpineState = None) %} - {# An alert to display on top of non moderated news, - with actions to either moderate or delete them. + {# An alert to display on top of unpublished news, + with actions to either publish or delete them. The current state of the alert is accessible through the given `alpineState` variable. @@ -8,7 +8,7 @@ This comes in three flavours : - You can pass the `News` object itself to the macro. - In this case, if `request.user` can moderate news, + In this case, if `request.user` can publish news, it will perform an additional db query to know if it is a recurring event. - You can also give only the news id. In this case, a server request will be issued to know @@ -64,16 +64,16 @@ -