Restapi

Changes and enhancements 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.

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

Response: File download

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

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

Enhanced Field Serialization

File Fields with Download URLs

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

GET /Plone/my-document HTTP/1.1
Host: localhost:8080
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.

GET /Plone/folder-workingcopy?include_items=true HTTP/1.1
Host: localhost:8080
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.

GET /Plone/contact-form HTTP/1.1
Host: localhost:8080
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.

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"
}

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.

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"
}

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.

GET /Plone/@relations?sources=/Plone/page1 HTTP/1.1
Host: localhost:8080
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.

GET /Plone/@es-search?SearchableText=content&portal_type=Document HTTP/1.1
Host: localhost:8080
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.

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"]
}

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

Domain-based API Search Filter

When operating with separate domains for the public API (e.g. bl-api.example.ch) and the backend (e.g. bl-backend.example.ch), you can configure Elasticsearch filters that are only applied to requests coming from the API domain.

This is useful for hiding certain content from public search results while keeping it visible in the backend.

The feature is controlled by two registry records:

wcs.backend.search.api_domains A list of hostnames that are considered API domains. When a search request arrives from one of these hosts, the api_filter is applied.

wcs.backend.search.api_filter A JSON array of Elasticsearch filter clauses. These are appended to the query only when the request originates from a configured API domain.

Both default to empty, meaning no domain-based filtering is active until configured.

Example: Exclude content from public search

Assuming you have a custom Elasticsearch field exclude_from_search indexed on your content, configure the registry as follows:

wcs.backend.search.api_domains:
    - bl-api.example.ch

wcs.backend.search.api_filter:
    [{"bool": {"must_not": [{"term": {"exclude_from_search": true}}]}}]

With this configuration:

  • Searches via bl-api.example.ch will exclude content where exclude_from_search is true.

  • Searches via bl-backend.example.ch (or any other domain not listed in api_domains) will return all content regardless.

The api_filter supports any valid Elasticsearch filter clause and can be combined with the global wcs.backend.search.filter – both are applied independently.

Note

The domain is determined from the HTTP_HOST header of the incoming request. Port numbers are stripped before matching. For example, a request to bl-api.example.ch:8080 will match the configured domain bl-api.example.ch.

Configuration via Registry

Elasticsearch behavior is configurable through Plone registry records:

  • wcs.backend.search.regular: Search query pattern for regular (unquoted) searches

  • wcs.backend.search.quoted: Search query pattern for quoted searches

  • wcs.backend.search.filter: Global Elasticsearch filter clauses applied to all searches

  • wcs.backend.search.api_domains: Hostnames for which the API filter is applied

  • wcs.backend.search.api_filter: Elasticsearch filter clauses applied only on API domains

  • 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