EntityAction Setup

Registering the EntityAction:

To create a custom EntityAction, you’ll need to register it by creating some TypeScript files:

In the assets/src/actions/entity folder

manifest.ts

//import the entity type for Member
import { UMB_MEMBER_ENTITY_TYPE } from "@umbraco-cms/backoffice/member";
import { ManifestEntityAction } from "@umbraco-cms/backoffice/extension-registry";
//import our entity action definition
import { MemberEntityAction } from "./member.entity.action";

const entityAction: ManifestEntityAction = {
    type: 'entityAction',
    kind: 'default',
    alias: 'member.entity.action',
    name: 'member action',
    weight: -100,
    forEntityTypes: [
        UMB_MEMBER_ENTITY_TYPE //only appear for the Member entity
    ],
    api: MemberEntityAction,
    meta: {
        icon: 'icon-message',
        label: 'Resend Validation',
    },
    conditions: [{
        alias: "Umb.Condition.SectionAlias",
        match: "Umb.Section.Members"
    }]
}

export const manifests = [entityAction];

Customizing EntityActions:

  • You can attach a class to the EntityAction as part of the extension manifest.
  • This class will be instantiated when the action is triggered.
  • It has access to the host element, repository alias, and unique identifier (key) of the entity.
  • You can provide either a getHref method (for a link) or an execute method (for custom logic).

MemberEntityAction definition:

  • Here we attach the UmbMemberDetailRepository to instantiate hen action is triggered.
  • In the provided execute method, we open our custom modal.
  • We also add an onSubmit method handler to call our api with the unique identifier (key) of the entity.

member.entity.action.ts

import { UmbControllerHostElement } from "@umbraco-cms/backoffice/controller-api";
import { UmbEntityActionArgs, UmbEntityActionBase } from "@umbraco-cms/backoffice/entity-action";
import { UMB_MODAL_MANAGER_CONTEXT, UmbModalManagerContext } from "@umbraco-cms/backoffice/modal";
import { MEMBER_CUSTOM_MODAL } from "../../modal/modal-token.ts";
import { UmbMemberDetailRepository  } from '@umbraco-cms/backoffice/member';

export class MemberEntityAction extends UmbEntityActionBase<UmbMemberDetailRepository> {
    #modalManagerContext?: UmbModalManagerContext;
    constructor(host: UmbControllerHostElement, args: UmbEntityActionArgs<UmbMemberDetailRepository>)
    {
        super(host, args)
        // Fetch/consume the contexts & assign to the private fields
        this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, (instance) => {
            this.#modalManagerContext = instance;
        });
    }
    
    async execute() {
        //The modal does NOT return any data when closed (it does not submit)
        const modal = this.#modalManagerContext?.open(this, MEMBER_CUSTOM_MODAL, {
            data: {
                headline:'Resend Validation',
                content: 'Do you want to resend the validation Email?'
            }
        });

        await modal?.onSubmit().then(() => {
            const headers: Headers = new Headers()
            headers.set('Content-Type', 'application/json')
            headers.set('Accept', 'application/json')
            const request: RequestInfo = new Request('/sendvalidation/' + this.args.unique?.toString(), {
                method: 'GET',
                headers: headers,
            })
            // Send the request and print the response
            return fetch(request)
            .then(res => {
                console.log("got response:", res)
            })
        }).catch(() => {
            return;
        });
    }
}

 

Modal Dialog Setup

  • In much the same way as we did for the EntityAction, we need to create a few files to define the modal.

manifest.ts - this the declaration for the modal dialog

import { ManifestModal } from "@umbraco-cms/backoffice/extension-registry";

const modals: Array<ManifestModal> = [
    {
        type: 'modal',
        alias: 'member.custom.modal',
        name: 'Member custom modal',
        js: () => import('./modal-element.js')
    }
];

export const manifests = [...modals];

modal-element.ts - code that renders the dialog ui and registers methods to return data or cancel

import { customElement, html, state } from "@umbraco-cms/backoffice/external/lit";
import { UmbModalBaseElement } from "@umbraco-cms/backoffice/modal";
import { MemberCustomModalData, MemberCustomModalValue } from "./modal-token";

@customElement('member-custom-modal')
export class MemberCustomModalElement extends 
    UmbModalBaseElement<MemberCustomModalData, MemberCustomModalValue> 
{
    constructor() {
        super();
    }

    connectedCallback(): void {
        super.connectedCallback();    
    }

    @state()
    content: string = '';

    #handleConfirm() {
        
		this.modalContext?.submit();      
	}

	#handleCancel() {
		this.modalContext?.reject();
	}

    render() {
        return html`
            <umb-body-layout headline=${this.data?.headline ?? 'Custom dialog'}>
                <uui-box>
                    <h3>${this.data?.content}</h3>
                </uui-box>
                <uui-box>
                        <uui-button
                            id="submit"
                            color='positive'
                            look="primary"
                            label="Submit"
                            @click=${this.#handleConfirm}></uui-button>
                </uui-box>
                <div slot="actions">
                        <uui-button id="cancel" label="Cancel" @click="${this.#handleCancel}">Cancel</uui-button>
            </div>
            </umb-body-layout>
        `;
    }
}

export default MemberCustomModalElement;​

modal-token.ts (modal definition)

import { UmbModalToken } from "@umbraco-cms/backoffice/modal";

export interface MemberCustomModalData {
    headline: string;
    content: string;
}

export interface MemberCustomModalValue {
    content: string 
}

export const MEMBER_CUSTOM_MODAL = new UmbModalToken<MemberCustomModalData, MemberCustomModalValue>(
    "member.custom.modal",
    {
        modal: {
            type: 'sidebar',
            size: 'medium'
        }
    }
);

 

 

Register the new manifests

Finally we register the new manifests in our index.ts

import { UmbEntryPointOnInit } from '@umbraco-cms/backoffice/extension-api';
import { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';

// load up the manifests here.
import { manifests as entityActionManifests } from './actions/entity/manifest.ts';
import { manifests as modalManifests } from './modal/manifest.ts';

const manifests: Array<ManifestTypes> = [
    ...entityActionManifests,
    ...modalManifests
];

export const onInit: UmbEntryPointOnInit = (_host, extensionRegistry) => {
    
    // register them here. 
    extensionRegistry.registerMany(manifests);
};

It took a lot of help and hints from people on the Discord channel, lots of googling and reading various blog posts and Umbraco documentation. I finally came up with the code above, it works, but may not be entirely the correct approach laughing

In this article I will explain how to configure the TinyMCE rich text editor in Umbraco v14

This is my dive into the new Umbraco 14 backoffice to create a Member EntityAction in order to send an email to the selected member.

Previously known as Tree Actions, Entity Actions is a feature that provides a generic place for secondary or additional functionality for an entity type. An entity type can be a media, document and so on.

In this blog post I explain how to implement an email validation flow for Member registration.