Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Date and time fields use browser-default selection widgets #4202

Merged
merged 11 commits into from
May 30, 2024
8 changes: 5 additions & 3 deletions src/comms/forms.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
from django import forms

from django_summernote.widgets import SummernoteWidget

from comms import models
from utils.forms import HTMLDateInput


class NewsItemForm(forms.ModelForm):


image_file = forms.FileField(required=False)
tags = forms.CharField(required=False)

Expand All @@ -22,3 +20,7 @@ def save(self, commit=True):
class Meta:
model = models.NewsItem
exclude = ('content_type', 'object_id', 'posted', 'posted_by', 'large_image_file', 'tags')
widgets = {
'start_display': HTMLDateInput,
'end_display': HTMLDateInput,
}
5 changes: 5 additions & 0 deletions src/copyediting/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from core.models import Account
from core import forms as core_forms
from copyediting import models
from utils.forms import HTMLDateInput


class CopyeditAssignmentForm(forms.ModelForm, core_forms.ConfirmableIfErrorsForm):
Expand All @@ -29,6 +30,7 @@ class Meta:
widgets = {
'files_for_copyediting': forms.CheckboxSelectMultiple(attrs=attrs),
'copyeditor': forms.RadioSelect(attrs=attrs),
'due': HTMLDateInput,
}

def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -81,6 +83,9 @@ class Meta:
'editor_note',
'due',
)
widgets = {
'due': HTMLDateInput,
}


class CopyEditForm(forms.ModelForm):
Expand Down
15 changes: 15 additions & 0 deletions src/core/model_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -613,3 +613,18 @@ def default_press_id():
default_press_obj = default_press()
if default_press_obj:
return default_press_obj.pk


class DateTimePickerInput(forms.DateTimeInput):
format_key = 'DATETIME_INPUT_FORMATS'
template_name = 'admin/core/widgets/datetimepicker.html'


class DateTimePickerFormField(forms.DateTimeField):
widget = DateTimePickerInput


class DateTimePickerModelField(models.DateTimeField):
def formfield(self, **kwargs):
kwargs['form_class'] = DateTimePickerFormField
return super().formfield(**kwargs)
25 changes: 0 additions & 25 deletions src/journal/logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

from bs4 import BeautifulSoup
import csv
from dateutil import parser as dateparser
import os
from os import listdir, makedirs
from os.path import isfile, join
Expand All @@ -22,7 +21,6 @@
from django.utils.translation import gettext_lazy as _
from django.template.loader import get_template
from django.core.validators import validate_email, ValidationError
from django.utils.timezone import make_aware

from core import models as core_models, files
from journal import models as journal_models, issue_forms
Expand Down Expand Up @@ -250,29 +248,6 @@ def handle_unassign_issue(request, article, issues):
messages.add_message(request, messages.WARNING, 'Issue does not exist.')


def handle_set_pubdate(request, article):
date = request.POST.get('date')
time = request.POST.get('time')

date_time_str = "{0} {1}".format(date, time)

try:
date_time = dateparser.parse(date_time_str)
article.date_published = make_aware(date_time)
article.fixedpubcheckitems.set_pub_date = True
article.fixedpubcheckitems.save()
article.save()

messages.add_message(
request, messages.SUCCESS,
'Publication Date set to {0}'.format(article.date_published)
)

return [date_time, []]
except ValueError:
return [date_time_str, ['Not a recognised Date/Time format. Date: 2016-12-16, Time: 20:20.']]


