Add source to module hero image

Resolves MS-601
This commit is contained in:
Ramon Wenger 2023-02-14 15:25:43 +01:00
parent 46d42eb279
commit 3ab0662a6a
6 changed files with 107 additions and 57 deletions

View File

@ -16,11 +16,19 @@
> >
{{ module.title }} {{ module.title }}
</h1> </h1>
<img <div class="module__hero">
:src="module.heroImage" <img
alt="" :src="module.heroImage"
class="module__hero" alt=""
/> class="module__hero-image"
/>
<h5
class="module__hero-source"
v-if="module.heroSource"
>
Quelle: {{ module.heroSource }}
</h5>
</div>
<div class="module__intro-wrapper"> <div class="module__intro-wrapper">
<bookmark-actions <bookmark-actions
@ -131,8 +139,16 @@ export default {
&__hero { &__hero {
margin-bottom: 35px; margin-bottom: 35px;
border-radius: 12px; }
&__hero-image {
max-width: 100%; max-width: 100%;
border-radius: 12px;
}
&__hero-source {
@include tiny-text;
ling-height: 25px;
} }
&__meta-title { &__meta-title {

View File

@ -8,11 +8,7 @@
:src="teaser.imageUrl" :src="teaser.imageUrl"
class="news-teaser__image" class="news-teaser__image"
/> />
<a <h5 class="news-teaser__image-source">Quelle {{ teaser.imageSource }}</h5>
:href="teaser.imageSource"
class="news-teaser__image-source"
>Quelle {{ teaser.imageSource }}</a
>
<h4 class="news-teaser__title">{{ teaser.title }}</h4> <h4 class="news-teaser__title">{{ teaser.title }}</h4>
<p class="news-teaser__description">{{ teaser.description }}</p> <p class="news-teaser__description">{{ teaser.description }}</p>

View File

@ -6,6 +6,7 @@ fragment ModuleParts on ModuleNode {
intro intro
slug slug
heroImage heroImage
heroSource
solutionsEnabled solutionsEnabled
inEditMode @client inEditMode @client
topic { topic {

View File

@ -11,19 +11,20 @@ from users.models import SchoolClass
class Module(StrictHierarchyPage): class Module(StrictHierarchyPage):
class Meta: class Meta:
verbose_name = 'Modul' verbose_name = "Modul"
verbose_name_plural = 'Module' verbose_name_plural = "Module"
meta_title = models.CharField( meta_title = models.CharField(
max_length=255, max_length=255, help_text="e.g. 'Intro' or 'Modul 1'")
help_text='e.g. \'Intro\' or \'Modul 1\''
)
hero_image = models.ForeignKey( hero_image = models.ForeignKey(
'wagtailimages.Image', "wagtailimages.Image",
null=True, null=True,
blank=True, blank=True,
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
related_name='+' related_name="+",
)
hero_source = models.CharField(
max_length=255, help_text="e.g. 'Reuters', 'Wikipedia'", blank=True
) )
teaser = models.TextField() teaser = models.TextField()
@ -32,28 +33,26 @@ class Module(StrictHierarchyPage):
solutions_enabled_for = models.ManyToManyField(SchoolClass) solutions_enabled_for = models.ManyToManyField(SchoolClass)
content_panels = [ content_panels = [
FieldPanel('title', classname="full title"), FieldPanel("title", classname="full title"),
FieldPanel('meta_title', classname="full title"), FieldPanel("meta_title", classname="full title"),
ImageChooserPanel('hero_image'), ImageChooserPanel("hero_image"),
FieldPanel('teaser'), FieldPanel("hero_source"),
FieldPanel('intro'), FieldPanel("teaser"),
FieldPanel("intro"),
] ]
edit_handler = TabbedInterface([ edit_handler = TabbedInterface(
ObjectList(content_panels, heading='Content'), [ObjectList(content_panels, heading="Content"), get_default_settings()]
get_default_settings() )
])
template = 'generic_page.html'
parent_page_types = ['books.Topic']
subpage_types = ['books.Chapter']
template = "generic_page.html"
parent_page_types = ["books.Topic"]
subpage_types = ["books.Chapter"]
# todo: isn't this a duplicate definition? # todo: isn't this a duplicate definition?
def get_child_ids(self): def get_child_ids(self):
return self.get_children().values_list('id', flat=True) return self.get_children().values_list("id", flat=True)
def sync_from_school_class(self, school_class_template, 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 # import here so we don't get a circular import error
@ -65,41 +64,57 @@ class Module(StrictHierarchyPage):
# get content blocks of chapters # get content blocks of chapters
for chapter in chapters: for chapter in chapters:
content_block_query = content_block_query.union(ContentBlock.get_by_parent(chapter)) 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` # 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): for content_block in school_class_to_sync.hidden_content_blocks.intersection(
content_block_query
):
content_block.hidden_for.remove(school_class_to_sync) content_block.hidden_for.remove(school_class_to_sync)
for content_block in school_class_to_sync.visible_content_blocks.intersection(content_block_query): for content_block in school_class_to_sync.visible_content_blocks.intersection(
content_block_query
):
content_block.visible_for.remove(school_class_to_sync) content_block.visible_for.remove(school_class_to_sync)
# get all content blocks with `hidden for` for class `school_class_pattern` # get all content blocks with `hidden for` for class `school_class_pattern`
for content_block in school_class_template.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` # add `school_class_to_sync` to these blocks' `hidden for`
content_block.hidden_for.add(school_class_to_sync) content_block.hidden_for.add(school_class_to_sync)
# get all content blocks with `visible for` for class `school_class_pattern` # get all content blocks with `visible for` for class `school_class_pattern`
for content_block in school_class_template.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` # add `school_class_to_sync` to these blocks' `visible for`
content_block.visible_for.add(school_class_to_sync) content_block.visible_for.add(school_class_to_sync)
for chapter in chapters: for chapter in chapters:
chapter.sync_title_visibility(school_class_template, school_class_to_sync) chapter.sync_title_visibility(
chapter.sync_description_visibility(school_class_template, school_class_to_sync) school_class_template, school_class_to_sync)
chapter.sync_description_visibility(
school_class_template, school_class_to_sync
)
objective_groups = self.objective_groups.all() objective_groups = self.objective_groups.all()
for objective_group in objective_groups: for objective_group in objective_groups:
objective_group.sync_visibility(school_class_template, school_class_to_sync) objective_group.sync_visibility(
school_class_template, school_class_to_sync)
def get_admin_display_title(self): def get_admin_display_title(self):
return f"{self.meta_title} - {self.title}" return f"{self.meta_title} - {self.title}"
class RecentModule(models.Model): class RecentModule(models.Model):
module = models.ForeignKey(Module, on_delete=models.CASCADE, related_name='recent_modules') module = models.ForeignKey(
user = models.ForeignKey('users.User', on_delete=models.CASCADE) Module, on_delete=models.CASCADE, related_name="recent_modules"
)
user = models.ForeignKey("users.User", on_delete=models.CASCADE)
visited = models.DateTimeField(default=timezone.now) visited = models.DateTimeField(default=timezone.now)
class Meta: class Meta:
get_latest_by = 'visited' get_latest_by = "visited"

View File

@ -10,7 +10,11 @@ from books.models import Module, Chapter, ContentBlock, RecentModule
from books.schema.interfaces.module import ModuleInterface from books.schema.interfaces.module import ModuleInterface
from books.schema.nodes.chapter import ChapterNode from books.schema.nodes.chapter import ChapterNode
from notes.models import ModuleBookmark, ContentBlockBookmark, ChapterBookmark from notes.models import ModuleBookmark, ContentBlockBookmark, ChapterBookmark
from notes.schema import ModuleBookmarkNode, ContentBlockBookmarkNode, ChapterBookmarkNode from notes.schema import (
ModuleBookmarkNode,
ContentBlockBookmarkNode,
ChapterBookmarkNode,
)
from objectives.schema import ObjectiveGroupNode from objectives.schema import ObjectiveGroupNode
from surveys.models import Answer from surveys.models import Answer
from surveys.schema import AnswerNode from surveys.schema import AnswerNode
@ -20,11 +24,20 @@ class ModuleNode(DjangoObjectType):
class Meta: class Meta:
model = Module model = Module
only_fields = [ only_fields = [
'slug', 'title', 'meta_title', 'teaser', 'intro', 'objective_groups', 'assignments', 'hero_image', 'topic' "slug",
"title",
"meta_title",
"teaser",
"intro",
"objective_groups",
"assignments",
"hero_image",
"hero_source",
"topic",
] ]
filter_fields = { filter_fields = {
'slug': ['exact', 'icontains', 'in'], "slug": ["exact", "icontains", "in"],
'title': ['exact', 'icontains', 'in'], "title": ["exact", "icontains", "in"],
} }
interfaces = (ModuleInterface,) interfaces = (ModuleInterface,)
@ -33,9 +46,10 @@ class ModuleNode(DjangoObjectType):
bookmark = graphene.Field(ModuleBookmarkNode) bookmark = graphene.Field(ModuleBookmarkNode)
my_submissions = DjangoFilterConnectionField(StudentSubmissionNode) my_submissions = DjangoFilterConnectionField(StudentSubmissionNode)
my_answers = DjangoFilterConnectionField(AnswerNode) my_answers = DjangoFilterConnectionField(AnswerNode)
my_content_bookmarks = DjangoFilterConnectionField(ContentBlockBookmarkNode) my_content_bookmarks = DjangoFilterConnectionField(
ContentBlockBookmarkNode)
my_chapter_bookmarks = DjangoFilterConnectionField(ChapterBookmarkNode) my_chapter_bookmarks = DjangoFilterConnectionField(ChapterBookmarkNode)
snapshots = graphene.List('books.schema.nodes.SnapshotNode') snapshots = graphene.List("books.schema.nodes.SnapshotNode")
objective_groups = graphene.List(ObjectiveGroupNode) objective_groups = graphene.List(ObjectiveGroupNode)
assignments = graphene.List(AssignmentNode) assignments = graphene.List(AssignmentNode)
@ -47,12 +61,15 @@ class ModuleNode(DjangoObjectType):
def resolve_solutions_enabled(self, info, **kwargs): def resolve_solutions_enabled(self, info, **kwargs):
school_class = info.context.user.selected_class school_class = info.context.user.selected_class
return self.solutions_enabled_for.filter(pk=school_class.pk).exists() if school_class is not None else False return (
self.solutions_enabled_for.filter(pk=school_class.pk).exists()
if school_class is not None
else False
)
def resolve_bookmark(self, info, **kwags): def resolve_bookmark(self, info, **kwags):
return ModuleBookmark.objects.filter( return ModuleBookmark.objects.filter(
user=info.context.user, user=info.context.user, module=self
module=self
).first() ).first()
def resolve_my_submissions(self, info, **kwargs): def resolve_my_submissions(self, info, **kwargs):
@ -69,7 +86,9 @@ class ModuleNode(DjangoObjectType):
def resolve_my_content_bookmarks(self, info, **kwargs): def resolve_my_content_bookmarks(self, info, **kwargs):
user = info.context.user user = info.context.user
content_blocks = ContentBlock.objects.live().descendant_of(self) content_blocks = ContentBlock.objects.live().descendant_of(self)
return ContentBlockBookmark.objects.filter(content_block__in=content_blocks, user=user) return ContentBlockBookmark.objects.filter(
content_block__in=content_blocks, user=user
)
# Bookmark Text # Bookmark Text
# Bookmark Image etc # Bookmark Image etc
# Bookmark Other # Bookmark Other
@ -83,13 +102,14 @@ class ModuleNode(DjangoObjectType):
@staticmethod @staticmethod
def resolve_objective_groups(parent, info, **kwargs): def resolve_objective_groups(parent, info, **kwargs):
return parent.objective_groups.all() \ return parent.objective_groups.all().prefetch_related("hidden_for")
.prefetch_related('hidden_for')
@staticmethod @staticmethod
def resolve_snapshots(parent, info, **kwargs): def resolve_snapshots(parent, info, **kwargs):
user = info.context.user user = info.context.user
return parent.snapshots.filter(Q(creator=user) | Q(Q(creator__team=user.team ) & Q(shared=True))) return parent.snapshots.filter(
Q(creator=user) | Q(Q(creator__team=user.team) & Q(shared=True))
)
@staticmethod @staticmethod
def resolve_assignments(parent: Module, info, **kwargs): def resolve_assignments(parent: Module, info, **kwargs):

View File

@ -227,6 +227,7 @@ type ChapterNode implements Node & ChapterInterface {
id: ID! id: ID!
bookmark: ChapterBookmarkNode bookmark: ChapterBookmarkNode
contentBlocks: [ContentBlockNode] contentBlocks: [ContentBlockNode]
path: String
} }
type ChapterNodeConnection { type ChapterNodeConnection {
@ -621,6 +622,7 @@ type ModuleNode implements ModuleInterface {
slug: String! slug: String!
metaTitle: String! metaTitle: String!
heroImage: String! heroImage: String!
heroSource: String!
teaser: String! teaser: String!
intro: String! intro: String!
assignments: [AssignmentNode] assignments: [AssignmentNode]