Skip to content

Commit

Permalink
Merge 079e06a into bfd78ce
Browse files Browse the repository at this point in the history
  • Loading branch information
jaroel committed Apr 17, 2018
2 parents bfd78ce + 079e06a commit 00fc558
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 21 deletions.
6 changes: 6 additions & 0 deletions CHANGES.rst
Expand Up @@ -4,6 +4,12 @@ Changelog
1.6.1 (unreleased)
------------------

- Expose the tagged values for widgets in the @types endpoint.
[jaroel]

- Render subject vocabulary as items for subjects field.
[jaroel]

Bugfixes:

- Add VHM support to @search
Expand Down
22 changes: 11 additions & 11 deletions docs/source/_json/types_document.resp
Expand Up @@ -92,7 +92,8 @@ Content-Type: application/json+schema
},
"title": "Contributors",
"type": "array",
"uniqueItems": true
"uniqueItems": true,
"vocabulary": "plone.app.vocabularies.Users"
},
"creators": {
"additionalItems": true,
Expand All @@ -104,7 +105,8 @@ Content-Type: application/json+schema
},
"title": "Creators",
"type": "array",
"uniqueItems": true
"uniqueItems": true,
"vocabulary": "plone.app.vocabularies.Users"
},
"description": {
"description": "Used in item listings and search results.",
Expand Down Expand Up @@ -183,7 +185,8 @@ Content-Type: application/json+schema
},
"title": "Related Items",
"type": "array",
"uniqueItems": true
"uniqueItems": true,
"vocabulary": "plone.app.vocabularies.Catalog"
},
"rights": {
"description": "Copyright statement or other rights information on this item.",
Expand All @@ -193,16 +196,13 @@ Content-Type: application/json+schema
"widget": "textarea"
},
"subjects": {
"additionalItems": true,
"choices": [],
"description": "Tags are commonly used for ad-hoc organization of content.",
"items": {
"description": "",
"title": "",
"type": "string"
},
"enum": [],
"enumNames": [],
"title": "Tags",
"type": "array",
"uniqueItems": true
"type": "string",
"vocabulary": "plone.app.vocabularies.Keywords"
},
"table_of_contents": {
"description": "If selected, this will show a table of contents at the top of the page.",
Expand Down
2 changes: 2 additions & 0 deletions docs/source/types.rst
Expand Up @@ -24,5 +24,7 @@ To get the schema of a content type, access the ``/@types`` endpoint with the n
:language: http

The content type schema uses the `JSON Schema <http://json-schema.org/>`_ format.
The tagged values for the widgets are also exposed in the the "properties" attribute of the schema.
If a 'vocabulary' is defined, it will be the name of the vocabulary which should be used via the `@vocabularies` endpoint on the actual resource.

See :ref:`types-schema` for a detailed documentation about the available field types.
20 changes: 15 additions & 5 deletions src/plone/restapi/tests/test_types.py
Expand Up @@ -58,6 +58,9 @@ class ITaggedValuesSchema(model.Schema):
description=u"",
)

another_field = schema.TextLine(title=u"Tagged Values widget params")
form.widget('another_field', a_param='some_value')


class TestJsonSchemaUtils(TestCase):

Expand Down Expand Up @@ -169,11 +172,18 @@ def test_get_jsonschema_with_hidden_field(self):
'input',
jsonschema['properties']['field_mode_input']['mode']
)
# XXX: To be decided if we always return a mode attribute
# self.assertEqual(
# 'input',
# jsonschema['properties']['field_mode_default']['mode']
# )

def test_get_jsonschema_with_widget_params(self):
ttool = getToolByName(self.portal, 'portal_types')
jsonschema = get_jsonschema_for_fti(
ttool['TaggedDocument'],
self.portal,
self.request
)
self.assertEqual(
'some_value',
jsonschema['properties']['another_field']['a_param']
)


class TestJsonSchemaProviders(TestCase):
Expand Down
23 changes: 20 additions & 3 deletions src/plone/restapi/types/adapters.py
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
"""JsonSchema providers."""
from plone.autoform.interfaces import WIDGETS_KEY

from plone.app.textfield.interfaces import IRichText
from zope.component import adapter
from zope.component import getMultiAdapter
Expand Down Expand Up @@ -30,7 +32,7 @@
from zope.schema.interfaces import IVocabularyFactory

from plone.restapi.types.interfaces import IJsonSchemaProvider
from plone.restapi.types.utils import get_fieldsets
from plone.restapi.types.utils import get_fieldsets, get_tagged_values
from plone.restapi.types.utils import get_jsonschema_properties


