Skip to content

Commit

Permalink
Merge pull request #1379 from chris34/django4-timezones
Browse files Browse the repository at this point in the history
Django4 timezones: Enable & migrate ikhaya article model to DateTime field
  • Loading branch information
chris34 authored Jan 19, 2025
2 parents 8bf4f6d + 269283d commit 6b0dea2
Show file tree
Hide file tree
Showing 66 changed files with 1,343 additions and 1,091 deletions.
5 changes: 4 additions & 1 deletion ChangeLog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,20 @@ Deployment notes

✨ New features
---------------
* Wiki: Update metadata and content of related pages after a edit
* Wiki: Update metadata and content of related pages after a edit

🏗 Changes
----------
* ``*.pot`` files are no longer in git
* Enable timezon-aware datetimes from Django
* Fix deprecation warnings related to UTC methods

🗑 Deprecations
--------------

🔥 Removals
-----------
* Replace javascript based datetime picker with native HTML one

🐛 Fixes
--------
Expand Down
3 changes: 1 addition & 2 deletions inyoka/default_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

from celery.schedules import crontab

import inyoka

#: Base path of this application
BASE_PATH = dirname(__file__)
Expand All @@ -41,7 +40,7 @@
TIME_ZONE = 'Europe/Berlin'

# https://docs.djangoproject.com/en/3.2/topics/i18n/timezones/
USE_TZ = False
USE_TZ = True

# Language code for this installation. All choices can be found here:
# http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes
Expand Down
2 changes: 0 additions & 2 deletions inyoka/forum/jinja2/forum/forum_edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
#}
{%- extends 'forum/page.html' %}
{% from 'macros.html' import render_form %}
{% set styles = ['editor', 'datetimefield'] %}
{% set scripts = ['WikiEditor', 'DateTime'] %}

{% if not forum %}
{% set BREADCRUMBS = [(_('Create forum'), href('forum', 'forum', 'new'))] + BREADCRUMBS|d([]) %}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Generated by Django 4.2.13 on 2024-06-15 22:17

import django.utils.timezone
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("forum", "0016_auto_20230312_1704"),
]

