From a629f6a5e6eb751ef5cab3fb7da374fa995c4abc Mon Sep 17 00:00:00 2001 From: Ramon Wenger Date: Thu, 9 Feb 2023 17:46:57 +0100 Subject: [PATCH] Add copy link button to chapters, also generate redirect link --- client/src/components/Chapter.vue | 14 +++++- client/src/components/ContentBlock.vue | 3 -- client/src/components/CopyLink.vue | 12 ++--- client/src/pages/contentBlockLocator.vue | 54 ++++++++++++-------- server/books/models/chapter.py | 64 +++++++++++++++--------- server/books/schema/nodes/chapter.py | 43 +++++++++++----- 6 files changed, 119 insertions(+), 71 deletions(-) diff --git a/client/src/components/Chapter.vue b/client/src/components/Chapter.vue index e0bd9006..16d6bba8 100644 --- a/client/src/components/Chapter.vue +++ b/client/src/components/Chapter.vue @@ -9,8 +9,12 @@ class="hideable-element" v-if="!titleHidden" > -

+

{{ chapter.title }} +

@@ -66,6 +70,7 @@ import ContentBlock from '@/components/ContentBlock'; import AddContentButton from '@/components/AddContentButton'; import BookmarkActions from '@/components/notes/BookmarkActions'; import VisibilityAction from '@/components/visibility/VisibilityAction'; +import CopyLink from '@/components/CopyLink.vue'; import { hidden } from '@/helpers/visibility'; import { CHAPTER_DESCRIPTION_TYPE, CHAPTER_TITLE_TYPE, CONTENT_TYPE } from '@/consts/types'; @@ -98,6 +103,7 @@ export default { VisibilityAction, ContentBlock, AddContentButton, + CopyLink, }, computed: { @@ -231,6 +237,12 @@ export default { position: relative; } + &__title { + display: flex; + justify-content: space-between; + align-items: center; + } + &__description { @include lead-paragraph; diff --git a/client/src/components/ContentBlock.vue b/client/src/components/ContentBlock.vue index 84a9fdee..025b630e 100644 --- a/client/src/components/ContentBlock.vue +++ b/client/src/components/ContentBlock.vue @@ -219,10 +219,7 @@ onMounted(() => { const rect = element.getBoundingClientRect(); window.scrollTo({ top: rect.y, behavior: 'smooth' }); top.value = rect.y; - console.log(rect.y); - console.log(rect); }, 750); - console.log(document.readyState); } } }); diff --git a/client/src/components/CopyLink.vue b/client/src/components/CopyLink.vue index 304d645a..2085be8e 100644 --- a/client/src/components/CopyLink.vue +++ b/client/src/components/CopyLink.vue @@ -11,6 +11,7 @@ diff --git a/server/books/models/chapter.py b/server/books/models/chapter.py index 269c8b63..62dd1541 100644 --- a/server/books/models/chapter.py +++ b/server/books/models/chapter.py @@ -5,51 +5,67 @@ from wagtail.admin.edit_handlers import FieldPanel, TabbedInterface, ObjectList from core.wagtail_utils import StrictHierarchyPage, get_default_settings from users.models import SchoolClass +from core.mixins import GraphqlNodeMixin logger = logging.getLogger(__name__) -class Chapter(StrictHierarchyPage): +class Chapter(StrictHierarchyPage, GraphqlNodeMixin): class Meta: - verbose_name = 'Kapitel' - verbose_name_plural = 'Kapitel' + verbose_name = "Kapitel" + verbose_name_plural = "Kapitel" description = models.TextField(blank=True) content_panels = [ - FieldPanel('title', classname="full title"), - FieldPanel('description', classname="full description"), + FieldPanel("title", classname="full title"), + FieldPanel("description", classname="full description"), ] - edit_handler = TabbedInterface([ - ObjectList(content_panels, heading='Content'), - get_default_settings() - ]) + edit_handler = TabbedInterface( + [ObjectList(content_panels, heading="Content"), get_default_settings()] + ) - template = 'generic_page.html' + template = "generic_page.html" - parent_page_types = ['books.Module'] - subpage_types = ['books.ContentBlock'] + parent_page_types = ["books.Module"] + subpage_types = ["books.ContentBlock"] - title_hidden_for = models.ManyToManyField(SchoolClass, related_name='hidden_chapter_titles') - description_hidden_for = models.ManyToManyField(SchoolClass, related_name='hidden_chapter_descriptions') + title_hidden_for = models.ManyToManyField( + SchoolClass, related_name="hidden_chapter_titles" + ) + description_hidden_for = models.ManyToManyField( + SchoolClass, related_name="hidden_chapter_descriptions" + ) def sync_title_visibility(self, school_class_template, school_class_to_sync): - if self.title_hidden_for.filter(id=school_class_template.id).exists() \ - and not self.title_hidden_for.filter(id=school_class_to_sync.id).exists(): + if ( + self.title_hidden_for.filter(id=school_class_template.id).exists() + and not self.title_hidden_for.filter(id=school_class_to_sync.id).exists() + ): self.title_hidden_for.add(school_class_to_sync) - if self.title_hidden_for.filter( - id=school_class_to_sync.id).exists() \ - and not self.title_hidden_for.filter(id=school_class_template.id).exists(): + if ( + self.title_hidden_for.filter(id=school_class_to_sync.id).exists() + and not self.title_hidden_for.filter(id=school_class_template.id).exists() + ): self.title_hidden_for.remove(school_class_to_sync) def sync_description_visibility(self, school_class_template, school_class_to_sync): - if self.description_hidden_for.filter(id=school_class_template.id).exists() \ - and not self.description_hidden_for.filter(id=school_class_to_sync.id).exists(): + if ( + self.description_hidden_for.filter( + id=school_class_template.id).exists() + and not self.description_hidden_for.filter( + id=school_class_to_sync.id + ).exists() + ): self.description_hidden_for.add(school_class_to_sync) - if self.description_hidden_for.filter( - id=school_class_to_sync.id).exists() \ - and not self.description_hidden_for.filter(id=school_class_template.id).exists(): + if ( + self.description_hidden_for.filter( + id=school_class_to_sync.id).exists() + and not self.description_hidden_for.filter( + id=school_class_template.id + ).exists() + ): self.description_hidden_for.remove(school_class_to_sync) diff --git a/server/books/schema/nodes/chapter.py b/server/books/schema/nodes/chapter.py index 20bb6af0..bdd71b3f 100644 --- a/server/books/schema/nodes/chapter.py +++ b/server/books/schema/nodes/chapter.py @@ -11,43 +11,55 @@ from notes.schema import ChapterBookmarkNode class ChapterNode(DjangoObjectType): bookmark = graphene.Field(ChapterBookmarkNode) - content_blocks = graphene.List('books.schema.nodes.ContentBlockNode') - title_hidden_for = graphene.List('users.schema.SchoolClassNode') - description_hidden_for = graphene.List('users.schema.SchoolClassNode') + content_blocks = graphene.List("books.schema.nodes.ContentBlockNode") + title_hidden_for = graphene.List("users.schema.SchoolClassNode") + description_hidden_for = graphene.List("users.schema.SchoolClassNode") + path = graphene.String() class Meta: model = Chapter only_fields = [ - 'slug', 'title', 'description', 'title_hidden_for', 'description_hidden_for' + "slug", + "title", + "description", + "title_hidden_for", + "description_hidden_for", ] filter_fields = [ - 'slug', 'title', + "slug", + "title", ] interfaces = (relay.Node, ChapterInterface) def resolve_content_blocks(self, info, **kwargs): user = info.context.user - school_classes = user.school_classes.values_list('pk', flat=True) + school_classes = user.school_classes.values_list("pk", flat=True) - by_parent = ContentBlock.get_by_parent(self) \ - .filter(contentblocksnapshot__isnull=True) # exclude snapshot + by_parent = ContentBlock.get_by_parent(self).filter( + contentblocksnapshot__isnull=True + ) # exclude snapshot # .prefetch_related('visible_for') \ # .prefetch_related('hidden_for') # don't filter the hidden blocks on the server any more, we do this on the client now, as they are not secret default_blocks = Q(user_created=False) owned_by_user = Q(user_created=True, owner=user) - teacher_created_and_visible = Q(Q(user_created=True) & Q(visible_for__pk__in=school_classes)) + teacher_created_and_visible = Q( + Q(user_created=True) & Q(visible_for__pk__in=school_classes) + ) if user.can_manage_school_class_content: # teacher - return by_parent.filter(default_blocks | owned_by_user | teacher_created_and_visible).distinct() + return by_parent.filter( + default_blocks | owned_by_user | teacher_created_and_visible + ).distinct() else: # student - return by_parent.filter(default_blocks | teacher_created_and_visible).distinct() + return by_parent.filter( + default_blocks | teacher_created_and_visible + ).distinct() def resolve_bookmark(self, info, **kwargs): return ChapterBookmark.objects.filter( - user=info.context.user, - chapter=self + user=info.context.user, chapter=self ).first() @staticmethod @@ -57,3 +69,8 @@ class ChapterNode(DjangoObjectType): @staticmethod def resolve_description_hidden_for(parent: Chapter, info, **kwargs): return parent.description_hidden_for.all() + + @staticmethod + def resolve_path(root: Chapter, info, **kwargs): + module = root.get_parent() + return f"module/{module.slug}#{root.graphql_id}"