diff --git a/api/v1/serializers/course.py b/api/v1/serializers/course.py index ac14f50a..4c6814a8 100644 --- a/api/v1/serializers/course.py +++ b/api/v1/serializers/course.py @@ -138,7 +138,7 @@ class CourseCreateSerializer(DispatchContentMixin, materials = validated_data.pop('materials', []) gallery = validated_data.pop('gallery', {}) author = validated_data.get('author', None) - if not author: + if not instance.author or not author: validated_data['author'] = self.context['request'].user course = super().update(instance, validated_data) self.dispatch_materials(course, materials) diff --git a/apps/course/migrations/0032_auto_20180214_1336.py b/apps/course/migrations/0032_auto_20180214_1336.py new file mode 100644 index 00000000..9da8cb90 --- /dev/null +++ b/apps/course/migrations/0032_auto_20180214_1336.py @@ -0,0 +1,28 @@ +# Generated by Django 2.0.2 on 2018-02-14 13:36 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0031_auto_20180213_0906'), + ] + + operations = [ + migrations.AlterField( + model_name='course', + name='short_description', + field=models.TextField(db_index=True, default='', verbose_name='Краткое описание курса'), + ), + migrations.AlterField( + model_name='course', + name='status', + field=models.PositiveSmallIntegerField(choices=[(0, 'Draft'), (1, 'Pending'), (2, 'Published'), (3, 'Archived'), (4, 'Denied')], default=0, verbose_name='Статус'), + ), + migrations.AlterField( + model_name='course', + name='title', + field=models.CharField(db_index=True, default='', max_length=100, verbose_name='Название курса'), + ), + ] diff --git a/apps/course/models.py b/apps/course/models.py index be1637a3..1ed76ca8 100644 --- a/apps/course/models.py +++ b/apps/course/models.py @@ -11,7 +11,7 @@ from project.mixins import BaseModel, DeactivatedMixin from .manager import CategoryQuerySet -from apps.content.models import ImageObject, Gallery +from apps.content.models import ImageObject, Gallery, Video User = get_user_model() @@ -45,10 +45,11 @@ class Course(BaseModel, DeactivatedMixin): max_length=100, unique=True, db_index=True, ) author = models.ForeignKey( - User, on_delete=models.SET_NULL, null=True, blank=True) - title = models.CharField('Название курса', max_length=100, db_index=True) + User, on_delete=models.SET_NULL, null=True, blank=True + ) + title = models.CharField('Название курса', default='', max_length=100, db_index=True) short_description = models.TextField( - 'Краткое описание курса', db_index=True + 'Краткое описание курса', default='', db_index=True ) from_author = models.TextField( 'От автора', default='', null=True, blank=True @@ -84,6 +85,14 @@ class Course(BaseModel, DeactivatedMixin): created_at = models.DateTimeField(auto_now_add=True) update_at = models.DateTimeField(auto_now=True) + class Meta: + verbose_name = 'Курс' + verbose_name_plural = 'Курсы' + ordering = ['-created_at'] + + def __str__(self): + return str(self.id) + ' ' + self.title + def save(self, *args, **kwargs): if not self.slug: self.slug = slugify( @@ -126,13 +135,9 @@ class Course(BaseModel, DeactivatedMixin): return True return False - def __str__(self): - return str(self.id) + ' ' + self.title - - class Meta: - verbose_name = 'Курс' - verbose_name_plural = 'Курсы' - ordering = ['-created_at'] + @property + def count_videos_in_lessons(self): + return Video.objects.filter(lesson__in=self.lessons.all()).count() class Category(models.Model): diff --git a/apps/course/templates/course/_items.html b/apps/course/templates/course/_items.html index 8c8dc4ac..407526ca 100644 --- a/apps/course/templates/course/_items.html +++ b/apps/course/templates/course/_items.html @@ -8,7 +8,7 @@ > {% if course.cover %} - + {% else %} {% endif %} diff --git a/apps/course/templates/course/course.html b/apps/course/templates/course/course.html index 93e3b235..51f12405 100644 --- a/apps/course/templates/course/course.html +++ b/apps/course/templates/course/course.html @@ -100,7 +100,7 @@ -
15 ноября
+
{{ course.created_at | date:"d F Yг." }}
@@ -126,7 +126,7 @@
-
12 видео
+
{{ course.count_videos_in_lessons }} видео
@@ -142,7 +142,11 @@
- + {% if course.cover %} + + {% else %} + + {% endif %} {% if course.is_deferred_start %}
Курс начнется:
@@ -201,14 +205,14 @@
{% endfor %} -{% if user.is_authenticated %} +{% if user.is_authenticated and course.lessons.exists %}
Содержание курса
-
+{% comment %}
Материалы, которые понадобятся
@@ -256,14 +260,19 @@ {% endfor %}
-
+
{% endcomment %} {% endif %} +{% if course.gallery %}
{% include "course/content/gallery.html" with results=True %}
-
+{% endif %} +
ПЕРСОНАЖИ
@@ -326,7 +335,7 @@
-
15 ноября
+
{{ course.created_at | date:"d F Yг." }}
diff --git a/apps/course/views.py b/apps/course/views.py index b41115ff..da8af471 100644 --- a/apps/course/views.py +++ b/apps/course/views.py @@ -1,14 +1,18 @@ +from django.contrib.auth import get_user_model from django.contrib.auth.decorators import login_required from django.db.models import Q -from django.http import JsonResponse +from django.http import JsonResponse, Http404 from django.shortcuts import get_object_or_404 from django.template import loader, Context, Template from django.views.generic import View, CreateView, DetailView, ListView, TemplateView +from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_http_methods from .models import Course, Like, Lesson, CourseComment, LessonComment from .filters import CourseFilter +User = get_user_model() + @login_required @csrf_exempt @@ -154,11 +158,20 @@ class CourseEditView(TemplateView): return context +@method_decorator(login_required, name='dispatch') class CourseView(DetailView): model = Course context_object_name = 'course' template_name = 'course/course.html' + def get(self, request, *args, **kwargs): + response = super().get(request, *args, **kwargs) + if (self.object.status != Course.PUBLISHED and + (request.user.role not in [User.AUTHOR_ROLE, User.ADMIN_ROLE] or + self.object.author != request.user)): + raise Http404 + return response + def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['next'] = self.request.GET.get('next', None) @@ -228,11 +241,20 @@ class CoursesView(ListView): return 'course/courses.html' +@method_decorator(login_required, name='dispatch') class LessonView(DetailView): model = Lesson context_object_name = 'lesson' template_name = 'course/lesson.html' + def get(self, request, *args, **kwargs): + response = super().get(request, *args, **kwargs) + if (self.object.course.status != Course.PUBLISHED and + (request.user.role not in [User.AUTHOR_ROLE, User.ADMIN_ROLE] or + self.object.course.author != request.user)): + raise Http404 + return response + class SearchView(CoursesView): template_name = 'course/result.html' diff --git a/web/src/components/blocks/BlockImages.vue b/web/src/components/blocks/BlockImages.vue index e601f9ed..0d8b6017 100644 --- a/web/src/components/blocks/BlockImages.vue +++ b/web/src/components/blocks/BlockImages.vue @@ -25,8 +25,13 @@