ShowNewObjectWindow (Blank and UserDefined Template)
Overview
This sample creates a basic User Interface Extensibility Framework application consisting of one ShellUI module and dashboard which allow to create a new object with blank and user defined template.
This sample does not show how to create a local development folder or to deploy the code to the M-Files server. It is assumed that a local development folder already exists, and that is the location in which the development is occurring.
Creating the application structure
Creating the application definition file
Into this folder we will create an application definition file. This file must be named appdef.xml. The application will use version 5 of the client schema (as we are only targeting newer M-Files versions). The application will declare a single Shell UI module (with its code in main.js), and no dashboards.
appdef.xml
<?xml version="1.0"?>
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.m-files.com/schemas/appdef-client-v5.xsd">
<guid>4753229E-9382-4BCC-AEA9-9997E73D2370</guid>
<name>ShowNewObjectWindow</name>
<version>1.0</version>
<description>A demonstration application for the support of ShowNewObjectWindow. Covered Blank template and user defined template object creations.</description>
<publisher>M-Files Corporation</publisher>
<modules>
<module environment="shellui">
<file>main.js</file>
</module>
</modules>
<dashboards>
<dashboard id="MyDashboard">
<content>index.html</content>
</dashboard>
</dashboards>
</application>
Ensure that your application has a unique GUID by using a GUID generator.
Creating the module
Next we will create a module file to contain our actual application logic. At this point we will just register to be notified of main lifecycle events:
- We will declare a default entry point for the ShellUI module.
- We will react to the NewShellFrame event and obtain a reference to the shell frame.
- We will react to the shell frame’s Started event (as using the shell frame before this point will result in an exception).
main.js
// NOTE! This code is for demonstration purposes only and does not contain any kind of
// error handling. MUST be revised before using in production.
function OnNewShellUI( shellUI ) {
/// <summary>Executed by the UIX when a ShellUI module is started.</summary>
/// <param name="shellUI" type="MFiles.ShellUI">The shell UI object which was created.</param>
// This is the start point of a ShellUI module.
// Register to be notified when a new shell frame (MFiles.Event.NewShellFrame) is created.
shellUI.Events.Register(
MFiles.Event.NewShellFrame,
handleNewShellFrame );
}
function handleNewShellFrame( shellFrame ) {
/// <summary>Handles the OnNewNormalShellFrame event for an IShellUI.</summary>
/// <param name="shellFrame" type="MFiles.ShellFrame">The shell frame object which was created.</param>
// The shell frame was created but it cannot be used yet.
// The following line would throw an exception ("The object cannot be accessed, because it is not ready."):
// shellFrame.ShowMessage("A shell frame was created");
// Register to be notified when the shell frame is started.
shellFrame.Events.Register(
MFiles.Event.Started,
getShellFrameStartedHandler( shellFrame ) );
}
function getShellFrameStartedHandler( shellFrame ) {
/// <summary>Returns a function which handles the OnStarted event for an IShellFrame.</summary>
// The shell frame is now started and can be used.
return async () => {};
}
Adding a new right pane tab
Adding a right pane tab involves two steps:
- Creating a new tab using shellFrame.RightPane.AddTab.
- Set the dashboard and make the seletion.
main.js
// NOTE! This code is for demonstration purposes only and does not contain any kind of
// error handling. MUST be revised before using in production.
function OnNewShellUI( shellUI ) {
/// <summary>Executed by the UIX when a ShellUI module is started.</summary>
/// <param name="shellUI" type="MFiles.ShellUI">The shell UI object which was created.</param>
// This is the start point of a ShellUI module.
// Register to be notified when a new shell frame (MFiles.Event.NewShellFrame) is created.
shellUI.Events.Register(
MFiles.Event.NewShellFrame,
handleNewShellFrame );
}
function handleNewShellFrame( shellFrame ) {
/// <summary>Handles the OnNewShellFrame event for an IShellUI.</summary>
/// <param name="shellFrame" type="MFiles.ShellFrame">The shell frame object which was created.</param>
// Register to listen to the started event.
shellFrame.Events.Register(
MFiles.Event.Started,
getShellFrameStartedHandler( shellFrame ) );
function getShellFrameStartedHandler( shellFrame ) {
/// <summary>Gets a function to handle the Started event for shell frame.</summary>
/// <param name="shellFrame" type="MFiles.ShellFrame">The current shell frame object.</param>
/// <returns type="MFiles.Events.OnStarted">The event handler.</returns>
// Return the handler function for ShellFrame's Started event.
return async () => {
// Show the tab for new object creation.
const newObjectTab = await shellFrame.RightPane.AddTab( "newObject", "New Object", "_last");
newObjectTab.ShowDashboard( "MyDashboard", {} );
newObjectTab.Select();
newObjectTab.SetVisible( true );
};
}
Creating the dashboard
Next we will create a dashboard file that will be shown in the popup. It involves two steps:
- Create a index.html file which will load styles and dashboard handler
- Create a dashboard.js file which will handle the dashboard.
index.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Sample</title>
<script src="mfiles.extensibility.protocol.js"></script>
<!-- Load styles and dashboard handler js file -->
<link href="style.css" rel="stylesheet" />
<script src="dashboard.js"></script>
</head>
<body>
<div id="Main">
<div id="basicInfo"></div>
</div>
<button id="btnOpenNewObjectWindow">Open</button>
<span id="error_message" class="error-message"></span>
</body>
</html>
Create dashboard handler file using OnNewDashboard event. Once the dashboard started, the html content will be loaded and the content will be updated.
dashboard.js
function OnNewDashboard( dashboard ) {
/// <summary>Executed by the UIX when a dashboared is started.</summary>
/// <param name="dashboard" type="MFiles.Dashboard">The dashboard object which was created.</param>
// Register a handler to listen the started event.
dashboard.Events.Register(
MFiles.Event.Started,
() => {
// Prepare the html content that to be updated.
const htmlContent = getDashBoardContent();
// Show and Update the dashboard content.
mainContent = document.getElementById( "Main" );
mainContent.innerHTML = htmlContent;
mainContent.style.display = "block";
// Bind the events.
bindEvents();
}
);
}
function getDashBoardContent() {
return `
<div>
<div class="method-container">
<div class="method-title">Show New Object Window (Blank template and User defined template)</div>
<div><div class="label">Object Type Id</div>: <input id="objectType" /></div>
<div><div class="label">Extension (Blank Template)</div>: <input id="extension" type="text" /></div>
<div><div class="label">Metadata card title</div>: <input id="mdCardTitle" type="text" /></div>
<div><div class="label">Object Title </div>: <input id="objectTitle" type="text" /></div>
<div><div class="label">Check in immediately</div>: <input id="checkin" class="checkbox" type="checkbox" /></div>
<div id="Sourcefile"><div><div>Source file (User defined template info) </div><textarea id="srcfile" spellcheck="false">{}</textarea><div></div></div></div>
<div>Currently we are supporting only default blank template formats such as .bmp, .accdb, .xlsx, .pptx, .pub, .vsdx, .docx, .txt. For other formats, MFD will be created by default.</div>
</div>
</div>
`;
}
Show the Create object window on clicking button
First bind the click event in dashboard to call the ShowNewObjectWindow from right pane content.
We will have 4 parameters for this ShowPopupDashboard
- objectType - Target Object type
- objectCreationInfo - Holds creation information, e.g., MD card button visibility, file source information, etc.,.
- prefillPropertyValues - List of properties which can be prefilled.
- accessControlList - ACL
Creating Blank template
For document type we need to pass the extension or else it will create multifile document by default. Other then document type, no need to pass the extension information.
Currently we are supporting only default blank template formats such as .bmp, .accdb, .xlsx, .pptx, .pub, .vsdx, .docx, .txt. For other formats, MFD will be created by default.
Creating User defined template
For User defined template we need to pass the template object information.
main.js
// NOTE! This code is for demonstration purposes only and does not contain any kind of
// error handling. MUST be revised before using in production.
// NOTE! This code is for demonstration purposes only and does not contain any kind of
// error handling. MUST be revised before using in production.
function OnNewShellUI( shellUI ) {
/// <summary>The entry point of ShellUI module.</summary>
/// <param name="shellUI" type="MFiles.ShellUI">The new shell UI object.</param>
// Register to be notified when a new normal shell frame (Event_NewShellFrame) is created.
// We use Event_NewShellFrame rather than Event_NewShellFrame as this won't fire for history (etc.) dialogs.
shellUI.Events.Register(
MFiles.Event.NewShellFrame,
handleNewShellFrame );
}
function handleNewShellFrame( shellFrame ) {
/// <summary>Handles the OnNewShellFrame event for an IShellUI.</summary>
/// <param name="shellFrame" type="MFiles.ShellFrame">The shell frame object which was created.</param>
// Register to listen to the started event.
shellFrame.Events.Register(
MFiles.Event.Started,
getShellFrameStartedHandler( shellFrame ) );
}
function getShellFrameStartedHandler( shellFrame ) {
/// <summary>Gets a function to handle the Started event for shell frame.</summary>
/// <param name="shellFrame" type="MFiles.ShellFrame">The current shell frame object.</param>
/// <returns type="MFiles.Events.OnStarted">The event handler.</returns>
// Return the handler function for ShellFrame's Started event.
return async () => {
// Show the tab for new object creation.
const newObjectTab = await shellFrame.RightPane.AddTab( "newObject", "New Object", "_last");
newObjectTab.ShowDashboard( "MyDashboard", {} );
newObjectTab.Select();
newObjectTab.SetVisible( true );
};
}
dashboard.js
function getDashBoardContent() {
return `
<div>
<div class="method-container">
<div class="method-title">Show New Object Window (Blank template and User defined template)</div>
<div><div class="label">Object Type Id</div>: <input id="objectType" /></div>
<div><div class="label">Extension (Blank Template)</div>: <input id="extension" type="text" /></div>
<div><div class="label">Metadata card title</div>: <input id="mdCardTitle" type="text" /></div>
<div><div class="label">Object Title </div>: <input id="objectTitle" type="text" /></div>
<div><div class="label">Check in immediately</div>: <input id="checkin" class="checkbox" type="checkbox" /></div>
<div id="Sourcefile"><div><div>Source file (User defined template info) </div><textarea id="srcfile" spellcheck="false">{}</textarea><div></div></div></div>
<div>Currently we are supporting only default blank template formats such as .bmp, .accdb, .xlsx, .pptx, .pub, .vsdx, .docx, .txt. For other formats, MFD will be created by default.</div>
</div>
</div>
`;
}
// Show new object window.
async function showNewObjectWindow() {
// Read object type id value.
const objectTypeID = document.getElementById( "objectType" );
const objectType = Number( objectTypeID?.value );
// Read extension value.
const extensionType = document.getElementById( "extension" );
const extension = extensionType.value;
// Read metadata card title value.
const mdCardTitle = document.getElementById( "mdCardTitle" );
const mdTitle = mdCardTitle.value || "Untitled";
// Read object title value.
const objectTitleInfo = document.getElementById( "objectTitle" );
let objectTitle = objectTitleInfo.value;
// Read Check in immediately.
const checkin = document.getElementById( "checkin" );
const isCheckin = Boolean( checkin.checked );
// Read source files info.
const srcFile = document.getElementById( "srcfile" );
let srcfile = srcFile.value;
// Validate source file.
try {
srcfile = typeof srcfile === 'object' ? srcfile : JSON.parse( srcfile );
} catch( exception ) {
console.error( exception );
return;
}
// Clear all the error messages.
showErrorMessage( "" );
let objFileSourceType = -1;
// Validate source file.
if( srcfile && Object.keys( srcfile ).length > 0 ) {
objFileSourceType = srcfile.type;
}
// Show error message for invalid object type.
if( isNaN( objectType ) || objectTypeID?.value === "" ) {
showErrorMessage( "Invalid object type id" );
return;
}
// In this example, we allowed only BLANK_TEMPLATE and USER_DEFINED_TEMPLATE.
if( objFileSourceType != 2 && objFileSourceType >=0 ) {
showErrorMessage( "Invalid ObjFileSource type. Use Type '2' for user defined template." );
return;
}
// Create object creation info.
let objectCreationInfo = null;
objectCreationInfo = {
title: objectTitle || "",
metadata_card_title: mdTitle || "",
extension: extension,
object_type: objectType,
source_files: srcfile,
check_in_immediately_enabled: isCheckin,
disallow_template_selection: true,
single_file_document: extension ? true : null
};
// Show new object window.
const resultFromCreationWindow = await MFiles.ShowNewObjectWindow(
objectType, // ObjectType
objectCreationInfo, // ObjectCreationInfo
[], // PrefilledPropertyValues
[] // AccessControlList
);
// Log the results from new object creation window.
console.log( resultFromCreationWindow );
}
// Show error message for given method.
function showErrorMessage( errorMessage ) {
const errorMessageElement = document.getElementById( "error_message" );
errorMessageElement.innerHTML = errorMessage;
}
function OnNewDashboard( dashboard ) {
/// <summary>Executed by the UIX when a dashboared is started.</summary>
/// <param name="dashboard" type="MFiles.Dashboard">The dashboard object which was created.</param>
// Register a handler to listen the started event.
dashboard.Events.Register(
MFiles.Event.Started,
() => {
// Prepare the html content that to be updated.
const htmlContent = getDashBoardContent();
// Show and Update the dashboard content.
mainContent = document.getElementById( "Main" );
mainContent.innerHTML = htmlContent;
mainContent.style.display = "block";
// Bind the events.
// For opening a prefilled object creation window.
const btnOpenNewObjectWindow = document.getElementById( "btnOpenNewObjectWindow" );
btnOpenNewObjectWindow.addEventListener( "click", showNewObjectWindow );
}
);
}
style.css
html, body {
font-family: Lato, "Segoe UI", Sans-Serif;
margin: 0;
background: #FFF;
height: 100%;
color: #0a1541;
font-size: 14px;
padding: 0 10px;
}
div {
padding: 4px 0;
}
.method-title {
font-size: 16px;
font-weight: 500;
color: #0a1541;
}
.method-container {
background: #F2F4F9;
border-radius: 14px;
padding: 10px 20px;
margin-bottom: 15px;
}
button {
width: 70px;
box-sizing: border-box;
border-width: 1px;
border-radius: 16px;
height: 26px;
cursor: pointer;
font-size: 14px;
border-color: #006eef;
background-color: #006eef;
color: #fff;
}
input {
height: 22px;
padding: 2px 10px;
font-size: 14px;
line-height: 1.428571429;
color: #0a1541;
background-color: #fff;
background-image: none;
border: 1px solid #9da1b3;
border-radius: 8px;
}
input:focus {
border-color: #006eef;
outline: 0;
}
.error-message {
color: red;
}
.label {
width: 200px;
display: inline-block;
}
textarea {
height: 250px;
padding: 2px 5px;
font-size: 14px;
color: rgb(10, 21, 65);
border: 1px solid rgb(157, 161, 179);
border-radius: 8px;
width: calc(100% - 20px);
}
.checkbox {
height: 13px;
padding: 0;
margin: 0;
}
Testing the application
Pass different object type, extensions and user defined template object info to test object creation with blank template and user defined templates.