From 4ecb7a8c2bef770c6b3e2ea117e6dadbe09da5a7 Mon Sep 17 00:00:00 2001 From: Christian Cueni Date: Tue, 13 Aug 2019 09:15:37 +0200 Subject: [PATCH 1/8] Use assignments from chapters for list generation --- .../components/modules/ModuleNavigation.vue | 61 +++++++++++++++---- 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/client/src/components/modules/ModuleNavigation.vue b/client/src/components/modules/ModuleNavigation.vue index d7496eb0..0ad57d14 100644 --- a/client/src/components/modules/ModuleNavigation.vue +++ b/client/src/components/modules/ModuleNavigation.vue @@ -10,7 +10,7 @@ :key="assignment.id" class="module-navigation__anchor sub-navigation-item__link" exact-active-class="module-navigation__anchor--active" - >{{assignmentTitle(assignment)}} + >{{assignment.value.assignment}}
@@ -41,8 +41,6 @@ class="module-navigation__solution-toggle" data-cy="toggle-enable-solutions">
- - @@ -79,13 +77,14 @@ showResults() { return this.me.permissions.includes('users.can_manage_school_class_content'); }, - assignments() { - return [...this.module.assignments].sort((a, b) => { - return a.title.toLowerCase() > b.title.toLowerCase() ? 1 : -1; - }) - }, canManageContent() { return this.me.permissions.includes('users.can_manage_school_class_content'); + }, + assignments() { + if (!this.module.chapters) { + return []; + } + return this.extractAssignmentsFromChapters(this.module.chapters, []); } }, @@ -105,10 +104,47 @@ return `#chapter-${index}` }, submissionsLink(assignment) { - return `/module/${this.module.slug}/submissions/${assignment.id}`; + return `/module/${this.module.slug}/submissions/${assignment.value.id}`; }, - assignmentTitle(assignment) { - return assignment.assignment.length > 25 ? assignment.assignment.substring(0, 22) + '...' : assignment.assignment; + extractAssignmentsFromChapters(chapters, assignments) { + chapters.forEach(node => { + if (node.contentBlocks) { // in chapter node + // if chapter information is required then do it here like so: + // assignments.push({ + // chapterTitle: node.title + // }); + // return this.extractAssignmentsFromChapters(node.contentBlocks, assignments); + assignments = this.extractAssignmentsFromChapters(node.contentBlocks, assignments); + } else if (node.contents) { + let foundAssignments = []; + node.contents.forEach(contentNode => { + foundAssignments = this.concatAssignments(foundAssignments, contentNode); + }); + assignments = [...assignments, ...foundAssignments]; + } + }); + return assignments; + }, + concatAssignments(foundAssignments, node) { + let foundAssignment = this.findAssignment(node); + return foundAssignment ? [...foundAssignments, ...foundAssignment] : foundAssignments; + }, + findAssignment(node) { + if (node.type && node.type === 'assignment') { + return [node]; + } else if (node.type && node.type === 'content_list_item') { + let foundAssignments = []; + node.value.forEach(contentNode => { + foundAssignments = this.concatAssignments(foundAssignments, contentNode); + }); + return this.flattenArray(foundAssignments) + } else { + return null; + } + }, + flattenArray(arrayToFlatten) { + // https://stackoverflow.com/questions/10865025/merge-flatten-an-array-of-arrays + return [].concat.apply([], arrayToFlatten); } } } @@ -156,6 +192,9 @@ font-size: 0.875rem; line-height: 1.2rem; margin-bottom: .6875rem; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; &--active { color: $color-brand; From 8c42daca7ffd1dc6bd051af1fdc5bbcea640ff4f Mon Sep 17 00:00:00 2001 From: Christian Cueni Date: Tue, 13 Aug 2019 11:25:38 +0200 Subject: [PATCH 2/8] Update copy, fix widget height --- client/src/components/portfolio/ProjectWidget.vue | 4 ++-- .../src/components/toggle-menu/ToggleSolutionsForModule.vue | 2 +- client/src/pages/portfolio.vue | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/src/components/portfolio/ProjectWidget.vue b/client/src/components/portfolio/ProjectWidget.vue index 87873352..df9493ad 100644 --- a/client/src/components/portfolio/ProjectWidget.vue +++ b/client/src/components/portfolio/ProjectWidget.vue @@ -59,8 +59,8 @@ display: grid; margin-bottom: 0; } - grid-template-rows: 150px 1fr; - -ms-grid-rows: 150px 48px; + grid-template-rows: 175px 1fr; + -ms-grid-rows: 175px 48px; -ms-grid-columns: 1fr; &__content { diff --git a/client/src/components/toggle-menu/ToggleSolutionsForModule.vue b/client/src/components/toggle-menu/ToggleSolutionsForModule.vue index 21d23ce9..503c448f 100644 --- a/client/src/components/toggle-menu/ToggleSolutionsForModule.vue +++ b/client/src/components/toggle-menu/ToggleSolutionsForModule.vue @@ -1,5 +1,5 @@ diff --git a/client/src/pages/portfolio.vue b/client/src/pages/portfolio.vue index 4b74e30d..8b796d3f 100644 --- a/client/src/pages/portfolio.vue +++ b/client/src/pages/portfolio.vue @@ -66,7 +66,7 @@ grid-template-columns: minmax(max-content, 840px); } grid-row-gap: 30px; - grid-auto-rows: 200px; + grid-auto-rows: 225px; max-width: 840px; width: 100vw; /*justify-self: center;*/ From 083a8b03a88fe4702d09d0d0a8705b4ecd26dbe9 Mon Sep 17 00:00:00 2001 From: Christian Cueni Date: Wed, 7 Aug 2019 14:42:20 +0200 Subject: [PATCH 3/8] Add admin slug model --- server/api/schema.py | 4 +- server/books/blocks.py | 8 ++ .../migrations/0013_auto_20190807_1220.py | 24 +++++ server/books/models/contentblock.py | 5 +- server/rooms/admin.py | 8 +- server/rooms/factories.py | 11 ++- .../migrations/0007_auto_20190807_1218.py | 26 +++++ server/rooms/models.py | 7 ++ server/rooms/schema.py | 36 ++++++- server/rooms/tests/test_admin_room_query.py | 96 +++++++++++++++++++ .../rooms/tests/test_room_query_permission.py | 21 ++++ server/rooms/wagtail_hooks.py | 39 ++++++++ 12 files changed, 277 insertions(+), 8 deletions(-) create mode 100644 server/books/migrations/0013_auto_20190807_1220.py create mode 100644 server/rooms/migrations/0007_auto_20190807_1218.py create mode 100644 server/rooms/tests/test_admin_room_query.py create mode 100644 server/rooms/wagtail_hooks.py diff --git a/server/api/schema.py b/server/api/schema.py index 9c39b4db..b4800f09 100644 --- a/server/api/schema.py +++ b/server/api/schema.py @@ -18,12 +18,12 @@ from portfolio.schema import PortfolioQuery from surveys.schema import SurveysQuery from surveys.mutations import SurveysMutations from rooms.mutations import RoomMutations -from rooms.schema import RoomsQuery +from rooms.schema import RoomsQuery, AdminRoomsQuery from users.schema import UsersQuery from users.mutations import ProfileMutations -class Query(UsersQuery, RoomsQuery, ObjectivesQuery, BookQuery, AssignmentsQuery, StudentSubmissionQuery, +class Query(UsersQuery, AdminRoomsQuery, RoomsQuery, ObjectivesQuery, BookQuery, AssignmentsQuery, StudentSubmissionQuery, BasicKnowledgeQuery, PortfolioQuery, MyActivityQuery, SurveysQuery, graphene.ObjectType): node = relay.Node.Field() diff --git a/server/books/blocks.py b/server/books/blocks.py index 585157fc..38c0a094 100644 --- a/server/books/blocks.py +++ b/server/books/blocks.py @@ -98,6 +98,14 @@ class InstrumentTextBlock(blocks.StructBlock): text = blocks.RichTextBlock(features=INSTRUMENTS_RICH_TEXT_FEATURES) + +class AdminRoomSlugBlock(blocks.StructBlock): + class Meta: + icon = 'link' + + slug = blocks.TextBlock() + title = blocks.TextBlock() + # 'text_block' 'task' 'basic_knowledge' 'student_entry' 'image_block' # # url = blocks.URLBlock() diff --git a/server/books/migrations/0013_auto_20190807_1220.py b/server/books/migrations/0013_auto_20190807_1220.py new file mode 100644 index 00000000..e3bf9b9f --- /dev/null +++ b/server/books/migrations/0013_auto_20190807_1220.py @@ -0,0 +1,24 @@ +# Generated by Django 2.0.6 on 2019-08-07 12:20 + +import assignments.models +from django.db import migrations +import surveys.models +import wagtail.core.blocks +import wagtail.core.fields +import wagtail.images.blocks +import wagtail.snippets.blocks + + +class Migration(migrations.Migration): + + dependencies = [ + ('books', '0012_auto_20190722_0932'), + ] + + operations = [ + migrations.AlterField( + model_name='contentblock', + name='contents', + field=wagtail.core.fields.StreamField([('text_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))])), ('basic_knowledge', wagtail.core.blocks.StructBlock([('description', wagtail.core.blocks.RichTextBlock(required=False)), ('basic_knowledge', wagtail.core.blocks.PageChooserBlock(required=True, target_model=['basicknowledge.BasicKnowledge']))])), ('assignment', wagtail.core.blocks.StructBlock([('assignment_id', wagtail.snippets.blocks.SnippetChooserBlock(assignments.models.Assignment))])), ('survey', wagtail.core.blocks.StructBlock([('survey_id', wagtail.snippets.blocks.SnippetChooserBlock(surveys.models.Survey))])), ('image_block', wagtail.images.blocks.ImageChooserBlock()), ('image_url_block', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('link_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('solution', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))], icon='tick')), ('video_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('document_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('infogram_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock()), ('title', wagtail.core.blocks.TextBlock())])), ('genially_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock())])), ('subtitle', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock())])), ('admin_room_slug', wagtail.core.blocks.StructBlock([('slug', wagtail.core.blocks.TextBlock()), ('title', wagtail.core.blocks.TextBlock())])), ('content_list_item', wagtail.core.blocks.StreamBlock([('text_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))])), ('basic_knowledge', wagtail.core.blocks.StructBlock([('description', wagtail.core.blocks.RichTextBlock(required=False)), ('basic_knowledge', wagtail.core.blocks.PageChooserBlock(required=True, target_model=['basicknowledge.BasicKnowledge']))])), ('assignment', wagtail.core.blocks.StructBlock([('assignment_id', wagtail.snippets.blocks.SnippetChooserBlock(assignments.models.Assignment))])), ('survey', wagtail.core.blocks.StructBlock([('survey_id', wagtail.snippets.blocks.SnippetChooserBlock(surveys.models.Survey))])), ('image_block', wagtail.images.blocks.ImageChooserBlock()), ('image_url_block', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('link_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('solution', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))], icon='tick')), ('video_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('document_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('infogram_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock()), ('title', wagtail.core.blocks.TextBlock())])), ('genially_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock())])), ('subtitle', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock())])), ('admin_room_slug', wagtail.core.blocks.StructBlock([('slug', wagtail.core.blocks.TextBlock()), ('title', wagtail.core.blocks.TextBlock())]))]))], blank=True, null=True), + ), + ] diff --git a/server/books/models/contentblock.py b/server/books/models/contentblock.py index 95a3ef80..a4bab50f 100644 --- a/server/books/models/contentblock.py +++ b/server/books/models/contentblock.py @@ -7,7 +7,7 @@ from wagtail.core.fields import StreamField from wagtail.images.blocks import ImageChooserBlock from books.blocks import TextBlock, BasicKnowledgeBlock, LinkBlock, VideoBlock, DocumentBlock, \ - ImageUrlBlock, AssignmentBlock, InfogramBlock, GeniallyBlock, SubtitleBlock, SurveyBlock + ImageUrlBlock, AssignmentBlock, InfogramBlock, GeniallyBlock, SubtitleBlock, SurveyBlock, AdminRoomSlugBlock from core.wagtail_utils import StrictHierarchyPage from users.models import SchoolClass @@ -48,7 +48,8 @@ class ContentBlock(StrictHierarchyPage): ('document_block', DocumentBlock()), ('infogram_block', InfogramBlock()), ('genially_block', GeniallyBlock()), - ('subtitle', SubtitleBlock()) + ('subtitle', SubtitleBlock()), + ('admin_room_slug', AdminRoomSlugBlock()) ] content_list_item = StreamBlock(content_blocks) diff --git a/server/rooms/admin.py b/server/rooms/admin.py index 094630e9..c1c4a702 100644 --- a/server/rooms/admin.py +++ b/server/rooms/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin -from rooms.models import Room, RoomEntry +from rooms.models import Room, RoomEntry, AdminGeneratedRoomSlug @admin.register(Room) @@ -13,3 +13,9 @@ class RoomAdmin(admin.ModelAdmin): class RoomEntryAdmin(admin.ModelAdmin): list_display = ('id', 'slug', 'title', 'room', 'author') list_filter = ('room', 'author') + + +@admin.register(AdminGeneratedRoomSlug) +class AdminGeneratedRoomSlugAdmin(admin.ModelAdmin): + list_display = ('id', 'slug', 'title') + list_filter = ('slug', 'title') diff --git a/server/rooms/factories.py b/server/rooms/factories.py index e7c2acea..d34e1532 100644 --- a/server/rooms/factories.py +++ b/server/rooms/factories.py @@ -8,7 +8,7 @@ from wagtail.core.rich_text import RichText from books.factories import TextBlockFactory, ImageUrlBlockFactory, LinkBlockFactory from core.factories import fake, fake_paragraph -from rooms.models import Room, RoomEntry +from rooms.models import Room, RoomEntry, AdminGeneratedRoomSlug from users.models import SchoolClass @@ -77,3 +77,12 @@ class RoomEntryFactory(factory.django.DjangoModelFactory): def create(cls, **kwargs): cls.stream_field_magic(kwargs, 'contents') return cls._generate(CREATE_STRATEGY, kwargs) + + +class AdminGeneratedRoomSlugFactory(factory.django.DjangoModelFactory): + class Meta: + model = AdminGeneratedRoomSlug + + slug = factory.Sequence(lambda n: u'slug-{:d}'.format(n)) + title = factory.Sequence(lambda n: u'Title {:d}'.format(n)) + diff --git a/server/rooms/migrations/0007_auto_20190807_1218.py b/server/rooms/migrations/0007_auto_20190807_1218.py new file mode 100644 index 00000000..353dcef5 --- /dev/null +++ b/server/rooms/migrations/0007_auto_20190807_1218.py @@ -0,0 +1,26 @@ +# Generated by Django 2.0.6 on 2019-08-07 12:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('rooms', '0006_auto_20190722_0932'), + ] + + operations = [ + migrations.CreateModel( + name='AdminGeneratedRoomSlug', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('slug', models.CharField(max_length=255)), + ('title', models.CharField(max_length=255)), + ], + ), + migrations.AddField( + model_name='room', + name='user_created', + field=models.BooleanField(default=True), + ), + ] diff --git a/server/rooms/models.py b/server/rooms/models.py index a22f9dd5..47120c14 100644 --- a/server/rooms/models.py +++ b/server/rooms/models.py @@ -15,6 +15,7 @@ class Room(TitleSlugDescriptionModel): school_class = models.ForeignKey(SchoolClass, blank=False, null=False, on_delete=models.CASCADE, related_name='rooms') appearance = models.CharField(blank=True, null=False, max_length=255) + user_created = models.BooleanField(blank=False, null=False, default=True) def __str__(self): return 'Room {}-{}-{}'.format(self.id, self.title, self.school_class) @@ -41,3 +42,9 @@ class RoomEntry(TitleSlugDescriptionModel): def can_user_see_entry(self, user): return user.is_superuser or self.room.school_class.is_user_in_schoolclass(user) + + +class AdminGeneratedRoomSlug(models.Model): + slug = models.CharField(blank=False, null=False, max_length=255) + title = models.CharField(blank=False, null=False, max_length=255) + diff --git a/server/rooms/schema.py b/server/rooms/schema.py index 556c98b4..3db48198 100644 --- a/server/rooms/schema.py +++ b/server/rooms/schema.py @@ -6,7 +6,8 @@ from graphene_django import DjangoObjectType from graphene_django.filter import DjangoFilterConnectionField from api.utils import get_object, get_by_id_or_slug -from rooms.models import Room, RoomEntry +from rooms.models import Room, RoomEntry, AdminGeneratedRoomSlug +from users.models import SchoolClass from users.schema import UserNode logger = logging.getLogger(__name__) @@ -53,7 +54,7 @@ class RoomsQuery(object): user = info.context.user if user.is_superuser: return Room.objects.all() - return Room.objects.filter(school_class__in=user.school_classes.all()) + return Room.objects.filter(school_class__in=user.school_classes.all()).exclude(user_created=False) def resolve_room(self, info, **kwargs): room = get_by_id_or_slug(Room, **kwargs) @@ -82,3 +83,34 @@ class RoomsQuery(object): return RoomEntry.objects.none() else: return RoomEntry.objects.all() + + +class AdminRoomsQuery(object): + + admin_room = graphene.Field(RoomNode, slug=graphene.String(), class_id=graphene.ID()) + + def resolve_admin_room(self, info, **kwargs): + + slug = kwargs.get('slug') + schoolclass = get_object(SchoolClass, kwargs.get('class_id')) + + try: + slug = AdminGeneratedRoomSlug.objects.get(slug=slug) + except AdminGeneratedRoomSlug.DoesNotExist: + return None + + if schoolclass is None or not schoolclass.is_user_in_schoolclass(info.context.user): + return None + + room, created = Room.objects.get_or_create(school_class=schoolclass, user_created=False, slug=slug, + title=slug.title) + if not room.user_created and room.school_class.is_user_in_schoolclass(info.context.user): + return room + else: + return None + + # def resolve_all_room_entries(self, info, **kwargs): + # if not info.context.user.is_superuser: + # return RoomEntry.objects.none() + # else: + # return RoomEntry.objects.all() diff --git a/server/rooms/tests/test_admin_room_query.py b/server/rooms/tests/test_admin_room_query.py new file mode 100644 index 00000000..9c4ce36c --- /dev/null +++ b/server/rooms/tests/test_admin_room_query.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +# +# ITerativ GmbH +# http://www.iterativ.ch/ +# +# Copyright (c) 2019 ITerativ GmbH. All rights reserved. +# +# Created on 2019-08-07 +# @author: chrigu +from django.test import TestCase, RequestFactory +from graphene.test import Client +from graphql_relay import to_global_id + +from api.schema import schema +from core.factories import UserFactory +from rooms.factories import RoomFactory, AdminGeneratedRoomSlugFactory +from users.factories import SchoolClassFactory + + +class AdminRoomQueryPermission(TestCase): + + def setUp(self): + self.user = UserFactory(username='aschi') + self.another_user = UserFactory(username='pesche') + self.sc1 = SchoolClassFactory(users=[self.user]) + sc2 = SchoolClassFactory(users=[self.another_user]) + self.room1 = RoomFactory(school_class=self.sc1) + self.room2 = RoomFactory(school_class=sc2) + + self.admin_slug = AdminGeneratedRoomSlugFactory(slug='test-slug', title='title') + + self.sc1_id = to_global_id('SchoolClass', self.sc1.pk) + self.sc2_id = to_global_id('SchoolClass', sc2.pk) + + request = RequestFactory().get('/') + request.user = self.user + self.client = Client(schema=schema, context_value=request) + + self.query = ''' + query AdminRoomQuery($slug: String, $classId: ID!) { + adminRoom(slug: $slug, classId: $classId) { + title + } + } + ''' + + def test_should_return_none_if_slug_does_not_exist(self): + + result = self.client.execute(self.query, variables={ + 'slug': 'no-slug', + 'classId': 'norealId' + }) + self.assertIsNone(result.get('errors')) + self.assertIsNone(result.get('data').get('adminRoom')) + + def test_should_return_none_if_class_id_does_not_exist(self): + + result = self.client.execute(self.query, variables={ + 'slug': 'no-slug', + 'classId': 'norealId' + }) + self.assertIsNone(result.get('errors')) + self.assertIsNone(result.get('data').get('adminRoom')) + + def test_user_should_not_be_able_to_create_room_for_other_class(self): + + result = self.client.execute(self.query, variables={ + 'slug': self.admin_slug.slug, + 'classId': self.sc2_id + }) + + self.assertIsNone(result.get('errors')) + self.assertIsNone(result.get('data').get('adminRoom')) + + def test_should_create_room_if_none_exists(self): + + result = self.client.execute(self.query, variables={ + 'slug': self.admin_slug.slug, + 'classId': self.sc1_id + }) + + self.assertIsNone(result.get('errors')) + self.assertEqual(result.get('data').get('adminRoom').get('title'), self.admin_slug.title) + + def test_should_return_room_if_one_exists(self): + + existing_room = RoomFactory(school_class=self.sc1, user_created=False) + admin_slug = AdminGeneratedRoomSlugFactory(slug=existing_room.slug, title=existing_room.title) + + result = self.client.execute(self.query, variables={ + 'slug': admin_slug.slug, + 'classId': self.sc1_id + }) + + self.assertIsNone(result.get('errors')) + self.assertEqual(result.get('data').get('adminRoom').get('title'), existing_room.title) diff --git a/server/rooms/tests/test_room_query_permission.py b/server/rooms/tests/test_room_query_permission.py index 6a2cc583..a4b35b6e 100644 --- a/server/rooms/tests/test_room_query_permission.py +++ b/server/rooms/tests/test_room_query_permission.py @@ -61,6 +61,27 @@ class RoomQueryPermission(TestCase): self.assertIsNone(result.get('errors')) self.assertEqual(result.get('data').get('room'), None) + def test_student_should_only_user_created_rooms(self): + + admin_room = RoomFactory(school_class=self.room1.school_class, user_created=False) + + query = ''' + query { + rooms { + edges { + node { + title + } + } + } + } + ''' + + result = self.client.execute(query) + self.assertIsNone(result.get('errors')) + self.assertEqual(len(result.get('data').get('rooms').get('edges')), 1) + self.assertNotEqual(result.get('data').get('rooms').get('edges')[0].get('node').get('title'), admin_room.title) + class RoomEntryQueryPermissions(TestCase): diff --git a/server/rooms/wagtail_hooks.py b/server/rooms/wagtail_hooks.py new file mode 100644 index 00000000..26bbfee5 --- /dev/null +++ b/server/rooms/wagtail_hooks.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# +# ITerativ GmbH +# http://www.iterativ.ch/ +# +# Copyright (c) 2019 ITerativ GmbH. All rights reserved. +# +# Created on 2019-08-05 +# @author: chrigu +from wagtail.core import hooks + +from rooms.models import AdminGeneratedRoomSlug + + +@hooks.register('after_edit_page') +@hooks.register('after_create_page') +def do_after_page_edit(request, page): + blocks = get_room_blocks(page) + for block in blocks: + AdminGeneratedRoomSlug.objects.get_or_create(slug=block[1]['slug']) + + +def get_room_blocks(page): + top_level_admin_slug_blocks = get_block_from_stream_data(page.contents.stream_data, 'admin_room_slug') + content_list_admin_slug_blocks = get_admin_slugs_from_content_list(page.contents.stream_data) + return top_level_admin_slug_blocks + content_list_admin_slug_blocks + + +def get_block_from_stream_data(stream_data, block_name): + return [block for block in stream_data if block[0] in [block_name]] + + +def get_admin_slugs_from_content_list(stream_data): + admin_slug_blocks = [] + content_list_items = get_block_from_stream_data(stream_data, 'content_list_item') + for content_list_item in content_list_items: + admin_slug_blocks = admin_slug_blocks + get_block_from_stream_data(content_list_item[1].stream_data, + 'admin_room_slug') + return admin_slug_blocks From b0ae5a7cf1271f114bc221929278cd7d14b92088 Mon Sep 17 00:00:00 2001 From: Christian Cueni Date: Wed, 7 Aug 2019 15:42:00 +0200 Subject: [PATCH 4/8] Use titleslug model for admin slug --- server/books/blocks.py | 1 - ...to_20190807_1220.py => 0013_auto_20190807_1340.py} | 4 ++-- ...to_20190807_1218.py => 0007_auto_20190807_1323.py} | 11 ++++++++--- server/rooms/models.py | 6 +++--- server/rooms/schema.py | 8 +------- server/rooms/tests/test_admin_room_query.py | 2 +- server/rooms/wagtail_hooks.py | 2 +- 7 files changed, 16 insertions(+), 18 deletions(-) rename server/books/migrations/{0013_auto_20190807_1220.py => 0013_auto_20190807_1340.py} (51%) rename server/rooms/migrations/{0007_auto_20190807_1218.py => 0007_auto_20190807_1323.py} (54%) diff --git a/server/books/blocks.py b/server/books/blocks.py index 38c0a094..d2f8f560 100644 --- a/server/books/blocks.py +++ b/server/books/blocks.py @@ -103,7 +103,6 @@ class AdminRoomSlugBlock(blocks.StructBlock): class Meta: icon = 'link' - slug = blocks.TextBlock() title = blocks.TextBlock() # 'text_block' 'task' 'basic_knowledge' 'student_entry' 'image_block' diff --git a/server/books/migrations/0013_auto_20190807_1220.py b/server/books/migrations/0013_auto_20190807_1340.py similarity index 51% rename from server/books/migrations/0013_auto_20190807_1220.py rename to server/books/migrations/0013_auto_20190807_1340.py index e3bf9b9f..34bc13f8 100644 --- a/server/books/migrations/0013_auto_20190807_1220.py +++ b/server/books/migrations/0013_auto_20190807_1340.py @@ -1,4 +1,4 @@ -# Generated by Django 2.0.6 on 2019-08-07 12:20 +# Generated by Django 2.0.6 on 2019-08-07 13:40 import assignments.models from django.db import migrations @@ -19,6 +19,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='contentblock', name='contents', - field=wagtail.core.fields.StreamField([('text_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))])), ('basic_knowledge', wagtail.core.blocks.StructBlock([('description', wagtail.core.blocks.RichTextBlock(required=False)), ('basic_knowledge', wagtail.core.blocks.PageChooserBlock(required=True, target_model=['basicknowledge.BasicKnowledge']))])), ('assignment', wagtail.core.blocks.StructBlock([('assignment_id', wagtail.snippets.blocks.SnippetChooserBlock(assignments.models.Assignment))])), ('survey', wagtail.core.blocks.StructBlock([('survey_id', wagtail.snippets.blocks.SnippetChooserBlock(surveys.models.Survey))])), ('image_block', wagtail.images.blocks.ImageChooserBlock()), ('image_url_block', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('link_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('solution', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))], icon='tick')), ('video_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('document_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('infogram_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock()), ('title', wagtail.core.blocks.TextBlock())])), ('genially_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock())])), ('subtitle', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock())])), ('admin_room_slug', wagtail.core.blocks.StructBlock([('slug', wagtail.core.blocks.TextBlock()), ('title', wagtail.core.blocks.TextBlock())])), ('content_list_item', wagtail.core.blocks.StreamBlock([('text_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))])), ('basic_knowledge', wagtail.core.blocks.StructBlock([('description', wagtail.core.blocks.RichTextBlock(required=False)), ('basic_knowledge', wagtail.core.blocks.PageChooserBlock(required=True, target_model=['basicknowledge.BasicKnowledge']))])), ('assignment', wagtail.core.blocks.StructBlock([('assignment_id', wagtail.snippets.blocks.SnippetChooserBlock(assignments.models.Assignment))])), ('survey', wagtail.core.blocks.StructBlock([('survey_id', wagtail.snippets.blocks.SnippetChooserBlock(surveys.models.Survey))])), ('image_block', wagtail.images.blocks.ImageChooserBlock()), ('image_url_block', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('link_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('solution', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))], icon='tick')), ('video_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('document_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('infogram_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock()), ('title', wagtail.core.blocks.TextBlock())])), ('genially_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock())])), ('subtitle', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock())])), ('admin_room_slug', wagtail.core.blocks.StructBlock([('slug', wagtail.core.blocks.TextBlock()), ('title', wagtail.core.blocks.TextBlock())]))]))], blank=True, null=True), + field=wagtail.core.fields.StreamField([('text_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))])), ('basic_knowledge', wagtail.core.blocks.StructBlock([('description', wagtail.core.blocks.RichTextBlock(required=False)), ('basic_knowledge', wagtail.core.blocks.PageChooserBlock(required=True, target_model=['basicknowledge.BasicKnowledge']))])), ('assignment', wagtail.core.blocks.StructBlock([('assignment_id', wagtail.snippets.blocks.SnippetChooserBlock(assignments.models.Assignment))])), ('survey', wagtail.core.blocks.StructBlock([('survey_id', wagtail.snippets.blocks.SnippetChooserBlock(surveys.models.Survey))])), ('image_block', wagtail.images.blocks.ImageChooserBlock()), ('image_url_block', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('link_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('solution', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))], icon='tick')), ('video_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('document_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('infogram_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock()), ('title', wagtail.core.blocks.TextBlock())])), ('genially_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock())])), ('subtitle', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock())])), ('admin_room_slug', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.TextBlock())])), ('content_list_item', wagtail.core.blocks.StreamBlock([('text_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))])), ('basic_knowledge', wagtail.core.blocks.StructBlock([('description', wagtail.core.blocks.RichTextBlock(required=False)), ('basic_knowledge', wagtail.core.blocks.PageChooserBlock(required=True, target_model=['basicknowledge.BasicKnowledge']))])), ('assignment', wagtail.core.blocks.StructBlock([('assignment_id', wagtail.snippets.blocks.SnippetChooserBlock(assignments.models.Assignment))])), ('survey', wagtail.core.blocks.StructBlock([('survey_id', wagtail.snippets.blocks.SnippetChooserBlock(surveys.models.Survey))])), ('image_block', wagtail.images.blocks.ImageChooserBlock()), ('image_url_block', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('link_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('solution', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))], icon='tick')), ('video_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('document_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('infogram_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock()), ('title', wagtail.core.blocks.TextBlock())])), ('genially_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock())])), ('subtitle', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock())])), ('admin_room_slug', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.TextBlock())]))]))], blank=True, null=True), ), ] diff --git a/server/rooms/migrations/0007_auto_20190807_1218.py b/server/rooms/migrations/0007_auto_20190807_1323.py similarity index 54% rename from server/rooms/migrations/0007_auto_20190807_1218.py rename to server/rooms/migrations/0007_auto_20190807_1323.py index 353dcef5..59e7e654 100644 --- a/server/rooms/migrations/0007_auto_20190807_1218.py +++ b/server/rooms/migrations/0007_auto_20190807_1323.py @@ -1,6 +1,7 @@ -# Generated by Django 2.0.6 on 2019-08-07 12:18 +# Generated by Django 2.0.6 on 2019-08-07 13:23 from django.db import migrations, models +import django_extensions.db.fields class Migration(migrations.Migration): @@ -14,9 +15,13 @@ class Migration(migrations.Migration): name='AdminGeneratedRoomSlug', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('slug', models.CharField(max_length=255)), - ('title', models.CharField(max_length=255)), + ('title', models.CharField(max_length=255, verbose_name='title')), + ('description', models.TextField(blank=True, null=True, verbose_name='description')), + ('slug', django_extensions.db.fields.AutoSlugField(blank=True, editable=False, populate_from='title', verbose_name='slug')), ], + options={ + 'abstract': False, + }, ), migrations.AddField( model_name='room', diff --git a/server/rooms/models.py b/server/rooms/models.py index 47120c14..44e43659 100644 --- a/server/rooms/models.py +++ b/server/rooms/models.py @@ -44,7 +44,7 @@ class RoomEntry(TitleSlugDescriptionModel): return user.is_superuser or self.room.school_class.is_user_in_schoolclass(user) -class AdminGeneratedRoomSlug(models.Model): - slug = models.CharField(blank=False, null=False, max_length=255) - title = models.CharField(blank=False, null=False, max_length=255) +class AdminGeneratedRoomSlug(TitleSlugDescriptionModel): + def __str__(self): + return 'AdmimSlug {}-{}'.format(self.id, self.title) diff --git a/server/rooms/schema.py b/server/rooms/schema.py index 3db48198..5f5a506c 100644 --- a/server/rooms/schema.py +++ b/server/rooms/schema.py @@ -103,14 +103,8 @@ class AdminRoomsQuery(object): return None room, created = Room.objects.get_or_create(school_class=schoolclass, user_created=False, slug=slug, - title=slug.title) + title=slug.title, appearance='blue') if not room.user_created and room.school_class.is_user_in_schoolclass(info.context.user): return room else: return None - - # def resolve_all_room_entries(self, info, **kwargs): - # if not info.context.user.is_superuser: - # return RoomEntry.objects.none() - # else: - # return RoomEntry.objects.all() diff --git a/server/rooms/tests/test_admin_room_query.py b/server/rooms/tests/test_admin_room_query.py index 9c4ce36c..e64b23ab 100644 --- a/server/rooms/tests/test_admin_room_query.py +++ b/server/rooms/tests/test_admin_room_query.py @@ -27,7 +27,7 @@ class AdminRoomQueryPermission(TestCase): self.room1 = RoomFactory(school_class=self.sc1) self.room2 = RoomFactory(school_class=sc2) - self.admin_slug = AdminGeneratedRoomSlugFactory(slug='test-slug', title='title') + self.admin_slug = AdminGeneratedRoomSlugFactory(title='some title') self.sc1_id = to_global_id('SchoolClass', self.sc1.pk) self.sc2_id = to_global_id('SchoolClass', sc2.pk) diff --git a/server/rooms/wagtail_hooks.py b/server/rooms/wagtail_hooks.py index 26bbfee5..ed05d5cf 100644 --- a/server/rooms/wagtail_hooks.py +++ b/server/rooms/wagtail_hooks.py @@ -17,7 +17,7 @@ from rooms.models import AdminGeneratedRoomSlug def do_after_page_edit(request, page): blocks = get_room_blocks(page) for block in blocks: - AdminGeneratedRoomSlug.objects.get_or_create(slug=block[1]['slug']) + AdminGeneratedRoomSlug.objects.get_or_create(title=block[1]['title']) def get_room_blocks(page): From 8e0f9fd3776a541e7e5f8458028a1a7e5e2c50ee Mon Sep 17 00:00:00 2001 From: Christian Cueni Date: Wed, 7 Aug 2019 16:27:30 +0200 Subject: [PATCH 5/8] Support dicts in page update --- server/rooms/wagtail_hooks.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/server/rooms/wagtail_hooks.py b/server/rooms/wagtail_hooks.py index ed05d5cf..cc28d9aa 100644 --- a/server/rooms/wagtail_hooks.py +++ b/server/rooms/wagtail_hooks.py @@ -17,7 +17,11 @@ from rooms.models import AdminGeneratedRoomSlug def do_after_page_edit(request, page): blocks = get_room_blocks(page) for block in blocks: - AdminGeneratedRoomSlug.objects.get_or_create(title=block[1]['title']) + if isinstance(block, tuple): + title = block[1]['title'] + if isinstance(block, dict): + title = block['value']['title'] + AdminGeneratedRoomSlug.objects.get_or_create(title=title) def get_room_blocks(page): @@ -27,7 +31,11 @@ def get_room_blocks(page): def get_block_from_stream_data(stream_data, block_name): - return [block for block in stream_data if block[0] in [block_name]] + if isinstance(stream_data[0], tuple): + return [block for block in stream_data if block[0] in [block_name]] + if isinstance(stream_data[0], dict): + return [block for block in stream_data if block['type'] in [block_name]] + return [] def get_admin_slugs_from_content_list(stream_data): From 2fa006d79048f338d77757d1a8915f22e25aa7ce Mon Sep 17 00:00:00 2001 From: Christian Cueni Date: Thu, 8 Aug 2019 12:58:37 +0200 Subject: [PATCH 6/8] Rename block, update frontend --- client/src/App.vue | 2 + client/src/components/ContentBlock.vue | 2 + .../content-blocks/ModuleRoomSlug.vue | 20 +++++ client/src/components/mixins/room.js | 38 ++++++++ client/src/components/profile/Avatar.vue | 6 +- .../src/graphql/gql/moduleRoomEntryQuery.gql | 14 +++ client/src/layouts/DefaultLayout.vue | 70 +-------------- client/src/layouts/FullScreenLayout.vue | 51 +++++++++++ client/src/pages/moduleRoom.vue | 75 ++++++++++++++++ client/src/pages/room.vue | 90 +------------------ client/src/router/index.js | 10 ++- client/src/styles/_default-layout.scss | 72 +++++++++++++++ client/src/styles/_room.scss | 50 +++++++++++ client/src/styles/main.scss | 1 + server/api/schema.py | 4 +- server/books/blocks.py | 2 +- ...807_1340.py => 0013_auto_20190808_0649.py} | 4 +- server/books/models/contentblock.py | 4 +- server/books/schema/queries.py | 21 ++++- server/rooms/admin.py | 4 +- server/rooms/factories.py | 6 +- ...807_1323.py => 0007_auto_20190808_0649.py} | 4 +- server/rooms/models.py | 4 +- server/rooms/schema.py | 20 +++-- ...oom_query.py => test_module_room_query.py} | 24 ++--- .../rooms/tests/test_room_query_permission.py | 4 +- server/rooms/wagtail_hooks.py | 18 ++-- 27 files changed, 410 insertions(+), 210 deletions(-) create mode 100644 client/src/components/content-blocks/ModuleRoomSlug.vue create mode 100644 client/src/components/mixins/room.js create mode 100644 client/src/graphql/gql/moduleRoomEntryQuery.gql create mode 100644 client/src/layouts/FullScreenLayout.vue create mode 100644 client/src/pages/moduleRoom.vue create mode 100644 client/src/styles/_default-layout.scss create mode 100644 client/src/styles/_room.scss rename server/books/migrations/{0013_auto_20190807_1340.py => 0013_auto_20190808_0649.py} (52%) rename server/rooms/migrations/{0007_auto_20190807_1323.py => 0007_auto_20190808_0649.py} (91%) rename server/rooms/tests/{test_admin_room_query.py => test_module_room_query.py} (74%) diff --git a/client/src/App.vue b/client/src/App.vue index 35725e99..8e26ed22 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -10,6 +10,7 @@ import DefaultLayout from '@/layouts/DefaultLayout'; import SimpleLayout from '@/layouts/SimpleLayout'; import BlankLayout from '@/layouts/BlankLayout'; + import FullScreenLayout from '@/layouts/FullScreenLayout'; import Modal from '@/components/Modal'; import MobileNavigation from '@/components/MobileNavigation'; import NewContentBlockWizard from '@/components/content-block-form/NewContentBlockWizard'; @@ -33,6 +34,7 @@ DefaultLayout, SimpleLayout, BlankLayout, + FullScreenLayout, Modal, MobileNavigation, NewContentBlockWizard, diff --git a/client/src/components/ContentBlock.vue b/client/src/components/ContentBlock.vue index 1be89d92..f15b1161 100644 --- a/client/src/components/ContentBlock.vue +++ b/client/src/components/ContentBlock.vue @@ -61,6 +61,7 @@ import EyeIcon from '@/components/icons/EyeIcon'; import PenIcon from '@/components/icons/PenIcon'; import TrashIcon from '@/components/icons/TrashIcon'; + import ModuleRoomSlug from '@/components/content-blocks/ModuleRoomSlug' import CHAPTER_QUERY from '@/graphql/gql/chapterQuery.gql'; import DELETE_CONTENT_BLOCK_MUTATION from '@/graphql/gql/mutations/deleteContentBlock.gql'; @@ -93,6 +94,7 @@ 'genially_block': GeniallyBlock, 'subtitle': SubtitleBlock, 'content_list': ContentListBlock, + 'module_room_slug': ModuleRoomSlug, Survey, Solution, Assignment, diff --git a/client/src/components/content-blocks/ModuleRoomSlug.vue b/client/src/components/content-blocks/ModuleRoomSlug.vue new file mode 100644 index 00000000..b5f2f686 --- /dev/null +++ b/client/src/components/content-blocks/ModuleRoomSlug.vue @@ -0,0 +1,20 @@ + + + + + diff --git a/client/src/components/mixins/room.js b/client/src/components/mixins/room.js new file mode 100644 index 00000000..6059b87e --- /dev/null +++ b/client/src/components/mixins/room.js @@ -0,0 +1,38 @@ +import AddRoomEntryButton from '@/components/rooms/AddRoomEntryButton.vue'; +import RoomEntry from '@/components/rooms/RoomEntry.vue'; +import RoomGroupWidget from '@/components/rooms/RoomGroupWidget'; +import EntryCountWidget from '@/components/rooms/EntryCountWidget'; +import RoomActions from '@/components/rooms/RoomActions'; + +export default { + components: { + EntryCountWidget, + RoomGroupWidget, + AddRoomEntryButton, + RoomEntry, + RoomActions + }, + + beforeDestroy() { + this.$store.dispatch('setSpecialContainerClass', ''); + }, + + created() { + }, + + data() { + return { + room: [], + entries: [] + } + }, + + computed: { + roomEntryCount() { + return (this.room && this.room.roomEntries) ? this.room.roomEntries.length : 0 + }, + roomAppearance() { + return this.room ? this.room.appearance : '' + } + } +} diff --git a/client/src/components/profile/Avatar.vue b/client/src/components/profile/Avatar.vue index 8e781a7e..7d19d570 100644 --- a/client/src/components/profile/Avatar.vue +++ b/client/src/components/profile/Avatar.vue @@ -25,8 +25,10 @@ mounted () { if (this.avatarUrl !== '') { this.$refs.fakeImage.addEventListener('load', () => { - this.$refs.fakeImage.remove(); - this.isAvatarLoaded = true; + if (this.$refs.fakeImage) { + this.$refs.fakeImage.remove(); + this.isAvatarLoaded = true; + } }); }; } diff --git a/client/src/graphql/gql/moduleRoomEntryQuery.gql b/client/src/graphql/gql/moduleRoomEntryQuery.gql new file mode 100644 index 00000000..5f0d275d --- /dev/null +++ b/client/src/graphql/gql/moduleRoomEntryQuery.gql @@ -0,0 +1,14 @@ +#import "./fragments/roomParts.gql" +#import "./fragments/roomEntryParts.gql" +query ModuleRoomEntriesQuery($slug: String, $classId: ID!) { + moduleRoom(slug: $slug, classId: $classId) { + ...RoomParts + roomEntries { + edges { + node { + ...RoomEntryParts + } + } + } + } +} diff --git a/client/src/layouts/DefaultLayout.vue b/client/src/layouts/DefaultLayout.vue index d9e05517..b29dabc7 100644 --- a/client/src/layouts/DefaultLayout.vue +++ b/client/src/layouts/DefaultLayout.vue @@ -35,74 +35,6 @@ diff --git a/client/src/layouts/FullScreenLayout.vue b/client/src/layouts/FullScreenLayout.vue new file mode 100644 index 00000000..446edf75 --- /dev/null +++ b/client/src/layouts/FullScreenLayout.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/client/src/pages/moduleRoom.vue b/client/src/pages/moduleRoom.vue new file mode 100644 index 00000000..556f85de --- /dev/null +++ b/client/src/pages/moduleRoom.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/client/src/pages/room.vue b/client/src/pages/room.vue index 9b781af1..bc527133 100644 --- a/client/src/pages/room.vue +++ b/client/src/pages/room.vue @@ -25,46 +25,11 @@ diff --git a/client/src/router/index.js b/client/src/router/index.js index dc9384eb..fac2196a 100644 --- a/client/src/router/index.js +++ b/client/src/router/index.js @@ -26,6 +26,7 @@ import editProject from '@/pages/editProject' import newProject from '@/pages/newProject' import surveyPage from '@/pages/survey' import styleGuidePage from '@/pages/styleguide' +import moduleRoom from '@/pages/moduleRoom' import store from '@/store/index'; @@ -46,13 +47,20 @@ const routes = [ name: 'submissions', component: submissions, meta: {filter: true} - }, + } ] }, {path: '/rooms', name: 'rooms', component: rooms, meta: {filter: true}}, {path: '/new-room/', name: 'new-room', component: newRoom}, {path: '/edit-room/:id', name: 'edit-room', component: editRoom, props: true}, {path: '/room/:slug', name: 'room', component: room, props: true}, + { + path: '/module-room/:slug', + name: 'moduleRoom', + component: moduleRoom, + props: true, + meta: {layout: 'fullScreen'} + }, {path: '/article/:slug', name: 'article', component: article, meta: {layout: 'simple'}}, { path: '/instruments/:slug', diff --git a/client/src/styles/_default-layout.scss b/client/src/styles/_default-layout.scss new file mode 100644 index 00000000..d6fd7f87 --- /dev/null +++ b/client/src/styles/_default-layout.scss @@ -0,0 +1,72 @@ +@import "@/styles/_variables.scss"; +@import "@/styles/_mixins.scss"; + +.skillbox { + margin: 0 auto; + width: 100%; + @supports (display: grid) { + display: grid; + } + grid-template-rows: auto 1fr; + min-height: 100vh; + grid-auto-rows: 1fr; + + grid-template-areas: "h" "c"; + padding-bottom: 50px; + + &--show-filter { + grid-template-rows: auto auto 1fr; + -ms-grid-rows: 50px 50px 30px 1fr; // 1 extra row for gap + grid-template-areas: "h" "." "c"; + } + + /* + * For IE10+ + */ + + &--show-filter &__content { + -ms-grid-row: 4; + -ms-grid-column: 1; + } + + &--show-filter &__filter-bar { + -ms-grid-row: 2; + -ms-grid-column: 1; + } + + /* + * For IE10+ + */ + display: -ms-grid; + -ms-grid-rows: 50px 30px auto; // 1 extra row for gap + -ms-grid-columns: 1fr; + + @include skillbox-colors; + + &__header { + grid-area: h; + -ms-grid-row: 1; + } + + &__content { + -ms-grid-row: 3; + -ms-grid-column: 1; + } + + &__footer { + grid-area: f; + display: none; + } + + /* + * For IE10+ + */ + & > :nth-child(2) { + + } + + & > :nth-child(3) { + -ms-grid-row: 4; + -ms-grid-column: 1; + } +} diff --git a/client/src/styles/_room.scss b/client/src/styles/_room.scss new file mode 100644 index 00000000..abb484af --- /dev/null +++ b/client/src/styles/_room.scss @@ -0,0 +1,50 @@ +@import "@/styles/_variables.scss"; +@import "@/styles/_functions.scss"; +@import "@/styles/_mixins.scss"; + +.room { + display: grid; + grid-template-rows: auto 1fr; + margin-bottom: -50px; + + &__header { + padding: 30px; + } + + &__intro { + font-family: $sans-serif-font-family; + font-weight: $font-weight-regular; + font-size: toRem(17px); + max-width: 900px; + line-height: 1.5; + margin-bottom: 25px; + } + + &__meta { + display: flex; + flex-direction: column; + @include desktop { + flex-direction: row-reverse; + } + justify-content: start; + position: relative; + + & > :first-child { + margin-left: $large-spacing; + } + + & > :nth-child(2) { + margin-left: $large-spacing; + } + } + + &__content { + padding: 50px 15px; + background-color: rgba($color-charcoal-dark, 0.18); + @include desktop { + columns: 4; + padding: 50px 60px; + } + + } +} diff --git a/client/src/styles/main.scss b/client/src/styles/main.scss index 92f46300..74e4914d 100644 --- a/client/src/styles/main.scss +++ b/client/src/styles/main.scss @@ -6,6 +6,7 @@ @import "reset"; @import "typography"; @import "variables"; +@import "default-layout"; @import "buttons"; @import "forms"; @import "uploadcare_overwrite"; diff --git a/server/api/schema.py b/server/api/schema.py index b4800f09..68436053 100644 --- a/server/api/schema.py +++ b/server/api/schema.py @@ -18,12 +18,12 @@ from portfolio.schema import PortfolioQuery from surveys.schema import SurveysQuery from surveys.mutations import SurveysMutations from rooms.mutations import RoomMutations -from rooms.schema import RoomsQuery, AdminRoomsQuery +from rooms.schema import RoomsQuery, ModuleRoomsQuery from users.schema import UsersQuery from users.mutations import ProfileMutations -class Query(UsersQuery, AdminRoomsQuery, RoomsQuery, ObjectivesQuery, BookQuery, AssignmentsQuery, StudentSubmissionQuery, +class Query(UsersQuery, ModuleRoomsQuery, RoomsQuery, ObjectivesQuery, BookQuery, AssignmentsQuery, StudentSubmissionQuery, BasicKnowledgeQuery, PortfolioQuery, MyActivityQuery, SurveysQuery, graphene.ObjectType): node = relay.Node.Field() diff --git a/server/books/blocks.py b/server/books/blocks.py index d2f8f560..fb7b6df7 100644 --- a/server/books/blocks.py +++ b/server/books/blocks.py @@ -99,7 +99,7 @@ class InstrumentTextBlock(blocks.StructBlock): text = blocks.RichTextBlock(features=INSTRUMENTS_RICH_TEXT_FEATURES) -class AdminRoomSlugBlock(blocks.StructBlock): +class ModuleRoomSlugBlock(blocks.StructBlock): class Meta: icon = 'link' diff --git a/server/books/migrations/0013_auto_20190807_1340.py b/server/books/migrations/0013_auto_20190808_0649.py similarity index 52% rename from server/books/migrations/0013_auto_20190807_1340.py rename to server/books/migrations/0013_auto_20190808_0649.py index 34bc13f8..41454741 100644 --- a/server/books/migrations/0013_auto_20190807_1340.py +++ b/server/books/migrations/0013_auto_20190808_0649.py @@ -1,4 +1,4 @@ -# Generated by Django 2.0.6 on 2019-08-07 13:40 +# Generated by Django 2.0.6 on 2019-08-08 06:49 import assignments.models from django.db import migrations @@ -19,6 +19,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='contentblock', name='contents', - field=wagtail.core.fields.StreamField([('text_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))])), ('basic_knowledge', wagtail.core.blocks.StructBlock([('description', wagtail.core.blocks.RichTextBlock(required=False)), ('basic_knowledge', wagtail.core.blocks.PageChooserBlock(required=True, target_model=['basicknowledge.BasicKnowledge']))])), ('assignment', wagtail.core.blocks.StructBlock([('assignment_id', wagtail.snippets.blocks.SnippetChooserBlock(assignments.models.Assignment))])), ('survey', wagtail.core.blocks.StructBlock([('survey_id', wagtail.snippets.blocks.SnippetChooserBlock(surveys.models.Survey))])), ('image_block', wagtail.images.blocks.ImageChooserBlock()), ('image_url_block', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('link_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('solution', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))], icon='tick')), ('video_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('document_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('infogram_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock()), ('title', wagtail.core.blocks.TextBlock())])), ('genially_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock())])), ('subtitle', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock())])), ('admin_room_slug', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.TextBlock())])), ('content_list_item', wagtail.core.blocks.StreamBlock([('text_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))])), ('basic_knowledge', wagtail.core.blocks.StructBlock([('description', wagtail.core.blocks.RichTextBlock(required=False)), ('basic_knowledge', wagtail.core.blocks.PageChooserBlock(required=True, target_model=['basicknowledge.BasicKnowledge']))])), ('assignment', wagtail.core.blocks.StructBlock([('assignment_id', wagtail.snippets.blocks.SnippetChooserBlock(assignments.models.Assignment))])), ('survey', wagtail.core.blocks.StructBlock([('survey_id', wagtail.snippets.blocks.SnippetChooserBlock(surveys.models.Survey))])), ('image_block', wagtail.images.blocks.ImageChooserBlock()), ('image_url_block', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('link_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('solution', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))], icon='tick')), ('video_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('document_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('infogram_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock()), ('title', wagtail.core.blocks.TextBlock())])), ('genially_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock())])), ('subtitle', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock())])), ('admin_room_slug', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.TextBlock())]))]))], blank=True, null=True), + field=wagtail.core.fields.StreamField([('text_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))])), ('basic_knowledge', wagtail.core.blocks.StructBlock([('description', wagtail.core.blocks.RichTextBlock(required=False)), ('basic_knowledge', wagtail.core.blocks.PageChooserBlock(required=True, target_model=['basicknowledge.BasicKnowledge']))])), ('assignment', wagtail.core.blocks.StructBlock([('assignment_id', wagtail.snippets.blocks.SnippetChooserBlock(assignments.models.Assignment))])), ('survey', wagtail.core.blocks.StructBlock([('survey_id', wagtail.snippets.blocks.SnippetChooserBlock(surveys.models.Survey))])), ('image_block', wagtail.images.blocks.ImageChooserBlock()), ('image_url_block', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('link_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('solution', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))], icon='tick')), ('video_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('document_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('infogram_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock()), ('title', wagtail.core.blocks.TextBlock())])), ('genially_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock())])), ('subtitle', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock())])), ('module_room_slug', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.TextBlock())])), ('content_list_item', wagtail.core.blocks.StreamBlock([('text_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))])), ('basic_knowledge', wagtail.core.blocks.StructBlock([('description', wagtail.core.blocks.RichTextBlock(required=False)), ('basic_knowledge', wagtail.core.blocks.PageChooserBlock(required=True, target_model=['basicknowledge.BasicKnowledge']))])), ('assignment', wagtail.core.blocks.StructBlock([('assignment_id', wagtail.snippets.blocks.SnippetChooserBlock(assignments.models.Assignment))])), ('survey', wagtail.core.blocks.StructBlock([('survey_id', wagtail.snippets.blocks.SnippetChooserBlock(surveys.models.Survey))])), ('image_block', wagtail.images.blocks.ImageChooserBlock()), ('image_url_block', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('link_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())])), ('solution', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock(features=['ul']))], icon='tick')), ('video_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('document_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('infogram_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock()), ('title', wagtail.core.blocks.TextBlock())])), ('genially_block', wagtail.core.blocks.StructBlock([('id', wagtail.core.blocks.TextBlock())])), ('subtitle', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock())])), ('module_room_slug', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.TextBlock())]))]))], blank=True, null=True), ), ] diff --git a/server/books/models/contentblock.py b/server/books/models/contentblock.py index a4bab50f..0551635c 100644 --- a/server/books/models/contentblock.py +++ b/server/books/models/contentblock.py @@ -7,7 +7,7 @@ from wagtail.core.fields import StreamField from wagtail.images.blocks import ImageChooserBlock from books.blocks import TextBlock, BasicKnowledgeBlock, LinkBlock, VideoBlock, DocumentBlock, \ - ImageUrlBlock, AssignmentBlock, InfogramBlock, GeniallyBlock, SubtitleBlock, SurveyBlock, AdminRoomSlugBlock + ImageUrlBlock, AssignmentBlock, InfogramBlock, GeniallyBlock, SubtitleBlock, SurveyBlock, ModuleRoomSlugBlock from core.wagtail_utils import StrictHierarchyPage from users.models import SchoolClass @@ -49,7 +49,7 @@ class ContentBlock(StrictHierarchyPage): ('infogram_block', InfogramBlock()), ('genially_block', GeniallyBlock()), ('subtitle', SubtitleBlock()), - ('admin_room_slug', AdminRoomSlugBlock()) + ('module_room_slug', ModuleRoomSlugBlock()) ] content_list_item = StreamBlock(content_blocks) diff --git a/server/books/schema/queries.py b/server/books/schema/queries.py index 478dc614..2eae1eab 100644 --- a/server/books/schema/queries.py +++ b/server/books/schema/queries.py @@ -5,6 +5,7 @@ from graphene_django.filter import DjangoFilterConnectionField from api.utils import get_object from books.utils import are_solutions_enabled_for +from rooms.models import ModuleRoomSlug from ..models import Book, Topic, Module, Chapter, ContentBlock @@ -25,9 +26,23 @@ class ContentBlockNode(DjangoObjectType): return self.owner is not None and self.owner.pk == info.context.user.pk def resolve_contents(self, info, **kwargs): - if not are_solutions_enabled_for(info.context.user, self.module): - self.contents.stream_data = [content for content in self.contents.stream_data if - content['type'] != 'solution'] + updated_stream_data = [] + for content in self.contents.stream_data: + if not are_solutions_enabled_for(info.context.user, self.module) and content['type'] == 'solution': + continue + + if content['type'] == 'module_room_slug': + try: + module_room_slug = ModuleRoomSlug.objects.get(title=content['value']['title']) + content['value'] = { + 'title': content['value']['title'], + 'slug': module_room_slug.slug + } + except ModuleRoomSlug.DoesNotExist: + pass + updated_stream_data.append(content) + + self.contents.stream_data = updated_stream_data return self.contents diff --git a/server/rooms/admin.py b/server/rooms/admin.py index c1c4a702..128859ef 100644 --- a/server/rooms/admin.py +++ b/server/rooms/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin -from rooms.models import Room, RoomEntry, AdminGeneratedRoomSlug +from rooms.models import Room, RoomEntry, ModuleRoomSlug @admin.register(Room) @@ -15,7 +15,7 @@ class RoomEntryAdmin(admin.ModelAdmin): list_filter = ('room', 'author') -@admin.register(AdminGeneratedRoomSlug) +@admin.register(ModuleRoomSlug) class AdminGeneratedRoomSlugAdmin(admin.ModelAdmin): list_display = ('id', 'slug', 'title') list_filter = ('slug', 'title') diff --git a/server/rooms/factories.py b/server/rooms/factories.py index d34e1532..9a33f956 100644 --- a/server/rooms/factories.py +++ b/server/rooms/factories.py @@ -8,7 +8,7 @@ from wagtail.core.rich_text import RichText from books.factories import TextBlockFactory, ImageUrlBlockFactory, LinkBlockFactory from core.factories import fake, fake_paragraph -from rooms.models import Room, RoomEntry, AdminGeneratedRoomSlug +from rooms.models import Room, RoomEntry, ModuleRoomSlug from users.models import SchoolClass @@ -79,9 +79,9 @@ class RoomEntryFactory(factory.django.DjangoModelFactory): return cls._generate(CREATE_STRATEGY, kwargs) -class AdminGeneratedRoomSlugFactory(factory.django.DjangoModelFactory): +class ModuleRoomSlugFactory(factory.django.DjangoModelFactory): class Meta: - model = AdminGeneratedRoomSlug + model = ModuleRoomSlug slug = factory.Sequence(lambda n: u'slug-{:d}'.format(n)) title = factory.Sequence(lambda n: u'Title {:d}'.format(n)) diff --git a/server/rooms/migrations/0007_auto_20190807_1323.py b/server/rooms/migrations/0007_auto_20190808_0649.py similarity index 91% rename from server/rooms/migrations/0007_auto_20190807_1323.py rename to server/rooms/migrations/0007_auto_20190808_0649.py index 59e7e654..a3d7283e 100644 --- a/server/rooms/migrations/0007_auto_20190807_1323.py +++ b/server/rooms/migrations/0007_auto_20190808_0649.py @@ -1,4 +1,4 @@ -# Generated by Django 2.0.6 on 2019-08-07 13:23 +# Generated by Django 2.0.6 on 2019-08-08 06:49 from django.db import migrations, models import django_extensions.db.fields @@ -12,7 +12,7 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='AdminGeneratedRoomSlug', + name='ModuleRoomSlug', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('title', models.CharField(max_length=255, verbose_name='title')), diff --git a/server/rooms/models.py b/server/rooms/models.py index 44e43659..097638db 100644 --- a/server/rooms/models.py +++ b/server/rooms/models.py @@ -44,7 +44,7 @@ class RoomEntry(TitleSlugDescriptionModel): return user.is_superuser or self.room.school_class.is_user_in_schoolclass(user) -class AdminGeneratedRoomSlug(TitleSlugDescriptionModel): +class ModuleRoomSlug(TitleSlugDescriptionModel): def __str__(self): - return 'AdmimSlug {}-{}'.format(self.id, self.title) + return 'ModuleRoomSlug {}-{}'.format(self.id, self.title) diff --git a/server/rooms/schema.py b/server/rooms/schema.py index 5f5a506c..e9bc1306 100644 --- a/server/rooms/schema.py +++ b/server/rooms/schema.py @@ -6,7 +6,7 @@ from graphene_django import DjangoObjectType from graphene_django.filter import DjangoFilterConnectionField from api.utils import get_object, get_by_id_or_slug -from rooms.models import Room, RoomEntry, AdminGeneratedRoomSlug +from rooms.models import Room, RoomEntry, ModuleRoomSlug from users.models import SchoolClass from users.schema import UserNode @@ -85,25 +85,27 @@ class RoomsQuery(object): return RoomEntry.objects.all() -class AdminRoomsQuery(object): +class ModuleRoomsQuery(object): - admin_room = graphene.Field(RoomNode, slug=graphene.String(), class_id=graphene.ID()) + module_room = graphene.Field(RoomNode, slug=graphene.String(), class_id=graphene.ID()) - def resolve_admin_room(self, info, **kwargs): - - slug = kwargs.get('slug') + def resolve_module_room(self, info, **kwargs): schoolclass = get_object(SchoolClass, kwargs.get('class_id')) try: - slug = AdminGeneratedRoomSlug.objects.get(slug=slug) - except AdminGeneratedRoomSlug.DoesNotExist: + slug = ModuleRoomSlug.objects.get(slug=kwargs.get('slug')) + except ModuleRoomSlug.DoesNotExist: return None if schoolclass is None or not schoolclass.is_user_in_schoolclass(info.context.user): return None - room, created = Room.objects.get_or_create(school_class=schoolclass, user_created=False, slug=slug, + room, created = Room.objects.get_or_create(school_class=schoolclass, user_created=False, slug=slug.slug, title=slug.title, appearance='blue') + if created: + room.slug = slug.slug + room.save() + if not room.user_created and room.school_class.is_user_in_schoolclass(info.context.user): return room else: diff --git a/server/rooms/tests/test_admin_room_query.py b/server/rooms/tests/test_module_room_query.py similarity index 74% rename from server/rooms/tests/test_admin_room_query.py rename to server/rooms/tests/test_module_room_query.py index e64b23ab..fd451f5c 100644 --- a/server/rooms/tests/test_admin_room_query.py +++ b/server/rooms/tests/test_module_room_query.py @@ -13,7 +13,7 @@ from graphql_relay import to_global_id from api.schema import schema from core.factories import UserFactory -from rooms.factories import RoomFactory, AdminGeneratedRoomSlugFactory +from rooms.factories import RoomFactory, ModuleRoomSlugFactory from users.factories import SchoolClassFactory @@ -27,7 +27,7 @@ class AdminRoomQueryPermission(TestCase): self.room1 = RoomFactory(school_class=self.sc1) self.room2 = RoomFactory(school_class=sc2) - self.admin_slug = AdminGeneratedRoomSlugFactory(title='some title') + self.module_room_slug = ModuleRoomSlugFactory(title='some title') self.sc1_id = to_global_id('SchoolClass', self.sc1.pk) self.sc2_id = to_global_id('SchoolClass', sc2.pk) @@ -37,8 +37,8 @@ class AdminRoomQueryPermission(TestCase): self.client = Client(schema=schema, context_value=request) self.query = ''' - query AdminRoomQuery($slug: String, $classId: ID!) { - adminRoom(slug: $slug, classId: $classId) { + query ModuleRoomEntriesQuery($slug: String, $classId: ID!) { + moduleRoom(slug: $slug, classId: $classId) { title } } @@ -51,7 +51,7 @@ class AdminRoomQueryPermission(TestCase): 'classId': 'norealId' }) self.assertIsNone(result.get('errors')) - self.assertIsNone(result.get('data').get('adminRoom')) + self.assertIsNone(result.get('data').get('moduleRoom')) def test_should_return_none_if_class_id_does_not_exist(self): @@ -60,32 +60,32 @@ class AdminRoomQueryPermission(TestCase): 'classId': 'norealId' }) self.assertIsNone(result.get('errors')) - self.assertIsNone(result.get('data').get('adminRoom')) + self.assertIsNone(result.get('data').get('moduleRoom')) def test_user_should_not_be_able_to_create_room_for_other_class(self): result = self.client.execute(self.query, variables={ - 'slug': self.admin_slug.slug, + 'slug': self.module_room_slug.slug, 'classId': self.sc2_id }) self.assertIsNone(result.get('errors')) - self.assertIsNone(result.get('data').get('adminRoom')) + self.assertIsNone(result.get('data').get('moduleRoom')) def test_should_create_room_if_none_exists(self): result = self.client.execute(self.query, variables={ - 'slug': self.admin_slug.slug, + 'slug': self.module_room_slug.slug, 'classId': self.sc1_id }) self.assertIsNone(result.get('errors')) - self.assertEqual(result.get('data').get('adminRoom').get('title'), self.admin_slug.title) + self.assertEqual(result.get('data').get('moduleRoom').get('title'), self.module_room_slug.title) def test_should_return_room_if_one_exists(self): existing_room = RoomFactory(school_class=self.sc1, user_created=False) - admin_slug = AdminGeneratedRoomSlugFactory(slug=existing_room.slug, title=existing_room.title) + admin_slug = ModuleRoomSlugFactory(slug=existing_room.slug, title=existing_room.title) result = self.client.execute(self.query, variables={ 'slug': admin_slug.slug, @@ -93,4 +93,4 @@ class AdminRoomQueryPermission(TestCase): }) self.assertIsNone(result.get('errors')) - self.assertEqual(result.get('data').get('adminRoom').get('title'), existing_room.title) + self.assertEqual(result.get('data').get('moduleRoom').get('title'), existing_room.title) diff --git a/server/rooms/tests/test_room_query_permission.py b/server/rooms/tests/test_room_query_permission.py index a4b35b6e..f20490f8 100644 --- a/server/rooms/tests/test_room_query_permission.py +++ b/server/rooms/tests/test_room_query_permission.py @@ -63,7 +63,7 @@ class RoomQueryPermission(TestCase): def test_student_should_only_user_created_rooms(self): - admin_room = RoomFactory(school_class=self.room1.school_class, user_created=False) + modlue_room = RoomFactory(school_class=self.room1.school_class, user_created=False) query = ''' query { @@ -80,7 +80,7 @@ class RoomQueryPermission(TestCase): result = self.client.execute(query) self.assertIsNone(result.get('errors')) self.assertEqual(len(result.get('data').get('rooms').get('edges')), 1) - self.assertNotEqual(result.get('data').get('rooms').get('edges')[0].get('node').get('title'), admin_room.title) + self.assertNotEqual(result.get('data').get('rooms').get('edges')[0].get('node').get('title'), modlue_room.title) class RoomEntryQueryPermissions(TestCase): diff --git a/server/rooms/wagtail_hooks.py b/server/rooms/wagtail_hooks.py index cc28d9aa..22db624a 100644 --- a/server/rooms/wagtail_hooks.py +++ b/server/rooms/wagtail_hooks.py @@ -9,7 +9,7 @@ # @author: chrigu from wagtail.core import hooks -from rooms.models import AdminGeneratedRoomSlug +from rooms.models import ModuleRoomSlug @hooks.register('after_edit_page') @@ -21,13 +21,13 @@ def do_after_page_edit(request, page): title = block[1]['title'] if isinstance(block, dict): title = block['value']['title'] - AdminGeneratedRoomSlug.objects.get_or_create(title=title) + ModuleRoomSlug.objects.get_or_create(title=title) def get_room_blocks(page): - top_level_admin_slug_blocks = get_block_from_stream_data(page.contents.stream_data, 'admin_room_slug') - content_list_admin_slug_blocks = get_admin_slugs_from_content_list(page.contents.stream_data) - return top_level_admin_slug_blocks + content_list_admin_slug_blocks + top_level_module_room_slug_blocks = get_block_from_stream_data(page.contents.stream_data, 'module_room_slug') + content_list_module_room_slug_blocks = get_admin_slugs_from_content_list(page.contents.stream_data) + return top_level_module_room_slug_blocks + content_list_module_room_slug_blocks def get_block_from_stream_data(stream_data, block_name): @@ -39,9 +39,9 @@ def get_block_from_stream_data(stream_data, block_name): def get_admin_slugs_from_content_list(stream_data): - admin_slug_blocks = [] + module_room_slug_blocks = [] content_list_items = get_block_from_stream_data(stream_data, 'content_list_item') for content_list_item in content_list_items: - admin_slug_blocks = admin_slug_blocks + get_block_from_stream_data(content_list_item[1].stream_data, - 'admin_room_slug') - return admin_slug_blocks + module_room_slug_blocks = module_room_slug_blocks + get_block_from_stream_data(content_list_item[1].stream_data, + 'module_room_slug') + return module_room_slug_blocks From c2985d663a0c15cb4eb5b4e1030ccd744c5b20c8 Mon Sep 17 00:00:00 2001 From: Christian Cueni Date: Wed, 14 Aug 2019 10:49:23 +0200 Subject: [PATCH 7/8] Make layout styles scoped --- client/src/layouts/SimpleLayout.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/layouts/SimpleLayout.vue b/client/src/layouts/SimpleLayout.vue index ff35ba0f..58442114 100644 --- a/client/src/layouts/SimpleLayout.vue +++ b/client/src/layouts/SimpleLayout.vue @@ -7,7 +7,7 @@ -