Restapi

Changes and enhancments on the Plone RestAPI by 7inOne

File GET

For the original, check Plone RestAPI File GET for details.

In case of files 7inOne provides an option to activate direct downloads of files via the RestAPI GET endpoint.

Via registry the value of wcs.backend.api.directdownload can be activated, this enables the direct download feature. This feature only works with the portal_type File.

http

GET /Plone/any-file.pdf HTTP/1.1
Host: localhost:8080
Accept: application/json

curl

curl -i -X GET http://localhost:8080/Plone/any-file.pdf -H "Accept: application/json"

python-requests

requests.get('http://localhost:8080/Plone/any-file.pdf', headers={'Accept': 'application/json'})

Response:

File download

This behavior can be overriden again by a querystring variable named json:

http

GET /Plone/any-file.pdf?json=true HTTP/1.1
Host: localhost:8080
Accept: application/json

curl

curl -i -X GET 'http://localhost:8080/Plone/any-file.pdf?json=true' -H "Accept: application/json"

python-requests

requests.get('http://localhost:8080/Plone/any-file.pdf?json=true', headers={'Accept': 'application/json'})

Enhanced Field Serialization

File Fields with Download URLs

File fields include enhanced download URLs with filename and cache-busting timestamps.

http

GET /Plone/my-document HTTP/1.1
Host: localhost:8080
Accept: application/json

curl

curl -i -X GET http://localhost:8080/Plone/my-document -H "Accept: application/json"

python-requests

requests.get('http://localhost:8080/Plone/my-document', headers={'Accept': 'application/json'})

Response shows file field with enhanced download URL:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "@id": "http://localhost:8080/plone/my-document",
    "@type": "Document",
    "title": "My Document",
    "file": {
        "@type": "File",
        "content-type": "application/pdf",
        "download": "http://localhost:8080/plone/my-document/@@download/file/document.pdf?_t=1694847123456",
        "filename": "document.pdf",
        "size": 245760
    }
}

Staging Folder Serialization

Working Copy with Baseline Items

When viewing a working copy folder, items include both working copy content and baseline content for easier navigation.

http

GET /Plone/folder-workingcopy?include_items=true HTTP/1.1
Host: localhost:8080
Accept: application/json

curl

curl -i -X GET 'http://localhost:8080/Plone/folder-workingcopy?include_items=true' -H "Accept: application/json"

python-requests

requests.get('http://localhost:8080/Plone/folder-workingcopy?include_items=true', headers={'Accept': 'application/json'})

Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "@id": "http://localhost:8080/plone/folder-workingcopy",
    "@type": "Folder",
    "title": "My Folder (Working Copy)",
    "is_folderish": true,
    "items_total": 4,
    "items": [
        {
            "@id": "http://localhost:8080/plone/folder-workingcopy/new-page",
            "@type": "Document",
            "title": "New Page",
            "review_state": "private"
        },
        {
            "@id": "http://localhost:8080/plone/folder-workingcopy/modified-page",
            "@type": "Document",
            "title": "Modified Page",
            "review_state": "private"
        },
        {
            "@id": "http://localhost:8080/plone/folder/existing-page",
            "@type": "Document",
            "title": "Existing Page",
            "review_state": "published"
        },
        {
            "@id": "http://localhost:8080/plone/folder/another-page",
            "@type": "Document",
            "title": "Another Page",
            "review_state": "published"
        }
    ]
}

EasyForm Integration

Form Schema Serialization

EasyForm objects include form field definitions and submission endpoint.

http

GET /Plone/contact-form HTTP/1.1
Host: localhost:8080
Accept: application/json

curl

curl -i -X GET http://localhost:8080/Plone/contact-form -H "Accept: application/json"

python-requests

requests.get('http://localhost:8080/Plone/contact-form', headers={'Accept': 'application/json'})

Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "@id": "http://localhost:8080/plone/contact-form",
    "@type": "EasyForm",
    "title": "Contact Form",
    "form_action": "http://localhost:8080/plone/contact-form/@submit",
    "form_fields": [
        {
            "name": "form.widgets.name",
            "type": "TextLine",
            "title": "Name",
            "description": "Your full name",
            "required": true
        },
        {
            "name": "form.widgets.email",
            "type": "Email",
            "title": "Email",
            "description": "Your email address",
            "required": true
        },
        {
            "name": "form.widgets.subject",
            "type": "Choice",
            "title": "Subject",
            "description": "Select a subject",
            "required": true,
            "items": [
                {"token": "general", "title": "General Inquiry"},
                {"token": "support", "title": "Technical Support"},
                {"token": "sales", "title": "Sales"}
            ]
        },
        {
            "name": "form.widgets.message",
            "type": "Text",
            "title": "Message",
            "description": "Your message",
            "required": true
        }
    ]
}

