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}"