Expand Down Expand Up @@ -249,12 +251,21 @@ def additional(self):
choices = []
enum = []
enum_names = []
if self.field.vocabularyName:
vocabulary = None

if getattr(self.field, 'vocabularyName', None):
vocabulary = getUtility(
IVocabularyFactory,
name=self.field.vocabularyName)(self.context)
else:
elif getattr(self.field, 'vocabulary', None):
vocabulary = self.field.vocabulary
else:
tagged = get_tagged_values([self.field.interface], WIDGETS_KEY)
tagged_field_values = tagged.get(self.field.getName(), {})
vocab_name = tagged_field_values.get('vocabulary', None)
if vocab_name:
vocab_fac = getUtility(IVocabularyFactory, name=vocab_name)
vocabulary = vocab_fac(self.context)

if IContextSourceBinder.providedBy(vocabulary):
vocabulary = vocabulary(self.context)
Expand Down Expand Up @@ -370,3 +381,9 @@ class DatetimeJsonSchemaProvider(DateJsonSchemaProvider):

def get_widget(self):
return 'datetime'


@adapter(ITuple, Interface, Interface)
@implementer(IJsonSchemaProvider)
class SubjectsFieldJsonSchemaProvider(ChoiceJsonSchemaProvider):
pass
1 change: 1 addition & 0 deletions src/plone/restapi/types/configure.zcml
Expand Up @@ -19,6 +19,7 @@
zcml:condition="installed z3c.relationfield"
factory=".z3crelationadapter.ChoiceslessRelationListSchemaProvider"
name="IRelatedItems.relatedItems" />
<adapter factory=".adapters.SubjectsFieldJsonSchemaProvider" name="IDublinCore.subjects" />
<adapter factory=".adapters.SetJsonSchemaProvider" />
<adapter factory=".adapters.TupleJsonSchemaProvider" />
<adapter factory=".adapters.ChoiceJsonSchemaProvider" />
Expand Down
33 changes: 31 additions & 2 deletions src/plone/restapi/types/utils.py
Expand Up @@ -16,9 +16,11 @@
from collections import OrderedDict
from copy import copy
from plone.autoform.form import AutoExtensibleForm
from plone.autoform.interfaces import WIDGETS_KEY
from plone.dexterity.utils import getAdditionalSchemata
from plone.restapi.types.interfaces import IJsonSchemaProvider
from Products.CMFCore.utils import getToolByName
from plone.supermodel.utils import mergedTaggedValueDict
from z3c.form import form as z3c_form
from zope.component import getMultiAdapter
from zope.component import queryMultiAdapter
Expand Down Expand Up @@ -91,7 +93,7 @@ def get_fieldset_infos(fieldsets):


def get_jsonschema_properties(context, request, fieldsets, prefix='',
excluded_fields=None):
excluded_fields=None, tagged_values={}):
"""Build a JSON schema 'properties' list, based on a list of fieldset
dicts as returned by `get_fieldsets()`.
"""
Expand Down Expand Up @@ -120,9 +122,27 @@ def get_jsonschema_properties(context, request, fieldsets, prefix='',

properties[fieldname] = adapter.get_schema()

for key, value in tagged_values.get(fieldname, {}).items():
if key in properties[fieldname]:
continue
properties[fieldname][key] = value

return properties


def get_tagged_values(schemas, key):
params = {}
for schema in schemas:
if not schema:
continue
tagged_values = mergedTaggedValueDict(schema, key)
for field_name in schema:
widget = tagged_values.get(field_name)
if widget and widget.params:
params[field_name] = widget.params
return params


def get_jsonschema_for_fti(fti, context, request, excluded_fields=None):
"""Build a complete JSON schema for the given FTI.
"""
Expand All @@ -136,15 +156,24 @@ def get_jsonschema_for_fti(fti, context, request, excluded_fields=None):
except AttributeError:
schema = None
fieldsets = ()
additional_schemata = ()
else:
additional_schemata = tuple(getAdditionalSchemata(portal_type=fti.id))
fieldsets = get_fieldsets(
context, request, schema, additional_schemata
)

# Mangle the properties a bit to add widgets hints
schemas = (schema,) + additional_schemata

# Build JSON schema properties
properties = get_jsonschema_properties(
context, request, fieldsets, excluded_fields=excluded_fields)
context,
request,
fieldsets,
excluded_fields=excluded_fields,
tagged_values=get_tagged_values(schemas, WIDGETS_KEY)
)

# Determine required fields
required = []
Expand Down

0 comments on commit 00fc558

Please sign in to comment.