Rename news moderate to publish

This commit is contained in:
Antoine Bartuccio 2025-02-25 18:08:16 +01:00
parent 2e71275f5b
commit 4890fcf0e1
21 changed files with 186 additions and 153 deletions

View File

@ -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()

View File

@ -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)),
),
)

View File

@ -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

View File

@ -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"),
),
]

View File

@ -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)
)

View File

@ -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

View File

@ -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 = `<i class="fa fa-check"></i>${gettext("Moderate")}`;
button.innerHTML = `<i class="fa fa-check"></i>${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 = `<i class="fa fa-times"></i>${gettext("Remove")}`;
button.innerHTML = `<i class="fa fa-times"></i>${gettext("Unpublish")}`;
button.setAttribute("class", "btn btn-orange");
button.onclick = () => {
this.removeNews(newsId);
this.unpublishNews(newsId);
};
div.appendChild(button);
}

View File

@ -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<number> {
async nbToPublish(): Promise<number> {
// 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],
);

View File

@ -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 @@
<template x-if="state === AlertState.PENDING">
<div class="alert alert-yellow">
<div class="alert-main">
<strong>{% trans %}Waiting moderation{% endtrans %}</strong>
<strong>{% trans %}Waiting publication{% endtrans %}</strong>
<p>
{% trans trimmed %}
This news isn't moderated and is visible
This news isn't published and is visible
only by its author and the communication admins.
{% endtrans %}
</p>
<p>
{% trans trimmed %}
It will stay hidden for other users until it has been moderated.
It will stay hidden for other users until it has been published.
{% endtrans %}
</p>
{% if user.has_perm("com.moderate_news") %}
@ -84,7 +84,7 @@
<div
{% if news is integer or news is string %}
x-data="{ nbEvents: 0 }"
x-init="nbEvents = await nbToModerate()"
x-init="nbEvents = await nbToPublish()"
{% else %}
x-data="{ nbEvents: {{ news.dates.count() }} }"
{% endif %}
@ -101,8 +101,8 @@
</div>
{% if user.has_perm("com.moderate_news") %}
<span class="alert-aside" :aria-busy="loading">
<button class="btn btn-green" @click="moderateNews()" :disabled="loading">
<i class="fa fa-check"></i> {% trans %}Moderate{% endtrans %}
<button class="btn btn-green" @click="publishNews()" :disabled="loading">
<i class="fa fa-check"></i> {% trans %}Publish{% endtrans %}
</button>
{% endif %}
{% if user.has_perm("com.delete_news") %}
@ -113,9 +113,9 @@
{% endif %}
</div>
</template>
<template x-if="state === AlertState.MODERATED">
<template x-if="state === AlertState.PUBLISHED">
<div class="alert alert-green">
{% trans %}News moderated{% endtrans %}
{% trans %}News published{% endtrans %}
</div>
</template>
<template x-if="state === AlertState.DELETED">

View File

@ -27,7 +27,7 @@
</tr>
</thead>
<tbody>
{% for news in weeklies.filter(is_moderated=True) %}
{% for news in weeklies.filter(is_published=True) %}
<tr>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
@ -47,7 +47,7 @@
</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}?remove">{% trans %}Remove{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}?remove">{% trans %}Unpublish{% endtrans %}</a>
<a href="{{ url('com:news_delete', news_id=news.id) }}">{% trans %}Delete{% endtrans %}</a>
</td>
</tr>
@ -67,7 +67,7 @@
</tr>
</thead>
<tbody>
{% for news in weeklies.filter(is_moderated=False) %}
{% for news in weeklies.filter(is_published=False) %}
<tr>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
@ -86,7 +86,7 @@
</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}">{% trans %}Moderate{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}">{% trans %}Publish{% endtrans %}</a>
<a href="{{ url('com:news_delete', news_id=news.id) }}">{% trans %}Delete{% endtrans %}</a>
</td>
</tr>
@ -111,7 +111,7 @@
</tr>
</thead>
<tbody>
{% for news in events.filter(is_moderated=True) %}
{% for news in events.filter(is_published=True) %}
<tr>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
@ -124,7 +124,7 @@
{{ news.dates.all()[0].end_date|localtime|time(DATETIME_FORMAT) }}</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}?remove">{% trans %}Remove{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}?remove">{% trans %}Unpublish{% endtrans %}</a>
<a href="{{ url('com:news_delete', news_id=news.id) }}">{% trans %}Delete{% endtrans %}</a>
</td>
</tr>
@ -145,7 +145,7 @@
</tr>
</thead>
<tbody>
{% for news in events.filter(is_moderated=False) %}
{% for news in events.filter(is_published=False) %}
<tr>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
@ -157,7 +157,7 @@
{{ news.dates.all()[0].end_date|localtime|time(DATETIME_FORMAT) }}</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}">{% trans %}Moderate{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}">{% trans %}Publish{% endtrans %}</a>
<a href="{{ url('com:news_delete', news_id=news.id) }}">{% trans %}Delete{% endtrans %}</a>
</td>
</tr>

View File

@ -25,7 +25,7 @@
<p><a href="{{ url('com:news_list') }}">{% trans %}Back to news{% endtrans %}</a></p>
<div x-data="{newsState: AlertState.PENDING}">
{% if not news.is_moderated %}
{% if not news.is_published %}
{{ news_moderation_alert(news, user, "newsState") }}
{% endif %}
<article id="news_details" x-show="newsState !== AlertState.DELETED">
@ -51,7 +51,7 @@
{% if news.moderator %}
<p>{% trans %}Moderator: {% endtrans %}{{ user_profile_link(news.moderator) }}</p>
{% elif user.is_com_admin %}
<p> <a href="{{ url('com:news_moderate', news_id=news.id) }}">{% trans %}Moderate{% endtrans %}</a></p>
<p> <a href="{{ url('com:news_moderate', news_id=news.id) }}">{% trans %}Publish{% endtrans %}</a></p>
{% endif %}
{% if user.can_edit(news) %}
<p> <a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit (will be moderated again){% endtrans %}</a></p>

View File

@ -80,9 +80,9 @@
</fieldset>
{% if user.is_root or user.is_com_admin %}
<fieldset>
{{ form.auto_moderate.errors }}
{{ form.auto_moderate }}
{{ form.auto_moderate.label_tag() }}
{{ form.auto_publish.errors }}
{{ form.auto_publish }}
{{ form.auto_publish.label_tag() }}
</fieldset>
{% endif %}
<p><input type="submit" value="{% trans %}Save{% endtrans %}" class="btn btn-blue"/></p>

View File

@ -57,17 +57,17 @@
{% for date in dates_group %}
<article
class="news_event"
{%- if not date.news.is_moderated -%}
{%- if not date.news.is_published -%}
x-data="{newsState: AlertState.PENDING}"
{%- endif -%}
>
{% if not date.news.is_moderated %}
{# if a non moderated news is in the object list,
{% if not date.news.is_published %}
{# if a non published news is in the object list,
the logged user is either an admin or the news author #}
{{ news_moderation_alert(date.news, user, "newsState") }}
{% endif %}
<div
{% if not date.news.is_moderated -%}
{% if not date.news.is_published -%}
x-show="newsState !== AlertState.DELETED"
{%- endif -%}
>
@ -120,9 +120,9 @@
<template x-for="newsDate in newsList" :key="newsDate.id">
<article
class="news_event"
x-data="{ newsState: newsDate.news.is_moderated ? AlertState.MODERATED : AlertState.PENDING }"
x-data="{ newsState: newsDate.news.is_published ? AlertState.PULISHED : AlertState.PENDING }"
>
<template x-if="!newsDate.news.is_moderated">
<template x-if="!newsDate.news.is_published">
{{ news_moderation_alert("newsDate.news.id", user, "newsState") }}
</template>
<div x-show="newsState !== AlertState.DELETED">
@ -183,15 +183,15 @@
x-data
x-ref="calendar"
@news-moderated.window="
if ($event.target !== $refs.calendar){
// Avoid triggering a refresh with a dispatch
// from the calendar itself
if ($event.target !== $refs.calendar){
$refs.calendar.refreshEvents($event);
}
"
@calendar-remove="$dispatch('news-moderated', {newsId: $event.detail.id, state: AlertState.PENDING})"
@calendar-delete="$dispatch('news-moderated', {newsId: $event.detail.id, state: AlertState.DELETED})"
@calendar-moderate="$dispatch('news-moderated', {newsId: $event.detail.id, state: AlertState.MODERATED})"
@calendar-remove="$dispatch('news-moderated', {newsId: $event.detail.id, state: AlertState.DELETED})"
@calendar-unpublish="$dispatch('news-moderated', {newsId: $event.detail.id, state: AlertState.PENDING})"
@calendar-publish="$dispatch('news-moderated', {newsId: $event.detail.id, state: AlertState.PUBLISHED})"
locale="{{ get_language() }}"
can_moderate="{{ user.has_perm("com.moderate_news") }}"
can_delete="{{ user.has_perm("com.delete_news") }}"

View File

@ -129,14 +129,14 @@ class TestInternalCalendar:
@pytest.mark.django_db
class TestModerateNews:
@pytest.mark.parametrize("news_is_moderated", [True, False])
def test_moderation_ok(self, client: Client, news_is_moderated: bool): # noqa FBT
@pytest.mark.parametrize("news_is_published", [True, False])
def test_moderation_ok(self, client: Client, news_is_published: bool): # noqa FBT
user = baker.make(
User, user_permissions=[Permission.objects.get(codename="moderate_news")]
)
# The API call should work even if the news is initially moderated.
# In the latter case, the result should be a noop, rather than an error.
news = baker.make(News, is_moderated=news_is_moderated)
news = baker.make(News, is_published=news_is_published)
initial_moderator = news.moderator
client.force_login(user)
response = client.patch(
@ -147,22 +147,22 @@ class TestModerateNews:
# If it was already moderated, it should be a no-op, but not an error
assert response.status_code == 200
news.refresh_from_db()
assert news.is_moderated
if not news_is_moderated:
assert news.is_published
if not news_is_published:
assert news.moderator == user
else:
assert news.moderator == initial_moderator
def test_moderation_forbidden(self, client: Client):
user = baker.make(User)
news = baker.make(News, is_moderated=False)
news = baker.make(News, is_published=False)
client.force_login(user)
response = client.patch(
reverse("api:moderate_news", kwargs={"news_id": news.id})
)
assert response.status_code == 403
news.refresh_from_db()
assert not news.is_moderated
assert not news.is_published
@pytest.mark.django_db
@ -203,7 +203,7 @@ class TestFetchNewsDates(TestCase):
value=now() + timedelta(hours=2), increment_by=timedelta(days=1)
),
news=iter(
baker.make(News, is_moderated=True, _quantity=5, _bulk_create=True)
baker.make(News, is_published=True, _quantity=5, _bulk_create=True)
),
)
cls.dates.append(
@ -211,7 +211,7 @@ class TestFetchNewsDates(TestCase):
NewsDate,
start_date=now() + timedelta(days=2, hours=1),
end_date=now() + timedelta(days=2, hours=5),
news=baker.make(News, is_moderated=True),
news=baker.make(News, is_published=True),
)
)
cls.dates.sort(key=lambda d: d.start_date)

View File

@ -18,7 +18,7 @@ class TestNewsViewableBy(TestCase):
cls.news = baker.make(
News,
author=itertools.cycle(cls.users),
is_moderated=iter([True, True, True, False, False, False]),
is_published=iter([True, True, True, False, False, False]),
_quantity=6,
_bulk_create=True,
)

View File

@ -168,7 +168,7 @@ class TestNews(TestCase):
assert not self.new.can_be_viewed_by(self.sli)
assert not self.new.can_be_viewed_by(self.anonymous)
self.new.is_moderated = True
self.new.is_published = True
self.new.save()
assert self.new.can_be_viewed_by(self.com_admin)
assert self.new.can_be_viewed_by(self.sli)
@ -258,7 +258,7 @@ class TestNewsCreation(TestCase):
created = News.objects.order_by("id").last()
assertRedirects(response, created.get_absolute_url())
assert created.title == "Test news"
assert not created.is_moderated
assert not created.is_published
dates = list(created.dates.values("start_date", "end_date"))
assert dates == [{"start_date": self.start, "end_date": self.end}]
@ -281,7 +281,7 @@ class TestNewsCreation(TestCase):
]
def test_edit_news(self):
news = baker.make(News, author=self.user, is_moderated=True)
news = baker.make(News, author=self.user, is_published=True)
baker.make(
NewsDate,
news=news,
@ -296,7 +296,7 @@ class TestNewsCreation(TestCase):
created = News.objects.order_by("id").last()
assertRedirects(response, created.get_absolute_url())
assert created.title == "Test news"
assert not created.is_moderated
assert not created.is_published
dates = list(created.dates.values("start_date", "end_date"))
assert dates == [{"start_date": self.start, "end_date": self.end}]

View File

@ -217,9 +217,9 @@ class NewsModerateView(PermissionRequiredMixin, DetailView):
def get(self, request, *args, **kwargs):
self.object = self.get_object()
if "remove" in request.GET:
self.object.is_moderated = False
self.object.is_published = False
else:
self.object.is_moderated = True
self.object.is_published = True
self.object.moderator = request.user
self.object.save()
if "next" in self.request.GET:
@ -309,7 +309,7 @@ class NewsFeed(Feed):
def items(self):
return (
NewsDate.objects.filter(
news__is_moderated=True,
news__is_published=True,
end_date__gte=timezone.now() - (relativedelta(months=6)),
)
.select_related("news", "news__author")

View File

@ -690,7 +690,7 @@ Welcome to the wiki page!
content="Glou glou glou glou glou glou glou",
club=bar_club,
author=subscriber,
is_moderated=True,
is_published=True,
moderator=skia,
)
news_dates.append(
@ -704,12 +704,11 @@ Welcome to the wiki page!
title="Repas barman",
summary="Enjoy la fin du semestre!",
content=(
"Viens donc t'enjailler avec les autres barmans aux "
"frais du BdF! \\o/"
"Viens donc t'enjailler avec les autres barmans aux frais du BdF! \\o/"
),
club=bar_club,
author=subscriber,
is_moderated=True,
is_published=True,
moderator=skia,
)
news_dates.append(
@ -725,7 +724,7 @@ Welcome to the wiki page!
content="Fô viendre mangey d'la bonne fondue!",
club=bar_club,
author=subscriber,
is_moderated=True,
is_published=True,
moderator=skia,
)
news_dates.append(
@ -741,7 +740,7 @@ Welcome to the wiki page!
content="Viens faire la fête avec tout plein de gens!",
club=bar_club,
author=subscriber,
is_moderated=True,
is_published=True,
moderator=skia,
)
news_dates.append(
@ -759,7 +758,7 @@ Welcome to the wiki page!
"t'amuser le Vendredi soir!",
club=troll,
author=subscriber,
is_moderated=True,
is_published=True,
moderator=skia,
)
news_dates.extend(

View File

@ -0,0 +1,16 @@
# Generated by Django 4.2.17 on 2025-02-25 14:45
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("core", "0043_bangroup_alter_group_description_alter_user_groups_and_more"),
]
operations = [
migrations.AlterModelOptions(
name="userban",
options={"verbose_name": "user ban", "verbose_name_plural": "user bans"},
),
]

View File

@ -6,7 +6,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-25 11:04+0100\n"
"POT-Creation-Date: 2025-02-25 16:38+0100\n"
"PO-Revision-Date: 2016-07-18\n"
"Last-Translator: Maréchal <thomas.girod@utbm.fr\n"
"Language-Team: AE info <ae.info@utbm.fr>\n"
@ -812,7 +812,7 @@ msgstr "Nouvelle mailing liste"
msgid "Subscribe"
msgstr "S'abonner"
#: club/forms.py com/templates/com/news_admin_list.jinja
#: club/forms.py
msgid "Remove"
msgstr "Retirer"
@ -1296,8 +1296,8 @@ msgstr ""
"Combien de fois l'événement doit-il se répéter (en incluant la première fois)"
#: com/forms.py
msgid "Automoderation"
msgstr "Automodération"
msgid "Auto publication"
msgstr "Publication automatique"
#: com/models.py
msgid "alert message"
@ -1344,6 +1344,10 @@ msgstr "Le club qui organise l'évènement."
msgid "author"
msgstr "auteur"
#: com/models.py
msgid "is published"
msgstr "est publié"
#: com/models.py
msgid "news"
msgstr "nouvelle"
@ -1409,34 +1413,31 @@ msgid "Begin date should be before end date"
msgstr "La date de début doit être avant celle de fin"
#: com/templates/com/macros.jinja
msgid "Waiting moderation"
msgstr "En attente de modération"
msgid "Waiting publication"
msgstr "En attente de publication"
#: com/templates/com/macros.jinja
msgid ""
"This news isn't moderated and is visible only by its author and the "
"This news isn't published and is visible only by its author and the "
"communication admins."
msgstr ""
"Cette nouvelle n'est pas modérée et n'est visible que par son auteur et les "
"Cette nouvelle n'est pas publiée et n'est visible que par son auteur et les "
"admins communication."
#: com/templates/com/macros.jinja
msgid "It will stay hidden for other users until it has been moderated."
msgid "It will stay hidden for other users until it has been published."
msgstr ""
"Elle sera cachée pour les autres utilisateurs tant qu'elle ne sera pas "
"modérée."
"publiée."
#: com/templates/com/macros.jinja com/templates/com/mailing_admin.jinja
#: com/templates/com/news_admin_list.jinja com/templates/com/news_detail.jinja
#: core/templates/core/file_detail.jinja
#: core/templates/core/file_moderation.jinja sas/templates/sas/moderation.jinja
#: sas/templates/sas/picture.jinja
msgid "Moderate"
msgstr "Modérer"
#: com/templates/com/macros.jinja com/templates/com/news_admin_list.jinja
#: com/templates/com/news_detail.jinja
msgid "Publish"
msgstr "Publier"
#: com/templates/com/macros.jinja
msgid "News moderated"
msgstr "Nouvelle modérée"
msgid "News published"
msgstr "Nouvelle publiée"
#: com/templates/com/macros.jinja
msgid "News deleted"
@ -1447,6 +1448,12 @@ msgstr "Nouvelle supprimée"
msgid "Mailing lists administration"
msgstr "Administration des mailing listes"
#: com/templates/com/mailing_admin.jinja core/templates/core/file_detail.jinja
#: core/templates/core/file_moderation.jinja sas/templates/sas/moderation.jinja
#: sas/templates/sas/picture.jinja
msgid "Moderate"
msgstr "Modérer"
#: com/templates/com/mailing_admin.jinja
#, python-format
msgid "Moderated by %(user)s"
@ -1514,6 +1521,10 @@ msgstr "Modérateur"
msgid "Dates"
msgstr "Dates"
#: com/templates/com/news_admin_list.jinja
msgid "Unpublish"
msgstr "Dépublier"
#: com/templates/com/news_admin_list.jinja
msgid "Weeklies to moderate"
msgstr "Nouvelles hebdomadaires à modérer"
@ -6031,13 +6042,3 @@ msgstr "Vous ne pouvez plus écrire de commentaires, la date est passée."
#, python-format
msgid "Maximum characters: %(max_length)s"
msgstr "Nombre de caractères max: %(max_length)s"
#, python-format
#~ msgid ""
#~ "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."
#~ msgstr ""
#~ "Cet événement se déroulera chaque semaine pendant %%s semaines. Si vous "
#~ "modérez ou supprimez cet événement, il sera également modéré (ou "
#~ "supprimé) pour les semaines suivantes."

View File

@ -7,7 +7,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2025-02-25 14:38+0100\n"
"POT-Creation-Date: 2025-02-25 16:10+0100\n"
"PO-Revision-Date: 2024-09-17 11:54+0200\n"
"Last-Translator: Sli <antoine@bartuccio.fr>\n"
"Language-Team: AE info <ae.info@utbm.fr>\n"
@ -22,29 +22,30 @@ msgid "More info"
msgstr "Plus d'informations"
#: com/static/bundled/com/components/ics-calendar-index.ts
msgid "Moderate"
msgstr "Modérer"
msgid "Publish"
msgstr "Publier"
#: com/static/bundled/com/components/ics-calendar-index.ts
#: core/static/bundled/core/components/ajax-select-base.ts
msgid "Remove"
msgstr "Retirer"
msgid "Unpublish"
msgstr "Dépublier"
#: com/static/bundled/com/components/ics-calendar-index.ts
msgid "Delete"
msgstr "Supprimer"
#: com/static/bundled/com/components/moderation-alert-index.ts
#, javascript-format
msgid ""
"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."
"This event will take place every week for %s weeks. If you publish or delete "
"this event, it will also be published (or deleted) for the following weeks."
msgstr ""
"Cet événement se déroulera chaque semaine pendant %s semaines. Si vous "
"modérez ou supprimez cet événement, il sera également modéré (ou supprimé) "
"publiez ou supprimez cet événement, il sera également publié (ou supprimé) "
"pour les semaines suivantes."
#: core/static/bundled/core/components/ajax-select-base.ts
msgid "Remove"
msgstr "Retirer"
#: core/static/bundled/core/components/ajax-select-base.ts
msgid "You need to type %(number)s more characters"
msgstr "Vous devez taper %(number)s caractères de plus"