Control panels

Control panels allow you to configure the global site setup. The @controlpanels endpoint allows you to list all existing control panels.

Listing Control Panels

A list of all existing control panels in the portal can be retrieved by sending a GET request to the @controlpanels endpoint:

GET /@controlpanels HTTP/1.1
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImZ1bGxuYW1lIjoiQWRtaW4iLCJpYXQiOjE2NDkzMTI0NDl9.RS1Ny_r0v7vIylFfK6q0JVJrkiDuTOh9iG9IL8xbzAk

Or use the client directly:

import { Client } from '@robgietema/nick';

const cli = Client.initialize({ apiPath: 'http://localhost:8080' });
const login = await cli.login({ username: 'admin', password: 'admin' });

const { data } = await cli.getControlpanels();

Response:

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

[
  {
    "@id": "http://localhost:8080/@controlpanels/dexterity-types",
    "group": "Content",
    "title": "Content Types"
  },
  {
    "@id": "http://localhost:8080/@controlpanels/language",
    "group": "General",
    "title": "Language"
  },
  {
    "@id": "http://localhost:8080/@controlpanels/mail",
    "group": "General",
    "title": "Mail"
  },
  {
    "@id": "http://localhost:8080/@controlpanels/navigation",
    "group": "General",
    "title": "Navigation"
  },
  {
    "@id": "http://localhost:8080/@controlpanels/site",
    "group": "General",
    "title": "Site"
  }
]

The following fields are returned:

  • @id: hypermedia link to the control panel
  • title: the title of the control panel
  • group: the group in which the control panel should appear, for example, General, Content, Users, Security, Advanced, or Add-on Configuration.

Retrieve a single Control Panel

To retrieve a single control panel, send a GET request to the URL of the control panel:

GET /@controlpanels/mail HTTP/1.1
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImZ1bGxuYW1lIjoiQWRtaW4iLCJpYXQiOjE2NDkzMTI0NDl9.RS1Ny_r0v7vIylFfK6q0JVJrkiDuTOh9iG9IL8xbzAk

Or use the client directly:

import { Client } from '@robgietema/nick';

const cli = Client.initialize({ apiPath: 'http://localhost:8080' });
const login = await cli.login({ username: 'admin', password: 'admin' });

const { data } = await cli.getControlpanel({ path: '/mail' });

Response:

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

{
  "@id": "http://localhost:8080/@controlpanels/mail",
  "group": "General",
  "title": "Mail",
  "data": {
    "host": "localhost",
    "pass": "",
    "port": 25,
    "user": "",
    "debug": true,
    "secure": true,
    "email_from_name": "Webmaster",
    "email_from_address": "webmaster@nickcms.org"
  },
  "schema": {
    "required": ["host", "port", "email_from_name", "email_from_address"],
    "fieldsets": [
      {
        "id": "default",
        "title": "Default",
        "fields": [
          "host",
          "port",
          "secure",
          "user",
          "pass",
          "email_from_name",
          "email_from_address",
          "debug"
        ],
        "behavior": "plone"
      }
    ],
    "properties": {
      "host": {
        "type": "string",
        "title": "SMTP server",
        "default": "localhost",
        "description": "The address of your local SMTP (outgoing e-mail) server. Usually 'localhost', unless you use an external server to send e-mail."
      },
      "pass": {
        "type": "string",
        "title": "ESMTP password",
        "widget": "password",
        "description": "The password for the ESMTP user account."
      },
      "port": {
        "type": "integer",
        "title": "SMTP port",
        "default": 25,
        "description": "The port of your local SMTP (outgoing e-mail) server. Usually '25'."
      },
      "user": {
        "type": "string",
        "title": "ESMTP username",
        "description": "Username for authentication to your e-mail server. Not required unless you are using ESMTP."
      },
      "debug": {
        "type": "boolean",
        "title": "Debug",
        "description": "If enabled the mail is send to a test server."
      },
      "secure": {
        "type": "boolean",
        "title": "Secure",
        "description": "If enabled the mail is send using a secure connection."
      },
      "email_from_name": {
        "type": "string",
        "title": "Site 'From' name",
        "description": "Plone generates e-mail using this name as the e-mail sender."
      },
      "email_from_address": {
        "type": "string",
        "title": "Site 'From' address",
        "description": "Plone generates e-mail using this address as the e-mail return address. It is also used as the destination address for the site-wide contact form and the 'Send test e-mail' feature."
      }
    }
  }
}

