Backoffice Extensions
UmbSocial’s backoffice is built from ordinary Umbraco extensions, and it adds extension types of its own wherever a screen shows actions, so a package can add to Social’s screens without changing them. The paid UmbSocial packages use the same types.
| Extension type | Constant | Where | Props |
|---|---|---|---|
umbSocialComposerAction |
UMB_SOCIAL_COMPOSER_ACTION |
The composer’s toolbar | state |
umbSocialComposerPanel |
UMB_SOCIAL_COMPOSER_PANEL |
Under the composer’s text editor | state |
umbSocialQueueItemAction |
UMB_SOCIAL_QUEUE_ITEM_ACTION |
With each post’s actions in the queue and the calendar | post |
umbSocialQueueItemDetail |
UMB_SOCIAL_QUEUE_ITEM_DETAIL |
Inline on each post in the queue and the calendar, after its status | post |
umbSocialPostAction |
UMB_SOCIAL_POST_ACTION |
A post’s detail panel on the Published page | post |
umbSocialChannelAction |
UMB_SOCIAL_CHANNEL_ACTION |
Each channel in Settings › Social › Connections | channel |
Each constant comes with a manifest type (for example ManifestUmbSocialComposerAction) and a props type (UmbSocialComposerActionProps). Your element gets the props as properties. Dispatch a change event (bubbling and composed) to have the screen refresh or reload after your element changed something.
Import what you need from @umbsocial/core. UmbSocial adds it to the backoffice’s import map, so it resolves at runtime like @umbraco-cms/backoffice/*. Mark it as external in your bundler, as you do Umbraco’s own packages.
Composer actions
Section titled “Composer actions”umbSocialComposerAction puts an element in the composer’s toolbar, next to Preview: for example, a button that drafts the text for you. Your element gets the composer’s state as its state property. Change the state, then dispatch a change event (bubbling and composed) so the composer redraws and re-checks the post.
import { UMB_SOCIAL_COMPOSER_ACTION, socialPermission, SOCIAL_VERB, type ManifestUmbSocialComposerAction } from "@umbsocial/core";
export const manifests: Array<ManifestUmbSocialComposerAction> = [ { type: UMB_SOCIAL_COMPOSER_ACTION, alias: "My.Social.ComposerAction.Signature", name: "Add our signature", element: () => import("./signature-action.element.js"), // Optional: show it only to people who can write posts. conditions: [socialPermission(SOCIAL_VERB.write)], },];import { customElement, html, property } from "@umbraco-cms/backoffice/external/lit";import { UmbLitElement } from "@umbraco-cms/backoffice/lit-element";import type { ComposerState } from "@umbsocial/core";
@customElement("my-signature-action")export class MySignatureActionElement extends UmbLitElement { @property({ attribute: false }) state?: ComposerState;
#add() { if (!this.state) return; this.state.setText(`${this.state.text}\n\nThe UmbHost team`); this.dispatchEvent(new Event("change", { bubbles: true, composed: true })); }
override render() { return html`<uui-button compact look="outline" label="Add signature" @click=${this.#add}></uui-button>`; }}
export default MySignatureActionElement;What ComposerState offers:
| Member | Use |
|---|---|
text, setText(text) |
The main text. Use setText, so the post’s link (the first link in the text) is worked out again. |
channels, selected, toggleChannel(key) |
The channels the user can post to, and the keys of those chosen. |
overrides, setOverride(key, text) |
A channel’s own text (null to use the main text). |
linkUrl, linkTitle, linkDescription, linkImageUrl |
The post’s link and its card. |
image, setImage(image), imageAltText |
The image, with its focal point and crops. |
when, atUtc, setWhen(when, atUtc) |
When it goes out: Draft, Queue, At or Now. The composer limits the choices to the user’s permissions. |
toRequest() |
The post as the management API takes it. |
The server still checks everything when the post is saved, so an action can’t save what the user couldn’t.
Composer panels
Section titled “Composer panels”umbSocialComposerPanel puts an element under the composer’s text editor: for example a checklist, or a check against your brand rules. Like a composer action, it gets the composer’s state as state (the same object as the post changes), and can dispatch change to have the composer redraw and re-check the post.
import { UMB_SOCIAL_COMPOSER_PANEL, type ManifestUmbSocialComposerPanel } from "@umbsocial/core";
export const manifests: Array<ManifestUmbSocialComposerPanel> = [ { type: UMB_SOCIAL_COMPOSER_PANEL, alias: "My.Social.ComposerPanel.BrandRules", name: "Brand rules", element: () => import("./brand-rules-panel.element.js"), },];Queue and calendar items
Section titled “Queue and calendar items”umbSocialQueueItemAction puts an element with each post’s actions in the queue (next to Edit) and on each post in the calendar: for example “Duplicate”. umbSocialQueueItemDetail puts a small, read-only element inline on each post, after its status: a count or a badge. Both get the post as post (its key, text, status, scheduled time, image and each channel’s target). An action can dispatch change to have the page reload.
A detail is drawn once per post, so don’t fetch from it per post. The queue and the calendar provide UMB_SOCIAL_QUEUE_CONTEXT (type UmbSocialQueueContext) with the posts on screen, so you can fetch once for the page:
| Member | Use |
|---|---|
posts |
An observable of the posts on screen (Array<PostView>). |
keys |
An observable of their keys. |
getPosts(), getKeys() |
The same, now. |
Each time the page shows a new set of posts (another week, month or filter), it also raises a umb-social-queue-loaded event (UmbSocialQueueLoadedEvent, bubbling and composed) with their keys.
import { customElement, html, nothing, property, state } from "@umbraco-cms/backoffice/external/lit";import { UmbLitElement } from "@umbraco-cms/backoffice/lit-element";import { UMB_SOCIAL_QUEUE_CONTEXT, type PostView } from "@umbsocial/core";import { MY_COMMENTS_CONTEXT } from "./comments.context.js";
@customElement("my-comment-count")export class MyCommentCountElement extends UmbLitElement { @property({ attribute: false }) post?: PostView; @state() private _counts: Record<string, number> = {};
constructor() { super(); // Your own context, provided once for the page, fetches the counts for all the keys in one request and shares them. this.consumeContext(UMB_SOCIAL_QUEUE_CONTEXT, (queue) => { this.consumeContext(MY_COMMENTS_CONTEXT, (comments) => { this.observe(queue?.keys, (keys) => comments?.load(keys ?? [])); this.observe(comments?.counts, (counts) => (this._counts = counts ?? {})); }); }); }
override render() { const count = this.post ? this._counts[this.post.key] : undefined; return count ? html`<uui-tag look="secondary">${count}</uui-tag>` : nothing; }}
export default MyCommentCountElement;Post actions
Section titled “Post actions”umbSocialPostAction puts an element in a post’s detail panel on the Published page: for example, a button that sends the post’s results to your reporting tool. Your element gets the post as its post property (its key, text, status, scheduled time, image and each channel’s target: status, live link, error and Automate run). Dispatch a change event to have the list reload, after your action changes something.
import { UMB_SOCIAL_POST_ACTION, type ManifestUmbSocialPostAction } from "@umbsocial/core";
export const manifests: Array<ManifestUmbSocialPostAction> = [ { type: UMB_SOCIAL_POST_ACTION, alias: "My.Social.PostAction.Report", name: "Send to reporting", element: () => import("./report-action.element.js"), },];Channel actions
Section titled “Channel actions”umbSocialChannelAction puts an element with each channel’s actions in Settings › Social › Connections: for example “Copy to another site”. Your element gets the channel as channel (its key, name, network, options and whether it is enabled or paused). Dispatch change to have the list reload.
import { UMB_SOCIAL_CHANNEL_ACTION, socialPermission, SOCIAL_VERB, type ManifestUmbSocialChannelAction } from "@umbsocial/core";
export const manifests: Array<ManifestUmbSocialChannelAction> = [ { type: UMB_SOCIAL_CHANNEL_ACTION, alias: "My.Social.ChannelAction.Copy", name: "Copy to another site", element: () => import("./copy-channel-action.element.js"), conditions: [socialPermission(SOCIAL_VERB.manageSettings)], },];Adding to the footer bar
Section titled “Adding to the footer bar”The composer’s buttons are ordinary workspaceAction and workspaceActionMenuItem extensions, so you can add your own the Umbraco way: give it the condition { alias: "Umb.Condition.WorkspaceAlias", match: "UmbSocial.Workspace.Compose" }. To add an item to the main button’s menu, set forWorkspaceActions to [ADD_TO_QUEUE_ACTION, SCHEDULE_ACTION].
@umbsocial/core exports the aliases, so you don’t have to copy strings:
| Export | Alias |
|---|---|
ADD_TO_QUEUE_ACTION |
UmbSocial.WorkspaceAction.Compose.AddToQueue, the main button when the post has no time yet. |
SCHEDULE_ACTION |
UmbSocial.WorkspaceAction.Compose.Schedule, the main button when the post has a time. |
UMB_SOCIAL_SAVED_POST_CONDITION |
UmbSocial.Condition.SavedPost: the composer is editing a saved post (not a new one). |
UMB_SOCIAL_POST_HAS_TIME_CONDITION |
UmbSocial.Condition.PostHasTime: with match: true, the post has a time; with match: false, it hasn’t. |
The page actions context
Section titled “The page actions context”An action reaches the composer through UMB_SOCIAL_PAGE_ACTIONS_CONTEXT (type UmbSocialPageActionsContext), provided for Social’s workspaces. It tells your action about the page, and runs the page’s own save and delete:
| Member | Use |
|---|---|
state |
An observable of SocialPageState: busy (a save is running, or the post hasn’t loaded or couldn’t be), readOnly (the post can’t be changed now), postKey (the saved post, or undefined for a new one), postStatus (its status, such as Draft or AwaitingApproval), atUtc (its time) and timeZone (the site’s). |
postKey, atUtc |
Observables of those two parts. |
getState() |
The state now. |
save(when?, atUtc?) |
Saves the post as the composer’s own buttons do (Umbraco’s validation, the save, a notification, then back to the queue), with when one of Draft, Queue, At (with atUtc) or Now. A refusal is shown as a notification. |
remove() |
Deletes the saved post, after asking the user to confirm. |
import type { UmbControllerHost } from "@umbraco-cms/backoffice/controller-api";import { UmbWorkspaceActionBase, type UmbWorkspaceActionArgs } from "@umbraco-cms/backoffice/workspace";import { UMB_SOCIAL_PAGE_ACTIONS_CONTEXT, type UmbSocialPageActionsContext } from "@umbsocial/core";
export class MySaveForReviewAction extends UmbWorkspaceActionBase { #page?: UmbSocialPageActionsContext;
constructor(host: UmbControllerHost, args: UmbWorkspaceActionArgs<never>) { super(host, args); this.consumeContext(UMB_SOCIAL_PAGE_ACTIONS_CONTEXT, (page) => { this.#page = page; // Like Social's own buttons: disabled while the page is busy or the post can't be changed. this.observe(page?.state, (s) => (s && (s.busy || s.readOnly) ? this.disable() : this.enable())); }); }
override async execute() { await this.#page?.save("Draft"); }}
export { MySaveForReviewAction as api };To ask for a time the way Schedule for a time… does, open UMB_SOCIAL_SCHEDULE_AT_MODAL with { atUtc, timeZone } (UmbSocialScheduleAtModalData); it returns { atUtc } (UmbSocialScheduleAtModalValue), in UTC.
Also in @umbsocial/core
Section titled “Also in @umbsocial/core”SOCIAL_VERBandsocialPermission(verb): the permission verbs, and the condition for “the user has this verb”, for your own manifests.SOCIAL_PAGESandsocialPage(key): Social’s pages, with their workspace aliases and the verb each needs.errorMessage(code)andcodesFor(error): the localisation term for an error code, and the codes in a failed request.UMB_SOCIAL_LICENSE_CONTEXT: the licence status and the edition’s allowance.UMB_SOCIAL_PAGE_ACTIONS_CONTEXT,UMB_SOCIAL_SCHEDULE_AT_MODALand the footer bar’s action and condition aliases (see Adding to the footer bar).UMB_SOCIAL_QUEUE_CONTEXT,UMB_SOCIAL_QUEUE_LOADED_EVENTandUmbSocialQueueLoadedEvent(see Queue and calendar items), and thePostViewtype.
Following Umbraco’s patterns
Section titled “Following Umbraco’s patterns”Social’s screens use Umbraco’s own building blocks (UUI components, umb-property-layout, Umbraco’s validation, workspace actions and menu items in the footer bar, its modals and its media picker), so they look and behave like the rest of the backoffice and change with it. Do the same in your extensions: use UUI and Umbraco’s components rather than your own, and they will keep matching Social’s screens.
Loaders. Show waiting the way Umbraco does:
- The first time a view or modal loads, show a centred
uui-loaderin place of the content, never an empty page or a “nothing here” message before the data has arrived. - When data that is already on screen is reloaded (a filter, another week, a re-check), keep the content and show a
uui-loader-barat the top of its box or view. - A button that starts a request shows
state="waiting"while it runs, then"success"or"failed", as Umbraco’s buttons do. Workspace actions in the footer bar already do this, so don’t add it to them. - A small part of a screen that is waiting (a link card being read, an image preview) shows a
uui-loader-circlein that part only, and the rest of the screen stays usable. - A load that fails ends the loader: show the error, never a loader that never ends.