operations = [
migrations.AlterField(
model_name="poll",
name="start_time",
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AlterField(
model_name="post",
name="pub_date",
field=models.DateTimeField(
db_index=True, default=django.utils.timezone.now
),
),
migrations.AlterField(
model_name="postrevision",
name="store_date",
field=models.DateTimeField(default=django.utils.timezone.now),
),
]
17 changes: 9 additions & 8 deletions inyoka/forum/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import os
import pickle
import re
from datetime import datetime
from datetime import datetime, timezone
from functools import reduce
from hashlib import md5
from itertools import groupby
Expand All @@ -26,6 +26,7 @@
from django.core.exceptions import PermissionDenied
from django.db import models, transaction
from django.db.models import Count, F, Max, QuerySet, Sum
from django.utils import timezone as dj_timezone
from django.utils.encoding import DjangoUnicodeDecodeError, force_str
from django.utils.html import escape, format_html
from django.utils.translation import gettext as _
Expand Down Expand Up @@ -779,7 +780,7 @@ class PostRevision(models.Model):
"""

text = InyokaMarkupField(application='forum')
store_date = models.DateTimeField(default=datetime.utcnow)
store_date = models.DateTimeField(default=dj_timezone.now)
post = models.ForeignKey('forum.Post', related_name='revisions', on_delete=models.CASCADE)

def get_absolute_url(self, action='restore'):
Expand Down Expand Up @@ -815,7 +816,7 @@ class Post(models.Model, LockableObject):
lock_key_base = 'forum/post_lock'

position = models.IntegerField(default=None, db_index=True)
pub_date = models.DateTimeField(default=datetime.utcnow, db_index=True)
pub_date = models.DateTimeField(default=dj_timezone.now, db_index=True)
hidden = models.BooleanField(default=False)
text = InyokaMarkupField(application='forum')
has_revision = models.BooleanField(default=False)
Expand Down Expand Up @@ -1095,7 +1096,8 @@ def check_ownpost_limit(self, type='edit'):
return False
if t == -1:
return True
delta = datetime.utcnow() - self.pub_date.replace(tzinfo=None)

delta = datetime.now(timezone.utc) - self.pub_date
return delta.total_seconds() < t

def mark_ham(self):
Expand Down Expand Up @@ -1245,8 +1247,7 @@ def update_post_ids(att_ids, post):
return False

attachments = Attachment.objects.filter(id__in=att_ids, post=None).all()

base_path = datetime.utcnow().strftime('forum/attachments/%S/%W')
base_path = dj_timezone.now().strftime('forum/attachments/%S/%W')

for attachment in attachments:
new_name = secure_filename('%d-%s' % (post.pk, attachment.name))
Expand Down Expand Up @@ -1386,7 +1387,7 @@ class Meta:

class Poll(models.Model):
question = models.CharField(max_length=250)
start_time = models.DateTimeField(default=datetime.utcnow)
start_time = models.DateTimeField(default=dj_timezone.now)
end_time = models.DateTimeField(null=True)
multiple_votes = models.BooleanField(default=False)

Expand All @@ -1405,7 +1406,7 @@ def participated(self):
@property
def ended(self):
"""Returns a boolean whether the poll ended already"""
return self.end_time and datetime.utcnow() > self.end_time
return self.end_time and dj_timezone.now() > self.end_time

@deferred
def can_vote(self):
Expand Down
16 changes: 10 additions & 6 deletions inyoka/forum/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
:copyright: (c) 2007-2024 by the Inyoka Team, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
from datetime import datetime, timedelta
from datetime import timedelta
from functools import partial
from itertools import groupby
from operator import attrgetter
Expand All @@ -18,8 +18,10 @@
from django.core.cache import cache
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
from django.db.models import F, Q
from django.db.models.functions import Now
from django.http import Http404, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect
from django.utils import timezone as dj_timezone
from django.utils.translation import gettext as _
from django.utils.translation import gettext_lazy
from django.views.generic import CreateView, DetailView, UpdateView
Expand Down Expand Up @@ -327,9 +329,11 @@ def handle_polls(request, topic, poll_ids):

if 'add_poll' in request.POST and poll_form.is_valid():
d = poll_form.cleaned_data
now = datetime.utcnow()
end_time = (d['duration'] and now + timedelta(days=d['duration'])
or None)
now = dj_timezone.now()
if d['duration']:
end_time = now + timedelta(days=d['duration'])
else:
end_time = None
poll = Poll(topic=topic, question=d['question'],
multiple_votes=d['multiple'],
start_time=now, end_time=end_time)
Expand Down Expand Up @@ -624,7 +628,7 @@ def edit(request, forum_slug=None, topic_slug=None, post_id=None,
if not post: # not when editing an existing post
doublepost = Post.objects \
.filter(author=request.user, text=d['text'],
pub_date__gt=(datetime.utcnow() - timedelta(0, 300)))
pub_date__gt=(Now() - timedelta(0, 300)))
if not newtopic:
doublepost = doublepost.filter(topic=topic)
try:
Expand Down Expand Up @@ -1626,7 +1630,7 @@ def topiclist(request, page=1, action='newposts', hours=24, user=None, forum=Non
hours = int(hours)
if hours > 24:
raise Http404()
topics = topics.filter(posts__pub_date__gt=datetime.utcnow() - timedelta(hours=hours))
topics = topics.filter(posts__pub_date__gt=Now() - timedelta(hours=hours))
topics = topics.distinct()
title = _('Posts of the last %(n)d hours') % {'n': hours}
url = href('forum', 'last%d' % hours, forum)
Expand Down
79 changes: 36 additions & 43 deletions inyoka/ikhaya/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,19 @@
from datetime import time as dt_time

from django import forms
from django.forms import SplitDateTimeField
from django.utils import timezone as dj_timezone
from django.utils.timezone import get_current_timezone
from django.utils.translation import gettext_lazy

from inyoka.ikhaya.models import Article, Category, Event, Suggestion
from inyoka.portal.models import StaticFile
from inyoka.utils.dates import datetime_to_timezone
from inyoka.utils.forms import (
DateTimeField,
DateWidget,
NativeDateInput,
NativeSplitDateTimeWidget,
NativeTimeInput,
StrippedCharField,
TimeWidget,
UserField,
)
from inyoka.utils.text import slugify
Expand Down Expand Up @@ -55,72 +57,63 @@ class EditArticleForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
instance = kwargs.get('instance')
readonly = kwargs.pop('readonly', False)

if instance:
initial = kwargs.setdefault('initial', {})
if instance.pub_datetime != instance.updated:
initial['updated'] = instance.updated
if instance.public and not instance.updated:
initial['updated'] = dj_timezone.now()
initial['author'] = instance.author.username

super().__init__(*args, **kwargs)
# Following stuff is in __init__ to keep helptext etc intact.

self.fields['icon'].queryset = StaticFile.objects.filter(is_ikhaya_icon=True)
if readonly:
for field in ('subject', 'intro', 'text'):
self.fields[field].widget.attrs['readonly'] = True

if not instance:
del self.fields['updated']

author = UserField(label=gettext_lazy('Author'), required=True)
updated = DateTimeField(label=gettext_lazy('Last update'),
help_text=gettext_lazy('If you keep this field empty, the '
'publication date will be used.'),
localize=True, required=False)

def save(self):
instance = super().save(commit=False)
if 'pub_date' in self.cleaned_data and (not instance.pk or
not instance.public or self.cleaned_data.get('public', None)):
instance.pub_date = self.cleaned_data['pub_date']
instance.pub_time = self.cleaned_data['pub_time']
if self.cleaned_data.get('updated', None):
instance.updated = self.cleaned_data['updated']
elif {'pub_date', 'pub_time'} in set(self.cleaned_data.keys()):
instance.updated = datetime.combine(
self.cleaned_data['pub_date'],
self.cleaned_data['pub_time'])
instance.save()
return instance

def clean_slug(self):
slug = self.cleaned_data['slug']
pub_date = self.cleaned_data.get('pub_date', None)
if slug and pub_date:
pub_datetime = self.cleaned_data.get('publication_datetime', None)

if slug and pub_datetime:
slug = slugify(slug)
q = Article.objects.filter(slug=slug, pub_date=pub_date)

q = Article.objects.annotate_publication_date_utc()
q = q.filter(slug=slug, publication_date_utc=pub_datetime)

if self.instance.pk:
q = q.exclude(id=self.instance.pk)

if q.exists():
raise forms.ValidationError(gettext_lazy('There already '
'exists an article with this slug!'))
return slug

class Meta:
model = Article
exclude = ['updated', 'comment_count']
fields = ('subject', 'intro', 'text', 'author', 'category', 'icon', 'public', 'comments_enabled', 'updated', 'publication_datetime', 'slug')
field_classes = {
'updated': SplitDateTimeField,
'publication_datetime': SplitDateTimeField,
}
widgets = {
'subject': forms.TextInput(attrs={'size': 50}),
'intro': forms.Textarea(attrs={'rows': 3}),
'text': forms.Textarea(attrs={'rows': 15}),
'pub_date': DateWidget(),
'pub_time': TimeWidget(),
'subject': forms.TextInput(),
'intro': forms.Textarea(),
'text': forms.Textarea(),
'publication_datetime': NativeSplitDateTimeWidget(),
'updated': NativeSplitDateTimeWidget(),
}


class EditPublicArticleForm(EditArticleForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
del self.fields['pub_date']
del self.fields['pub_time']

class Meta(EditArticleForm.Meta):
exclude = EditArticleForm.Meta.exclude + ['slug']
exclude = ['slug', 'publication_datetime']


class EditCategoryForm(forms.ModelForm):
Expand Down Expand Up @@ -195,10 +188,10 @@ def clean(self):
class Meta:
model = Event
widgets = {
'date': DateWidget,
'time': TimeWidget,
'enddate': DateWidget,
'endtime': TimeWidget,
'date': NativeDateInput,
'time': NativeTimeInput,
'enddate': NativeDateInput,
'endtime': NativeTimeInput,
}
exclude = ['author', 'slug', 'visible']

Expand Down
16 changes: 6 additions & 10 deletions inyoka/ikhaya/jinja2/ikhaya/article_edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
#}
{%- extends 'ikhaya/page.html' %}
{% from 'macros.html' import render_form %}
{% set styles = ['editor', 'datetimefield'] %}
{% set scripts = ['WikiEditor', 'DateTime'] %}
{% set styles = ['editor'] %}
{% set scripts = ['WikiEditor'] %}

{% if not article %}
{% set BREADCRUMBS = [(_('New Article'), href('ikhaya', 'new'))] + BREADCRUMBS|d([]) %}
{% set BREADCRUMBS = [(_('New Article'), href('ikhaya', 'article', 'new'))] + BREADCRUMBS|d([]) %}
{% else %}
{% set BREADCRUMBS = [(article.subject, article|url),
(_('Edit'), article|url('edit'))] + BREADCRUMBS|d([]) %}
Expand All @@ -36,13 +36,9 @@ <h3>{% trans %}New Article{% endtrans %}</h3>
{%- if article.article_icon %}
<div class="icon"><img src="{{ article.article_icon|url }}" alt="{{ article.article_icon.identifier|e }}"></div>
{%- endif %}
<dl>
{{ render_form(form, ['subject', 'intro', 'text', 'author', 'category', 'icon', 'public', 'comments_enabled'], inline=true) }}
{%- if article != none %}
{{ render_form(form, ['updated'], inline=true) }}
{%- endif %}
{{ render_form(form, ['pub_date', 'pub_time', 'slug'], inline=true) }}
</dl>

{{ form.as_div() }}

<p>
<input type="submit" value="{% trans %}Submit{% endtrans %}" name="send">
<input type="submit" value="{% trans %}Preview{% endtrans %}" name="preview">
Expand Down
4 changes: 2 additions & 2 deletions inyoka/ikhaya/jinja2/ikhaya/detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ <h3 class="title"><a href="{{ article|url }}">{{ article.subject|e }}</a></h3>
{% trans link=article.author|url, author=article.author.username|e -%}
Published by <a href="{{ link }}">{{ author }}</a>
{%- endtrans %} |
{{ article.pub_datetime|datetime }} |
{{ article.publication_datetime|datetime }} |
{% trans %}Category:{% endtrans %} <a href="{{ article.category|url }}">{{ article.category.name|e }}</a>
{%- if article.updated > article.pub_datetime %}
{%- if article.is_updated %}
| {% trans %}Last update:{% endtrans %} {{ article.updated|datetime }}
{%- endif %}
| <a href="{{ article|url('id') }}">#</a>
Expand Down
Loading

0 comments on commit 6b0dea2

Please sign in to comment.