Rename news moderate to publish

This commit is contained in:
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")