From 357f0cdd18ab242eb68550751f441783f4a6c1d3 Mon Sep 17 00:00:00 2001 From: MrTango Date: Thu, 11 Apr 2024 10:50:56 +0300 Subject: [PATCH 1/8] add more info about RichText field and the usage of transforms and RichTextValue --- docs/backend/fields.md | 111 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/docs/backend/fields.md b/docs/backend/fields.md index 735fd74c3..2c5c01267 100644 --- a/docs/backend/fields.md +++ b/docs/backend/fields.md @@ -115,6 +115,8 @@ See [`z3c.relationfield`](https://pypi.org/project/z3c.relationfield/) for more | `RelationChoice` | `RelationValue` | A `Choice` field intended to store `RelationValue`s | See {ref}`Choice ` | +(backend-fields-richtext-label)= + ### Fields in `plone.app.textfield` See [`plone.app.textfield`](https://pypi.org/project/plone.app.textfield/) for more details. @@ -124,6 +126,115 @@ See [`plone.app.textfield`](https://pypi.org/project/plone.app.textfield/) for m | `RichText` | `RichTextValue` | Stores a `RichTextValue`, which encapsulates a raw text value, the source MIME type, and a cached copy of the raw text transformed to the default output MIME type. | `IField`, `IRichText` | +The RichText field allows for alternative markups and content filtering. + +```python +from plone.app.textfield import RichText +from plone.supermodel import model + +class ITestSchema(model.Schema): + + body = RichText(title="Body text") +``` + +The `RichText` field constructor can take the following arguments, in addition to the usual arguments for a `Text` field. + +`default_mime_type` +: A string representing the default MIME type of the input markup. + This defaults to `text/html`. + +`output_mime_type` +: A string representing the default output MIME type. + This defaults to `text/x-html-safe`, which is a Plone-specific MIME type that disallows certain tags. + Use the {guilabel}`HTML Filtering` control panel in Plone to control the tags. + +`allowed_mime_types` +: A tuple of strings giving a vocabulary of allowed input MIME types. + If this is `None` (the default), the allowable types will be restricted to those set in Plone's {guilabel}`Markup` control panel. + +The *default* field can be set to either a Unicode object (in which case it will be assumed to be a string of the default MIME type) or a {ref}`backend-fields-richtextvalue-label`. + + +#### reStructuredText transformation + +Below is an example of a field that allows StructuredText and reStructuredText, transformed to HTML by default. + +```python +from plone.app.textfield import RichText +from plone.supermodel import model + +defaultBody = """\ +Background +========== + +Please fill this in + +Details +------- + +And this +""" + +class ITestSchema(model.Schema): + + body = RichText( + title="Body text", + default_mime_type='text/x-rst', + output_mime_type='text/x-html', + allowed_mime_types=('text/x-rst', 'text/structured',), + default=defaultBody, + ) +``` + + +(backend-fields-richtextvalue-label)= + +#### RichTextValue + +The `RichText` field, most of the time does not store a string. +Instead, it stores a `RichTextValue` object. +This is an immutable object that has the following properties. + +`raw` +: A Unicode string with the original input markup. + +`mimeType` +: The MIME type of the original markup, for example `text/html` or `text/structured`. + +`encoding` +: The default character encoding used when transforming the input markup. + Most likely, this will be UTF-8. + +`raw_encoded` +: The raw input encoded in the given encoding. + +`outputMimeType` +: The MIME type of the default output, taken from the field at the time of instantiation. + +`output` +: A Unicode object representing the transformed output. + If possible, this is cached persistently, until the `RichTextValue` is replaced with a new one (as happens when an edit form is saved, for example). + +The storage of the `RichTextValue` object is optimized for the case where the transformed output will be read frequently (for example, on the view screen of the content object) and the raw value will be read infrequently (for example, on the edit screen). +Because the output value is cached indefinitely, you will need to replace the `RichTextValue` object with a new one if any of the transformation parameters change. +However, as we will see below, it is possible to apply a different transformation on demand, if you need to. + +The code snippet below shows how a `RichTextValue` object can be constructed in code. +In this case, we have a raw input string of type `text/plain` that will be transformed to a default output of `text/html`. +Note that we would normally look up the default output type from the field instance. + +```python +from plone.app.textfield.value import RichTextValue + +context.body = RichTextValue("Some input text", 'text/plain', 'text/html') +``` + +The standard widget used for a `RichText` field will correctly store this type of object for you, so it is rarely necessary to create one yourself. + + + + + ### Fields in `plone.schema` See {ref}`backend-ploneschema-label` for more details. From f8fa0caa7b27914f1f641c2f4b44cd7a39dcb936 Mon Sep 17 00:00:00 2001 From: MrTango Date: Thu, 11 Apr 2024 10:58:11 +0300 Subject: [PATCH 2/8] add: "Using RichText fields in templates" and "Alternative transformations" --- docs/backend/fields.md | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/docs/backend/fields.md b/docs/backend/fields.md index 2c5c01267..457412fbf 100644 --- a/docs/backend/fields.md +++ b/docs/backend/fields.md @@ -232,6 +232,62 @@ context.body = RichTextValue("Some input text", 'text/plain', 'text/html') The standard widget used for a `RichText` field will correctly store this type of object for you, so it is rarely necessary to create one yourself. +##### Using RichText fields in templates + +If you use a `DisplayForm`, the display widget for the `RichText` field will render the transformed output markup automatically. +If you write TAL manually, you may try something like the following. + +```xml +
+``` + +This, however, will render a string as follows. + +```html +RichTextValue object. (Did you mean .raw or .output?) +``` + +The correct syntax is: + +```xml +
+``` + +This will render the cached, transformed output. +This operation is approximately as efficient as rendering a simple `Text` field, since the transformation is only applied once, when the value is first saved. + + +##### Alternative transformations + +Sometimes, you may want to invoke alternative transformations. +Under the hood, the default implementation uses the `portal_transforms` tool to calculate a transform chain from the raw value's input MIME type to the desired output MIME type. +If you need to write your own transforms, take a look at [this tutorial](https://5.docs.plone.org/develop/plone/misc/portal_transforms.html). +This is abstracted behind an `ITransformer` adapter to allow alternative implementations. + +To invoke a transformation in code, you can use the following syntax. + +```python +from plone.app.textfield.interfaces import ITransformer + +transformer = ITransformer(context) +transformedValue = transformer(context.body, 'text/plain') +``` + +The `__call__()` method of the `ITransformer` adapter takes a `RichTextValue` object and an output MIME type as parameters. + +If you write a page template, there is an even more convenient syntax. + +```xml +
+``` + +The first traversal name gives the name of the field on the context (`body` in this case). +The second and third give the output MIME type. +If the MIME type is omitted, the default output MIME type will be used. + +```{note} +Unlike the `output` property, the value is not cached, and so will be calculated each time the page is rendered. +``` From 12ec16bf0a708c64fff95e7cc8ba646dd4995791 Mon Sep 17 00:00:00 2001 From: MrTango Date: Thu, 11 Apr 2024 15:29:09 +0300 Subject: [PATCH 3/8] fix RichTextValue paramters --- docs/backend/fields.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/backend/fields.md b/docs/backend/fields.md index 457412fbf..084e342e9 100644 --- a/docs/backend/fields.md +++ b/docs/backend/fields.md @@ -226,7 +226,7 @@ Note that we would normally look up the default output type from the field insta ```python from plone.app.textfield.value import RichTextValue -context.body = RichTextValue("Some input text", 'text/plain', 'text/html') +context.body = RichTextValue("Some input text", mimeType="text/html", outputMimeType="text/x-html-safe") ``` The standard widget used for a `RichText` field will correctly store this type of object for you, so it is rarely necessary to create one yourself. From 3ec56fdddc11dc14eee26e9a3aaa5ae386f6d730 Mon Sep 17 00:00:00 2001 From: MrTango Date: Thu, 11 Apr 2024 21:34:13 +0300 Subject: [PATCH 4/8] describe how to use NamedBlobImage --- docs/backend/fields.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/backend/fields.md b/docs/backend/fields.md index 084e342e9..28d438974 100644 --- a/docs/backend/fields.md +++ b/docs/backend/fields.md @@ -103,6 +103,25 @@ See [`plone.namedfile`](https://pypi.org/project/plone.namedfile/) and [plone.fo | `NamedBlobFile` | `NamedBlobFile` | A binary uploaded file stored as a ZODB blob. Requires the `blobs` extra to `plone.namedfile`. Otherwise identical to `NamedFile`. | `IField` | | `NamedBlobImage` | `NamedBlobImage` | A binary uploaded image stored as a ZODB blob. Requires the `blobs` extra to `plone.namedfile`. Otherwise identical to `NamedImage`. | `IField` | +#### NamedBlobImage + +The following example shows, how to create an Image object and attach the image file to it. + +```python +img_obj = api.content.create( + container=ww_article, + type="Image", + id="test.jpg", +) +img_obj.image = NamedBlobImage( + data=img_file, + filename="test.jpg", +) +``` + + + + ### Fields in `z3c.relationfield.schema` From fbab5c718487986f893eb46f11c6e8fe59f49e1a Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sun, 28 Apr 2024 13:52:48 -0700 Subject: [PATCH 5/8] Apply suggestions from code review --- docs/backend/fields.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/backend/fields.md b/docs/backend/fields.md index 28d438974..ca669384c 100644 --- a/docs/backend/fields.md +++ b/docs/backend/fields.md @@ -103,9 +103,9 @@ See [`plone.namedfile`](https://pypi.org/project/plone.namedfile/) and [plone.fo | `NamedBlobFile` | `NamedBlobFile` | A binary uploaded file stored as a ZODB blob. Requires the `blobs` extra to `plone.namedfile`. Otherwise identical to `NamedFile`. | `IField` | | `NamedBlobImage` | `NamedBlobImage` | A binary uploaded image stored as a ZODB blob. Requires the `blobs` extra to `plone.namedfile`. Otherwise identical to `NamedImage`. | `IField` | -#### NamedBlobImage +#### `NamedBlobImage` -The following example shows, how to create an Image object and attach the image file to it. +The following example shows how to create an `Image` object, and attach the image file to it. ```python img_obj = api.content.create( @@ -145,7 +145,8 @@ See [`plone.app.textfield`](https://pypi.org/project/plone.app.textfield/) for m | `RichText` | `RichTextValue` | Stores a `RichTextValue`, which encapsulates a raw text value, the source MIME type, and a cached copy of the raw text transformed to the default output MIME type. | `IField`, `IRichText` | -The RichText field allows for alternative markups and content filtering. +The `RichText` field allows for alternative markups and content filtering. +The following code sample shows how to create a schema with a `RichText` field. ```python from plone.app.textfield import RichText @@ -208,9 +209,9 @@ class ITestSchema(model.Schema): (backend-fields-richtextvalue-label)= -#### RichTextValue +#### `RichTextValue` -The `RichText` field, most of the time does not store a string. +The `RichText` field usually does not store a string. Instead, it stores a `RichTextValue` object. This is an immutable object that has the following properties. @@ -218,7 +219,7 @@ This is an immutable object that has the following properties. : A Unicode string with the original input markup. `mimeType` -: The MIME type of the original markup, for example `text/html` or `text/structured`. +: The MIME type of the original markup, for example, `text/html` or `text/structured`. `encoding` : The default character encoding used when transforming the input markup. @@ -251,7 +252,7 @@ context.body = RichTextValue("Some input text", mimeType="text/html", outputMime The standard widget used for a `RichText` field will correctly store this type of object for you, so it is rarely necessary to create one yourself. -##### Using RichText fields in templates +##### `RichText` fields in templates If you use a `DisplayForm`, the display widget for the `RichText` field will render the transformed output markup automatically. If you write TAL manually, you may try something like the following. @@ -280,7 +281,7 @@ This operation is approximately as efficient as rendering a simple `Text` field, Sometimes, you may want to invoke alternative transformations. Under the hood, the default implementation uses the `portal_transforms` tool to calculate a transform chain from the raw value's input MIME type to the desired output MIME type. -If you need to write your own transforms, take a look at [this tutorial](https://5.docs.plone.org/develop/plone/misc/portal_transforms.html). +If you need to write your own transforms, take a look at [Changing Portal Transforms Settings via Python](https://5.docs.plone.org/develop/plone/misc/portal_transforms.html). This is abstracted behind an `ITransformer` adapter to allow alternative implementations. To invoke a transformation in code, you can use the following syntax. @@ -300,8 +301,8 @@ If you write a page template, there is an even more convenient syntax.
``` -The first traversal name gives the name of the field on the context (`body` in this case). -The second and third give the output MIME type. +The first traversal name segment gives the name of the field on the context (`body` in this case). +The second and third segments give the output MIME type. If the MIME type is omitted, the default output MIME type will be used. ```{note} From 0936804a47ca46a4b0d7b93035e94a9c16e26da6 Mon Sep 17 00:00:00 2001 From: MrTango Date: Wed, 8 May 2024 17:55:18 +0300 Subject: [PATCH 6/8] set image directly in create call --- docs/backend/fields.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/backend/fields.md b/docs/backend/fields.md index ca669384c..ed878afdf 100644 --- a/docs/backend/fields.md +++ b/docs/backend/fields.md @@ -112,10 +112,10 @@ img_obj = api.content.create( container=ww_article, type="Image", id="test.jpg", -) -img_obj.image = NamedBlobImage( - data=img_file, - filename="test.jpg", + image=NamedBlobImage( + data=img_file, + filename="test.jpg", + ) ) ``` From aaa3d17d7962a07934f82d474f22276fa89abeb2 Mon Sep 17 00:00:00 2001 From: MrTango Date: Wed, 8 May 2024 17:57:42 +0300 Subject: [PATCH 7/8] fix linting in code example --- docs/backend/fields.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/backend/fields.md b/docs/backend/fields.md index ed878afdf..d2dc8d4c8 100644 --- a/docs/backend/fields.md +++ b/docs/backend/fields.md @@ -199,9 +199,9 @@ class ITestSchema(model.Schema): body = RichText( title="Body text", - default_mime_type='text/x-rst', - output_mime_type='text/x-html', - allowed_mime_types=('text/x-rst', 'text/structured',), + default_mime_type="text/x-rst", + output_mime_type="text/x-html", + allowed_mime_types=("text/x-rst", "text/structured",), default=defaultBody, ) ``` From ce5009d5cce5d21daffc04a647a281d4adad5912 Mon Sep 17 00:00:00 2001 From: MrTango Date: Wed, 8 May 2024 17:59:28 +0300 Subject: [PATCH 8/8] linting --- docs/backend/fields.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/backend/fields.md b/docs/backend/fields.md index d2dc8d4c8..b7fa2ad71 100644 --- a/docs/backend/fields.md +++ b/docs/backend/fields.md @@ -290,7 +290,7 @@ To invoke a transformation in code, you can use the following syntax. from plone.app.textfield.interfaces import ITransformer transformer = ITransformer(context) -transformedValue = transformer(context.body, 'text/plain') +transformedValue = transformer(context.body, "text/plain") ``` The `__call__()` method of the `ITransformer` adapter takes a `RichTextValue` object and an output MIME type as parameters.