Commit 4fb3e077 by Awais Committed by Awais Qureshi

Implement the edit course run page.

Add new tests.
Remove tests not require any more.

ECOM-6067
parent a025c2cf
...@@ -134,6 +134,7 @@ class CustomCourseForm(CourseForm): ...@@ -134,6 +134,7 @@ class CustomCourseForm(CourseForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None) user = kwargs.pop('user', None)
edit_mode = kwargs.pop('edit_mode', None)
organization = kwargs.pop('organization', None) organization = kwargs.pop('organization', None)
if organization: if organization:
org_extension = OrganizationExtension.objects.get(organization=organization) org_extension = OrganizationExtension.objects.get(organization=organization)
...@@ -146,6 +147,11 @@ class CustomCourseForm(CourseForm): ...@@ -146,6 +147,11 @@ class CustomCourseForm(CourseForm):
) )
super(CustomCourseForm, self).__init__(*args, **kwargs) super(CustomCourseForm, self).__init__(*args, **kwargs)
if edit_mode:
self.fields['title'].widget = forms.HiddenInput()
self.fields['number'].widget = forms.HiddenInput()
self.fields['team_admin'].widget = forms.HiddenInput()
self.fields['organization'].widget = forms.HiddenInput()
class UpdateCourseForm(BaseCourseForm): class UpdateCourseForm(BaseCourseForm):
......
""" Tests publisher.utils""" """ Tests publisher.utils"""
import ddt
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from django.core.urlresolvers import reverse
from django.test import TestCase, RequestFactory from django.test import TestCase, RequestFactory
from mock import Mock from mock import Mock
...@@ -17,10 +20,12 @@ from course_discovery.apps.publisher.models import OrganizationExtension ...@@ -17,10 +20,12 @@ from course_discovery.apps.publisher.models import OrganizationExtension
from course_discovery.apps.publisher.tests import factories from course_discovery.apps.publisher.tests import factories
from course_discovery.apps.publisher.utils import ( from course_discovery.apps.publisher.utils import (
is_email_notification_enabled, is_publisher_admin, is_internal_user, is_email_notification_enabled, is_publisher_admin, is_internal_user,
get_internal_users, is_partner_coordinator_user, is_publisher_user get_internal_users, is_partner_coordinator_user, is_publisher_user,
make_bread_crumbs
) )
@ddt.ddt
class PublisherUtilsTests(TestCase): class PublisherUtilsTests(TestCase):
""" Tests for the publisher utils. """ """ Tests for the publisher utils. """
...@@ -193,3 +198,11 @@ class PublisherUtilsTests(TestCase): ...@@ -193,3 +198,11 @@ class PublisherUtilsTests(TestCase):
decorated_func(request, self.user) decorated_func(request, self.user)
self.assertTrue(func.called) self.assertTrue(func.called)
def test_make_bread_crumbs(self):
""" Verify the function parse the list of tuple and returns list of dicts."""
links = [(reverse('publisher:publisher_courses_new'), 'Courses'), (None, 'Testing')]
self.assertEqual(
[{'url': '/publisher/courses/new/', 'slug': 'Courses'}, {'url': None, 'slug': 'Testing'}],
make_bread_crumbs(links)
)
...@@ -76,3 +76,21 @@ def is_publisher_user(user): ...@@ -76,3 +76,21 @@ def is_publisher_user(user):
bool: True, if user is an publisher user; otherwise, False. bool: True, if user is an publisher user; otherwise, False.
""" """
return user.groups.exists() return user.groups.exists()
def make_bread_crumbs(links):
""" Returns lists of dicts containing bread-crumbs url and slug.
Arguments:
links (list): List of tuple contains links and slug.
Returns:
list: list containing dicts [{'url':'/courses/', 'slug':'test'}].
"""
return [
{
"url": url,
"slug": slug,
}
for url, slug in links
]
...@@ -20,7 +20,8 @@ from course_discovery.apps.core.models import User ...@@ -20,7 +20,8 @@ from course_discovery.apps.core.models import User
from course_discovery.apps.publisher.choices import PublisherUserRole from course_discovery.apps.publisher.choices import PublisherUserRole
from course_discovery.apps.publisher.emails import send_email_for_course_creation from course_discovery.apps.publisher.emails import send_email_for_course_creation
from course_discovery.apps.publisher.forms import ( from course_discovery.apps.publisher.forms import (
SeatForm, CustomCourseForm, CustomCourseRunForm, CustomSeatForm, UpdateCourseForm SeatForm, CustomCourseForm, CustomCourseRunForm,
CustomSeatForm, UpdateCourseForm
) )
from course_discovery.apps.publisher import mixins from course_discovery.apps.publisher import mixins
from course_discovery.apps.publisher.models import ( from course_discovery.apps.publisher.models import (
...@@ -28,7 +29,8 @@ from course_discovery.apps.publisher.models import ( ...@@ -28,7 +29,8 @@ from course_discovery.apps.publisher.models import (
OrganizationExtension, CourseUserRole) OrganizationExtension, CourseUserRole)
from course_discovery.apps.publisher.utils import ( from course_discovery.apps.publisher.utils import (
is_internal_user, get_internal_users, is_publisher_admin, is_internal_user, get_internal_users, is_publisher_admin,
is_partner_coordinator_user is_partner_coordinator_user,
make_bread_crumbs
) )
from course_discovery.apps.publisher.wrappers import CourseRunWrapper from course_discovery.apps.publisher.wrappers import CourseRunWrapper
...@@ -151,21 +153,18 @@ class CourseRunDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionM ...@@ -151,21 +153,18 @@ class CourseRunDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionM
context['role_widgets'] = self.get_role_widgets_data(course_roles) context['role_widgets'] = self.get_role_widgets_data(course_roles)
context['user_list'] = get_internal_users() context['user_list'] = get_internal_users()
context['breadcrumbs'] = [ context['breadcrumbs'] = make_bread_crumbs(
{ [
'url': reverse('publisher:publisher_courses'), 'slug': 'Courses' (reverse('publisher:publisher_courses'), 'Courses'),
}, (
{ reverse('publisher:publisher_course_detail', kwargs={'pk': course_run.course.id}),
'url': reverse('publisher:publisher_course_detail', kwargs={'pk': course_run.course.id}), course_run.course.title
'slug': course_run.course.title ),
}, (None, '{type}: {start}'.format(
{
'url': None,
'slug': '{type}: {start}'.format(
type=course_run.get_pacing_type_display(), start=course_run.start.strftime("%B %d, %Y") type=course_run.get_pacing_type_display(), start=course_run.start.strftime("%B %d, %Y")
) ))
} ]
] )
context['can_view_all_tabs'] = mixins.check_roles_access(self.request.user) context['can_view_all_tabs'] = mixins.check_roles_access(self.request.user)
context['publisher_hide_features_for_pilot'] = waffle.switch_is_active('publisher_hide_features_for_pilot') context['publisher_hide_features_for_pilot'] = waffle.switch_is_active('publisher_hide_features_for_pilot')
...@@ -180,7 +179,7 @@ class CreateCourseView(mixins.LoginRequiredMixin, mixins.PublisherUserRequiredMi ...@@ -180,7 +179,7 @@ class CreateCourseView(mixins.LoginRequiredMixin, mixins.PublisherUserRequiredMi
course_form = CustomCourseForm course_form = CustomCourseForm
run_form = CustomCourseRunForm run_form = CustomCourseRunForm
seat_form = CustomSeatForm seat_form = CustomSeatForm
template_name = 'publisher/add_course_form.html' template_name = 'publisher/add_update_course_form.html'
success_url = 'publisher:publisher_course_run_detail' success_url = 'publisher:publisher_course_run_detail'
def get_success_url(self, course_id): # pylint: disable=arguments-differ def get_success_url(self, course_id): # pylint: disable=arguments-differ
...@@ -348,15 +347,12 @@ class CourseDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMixi ...@@ -348,15 +347,12 @@ class CourseDetailView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMixi
self.request.user, self.object, OrganizationExtension.EDIT_COURSE self.request.user, self.object, OrganizationExtension.EDIT_COURSE
) )
context['breadcrumbs'] = [ context['breadcrumbs'] = make_bread_crumbs(
{ [
'url': reverse('publisher:publisher_courses'), 'slug': 'Courses' (reverse('publisher:publisher_courses'), 'Courses'),
}, (None, self.object.title),
{ ]
'url': None, )
'slug': self.object.title
}
]
return context return context
...@@ -432,26 +428,122 @@ class CreateCourseRunView(mixins.LoginRequiredMixin, CreateView): ...@@ -432,26 +428,122 @@ class CreateCourseRunView(mixins.LoginRequiredMixin, CreateView):
return render(request, self.template_name, context, status=400) return render(request, self.template_name, context, status=400)
class CourseRunEditView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMixin, mixins.FormValidMixin, UpdateView): class CourseRunEditView(mixins.LoginRequiredMixin, mixins.PublisherPermissionMixin, UpdateView):
""" Course Run Edit View.""" """ Course Run Edit View."""
model = CourseRun model = CourseRun
course_form = CustomCourseForm
run_form = CustomCourseRunForm
seat_form = CustomSeatForm
template_name = 'publisher/add_update_course_form.html'
success_url = 'publisher:publisher_course_run_detail'
form_class = CustomCourseRunForm form_class = CustomCourseRunForm
permission = OrganizationExtension.EDIT_COURSE_RUN permission = OrganizationExtension.EDIT_COURSE_RUN
template_name = 'publisher/course_run_form.html'
success_url = 'publisher:publisher_course_runs_edit'
change_state = True
def get_context_data(self, **kwargs): def get_success_url(self): # pylint: disable=arguments-differ
context = super(CourseRunEditView, self).get_context_data(**kwargs)
if not self.object:
self.object = self.get_object()
context['workflow_state'] = self.object.current_state
context['comment_object'] = self.object
return context
def get_success_url(self):
return reverse(self.success_url, kwargs={'pk': self.object.id}) return reverse(self.success_url, kwargs={'pk': self.object.id})
def get_context_data(self):
course_run = self.get_object()
team_admin_name = course_run.course.course_team_admin
organization = course_run.course.organizations.first()
initial = {
'organization': organization,
'team_admin': team_admin_name,
}
return {
'initial': initial,
'course_run': self.get_object(),
'team_admin_name': team_admin_name.get_full_name(),
'organization_name': organization.name,
'organization': organization,
'publisher_hide_features_for_pilot': waffle.switch_is_active('publisher_hide_features_for_pilot'),
'edit_mode': True,
}
def get(self, request, *args, **kwargs):
context = self.get_context_data()
course_run = context.get('course_run')
course = course_run.course
context['course_form'] = self.course_form(
instance=course,
initial=context.get('initial'),
organization=context.get('organization'),
edit_mode=True
)
context['run_form'] = self.run_form(instance=course_run)
context['seat_form'] = self.seat_form(instance=course_run.seats.first())
context['breadcrumbs'] = make_bread_crumbs(
[
(reverse('publisher:publisher_courses'), 'Courses'),
(reverse('publisher:publisher_course_detail', kwargs={'pk': course.id}), course.title),
(None, '{type}: {start}'.format(
type=course_run.get_pacing_type_display(), start=course_run.start.strftime("%B %d, %Y")
))
]
)
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
user = request.user
context = self.get_context_data()
course_run = context.get('course_run')
course_form = self.course_form(
request.POST, request.FILES,
instance=course_run.course,
initial=context.get('initial'),
organization=context.get('organization'),
edit_mode=True
)
run_form = self.run_form(request.POST, instance=course_run)
seat_form = self.seat_form(request.POST, instance=course_run.seats.first())
if course_form.is_valid() and run_form.is_valid() and seat_form.is_valid():
try:
with transaction.atomic():
course = course_form.save()
course.changed_by = self.request.user
course.save()
course_run = run_form.save()
course_run.changed_by = self.request.user
course_run.save()
run_form.save_m2m()
# If price-type comes with request then save the seat object.
if request.POST.get('type'):
seat_form.save(changed_by=user, course_run=course_run)
# in case of any updating move the course-run state to draft.
if course_run.state.name != State.DRAFT:
course_run.change_state(user=user)
# pylint: disable=no-member
messages.success(request, _('Course run updated successfully.'))
return HttpResponseRedirect(reverse(self.success_url, kwargs={'pk': course_run.id}))
except Exception as e: # pylint: disable=broad-except
# pylint: disable=no-member
error_message = _('An error occurred while saving your changes. {error}').format(error=str(e))
messages.error(request, error_message)
logger.exception('Unable to update course run and seat for course [%s].', course_run.id)
if not messages.get_messages(request):
messages.error(request, _('Please fill all required fields.'))
context.update(
{
'course_form': course_form,
'run_form': run_form,
'seat_form': seat_form
}
)
return render(request, self.template_name, context, status=400)
class CreateSeatView(mixins.LoginRequiredMixin, mixins.FormValidMixin, CreateView): class CreateSeatView(mixins.LoginRequiredMixin, mixins.FormValidMixin, CreateView):
""" Create Seat View.""" """ Create Seat View."""
......
...@@ -40,18 +40,6 @@ class CommentsTests(TestCase): ...@@ -40,18 +40,6 @@ class CommentsTests(TestCase):
toggle_switch('enable_publisher_email_notifications', True) toggle_switch('enable_publisher_email_notifications', True)
def test_course_run_edit_page_with_multiple_comments(self):
""" Verify course-run edit page can load multiple comments"""
self._add_assert_multiple_comments(self.course_run, self.course_run_edit_page)
def test_comment_edit_with_courserun(self):
""" Verify that only comments attached with specific course run appears on edited page. """
comments = self._generate_comments_for_all_content_types()
response = self.client.get(reverse(self.course_run_edit_page, kwargs={'pk': self.course_run.id}))
self.assertContains(response, comments.get(self.course_run).comment)
self.assertNotContains(response, comments.get(self.course).comment)
self.assertNotContains(response, comments.get(self.seat).comment)
def test_comment_edit_with_seat(self): def test_comment_edit_with_seat(self):
""" Verify that only comments attached with specific seat appears on edited page. """ """ Verify that only comments attached with specific seat appears on edited page. """
comments = self._generate_comments_for_all_content_types() comments = self._generate_comments_for_all_content_types()
...@@ -66,12 +54,6 @@ class CommentsTests(TestCase): ...@@ -66,12 +54,6 @@ class CommentsTests(TestCase):
self.course, reverse(self.course_edit_page, kwargs={'pk': self.course.id}) self.course, reverse(self.course_edit_page, kwargs={'pk': self.course.id})
) )
def test_edit_course_run_comment(self):
""" Verify that course run comment can be edited. """
self._edit_comment_page(
self.course_run, reverse(self.course_run_edit_page, kwargs={'pk': self.course_run.id})
)
def test_edit_seat_comment(self): def test_edit_seat_comment(self):
""" Verify that seat comment can be edited. """ """ Verify that seat comment can be edited. """
self._edit_comment_page( self._edit_comment_page(
......
...@@ -7,14 +7,14 @@ msgid "" ...@@ -7,14 +7,14 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-01-26 15:26+0500\n" "POT-Creation-Date: 2017-01-27 12:46+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: \n"
#: static/js/catalogs-change-form.js #: static/js/catalogs-change-form.js
msgid "Preview" msgid "Preview"
......
...@@ -7,14 +7,14 @@ msgid "" ...@@ -7,14 +7,14 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-01-26 15:26+0500\n" "POT-Creation-Date: 2017-01-27 12:46+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Language: \n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: static/js/catalogs-change-form.js #: static/js/catalogs-change-form.js
......
...@@ -29,11 +29,20 @@ $(document).ready(function(){ ...@@ -29,11 +29,20 @@ $(document).ready(function(){
loadAdminUsers(org_id); loadAdminUsers(org_id);
} }
$('#id_is_micromasters').click( function(){ var microMaster = $('#id_is_micromasters'),
$('#micromasters_name_group').toggle(this.checked); xseries = $('#id_is_xseries');
if (microMaster.is(':checked')) {
toggleMicroMaster(true);
}
if (xseries.is(':checked')) {
toggleXseries(true);
}
microMaster.click( function(){
toggleMicroMaster(this.checked);
}); });
$('#id_is_xseries').click( function(e){ xseries.click( function(e){
$('#xseries_name_group').toggle(this.checked); toggleXseries(this.checked)
}); });
$('#add-new-instructor').click(function(e){ $('#add-new-instructor').click(function(e){
$('#addInstructorModal').show(); $('#addInstructorModal').show();
...@@ -153,3 +162,13 @@ function renderSelectedInstructor(id, name, image) { ...@@ -153,3 +162,13 @@ function renderSelectedInstructor(id, name, image) {
$('.selected-instructor').append(instructorHtml); $('.selected-instructor').append(instructorHtml);
} }
function toggleMicroMaster (checked) {
// If is-micromaster checkbox value true from db then show the x-micromaster block.
$('#micromasters_name_group').toggle(checked);
}
function toggleXseries(checked) {
// If is-xseries checkbox value true from db then show the x-series block.
$('#xseries_name_group').toggle(checked);
}
...@@ -47,8 +47,6 @@ $(function () { ...@@ -47,8 +47,6 @@ $(function () {
}; };
change_fields();
$('#id_type').change(function () { $('#id_type').change(function () {
change_fields(this); change_fields(this);
}); });
......
...@@ -475,6 +475,9 @@ select { ...@@ -475,6 +475,9 @@ select {
.field-label { .field-label {
font-weight: bold; font-weight: bold;
} }
.field-value {
margin-bottom: 20px;
}
.help-text { .help-text {
p { p {
margin-bottom: 0; margin-bottom: 0;
......
...@@ -9,7 +9,13 @@ ...@@ -9,7 +9,13 @@
{% block page_content %} {% block page_content %}
<div> <div>
<h1 class="hd-1 emphasized">{% trans "Add Course" %}</h1> <h1 class="hd-1 emphasized">
{% if edit_mode %}
{% trans "Edit Course" %}
{% else %}
{% trans "Add Course" %}
{% endif %}
</h1>
<div class="alert-messages"> <div class="alert-messages">
{% if messages %} {% if messages %}
...@@ -88,26 +94,35 @@ ...@@ -88,26 +94,35 @@
</div> </div>
</div> </div>
<div class="col col-6"> <div class="col col-6">
{% if course_form.organization.field.queryset.all.count > 1 %} {% if edit_mode %}
<label class="field-label">{{ course_form.organization.label_tag }} </label> <label class="field-label">{{ course_form.organization.label_tag }} </label>
{{ course_form.organization }} <div class="field-value">
{{ organization_name }}{{ course_form.organization }}</div>
{% else %} {% else %}
{% with course_form.organization.field.queryset|first as organization %} {% if course_form.organization.field.queryset.all.count > 1 %}
<label id="organization-name" class="field-label" <label class="field-label">{{ course_form.organization.label_tag }} </label>
data-org_id="{{ organization.id }}">{{ course_form.organization.label_tag }} {{ course_form.organization }}
</label> {% else %}
<span class="read-only-field">{{ organization.name }}</span> {% with course_form.organization.field.queryset|first as organization %}
<input id="id_organization" name="organization" type="hidden" value="{{ organization.id }}"> <label id="organization-name" class="field-label"
{% endwith %} data-org_id="{{ organization.id }}">{{ course_form.organization.label_tag }}
</label>
<span class="read-only-field">{{ organization.name }}</span>
<input id="id_organization" name="organization" type="hidden" value="{{ organization.id }}">
{% endwith %}
{% endif %}
{% endif %} {% endif %}
<label class="field-label"> <label class="field-label">
{{ course_form.team_admin.label_tag }} {{ course_form.team_admin.label_tag }}
</label> {{ course_form.team_admin }} </label>
<div class="field-value">
{% if edit_mode %}{{ team_admin_name }}{% endif %}</div>
{{ course_form.team_admin }}
<label class="field-label ">{{ course_form.title.label }}</label> <label class="field-label ">{{ course_form.title.label }}</label>
{{ course_form.title }} {% if edit_mode %}<div class="field-value">{{ course_form.title.value }}</div>{% endif %}
{{ course_form.title }}
</div> </div>
</div> </div>
...@@ -165,7 +180,8 @@ ...@@ -165,7 +180,8 @@
</div> </div>
<div class="col col-6"> <div class="col col-6">
<label class="field-label ">{{ course_form.number.label_tag }}</label> <label class="field-label ">{{ course_form.number.label_tag }}</label>
{{ course_form.number }} {% if edit_mode %}{{ course_form.number.value }}{% endif %}
{{ course_form.number }}
</div> </div>
</div> </div>
...@@ -237,7 +253,6 @@ ...@@ -237,7 +253,6 @@
<div class="row"> <div class="row">
<div class="col col-6"> <div class="col col-6">
<label class="field-label ">{{ seat_form.type.label_tag }} <label class="field-label ">{{ seat_form.type.label_tag }}
<span class="required">* {% trans "required" %}</span>
</label> </label>
{{ seat_form.type}} {{ seat_form.type}}
</div> </div>
......
{% extends 'publisher/base.html' %}
{% load i18n %}
{% block title %}
{% trans "Course Run Form" %}
{% endblock title %}
{% block page_content %}
<div class="layout-full layout publisher-layout">
<div class="publisher-container">
<div class="course-information">
<h4 class="hd-4">{% trans "Course Run Form" %}</h4>
<div class="status-information">
{% if object.id %}
<div class="info-item">
<span class="item">
<span class="heading">{% trans "Status" %}:</span>
<span>{{ workflow_state }}</span>
</span>
</div>
{% endif %}
</div>
<form class="form" method="post" action=""> {% csrf_token %}
<fieldset class="form-group">
{% for field in form %}
{% include "publisher/form_field.html" %}
{% endfor %}
</fieldset>
<button class="btn-brand btn-base" type="submit">{% trans "Save" %}</button>
</form>
</div>
<div class="comment-container">
<span class="item">
{% if object.id %}
<a href="{% url 'publisher:publisher_seats_new' %}" class="btn btn-neutral btn-add">
{% trans "Add Seat" %}
</a>
{% endif %}
</span>
{% include 'comments/comments_list.html' %}
{% include 'comments/add_auth_comments.html' %}
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
{{ form.media }}
{% endblock %}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment