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

View File

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

View File

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

View File

@ -11,19 +11,20 @@ from users.models import SchoolClass
class Module(StrictHierarchyPage):
class Meta:
verbose_name = 'Modul'
verbose_name_plural = 'Module'
verbose_name = "Modul"
verbose_name_plural = "Module"
meta_title = models.CharField(
max_length=255,
help_text='e.g. \'Intro\' or \'Modul 1\''
)
max_length=255, help_text="e.g. 'Intro' or 'Modul 1'")
hero_image = models.ForeignKey(
'wagtailimages.Image',
"wagtailimages.Image",
null=True,
blank=True,
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()
@ -32,28 +33,26 @@ class Module(StrictHierarchyPage):
solutions_enabled_for = models.ManyToManyField(SchoolClass)
content_panels = [
FieldPanel('title', classname="full title"),
FieldPanel('meta_title', classname="full title"),
ImageChooserPanel('hero_image'),
FieldPanel('teaser'),
FieldPanel('intro'),
FieldPanel("title", classname="full title"),
FieldPanel("meta_title", classname="full title"),
ImageChooserPanel("hero_image"),
FieldPanel("hero_source"),
FieldPanel("teaser"),
FieldPanel("intro"),
]
edit_handler = TabbedInterface([
ObjectList(content_panels, heading='Content'),
get_default_settings()
])
template = 'generic_page.html'
parent_page_types = ['books.Topic']
subpage_types = ['books.Chapter']
edit_handler = TabbedInterface(
[ObjectList(content_panels, heading="Content"), get_default_settings()]
)
template = "generic_page.html"
parent_page_types = ["books.Topic"]
subpage_types = ["books.Chapter"]
# todo: isn't this a duplicate definition?
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):
# import here so we don't get a circular import error
@ -65,41 +64,57 @@ class Module(StrictHierarchyPage):
# get content blocks of 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`
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)
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)
# 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`
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_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`
content_block.visible_for.add(school_class_to_sync)
for chapter in chapters:
chapter.sync_title_visibility(school_class_template, school_class_to_sync)
chapter.sync_description_visibility(school_class_template, school_class_to_sync)
chapter.sync_title_visibility(
school_class_template, school_class_to_sync)
chapter.sync_description_visibility(
school_class_template, school_class_to_sync
)
objective_groups = self.objective_groups.all()
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):
return f"{self.meta_title} - {self.title}"
class RecentModule(models.Model):
module = models.ForeignKey(Module, on_delete=models.CASCADE, related_name='recent_modules')
user = models.ForeignKey('users.User', on_delete=models.CASCADE)
module = models.ForeignKey(
Module, on_delete=models.CASCADE, related_name="recent_modules"
)
user = models.ForeignKey("users.User", on_delete=models.CASCADE)
visited = models.DateTimeField(default=timezone.now)
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.nodes.chapter import ChapterNode
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 surveys.models import Answer
from surveys.schema import AnswerNode
@ -20,11 +24,20 @@ class ModuleNode(DjangoObjectType):
class Meta:
model = Module
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 = {
'slug': ['exact', 'icontains', 'in'],
'title': ['exact', 'icontains', 'in'],
"slug": ["exact", "icontains", "in"],
"title": ["exact", "icontains", "in"],
}
interfaces = (ModuleInterface,)
@ -33,9 +46,10 @@ class ModuleNode(DjangoObjectType):
bookmark = graphene.Field(ModuleBookmarkNode)
my_submissions = DjangoFilterConnectionField(StudentSubmissionNode)
my_answers = DjangoFilterConnectionField(AnswerNode)
my_content_bookmarks = DjangoFilterConnectionField(ContentBlockBookmarkNode)
my_content_bookmarks = DjangoFilterConnectionField(
ContentBlockBookmarkNode)
my_chapter_bookmarks = DjangoFilterConnectionField(ChapterBookmarkNode)
snapshots = graphene.List('books.schema.nodes.SnapshotNode')
snapshots = graphene.List("books.schema.nodes.SnapshotNode")
objective_groups = graphene.List(ObjectiveGroupNode)
assignments = graphene.List(AssignmentNode)
@ -47,12 +61,15 @@ class ModuleNode(DjangoObjectType):
def resolve_solutions_enabled(self, info, **kwargs):
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):
return ModuleBookmark.objects.filter(
user=info.context.user,
module=self
user=info.context.user, module=self
).first()
def resolve_my_submissions(self, info, **kwargs):
@ -69,7 +86,9 @@ class ModuleNode(DjangoObjectType):
def resolve_my_content_bookmarks(self, info, **kwargs):
user = info.context.user
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 Image etc
# Bookmark Other
@ -83,13 +102,14 @@ class ModuleNode(DjangoObjectType):
@staticmethod
def resolve_objective_groups(parent, info, **kwargs):
return parent.objective_groups.all() \
.prefetch_related('hidden_for')
return parent.objective_groups.all().prefetch_related("hidden_for")
@staticmethod
def resolve_snapshots(parent, info, **kwargs):
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
def resolve_assignments(parent: Module, info, **kwargs):

View File

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