The following fields are returned:

  • @id: hypermedia link to the control panel
  • title: title of the control panel
  • group: group name of the control panel
  • schema: JSON Schema of the control panel
  • data: current values of the control panel

Updating a Control Panel

To update the settings on a control panel, send a PATCH request to control panel resource:

PATCH /@controlpanels/mail HTTP/1.1
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImZ1bGxuYW1lIjoiQWRtaW4iLCJpYXQiOjE2NDkzMTI0NDl9.RS1Ny_r0v7vIylFfK6q0JVJrkiDuTOh9iG9IL8xbzAk
Content-Type: application/json

{
  "host": "mail.someserver.com",
  "port": 25
}

Or use the client directly:

import { Client } from '@robgietema/nick';

const cli = Client.initialize({ apiPath: 'http://localhost:8080' });
const login = await cli.login({ username: 'admin', password: 'admin' });

const { data } = await cli.updateControlpanel({
  token: login.data.token,
  path: '/@controlpanels/mail',
  data: {
    host: 'mail.someserver.com',
    port: 25,
  },
});

A successful response to a PATCH request will be indicated by a 204 No Content response:

HTTP/1.1 204 No Content

Control Panels not based on schemas

Control panels which are not based on schemas have a custom @controlpanels/:panel endpoint implementation.

Content Types

@controlpanels/dexterity-types is a custom control panel endpoint that will allow you to add, remove, and configure available types.

Reading or writing content types require the Manage Site permission.

Verb URL Action
GET /@controlpanels/dexterity-types List configurable content types
POST /@controlpanels/dexterity-types Creates a new content type
GET /@controlpanels/dexterity-types/{type-id} Get the current state of the content type
PATCH /@controlpanels/dexterity-types/{type-id} Update the content type details
DELETE /@controlpanels/dexterity-types/{type-id} Remove the content type

Listing Content Types

To list the available content types, send a GET request to @controlpanels/dexterity-types

GET /@controlpanels/dexterity-types HTTP/1.1
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImZ1bGxuYW1lIjoiQWRtaW4iLCJpYXQiOjE2NDkzMTI0NDl9.RS1Ny_r0v7vIylFfK6q0JVJrkiDuTOh9iG9IL8xbzAk

Or use the client directly:

import { Client } from '@robgietema/nick';

const cli = Client.initialize({ apiPath: 'http://localhost:8080' });
const login = await cli.login({ username: 'admin', password: 'admin' });

const { data } = await cli.getControlpanel({ path: '/dexterity-types' });

Response:

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

{
  "@id": "http://localhost:8080/@controlpanels/dexterity-types",
  "items": [
    {
      "@id": "http://localhost:8080/@controlpanels/dexterity-types/File",
      "@type": "File",
      "title": "File",
      "description": "Lets you upload a file to the site.",
      "id": "File",
      "count": 0,
      "meta_type": "File"
    },
    {
      "@id": "http://localhost:8080/@controlpanels/dexterity-types/Folder",
      "@type": "Folder",
      "title": "Folder",
      "description": "",
      "id": "Folder",
      "count": 3,
      "meta_type": "Folder"
    },
    {
      "@id": "http://localhost:8080/@controlpanels/dexterity-types/Image",
      "@type": "Image",
      "title": "Image",
      "description": "Images can be referenced in pages or displayed in an album.",
      "id": "Image",
      "count": 0,
      "meta_type": "Image"
    },
    {
      "@id": "http://localhost:8080/@controlpanels/dexterity-types/Page",
      "@type": "Page",
      "title": "Page",
      "description": "",
      "id": "Page",
      "count": 1,
      "meta_type": "Page"
    },
    {
      "@id": "http://localhost:8080/@controlpanels/dexterity-types/Site",
      "@type": "Site",
      "title": "Site",
      "description": "",
      "id": "Site",
      "count": 1,
      "meta_type": "Site"
    }
  ],
  "title": "Content Types",
  "group": "Content"
}

