Skip to main content

🔌 Extensions

Extensions add links, buttons and pages to the admin's menu: a link to an external tool, a button that runs your code, or another web application shown inside the admin.

# mysite/studio.py
from content_studio.admin import ContentStudioSite
from content_studio.extensions import IFramePage, MainMenuLink


class BlogStudioSite(ContentStudioSite):
extensions = [
MainMenuLink(
url="https://plausible.io/example.com",
label="Analytics",
icon="ph-bold ph-chart-line",
),
IFramePage(
path="reports/",
iframe_url="https://metabase.example.com/dashboard/1",
),
]


admin_site = BlogStudioSite()

A link in the admin's main menu. It takes a url, a label and an icon, and optionally a color and a weight. The menu orders its links and buttons by weight, lowest first; with equal weights, which is the default of 0, they keep their order in extensions.

A button in the admin's main menu that runs your code on the server, such as clearing a cache or starting a deployment. Subclass MainMenuButton and implement handle(request):

# mysite/studio.py
from django.core.cache import cache

from content_studio.admin import ContentStudioSite
from content_studio.extensions import MainMenuButton


class ClearCacheButton(MainMenuButton):
label = "Clear cache"
icon = "ph-bold ph-broom"
confirm = "Clear the cache for every visitor?"

def is_visible(self, request):
return request.user.is_superuser

def handle(self, request):
cache.clear()
return "The cache is cleared."


class BlogStudioSite(ContentStudioSite):
extensions = [ClearCacheButton()]


admin_site = BlogStudioSite()
  • label, icon, color and weight: as for MainMenuLink.
  • confirm: a question to confirm before the button runs.
  • is_visible(request): who sees the button. By default every staff user does. Content Studio checks it again when the button runs.

handle returns what the editor sees: a string as a notification, or a ButtonResult with a message and a value that appears once in a dialog with a copy button. The options can also be passed as arguments, such as ClearCacheButton(label="Empty cache"). Record the changes the button makes with record_change, so they appear in the objects' history. The webhooks app ships one ready to use: DeployButton.

IFramePage​

A page in the admin that shows another web page in an iframe. path is the page's address under the admin's URL prefix, iframe_url the page it shows.

The embedded page runs in a sandbox: it can run scripts, keep its origin, submit forms, open windows and offer downloads, but it cannot navigate the admin away, show modal dialogs or lock the pointer. The sandbox option takes another string of sandbox tokens to tighten or widen it, or None to embed the page without a sandbox:

IFramePage(
path="reports/",
iframe_url="https://metabase.example.com/dashboard/1",
sandbox="allow-scripts allow-same-origin",
)

You choose the page the iframe shows, so the admin trusts it to that extent: a page that may navigate the admin or open dialogs needs sandbox=None.

Identity​

Each extension gets an ID from its class and its settings, so every worker of a deployment agrees on it. Two extensions with the same settings need an explicit extension_id (a UUID) to tell them apart; manage.py check reports duplicates.