Form Submission

Submit form data with automatic CSRF protection handling.

http

POST /Plone/contact-form/@submit HTTP/1.1
Host: localhost:8080
Accept: application/json
Content-Type: application/json

{
    "form.widgets.name": "John Doe",
    "form.widgets.email": "john@example.com",
    "form.widgets.message": "Hello World"
}

curl

curl -i -X POST http://localhost:8080/Plone/contact-form/@submit -H "Accept: application/json" -H "Content-Type: application/json" --data-raw '{"form.widgets.email": "john@example.com", "form.widgets.message": "Hello World", "form.widgets.name": "John Doe"}'

python-requests

requests.post('http://localhost:8080/Plone/contact-form/@submit', headers={'Accept': 'application/json', 'Content-Type': 'application/json'}, json={'form.widgets.email': 'john@example.com', 'form.widgets.message': 'Hello World', 'form.widgets.name': 'John Doe'})

Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "success": true
}

Workflow Management

Direct State Setting

Set workflow state directly bypassing normal transitions, with security updates.

http

POST /Plone/my-content/@workflow-set-state/published HTTP/1.1
Host: localhost:8080
Accept: application/json
Content-Type: application/json

{
    "comment": "Direct publication via API"
}

curl

curl -i -X POST http://localhost:8080/Plone/my-content/@workflow-set-state/published -H "Accept: application/json" -H "Content-Type: application/json" --data-raw '{"comment": "Direct publication via API"}'

python-requests

requests.post('http://localhost:8080/Plone/my-content/@workflow-set-state/published', headers={'Accept': 'application/json', 'Content-Type': 'application/json'}, json={'comment': 'Direct publication via API'})

Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "action": "set state",
    "actor": "system",
    "comment": "Direct publication via API",
    "review_state": "published",
    "time": "2023-09-16T10:30:00+00:00",
    "title": "Published"
}

Relations

Enhanced Relations with Trash Filtering

Get object relations with trashed objects automatically filtered out.

http

GET /Plone/@relations?sources=%2FPlone%2Fpage1 HTTP/1.1
Host: localhost:8080
Accept: application/json

curl

curl -i -X GET 'http://localhost:8080/Plone/@relations?sources=%2FPlone%2Fpage1' -H "Accept: application/json"

python-requests

requests.get('http://localhost:8080/Plone/@relations?sources=%2FPlone%2Fpage1', headers={'Accept': 'application/json'})

Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "relatedItems": [
        {
            "source": {
                "@id": "http://localhost:8080/plone/page1",
                "@type": "Document",
                "title": "Page 1",
                "UID": "12345678-1234-1234-1234-123456789abc"
            },
            "target": {
                "@id": "http://localhost:8080/plone/page2",
                "@type": "Document",
                "title": "Related Page 2",
                "UID": "87654321-4321-4321-4321-cba987654321"
            }
        }
    ],
    "backRelatedItems": [
        {
            "source": {
                "@id": "http://localhost:8080/plone/page3",
                "@type": "Document",
                "title": "Page 3",
                "UID": "abcdef12-3456-7890-abcd-ef1234567890"
            },
            "target": {
                "@id": "http://localhost:8080/plone/page1",
                "@type": "Document",
                "title": "Page 1",
                "UID": "12345678-1234-1234-1234-123456789abc"
            }
        }
    ]
}

Elasticsearch Integration (RESTAPI)

For the original Elasticsearch support, check collective.elasticsearch documentation.

7inOne enhances Elasticsearch integration with custom German text analysis, aggregations, suggestions, and raw query support.

Aggregations and suggestions can be configured through the Plone registry. Alternatively you can provide your own aggregations and suggestions in the query body using the @raw-es-search endpoint.

Enhanced Search with Aggregations

The @es-search endpoint provides Elasticsearch queries with faceted search capabilities and search suggestions.

http

GET /Plone/@es-search?SearchableText=content&portal_type=Document HTTP/1.1
Host: localhost:8080
Accept: application/json

curl

curl -i -X GET 'http://localhost:8080/Plone/@es-search?SearchableText=content&portal_type=Document' -H "Accept: application/json"

