diff --git a/src/locale/en_US/LC_MESSAGES/django.po b/src/locale/en_US/LC_MESSAGES/django.po
index c2d4e654a..9c6def55c 100644
--- a/src/locale/en_US/LC_MESSAGES/django.po
+++ b/src/locale/en_US/LC_MESSAGES/django.po
@@ -1571,6 +1571,14 @@ msgstr "Change Password"
msgid "Reviews"
msgstr "Reviews"
+#: templates/default/_includes/dashboard_tablist.html:28
+#: templates/default/reviews/review_stages.html:16
+#: templates/default/reviews/review_stages.html:44
+#, fuzzy
+#| msgid "Reviews"
+msgid "Review Stages"
+msgstr "Reviews"
+
#: templates/default/_includes/nav/dashboard_nav.html:10
msgid "Log out"
msgstr "Log out"
@@ -1799,6 +1807,7 @@ msgstr "Change Log In Password"
#: templates/default/registration/password_change_form.html:22
#: templates/default/reviews/review_form.html:101
+#: templates/default/reviews/review_stages.html:114
msgid "Submit"
msgstr "Submit"
@@ -2018,6 +2027,18 @@ msgstr "My Previous Reviews"
msgid "Review Proposal"
msgstr "Review Proposal"
+#: templates/default/reviews/review_stages.html:23
+#, fuzzy
+#| msgid "Current Review Stage: %(review_stage)s"
+msgid "Current Review Stage Setting"
+msgstr "Current Review Stage: %(review_stage)s"
+
+#: templates/default/reviews/review_stages.html:35
+#, fuzzy
+#| msgid "Personal Review Stats"
+msgid "Set Review Stage"
+msgstr "Personal Review Stats"
+
#: templates/default/reviews/talk_proposal_list.html:14
#, python-format
msgid "Current Review Stage: %(review_stage)s"
@@ -5116,28 +5137,28 @@ msgstr "latest agreed CoC version"
msgid "agreed at"
msgstr "agreed at"
-#: users/views.py:44
+#: users/views.py:48
msgid "Sign up successful. You are now logged in."
msgstr "Sign up successful. You are now logged in."
-#: users/views.py:61
+#: users/views.py:65
msgid "Email verification successful."
msgstr "Email verification successful."
-#: users/views.py:73
+#: users/views.py:77
#, python-brace-format
msgid "A verification email has been sent to {email}"
msgstr "A verification email has been sent to {email}"
-#: users/views.py:103
+#: users/views.py:107
msgid "Your profile has been updated successfully."
msgstr "Your profile has been updated successfully."
-#: users/views.py:116
+#: users/views.py:120
msgid "Your new password has been applied successfully."
msgstr "Your new password has been applied successfully."
-#: users/views.py:123
+#: users/views.py:127
msgid ""
"An email is sent to your email account. Please check your inbox for furthur "
"instructions to reset your password."
@@ -5145,7 +5166,7 @@ msgstr ""
"An email is sent to your email account. Please check your inbox for furthur "
"instructions to reset your password."
-#: users/views.py:131
+#: users/views.py:135
msgid "Password reset successful. You can now login."
msgstr "Password reset successful. You can now login."
diff --git a/src/locale/zh_Hant/LC_MESSAGES/django.po b/src/locale/zh_Hant/LC_MESSAGES/django.po
index 6f34ddf89..a890cf686 100644
--- a/src/locale/zh_Hant/LC_MESSAGES/django.po
+++ b/src/locale/zh_Hant/LC_MESSAGES/django.po
@@ -1524,6 +1524,12 @@ msgstr "更改密碼"
msgid "Reviews"
msgstr "審查"
+#: templates/default/_includes/dashboard_tablist.html:28
+#: templates/default/reviews/review_stages.html:16
+#: templates/default/reviews/review_stages.html:44
+msgid "Review Stages"
+msgstr "審查階段"
+
#: templates/default/_includes/nav/dashboard_nav.html:10
msgid "Log out"
msgstr "登出"
@@ -1747,6 +1753,7 @@ msgstr "更改密碼"
#: templates/default/registration/password_change_form.html:22
#: templates/default/reviews/review_form.html:101
+#: templates/default/reviews/review_stages.html:114
msgid "Submit"
msgstr "送出"
@@ -1960,6 +1967,14 @@ msgstr "我之前的審查意見"
msgid "Review Proposal"
msgstr "審查提案"
+#: templates/default/reviews/review_stages.html:23
+msgid "Current Review Stage Setting"
+msgstr "目前審查階段設定"
+
+#: templates/default/reviews/review_stages.html:35
+msgid "Set Review Stage"
+msgstr "設定審查階段"
+
#: templates/default/reviews/talk_proposal_list.html:14
#, python-format
msgid "Current Review Stage: %(review_stage)s"
@@ -4991,35 +5006,35 @@ msgstr "最後同意的 CoC 版本"
msgid "agreed at"
msgstr "同意於"
-#: users/views.py:44
+#: users/views.py:48
msgid "Sign up successful. You are now logged in."
msgstr "註冊成功。您現在已被登入。"
-#: users/views.py:61
+#: users/views.py:65
msgid "Email verification successful."
msgstr "Email 認證成功。"
-#: users/views.py:73
+#: users/views.py:77
#, python-brace-format
msgid "A verification email has been sent to {email}"
msgstr "一封認證用的 email 已經寄送至 {email}"
-#: users/views.py:103
+#: users/views.py:107
msgid "Your profile has been updated successfully."
msgstr "您的講者資訊已成功地更新。"
-#: users/views.py:116
+#: users/views.py:120
msgid "Your new password has been applied successfully."
msgstr "已成功地使用您的新密碼。"
-#: users/views.py:123
+#: users/views.py:127
msgid ""
"An email is sent to your email account. Please check your inbox for furthur "
"instructions to reset your password."
msgstr ""
"一封電子郵件已經發送至您的信箱。請檢查您的收件匣並參照信中指示重設密碼。"
-#: users/views.py:131
+#: users/views.py:135
msgid "Password reset successful. You can now login."
msgstr "密碼重設成功。您現在即可登入。"
diff --git a/src/proposals/templatetags/proposals.py b/src/proposals/templatetags/proposals.py
index b8a245e88..d24ee2aa3 100644
--- a/src/proposals/templatetags/proposals.py
+++ b/src/proposals/templatetags/proposals.py
@@ -1,3 +1,4 @@
+from django.conf import settings
from django.template import Library
from proposals.utils import SEP_DEFAULT, SEP_LAST, format_names
@@ -7,6 +8,11 @@
@register.filter
def speaker_names_display(
- proposal, sep_default=SEP_DEFAULT, sep_last=SEP_LAST):
+ proposal, sep_default=SEP_DEFAULT, sep_last=SEP_LAST):
names = [info.user.speaker_name for info in proposal.speakers]
return format_names(names, sep_default=sep_default, sep_last=sep_last)
+
+
+@register.filter
+def configuration_switch(value):
+ return settings.CONFERENCE_DEFAULT_SLUG + value
diff --git a/src/proposals/tests/test_templatetags.py b/src/proposals/tests/test_templatetags.py
index 97ba79818..0dc2de2f5 100644
--- a/src/proposals/tests/test_templatetags.py
+++ b/src/proposals/tests/test_templatetags.py
@@ -53,3 +53,34 @@ def test_speaker_names_display(talk_proposals, parser):
""")
assert actual == expected
+
+@pytest.fixture
+def review_stage_keys():
+ review_stage_keys = [
+ '.proposals.creatable', '.proposals.editable', '.proposals.withdrawable',
+ '.reviews.visible.to.submitters', '.reviews.stage',
+ '.proposals.disable.after'
+ ]
+ return review_stage_keys
+
+def test_configuration_switch(review_stage_keys, parser):
+ result = render_template(
+ '{% load proposals %}'
+ '
'
+ '{% for review_stage_key in proposals %}'
+ '- {{ review_stage_key|configuration_switch }}
'
+ '{% endfor %}'
+ '
', {'proposals': review_stage_keys},
+ )
+ actual = parser.arrange(parser.parse(text=result, create_parent=False))
+ expected = parser.arrange("""
+
+ - pycontw-2021.proposals.creatable
+ - pycontw-2021.proposals.editable
+ - pycontw-2021.proposals.withdrawable
+ - pycontw-2021.reviews.visible.to.submitters
+ - pycontw-2021.reviews.stage
+ - pycontw-2021.proposals.disable.after
+
+ """)
+ assert actual == expected
diff --git a/src/reviews/tests.py b/src/reviews/tests/__init__.py
similarity index 100%
rename from src/reviews/tests.py
rename to src/reviews/tests/__init__.py
diff --git a/src/reviews/tests/test_views.py b/src/reviews/tests/test_views.py
new file mode 100644
index 000000000..6a837c77e
--- /dev/null
+++ b/src/reviews/tests/test_views.py
@@ -0,0 +1,82 @@
+from unittest.mock import patch
+
+import pytest
+from django.urls import reverse
+
+
+@pytest.fixture
+def review_stages_url():
+ return reverse('review_stages')
+
+@pytest.mark.django_db
+def test_review_stages_get(client, review_stages_url):
+ response = client.get(review_stages_url)
+
+ assert response.status_code == 200
+ assert 'review_stages_list' in response.context
+ assert 'current_review_stages_setting' in response.context
+
+@pytest.mark.django_db
+@patch('reviews.views.reg')
+@patch('reviews.views.messages')
+def test_review_stages_post_valid_data(mock_messages, mock_reg, client, review_stages_url):
+ mock_reg.get.return_value = ''
+ mock_reg.__setitem__.side_effect = lambda key, value: None
+
+ post_data = {
+ 'proposals.creatable': 'True',
+ 'proposals.editable': 'True',
+ 'proposals.withdrawable': 'True',
+ 'reviews.visible.to.submitters': 'True',
+ 'reviews.stage': '1',
+ 'proposals.disable.after': '2024-12-31T12:00',
+ 'review_timezone': 'UTC',
+ }
+
+ response = client.post(review_stages_url, post_data)
+
+ assert response.status_code == 200
+ mock_messages.info.assert_called_once_with(
+ response.wsgi_request, 'This setting has been changed successfully.'
+ )
+
+@pytest.mark.django_db
+@patch('reviews.views.reg')
+@patch('reviews.views.messages')
+def test_review_stages_post_invalid_date(mock_messages, mock_reg, client, review_stages_url):
+ post_data = {
+ 'proposals.creatable': 'True',
+ 'proposals.editable': 'True',
+ 'proposals.withdrawable': 'True',
+ 'reviews.visible.to.submitters': 'True',
+ 'reviews.stage': '1',
+ 'proposals.disable.after': 'invalid-date',
+ 'review_timezone': '',
+ }
+
+ response = client.post(review_stages_url, post_data)
+
+ assert response.status_code == 200
+ mock_messages.error.assert_called_once_with(
+ response.wsgi_request, 'Please input valid date format : " + "%Y-%m-%dT%H:%M'
+ )
+
+@pytest.mark.django_db
+def test_review_stages_timezone_conversion(client, review_stages_url):
+ post_data = {
+ 'proposals.creatable': 'True',
+ 'proposals.editable': 'True',
+ 'proposals.withdrawable': 'True',
+ 'reviews.visible.to.submitters': 'True',
+ 'reviews.stage': '1',
+ 'proposals.disable.after': '2024-12-31T12:00',
+ 'review_timezone': 'Asia/Taipei',
+ }
+
+ with patch('reviews.views.pytz.timezone') as mock_timezone:
+ mock_timezone.return_value.localize.return_value.strftime.return_value = '2024-12-31 12:00:00-0500'
+ response = client.post(review_stages_url, post_data)
+
+ assert response.status_code == 200
+ assert mock_timezone.call_count == 1
+ assert mock_timezone.called_with('Asia/Taipei')
diff --git a/src/reviews/urls.py b/src/reviews/urls.py
index f81b1df27..2c18103ee 100644
--- a/src/reviews/urls.py
+++ b/src/reviews/urls.py
@@ -1,9 +1,11 @@
from django.conf.urls import url
+from . import views
from .views import ReviewEditView, TalkProposalListView
urlpatterns = [
url(r'^$', TalkProposalListView.as_view(), name='review_proposal_list'),
url(r'^talk/(?P\d+)/$',
ReviewEditView.as_view(), name='review_edit'),
+ url(r'^review-stages/$', views.review_stages, name='review_stages'),
]
diff --git a/src/reviews/views.py b/src/reviews/views.py
index 6e0ea7534..784cf617f 100644
--- a/src/reviews/views.py
+++ b/src/reviews/views.py
@@ -1,12 +1,19 @@
import collections
+import datetime
import json
import random
+import pytz
+from django.conf import settings
+from django.conf.global_settings import DATETIME_INPUT_FORMATS
+from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db.models import Count
from django.http import Http404
+from django.shortcuts import render
from django.urls import reverse
from django.views.generic import ListView, UpdateView
+from registry.helper import reg
from core.utils import SequenceQuerySet
from proposals.models import TalkProposal
@@ -26,7 +33,6 @@ def dispatch(self, request, *args, **kwargs):
class TalkProposalListView(ReviewableMixin, PermissionRequiredMixin, ListView):
-
model = TalkProposal
permission_required = REVIEW_REQUIRED_PERMISSIONS
template_name = 'reviews/talk_proposal_list.html'
@@ -170,9 +176,9 @@ def get_stage_1_reviews(self):
review__stage=1,
review__reviewer=self.request.user
) & TalkProposal.objects.filter(
- review__stage=2,
- review__reviewer=self.request.user
- )
+ review__stage=2,
+ review__reviewer=self.request.user
+ )
)
stage_1_reviews = (
Review.objects
@@ -200,7 +206,6 @@ def get_stage_2_reviews(self):
class ReviewEditView(ReviewableMixin, PermissionRequiredMixin, UpdateView):
-
form_class = ReviewForm
permission_required = REVIEW_REQUIRED_PERMISSIONS
template_name = 'reviews/review_form.html'
@@ -327,3 +332,77 @@ def get_success_url(self):
if query_string:
return url + '?' + query_string
return url
+
+
+def review_stages(request):
+ current_review_stages_setting = {}
+ review_stages_list = [
+ 'Call for Proposals',
+ 'Locked (proposal editing and reviewing disabled)',
+ 'First Round Review', 'Modification Stage', 'Second Round Review',
+ 'Internal Decision', 'Announcement of Acceptance'
+ ]
+ review_stages_var = [
+ 'proposals.creatable', 'proposals.editable', 'proposals.withdrawable',
+ 'reviews.visible.to.submitters', 'reviews.stage',
+ 'proposals.disable.after'
+ ]
+
+ # Initialize current setting with existing value
+ for tag in review_stages_var:
+ key = settings.CONFERENCE_DEFAULT_SLUG + '.' + tag
+ value = reg.get(key, '')
+ update_current_review_stages_setting(tag, value, current_review_stages_setting)
+
+ if request.method == 'POST':
+ for tag in review_stages_var:
+ key = settings.CONFERENCE_DEFAULT_SLUG + '.' + tag
+ if tag == 'proposals.disable.after':
+ if request.POST[tag] == "":
+ continue
+ else:
+ date_time_obj = date_preprocess(DATETIME_INPUT_FORMATS, request.POST[tag])
+ if date_time_obj is None:
+ messages.error(request, 'Please input valid date format : " + "%Y-%m-%dT%H:%M')
+ value = None
+ else:
+ tz_selectd = pytz.timezone(request.POST['review_timezone'])
+ loc_dt = tz_selectd.localize(date_time_obj).strftime(
+ '%Y-%m-%d %H:%M:%S%z')
+ value = loc_dt
+ elif tag == 'reviews.stage':
+ value = int(request.POST[tag])
+ else:
+ value = request.POST[tag]
+ reg[key] = value
+ update_current_review_stages_setting(tag, value, current_review_stages_setting)
+
+ messages.info(request, 'This setting has been changed successfully.')
+
+ return render(
+ request, 'reviews/review_stages.html', {
+ 'timezones': pytz.common_timezones,
+ 'review_stages_list': review_stages_list,
+ 'current_review_stages_setting': current_review_stages_setting,
+ **reviews_state()._asdict()
+ })
+
+
+def update_current_review_stages_setting(tag, value, current_review_stages_setting):
+ # Django template language does not support dictionary keys containing "."
+ if "." in tag:
+ tag = tag.replace(".", "_")
+ current_review_stages_setting[tag] = value
+
+
+def date_preprocess(datetime_input_format, value):
+ # Add defined datetime formatx
+ datetime_input_format += ['%Y-%m-%dT%H:%M:%S', '%Y-%m-%dT%H:%M']
+ value = value.strip()
+ # Try to strptime against each input format.
+ for format in datetime_input_format:
+ try:
+ return datetime.datetime.strptime(value, format)
+ except (ValueError, TypeError):
+ continue
+ return None
diff --git a/src/static/css/components/_buttons.scss b/src/static/css/components/_buttons.scss
index c7240dbd7..0ee29b276 100644
--- a/src/static/css/components/_buttons.scss
+++ b/src/static/css/components/_buttons.scss
@@ -10,6 +10,10 @@
@include button-variant($btn-natural-color, $btn-natural-bg, $btn-natural-border);
}
+.btn-natural-noborder {
+ @include button-variant($btn-natural-color, $btn-natural-bg, $btn-natural-border);
+}
+
.btn-action{
@include button-variant($btn-action-color, $btn-action-bg, $btn-action-border);
}
@@ -40,6 +44,19 @@
}
}
+.btn-natural-noborder {
+ &:hover {
+ background-color: $btn-natural-hover-bg;
+ }
+ &,
+ &:focus,
+ &:active {
+ outline: none;
+ border-color: transparent;
+ }
+ margin-bottom: 20px;
+}
+
.btn-natural.btn-withdraw {
&,
&:hover,
diff --git a/src/static/css/components/_texts.scss b/src/static/css/components/_texts.scss
index 659429f2c..cf494cfb1 100644
--- a/src/static/css/components/_texts.scss
+++ b/src/static/css/components/_texts.scss
@@ -8,3 +8,8 @@
.text-emphasize {
@include roboto-medium();
}
+
+.input-customized-size input{
+ width: 16.2em;
+}
+
diff --git a/src/static/css/components/_toggle.scss b/src/static/css/components/_toggle.scss
new file mode 100644
index 000000000..f5d121e78
--- /dev/null
+++ b/src/static/css/components/_toggle.scss
@@ -0,0 +1,47 @@
+.material-switch > input[type="checkbox"] {
+ display: none;
+}
+
+.material-switch > label {
+ cursor: pointer;
+ height: 0px;
+ position: relative;
+ width: 40px;
+}
+
+.material-switch > label::before {
+ background: rgb(0, 0, 0);
+ box-shadow: inset 0px 0px 10px rgba(0, 0, 0, 0.5);
+ border-radius: 8px;
+ content: '';
+ height: 16px;
+ margin-top: -8px;
+ position:absolute;
+ opacity: 0.3;
+ transition: all 0.4s ease-in-out;
+ width: 40px;
+}
+
+.material-switch > label::after {
+ background: rgb(255, 255, 255);
+ border-radius: 16px;
+ box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3);
+ content: '';
+ height: 24px;
+ left: -4px;
+ margin-top: -8px;
+ position: absolute;
+ top: -4px;
+ transition: all 0.3s ease-in-out;
+ width: 24px;
+}
+
+.material-switch > input[type="checkbox"]:checked + label::before {
+ background: inherit;
+ opacity: 0.5;
+}
+
+.material-switch > input[type="checkbox"]:checked + label::after {
+ background: inherit;
+ left: 20px;
+}
\ No newline at end of file
diff --git a/src/static/css/main.scss b/src/static/css/main.scss
index 962ffb4d4..ee36a5e16 100644
--- a/src/static/css/main.scss
+++ b/src/static/css/main.scss
@@ -20,6 +20,7 @@
@import "components/tables";
@import "components/texts";
@import "components/lists";
+@import "components/toggle";
// Pages
@import "pages/proposals";
diff --git a/src/static/css/vendors/bootstrap/_buttons.scss b/src/static/css/vendors/bootstrap/_buttons.scss
index 6452b709f..da8828aba 100755
--- a/src/static/css/vendors/bootstrap/_buttons.scss
+++ b/src/static/css/vendors/bootstrap/_buttons.scss
@@ -110,6 +110,7 @@ a.btn {
&:hover,
&:focus,
&:active {
+ outline: none;
border-color: transparent;
}
&:hover,
@@ -128,7 +129,6 @@ a.btn {
}
}
-
// Button Sizes
// --------------------------------------------------
@@ -166,3 +166,4 @@ input[type="button"] {
width: 100%;
}
}
+
diff --git a/src/static/css/vendors/bootstrap/_panels.scss b/src/static/css/vendors/bootstrap/_panels.scss
index be9410f5b..d2c65b749 100755
--- a/src/static/css/vendors/bootstrap/_panels.scss
+++ b/src/static/css/vendors/bootstrap/_panels.scss
@@ -15,6 +15,7 @@
// Panel contents
.panel-body {
padding: $panel-body-padding;
+ color: black;
@include clearfix;
}
@@ -33,8 +34,7 @@
.panel-title {
margin-top: 0;
margin-bottom: 0;
- font-size: ceil(($font-size-base * 1.125));
- color: inherit;
+ color: black;
> a,
> small,
diff --git a/src/static/js/reviews/review_stages.js b/src/static/js/reviews/review_stages.js
new file mode 100644
index 000000000..8fa376d34
--- /dev/null
+++ b/src/static/js/reviews/review_stages.js
@@ -0,0 +1,85 @@
+var proposals_creatable = document.getElementById("proposals.creatable");
+var proposals_editable = document.getElementById("proposals.editable");
+var proposals_withdrawable = document.getElementById("proposals.withdrawable");
+var proposals_disable_after = document.getElementById("proposals.disable.after");
+var reviews_stage = document.getElementById("reviews.stage");
+var reviews_visible_to_submitters = document.getElementById("reviews.visible.to.submitters");
+
+$('.hotkey').click(function () {
+ if ($(this).val() == "Call for Proposals") {
+ Call_for_Proposals();
+ } else if ($(this).val() == "Locked (proposal editing and reviewing disabled)") {
+ Locked()
+ } else if ($(this).val() == "First Round Review") {
+ First_Round_Review()
+ } else if ($(this).val() == "Modification Stage") {
+ Modification_Stage()
+ } else if ($(this).val() == "Second Round Review") {
+ Second_Round_Review()
+ } else if ($(this).val() == "Internal Decision") {
+ Internal_Decision()
+ } else {
+ Announcement_of_Acceptance()
+ }
+
+ /*
+ Proposal Review Stage Setting
+ Reference : https://github.com/pycontw/pycon.tw/blob/master/src/reviews/README.md
+ */
+ function Call_for_Proposals() {
+ proposals_creatable.checked = true;
+ proposals_editable.checked = true;
+ proposals_withdrawable.checked = true;
+ reviews_stage.value = "0";
+ reviews_visible_to_submitters.checked = false;
+ }
+
+ function Locked() {
+ proposals_creatable.checked = false;
+ proposals_editable.checked = false;
+ proposals_withdrawable.checked = false;
+ reviews_stage.value = "0";
+ reviews_visible_to_submitters.checked = false;
+ }
+
+ function First_Round_Review() {
+ proposals_creatable.checked = false;
+ proposals_editable.checked = false;
+ proposals_withdrawable.checked = false;
+ reviews_stage.value = "1";
+ reviews_visible_to_submitters.checked = false;
+ }
+
+ function Modification_Stage() {
+ proposals_creatable.checked = false;
+ proposals_editable.checked = true;
+ proposals_withdrawable.checked = false;
+ reviews_stage.value = "0";
+ reviews_visible_to_submitters.checked = true;
+ }
+
+ function Second_Round_Review() {
+ proposals_creatable.checked = false;
+ proposals_editable.checked = false;
+ proposals_withdrawable.checked = false;
+ reviews_stage.value = "2";
+ reviews_visible_to_submitters.checked = false;
+ }
+
+ function Internal_Decision() {
+ proposals_creatable.checked = false;
+ proposals_editable.checked = false;
+ proposals_withdrawable.checked = false;
+ reviews_stage.value = "0";
+ reviews_visible_to_submitters.checked = false;
+ }
+
+ function Announcement_of_Acceptance() {
+ proposals_creatable.checked = false;
+ proposals_editable.checked = true;
+ proposals_withdrawable.checked = false;
+ reviews_stage.value = "0";
+ reviews_visible_to_submitters.checked = true;
+ }
+
+});
diff --git a/src/templates/default/_includes/dashboard_tablist.html b/src/templates/default/_includes/dashboard_tablist.html
index 05154e393..b3c72d496 100644
--- a/src/templates/default/_includes/dashboard_tablist.html
+++ b/src/templates/default/_includes/dashboard_tablist.html
@@ -22,4 +22,10 @@
{% endif %}
{% endif %}
+
+ {% if user.is_superuser %}
+
+ {% trans 'Review Stages' %}
+
+ {% endif %}
diff --git a/src/templates/default/reviews/review_stages.html b/src/templates/default/reviews/review_stages.html
new file mode 100644
index 000000000..d47929ff3
--- /dev/null
+++ b/src/templates/default/reviews/review_stages.html
@@ -0,0 +1,141 @@
+{% extends 'dashboard_base.html' %}
+
+{% load i18n static %}
+{% load proposals %}
+{% load compress crispy_forms_tags %}
+{% load tz %}
+{% get_current_timezone as TIME_ZONE %}
+
+{% block dashboard_tablist %}
+{% include '_includes/dashboard_tablist.html' with active='admin' %}
+{% endblock dashboard_tablist %}
+
+{% block main-content %}
+
+
+
+
+
+
+
+
{% trans 'Current Review Stage Setting' %}
+
+ -
+ {{ ".proposals.disable.after"|configuration_switch }}
+
+
{{ current_review_stages_setting.proposals_disable_after }}
+
+
+
+
+
+
+
{% trans 'Set Review Stage' %}
+
+ {% for rs in review_stages_list %}
+
+ {% endfor %}
+
+
+
+
+
+{% endblock main-content %}
+
+{% block extra_js %}
+{% compress js %}
+
+
+{% endcompress %}
+{% endblock extra_js %}
+
diff --git a/src/users/urls.py b/src/users/urls.py
index f71dd74ec..63b9d6636 100644
--- a/src/users/urls.py
+++ b/src/users/urls.py
@@ -4,11 +4,9 @@
from . import views
urlpatterns = [
-
url(r'^login/$', views.login, name='login'),
url(r'^logout/$', views.logout, name='logout'),
url(r'^profile/$', views.user_profile_update, name='user_profile_update'),
-
url(r'^password-change/$', views.password_change, name='password_change'),
url(r'^password-change/done/$',
views.password_change_done, name='password_change_done'),
@@ -30,5 +28,4 @@
url(r'^agreement/$',
views.coc_agree, name='coc_agreement'),
-
]
diff --git a/src/users/views.py b/src/users/views.py
index 24415ac66..6d53b914b 100644
--- a/src/users/views.py
+++ b/src/users/views.py
@@ -193,7 +193,6 @@ def get_context_data(self, **kwargs):
context.update(**reviews_state()._asdict())
return context
-
login = auth_views.LoginView.as_view(authentication_form=AuthenticationForm)
logout = auth_views.LogoutView.as_view()
password_change = PasswordChangeView.as_view()