From a1490c60494aeb452f209e23670f727e34da022a Mon Sep 17 00:00:00 2001
From: Ramon Wenger
Date: Mon, 1 Mar 2021 23:25:38 +0100
Subject: [PATCH 01/19] Add basic test for new visibility feature
---
server/books/schema/queries.py | 2 +-
.../test_copy_visibility_for_other_class.py | 83 +++++++++++++++++++
server/core/settings.py | 5 ++
3 files changed, 89 insertions(+), 1 deletion(-)
create mode 100644 server/books/tests/test_copy_visibility_for_other_class.py
diff --git a/server/books/schema/queries.py b/server/books/schema/queries.py
index 338aae4b..bcf41e0e 100644
--- a/server/books/schema/queries.py
+++ b/server/books/schema/queries.py
@@ -199,7 +199,7 @@ class TopicNode(DjangoObjectType):
class Meta:
model = Topic
only_fields = [
- 'slug', 'title', 'meta_title', 'teaser', 'description', 'vimeo_id', 'order', 'instructions'
+ 'slug', 'title', 'teaser', 'description', 'vimeo_id', 'order', 'instructions'
]
filter_fields = {
'slug': ['exact', 'icontains', 'in'],
diff --git a/server/books/tests/test_copy_visibility_for_other_class.py b/server/books/tests/test_copy_visibility_for_other_class.py
new file mode 100644
index 00000000..39c3dcfd
--- /dev/null
+++ b/server/books/tests/test_copy_visibility_for_other_class.py
@@ -0,0 +1,83 @@
+import logging
+
+from django.test import TestCase, RequestFactory
+from graphene.test import Client
+from graphql_relay import to_global_id
+
+from api.schema import schema
+from books.models import ContentBlock, Chapter
+from books.factories import ModuleFactory
+from users.factories import SchoolClassFactory
+from users.models import User
+from users.services import create_users
+
+logger = logging.getLogger(__name__)
+
+class CopyVisibilityForClassesTestCase(TestCase):
+ def setUp(self):
+ module = ModuleFactory()
+ chapter = Chapter(title='Some Chapter')
+ module.add_child(instance=chapter)
+ create_users()
+ teacher = User.objects.get(username='teacher')
+ student1 = User.objects.get(username='student1')
+ student2 = User.objects.get(username='student2')
+ school_class1 = SchoolClassFactory(name='hidden-class', users=[teacher, student1])
+ school_class2 = SchoolClassFactory(name='default-class', users=[teacher, student2])
+
+ default_content_block = ContentBlock(title='default block', slug='default')
+ hidden_content_block = ContentBlock(title='hidden block', slug='hidden')
+
+ chapter.specific.add_child(instance=default_content_block)
+ chapter.specific.add_child(instance=hidden_content_block)
+
+ hidden_content_block.hidden_for.add(school_class1)
+
+ teacher_request = RequestFactory().get('/')
+ teacher_request.user = teacher
+ self.teacher_client = Client(schema=schema, context_value=teacher_request)
+
+ student1_request = RequestFactory().get('/')
+ student1_request.user = student1
+ self.student1_client = Client(schema=schema, context_value=student1_request)
+
+ student2_request = RequestFactory().get('/')
+ student2_request.user = student2
+ self.student2_client = Client(schema=schema, context_value=student2_request)
+
+ self.chapter = to_global_id('ChapterNode', chapter.pk)
+ self.default_content_block = to_global_id('ContentBlockNode', default_content_block.pk)
+ self.hidden_content_block = to_global_id('ContentBlockNode', hidden_content_block.pk)
+
+ def _get_result(self, query, client, id):
+ result = client.execute(query, variables={
+ 'id': id
+ })
+ return result
+
+ def test_hidden_for_set_correctly(self):
+ self.assertEqual(ContentBlock.objects.count(), 2)
+
+ query = """
+ query ContentBlockQuery($id: ID!) {
+ contentBlock(id: $id) {
+ hiddenFor {
+ edges {
+ node {
+ id
+ name
+ }
+ }
+ }
+ }
+ }
+ """
+
+ result = self._get_result(query, self.teacher_client, self.hidden_content_block)
+ logger.info(result)
+ hiddenFor = result.get('data').get('contentBlock').get('hiddenFor').get('edges')
+ logger.info(hiddenFor)
+ self.assertTrue('hidden-class' in map(lambda x: x['node']['name'], hiddenFor))
+ self.assertFalse('default-class' in map(lambda x: x['node']['name'], hiddenFor))
+
+
diff --git a/server/core/settings.py b/server/core/settings.py
index 74892f06..f74aacd3 100644
--- a/server/core/settings.py
+++ b/server/core/settings.py
@@ -335,6 +335,11 @@ LOGGING = {
'level': 'WARNING',
'propagate': True,
},
+ 'wagtail': {
+ 'handlers': ['console'],
+ 'level': 'WARNING',
+ 'propagate': False,
+ },
}
}
From b3268a62999c5519fca9db3ca2e29f8c39b6ceb1 Mon Sep 17 00:00:00 2001
From: Ramon Wenger
Date: Wed, 3 Mar 2021 16:36:44 +0100
Subject: [PATCH 02/19] Add settings pages
---
client/src/pages/moduleSettings.vue | 35 +++++++++++++
client/src/pages/moduleVisibility.vue | 73 +++++++++++++++++++++++++++
client/src/router/index.js | 13 +++++
client/src/styles/_mixins.scss | 29 +++++++++++
4 files changed, 150 insertions(+)
create mode 100644 client/src/pages/moduleSettings.vue
create mode 100644 client/src/pages/moduleVisibility.vue
diff --git a/client/src/pages/moduleSettings.vue b/client/src/pages/moduleSettings.vue
new file mode 100644
index 00000000..8ed8a4dd
--- /dev/null
+++ b/client/src/pages/moduleSettings.vue
@@ -0,0 +1,35 @@
+
+
+
Einstellungen
+
+ Lösungen
+ Wollen Sie die Lösungen in diesem Modul für Lernende anzeigen?
+
+
+
+ Sichtbarkeit
+ Haben Sie die Sichtbarkeit für eine andere Klasse bereits angepasst? Dann
+ können Sie diese Anpassungen hier
+ übernehmen
+
+
+
+
+
+
+
diff --git a/client/src/pages/moduleVisibility.vue b/client/src/pages/moduleVisibility.vue
new file mode 100644
index 00000000..d7ac79eb
--- /dev/null
+++ b/client/src/pages/moduleVisibility.vue
@@ -0,0 +1,73 @@
+
+
+
Sichtbarkeit
+
+
+ Wollen Sie die angepasste Sichtbarkeit (
+
+ ) von Inhalten einer anderen Klasse übernehmen?
+
+
+
+
+ Von
+
+ für INF2019i übernehmen.
+
+
+
+
+
+
+
+
diff --git a/client/src/router/index.js b/client/src/router/index.js
index 759df143..70a678c0 100644
--- a/client/src/router/index.js
+++ b/client/src/router/index.js
@@ -40,9 +40,11 @@ import onboardingStart from '@/pages/onboarding/start';
import onboardingStep1 from '@/pages/onboarding/step1';
import onboardingStep2 from '@/pages/onboarding/step2';
import onboardingStep3 from '@/pages/onboarding/step3';
+import settingsPage from '@/pages/moduleSettings';
import portfolioRoutes from './portfolio.routes';
import store from '@/store/index';
+import moduleVisibility from '@/pages/moduleVisibility';
const ONBOARDING_STEP_1 = 'onboarding-step-1';
const ONBOARDING_STEP_2 = 'onboarding-step-2';
@@ -240,6 +242,17 @@ const routes = [
},
]
},
+ {
+ path: '/settings',
+ component: settingsPage
+ },
+ {
+ path: '/visibility',
+ component: moduleVisibility,
+ meta: {
+ layout: 'simple'
+ }
+ },
{path: '/styleguide', component: styleGuidePage},
{
path: '*',
diff --git a/client/src/styles/_mixins.scss b/client/src/styles/_mixins.scss
index dd5aea82..bf627f81 100644
--- a/client/src/styles/_mixins.scss
+++ b/client/src/styles/_mixins.scss
@@ -211,3 +211,32 @@
line-height: $default-heading-line-height;
margin-top: -0.2rem; // to offset the 1.2 line height, it leaves a padding on top
}
+
+@mixin settings-page {
+ padding: $section-spacing;
+
+ @include desktop {
+ width: 800px;
+ display: flex;
+ flex-direction: column;
+ justify-self: center;
+ }
+
+ &__page-title {
+ @include main-title;
+ margin-bottom: $section-spacing;
+ }
+
+ &__heading {
+ @include heading-1;
+ margin-bottom: $medium-spacing;
+ }
+
+ &__paragraph {
+ @include lead-paragraph;
+ }
+
+ &__section {
+ margin-bottom: $section-spacing;
+ }
+}
From 14022fa9eb90ce47ac7deaf2dee65d23db71ac7c Mon Sep 17 00:00:00 2001
From: Ramon Wenger
Date: Wed, 3 Mar 2021 17:17:06 +0100
Subject: [PATCH 03/19] Remove unused code
---
client/src/pages/moduleVisibility.vue | 7 -------
1 file changed, 7 deletions(-)
diff --git a/client/src/pages/moduleVisibility.vue b/client/src/pages/moduleVisibility.vue
index d7ac79eb..ab61135b 100644
--- a/client/src/pages/moduleVisibility.vue
+++ b/client/src/pages/moduleVisibility.vue
@@ -28,19 +28,12 @@
From 209838dadb6518281172032838a3ca5ace1e3554 Mon Sep 17 00:00:00 2001
From: Ramon Wenger
Date: Fri, 5 Mar 2021 14:27:36 +0100
Subject: [PATCH 04/19] Add method for syncing school classes
---
server/books/models/module.py | 30 +++-
.../test_copy_visibility_for_other_class.py | 141 ++++++++++++++----
2 files changed, 143 insertions(+), 28 deletions(-)
diff --git a/server/books/models/module.py b/server/books/models/module.py
index 159d923b..2785d3a1 100644
--- a/server/books/models/module.py
+++ b/server/books/models/module.py
@@ -10,8 +10,6 @@ from books.blocks import DEFAULT_RICH_TEXT_FEATURES
from core.wagtail_utils import StrictHierarchyPage
from users.models import SchoolClass
-logger = logging.getLogger(__name__)
-
class Module(StrictHierarchyPage):
class Meta:
@@ -60,6 +58,34 @@ class Module(StrictHierarchyPage):
def get_child_ids(self):
return self.get_children().values_list('id', flat=True)
+ def sync_from_school_class(self, school_class_pattern, school_class_to_sync):
+ # import here so we don't get a circular import error
+ from books.models import Chapter, ContentBlock
+
+ # get chapters of module
+ chapters = Chapter.get_by_parent(self)
+ content_block_query = ContentBlock.objects.none()
+
+ # get content blocks of chapters
+ for chapter in chapters:
+ content_block_query = content_block_query.union(ContentBlock.get_by_parent(chapter))
+
+ # clear all `hidden for` and `visible for` for class `school_class_to_sync`
+ for content_block in school_class_to_sync.hidden_content_blocks.intersection(content_block_query):
+ content_block.hidden_for.remove(school_class_to_sync)
+ for content_block in school_class_to_sync.visible_content_blocks.intersection(content_block_query):
+ content_block.visible_for.remove(school_class_to_sync)
+
+ # get all content blocks with `hidden for` for class `school_class_pattern`
+ for content_block in school_class_pattern.hidden_content_blocks.intersection(content_block_query):
+ # add `school_class_to_sync` to these blocks' `hidden for`
+ content_block.hidden_for.add(school_class_to_sync)
+
+ # get all content blocks with `visible for` for class `school_class_pattern`
+ for content_block in school_class_pattern.visible_content_blocks.intersection(content_block_query):
+ # add `school_class_to_sync` to these blocks' `visible for`
+ content_block.visible_for.add(school_class_to_sync)
+
class RecentModule(models.Model):
module = models.ForeignKey(Module, on_delete=models.CASCADE, related_name='recent_modules')
diff --git a/server/books/tests/test_copy_visibility_for_other_class.py b/server/books/tests/test_copy_visibility_for_other_class.py
index 39c3dcfd..60cff107 100644
--- a/server/books/tests/test_copy_visibility_for_other_class.py
+++ b/server/books/tests/test_copy_visibility_for_other_class.py
@@ -11,9 +11,46 @@ from users.factories import SchoolClassFactory
from users.models import User
from users.services import create_users
-logger = logging.getLogger(__name__)
+CONTENT_BLOCK_QUERY = """
+ query ContentBlockQuery($id: ID!) {
+ contentBlock(id: $id) {
+ hiddenFor {
+ edges {
+ node {
+ id
+ name
+ }
+ }
+ }
+ visibleFor {
+ edges {
+ node {
+ id
+ name
+ }
+ }
+ }
+ }
+ }
+ """
+
class CopyVisibilityForClassesTestCase(TestCase):
+ """
+ what do we want to happen?
+ we have 3 public content blocks [X, Y, Z]
+ we have 2 custom content block [M, N]
+ we have 2 school classes [A, B]
+ one public content block is hidden for class A | [X, Y]
+ one custom content block is visible for class A | [M]
+ class B also sees two of three public content blocks, but one is different from what A sees | [X, Z]
+ class B doesn't see the custom content block, but another one | [N]
+ so A sees | [X, Y, M]
+ B sees | [X, Z, N]
+ we want to copy the settings from class A to class B
+ now class B sees the same content blocks as class A | [X, Y, N]
+ """
+
def setUp(self):
module = ModuleFactory()
chapter = Chapter(title='Some Chapter')
@@ -22,16 +59,28 @@ class CopyVisibilityForClassesTestCase(TestCase):
teacher = User.objects.get(username='teacher')
student1 = User.objects.get(username='student1')
student2 = User.objects.get(username='student2')
- school_class1 = SchoolClassFactory(name='hidden-class', users=[teacher, student1])
- school_class2 = SchoolClassFactory(name='default-class', users=[teacher, student2])
+ # school class to be used as the pattern or model
+ template_school_class = SchoolClassFactory(name='template-class', users=[teacher, student1])
+ # school class to be synced, e.g. adapted to be like the other
+ school_class_to_be_synced = SchoolClassFactory(name='class-to-be-synced', users=[teacher, student2])
default_content_block = ContentBlock(title='default block', slug='default')
hidden_content_block = ContentBlock(title='hidden block', slug='hidden')
+ other_hidden_content_block = ContentBlock(title='other hidden block', slug='other-hidden')
+ custom_content_block = ContentBlock(title='custom block', slug='custom', owner=teacher)
+ other_custom_content_block = ContentBlock(title='other custom block', slug='other-custom', owner=teacher)
chapter.specific.add_child(instance=default_content_block)
chapter.specific.add_child(instance=hidden_content_block)
+ chapter.specific.add_child(instance=custom_content_block)
+ chapter.specific.add_child(instance=other_custom_content_block)
+ chapter.specific.add_child(instance=other_hidden_content_block)
- hidden_content_block.hidden_for.add(school_class1)
+ hidden_content_block.hidden_for.add(template_school_class)
+ custom_content_block.visible_for.add(template_school_class)
+
+ other_hidden_content_block.hidden_for.add(school_class_to_be_synced)
+ other_custom_content_block.visible_for.add(school_class_to_be_synced)
teacher_request = RequestFactory().get('/')
teacher_request.user = teacher
@@ -45,9 +94,15 @@ class CopyVisibilityForClassesTestCase(TestCase):
student2_request.user = student2
self.student2_client = Client(schema=schema, context_value=student2_request)
+ self.template_school_class = template_school_class
+ self.school_class_to_be_synced = school_class_to_be_synced
+ self.module = module
self.chapter = to_global_id('ChapterNode', chapter.pk)
self.default_content_block = to_global_id('ContentBlockNode', default_content_block.pk)
self.hidden_content_block = to_global_id('ContentBlockNode', hidden_content_block.pk)
+ self.custom_content_block = to_global_id('ContentBlockNode', custom_content_block.pk)
+ self.other_custom_content_block = to_global_id('ContentBlockNode', other_custom_content_block.pk)
+ self.other_hidden_content_block = to_global_id('ContentBlockNode', other_hidden_content_block.pk)
def _get_result(self, query, client, id):
result = client.execute(query, variables={
@@ -55,29 +110,63 @@ class CopyVisibilityForClassesTestCase(TestCase):
})
return result
- def test_hidden_for_set_correctly(self):
- self.assertEqual(ContentBlock.objects.count(), 2)
+ def test_hidden_for_and_visible_for_set_correctly(self):
+ self.assertEqual(ContentBlock.objects.count(), 5)
- query = """
- query ContentBlockQuery($id: ID!) {
- contentBlock(id: $id) {
- hiddenFor {
- edges {
- node {
- id
- name
- }
- }
- }
- }
- }
- """
+ hidden_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, self.hidden_content_block)
+ hidden_for = hidden_result.get('data').get('contentBlock').get('hiddenFor').get('edges')
+ self.assertTrue('template-class' in map(lambda x: x['node']['name'], hidden_for))
+ self.assertFalse('class-to-be-synced' in map(lambda x: x['node']['name'], hidden_for))
- result = self._get_result(query, self.teacher_client, self.hidden_content_block)
- logger.info(result)
- hiddenFor = result.get('data').get('contentBlock').get('hiddenFor').get('edges')
- logger.info(hiddenFor)
- self.assertTrue('hidden-class' in map(lambda x: x['node']['name'], hiddenFor))
- self.assertFalse('default-class' in map(lambda x: x['node']['name'], hiddenFor))
+ other_hidden_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client,
+ self.other_hidden_content_block)
+ hidden_for = other_hidden_result.get('data').get('contentBlock').get('hiddenFor').get('edges')
+ self.assertFalse('template-class' in map(lambda x: x['node']['name'], hidden_for))
+ self.assertTrue('class-to-be-synced' in map(lambda x: x['node']['name'], hidden_for))
+ default_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, self.default_content_block)
+ hidden_for = default_result.get('data').get('contentBlock').get('hiddenFor').get('edges')
+ self.assertEqual(len(hidden_for), 0)
+ custom_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, self.custom_content_block)
+ visible_for = custom_result.get('data').get('contentBlock').get('visibleFor').get('edges')
+ self.assertTrue('template-class' in map(lambda x: x['node']['name'], visible_for))
+ self.assertFalse('class-to-be-synced' in map(lambda x: x['node']['name'], visible_for))
+
+ other_custom_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client,
+ self.other_custom_content_block)
+ visible_for = other_custom_result.get('data').get('contentBlock').get('visibleFor').get('edges')
+ self.assertFalse('template-class' in map(lambda x: x['node']['name'], visible_for))
+ self.assertTrue('class-to-be-synced' in map(lambda x: x['node']['name'], visible_for))
+
+ def test_syncs_correctly(self):
+ self.module.sync_from_school_class(self.template_school_class, self.school_class_to_be_synced)
+
+ # the hidden block is hidden for both now
+ hidden_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, self.hidden_content_block)
+ hidden_for = hidden_result.get('data').get('contentBlock').get('hiddenFor').get('edges')
+ self.assertTrue('template-class' in map(lambda x: x['node']['name'], hidden_for))
+ self.assertTrue('class-to-be-synced' in map(lambda x: x['node']['name'], hidden_for))
+
+ # the other hidden block is hidden for no one now
+ other_hidden_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client,
+ self.other_hidden_content_block)
+ hidden_for = other_hidden_result.get('data').get('contentBlock').get('hiddenFor').get('edges')
+ self.assertEqual(len(hidden_for), 0)
+
+ # the default block is still hidden for no one
+ default_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, self.default_content_block)
+ hidden_for = default_result.get('data').get('contentBlock').get('hiddenFor').get('edges')
+ self.assertEqual(len(hidden_for), 0)
+
+ # the custom block is visible for both
+ custom_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, self.custom_content_block)
+ visible_for = custom_result.get('data').get('contentBlock').get('visibleFor').get('edges')
+ self.assertTrue('template-class' in map(lambda x: x['node']['name'], visible_for))
+ self.assertTrue('class-to-be-synced' in map(lambda x: x['node']['name'], visible_for))
+
+ # the other custom block is visible for no one
+ other_custom_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client,
+ self.other_custom_content_block)
+ visible_for = other_custom_result.get('data').get('contentBlock').get('visibleFor').get('edges')
+ self.assertEqual(len(visible_for), 0)
From 9490ffd443c3098a4a15528f2e9a32d9ce769ca9 Mon Sep 17 00:00:00 2001
From: Ramon Wenger
Date: Fri, 5 Mar 2021 17:21:53 +0100
Subject: [PATCH 05/19] Add module visibility sync mutation
---
.../sync-module-visibility.spec.js | 5 +
.../components/school-class/CurrentClass.vue | 9 --
.../gql/mutations/syncModuleVisibility.gql | 5 +
client/src/mixins/me.js | 22 +++--
client/src/pages/module-base.vue | 8 +-
client/src/pages/moduleVisibility.vue | 58 +++++++++--
client/src/router/index.js | 97 +++++++------------
client/src/router/module.names.js | 3 +
client/src/router/module.routes.js | 35 +++++++
server/api/schema.py | 3 +-
server/books/models/module.py | 6 +-
server/books/schema/mutations/__init__.py | 4 +-
server/books/schema/mutations/module.py | 36 ++++---
server/books/schema/mutations/topic.py | 28 ++++++
.../test_copy_visibility_for_other_class.py | 77 +++++++++------
server/graphql.config.json | 25 -----
16 files changed, 262 insertions(+), 159 deletions(-)
create mode 100644 client/cypress/integration/sync-module-visibility.spec.js
create mode 100644 client/src/graphql/gql/mutations/syncModuleVisibility.gql
create mode 100644 client/src/router/module.names.js
create mode 100644 client/src/router/module.routes.js
create mode 100644 server/books/schema/mutations/topic.py
delete mode 100644 server/graphql.config.json
diff --git a/client/cypress/integration/sync-module-visibility.spec.js b/client/cypress/integration/sync-module-visibility.spec.js
new file mode 100644
index 00000000..6f778a03
--- /dev/null
+++ b/client/cypress/integration/sync-module-visibility.spec.js
@@ -0,0 +1,5 @@
+describe('Survey', () => {
+ it('needs to be implemented', () => {
+ expect(true).to.equal(false);
+ });
+});
diff --git a/client/src/components/school-class/CurrentClass.vue b/client/src/components/school-class/CurrentClass.vue
index 3b8109cd..a385418b 100644
--- a/client/src/components/school-class/CurrentClass.vue
+++ b/client/src/components/school-class/CurrentClass.vue
@@ -9,15 +9,6 @@
export default {
mixins: [me],
-
- computed: {
- currentClassName() {
- let currentClass = this.me.schoolClasses.find(schoolClass => {
- return schoolClass.id === this.me.selectedClass.id;
- });
- return currentClass ? currentClass.name : this.me.schoolClasses.length ? this.me.schoolClasses[0].name : '';
- }
- }
};
diff --git a/client/src/graphql/gql/mutations/syncModuleVisibility.gql b/client/src/graphql/gql/mutations/syncModuleVisibility.gql
new file mode 100644
index 00000000..00cd6040
--- /dev/null
+++ b/client/src/graphql/gql/mutations/syncModuleVisibility.gql
@@ -0,0 +1,5 @@
+mutation SyncModuleVisibility($input: SyncModuleVisibilityInput!) {
+ syncModuleVisibility(input: $input) {
+ success
+ }
+}
diff --git a/client/src/mixins/me.js b/client/src/mixins/me.js
index aaa043d3..e2e592c7 100644
--- a/client/src/mixins/me.js
+++ b/client/src/mixins/me.js
@@ -5,13 +5,13 @@ export default {
return {
me: {
selectedClass: {
- id: ''
+ id: '',
},
permissions: [],
schoolClasses: [],
- isTeacher: false
+ isTeacher: false,
},
- showPopover: false
+ showPopover: false,
};
},
@@ -21,8 +21,8 @@ export default {
return {
name: 'topic',
params: {
- topicSlug: this.me.lastTopic.slug
- }
+ topicSlug: this.me.lastTopic.slug,
+ },
};
}
return '/book/topic/berufliche-grundbildung';
@@ -33,15 +33,21 @@ export default {
canManageContent() {
return this.me.permissions.includes('users.can_manage_school_class_content');
},
+ currentClassName() {
+ let currentClass = this.me.schoolClasses.find(schoolClass => {
+ return schoolClass.id === this.me.selectedClass.id;
+ });
+ return currentClass ? currentClass.name : this.me.schoolClasses.length ? this.me.schoolClasses[0].name : '';
+ },
},
apollo: {
me: {
query: ME_QUERY,
- update(data) {
- return this.$getRidOfEdges(data).me;
+ update({me}) {
+ return this.$getRidOfEdges(me);
},
- fetchPolicy: 'cache-first'
+ fetchPolicy: 'cache-first',
},
},
};
diff --git a/client/src/pages/module-base.vue b/client/src/pages/module-base.vue
index f2b39298..b6c529c6 100644
--- a/client/src/pages/module-base.vue
+++ b/client/src/pages/module-base.vue
@@ -1,6 +1,6 @@
-
+
@@ -11,6 +11,12 @@
export default {
components: {
ModuleNavigation
+ },
+
+ computed: {
+ showNavigation() {
+ return !this.$route.meta.hideNavigation;
+ }
}
};
diff --git a/client/src/pages/moduleVisibility.vue b/client/src/pages/moduleVisibility.vue
index ab61135b..b0474497 100644
--- a/client/src/pages/moduleVisibility.vue
+++ b/client/src/pages/moduleVisibility.vue
@@ -12,16 +12,25 @@
Von
- für INF2019i übernehmen.
+ für {{ currentClassName }} übernehmen.
@@ -29,11 +38,48 @@
diff --git a/client/src/router/index.js b/client/src/router/index.js
index 70a678c0..57e36b6d 100644
--- a/client/src/router/index.js
+++ b/client/src/router/index.js
@@ -1,8 +1,5 @@
import Vue from 'vue';
-// import index from '@/pages/index'
import topic from '@/pages/topic';
-import moduleBase from '@/pages/module-base';
-import module from '@/pages/module';
import rooms from '@/pages/rooms';
import room from '@/pages/room';
import newRoom from '@/pages/newRoom';
@@ -10,7 +7,6 @@ import editRoom from '@/pages/editRoom';
import article from '@/pages/article';
import instrument from '@/pages/instrument';
import instrumentOverview from '@/pages/instrumentOverview';
-import submissions from '@/pages/submissions';
import p404 from '@/pages/p404';
import start from '@/pages/start';
import submission from '@/pages/studentSubmission';
@@ -41,10 +37,11 @@ import onboardingStep1 from '@/pages/onboarding/step1';
import onboardingStep2 from '@/pages/onboarding/step2';
import onboardingStep3 from '@/pages/onboarding/step3';
import settingsPage from '@/pages/moduleSettings';
+
+import moduleRoutes from './module.routes';
import portfolioRoutes from './portfolio.routes';
import store from '@/store/index';
-import moduleVisibility from '@/pages/moduleVisibility';
const ONBOARDING_STEP_1 = 'onboarding-step-1';
const ONBOARDING_STEP_2 = 'onboarding-step-2';
@@ -54,7 +51,7 @@ const routes = [
{
path: '/',
name: 'home',
- component: start
+ component: start,
},
{
path: '/login',
@@ -62,8 +59,8 @@ const routes = [
component: login,
meta: {
layout: 'public',
- public: true
- }
+ public: true,
+ },
},
{
path: '/hello',
@@ -71,8 +68,8 @@ const routes = [
component: hello,
meta: {
layout: 'public',
- public: true
- }
+ public: true,
+ },
},
{
path: '/beta-login',
@@ -80,27 +77,10 @@ const routes = [
component: betaLogin,
meta: {
layout: 'public',
- public: true
- }
- },
- {
- path: '/module/:slug',
- component: moduleBase,
- children: [
- {
- path: '',
- name: 'module',
- component: module,
- meta: {filter: true}
- },
- {
- path: 'submissions/:id',
- name: 'submissions',
- component: submissions,
- meta: {filter: true}
- }
- ]
+ public: true,
+ },
},
+ ...moduleRoutes,
{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},
@@ -110,13 +90,13 @@ const routes = [
name: 'moduleRoom',
component: moduleRoom,
props: true,
- meta: {layout: 'fullScreen'}
+ meta: {layout: 'fullScreen'},
},
{path: '/article/:slug', name: 'article', component: article, meta: {layout: 'simple'}},
{
path: '/instruments/',
name: 'instrument-overview',
- component: instrumentOverview
+ component: instrumentOverview,
},
{path: '/instrument/:slug', name: 'instrument', component: instrument, meta: {layout: 'simple'}},
{path: '/submission/:id', name: 'submission', component: submission, meta: {layout: 'simple'}},
@@ -134,11 +114,11 @@ const routes = [
path: 'old-classes',
name: 'old-classes',
component: oldClasses,
- meta: {isProfile: true}
+ meta: {isProfile: true},
},
{path: 'create-class', name: 'create-class', component: createClass, meta: {layout: 'simple'}},
{path: 'show-code', name: 'show-code', component: showCode, meta: {layout: 'simple'}},
- ]
+ ],
},
{path: 'join-class', name: 'join-class', component: joinClass, meta: {layout: 'public'}},
{
@@ -146,7 +126,7 @@ const routes = [
component: surveyPage,
name: 'survey',
props: true,
- meta: {layout: 'simple'}
+ meta: {layout: 'simple'},
},
{
path: '/register',
@@ -155,7 +135,7 @@ const routes = [
meta: {
public: true,
layout: 'public',
- }
+ },
},
{
path: '/check-email',
@@ -163,8 +143,8 @@ const routes = [
name: 'checkEmail',
meta: {
public: true,
- layout: 'public'
- }
+ layout: 'public',
+ },
},
{
path: '/verify-email',
@@ -172,16 +152,16 @@ const routes = [
name: 'emailVerification',
meta: {
public: true,
- layout: 'public'
- }
+ layout: 'public',
+ },
},
{
path: '/license-activation',
component: licenseActivation,
name: 'licenseActivation',
meta: {
- layout: 'public'
- }
+ layout: 'public',
+ },
},
{
path: '/forgot-password',
@@ -189,13 +169,13 @@ const routes = [
name: 'forgotPassword',
meta: {
layout: 'public',
- public: true
- }
+ public: true,
+ },
},
{
path: '/news',
component: news,
- name: 'news'
+ name: 'news',
},
{
path: '/onboarding',
@@ -207,7 +187,7 @@ const routes = [
name: 'onboarding-start',
meta: {
layout: 'blank',
- next: ONBOARDING_STEP_1
+ next: ONBOARDING_STEP_1,
},
},
{
@@ -217,7 +197,7 @@ const routes = [
meta: {
layout: 'blank',
next: ONBOARDING_STEP_2,
- illustration: 'contents'
+ illustration: 'contents',
},
},
{
@@ -227,7 +207,7 @@ const routes = [
meta: {
layout: 'blank',
next: ONBOARDING_STEP_3,
- illustration: 'rooms'
+ illustration: 'rooms',
},
},
{
@@ -237,30 +217,23 @@ const routes = [
meta: {
layout: 'blank',
next: 'home',
- illustration: 'portfolio'
+ illustration: 'portfolio',
},
},
- ]
+ ],
},
{
path: '/settings',
- component: settingsPage
- },
- {
- path: '/visibility',
- component: moduleVisibility,
- meta: {
- layout: 'simple'
- }
+ component: settingsPage,
},
{path: '/styleguide', component: styleGuidePage},
{
path: '*',
component: p404,
meta: {
- layout: 'blank'
- }
- }
+ layout: 'blank',
+ },
+ },
];
Vue.use(Router);
@@ -273,7 +246,7 @@ const router = new Router({
return savedPosition;
}
return {x: 0, y: 0};
- }
+ },
});
router.afterEach((to, from) => {
diff --git a/client/src/router/module.names.js b/client/src/router/module.names.js
new file mode 100644
index 00000000..ec35f516
--- /dev/null
+++ b/client/src/router/module.names.js
@@ -0,0 +1,3 @@
+export const SUBMISSIONS_PAGE = 'submissions';
+export const MODULE_PAGE = 'module';
+export const VISIBILITY_PAGE = 'visibility';
diff --git a/client/src/router/module.routes.js b/client/src/router/module.routes.js
new file mode 100644
index 00000000..4eef3e0b
--- /dev/null
+++ b/client/src/router/module.routes.js
@@ -0,0 +1,35 @@
+import moduleBase from '@/pages/module-base';
+import module from '@/pages/module';
+import submissions from '@/pages/submissions';
+import moduleVisibility from '@/pages/moduleVisibility';
+import {MODULE_PAGE, SUBMISSIONS_PAGE, VISIBILITY_PAGE} from '@/router/module.names';
+
+export default [
+ {
+ path: '/module/:slug',
+ component: moduleBase,
+ children: [
+ {
+ path: '',
+ name: MODULE_PAGE,
+ component: module,
+ meta: {filter: true},
+ },
+ {
+ path: 'submissions/:id',
+ name: SUBMISSIONS_PAGE,
+ component: submissions,
+ meta: {filter: true},
+ },
+ {
+ path: 'visibility',
+ name: VISIBILITY_PAGE,
+ component: moduleVisibility,
+ meta: {
+ layout: 'simple',
+ hideNavigation: true
+ },
+ },
+ ],
+}
+];
diff --git a/server/api/schema.py b/server/api/schema.py
index f5e87a87..7421800f 100644
--- a/server/api/schema.py
+++ b/server/api/schema.py
@@ -24,7 +24,6 @@ from rooms.mutations import RoomMutations
from rooms.schema import RoomsQuery, ModuleRoomsQuery
from users.schema import AllUsersQuery, UsersQuery
from users.mutations import ProfileMutations
-from registration.mutations_public import RegistrationMutations
class CustomQuery(UsersQuery, AllUsersQuery, ModuleRoomsQuery, RoomsQuery, ObjectivesQuery, BookQuery, AssignmentsQuery,
@@ -36,7 +35,7 @@ class CustomQuery(UsersQuery, AllUsersQuery, ModuleRoomsQuery, RoomsQuery, Objec
class CustomMutation(BookMutations, RoomMutations, AssignmentMutations, ObjectiveMutations, CoreMutations, PortfolioMutations,
- ProfileMutations, SurveyMutations, NoteMutations, RegistrationMutations, SpellCheckMutations,
+ ProfileMutations, SurveyMutations, NoteMutations, SpellCheckMutations,
CouponMutations, graphene.ObjectType):
if settings.DEBUG:
debug = graphene.Field(DjangoDebug, name='_debug')
diff --git a/server/books/models/module.py b/server/books/models/module.py
index 2785d3a1..093a082b 100644
--- a/server/books/models/module.py
+++ b/server/books/models/module.py
@@ -58,7 +58,7 @@ class Module(StrictHierarchyPage):
def get_child_ids(self):
return self.get_children().values_list('id', flat=True)
- def sync_from_school_class(self, school_class_pattern, school_class_to_sync):
+ def sync_from_school_class(self, school_class_template, school_class_to_sync):
# import here so we don't get a circular import error
from books.models import Chapter, ContentBlock
@@ -77,12 +77,12 @@ class Module(StrictHierarchyPage):
content_block.visible_for.remove(school_class_to_sync)
# get all content blocks with `hidden for` for class `school_class_pattern`
- for content_block in school_class_pattern.hidden_content_blocks.intersection(content_block_query):
+ for content_block in school_class_template.hidden_content_blocks.intersection(content_block_query):
# add `school_class_to_sync` to these blocks' `hidden for`
content_block.hidden_for.add(school_class_to_sync)
# get all content blocks with `visible for` for class `school_class_pattern`
- for content_block in school_class_pattern.visible_content_blocks.intersection(content_block_query):
+ for content_block in school_class_template.visible_content_blocks.intersection(content_block_query):
# add `school_class_to_sync` to these blocks' `visible for`
content_block.visible_for.add(school_class_to_sync)
diff --git a/server/books/schema/mutations/__init__.py b/server/books/schema/mutations/__init__.py
index fc85ed51..999faf9b 100644
--- a/server/books/schema/mutations/__init__.py
+++ b/server/books/schema/mutations/__init__.py
@@ -1,6 +1,7 @@
from books.schema.mutations.chapter import UpdateChapterVisibility
from books.schema.mutations.contentblock import MutateContentBlock, AddContentBlock, DeleteContentBlock
-from books.schema.mutations.module import UpdateSolutionVisibility, UpdateLastModule, UpdateLastTopic
+from books.schema.mutations.module import UpdateSolutionVisibility, UpdateLastModule, SyncModuleVisibility
+from books.schema.mutations.topic import UpdateLastTopic
class BookMutations(object):
@@ -11,3 +12,4 @@ class BookMutations(object):
update_last_module = UpdateLastModule.Field()
update_last_topic = UpdateLastTopic.Field()
update_chapter_visibility = UpdateChapterVisibility.Field()
+ sync_module_visibility = SyncModuleVisibility.Field()
diff --git a/server/books/schema/mutations/module.py b/server/books/schema/mutations/module.py
index 77b00244..d50e8b20 100644
--- a/server/books/schema/mutations/module.py
+++ b/server/books/schema/mutations/module.py
@@ -3,9 +3,10 @@ from datetime import datetime
import graphene
from graphene import relay
-from api.utils import get_errors, get_object
-from books.models import Module, Topic, RecentModule
-from books.schema.queries import ModuleNode, TopicNode
+from api.utils import get_object
+from books.models import Module, RecentModule
+from books.schema.queries import ModuleNode
+from users.models import SchoolClass
class UpdateSolutionVisibility(relay.ClientIDMutation):
@@ -75,23 +76,30 @@ class UpdateLastModule(relay.ClientIDMutation):
return cls(last_module=last_module.module)
-class UpdateLastTopic(relay.ClientIDMutation):
+class SyncModuleVisibility(relay.ClientIDMutation):
class Input:
- # todo: use slug here too
- id = graphene.ID()
+ module = graphene.String(required=True)
+ template_school_class = graphene.ID(required=True)
+ school_class = graphene.ID(required=True)
- topic = graphene.Field(TopicNode)
+ success = graphene.Boolean()
@classmethod
def mutate_and_get_payload(cls, root, info, **args):
user = info.context.user
- id = args.get('id')
+ if not user.is_teacher():
+ raise Exception('Permission denied')
- topic = get_object(Topic, id)
- if not topic:
- raise Topic.DoesNotExist
+ module_slug = args.get('module')
+ template_id = args.get('template_school_class')
+ school_class_id = args.get('school_class')
- user.last_topic = topic
- user.save()
+ module = Module.objects.get(slug=module_slug)
+ template = get_object(SchoolClass, template_id)
+ school_class = get_object(SchoolClass, school_class_id)
+ if not template.is_user_in_schoolclass(user) or not school_class.is_user_in_schoolclass(user):
+ raise Exception('Permission denied')
- return cls(topic=topic)
+ module.sync_from_school_class(template, school_class)
+
+ return cls(success=True)
diff --git a/server/books/schema/mutations/topic.py b/server/books/schema/mutations/topic.py
new file mode 100644
index 00000000..fafadae1
--- /dev/null
+++ b/server/books/schema/mutations/topic.py
@@ -0,0 +1,28 @@
+import graphene
+from graphene import relay
+
+from api.utils import get_object
+from books.models import Topic
+from books.schema.queries import TopicNode
+
+
+class UpdateLastTopic(relay.ClientIDMutation):
+ class Input:
+ # todo: use slug here too
+ id = graphene.ID()
+
+ topic = graphene.Field(TopicNode)
+
+ @classmethod
+ def mutate_and_get_payload(cls, root, info, **args):
+ user = info.context.user
+ id = args.get('id')
+
+ topic = get_object(Topic, id)
+ if not topic:
+ raise Topic.DoesNotExist
+
+ user.last_topic = topic
+ user.save()
+
+ return cls(topic=topic)
diff --git a/server/books/tests/test_copy_visibility_for_other_class.py b/server/books/tests/test_copy_visibility_for_other_class.py
index 60cff107..6fd27a22 100644
--- a/server/books/tests/test_copy_visibility_for_other_class.py
+++ b/server/books/tests/test_copy_visibility_for_other_class.py
@@ -34,6 +34,14 @@ CONTENT_BLOCK_QUERY = """
}
"""
+SYNC_MUTATION = """
+mutation SyncMutationVisibility($input: SyncModuleVisibilityInput!) {
+ syncModuleVisibility(input: $input) {
+ success
+ }
+}
+"""
+
class CopyVisibilityForClassesTestCase(TestCase):
"""
@@ -52,7 +60,7 @@ class CopyVisibilityForClassesTestCase(TestCase):
"""
def setUp(self):
- module = ModuleFactory()
+ module = ModuleFactory(slug='some-module')
chapter = Chapter(title='Some Chapter')
module.add_child(instance=chapter)
create_users()
@@ -110,6 +118,36 @@ class CopyVisibilityForClassesTestCase(TestCase):
})
return result
+ def _test_in_sync(self):
+ # the hidden block is hidden for both now
+ hidden_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, self.hidden_content_block)
+ hidden_for = hidden_result.get('data').get('contentBlock').get('hiddenFor').get('edges')
+ self.assertTrue('template-class' in map(lambda x: x['node']['name'], hidden_for))
+ self.assertTrue('class-to-be-synced' in map(lambda x: x['node']['name'], hidden_for))
+
+ # the other hidden block is hidden for no one now
+ other_hidden_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client,
+ self.other_hidden_content_block)
+ hidden_for = other_hidden_result.get('data').get('contentBlock').get('hiddenFor').get('edges')
+ self.assertEqual(len(hidden_for), 0)
+
+ # the default block is still hidden for no one
+ default_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, self.default_content_block)
+ hidden_for = default_result.get('data').get('contentBlock').get('hiddenFor').get('edges')
+ self.assertEqual(len(hidden_for), 0)
+
+ # the custom block is visible for both
+ custom_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, self.custom_content_block)
+ visible_for = custom_result.get('data').get('contentBlock').get('visibleFor').get('edges')
+ self.assertTrue('template-class' in map(lambda x: x['node']['name'], visible_for))
+ self.assertTrue('class-to-be-synced' in map(lambda x: x['node']['name'], visible_for))
+
+ # the other custom block is visible for no one
+ other_custom_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client,
+ self.other_custom_content_block)
+ visible_for = other_custom_result.get('data').get('contentBlock').get('visibleFor').get('edges')
+ self.assertEqual(len(visible_for), 0)
+
def test_hidden_for_and_visible_for_set_correctly(self):
self.assertEqual(ContentBlock.objects.count(), 5)
@@ -142,31 +180,14 @@ class CopyVisibilityForClassesTestCase(TestCase):
def test_syncs_correctly(self):
self.module.sync_from_school_class(self.template_school_class, self.school_class_to_be_synced)
- # the hidden block is hidden for both now
- hidden_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, self.hidden_content_block)
- hidden_for = hidden_result.get('data').get('contentBlock').get('hiddenFor').get('edges')
- self.assertTrue('template-class' in map(lambda x: x['node']['name'], hidden_for))
- self.assertTrue('class-to-be-synced' in map(lambda x: x['node']['name'], hidden_for))
+ self._test_in_sync()
- # the other hidden block is hidden for no one now
- other_hidden_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client,
- self.other_hidden_content_block)
- hidden_for = other_hidden_result.get('data').get('contentBlock').get('hiddenFor').get('edges')
- self.assertEqual(len(hidden_for), 0)
-
- # the default block is still hidden for no one
- default_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, self.default_content_block)
- hidden_for = default_result.get('data').get('contentBlock').get('hiddenFor').get('edges')
- self.assertEqual(len(hidden_for), 0)
-
- # the custom block is visible for both
- custom_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client, self.custom_content_block)
- visible_for = custom_result.get('data').get('contentBlock').get('visibleFor').get('edges')
- self.assertTrue('template-class' in map(lambda x: x['node']['name'], visible_for))
- self.assertTrue('class-to-be-synced' in map(lambda x: x['node']['name'], visible_for))
-
- # the other custom block is visible for no one
- other_custom_result = self._get_result(CONTENT_BLOCK_QUERY, self.teacher_client,
- self.other_custom_content_block)
- visible_for = other_custom_result.get('data').get('contentBlock').get('visibleFor').get('edges')
- self.assertEqual(len(visible_for), 0)
+ def test_mutation(self):
+ self.teacher_client.execute(SYNC_MUTATION, variables={
+ 'input': {
+ 'module': self.module.slug,
+ 'templateSchoolClass': to_global_id('SchoolClassNode', self.template_school_class.pk),
+ 'schoolClass': to_global_id('SchoolClassNode', self.school_class_to_be_synced.pk)
+ }
+ })
+ self._test_in_sync()
diff --git a/server/graphql.config.json b/server/graphql.config.json
deleted file mode 100644
index 8208188c..00000000
--- a/server/graphql.config.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "schema": {
- "request": {
- "url": "http://localhost:8000/graphql",
- "method": "POST",
- "postIntrospectionQuery": true,
- "options": {
- "headers": {
- "user-agent": "JS GraphQL"
- }
- }
- }
- },
- "endpoints": [
- {
- "name": "Default (http://localhost:8000/graphql",
- "url": "http://localhost:8000/graphql",
- "options": {
- "headers": {
- "user-agent": "JS GraphQL"
- }
- }
- }
- ]
-}
\ No newline at end of file
From 50dbc4e4fcd928f369b25c533eeb2adaeeac80d6 Mon Sep 17 00:00:00 2001
From: Ramon Wenger
Date: Tue, 9 Mar 2021 15:36:51 +0100
Subject: [PATCH 06/19] Update readme
---
README.md | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/README.md b/README.md
index 174efafa..0f7a0002 100644
--- a/README.md
+++ b/README.md
@@ -221,3 +221,12 @@ To generate a new schema, use the management command
```
python manage.py export_schema_for_cypress
```
+
+
+## GraphQL
+
+### Generate GraphQL SDL Document
+
+```
+python manage.py export_schema_graphql
+```
From 2e424198f07b9e170c5a59684023ca99709a0362 Mon Sep 17 00:00:00 2001
From: Ramon Wenger
Date: Wed, 10 Mar 2021 09:19:13 +0100
Subject: [PATCH 07/19] Add settings button
---
.../integration/{ => rooms}/room-page.spec.js | 0
.../{ => rooms}/rooms-page.spec.js | 0
.../{ => users}/beta-login.spec.js | 0
.../integration/{ => users}/coupon.spec.js | 2 +-
.../integration/{ => users}/login.spec.js | 2 +-
.../components/modules/ModuleNavigation.vue | 8 +++
client/src/pages/{ => module}/module-base.vue | 0
client/src/pages/{ => module}/module.vue | 0
client/src/pages/{ => module}/moduleRoom.vue | 0
.../src/pages/{ => module}/moduleSettings.vue | 0
.../pages/{ => module}/moduleVisibility.vue | 0
client/src/router/index.js | 7 +-
client/src/router/module.names.js | 1 +
client/src/router/module.routes.js | 64 ++++++++++---------
14 files changed, 47 insertions(+), 37 deletions(-)
rename client/cypress/integration/{ => rooms}/room-page.spec.js (100%)
rename client/cypress/integration/{ => rooms}/rooms-page.spec.js (100%)
rename client/cypress/integration/{ => users}/beta-login.spec.js (100%)
rename client/cypress/integration/{ => users}/coupon.spec.js (97%)
rename client/cypress/integration/{ => users}/login.spec.js (97%)
rename client/src/pages/{ => module}/module-base.vue (100%)
rename client/src/pages/{ => module}/module.vue (100%)
rename client/src/pages/{ => module}/moduleRoom.vue (100%)
rename client/src/pages/{ => module}/moduleSettings.vue (100%)
rename client/src/pages/{ => module}/moduleVisibility.vue (100%)
diff --git a/client/cypress/integration/room-page.spec.js b/client/cypress/integration/rooms/room-page.spec.js
similarity index 100%
rename from client/cypress/integration/room-page.spec.js
rename to client/cypress/integration/rooms/room-page.spec.js
diff --git a/client/cypress/integration/rooms-page.spec.js b/client/cypress/integration/rooms/rooms-page.spec.js
similarity index 100%
rename from client/cypress/integration/rooms-page.spec.js
rename to client/cypress/integration/rooms/rooms-page.spec.js
diff --git a/client/cypress/integration/beta-login.spec.js b/client/cypress/integration/users/beta-login.spec.js
similarity index 100%
rename from client/cypress/integration/beta-login.spec.js
rename to client/cypress/integration/users/beta-login.spec.js
diff --git a/client/cypress/integration/coupon.spec.js b/client/cypress/integration/users/coupon.spec.js
similarity index 97%
rename from client/cypress/integration/coupon.spec.js
rename to client/cypress/integration/users/coupon.spec.js
index 595f503b..0c80bd5d 100644
--- a/client/cypress/integration/coupon.spec.js
+++ b/client/cypress/integration/users/coupon.spec.js
@@ -1,6 +1,6 @@
import { GraphQLError } from 'graphql';
-const schema = require('../fixtures/schema.json');
+const schema = require('../../fixtures/schema.json');
describe('Email Verifcation', () => {
beforeEach(() => {
diff --git a/client/cypress/integration/login.spec.js b/client/cypress/integration/users/login.spec.js
similarity index 97%
rename from client/cypress/integration/login.spec.js
rename to client/cypress/integration/users/login.spec.js
index 3cb5d56b..f1defc6c 100644
--- a/client/cypress/integration/login.spec.js
+++ b/client/cypress/integration/users/login.spec.js
@@ -1,4 +1,4 @@
-const schema = require('../fixtures/schema_public.json');
+const schema = require('../../fixtures/schema_public.json');
const isEmailAvailableUrl = '**/rest/deutsch/V1/customers/isEmailAvailable';
const checkPasswordUrl = '**/rest/deutsch/V1/integration/customer/token';
diff --git a/client/src/components/modules/ModuleNavigation.vue b/client/src/components/modules/ModuleNavigation.vue
index e033a2e8..aedce220 100644
--- a/client/src/components/modules/ModuleNavigation.vue
+++ b/client/src/components/modules/ModuleNavigation.vue
@@ -45,6 +45,10 @@
class="module-navigation__toggle-menu"
v-if="canManageContent"
>
+ Einstellungen
diff --git a/client/src/pages/module-base.vue b/client/src/pages/module/module-base.vue
similarity index 100%
rename from client/src/pages/module-base.vue
rename to client/src/pages/module/module-base.vue
diff --git a/client/src/pages/module.vue b/client/src/pages/module/module.vue
similarity index 100%
rename from client/src/pages/module.vue
rename to client/src/pages/module/module.vue
diff --git a/client/src/pages/moduleRoom.vue b/client/src/pages/module/moduleRoom.vue
similarity index 100%
rename from client/src/pages/moduleRoom.vue
rename to client/src/pages/module/moduleRoom.vue
diff --git a/client/src/pages/moduleSettings.vue b/client/src/pages/module/moduleSettings.vue
similarity index 100%
rename from client/src/pages/moduleSettings.vue
rename to client/src/pages/module/moduleSettings.vue
diff --git a/client/src/pages/moduleVisibility.vue b/client/src/pages/module/moduleVisibility.vue
similarity index 100%
rename from client/src/pages/moduleVisibility.vue
rename to client/src/pages/module/moduleVisibility.vue
diff --git a/client/src/router/index.js b/client/src/router/index.js
index 57e36b6d..b1c58d2a 100644
--- a/client/src/router/index.js
+++ b/client/src/router/index.js
@@ -17,7 +17,7 @@ import activity from '@/pages/activity';
import Router from 'vue-router';
import surveyPage from '@/pages/survey';
import styleGuidePage from '@/pages/styleguide';
-import moduleRoom from '@/pages/moduleRoom';
+import moduleRoom from '@/pages/module/moduleRoom';
import login from '@/pages/login';
import betaLogin from '@/pages/beta-login';
import hello from '@/pages/hello';
@@ -36,7 +36,6 @@ import onboardingStart from '@/pages/onboarding/start';
import onboardingStep1 from '@/pages/onboarding/step1';
import onboardingStep2 from '@/pages/onboarding/step2';
import onboardingStep3 from '@/pages/onboarding/step3';
-import settingsPage from '@/pages/moduleSettings';
import moduleRoutes from './module.routes';
import portfolioRoutes from './portfolio.routes';
@@ -222,10 +221,6 @@ const routes = [
},
],
},
- {
- path: '/settings',
- component: settingsPage,
- },
{path: '/styleguide', component: styleGuidePage},
{
path: '*',
diff --git a/client/src/router/module.names.js b/client/src/router/module.names.js
index ec35f516..793e0b65 100644
--- a/client/src/router/module.names.js
+++ b/client/src/router/module.names.js
@@ -1,3 +1,4 @@
export const SUBMISSIONS_PAGE = 'submissions';
export const MODULE_PAGE = 'module';
+export const MODULE_SETTINGS_PAGE = 'module-settings';
export const VISIBILITY_PAGE = 'visibility';
diff --git a/client/src/router/module.routes.js b/client/src/router/module.routes.js
index 4eef3e0b..ba1d79f1 100644
--- a/client/src/router/module.routes.js
+++ b/client/src/router/module.routes.js
@@ -1,35 +1,41 @@
-import moduleBase from '@/pages/module-base';
-import module from '@/pages/module';
+import moduleBase from '@/pages/module/module-base';
+import module from '@/pages/module/module';
import submissions from '@/pages/submissions';
-import moduleVisibility from '@/pages/moduleVisibility';
-import {MODULE_PAGE, SUBMISSIONS_PAGE, VISIBILITY_PAGE} from '@/router/module.names';
+import moduleVisibility from '@/pages/module/moduleVisibility';
+import {MODULE_PAGE, MODULE_SETTINGS_PAGE, SUBMISSIONS_PAGE, VISIBILITY_PAGE} from '@/router/module.names';
+import settingsPage from '@/pages/module/moduleSettings';
export default [
- {
- path: '/module/:slug',
- component: moduleBase,
- children: [
{
- path: '',
- name: MODULE_PAGE,
- component: module,
- meta: {filter: true},
+ path: '/module/:slug',
+ component: moduleBase,
+ children: [
+ {
+ path: '',
+ name: MODULE_PAGE,
+ component: module,
+ meta: {filter: true},
+ },
+ {
+ path: 'submissions/:id',
+ name: SUBMISSIONS_PAGE,
+ component: submissions,
+ meta: {filter: true},
+ },
+ {
+ path: 'settings',
+ name: MODULE_SETTINGS_PAGE,
+ component: settingsPage,
+ },
+ {
+ path: 'visibility',
+ name: VISIBILITY_PAGE,
+ component: moduleVisibility,
+ meta: {
+ layout: 'simple',
+ hideNavigation: true,
+ },
+ },
+ ],
},
- {
- path: 'submissions/:id',
- name: SUBMISSIONS_PAGE,
- component: submissions,
- meta: {filter: true},
- },
- {
- path: 'visibility',
- name: VISIBILITY_PAGE,
- component: moduleVisibility,
- meta: {
- layout: 'simple',
- hideNavigation: true
- },
- },
- ],
-}
];
From adf662533d851ef2f93d9839c8a16c286f41151d Mon Sep 17 00:00:00 2001
From: Ramon Wenger
Date: Wed, 10 Mar 2021 09:19:45 +0100
Subject: [PATCH 08/19] Add more navigation buttons
---
client/src/pages/module/moduleSettings.vue | 7 +++++++
client/src/pages/module/moduleVisibility.vue | 2 ++
2 files changed, 9 insertions(+)
diff --git a/client/src/pages/module/moduleSettings.vue b/client/src/pages/module/moduleSettings.vue
index 8ed8a4dd..46928609 100644
--- a/client/src/pages/module/moduleSettings.vue
+++ b/client/src/pages/module/moduleSettings.vue
@@ -12,6 +12,13 @@
können Sie diese Anpassungen hier
übernehmen
+
diff --git a/client/src/pages/module/moduleVisibility.vue b/client/src/pages/module/moduleVisibility.vue
index b0474497..302748fa 100644
--- a/client/src/pages/module/moduleVisibility.vue
+++ b/client/src/pages/module/moduleVisibility.vue
@@ -13,6 +13,7 @@
Von