def body_to_stream(self, body): soup = BeautifulSoup(body, "html.parser") parsed_body = "" stream = [] for el in soup: if el.name == "div" and el.get("class") == ["wc-gallery"]: # dump existing parsed body into stream if len(parsed_body) > 0: stream.append(("paragraph", RichText(parsed_body))) parsed_body = "" gallery_photos = [] for item in el.find_all(class_="gallery-item"): url = item.find("a").get("href") caption = item.parent.find(class_="gallery-caption") if caption is None: self.stderr.write( "unable to determine photographer in gallery") raise Exception() photographer = str(caption.p)[3:-4].strip() image = self.get_or_create_image(url, photographer) gallery_photos.append({"image": image}) stream.append(("photo_gallery", gallery_photos)) else: if hasattr(el, "stripped_strings") and any( el.stripped_strings): parsed_body += str(el) return stream + [("paragraph", RichText(parsed_body))]
def migrate_standardpage_intro_and_body_to_streamfield(apps, schema_editor): StandardPage = apps.get_model("torchbox.StandardPage") stream_block = StandardPage._meta.get_field("streamfield").stream_block # Append body to beginning of streamfield for page in StandardPage.objects.exclude(body__in=["", "<p></p>", "<p><br/></p>"]): # Add body as first block so it appears in the same place on the template page.streamfield = StreamValue( stream_block, [("paragraph", RichText(page.body), str(uuid3(UUID_NAMESPACE, page.body))),] + [(child.block_type, child.value, child.id) for child in page.streamfield], ) page.save() # Append intro to beginning of streamfield for page in StandardPage.objects.exclude(intro__in=["", "<p></p>", "<p><br/></p>"]): # Add intro as first block so it appears in the same place on the template page.streamfield = StreamValue( stream_block, [ ( "paragraph", RichText(page.intro), str(uuid3(UUID_NAMESPACE, page.intro)), ), ] + [(child.block_type, child.value, child.id) for child in page.streamfield], ) page.save()
def convert_rich_text(source, request, absolute): try: from bs4 import BeautifulSoup except ImportError: return str(RichText(source)) soup = BeautifulSoup(source, 'html5lib') # Make document links absolute. for anchor in soup.find_all('a'): if anchor.attrs.get('linktype', '') == 'document': try: doc = get_document_model().objects.get(pk=anchor.attrs['id']) new_tag = soup.new_tag( 'a', href=resolve_absolute_url(doc.url, request, absolute=absolute)) new_tag.append(*anchor.contents) anchor.replace_with(new_tag) except get_document_model().DoesNotExist: new_tag = soup.new_tag('a') new_tag.append(*anchor.contents) anchor.replace_with(new_tag) return str(RichText(str(soup)))
def convert_to_streamfield(apps, schema_editor): FormPage = apps.get_model('main', 'FormPage') for page in FormPage.objects.all(): if page.body.raw_text and not page.body: page.body = [('paragraph', RichText(page.body.raw_text))] page.save() if page.thank_you_text.raw_text and not page.thank_you_text: page.thank_you_text = [('paragraph', RichText(page.thank_you_text.raw_text))] page.save()
def parse_article_body_blocks(body): article_body_blocks = [] try: soup = BeautifulSoup(body, "html.parser") except: soup = False print("Could not parse article body:", body) # Placeholder for gathering successive items rich_text_value = "" if soup: for item in soup: item_has_value = item.string is not None if item_has_value: item_contains_pullquote = "pullquote" in item.string if item_contains_pullquote: # Add current rich text value as paragraph block, if not empty if rich_text_value != "": rich_text_block = ("paragraph", RichText(rich_text_value)) article_body_blocks.append(rich_text_block) # reset rich text value rich_text_value = "" pullquotes = extract_pullquotes(item) # Add Pullquote block(s) to body streamfield # so they appear above the related rich text field # i.e. near the paragraph containing the pullquote for pullquote in pullquotes: block_content = ("pullquote", pullquote) article_body_blocks.append(block_content) item = clean_pullquote_tags(item) rich_text_value += str(item) if rich_text_value != "": # Add Paragraph Block with remaining rich text elements rich_text_block = ("paragraph", RichText(rich_text_value)) article_body_blocks.append(rich_text_block) return article_body_blocks
class ResourceFactory(factory.django.DjangoModelFactory): class Meta: model = Resource path = factory.Sequence( lambda n: u'00010{}'.format(n)) # from wagtailcore_pagerevision depth = 3 numchild = 0 title = 'Dummy Resource Page' slug = factory.LazyAttribute(lambda obj: slugify(obj.title)) live = True has_unpublished_changes = False show_in_menus = False search_description = '' go_live_at = '1995-02-07 12:00' expire_at = '2050-12-31 12:43' expired = False content_type = factory.SubFactory(ContentTypeFactory, app_label="resource", model="resource") locked = False latest_revision_created_at = '1995-02-07 12:00' first_published_at = '1995-02-07 12:00' language = 'en' content = [ ('authors', RichText('<p>XYZ</p>')), ('copyright', RichText( '<p>XYZ, Professor, Centre for Economic Studies and Planning</p><p>\u00a0</p>' )), ('focus', RichText( '<p>The 2008 global food price fluctuations -- especially the policies on bio-fuel and the neglect of agriculture.</p>' )), ('factoids', RichText( '<p>Lack of public investment in agriculture and agriculture research .</p>' )) ] @classmethod def _setup_next_sequence(cls): return getattr(cls, 'starting_sequence_num', 20) @factory.post_generation def categories(self, create, extracted, **kwargs): if not create: return if extracted: for category in extracted: self.categories.add(category)
def run_migration(apps, schema_editor): BlogPage = apps.get_model('blog', 'BlogPage') for page in BlogPage.objects.all(): if page.body.raw_text and not page.body: if page.body.raw_text not in ['', '<p></p>']: page.body = [('richtext', RichText(page.body.raw_text))] page.save() if page.body_de.raw_text and not page.body_de: if page.body_de.raw_text not in ['', '<p></p>']: page.body_de = [('richtext', RichText(page.body_de.raw_text))] page.save()
def parse_block(b, figure_index): val = None if b['type'] == 'paragraph': val = ('paragraph', RichText(b['html'])) elif b['type'] == 'heading': val = ('heading', b['text']) elif b['type'] == 'table': val = ('table', b['data']) elif b['type'] == 'inline_image': val = ('paragraph', RichText('<em>[[Figure %s]]</em>' % figure_index['i'])) figure_index['i'] += 1 return val
def test_render(self): text = '<p>To the <a linktype="page" id="{}">moon</a>!</p>'.format( self.single_event_page.id) value = RichText(text) result = str(value) expected = ('<p>To the <a href="/foo/pointless-suffix/">moon</a>!</p>') self.assertEqual(result, expected)
def convert_data(apps, schema_editor): for name in ['intro', 'thank_you_text']: FormPage = apps.get_model('teratree', 'FormPage') for form in FormPage.objects.all(): print('\n', form.title) # edit the live form page if getattr(form, name).raw_text and not getattr(form, name): setattr(form, name, [('paragraph', RichText(getattr(form, name).raw_text))]) print('Updated ' + form.title) form.save() # # edit drafts associated with post # if form.has_unpublished_changes: print(form.title + ' has revisions...') for rev in form.revisions.all(): data = json.loads(rev.content_json) value = data[name] print(value) print('This is current JSON:', data, '\n') data[name] = json.dumps([{ "type": "paragraph", "value": value }]) rev.content_json = json.dumps(data) print('This is updated JSON:', rev.content_json, '\n') rev.save() print('Completed ' + form.title + '.' + '\n')
def handle_block(self, block_type, block_value, segments): if hasattr(block_type, "restore_translated_segments"): return block_type.restore_translated_segments( block_value, segments) elif isinstance(block_type, (blocks.CharBlock, blocks.TextBlock)): return segments[0].render_text() elif isinstance(block_type, blocks.RichTextBlock): format, template, strings = organise_template_segments(segments) assert format == "html" return RichText(restore_strings(template, strings)) elif isinstance(block_type, blocks.ChooserBlock): return self.handle_related_object_block(block_value, segments) elif isinstance(block_type, blocks.StructBlock): return self.handle_struct_block(block_value, segments) elif isinstance(block_type, blocks.ListBlock): return self.handle_list_block(block_value, segments) elif isinstance(block_type, blocks.StreamBlock): return self.handle_stream_block(block_value, segments) else: raise Exception( "Unrecognised StreamField block type '{}'. Have you implemented restore_translated_segments() on this class?" .format(block_type.__class__.__name__))
def publish_page(page, page_parent, default_body_string, user): ''' Helper function for publishing a manually created Wagtail Page object. ''' logger.debug('publishing page: {}'.format(page)) paragraph = '<p>{}</p>'.format(default_body_string) page.body = [('paragraph', RichText(paragraph))] # only set page parent if handling newly created page # if page_parent is not None: if page.get_parent() is None: logger.debug('Set parent: {} for {}'.format(page_parent, page)) try: page_parent.add_child(instance=page) except ValidationError: slug_string = '{}-{}'.format(page.slug, page.id) page.slug = slugify(slug_string) logger.debug('page slug set to: {}'.format(page.slug)) page_parent.add_child(instance=page) revision = page.save_revision( user=user, submitted_for_moderation=False, ) revision.publish() page.save()
def convert_to_streamfield(apps, schema_editor): Concert = apps.get_model('main', 'Concert') for page in Concert.objects.all(): if page.description.raw_text and not page.description: page.description = [('paragraph', RichText(page.description.raw_text))] page.save()
def handle_block(self, block_type, block_value, segments): # Need to check if the app is installed before importing EmbedBlock # See: https://github.com/wagtail/wagtail-localize/issues/309 if apps.is_installed("wagtail.embeds"): from wagtail.embeds.blocks import EmbedBlock, EmbedValue if isinstance(block_type, EmbedBlock): if len(segments) > 1: raise ValueError( "EmbedBlock can only have a single segment. Found {}". format(len(segments))) segment = segments[0] if isinstance(segment, OverridableSegmentValue): return EmbedValue(segment.data) if hasattr(block_type, "restore_translated_segments"): return block_type.restore_translated_segments( block_value, segments) elif isinstance( block_type, (blocks.CharBlock, blocks.TextBlock, blocks.URLBlock, blocks.EmailBlock), ): if len(segments) > 1: raise ValueError( "TextBlock/CharBlock can only have a single segment. Found {}" .format(len(segments))) segment = segments[0] if isinstance(segment, OverridableSegmentValue): return segment.data else: # Assume it's a StringSegmentValue return segment.render_text() elif isinstance(block_type, blocks.RichTextBlock): format, template, strings = organise_template_segments(segments) assert format == "html" return RichText(restore_strings(template, strings)) elif isinstance(block_type, blocks.ChooserBlock): return self.handle_related_object_block(block_value, segments) elif isinstance(block_type, blocks.StructBlock): return self.handle_struct_block(block_value, segments) elif isinstance(block_type, blocks.ListBlock): return self.handle_list_block(block_value, segments) elif isinstance(block_type, blocks.StreamBlock): return self.handle_stream_block(block_value, segments) else: raise Exception( "Unrecognised StreamField block type '{}'. Have you implemented restore_translated_segments() on this class?" .format(block_type.__class__.__name__))
def test_render(self): value = RichText('<p>Merry <a linktype="page" id="4">Christmas</a>!</p>') result = str(value) self.assertEqual( result, '<div class="rich-text"><p>Merry <a href="/events/christmas/">Christmas</a>!</p></div>' )
def test_add_block_to_live_post_streamchild(blog_page_factory): live_posts = json.dumps([ { "type": "live_post", "id": "some-id", "value": { "message_id": "some-id", "created": "2021-01-01T12:00:00", "modified": "2021-01-01T12:00:00", "show": True, "content": [], }, }, ]) page = blog_page_factory(channel_id="some-id", live_posts=live_posts) live_post = page.get_live_post_by_index(live_post_index=0) text_block = construct_text_block(text="Some text") add_block_to_live_post(TEXT, text_block, live_post) assert isinstance(live_post, StreamValue.StreamChild) setattr(RichText, "__eq__", lambda self, other: self.source == other.source) assert live_post.value["content"] == StreamValue( ContentBlock(), [("text", RichText("Some text"))])
def test_that_article_with_no_recommended_articles_should_be_valid(self): author = BossFactory() body_block = StreamBlock([(ArticleBodyBlockNames.PARAGRAPH.value, RichTextBlock())]) body = StreamValue(body_block, [ (ArticleBodyBlockNames.PARAGRAPH.value, RichText("Hello, World")) ]) test_title = "Simple Article Title" blog_article_parameter = { "title": test_title, "page_title": test_title, "date": datetime.now(), "body": body, "author": author, "read_time": 7, "views": 0, } articles_block = StreamBlock([("page", PageChooserBlock())]) blog_article_page = BlogArticlePage(**blog_article_parameter) self.blog_index_page.add_child(instance=blog_article_page) blog_article_page.save() self.assertEqual(blog_article_page.recommended_articles, StreamValue(articles_block, []))
def get_rich_text_body(row): body = [] row = row.splitlines() for line in row: if len(line) != 0: body = body + [("paragraph", RichText(line))] return body
def migrate_blog_body_to_streamfield(apps, schema_editor): BlogPage = apps.get_model("blog.BlogPage") stream_block = BlogPage._meta.get_field("streamfield").stream_block # Update model for blog_page in ( BlogPage.objects.exclude(body="") .exclude(body="<p></p>") .exclude(body="<p><br/></p>") ): # Add body as first block so it appears in the same place on the template blog_page.streamfield = StreamValue( stream_block, [ ( "paragraph", RichText(blog_page.body), str(uuid3(UUID_NAMESPACE, blog_page.body)), ), ] + [ (child.block_type, child.value, child.id) for child in blog_page.streamfield ], ) blog_page.save() # Update revisions PageRevision = apps.get_model("wagtailcore.PageRevision") ContentType = apps.get_model("contenttypes.ContentType") blog_page_content_type = ContentType.objects.get(app_label="blog", model="blogpage") for revision in PageRevision.objects.filter( page__content_type=blog_page_content_type ): content = json.loads(revision.content_json) if content["body"] and content["body"] not in ["<p></p>", "<p><br/></p>"]: streamfield_json = content.get("streamfield", "") if streamfield_json: streamfield = json.loads(streamfield_json) else: streamfield = [] streamfield.insert( 0, { "type": "paragraph", "value": content["body"], "id": str(uuid3(UUID_NAMESPACE, content["body"])), }, ) content["streamfield"] = json.dumps(streamfield) revision.content_json = json.dumps(content) revision.save()
def _convert_to_streamfield(apps, schema_editor, field_name): RecordEntry = apps.get_model('cms', 'RecordEntry') for page in RecordEntry.objects.all(): field = getattr(page, field_name) if field.raw_text and not field: setattr(page, field_name, [('text', RichText(field.raw_text))]) page.save()
def setUpTestData(cls): # Cart page cls.cart = ShoppingCartPage.objects.first() # Sample score page parent = ScoreListingPage.objects.first() with open(join(settings.BASE_DIR, 'data/pdf_example.pdf'), 'rb') as f: pdf_file_one = InMemoryUploadedFile(file=f, field_name=None, name='pdf_example.pdf', size=f.tell, content_type='application/pdf', charset=None, content_type_extra=None) s1 = ScorePage(title='Example Score One', cover_image=None, description=[('rich_text', RichText('<p>Description text</p>'))], duration='00:05:43', price='14.99', file=pdf_file_one, preview_score=pdf_file_one) parent.add_child(instance=s1) s1.save_revision().publish() with open(join(settings.BASE_DIR, 'data/pdf_example.pdf'), 'rb') as f: pdf_file_two = InMemoryUploadedFile(file=f, field_name=None, name='pdf_example.pdf', size=f.tell, content_type='application/pdf', charset=None, content_type_extra=None) s2 = ScorePage(title='Example Score Two', cover_image=None, description=[('rich_text', RichText('<p>Description text</p>'))], duration='00:05:43', price='14.99', file=pdf_file_two, preview_score=pdf_file_two) parent.add_child(instance=s2) s2.save_revision().publish() cls.scores = [s1, s2]
def setUp(self): super().setUp() # Create Blog self.blog_page = BlogPageFactory(body=[ ("heading", "Test heading 1"), ("paragraph", RichText("This is a paragraph.")), ("heading", "Test heading 2"), ("image", wagtail_factories.ImageFactory()), ("decimal", decimal.Decimal(1.2)), ("date", datetime.date.today()), ("datetime", datetime.datetime.now()), ( "carousel", StreamValue( stream_block=CarouselBlock(), stream_data=[ ("image", wagtail_factories.ImageChooserBlockFactory()), ("image", wagtail_factories.ImageChooserBlockFactory()), ], ), ), ( "gallery", { "title": "Gallery title", "images": StreamValue( stream_block=ImageGalleryImages(), stream_data=[ ( "image", { "image": wagtail_factories.ImageChooserBlockFactory( ) }, ), ( "image", { "image": wagtail_factories.ImageChooserBlockFactory( ) }, ), ], ), }, ), ("objectives", ["Read all of article!"]), ("video", { "youtube_link": EmbedValue("https://youtube.com/") }), ])
def setUp(self): self.title = 'Awesome' self.body = 'Cool' self.source_warning = 'Alert' self.directory = DirectoryPageFactory( title=self.title, body=RichText(self.body), source_warning=self.source_warning) self.search_content = self.directory.get_search_content()
class HeroTextBlockFactory(wagtail_factories.StructBlockFactory): background_image = factory.LazyAttribute(get_an_image) text = factory.LazyAttribute( lambda x: RichText(fake.text(max_nb_chars=20))) button_link = factory.LazyAttribute(get_a_page) button_text = factory.LazyAttribute(fake_title) class Meta: model = HeroTextBlock
def setUp(self): self.campaign = SaleCampaign.objects.create( name='Test campaign', subject='The subject', body=[ ('rich_text', RichText('<p>The body</p>')) ] ) self.login()
def generate_article_body() -> StreamValue: return StreamValue( StreamBlock([("paragraph", RichTextBlock())]), [( "paragraph", RichText((lambda text, tag: f"<{tag}>{text}</{tag}>")( faker.paragraph(nb_sentences=random.randint(30, 100)), "p")), ) for _ in range(random.randint(2, 7))], )
def test_legend_display(self): """Verify that the legend appears if set.""" new_legend = "<h3>ZELDA</h3>" self.settings.legend = [('rich_text', RichText(new_legend))] self.settings.save() response = self.client.get(reverse("schedule_conference")) self.assertContains(response, new_legend) self.assertNotContains(response, "<p>View past PyData event schedules")
class FacultyBlockFactory(wagtail_factories.StructBlockFactory): """FacultyBlock factory class""" name = factory.Faker("name") image = factory.SubFactory(wagtail_factories.ImageFactory) text = factory.LazyFunction( lambda: RichText("<p>{}</p>".format(FAKE.paragraph()))) class Meta: model = FacultyBlock
def test_can_assign_as_list(self): self.json_body.body = [('rich_text', RichText("<h2>hello world</h2>"))] self.json_body.save() # the body should now be a stream consisting of a single rich_text block fetched_body = StreamModel.objects.get(id=self.json_body.id).body self.assertIsInstance(fetched_body, StreamValue) self.assertEqual(len(fetched_body), 1) self.assertIsInstance(fetched_body[0].value, RichText) self.assertEqual(fetched_body[0].value.source, "<h2>hello world</h2>")
def convert_to_streamfield(apps, schema_editor): ContentPage = apps.get_model('home', 'ContentPage') for page in ContentPage.objects.all(): # That page.body could be "false-y" yet have a raw_text attribute seems # weird; this is an intentional design choice by Wagtail meant to # simplify migrations from RichTextField to StreamField. if page.body.raw_text and not page.body: page.body = [ ('rich_text', RichText(page.body.raw_text)), ] page.save()