def get_notify_author_text(request, article):
context = {
'article': article,
Expand Down
40 changes: 28 additions & 12 deletions src/journal/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.decorators import login_required
from django.contrib.contenttypes.models import ContentType
from django.contrib.humanize.templatetags.humanize import naturaltime
from django.templatetags.static import static
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.core.files.base import ContentFile
Expand Down Expand Up @@ -1016,6 +1017,7 @@ def publish_article(request, article_id):
:param article_id: Article PK
:return: contextualised django template
"""
from submission import forms as submission_forms
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does having this import in the head cause a circular import?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I believe so. There is another function in this file that has the same import.

article = get_object_or_404(
submission_models.Article,
Q(stage=submission_models.STAGE_READY_FOR_PUBLICATION) |
Expand All @@ -1028,14 +1030,14 @@ def publish_article(request, article_id):
doi_data, doi = logic.get_doi_data(article)
issues = request.journal.issues
new_issue_form = issue_forms.NewIssue(journal=article.journal)
pub_date_form = submission_forms.PubDateForm(instance=article)
notify_author_email_form = core_forms.SimpleTinyMCEForm(
'email_to_author',
initial = {
'email_to_author': logic.get_notify_author_text(request, article)
}
)
modal = request.GET.get('m', None)
pubdate_errors = []

if request.POST:
if 'assign_issue' in request.POST:
Expand Down Expand Up @@ -1083,19 +1085,33 @@ def publish_article(request, article_id):
)

if 'pubdate' in request.POST:
date_set, pubdate_errors = logic.handle_set_pubdate(
request,
article,
pub_date_form = submission_forms.PubDateForm(
request.POST,
instance=article,
)
if not pubdate_errors:
return redirect(
reverse(
'publish_article',
kwargs={'article_id': article.pk},
if pub_date_form.is_valid():
article = pub_date_form.save()
if article.date_published:
messages.add_message(
request, messages.SUCCESS,
_(
f'Publication date set to { article.date_published.strftime("%Y-%m-%d %H:%M %Z") } '
f'({ naturaltime(article.date_published) })'
)
)
else:
messages.add_message(
request, messages.SUCCESS,
_('Publication date unset')
)
)
else:
modal = 'pubdate'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this unset on purpose? When the form is invalid generally we'd want to open the modal again.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I unset it because there is a general redirect that resets the view and keeps the modal = 'pubdate' from having any effect, and I didn't want to touch the redirect because I'd have to test all the other view branches:

https://github.com/BirkbeckCTP/janeway/blob/e740dbc8ce197360d8cf3e527267b1d7f8d8f133/src/journal/views.py#L1205-L1210

messages.add_message(
request, messages.WARNING,
_(
f'Something went wrong when trying to save the form. '
f'Please try again.'
)
)

if 'author' in request.POST:
logic.notify_author(request, article)
Expand Down Expand Up @@ -1201,7 +1217,7 @@ def publish_article(request, article_id):
'issues': issues,
'new_issue_form': new_issue_form,
'modal': modal,
'pubdate_errors': pubdate_errors,
'pub_date_form': pub_date_form,
'notify_author_email_form': notify_author_email_form,
}

Expand Down
12 changes: 12 additions & 0 deletions src/review/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ class ReviewAssignmentForm(forms.ModelForm, core_forms.ConfirmableIfErrorsForm):
class Meta:
model = models.ReviewAssignment
fields = ('visibility', 'form', 'date_due', 'reviewer')
widgets = {
'date_due': HTMLDateInput,
}

def __init__(self, *args, **kwargs):
self.journal = kwargs.pop('journal', None)
Expand Down Expand Up @@ -164,6 +167,9 @@ class EditReviewAssignmentForm(forms.ModelForm):
class Meta:
model = models.ReviewAssignment
fields = ('visibility', 'form', 'date_due')
widgets = {
'date_due': HTMLDateInput,
}

def __init__(self, *args, **kwargs):
self.journal = kwargs.pop('journal', None)
Expand Down Expand Up @@ -240,6 +246,9 @@ class Meta:
fields = (
'date_due', 'type', 'editor_note',
)
widgets = {
'date_due': HTMLDateInput,
}

def __init__(self, *args, **kwargs):
self.editor = kwargs.pop('editor', None)
Expand Down Expand Up @@ -272,6 +281,9 @@ class Meta:
fields = (
'date_due',
)
widgets = {
'date_due': HTMLDateInput,
}


class DoRevisions(forms.ModelForm, core_forms.ConfirmableForm):
Expand Down
2 changes: 1 addition & 1 deletion src/submission/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class FrozenAuthorAdmin(admin_utils.ArticleFKModelAdmin):
raw_id_fields = ('article', 'author',)


class ArticleAdmin(admin.ModelAdmin):
class ArticleAdmin(admin_utils.JanewayModelAdmin):
list_display = ('pk', 'title', 'correspondence_author',
'journal', 'date_submitted', 'stage',
'owner', 'is_import')
Expand Down
16 changes: 16 additions & 0 deletions src/submission/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,3 +524,19 @@ def utility_clean_orcid(orcid):

# ORCID is None.
return orcid


class PubDateForm(forms.ModelForm):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great.

class Meta:
model = models.Article
fields = ('date_published',)

def save(self, commit=True):
article = super().save(commit=commit)
if commit:
article.fixedpubcheckitems.set_pub_date = bool(
article.date_published
)
article.fixedpubcheckitems.save()
article.save()
return article
19 changes: 19 additions & 0 deletions src/submission/migrations/0076_alter_article_date_published.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 3.2.20 on 2024-05-24 17:29

import core.model_utils
from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('submission', '0075_auto_20240312_0922'),
]

operations = [
migrations.AlterField(
model_name='article',
name='date_published',
field=core.model_utils.DateTimePickerModelField(blank=True, null=True),
),
]
3 changes: 2 additions & 1 deletion src/submission/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
JanewayBleachField,
JanewayBleachCharField,
M2MOrderedThroughField,
DateTimePickerModelField,
)
from core import workflow, model_utils, files, models as core_models
from core.templatetags.truncate import truncatesmart
Expand Down Expand Up @@ -706,7 +707,7 @@ class Article(AbstractLastModifiedModel):
date_accepted = models.DateTimeField(blank=True, null=True)
date_declined = models.DateTimeField(blank=True, null=True)
date_submitted = models.DateTimeField(blank=True, null=True)
date_published = models.DateTimeField(blank=True, null=True)
date_published = DateTimePickerModelField(blank=True, null=True)
date_updated = models.DateTimeField(blank=True, null=True)
current_step = models.IntegerField(default=1)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ <h2>3. Set Options</h2>
{% include "admin/elements/open_modal.html" with target=form.modal.id %}
{% endif %}
{% include "elements/datatables.html" with target="#copyeditor" sort=2 order='asc' %}
{% include "elements/datepicker.html" with target="#id_due" %}
{% include "elements/jqte.html" %}
<script src="{% static "admin/js/csrf.js" %}"></script>
<script src="{% static "common/js/accountrole.js" %}"></script>
Expand Down
3 changes: 1 addition & 2 deletions src/templates/admin/copyediting/edit_assignment.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,5 @@ <h2>Assignment #{{ copyedit.pk }}</h2>
{% endblock %}

{% block js %}
{% include "elements/datepicker.html" with target="#id_due" %}
{% include "elements/jqte.html" %}
{% endblock %}
{% endblock %}
3 changes: 1 addition & 2 deletions src/templates/admin/copyediting/editor_review.html
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ <h4><i class="fa fa-inbox">&nbsp;</i>Reopen Copyedit</h4>
{% csrf_token %}
<textarea name="reset_note">{{ reopen_message|safe|linebreaksbr }}</textarea>
<label for="due">Due Date</label>
<input type="text" name="due" id="due" required="required">
<input type="date" name="due" id="due" required="required">
<button name="review" type="submit" class="button success"><i class="fa fa-envelope-o">&nbsp;</i>Send
</button>
</form>
Expand All @@ -260,5 +260,4 @@ <h4><i class="fa fa-inbox">&nbsp;</i>Reopen Copyedit</h4>
{% include "admin/elements/open_modal.html" with target=author_review_form.modal.id %}
{% endif %}
{% include "elements/jqte.html" %}
{% include "elements/datepicker.html" with target='#due' %}
{% endblock js %}
2 changes: 0 additions & 2 deletions src/templates/admin/core/manager/news/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,6 @@ <h2>{% if action == 'new' %}Add New News Item{% else %}Edit News Item{% endif %}
{% endblock body %}

{% block js %}
{% include "elements/datepicker.html" with target="#id_start_display" %}
{% include "elements/datepicker.html" with target="#id_end_display" %}
<link rel="stylesheet" href="https://code.jquery.com/ui/1.11.0/themes/smoothness/jquery-ui.css">
<script type="text/javascript" src="{% static "common/js/jq-ui.min.js" %}"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tag-it/2.0/js/tag-it.js"></script>
Expand Down
7 changes: 7 additions & 0 deletions src/templates/admin/core/widgets/datetimepicker.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<input
type="datetime-local"
name="{{ widget.name }}"
{% if widget.value != None %}
value="{{ widget.value|slice:'0:10'}}T{{widget.value|slice:'11:'}}"
{% endif %}
{% include "django/forms/widgets/attrs.html" %}>
18 changes: 2 additions & 16 deletions src/templates/admin/elements/publish/pubdate.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
<h4><i class="fa fa-folder-open-o">&nbsp;</i>Set Publication Date/Time</h4>
</div>
<div class="card-section">
{% include "elements/forms/generic_errors.html" with errors=pubdate_errors %}
<h5>Publication Date/Time</h5>
<p>You can set the publication date and time here so that even when an article is marked as published, users will not
be able to view it until the date/time has past.</p>
Expand All @@ -18,21 +17,8 @@ <h5>Publication Date/Time</h5>
{% endif %}
<form method="POST">
{% csrf_token %}
<div class="callout">
<div class="row">
<div class="large-8 columns">
<label for="date">Date</label>
<input type="text" name="date" id="date" required {% if article.date_published %}value="{{ article.date_published|date:'Y-m-d' }}"{% endif %}>
</div>
<div class="large-4 columns">
<label for="time">Time</label>
<input type="text" name="time" id="time" required {% if article.date_published %}value="{{ article.date_published|date:'H:i' }}"{% endif %}>
</div>
<div class="large-12 columns">
<button type="submit" name="pubdate" class="small success button">Set Publication Date</button>
</div>
</div>
</div>
{{ pub_date_form }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is being output in table format. Perhaps use as_p or the foundation tag?
Screenshot 2024-05-29 at 13 26 41

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh, that is wild. Firefox clears it in my inspector pane (after parsing the HTML) but I can see it when I inspect the source. Is this a Django default feature, or something we have configured? In any case, I've added the foundation filter.

<button type="submit" name="pubdate" class="small success button">Set Publication Date</button>
</form>
<button class="close-button" data-close aria-label="Close reveal" type="button">
<span aria-hidden="true">&times;</span>
Expand Down
Loading