diff --git a/client/src/components/ContentBlock.vue b/client/src/components/ContentBlock.vue index 5c541820..9da7b3dc 100644 --- a/client/src/components/ContentBlock.vue +++ b/client/src/components/ContentBlock.vue @@ -3,19 +3,20 @@
- + - +

{{instrumentLabel}}

-

{{contentBlock.title}}

+

{{contentBlock.title}}

- @@ -23,7 +24,7 @@
- + @@ -41,6 +42,7 @@ import InfogramBlock from '@/components/content-blocks/InfogramBlock'; import GeniallyBlock from '@/components/content-blocks/GeniallyBlock'; import SubtitleBlock from '@/components/content-blocks/SubtitleBlock'; + import ContentListBlock from '@/components/content-blocks/ContentListBlock'; import Assignment from '@/components/content-blocks/assignment/Assignment'; import Solution from '@/components/content-blocks/Solution'; import AddContentBlockButton from '@/components/AddContentBlockButton'; @@ -59,6 +61,7 @@ export default { props: ['contentBlock', 'parent'], + name: 'content-block', components: { 'text_block': TextBlock, @@ -72,6 +75,7 @@ 'infogram_block': InfogramBlock, 'genially_block': GeniallyBlock, 'subtitle': SubtitleBlock, + 'content_list': ContentListBlock, Solution, Assignment, Task, @@ -93,6 +97,59 @@ } return `Instrument - ${instruments[contentType]}` + }, + canEditContentBlock() { + return this.contentBlock.mine && !this.contentBlock.indent; + }, + contentBlocksWithContentLists() { + /* + collects all conent_list_items in content_lists: + { + text_block, + content_list_item: [contents...], + content_list_item: [contents...], + text_block + } becomes + { + text_block, + content_list: [content_list_item: [contents...], content_list_item: [contents...]], + text_block + } + if there's only a single content_list_item it should not be displayed as list like so + { + text_block, + content_list_item: [text_block, image_block], + } becomes + { + text_block, + text_block, + image_block + } + */ + let contentList = []; + let newContent = this.contentBlock.contents.reduce((newContents, content, index) => { + // collect content_list_items + if (content.type === 'content_list_item') { + contentList = [...contentList, content] + if (index === this.contentBlock.contents.length - 1) { // content is last element of contents array + return [...newContents, ...this.createContentListOrBlocks(contentList)]; + } + return newContents + } else { + // handle all other items and reset current content_list if necessary + if (contentList.length !== 0) { + newContents = [...newContents, ...this.createContentListOrBlocks(contentList), content]; + contentList = []; + return newContents; + } else { + return [...newContents, content] + } + } + }, []) + + return Object.assign({}, this.contentBlock, { + contents: newContent + }); } }, @@ -125,9 +182,20 @@ } } }); - } - }, + }, + createContentListOrBlocks(contentList) { + // if list contains only one item, return blocks + if (contentList.length === 1) { + return contentList[0].value; + } + return [{ + type: 'content_list', + contents: contentList, + id: contentList[0].id + }]; + }, + }, data() { return { showVisibility: false diff --git a/client/src/components/content-blocks/ContentListBlock.vue b/client/src/components/content-blocks/ContentListBlock.vue new file mode 100644 index 00000000..663e02dd --- /dev/null +++ b/client/src/components/content-blocks/ContentListBlock.vue @@ -0,0 +1,66 @@ + + + + + diff --git a/client/src/components/content-blocks/SubtitleBlock.vue b/client/src/components/content-blocks/SubtitleBlock.vue index b3ebd13b..c642fc51 100644 --- a/client/src/components/content-blocks/SubtitleBlock.vue +++ b/client/src/components/content-blocks/SubtitleBlock.vue @@ -11,6 +11,7 @@ diff --git a/server/api/graphene_wagtail.py b/server/api/graphene_wagtail.py index 1a870941..ff87dc5c 100644 --- a/server/api/graphene_wagtail.py +++ b/server/api/graphene_wagtail.py @@ -18,59 +18,67 @@ class GenericStreamFieldType(Scalar): @staticmethod def serialize(stream_value): stream_data = stream_value.stream_data - - for d in stream_data: - if isinstance(d, dict): - _type = d['type'] - if _type == 'image_block': - _value = d['value'] - value = { - # 'value': _value, - # 'id': d['id'], - 'path': Image.objects.get(id=_value).file.url - } - d['value'] = value - if _type == 'assignment': - _value = d['value'] - assignment_id = _value['assignment_id'] - try: - assignment = Assignment.objects.get(pk=assignment_id) - value = { - 'title': assignment.title, - 'assignment': assignment.assignment, - 'id': to_global_id('AssignmentNode', assignment.pk) - } - d['value'] = value - except Assignment.DoesNotExist: - logger.error('Assignment {} does not exist'.format(assignment_id)) - if _type == 'basic_knowledge' or _type == 'instrument': - _value = d['value'] - basic_knowledge = BasicKnowledge.objects.get(pk=_value['basic_knowledge']) - _value.update({ - 'slug': basic_knowledge.slug - }) - d['value'] = _value - - # value = dict(d['value']) - # if 'document' in value: - # value['document'] = Document.objects.get(id=value['document']).file.url - # if 'image' in value: - # value['image'] = Image.objects.get(id=value['image']).file.url - - # else: - # _type = d[0] - # value = dict(d[1]) - # if 'document' in value: - # value['document'] = value['document'].file.url - # if 'image' in value: - # value['image'] = value['image'].file.url - - return stream_data + return augment_fields(stream_data) # by_api = stream_value.stream_block.get_api_representation(stream_value) # return by_api +def augment_fields(stream_data): + for data in stream_data: + if isinstance(data, dict): + _type = data['type'] + if _type == 'image_block': + _value = data['value'] + value = { + # 'value': _value, + # 'id': d['id'], + 'path': Image.objects.get(id=_value).file.url + } + data['value'] = value + if _type == 'assignment': + _value = data['value'] + assignment_id = _value['assignment_id'] + try: + assignment = Assignment.objects.get(pk=assignment_id) + value = { + 'title': assignment.title, + 'assignment': assignment.assignment, + 'id': to_global_id('AssignmentNode', assignment.pk) + } + data['value'] = value + except Assignment.DoesNotExist: + logger.error('Assignment {} does not exist'.format(assignment_id)) + if _type == 'basic_knowledge' or _type == 'instrument': + _value = data['value'] + basic_knowledge = BasicKnowledge.objects.get(pk=_value['basic_knowledge']) + _value.update({ + 'slug': basic_knowledge.slug + }) + data['value'] = _value + + # value = dict(d['value']) + # if 'document' in value: + # value['document'] = Document.objects.get(id=value['document']).file.url + # if 'image' in value: + # value['image'] = Image.objects.get(id=value['image']).file.url + + # else: + # _type = d[0] + # value = dict(d[1]) + # if 'document' in value: + # value['document'] = value['document'].file.url + # if 'image' in value: + # value['image'] = value['image'].file.url + + if _type == 'content_list_item': + item_data = data['value'] + data['value'] = augment_fields(item_data) + + return stream_data + + @convert_django_field.register(StreamField) def convert_stream_field(field, registry=None): return GenericStreamFieldType(description=field.help_text, required=not field.null) + diff --git a/server/books/models/contentblock.py b/server/books/models/contentblock.py index 9044abd7..e59427e3 100644 --- a/server/books/models/contentblock.py +++ b/server/books/models/contentblock.py @@ -2,6 +2,7 @@ import logging from django.db import models from wagtail.admin.edit_handlers import FieldPanel, TabbedInterface, ObjectList, StreamFieldPanel +from wagtail.core.blocks import StreamBlock from wagtail.core.fields import StreamField from wagtail.images.blocks import ImageChooserBlock @@ -34,7 +35,7 @@ class ContentBlock(StrictHierarchyPage): visible_for = models.ManyToManyField(SchoolClass, related_name='visible_content_blocks') user_created = models.BooleanField(default=False) - contents = StreamField([ + content_blocks = [ ('text_block', TextBlock()), ('basic_knowledge', BasicKnowledgeBlock()), ('assignment', AssignmentBlock()), @@ -46,8 +47,11 @@ class ContentBlock(StrictHierarchyPage): ('document_block', DocumentBlock()), ('infogram_block', InfogramBlock()), ('genially_block', GeniallyBlock()), - ('subtitle', SubtitleBlock()), - ], null=True, blank=True) + ('subtitle', SubtitleBlock()) + ] + + content_list_item = StreamBlock(content_blocks) + contents = StreamField(content_blocks + [('content_list_item', content_list_item)], null=True, blank=True) type = models.CharField( max_length=100,