diff --git a/course/models.py b/course/models.py
index 1b43be705..165b452c4 100644
--- a/course/models.py
+++ b/course/models.py
@@ -16,6 +16,7 @@
from django.db.models.signals import post_save
from django.utils import timezone
from django.utils.functional import cached_property
+from django.utils.text import format_lazy
from django.utils.translation import ugettext_lazy as _
from django_colortag.models import ColorTag
@@ -450,6 +451,11 @@ def __str__(self):
def clean(self):
super().clean()
errors = {}
+ RESERVED = ("instances",)
+ if self.instance_name in RESERVED:
+ errors['instance_name'] = format_lazy(_("You cannot use word '{}' as an instance name."), self.instance_name)
+ if self.url in RESERVED:
+ errors['url'] = format_lazy(_("You cannot use word '{}' in the url."), self.url)
if self.ending_time <= self.starting_time:
errors['ending_time'] = _("Ending time must be later than starting time.")
if self.lifesupport_time and self.lifesupport_time < self.ending_time:
diff --git a/course/templates/course/course_instances.html b/course/templates/course/course_instances.html
new file mode 100644
index 000000000..63bd7b566
--- /dev/null
+++ b/course/templates/course/course_instances.html
@@ -0,0 +1,29 @@
+{% extends "base.html" %}
+{% load i18n %}
+{% load course %}
+
+{% block title %}{% trans "Course instances" %} | {{ block.super }}{% endblock %}
+{% block view_tag %}instances{% endblock %}
+
+{% block content %}
+
+
+ {% if instances %}
+ {% regroup instances by starting_time|date:"Y" as instances_by_year %}
+ {% for year, year_instances in instances_by_year %}
+
+
+
{{ year }}
+
+
+ {% include "course/_course_cards.html" with instances=year_instances condensed=True %}
+
+
+ {% endfor %}
+ {% else %}
+
{{ msg }}
+ {% endif %}
+
+{% endblock %}
diff --git a/course/urls.py b/course/urls.py
index 209d9888e..a710051e2 100644
--- a/course/urls.py
+++ b/course/urls.py
@@ -16,6 +16,12 @@
url(r'^archive/$',
views.ArchiveView.as_view(),
name="archive"),
+ url(COURSE_URL_PREFIX + r'instances/$',
+ views.CourseInstancesView.as_view(),
+ name="course_instances"),
+ url(COURSE_URL_PREFIX + r'$',
+ views.LastInstanceView.as_view(),
+ name="course_last_instance"),
url(INSTANCE_URL_PREFIX + r'$',
views.InstanceView.as_view(),
name="course"),
diff --git a/course/views.py b/course/views.py
index 2dcd25e1c..e161d08f7 100644
--- a/course/views.py
+++ b/course/views.py
@@ -21,13 +21,13 @@
from exercise.cache.hierarchy import NoSuchContent
from exercise.models import LearningObject
from lib.helpers import settings_text, remove_query_param_from_url
-from lib.viewbase import BaseTemplateView, BaseRedirectMixin, BaseFormView, BaseView
+from lib.viewbase import BaseTemplateView, BaseRedirectMixin, BaseFormView, BaseView, BaseRedirectView
from userprofile.viewbase import UserProfileView
from .forms import GroupsForm, GroupSelectForm
-from .models import CourseInstance, Enrollment
+from .models import CourseInstance, Course, Enrollment
from .permissions import EnrollInfoVisiblePermission
from .renders import group_info_context
-from .viewbase import CourseModuleBaseView, CourseInstanceMixin, EnrollableViewMixin
+from .viewbase import CourseModuleBaseView, CourseInstanceMixin, EnrollableViewMixin, CourseMixin
class HomeView(UserProfileView):
@@ -89,6 +89,38 @@ def get_common_objects(self):
self.instances = CourseInstance.objects.get_visible(self.request.user)
self.note("instances")
+class CourseInstancesView(UserProfileView):
+ access_mode = ACCESS.ANONYMOUS
+ template_name = "course/course_instances.html"
+
+ def get_common_objects(self, **kwargs):
+ course = get_object_or_404(Course, url=self.kwargs['course_slug'])
+ self.instances = []
+ self.msg = ""
+ if CourseInstance.objects.filter(course=course).count() > 0:
+ self.instances = CourseInstance.objects.get_visible(self.request.user).filter(course=course).order_by('-starting_time')
+ self.msg = _("The course instances of this course are not visible to students.")
+ else:
+ self.msg = _("There are no course instances for this course.")
+
+ self.note("instances", "msg")
+
+class LastInstanceView(CourseMixin, BaseRedirectView):
+ access_mode = ACCESS.STUDENT
+
+ def get_resource_objects(self):
+ super().get_resource_objects()
+ course = get_object_or_404(Course, url=self._get_kwarg(self.course_kw))
+ course_instances = CourseInstance.objects.filter(course=course).order_by('-starting_time')
+
+ if course_instances:
+ self.course_instance = course_instances[0]
+ else:
+ raise Http404(_("There are no course instances for this course."))
+
+ def get(self, request, *args, **kwargs):
+ return self.redirect(self.course_instance.url)
+
class InstanceView(EnrollableViewMixin, BaseTemplateView):
access_mode = ACCESS.STUDENT
# ACCESS.STUDENT requires users to log in, but the access mode is dropped
diff --git a/locale/fi/LC_MESSAGES/django.po b/locale/fi/LC_MESSAGES/django.po
index 8c8ccc471..cb50c972b 100644
--- a/locale/fi/LC_MESSAGES/django.po
+++ b/locale/fi/LC_MESSAGES/django.po
@@ -223,6 +223,14 @@ msgstr ""
"sähköposteihin. Syötä pilkuilla erotettuja sähköpostiosoitteita korvaamaan "
"oletusvastaanottajat."
+#: course/models.py
+msgid "You cannot use word '{}' as an instance name."
+msgstr "Et voi käyttää sanaa {} ilmentymän nimenä."
+
+#: course/models.py
+msgid "You cannot use word '{}' in the url."
+msgstr "Et voi käyttää sanaa {} URL-osoitteessa."
+
#: course/models.py
msgid "Ending time must be later than starting time."
msgstr "Päättymisajan tulee olla alkamisajan jälkeen."
@@ -601,6 +609,11 @@ msgstr "Aseta kurssikieleksi %(lang)s"
msgid "You can change the language from your profile."
msgstr "Voit muuttaa kielivalintaa profiilistasi."
+#: course/templates/course/course_instances.html
+#: edit_course/templates/edit_course/clone_instance.html
+msgid "Course instances"
+msgstr "Kurssikerrat"
+
#: course/templates/course/enroll.html
msgid "Enrollment"
msgstr "Ilmoittautuminen"
@@ -887,6 +900,14 @@ msgstr "Sähköposti"
msgid "Tags"
msgstr "Merkinnät"
+#: course/views.py
+msgid "The course instances of this course are not visible to students."
+msgstr "Kurssin esiintymät eivät ole opiskelijoiden nähtävissä."
+
+#: course/views.py
+msgid "There are no course instances for this course."
+msgstr "Tällä kurssilla ei ole esiintymiä."
+
#: course/views.py
msgid "You cannot enroll, or have already enrolled, in this course."
msgstr "Et voi ilmoittautua tai olet jo ilmoittautunut tälle kurssille."
@@ -1286,10 +1307,6 @@ msgstr "Käännös kesken"
msgid "Instances"
msgstr "Kurssikerrat"
-#: edit_course/templates/edit_course/clone_instance.html
-msgid "Course instances"
-msgstr "Kurssikerrat"
-
#: edit_course/templates/edit_course/clone_instance.html
msgid "hidden"
msgstr "piilotettu"