python-requests

requests.get('http://localhost:8080/Plone/@es-search?SearchableText=content&portal_type=Document', headers={'Accept': 'application/json'})

Response includes standard search results plus elasticsearch-specific data:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "@id": "http://localhost:8080/plone/@es-search?SearchableText=content&portal_type=Document",
    "items": [
        {
            "@id": "http://localhost:8080/plone/document1",
            "@type": "Document",
            "title": "Content Document 1",
            "description": "Document containing content",
            "review_state": "published"
        },
        {
            "@id": "http://localhost:8080/plone/document2",
            "@type": "Document",
            "title": "Another Content Document",
            "description": "More content here",
            "review_state": "published"
        }
    ],
    "items_total": 15,
    "elasticsearch": {
        "aggregations": {
            "portal_type": {
                "buckets": [
                    {"key": "Document", "doc_count": 12, "title": "Page"},
                    {"key": "News Item", "doc_count": 3, "title": "News Item"}
                ]
            },
            "review_state": {
                "buckets": [
                    {"key": "published", "doc_count": 10, "title": "Published"},
                    {"key": "private", "doc_count": 5, "title": "Private"}
                ]
            }
        },
        "original_aggregations": {
            "portal_type": {
                "buckets": [
                    {"key": "Document", "doc_count": 45, "title": "Page"},
                    {"key": "News Item", "doc_count": 12, "title": "News Item"},
                    {"key": "Folder", "doc_count": 8, "title": "Folder"}
                ]
            }
        },
        "suggest": {
            "content_suggest": [
                {
                    "text": "content",
                    "options": [
                        {"text": "content management", "score": 0.8},
                        {"text": "content types", "score": 0.6}
                    ]
                }
            ]
        }
    }
}

Raw Elasticsearch Queries

Execute raw Elasticsearch queries with automatic security filtering applied.

http

POST /Plone/@raw-es-search HTTP/1.1
Host: localhost:8080
Accept: application/json
Content-Type: application/json

{
    "query": {
        "match": {
            "SearchableText": "important content"
        }
    },
    "aggs": {
        "types": {
            "terms": {
                "field": "portal_type"
            }
        }
    },
    "size": 20,
    "sort": ["_score", "sortable_title"]
}

curl

curl -i -X POST http://localhost:8080/Plone/@raw-es-search -H "Accept: application/json" -H "Content-Type: application/json" --data-raw '{"aggs": {"types": {"terms": {"field": "portal_type"}}}, "query": {"match": {"SearchableText": "important content"}}, "size": 20, "sort": ["_score", "sortable_title"]}'

python-requests

requests.post('http://localhost:8080/Plone/@raw-es-search', headers={'Accept': 'application/json', 'Content-Type': 'application/json'}, json={'aggs': {'types': {'terms': {'field': 'portal_type'}}}, 'query': {'match': {'SearchableText': 'important content'}}, 'size': 20, 'sort': ['_score', 'sortable_title']})

Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "hits": {
        "total": {
            "value": 42,
            "relation": "eq"
        },
        "hits": [
            {
                "_score": 1.2345,
                "_source": {
                    "path": "/plone/important-document",
                    "title": "Important Document",
                    "portal_type": "Document",
                    "SearchableText": "This document contains important content for users."
                }
            },
            {
                "_score": 1.1234,
                "_source": {
                    "path": "/plone/content-guide",
                    "title": "Content Creation Guide",
                    "portal_type": "Document",
                    "SearchableText": "Guide for creating important content in the system."
                }
            }
        ]
    },
    "aggregations": {
        "types": {
            "buckets": [
                {"key": "Document", "doc_count": 35},
                {"key": "News Item", "doc_count": 5},
                {"key": "Folder", "doc_count": 2}
            ]
        }
    }
}

German Text Analysis

Custom Elasticsearch mapping includes German text analyzer for improved search in German content.

Features: - German stopwords filtering - Applied to SearchableText, Title, Description, and content_title fields - Custom fields configurable via registry wcs.backend.search.custom_fields

Configuration via Registry

Elasticsearch behavior is configurable through Plone registry records:

  • wcs.backend.search.aggregations: Faceted search aggregations configuration

  • wcs.backend.search.suggest: Search suggestions configuration

  • wcs.backend.search.popular_searches: Popular search terms list

  • wcs.backend.search.custom_fields: Additional Elasticsearch field mappings