def update_content(self, text, on_error=None): _, old_metadata, _ = render_markdown(self.text) html, metadata, messages = render_markdown(text, on_error=on_error) self.text = text self.text_html = html self.save() all_the_pings = list( filter(lambda user_name: user_name != self.author.username, metadata.get('ping', []))) all_the_pings = list( set(all_the_pings) - set(old_metadata.get('ping', []))) max_ping_count = settings.ZDS_APP['comment']['max_pings'] first_pings = all_the_pings[:max_ping_count] for username in first_pings: pinged_user = User.objects.filter(username=username).first() if not pinged_user: continue signals.new_content.send(sender=self.__class__, instance=self, user=pinged_user) unpinged_usernames = set(old_metadata.get('ping', [])) - set(all_the_pings) unpinged_users = User.objects.filter( username__in=list(unpinged_usernames)) for unpinged_user in unpinged_users: signals.unsubscribe.send(self.author, instance=self, user=unpinged_user)
def update_content(self, text, on_error=None): """ Updates the content of this comment. This method will render the new comment to HTML, store the rendered version, and store data to later analyze pings and spam. This method updates fields, but does not save the instance. :param text: The new comment content. :param on_error: A callable called if zmd returns an error, provided with a single argument: a list of user-friendly errors. See render_markdown. """ # This attribute will be used by `_save_check_spam`, called after save (but not saved into the database). # We only update it if it does not already exist, so if this method is called multiple times, the oldest # version (i.e. the one currently in the database) is used for comparison. `_save_check_spam` will delete # the attribute, so if we re-save the same instance, this will be re-set. if not hasattr(self, "old_text"): self.old_text = self.text _, old_metadata, _ = render_markdown(self.text) html, new_metadata, _ = render_markdown(text, on_error=on_error) # These attributes will be used by `_save_compute_pings` to create notifications if needed. # For the same reason as `old_text`, we only update `old_metadata` if not already set. if not hasattr(self, "old_metadata"): self.old_metadata = old_metadata self.new_metadata = new_metadata self.text = text self.text_html = html
def update_content(self, text, on_error=None): _, old_metadata, _ = render_markdown(self.text) html, metadata, messages = render_markdown(text, on_error=on_error) self.text = text self.text_html = html self.save() def filter_usernames(original_list): # removes duplicates and the message's author filtered_list = [] for username in original_list: if username != self.author.username and username not in filtered_list: filtered_list.append(username) return filtered_list max_pings_allowed = settings.ZDS_APP["comment"]["max_pings"] pinged_usernames_from_new_text = filter_usernames( metadata.get("ping", []))[:max_pings_allowed] pinged_usernames_from_old_text = filter_usernames( old_metadata.get("ping", []))[:max_pings_allowed] pinged_usernames = set(pinged_usernames_from_new_text) - set( pinged_usernames_from_old_text) pinged_users = User.objects.filter(username__in=pinged_usernames) for pinged_user in pinged_users: signals.ping.send(sender=self.__class__, instance=self, user=pinged_user) unpinged_usernames = set(pinged_usernames_from_old_text) - set( pinged_usernames_from_new_text) unpinged_users = User.objects.filter(username__in=unpinged_usernames) for unpinged_user in unpinged_users: signals.unping.send(self.author, instance=self, user=unpinged_user)
def test_edit_detail(self): # Login as staff self.assertTrue(self.client.login(username=self.staff.username, password='******')) # Check that the original content is displayed response = self.client.get(reverse('edit-detail', args=[self.edit.pk])) original_text_html, *_ = render_markdown(self.edit.original_text, disable_ping=True) self.assertContains(response, original_text_html)
def test_edit_detail(self): # Login as staff self.assertTrue(self.client.login(username=self.staff.username, password='******')) # Check that the original content is displayed response = self.client.get(reverse('edit-detail', args=[self.edit.pk])) original_text_html, *_ = render_markdown(self.edit.original_text, disable_ping=True) self.assertContains(response, original_text_html)
def test_edit_detail(self): # Login as staff self.client.force_login(self.staff) # Check that the original content is displayed response = self.client.get(reverse("edit-detail", args=[self.edit.pk])) original_text_html, *_ = render_markdown(self.edit.original_text, disable_ping=True) self.assertContains(response, original_text_html)
def update_content(self, text): from zds.notification.models import ping_url self.text = text md_instance = get_markdown_instance(ping_url=ping_url) self.text_html = render_markdown(md_instance, self.text) self.save() for username in list(md_instance.metadata.get('ping', []))[:settings.ZDS_APP['comment']['max_pings']]: signals.new_content.send(sender=self.__class__, instance=self, user=User.objects.get(username=username))
def publish_use_manifest(db_object, base_dir, versionable_content: VersionedContent): base_content = export_content(versionable_content, with_text=True) md, metadata, __ = render_markdown( base_content, disable_jsfiddle=not db_object.js_support, full_json=True, stats=True) publish_container_new(db_object, base_dir, versionable_content, md) return metadata.get("stats", {}).get("signs", 0)
def test_edit_detail(self): # Login as staff self.assertTrue( self.client.login(username=self.staff.username, password='******')) # Check that the original content is displayed response = self.client.get(reverse('edit-detail', args=[self.edit.pk])) md_instance = get_markdown_instance(ping_url=None) original_text_html = render_markdown(md_instance, self.edit.original_text) self.assertContains(response, original_text_html)
def publish(self, md_file_path, base_name, **kwargs): md_flat_content = _read_flat_markdown(md_file_path) published_content_entity = self.get_published_content_entity(md_file_path) html_flat_content, *_ = render_markdown(md_flat_content, disable_ping=True, disable_js=True) html_file_path = path.splitext(md_file_path)[0] + '.html' if str(MD_PARSING_ERROR) in html_flat_content: logging.getLogger(self.__class__.__name__).error('HTML could not be rendered') return with open(html_file_path, mode='w', encoding='utf-8') as final_file: final_file.write(html_flat_content) shutil.move(html_file_path, published_content_entity.get_extra_contents_directory())
def publish(self, md_file_path, base_name, **kwargs): md_flat_content = _read_flat_markdown(md_file_path) published_content_entity = self.get_published_content_entity( md_file_path) html_flat_content, *_ = render_markdown(md_flat_content, disable_ping=True, disable_js=True) html_file_path = path.splitext(md_file_path)[0] + '.html' if str(MD_PARSING_ERROR) in html_flat_content: logging.getLogger( self.__class__.__name__).error('HTML could not be rendered') return with open(html_file_path, mode='w', encoding='utf-8') as final_file: final_file.write(html_flat_content) shutil.move(html_file_path, published_content_entity.get_extra_contents_directory())
def publish(self, md_file_path, base_name, **kwargs): md_flat_content = _read_flat_markdown(md_file_path) published_content_entity = self.get_published_content_entity(md_file_path) gallery_pk = published_content_entity.content.gallery.pk depth_to_size_map = { 1: "small", # in fact this is an "empty" tutorial (i.e it is empty or has intro and/or conclusion) 2: "small", 3: "middle", 4: "big", } public_versionned_source = published_content_entity.content.load_version( sha=published_content_entity.sha_public ) base_directory = Path(base_name).parent image_dir = base_directory / "images" with contextlib.suppress(FileExistsError): image_dir.mkdir(parents=True) if (settings.MEDIA_ROOT / "galleries" / str(gallery_pk)).exists(): for image in (settings.MEDIA_ROOT / "galleries" / str(gallery_pk)).iterdir(): with contextlib.suppress(OSError): shutil.copy2(str(image.absolute()), str(image_dir)) content_type = depth_to_size_map[public_versionned_source.get_tree_level()] if self.latex_classes: content_type += ", " + self.latex_classes title = published_content_entity.title() authors = [a.username for a in published_content_entity.authors.all()] licence = published_content_entity.content.licence.code licence_short = licence.replace("CC", "").strip().lower() licence_logo = licences.get(licence_short, False) if licence_logo: licence_url = "https://creativecommons.org/licenses/{}/4.0/legalcode".format(licence_short) # we need a specific case for CC-0 as it is "public-domain" if licence_logo == licences["0"]: licence_url = "https://creativecommons.org/publicdomain/zero/1.0/legalcode.fr" else: licence = str(_("Tous droits réservés")) licence_logo = licences["copyright"] licence_url = "" replacement_image_url = str(settings.MEDIA_ROOT) if not replacement_image_url.endswith("/") and settings.MEDIA_URL.endswith("/"): replacement_image_url += "/" elif replacement_image_url.endswith("/") and not settings.MEDIA_URL.endswith("/"): replacement_image_url = replacement_image_url[:-1] content, __, messages = render_markdown( md_flat_content, output_format="texfile", # latex template arguments content_type=content_type, title=title, authors=authors, license=licence, license_directory=str(LICENSES_BASE_PATH), license_logo=licence_logo, license_url=licence_url, smileys_directory=str(SMILEYS_BASE_PATH / "svg"), images_download_dir=str(base_directory / "images"), local_url_to_local_path=[settings.MEDIA_URL, replacement_image_url], ) if content == "" and messages: raise FailureDuringPublication("Markdown was not parsed due to {}".format(messages)) zmd_class_dir_path = Path(settings.ZDS_APP["content"]["latex_template_repo"]) if zmd_class_dir_path.exists() and zmd_class_dir_path.is_dir(): with contextlib.suppress(FileExistsError): zmd_class_link = base_directory / "zmdocument.cls" zmd_class_link.symlink_to(zmd_class_dir_path / "zmdocument.cls") luatex_dir_link = base_directory / "utf8.lua" luatex_dir_link.symlink_to(zmd_class_dir_path / "utf8.lua", target_is_directory=True) true_latex_extension = ".".join(self.extension.split(".")[:-1]) + ".tex" latex_file_path = base_name + true_latex_extension pdf_file_path = base_name + self.extension default_logo_original_path = Path(__file__).parent / ".." / ".." / "assets" / "images" / "*****@*****.**" with contextlib.suppress(FileExistsError): shutil.copy(str(default_logo_original_path), str(base_directory / "default_logo.png")) with open(latex_file_path, mode="w", encoding="utf-8") as latex_file: latex_file.write(content) shutil.copy2(latex_file_path, published_content_entity.get_extra_contents_directory()) self.full_tex_compiler_call(latex_file_path, draftmode="-draftmode") self.full_tex_compiler_call(latex_file_path, draftmode="-draftmode") self.make_glossary(base_name.split("/")[-1], latex_file_path) self.full_tex_compiler_call(latex_file_path) shutil.copy2(pdf_file_path, published_content_entity.get_extra_contents_directory())
def publish(self, md_file_path, base_name, **kwargs): md_flat_content = _read_flat_markdown(md_file_path) published_content_entity = self.get_published_content_entity( md_file_path) gallery_pk = published_content_entity.content.gallery.pk depth_to_size_map = { 1: 'small', # in fact this is an "empty" tutorial (i.e it is empty or has intro and/or conclusion) 2: 'small', 3: 'middle', 4: 'big' } public_versionned_source = published_content_entity.content\ .load_version(sha=published_content_entity.sha_public) base_directory = Path(base_name).parent image_dir = base_directory / 'images' with contextlib.suppress(FileExistsError): image_dir.mkdir(parents=True) if (settings.MEDIA_ROOT / 'galleries' / str(gallery_pk)).exists(): for image in (settings.MEDIA_ROOT / 'galleries' / str(gallery_pk)).iterdir(): with contextlib.suppress(OSError): shutil.copy2(str(image.absolute()), str(image_dir)) content_type = depth_to_size_map[ public_versionned_source.get_tree_level()] if self.latex_classes: content_type += ', ' + self.latex_classes title = published_content_entity.title() authors = [a.username for a in published_content_entity.authors.all()] licence = published_content_entity.content.licence.code licence_short = licence.replace('CC', '').strip().lower() licence_logo = licences.get(licence_short, False) if licence_logo: licence_url = 'https://creativecommons.org/licenses/{}/4.0/legalcode'.format( licence_short) # we need a specific case for CC-0 as it is "public-domain" if licence_logo == licences['0']: licence_url = 'https://creativecommons.org/publicdomain/zero/1.0/legalcode.fr' else: licence = str(_('Tous droits réservés')) licence_logo = licences['copyright'] licence_url = '' replacement_image_url = str(settings.MEDIA_ROOT) if not replacement_image_url.endswith( '/') and settings.MEDIA_URL.endswith('/'): replacement_image_url += '/' elif replacement_image_url.endswith( '/') and not settings.MEDIA_URL.endswith('/'): replacement_image_url = replacement_image_url[:-1] content, __, messages = render_markdown( md_flat_content, output_format='texfile', # latex template arguments content_type=content_type, title=title, authors=authors, license=licence, license_directory=str(LICENSES_BASE_PATH), license_logo=licence_logo, license_url=licence_url, smileys_directory=str(SMILEYS_BASE_PATH / 'svg'), images_download_dir=str(base_directory / 'images'), local_url_to_local_path=[ settings.MEDIA_URL, replacement_image_url ]) if content == '' and messages: raise FailureDuringPublication( 'Markdown was not parsed due to {}'.format(messages)) zmd_class_dir_path = Path( settings.ZDS_APP['content']['latex_template_repo']) if zmd_class_dir_path.exists() and zmd_class_dir_path.is_dir(): with contextlib.suppress(FileExistsError): zmd_class_link = base_directory / 'zmdocument.cls' zmd_class_link.symlink_to(zmd_class_dir_path / 'zmdocument.cls') luatex_dir_link = base_directory / 'utf8.lua' luatex_dir_link.symlink_to(zmd_class_dir_path / 'utf8.lua', target_is_directory=True) true_latex_extension = '.'.join( self.extension.split('.')[:-1]) + '.tex' latex_file_path = base_name + true_latex_extension pdf_file_path = base_name + self.extension default_logo_original_path = Path( __file__ ).parent / '..' / '..' / 'assets' / 'images' / '*****@*****.**' with contextlib.suppress(FileExistsError): shutil.copy(str(default_logo_original_path), str(base_directory / 'default_logo.png')) with open(latex_file_path, mode='w', encoding='utf-8') as latex_file: latex_file.write(content) shutil.copy2(latex_file_path, published_content_entity.get_extra_contents_directory()) self.full_tex_compiler_call(latex_file_path, draftmode='-draftmode') self.full_tex_compiler_call(latex_file_path, draftmode='-draftmode') self.make_glossary(base_name.split('/')[-1], latex_file_path) self.full_tex_compiler_call(latex_file_path) shutil.copy2(pdf_file_path, published_content_entity.get_extra_contents_directory())
def publish(self, md_file_path, base_name, **kwargs): md_flat_content = _read_flat_markdown(md_file_path) published_content_entity = self.get_published_content_entity(md_file_path) gallery_pk = published_content_entity.content.gallery.pk depth_to_size_map = { 1: 'small', # in fact this is an "empty" tutorial (i.e it is empty or has intro and/or conclusion) 2: 'small', 3: 'middle', 4: 'big' } public_versionned_source = published_content_entity.content\ .load_version(sha=published_content_entity.sha_public) base_directory = Path(base_name).parent image_dir = base_directory / 'images' with contextlib.suppress(FileExistsError): image_dir.mkdir(parents=True) if Path(settings.MEDIA_ROOT, 'galleries', str(gallery_pk)).exists(): for image in Path(settings.MEDIA_ROOT, 'galleries', str(gallery_pk)).iterdir(): with contextlib.suppress(OSError): shutil.copy2(str(image.absolute()), str(image_dir)) content_type = depth_to_size_map[public_versionned_source.get_tree_level()] if self.latex_classes: content_type += ', ' + self.latex_classes title = published_content_entity.title() authors = [a.username for a in published_content_entity.authors.all()] smileys_directory = SMILEYS_BASE_PATH licence = published_content_entity.content.licence.code licence_short = licence.replace('CC', '').strip().lower() licence_logo = licences.get(licence_short, False) if licence_logo: licence_url = 'https://creativecommons.org/licenses/{}/4.0/legalcode'.format(licence_short) else: licence = str(_('Tous droits réservés')) licence_logo = licences['copyright'] licence_url = '' replacement_image_url = settings.MEDIA_ROOT if not replacement_image_url.endswith('/') and settings.MEDIA_URL.endswith('/'): replacement_image_url += '/' elif replacement_image_url.endswith('/') and not settings.MEDIA_URL.endswith('/'): replacement_image_url = replacement_image_url[:-1] content, __, messages = render_markdown( md_flat_content, output_format='texfile', # latex template arguments content_type=content_type, title=title, authors=authors, license=licence, license_directory=LICENSES_BASE_PATH, license_logo=licence_logo, license_url=licence_url, smileys_directory=smileys_directory, images_download_dir=str(base_directory / 'images'), local_url_to_local_path=[settings.MEDIA_URL, replacement_image_url] ) if content == '' and messages: raise FailureDuringPublication('Markdown was not parsed due to {}'.format(messages)) zmd_class_dir_path = Path(settings.ZDS_APP['content']['latex_template_repo']) if zmd_class_dir_path.exists() and zmd_class_dir_path.is_dir(): with contextlib.suppress(FileExistsError): zmd_class_link = base_directory / 'zmdocument.cls' zmd_class_link.symlink_to(zmd_class_dir_path / 'zmdocument.cls') luatex_dir_link = base_directory / 'utf8.lua' luatex_dir_link.symlink_to(zmd_class_dir_path / 'utf8.lua', target_is_directory=True) true_latex_extension = '.'.join(self.extension.split('.')[:-1]) + '.tex' latex_file_path = base_name + true_latex_extension pdf_file_path = base_name + self.extension default_logo_original_path = Path(__file__).parent / '..' / '..' / 'assets' / 'images' / 'logo.png' with contextlib.suppress(FileExistsError): shutil.copy(str(default_logo_original_path), str(base_directory / 'default_logo.png')) with open(latex_file_path, mode='w', encoding='utf-8') as latex_file: latex_file.write(content) try: self.full_tex_compiler_call(latex_file_path) self.full_tex_compiler_call(latex_file_path) self.make_glossary(base_name.split('/')[-1], latex_file_path) self.full_tex_compiler_call(latex_file_path) except FailureDuringPublication: logging.getLogger(self.__class__.__name__).exception('could not publish %s', base_name + self.extension) else: shutil.copy2(latex_file_path, published_content_entity.get_extra_contents_directory()) shutil.copy2(pdf_file_path, published_content_entity.get_extra_contents_directory()) logging.info('published latex=%s, pdf=%s', published_content_entity.has_type('tex'), published_content_entity.has_type(self.doc_type))