Merge remote-tracking branch 'origin/dev' into dev

remotes/origin/hasaccess
Vitaly Baev 8 years ago
commit 46d7b747bd
  1. 2
      api/v1/serializers/course.py
  2. 28
      apps/course/migrations/0032_auto_20180214_1336.py
  3. 27
      apps/course/models.py
  4. 2
      apps/course/templates/course/_items.html
  5. 27
      apps/course/templates/course/course.html
  6. 24
      apps/course/views.py
  7. 15
      web/src/components/blocks/BlockImages.vue
  8. 4
      web/src/components/inputs/LilSelect.vue
  9. 7
      web/src/js/modules/api.js
  10. 2
      web/src/sass/_common.sass

@ -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)

@ -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='Название курса'),
),
]

@ -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):

@ -8,7 +8,7 @@
>
<a class="courses__preview" href="{% url 'course' course.id %}?next={{ request.get_full_path }}">
{% if course.cover %}
<img class="courses__pic" src="{{ course.cover.url }}"/>
<img class="courses__pic" src="{{ course.cover.image.url }}"/>
{% else %}
<img class="courses__pic" src="{% static 'img/no_cover.png' %}"/>
{% endif %}

@ -100,7 +100,7 @@
<use xlink:href="{% static 'img/sprite.svg' %}#icon-date"></use>
</svg>
</div>
<div class="meta__title">15 ноября</div>
<div class="meta__title">{{ course.created_at | date:"d F Yг." }}</div>
</div>
<div class="meta__item">
<div class="meta__icon">
@ -126,7 +126,7 @@
<use xlink:href="{% static 'img/sprite.svg' %}#icon-video"></use>
</svg>
</div>
<div class="meta__title">12 видео</div>
<div class="meta__title">{{ course.count_videos_in_lessons }} видео</div>
</div>
</div>
</div>
@ -142,7 +142,11 @@
</button>
</div>
<a class="course__video video" href="#">
<img class="video__pic" src="{% static 'img/video-1.jpg' %}">
{% if course.cover %}
<img class="video__pic" src="{{ course.cover.image.url }}"/>
{% else %}
<img class="video__pic" src="{% static 'img/no_cover.png' %}"/>
{% endif %}
{% if course.is_deferred_start %}
<div class="video__soon">
<div class="video__title">Курс начнется:</div>
@ -201,14 +205,14 @@
</div>
</div>
{% endfor %}
{% if user.is_authenticated %}
{% if user.is_authenticated and course.lessons.exists %}
<div class="section">
<div class="section__center center center_sm">
<div class="lessons">
<div class="lessons__title title">Содержание курса</div>
<div class="lessons__list">
{% for lesson in course.lessons.all %}
<a href="{% url 'lesson' pk=lesson.id %}">
<a href="{% if request.user.role == request.user.AUTHOR_ROLE or request.user.role == request.user.ADMIN_ROLE %}{% url 'lesson' pk=lesson.id %}{% else %}#{% endif %}">
<div class="lessons__item">
<div class="lessons__subtitle subtitle">{{ lesson.title }}</div>
<div class="lessons__row">
@ -233,7 +237,7 @@
</div>
</div>
</div>
<div class="section">
{% comment %} <div class="section">
<div class="section__center center center_sm">
<div class="title">Материалы, которые понадобятся</div>
<div class="materials">
@ -256,14 +260,19 @@
{% endfor %}
</div>
</div>
</div>
</div> {% endcomment %}
{% endif %}
{% if course.gallery %}
<div class="section section_gradient">
<div class="section__center center center_sm">
{% include "course/content/gallery.html" with results=True %}
</div>
</div>
<div class="course course_promo" style="background-image: url({% static 'img/video-1.jpg' %});">
{% endif %}
<div
class="course course_promo"
style="background-image: url({% if course.cover %}{{ course.cover.image.url }}{% else %}{% static 'img/no_cover.png' %}{% endif %});"
>
<div class="course__center center center_sm">
<div class="course__head">
<div class="course__theme theme theme_green">ПЕРСОНАЖИ</div>
@ -326,7 +335,7 @@
<use xlink:href="{% static 'img/sprite.svg' %}#icon-date"></use>
</svg>
</div>
<div class="meta__title">15 ноября</div>
<div class="meta__title">{{ course.created_at | date:"d F Yг." }}</div>
</div>
<div class="meta__item">
<div class="meta__icon">

@ -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'

@ -25,8 +25,13 @@
</div>
</div>
<div class="kit__gallery">
<div class="kit__preview" v-for="image in images" v-bind:class="{ 'kit__preview--loading': image.loading }">
<div class="kit__preview" v-for="(image, index) in images" v-bind:class="{ 'kit__preview--loading': image.loading }">
<img :src="image.src" class="kit__pic">
<button type="button" @click="onRemoveImage(index)">
<svg class="icon icon-delete">
<use xlink:href="/static/img/sprite.svg#icon-delete"></use>
</svg>
</button>
</div>
<div class="kit__photo">
<svg class="icon icon-add-plus">
@ -85,6 +90,14 @@
},
onRemove() {
this.$emit('remove', this.index);
},
onRemoveImage(index) {
let images = this.images;
let id = images[index].img;
images.splice(index, 1);
this.$emit('update:images', images);
api.removeImage(id, this.accessToken);
}
}
}

@ -4,8 +4,8 @@
{{ selectedTitle }}
</div>
<div class="select__drop">
<div class="select__option" v-for="option in options">
<div class="select__title" @click.stop.prevent="selectOption(option)">{{ option.title }}</div>
<div class="select__option" v-for="option in options" @click.stop.prevent="selectOption(option)">
<div class="select__title">{{ option.title }}</div>
</div>
</div>
</div>

@ -21,6 +21,13 @@ export const api = {
}
});
},
removeImage: (imageId, accessToken) => {
return axios.delete(`/api/v1/image-objects/${imageId}/`, {
headers: {
'Authorization': `Token ${accessToken}`,
}
});
},
loadCourse: (courseId, accessToken) => {
return axios.get(`/api/v1/courses/${courseId}/`, {
headers: {

@ -1872,7 +1872,7 @@ a.grey-link
.info__field--light
.select
.select__head
color: #777
color: #fff
font-size: 15px
&:after
border-color: #fff transparent transparent transparent

Loading…
Cancel
Save