### Execute Custom Command Logic with Command Implementation Source: https://context7.com/sitefinity/sitefinity-admin-app-extensions-samples/llms.txt Implements the Command interface to define the logic executed when a custom command is triggered. This example demonstrates navigating to a print preview route with query parameters indicating the data item. ```typescript import { Injectable } from "@angular/core"; import { Router } from "@angular/router"; import { Observable, from } from "rxjs"; import { Command, ExecutionContext } from "@progress/sitefinity-adminapp-sdk/app/api/v1"; @Injectable() export class PrintPreviewCommand implements Command { constructor(protected router: Router) { } execute(context: ExecutionContext): Observable { const dataItem = context.data.dataItem; const currentQueryParams = { entitySet: dataItem.metadata.setName, id: dataItem.key, provider: dataItem.provider }; const url = `/print-preview`; const navPromise = this.router.navigate([url], { queryParams: currentQueryParams }); return from(navPromise); } } ``` -------------------------------- ### Sitefinity Custom DAM Provider: loadMediaSelector Method (TypeScript) Source: https://github.com/sitefinity/sitefinity-admin-app-extensions-samples/blob/master/library-extender/README.md The `loadMediaSelector` method handles the instantiation and display of the DAM widget. This example demonstrates loading a dynamic script, configuring the widget (e.g., for Cloudinary), and attaching event handlers for asset selection and errors. ```typescript loadMediaSelector(damWrapper: HTMLElement, mediaEntityData: EntityData, allowMultiSelect: boolean): void { if (!this.areSettingsPropertiesValid()) { this.error("Custom media selector cannot be loaded."); return; } this.loadDynamicScript(this.getPropertyValue("ScriptUrl")).subscribe(() => { if (typeof cloudinary === "undefined") { this.error("Custom media selector cannot be loaded."); return; } let config = { cloud_name: this.getPropertyValue("CloudName"), api_key: this.getPropertyValue("ApiKey"), multiple: allowMultiSelect, inline_container: `.${damWrapper.className.replace(/\s/g, ".")}`, remove_header: true, integration: { type: "custom_progress_sitefinity_connector_for_cloudinary", platform: "admin app extensions", version: "1.0", environment: null } }; const handlers = { insertHandler: this.insertHandler.bind(this), errorHandler: this.error.bind(this) }; try { const mediaLibrary = cloudinary.createMediaLibrary(config, handlers); mediaLibrary.show(config); } catch (error) { this.error(error); } }, () => { this.error("Custom media selector cannot be loaded."); }); } ``` -------------------------------- ### Sitefinity Custom DAM Provider: isSupported Method (TypeScript) Source: https://github.com/sitefinity/sitefinity-admin-app-extensions-samples/blob/master/library-extender/README.md Implement the `isSupported` method to indicate whether your custom provider is compatible with the DAM provider type enabled on the server. This example checks if the `providerTypeName` matches a predefined constant. ```typescript isSupported(providerTypeName: string): boolean { return providerTypeName === CUSTOM_PROVIDER_TYPE_NAME; } ``` -------------------------------- ### Implement Item Lifecycle Hooks in Sitefinity Admin App Source: https://context7.com/sitefinity/sitefinity-admin-app-extensions-samples/llms.txt This example demonstrates how to use ItemHooksProvider to intercept and execute custom logic during content item lifecycle events in Sitefinity's list and edit views. It covers hooks for item loading, changes, initialization, and unloading, allowing for pre-processing or post-processing of data. Dependencies include '@progress/sitefinity-adminapp-sdk'. ```typescript import { Injectable, ClassProvider } from "@angular/core"; import { Observable, ReplaySubject } from "rxjs"; import { ItemHooksProvider, ITEM_HOOKS_PROVIDER_TOKEN, DataItem, EditLifecycleHookParam, ListLifecycleHookParam } from "@progress/sitefinity-adminapp-sdk/app/api/v1"; @Injectable() class CustomItemHooksProvider implements ItemHooksProvider { onItemLoaded(item: DataItem): void { if (item.data) { console.log(`Item is loaded: ${item.data.Title}`); } else { console.log(`A new item is being created`); } } onEditItemChanged(data: EditLifecycleHookParam): Observable { return this.executeOperation(`Item changed: ${data.item?.data?.Title || "No item"}`); } onEditItemInitializing(data: EditLifecycleHookParam): Observable { return this.executeOperation(`Item initializing: ${data.item?.data?.Title || "No item"}`); } onEditItemUnloading(data: EditLifecycleHookParam): Observable { return this.executeOperation(`Item unloading: ${data.item?.data?.Title || "No item"}`); } afterEditItemInit(data: EditLifecycleHookParam): Observable { return this.executeOperation(`After edit item init: ${data.item?.data?.Title || "No item"}`); } onGridItemsChanged(data: ListLifecycleHookParam): Observable { return this.executeOperation(`Grid items changing: ${data.items?.length ? data.items.map(x => x.data.Title) : "No items"}`); } onGridItemsInitializing(data: ListLifecycleHookParam): Observable { return this.executeOperation(`Grid items initializing: ${data.items?.length ? data.items.map(x => x.data.Title) : "No items"}`); } onGridItemsUnloading(data: ListLifecycleHookParam): Observable { return this.executeOperation(`Grid items unloading: ${data.items?.length ? data.items.map(x => x.data.Title) : "No items"}`); } afterGridInit(data: ListLifecycleHookParam): Observable { return this.executeOperation(`After grid init: ${data.items?.length ? data.items.map(x => x.data.Title) : "No items"}`); } private executeOperation(message: string): Observable { const result$ = new ReplaySubject(); console.log(message); result$.next(); return result$.asObservable(); } } export const ITEM_HOOKS_PROVIDER: ClassProvider = { multi: true, provide: ITEM_HOOKS_PROVIDER_TOKEN, useClass: CustomItemHooksProvider }; ``` -------------------------------- ### Override Widget Designer Fields with Custom Component Source: https://context7.com/sitefinity/sitefinity-admin-app-extensions-samples/llms.txt This example demonstrates how to override individual fields within a widget designer using the `FieldsProvider`. It customizes the 'AllProperties' widget's short text fields by returning a custom write component, `CustomInputWriteComponent`. ```typescript import { Injectable, ClassProvider } from "@angular/core"; import { FIELDS_PROVIDER_TOKEN, FieldData, FieldsProvider, SettingsBase } from "@progress/sitefinity-adminapp-sdk/app/api/v1"; import { FieldRegistration, FieldTypes } from "@progress/sitefinity-adminapp-sdk/app/api/v1"; import { CustomInputWriteComponent } from './custom-field-write.component'; @Injectable() export class WidgetEditorCustomFieldsProvider implements FieldsProvider { overrideField(fieldRegistryKey: FieldData): FieldRegistration { // fieldRegistryKey.typeName is prefixed with "widget-" for widget fields if (fieldRegistryKey.fieldType === FieldTypes.shortText && fieldRegistryKey.typeName && fieldRegistryKey.typeName.startsWith("widget-AllProperties")) { return { writeComponent: CustomInputWriteComponent, settingsType: SettingsBase }; } } } export const WIDGET_EDITOR_CUSTOM_FIELDS_PROVIDER: ClassProvider = { provide: FIELDS_PROVIDER_TOKEN, useClass: WidgetEditorCustomFieldsProvider, multi: true }; ``` -------------------------------- ### TypeScript: Custom System Notification Icon Provider Source: https://github.com/sitefinity/sitefinity-admin-app-extensions-samples/blob/master/custom-system-notifications-icons/README.MD Implements a custom icon provider for Sitefinity system notifications. It overrides the default icon for specific notification keys or provides custom icons for new notifications. This example uses font awesome icons and a switch statement to map `NotificationKey` to icon classes. ```typescript const DEFAULT_ICON = "check"; const FIRST_CUSTOM_ICON = "user"; const VIDEO_PUBLISHED_ICON = "film"; const NEW_FORM_RESPONSE_ICON = "form-response"; @Injectable() export class CustomSystemNotificationIconProvider implements SystemNotificationIconProvider { parseIcon(key: string): string { switch (key) { // custom icons case "user-created": return FIRST_CUSTOM_ICON; case "video-published": return VIDEO_PUBLISHED_ICON; // override the default form resoinse notification icon case "subscribed-responses": return NEW_FORM_RESPONSE_ICON; // override the default icon default: return DEFAULT_ICON; } } } ``` -------------------------------- ### Integrate Cloudinary DAM with Sitefinity Admin App Extensions (TypeScript) Source: https://context7.com/sitefinity/sitefinity-admin-app-extensions-samples/llms.txt This TypeScript code demonstrates how to create a custom DAM provider for Sitefinity Admin App Extensions to integrate with Cloudinary. It handles loading the Cloudinary media library, selecting assets, and mapping them to Sitefinity's DamAsset structure, including custom metadata. It requires Angular core modules and Sitefinity admin app SDK. ```typescript import { ClassProvider, Injectable, NgZone } from "@angular/core"; import { DamProviderBase, DAM_PROVIDER_TOKEN } from "@progress/sitefinity-adminapp-sdk/app/api/v1"; import { DamAsset } from "@progress/sitefinity-adminapp-sdk/app/api/v1/dam/dam-asset"; import { EntityData } from "@progress/sitefinity-adminapp-sdk/app/api/v1/metadata/entity-data"; declare const cloudinary: any; const CLOUDINARY_PROVIDER_TYPE_NAME = "CloudinaryBlobStorageProvider"; const CLOUDINARY_ID_QUERY_PARAM = "sf_cl_id"; const CLOUDINARY_VERSION_QUERY_PARAM = "sf_cl_version"; @Injectable() class CustomCloudinaryDamProvider extends DamProviderBase { constructor(zone: NgZone) { super(zone); } isSupported(providerTypeName: string): boolean { return providerTypeName === CLOUDINARY_PROVIDER_TYPE_NAME; } loadMediaSelector(damWrapper: HTMLElement, mediaEntityData: EntityData, allowMultiSelect: boolean): void { if (!this.areSettingsPropertiesValid()) { this.error("Custom media selector cannot be loaded."); return; } this.loadDynamicScript(this.getPropertyValue("ScriptUrl")).subscribe(() => { if (typeof cloudinary === "undefined") { this.error("Custom media selector cannot be loaded."); return; } const config = { cloud_name: this.getPropertyValue("CloudName"), api_key: this.getPropertyValue("ApiKey"), multiple: allowMultiSelect, inline_container: `.${damWrapper.className.replace(/\s/g, ".")}`, remove_header: true, integration: { type: "custom_progress_sitefinity_connector_for_cloudinary", platform: "admin app extensions", version: "1.0", environment: null } }; const handlers = { insertHandler: this.insertHandler.bind(this), errorHandler: this.error.bind(this) }; try { cloudinary.openMediaLibrary(config, handlers); } catch (error) { this.error(error); } }, () => { this.error("Custom media selector cannot be loaded."); }); } private insertHandler(data): void { this.mediaSelected(); const damAssets: DamAsset[] = []; if (data.assets) { data.assets.forEach(asset => { const damAsset: DamAsset = this.getDamAsset(asset); damAssets.push(damAsset); }); this.assetsSelected(damAssets); } else { this.error("FAILED_TO_INSERT_MEDIA_ASSET"); } } private areSettingsPropertiesValid(): boolean { return !!this.getPropertyValue("CloudName") && !!this.getPropertyValue("ScriptUrl") && !!this.getPropertyValue("ApiKey"); } private getDamAsset(asset: any): DamAsset { const slashIndex = asset.public_id.lastIndexOf("/"); let title = asset.public_id.substring(slashIndex + 1); asset.format = asset.format || this.getExtensionFromFileName(title); title = this.getFileNameWithoutExtension(title); let url: string = asset.secure_url; if (asset.derived && asset.derived.length) { url = asset.derived[0].secure_url; asset.format = this.getExtensionFromFileName(url) || asset.format; } const appendSymbol = url.indexOf("?") === -1 ? "?" : "&"; url += `${appendSymbol}${CLOUDINARY_ID_QUERY_PARAM}=${asset.public_id}&${CLOUDINARY_VERSION_QUERY_PARAM}=${asset.version}`; const damAsset: DamAsset = { title: title, mimeType: null, extension: `.${asset.format}`, width: Math.floor(asset.width) || null, height: Math.floor(asset.height) || null, size: Math.floor(asset.bytes), url: url }; // Set custom fields on imported MediaContent damAsset.additionalProperties = { "MyCustomField": "custom field value" }; if (asset.context?.custom?.alt) { damAsset.additionalProperties["AlternativeText"] = asset.context.custom.alt; } if (asset.context?.custom?.author) { damAsset.additionalProperties["Author"] = asset.context?.custom?.author; } return damAsset; } private getExtensionFromFileName(fileName: string): string { const nameArray = fileName.split("."); if (nameArray.length > 1) { return nameArray.pop(); } return ""; } private getFileNameWithoutExtension(fileName: string): string { const slashIndex = fileName.lastIndexOf("."); if (slashIndex !== -1) { return fileName.substring(0, slashIndex); } return fileName; } } ``` -------------------------------- ### Define Custom Command Category - TypeScript Source: https://github.com/sitefinity/sitefinity-admin-app-extensions-samples/blob/master/commands-extender/README.md This example shows how to define a custom command category in the Sitefinity Admin App. It creates a `CommandCategory` object with a `name` property to specify where the commands will be listed and a `title` property for the display name in the Actions menu. ```typescript const customCommandCategory: CommandCategory = { name: "Custom", title: "Custom commands" }; ``` -------------------------------- ### Sitefinity Custom DAM Provider Implementation (TypeScript) Source: https://github.com/sitefinity/sitefinity-admin-app-extensions-samples/blob/master/library-extender/README.md This snippet shows the basic structure for a custom DAM provider in Sitefinity using TypeScript. It extends `DamProviderBase` and includes placeholders for `isSupported` and `loadMediaSelector` methods, along with necessary imports from the Sitefinity SDK. ```typescript import { ClassProvider, Injectable } from "@angular/core"; import { EntityData, DamAsset, DamProviderBase, DAM_PROVIDER_TOKEN } from "@progress/sitefinity-adminapp-sdk/app/api/v1"; declare var cloudinary: any; const CUSTOM_MEDIA_CANNOT_BE_LOADED = "Custom media selector cannot be loaded."; const CUSTOM_PROVIDER_TYPE_NAME = "CustomCloudinaryBlobStorageProvider"; @Injectable() class CustomDamProvider extends DamProviderBase { isSupported(providerTypeName: string): boolean { ... } loadMediaSelector(damWrapper: HTMLElement, mediaEntityData: EntityData, allowMultiSelect: boolean): void { ... } } ``` -------------------------------- ### Angular Module Registration for Sitefinity Commands Source: https://context7.com/sitefinity/sitefinity-admin-app-extensions-samples/llms.txt This snippet shows how to register custom commands as providers within an Angular module using the multi-provider pattern. This is essential for enabling multiple extensions to coexist within Sitefinity's Admin App. It imports necessary Angular core modules and custom command providers. ```typescript import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { COMMANDS_PROVIDER } from './commands-provider'; import { PrintPreviewCommand } from './print-preview.command'; import { ListSelectedItemsCommand } from './list-selected-items.command'; import { NotificationCommand } from './notification.command'; @NgModule({ declarations: [ // Component declarations if needed ], providers: [ COMMANDS_PROVIDER, PrintPreviewCommand, ListSelectedItemsCommand, NotificationCommand ], imports: [ CommonModule ], entryComponents: [ // Dynamic components that are loaded at runtime ] }) export class CommandsExtenderModule { } // Export for use in other modules export { COMMANDS_PROVIDER }; ``` -------------------------------- ### Sample Related Data TreeNodeComponentProvider Implementation Source: https://github.com/sitefinity/sitefinity-admin-app-extensions-samples/blob/master/tree/README.md This TypeScript sample demonstrates a functional implementation of the TreeNodeComponentProvider. It checks for specific features and entity sets to return custom component data, allowing for tailored display of related data in the Admin App. ```typescript import { ComponentData } from "@progress/sitefinity-component-framework"; import { TreeNodeComponentProvider } from "@progress/sitefinity-adminapp-sdk/app/api/v1"; import { TreeNodeComponentFeatures } from "@progress/sitefinity-adminapp-sdk/app/api/v1/tree/custom-tree-node-component-features"; import { RelatedDataCustomComponent } from "./related-data-custom.component"; export class RelatedDataTreeNodeComponentProvider implements TreeNodeComponentProvider { getComponentData(feature: TreeNodeComponentFeatures, entitySet: string): ComponentData { if (feature === TreeNodeComponentFeatures.RelatedData && entitySet === "newsitems") { const componentData: ComponentData = { type: RelatedDataCustomComponent }; return componentData; } return null; } } ``` -------------------------------- ### Extend Rich Text Editor Configuration with EditorConfigProvider in TypeScript Source: https://context7.com/sitefinity/sitefinity-admin-app-extensions-samples/llms.txt This sample shows how to extend the Rich Text Editor in Sitefinity Admin App using EditorConfigProvider. It allows adding custom toolbar items, removing existing ones, and configuring Kendo UI Editor settings. Dependencies include Sitefinity Admin App SDK and Kendo UI Editor. It returns ToolBarItem arrays and configuration objects. ```typescript import { ClassProvider, Injectable } from "@angular/core"; import { ToolBarItem, EditorConfigProvider, EDITOR_CONFIG_TOKEN } from "@progress/sitefinity-adminapp-sdk/app/api/v1"; require("!style-loader!css-loader!./editor-config-provider.css"); @Injectable() class WordCountProvider implements EditorConfigProvider { getToolBarItems(editorHost: any): ToolBarItem[] { const wordsCount = () => { const editor = editorHost.getKendoEditor(); const editorValue = this.stripHTML(editor.value()); const count = editorValue ? editorValue.split(" ").length : 0; alert(`Words count: ${count}`); }; const CUSTOM_TOOLBAR_ITEM: ToolBarItem = { name: "Words-count", tooltip: "Words count", ordinal: 5, exec: wordsCount }; return [CUSTOM_TOOLBAR_ITEM]; } getToolBarItemsNamesToRemove(): string[] { // Example: return ["embed"] to remove embed toolbar item return []; } configureEditor(configuration: any) { // Modify Kendo UI Editor configuration configuration.pasteCleanup.span = false; return configuration; } private stripHTML(html: string): string { const tmp = document.createElement("DIV"); tmp.innerHTML = html; return tmp.textContent || tmp.innerText || ""; } } export const WORD_COUNT_PROVIDER: ClassProvider = { multi: true, provide: EDITOR_CONFIG_TOKEN, useClass: WordCountProvider }; ``` -------------------------------- ### Customize Admin App Appearance with ThemeProvider in Sitefinity (TypeScript) Source: https://context7.com/sitefinity/sitefinity-admin-app-extensions-samples/llms.txt Define custom themes using CSS variables to rebrand the Admin App interface with custom colors for buttons and UI elements. The ThemeProvider allows you to specify theme items, each with a name and an array of theme variables. This requires importing relevant modules from '@progress/sitefinity-adminapp-sdk/app/api/v1/theme/'. ```typescript import { Injectable, ClassProvider } from "@angular/core"; import { ThemeProvider, THEME_TOKEN } from "@progress/sitefinity-adminapp-sdk/app/api/v1/theme/theme-provider"; import { ThemeItem } from "@progress/sitefinity-adminapp-sdk/app/api/v1/theme/theme-item"; import { ThemeVariables } from "@progress/sitefinity-adminapp-sdk/app/api/v1/theme/theme-variables"; import { ThemeVariablesKeyValuePair } from "@progress/sitefinity-adminapp-sdk/app/api/v1/theme/theme-variable-key-value-pair"; @Injectable() class SampleThemeProvider implements ThemeProvider { private sampleThemeVariables: Array = [{ key: ThemeVariables.DefaultButtonBorderColor, value: "#333" }, { key: ThemeVariables.DefaultButtonBackgroundColor, value: "#FFF" }, { key: ThemeVariables.DefaultButtonColor, value: "#000" }, { key: ThemeVariables.ActionButtonBorderColor, value: "#006CD9" }, { key: ThemeVariables.ActionButtonBackgroundColor, value: "#006CD9" }, { key: ThemeVariables.ActionButtonColor, value: "#FFF" }, { key: ThemeVariables.ActionButtonInteractionBorderColor, value: "#0053C0" }, { key: ThemeVariables.ActionButtonInteractionBackgroundColor, value: "#0053C0" }, { key: ThemeVariables.ActionButtonDisabledBorderColor, value: "#BFD4F3" }]; private themes: Array = [{ name: "Sample", themeVariables: this.sampleThemeVariables }]; getThemes(): Array { return this.themes; } } export const SAMPLE_THEME_PROVIDER: ClassProvider = { multi: true, provide: THEME_TOKEN, useClass: SampleThemeProvider }; ``` -------------------------------- ### Add Custom Actions to Admin Interface with CommandProvider Source: https://context7.com/sitefinity/sitefinity-admin-app-extensions-samples/llms.txt Registers custom commands that appear in action menus within the Sitefinity Admin App. This provider supports list views, edit views, and bulk operations, and it uses Angular's dependency injection system. The commands can be categorized and configured with specific properties. ```typescript import { CommandProvider, CommandsData, COMMANDS_TOKEN, CommandsTarget, CommandModel, CommandCategory } from "@progress/sitefinity-adminapp-sdk/app/api/v1"; import { Observable, of } from "rxjs"; import { ClassProvider, Injectable } from "@angular/core"; const CUSTOM_CATEGORY: CommandCategory = { name: "Custom", title: "Custom commands" }; const PRINT_PREVIEW_COMMAND: CommandModel = { name: "PrintPreview", title: "Print preview", ordinal: -1, category: "Custom" }; @Injectable() class CustomCommandProvider implements CommandProvider { getCommands(data: CommandsData): Observable { const commands: CommandModel[] = []; if (data.target === CommandsTarget.List && data.dataItem) { const command = Object.assign({}, PRINT_PREVIEW_COMMAND); command.token = { type: PrintPreviewCommand, properties: { dataItem: data.dataItem } }; commands.push(command); } return of(commands); } getCategories(data: CommandsData): Observable { return of([CUSTOM_CATEGORY]); } } export const COMMANDS_PROVIDER: ClassProvider = { useClass: CustomCommandProvider, multi: true, provide: COMMANDS_TOKEN }; ``` -------------------------------- ### Sitefinity Custom DAM Provider Registration (TypeScript) Source: https://github.com/sitefinity/sitefinity-admin-app-extensions-samples/blob/master/library-extender/README.md This snippet shows how to register your custom DAM provider using `ClassProvider` in Sitefinity's `library-extender/index.ts`. It provides the necessary configuration to inject your `CustomDamProvider` into the `DAM_PROVIDER_TOKEN`. ```typescript export const CUSTOM_DAM_PROVIDER: ClassProvider = { multi: true, provide: DAM_PROVIDER_TOKEN, useClass: CustomDamProvider }; ``` -------------------------------- ### Implement TreeNodeComponentProvider Interface Source: https://github.com/sitefinity/sitefinity-admin-app-extensions-samples/blob/master/tree/README.md This TypeScript code defines a basic implementation of the TreeNodeComponentProvider interface. It includes the necessary imports and the structure for the getComponentData method, which is essential for providing custom component data to the Admin App. ```typescript import { ComponentData } from "@progress/sitefinity-component-framework"; import { TreeNodeComponentProvider } from "@progress/sitefinity-adminapp-sdk/app/api/v1"; import { TreeNodeComponentFeatures } from "@progress/sitefinity-adminapp-sdk/app/api/v1/tree/custom-tree-node-component-features"; export class RelatedDataTreeNodeComponentProvider implements TreeNodeComponentProvider { getComponentData(feature: TreeNodeComponentFeatures, entitySet: string): ComponentData { } } ``` -------------------------------- ### Implement Custom CommandProvider in Sitefinity Admin App Source: https://github.com/sitefinity/sitefinity-admin-app-extensions-samples/blob/master/commands-extender/README.md This TypeScript code demonstrates how to implement the CommandProvider interface to register custom commands in the Sitefinity Admin App. It requires the @angular/core and @progress/sitefinity-adminapp-sdk packages. The getCommands method returns an Observable of CommandModel[], and getCategories returns an Observable of CommandCategory[]. ```typescript import { Injectable } from "@angular/core"; import { CommandCategory, CommandModel, CommandProvider, CommandsData } from "@progress/sitefinity-adminapp-sdk/app/api/v1"; import { Observable } from "rxjs"; @Injectable() class DynamicItemIndexCommandProvider implements CommandProvider { getCommands(data: CommandsData): Observable { return of([]); } getCategories(data: CommandsData): Observable { return of([]); } } ``` -------------------------------- ### Registering Sitefinity Extension Module Source: https://github.com/sitefinity/sitefinity-admin-app-extensions-samples/blob/master/tree/README.md This TypeScript code snippet demonstrates how to register a custom Angular module as a Sitefinity extension. It shows the import statement for the custom module and the call to `sitefinityExtensionsStore.addExtensionModule` to make the extension available in the Admin App. ```typescript import { SitefinityExtensionStore } from "@progress/sitefinity-adminapp-sdk/app/api/v1"; import { RelatedDateExtenderModule } from "./tree/related-data"; sitefinityExtensionsStore.addExtensionModule(RelatedDateExtenderModule); ``` -------------------------------- ### Implement CommandsFilter Interface in TypeScript Source: https://github.com/sitefinity/sitefinity-admin-app-extensions-samples/blob/master/commands-extender/README.md Demonstrates the basic structure for implementing the CommandsFilter interface in TypeScript. This involves creating a class that injects dependencies and implements the filter method to control command visibility. ```typescript import { Injectable } from "@angular/core"; import { CommandModel, CommandsData, CommandsFilter } from "@progress/sitefinity-adminapp-sdk/app/api/v1"; @Injectable() class CustomCommandsFilter implements CommandsFilter { filter(operations: CommandModel[], data: CommandsData): CommandModel[] { return []; } } ``` -------------------------------- ### Override Widget Designer UI with Custom Component Source: https://context7.com/sitefinity/sitefinity-admin-app-extensions-samples/llms.txt This snippet shows how to replace the default widget designer interface with a custom Angular component. It utilizes the `WidgetEditorViewProvider` to intercept and override the view for a specific widget, 'AllProperties', returning a custom component `CustomWidgetEditorComponent`. ```typescript import { Injectable, ClassProvider } from "@angular/core"; import { EditorComponentContext, EditorViewComponent, WidgetEditorViewProvider, WIDGET_VIEW_TOKEN } from "@progress/sitefinity-adminapp-sdk/app/api/v1"; import { CustomWidgetEditorComponent } from './custom-widget-editor.component'; @Injectable() export class CustomWidgetEditorViewProvider implements WidgetEditorViewProvider { overrideView(context: EditorComponentContext): EditorViewComponent { if (context.widgetName === "AllProperties") { return { componentData: { type: CustomWidgetEditorComponent } }; } } } export const CUSTOM_WIDGET_EDITOR_VIEW_TOKEN: ClassProvider = { useClass: CustomWidgetEditorViewProvider, multi: true, provide: WIDGET_VIEW_TOKEN }; ``` -------------------------------- ### Exporting Custom TreeNode Component Provider with Token Source: https://github.com/sitefinity/sitefinity-admin-app-extensions-samples/blob/master/tree/README.md This TypeScript code snippet shows how to export a custom TreeNode component provider using the Angular Dependency Injection token `CUSTOM_TREE_COMPONENT_TOKEN`. This is crucial for the Sitefinity Admin App to recognize and utilize the custom provider. ```typescript import { ClassProvider } from "@angular/core"; import { ComponentData } from "@progress/sitefinity-component-framework"; import { TreeNodeComponentProvider , CUSTOM_TREE_COMPONENT_TOKEN } from "@progress/sitefinity-adminapp-sdk/app/api/v1/tree"; import { TreeNodeComponentFeatures } from "@progress/sitefinity-adminapp-sdk/app/api/v1/tree/custom-tree-node-component-features"; import { RelatedDataCustomComponent } from "./related-data-custom.component"; export class RelatedDataTreeNodeComponentProvider implements TreeNodeComponentProvider { getComponentData(feature: TreeNodeComponentFeatures, entitySet: string): ComponentData { if (feature === TreeNodeComponentFeatures.RelatedData && entitySet === "newsitems") { const componentData: ComponentData = { type: RelatedDataCustomComponent }; return componentData; } return null; } } export const CUSTOM_TREE_COMPONENT_PROVIDER: ClassProvider = { useClass: RelatedDataTreeNodeComponentProvider, multi: true, provide: CUSTOM_TREE_COMPONENT_TOKEN }; ``` -------------------------------- ### React to Field Changes with FieldChangeService in Sitefinity Admin App (TypeScript) Source: https://context7.com/sitefinity/sitefinity-admin-app-extensions-samples/llms.txt Implement cross-field dependencies and validation by listening to field changes and updating related fields dynamically using the FieldChangeService. This service allows you to process changes for specific field types and update other fields accordingly. It requires importing necessary modules from the '@progress/sitefinity-adminapp-sdk'. ```typescript import { FieldChangeService, FieldWrapper, FIELDS_CHANGE_SERVICE_TOKEN } from "@progress/sitefinity-adminapp-sdk/app/api/v1"; import { Injectable } from "@angular/core"; const NEWS_TYPE_FULL_NAME = "Telerik.Sitefinity.News.Model.NewsItem"; const TITLE_FIELD_NAME = "Title"; const RICH_TEXT_EDITOR_FIELD_NAME = "Content"; @Injectable() export class NewsFieldsChangeService implements FieldChangeService { processChange(changedFieldName: string, changedValue: any, fields: FieldWrapper[]): void { // Mirror title field changes to content field if (changedFieldName === TITLE_FIELD_NAME) { const richTextEditorField = fields.find(x => x.fieldModel.key === RICH_TEXT_EDITOR_FIELD_NAME); if (richTextEditorField) { richTextEditorField.writeValue(changedValue); } } } canProcess(typeFullName: string): boolean { return typeFullName === NEWS_TYPE_FULL_NAME; } } export const NEWS_FIELDS_CHANGE_PROVIDER = { multi: true, provide: FIELDS_CHANGE_SERVICE_TOKEN, useClass: NewsFieldsChangeService }; ``` -------------------------------- ### Add Custom Command to Create View - TypeScript Source: https://github.com/sitefinity/sitefinity-admin-app-extensions-samples/blob/master/commands-extender/README.md This snippet demonstrates how to add a custom command to the create view's actions menu in the Sitefinity Admin App. It checks for the correct target and dataItem, then defines the command's properties and implementation token. The 'Custom' category is used, and a specific ordinal is assigned. ```typescript getCommands(data: CommandsData): Observable { const commands: CommandModel[] = []; if (data.target === CommandsTarget.Create && data.dataItem) { const commnad: CommandModel = { name: "exampleCommand", title: "Example Command", category: "Custom", ordinal: 1 } commnad.token = { type: CommandImplementationClass, properties: { dataItem: data.dataItem } }; commands.push(commnad); } return of(commands); } ``` -------------------------------- ### Filter Default Commands with CommandsFilter Source: https://context7.com/sitefinity/sitefinity-admin-app-extensions-samples/llms.txt Filters existing commands to modify their availability, such as removing specific actions like 'Delete' for certain content types. This allows for fine-grained control over the command menus in the Admin App. ```typescript import { CommandsFilter, CommandsData, COMMANDS_FILTER_TOKEN, CommandModel } from "@progress/sitefinity-adminapp-sdk/app/api/v1"; import { Injectable } from "@angular/core"; @Injectable() class CustomCommandsFilter implements CommandsFilter { filter(operations: CommandModel[], data: CommandsData): CommandModel[] { // Remove delete command for news items if (data.dataItem.metadata.setName === "newsitems") { return operations.filter(x => x.name !== "Delete"); } return operations; } } export const CUSTOM_COMMANDS_FILTER = { provide: COMMANDS_FILTER_TOKEN, useClass: CustomCommandsFilter, multi: true }; ``` -------------------------------- ### Add Custom Grid Columns with Angular Components in Sitefinity Source: https://context7.com/sitefinity/sitefinity-admin-app-extensions-samples/llms.txt This snippet shows how to use ListColumnsProvider to add custom columns to content list views in Sitefinity. It utilizes Angular components for rendering custom column data and allows for the removal of default columns. This requires the '@progress/sitefinity-adminapp-sdk' package. ```typescript import { Observable, of } from "rxjs"; import { ColumnModel, COLUMNS_TOKEN, EntityData, ListColumnsProvider } from "@progress/sitefinity-adminapp-sdk/app/api/v1"; import { ImageComponent } from "./image.component"; import { ClassProvider, Injectable } from "@angular/core"; const COLUMN_TO_REMOVE = "LastModified"; @Injectable() class DynamicItemIndexColumnsProvider implements ListColumnsProvider { private columnName: string = "image3"; private columnTitle: string = "Image"; getColumns(entityData: EntityData): Observable { const column: ColumnModel = { name: this.columnName, title: this.columnTitle, ordinal: 50, componentData: { type: ImageComponent } }; return of([column]); } getColumnsToRemove(entityData: EntityData): Observable { return of([COLUMN_TO_REMOVE]); } } export const COLUMNS_PROVIDER: ClassProvider = { useClass: DynamicItemIndexColumnsProvider, multi: true, provide: COLUMNS_TOKEN }; ``` -------------------------------- ### Register Sitefinity Extension Module Source: https://github.com/sitefinity/sitefinity-admin-app-extensions-samples/blob/master/tree/README.md This snippet shows how to register a Sitefinity extension module using the sitefinityExtensionsStore. It assumes the 'RelatedDateExtenderModule' is defined elsewhere and available in the scope. ```typescript declare var sitefinityExtensionsStore: SitefinityExtensionStore; sitefinityExtensionsStore.addExtensionModule(RelatedDateExtenderModule); ``` -------------------------------- ### Custom Angular Module for Sitefinity Extension Source: https://github.com/sitefinity/sitefinity-admin-app-extensions-samples/blob/master/tree/README.md This TypeScript code defines a custom Angular module for a Sitefinity extension. It declares and registers the custom component, includes necessary providers like the custom tree node component provider, and imports required modules. ```typescript import { CommonModule, DatePipe } from "@angular/common"; import { NgModule } from "@angular/core"; import { CUSTOM_TREE_COMPONENT_PROVIDER } from "./related-data-custom-tree-node-component-provider"; import { RelatedDataCustomComponent } from "./related-data-custom.component"; @NgModule({ declarations: [ RelatedDataCustomComponent ], entryComponents: [ RelatedDataCustomComponent ], providers: [ CUSTOM_TREE_COMPONENT_PROVIDER, DatePipe ], imports: [ CommonModule ] }) export class RelatedDateExtenderModule { /* empty */ } ``` -------------------------------- ### Customize Related Data Tree Nodes with Custom Component Source: https://context7.com/sitefinity/sitefinity-admin-app-extensions-samples/llms.txt This snippet shows how to customize the rendering of tree nodes for related data selectors using `TreeNodeComponentProvider`. It specifies a custom component, `RelatedDataCustomComponent`, for the 'newsitems' entity set when the `RelatedData` feature is requested. ```typescript import { ClassProvider } from "@angular/core"; import { ComponentData } from "@progress/sitefinity-component-framework"; import { TreeNodeComponentProvider, CUSTOM_TREE_COMPONENT_TOKEN } from "@progress/sitefinity-adminapp-sdk/app/api/v1/tree"; import { TreeNodeComponentFeatures } from "@progress/sitefinity-adminapp-sdk/app/api/v1/tree/custom-tree-node-component-features"; import { RelatedDataCustomComponent } from "./related-data-custom.component"; export class RelatedDataTreeNodeComponentProvider implements TreeNodeComponentProvider { getComponentData(feature: TreeNodeComponentFeatures, entitySet: string): ComponentData { if (feature === TreeNodeComponentFeatures.RelatedData && entitySet === "newsitems") { const componentData: ComponentData = { type: RelatedDataCustomComponent }; return componentData; } return null; } } export const CUSTOM_TREE_COMPONENT_PROVIDER: ClassProvider = { useClass: RelatedDataTreeNodeComponentProvider, multi: true, provide: CUSTOM_TREE_COMPONENT_TOKEN }; ``` -------------------------------- ### Override Content Field Rendering with FieldsProvider in TypeScript Source: https://context7.com/sitefinity/sitefinity-admin-app-extensions-samples/llms.txt This snippet demonstrates how to override default content field rendering in Sitefinity Admin App using the FieldsProvider. It allows specifying custom read and write components for specific fields or globally for field types. Dependencies include Sitefinity Admin App SDK. It takes FieldData as input and returns FieldRegistration or null. ```typescript import { Injectable, ClassProvider } from "@angular/core"; import { FIELDS_PROVIDER_TOKEN, FieldData, FieldsProvider, SettingsBase, FieldTypes } from "@progress/sitefinity-adminapp-sdk/app/api/v1"; import { FieldRegistration } from "@progress/sitefinity-adminapp-sdk/app/api/v1"; import { CustomInputReadonlyComponent } from "./custom-field-readonly.component"; import { CustomInputWriteComponent } from "./custom-field-write.component"; import { CustomShortTextSettings } from "./custom-field.settings"; @Injectable() export class CustomFieldsProvider implements FieldsProvider { private customFieldsMappings: RegistrationPair[]; constructor() { this.customFieldsMappings = []; // Override Title field for news items const customInputKey: FieldData = { fieldName: "Title", fieldType: FieldTypes.shortTextDefault, typeName: "newsitems" }; const customInputRegistration: FieldRegistration = { writeComponent: CustomInputWriteComponent, readComponent: CustomInputReadonlyComponent, settingsType: CustomShortTextSettings }; this.customFieldsMappings.push({ key: customInputKey, registration: customInputRegistration }); // Override all array of GUIDs fields globally const arrayOfGUIDsKey: FieldData = { fieldName: null, fieldType: FieldTypes.arrayOfGUIDs, typeName: null }; const arrayOfGUIDsRegistration: FieldRegistration = { writeComponent: ArrayOfGUIDsWriteComponent, readComponent: ArrayOfGUIDsReadonlyComponent, settingsType: SettingsBase }; this.customFieldsMappings.push({ key: arrayOfGUIDsKey, registration: arrayOfGUIDsRegistration }); } overrideField(fieldRegistryKey: FieldData): FieldRegistration { for (const pair of this.customFieldsMappings) { if (fieldRegistryKey.fieldName === pair.key.fieldName && fieldRegistryKey.fieldType === pair.key.fieldType && fieldRegistryKey.typeName === pair.key.typeName) { return pair.registration; } if (pair.key.fieldName === null && pair.key.typeName === null && fieldRegistryKey.fieldType === pair.key.fieldType) { return pair.registration; } } return null; } } export const CUSTOM_FIELDS_PROVIDER: ClassProvider = { provide: FIELDS_PROVIDER_TOKEN, useClass: CustomFieldsProvider, multi: true }; ```