from django.db import models from wagtail.admin.panels import ( FieldPanel, TabbedInterface, ObjectList, TitleFieldPanel, ) from wagtail.blocks import StreamBlock from wagtail.fields import StreamField from wagtail.images.blocks import ImageChooserBlock from books.managers import ContentBlockManager from core.logger import get_logger from core.wagtail_utils import get_default_settings from books.blocks import ( CMSDocumentBlock, SolutionBlock, TextBlock, BasicKnowledgeBlock, LinkBlock, VideoBlock, DocumentBlock, ImageUrlBlock, AssignmentBlock, InfogramBlock, GeniallyBlock, SubtitleBlock, SurveyBlock, ModuleRoomSlugBlock, ThinglinkBlock, InstructionBlock, ) from core.wagtail_utils import StrictHierarchyPage from notes.models import ContentBlockBookmark from users.models import SchoolClass, User from core.mixins import GraphqlNodeMixin logger = get_logger(__name__) def duplicate_entities_generator(new_module): def duplicate_entities(block): content_type = block.block_type value = block.value # logger.debug(block) if content_type == "assignment": assignment = value.get("assignment_id") if assignment is None: return None # copy the assignment assignment.pk = None assignment.title = f"{assignment.title} (Kopie)" assignment.module = new_module # logger.debug(f"setting new module {new_module}, {assignment.module}") assignment.save() data = {"assignment_id": assignment} new_block = ("assignment", data) return new_block if content_type == "survey": # logger.debug(value) survey = value.get("survey_id") if survey is None: return None # copy the survey survey.pk = None survey.title = f"{survey.title} (Kopie)" survey.module = new_module # logger.debug(f"setting new module {new_module}, {survey.module}") survey.save() data = {"survey_id": survey} new_block = ("survey", data) # logger.debug(new_block) return new_block return block return duplicate_entities class ContentBlock(StrictHierarchyPage, GraphqlNodeMixin): class Meta: verbose_name = "Inhaltsblock" verbose_name_plural = "Inhaltsblöcke" NORMAL = "normal" TASK = "task" INSTRUMENT = "instrument" TYPE_CHOICES = ( (NORMAL, "Normal"), (TASK, "Auftrag"), (INSTRUMENT, "Instrument"), ) # blocks without owner are visible by default, need to be hidden for each class hidden_for = models.ManyToManyField( SchoolClass, related_name="hidden_content_blocks" ) # blocks with owner are hidden by default, need to be shown for each class visible_for = models.ManyToManyField( SchoolClass, related_name="visible_content_blocks" ) user_created = models.BooleanField(default=False) original_creator = models.ForeignKey( User, null=True, blank=True, default=None, on_delete=models.SET_NULL ) bookmarks = models.ManyToManyField( User, through=ContentBlockBookmark, related_name="bookmarked_content_blocks" ) content_blocks = [ ("text_block", TextBlock()), ("basic_knowledge", BasicKnowledgeBlock()), ("assignment", AssignmentBlock()), ("survey", SurveyBlock()), ("image_block", ImageChooserBlock()), ("image_url_block", ImageUrlBlock()), ("link_block", LinkBlock()), # ('solution', TextBlock(icon='tick')), ("solution", SolutionBlock()), ("video_block", VideoBlock()), ("document_block", DocumentBlock()), ("infogram_block", InfogramBlock()), ("genially_block", GeniallyBlock()), ("thinglink_block", ThinglinkBlock()), ("subtitle", SubtitleBlock()), ("instruction", InstructionBlock()), ("module_room_slug", ModuleRoomSlugBlock()), # ('cms_document_block', DocumentChooserBlock(label='CMS Document')) ("cms_document_block", CMSDocumentBlock()), ] content_list_item = StreamBlock(content_blocks) contents = StreamField( content_blocks + [("content_list_item", content_list_item)], null=True, blank=True, use_json_field=True, ) type = models.CharField(max_length=100, choices=TYPE_CHOICES, default=NORMAL) content_panels = [ TitleFieldPanel("title", classname="full title"), FieldPanel("type"), FieldPanel("contents"), ] # edit_handler = TabbedInterface( [ObjectList(content_panels, heading="Content"), get_default_settings()] ) parent_page_types = ["books.Chapter"] subpage_types = [] # prevent these fields from being copied exclude_fields_in_copy = ["bookmarks"] objects = ContentBlockManager() @property def module(self): return self.get_parent().get_parent().specific @property def route(self): # path is probably used by treebeard return f"module/{self.module.slug}#{self.graphql_id}" # duplicate all attached Surveys and Assignments def duplicate_attached_entities(self): duplicate_entities_func = duplicate_entities_generator(self.module) new_contents = [duplicate_entities_func(content) for content in self.contents] cleaned_contents = [item for item in new_contents if item is not None] # we can't just insert a list here, we need a StreamValue data type # so we need to clear the list, then add each element in turn self.contents.clear() # like this, the internal methods of the SteamValue data type can work on the single elements for content in cleaned_contents: self.contents.append(content) # as an illustration # data = {'text': 'This is me'} # self.contents.append(('solution', data)) self.save() def is_hidden_for_class(self, school_class): return ( not self.user_created and self.hidden_for.filter(id=school_class.id).exists() ) or ( self.user_created and not self.visible_for.filter(id=school_class.id).exists() ) def reassign_entities(self): module = self.module logger.debug("reassigning entities") for content in self.contents: if content.block_type == "assignment": assignment = content.value.get("assignment_id") logger.debug(assignment.module) if assignment.module != module: assignment.module = module assignment.save() if content.block_type == "survey": survey = content.value.get("survey_id") logger.debug(survey.module) if survey.module != module: survey.module = module survey.save() # def save(self, *args, **kwargs): # todo: move this to the after_create_page and after_edit_page hooks, and remove from here. # for data in self.contents.raw_data: # block_type, value = get_type_and_value(data) # todo: also do the same for assignments # if block_type == "survey": # module = self.module # survey = value["survey_id"] # if isinstance(survey, int): # survey = Survey.objects.get(pk=survey) # if survey.module != module: # survey.module = module # survey.save() # super().save(*args, **kwargs) class ContentBlockSnapshot(ContentBlock): hidden = models.BooleanField(default=False) snapshot = models.ForeignKey( "books.snapshot", on_delete=models.SET_NULL, null=True, related_name="custom_content_blocks", ) def to_regular_content_block(self, owner, school_class): cb = ContentBlock( contents=self.contents, type=self.type, title=self.title, owner=owner, original_creator=self.original_creator, user_created=True, ) self.add_sibling(instance=cb, pos="right") # some wagtail magic revision = cb.save_revision() revision.publish() cb.visible_for.add(school_class) cb.save() return cb