The following fields are returned:

  • @id: hypermedia link to the control panel
  • title: title of the control panel
  • group: group name of the control panel
  • items: list of configurable content types

Creating a new type with POST

To create a new content type, send a POST request to the /@controlpanels/dexterity-types endpoint:

POST /@controlpanels/dexterity-types HTTP/1.1
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImZ1bGxuYW1lIjoiQWRtaW4iLCJpYXQiOjE2NDkzMTI0NDl9.RS1Ny_r0v7vIylFfK6q0JVJrkiDuTOh9iG9IL8xbzAk
Content-Type: application/json

{
  "title": "My Type",
  "description": "Type Description"
}

Or use the client directly:

import { Client } from '@robgietema/nick';

const cli = Client.initialize({ apiPath: 'http://localhost:8080' });
const login = await cli.login({ username: 'admin', password: 'admin' });

const { data } = await cli.createControlpanelType({
  token: login.data.token,
  path: '/controlpanels/dexterity-types',
  data: {
    title: 'My Type',
    description: 'Type Description',
  },
});

Response:

HTTP/1.1 201 Created
Content-Type: application/json

{
  "@id": "http://localhost:8080/@controlpanels/dexterity-types/My Type",
  "title": "My Type",
  "description": "Type Description",
  "schema": {
    "fieldsets": [
      {
        "fields": [
          "title",
          "description",
          "allowed_content_types",
          "filter_content_types"
        ],
        "id": "default",
        "title": "Default"
      },
      {
        "fields": [
          "basic",
          "blocks",
          "categorization",
          "dates",
          "dublin_core",
          "exclude_from_nav",
          "ownership",
          "preview_image_link",
          "short_name",
          "versioning"
        ],
        "id": "behaviors",
        "title": "Behaviors"
      }
    ],
    "properties": {
      "allowed_content_types": {
        "additionalItems": true,
        "description": "",
        "factory": "Multiple Choice",
        "items": {
          "description": "",
          "factory": "Choice",
          "title": "",
          "type": "string",
          "vocabulary": {
            "@id": "http://localhost:8080/@vocabularies/types"
          }
        },
        "title": "Allowed Content Types",
        "type": "array",
        "uniqueItems": true
      },
      "description": {
        "description": "",
        "factory": "Text",
        "title": "Description",
        "type": "string",
        "widget": "textarea"
      },
      "filter_content_types": {
        "factory": "Yes/No",
        "title": "Filter Contained Types",
        "description": "Items of this type can act as a folder containing other  items. What content types should be allowed inside?",
        "type": "boolean"
      },
      "title": {
        "description": "",
        "factory": "Text line (String)",
        "title": "Type Name",
        "type": "string"
      },
      "basic": {
        "description": "Adds title and description fields.",
        "factory": "Yes/No",
        "title": "Basic metadata",
        "type": "boolean"
      },
      "blocks": {
        "description": "Enables Volto Blocks support",
        "factory": "Yes/No",
        "title": "Blocks",
        "type": "boolean"
      },
      "categorization": {
        "description": "Adds keywords and language fields.",
        "factory": "Yes/No",
        "title": "Categorization",
        "type": "boolean"
      },
      "dates": {
        "description": "Adds effective and expiration dates.",
        "factory": "Yes/No",
        "title": "Dates",
        "type": "boolean"
      },
      "dublin_core": {
        "description": "Adds standard metadatafields",
        "factory": "Yes/No",
        "title": "Dublin Core metadata",
        "type": "boolean"
      },
      "exclude_from_nav": {
        "description": "If selected, this item will not appear in the navigation tree.",
        "factory": "Yes/No",
        "title": "Exclude from navigation",
        "type": "boolean"
      },
      "ownership": {
        "description": "Adds ownership and rights fields.",
        "factory": "Yes/No",
        "title": "Ownership",
        "type": "boolean"
      },
      "preview_image_link": {
        "description": "Gives the ability to rename an item from its edit form.",
        "factory": "Yes/No",
        "title": "Preview Image Link",
        "type": "boolean"
      },
      "short_name": {
        "description": "Gives the ability to rename an item from its edit form.",
        "factory": "Yes/No",
        "title": "Short name",
        "type": "boolean"
      },
      "versioning": {
        "description": "Versioning support",
        "factory": "Yes/No",
        "title": "Versioning",
        "type": "boolean"
      }
    },
    "required": [
      "title",
      "filter_content_types"
    ]
  },
  "data": {
      "title": "My Type",
      "description": "Type Description",
      "allowed_content_types": [],
      "filter_content_types": false,
      "basic": false,
      "blocks": false,
      "categorization": false,
      "dates": false,
      "dublin_core": false,
      "exclude_from_nav": false,
      "ownership": false,
      "preview_image_link": false,
      "short_name": false,
      "versioning": false
  }
}

Reading a type with GET

After a successful POST, access the content type by sending a GET request to the endpoint /@controlpanels/dexterity-types/{type-id}:

GET /@controlpanels/dexterity-types/Page HTTP/1.1
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImZ1bGxuYW1lIjoiQWRtaW4iLCJpYXQiOjE2NDkzMTI0NDl9.RS1Ny_r0v7vIylFfK6q0JVJrkiDuTOh9iG9IL8xbzAk

Or use the client directly:

import { Client } from '@robgietema/nick';

const cli = Client.initialize({ apiPath: 'http://localhost:8080' });
const login = await cli.login({ username: 'admin', password: 'admin' });

const { data } = await cli.getControlpanelType({
  token: login.data.token,
  path: '/controlpanels/dexterity-types/Page',
});

Response:

import { Client } from '@robgietema/nick';

const cli = Client.initialize({ apiPath: 'http://localhost:8080' });
const login = await cli.login({ username: 'admin', password: 'admin' });

const { data } = await cli.getControlpanelType({
  token: login.data.token,
  path: '/controlpanels/dexterity-types/Page',
});

Updating a type with PATCH

To update an existing content type, send a PATCH request to the server. PATCH allows to provide just a subset of the resource, that is, the values you actually want to change:

PATCH /@controlpanels/dexterity-types/Page HTTP/1.1
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImZ1bGxuYW1lIjoiQWRtaW4iLCJpYXQiOjE2NDkzMTI0NDl9.RS1Ny_r0v7vIylFfK6q0JVJrkiDuTOh9iG9IL8xbzAk
Content-Type: application/json

{
  "description": "Some Description",
  "preview_image_link": true
}

Or use the client directly:

import { Client } from '@robgietema/nick';

const cli = Client.initialize({ apiPath: 'http://localhost:8080' });
const login = await cli.login({ username: 'admin', password: 'admin' });

const { data } = await cli.updateControlpanelType({
  token: login.data.token,
  path: '/controlpanels/dexterity-types/Page',
  data: {
    description: "Some Description",
    preview_image_link: true
  },
});

Response:

HTTP/1.1 204 No Content

Removing a type with DELETE

Delete an existing content type by sending a DELETE request to the URL of an existing content type:

DELETE /@controlpanels/dexterity-types/File HTTP/1.1
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImZ1bGxuYW1lIjoiQWRtaW4iLCJpYXQiOjE2NDkzMTI0NDl9.RS1Ny_r0v7vIylFfK6q0JVJrkiDuTOh9iG9IL8xbzAk

Or use the client directly:

import { Client } from '@robgietema/nick';

const cli = Client.initialize({ apiPath: 'http://localhost:8080' });
const login = await cli.login({ username: 'admin', password: 'admin' });

const { data } = await cli.deleteControlpanelType({
  token: login.data.token,
  path: '/controlpanels/dexterity-types/File',
});

Response:

HTTP/1.1 204 No Content