We aim to implement a prefilled document creation form with values retrieved from a PluginService
object.
When a user right-clicks on a document and select "New document from this one", it fires an Action
which opens an AddContentItemDialog
. Then, the service is invoked to retrieve the properties of the selected document (maybe it's not necessary, through Firefox developper panel I see that most, maybe all custom properties are already fetched).
I'm able to fill text field properties, but not the ChoiceList
ones: they won't update, although they might be filled internally.
Here is a commented sample of code :
require(["dojo/_base/declare",
"dojo/_base/lang",
"dojo/aspect",
"ecm/model/Request",
"ecm/widget/dialog/AddContentItemDialog"],
function(declare, lang, aspect, Request, AddContentItemDialog) {
// Parameters passed to the service as HttpServletRequest
// (Custom function)
var serviceParams = getServicesParams(items[0]);
// Object store and parentFolder retrieving (needed below)
var parentFolder = items[0].parent;
var objectStore = items[0].objectStore;
// Retrieving the template to use for the dialog
// (Custom function)
var entryTemplate = retrieveTemplate(objectStore, parentFolder);
// Service call
Request.invokePluginService("FormPlugin", "FormService", {
requestParams: serviceParams,
requestCompleteCallback: function(response) {
// Creating the global dialog box
var addContentItemDialog = new AddContentItemDialog();
// Box containing the document properties
var addContentItemPropertiesPane =
addContentItemDialog.addContentItemPropertiesPane;
// Box containing general stuff
var addContentItemGeneralPane =
addContentItemDialog.addContentItemGeneralPane;
// Showing the dialog box
addContentItemDialog.show(
repository,
parentFolder, /* parent folder */
true, /* document being added*/
false, /* not virtual */
null, /* no callback function */
null, /* no teamspace */
true, /* use an entry template */
entryTemplate, /* entry template */
true /* can't choose directory from another rep */
);
// Waiting for complete rendering before filling the properties and general fields
aspect.after(addContentItemPropertiesPane,
"onCompleteRendering",
function() {
// Setting the destination and lock it
var folderSelector = addContentItemGeneralPane.folderSelector;
folderSelector.setRoot(parentFolder, objectStore);
folderSelector .setDisabled(true);
// Property filling - Work :-)
addContentItemDialog.setTitle("New document from another");
addContentItemDialog.setIntroText("This form allow you to create a document from another one.");
addContentItemPropertiesPane.setPropertyValue("DocumentTitle", "Prefilled title");
// Property filling - Doesn't work :-(
addContentItemPropertiesPane.setPropertyValue("A_ChoiceList_Prop",
[ "Value\\1", "Value\\2", "Value\\3"]);
}, true);
}
});
});
});
Maybe I missed some magic IBM code lines to get it done.
Updated. 1) The code waits now correctly for the retrieval of the entry template content. 2) This answer should also work with previous versions of ICN. 3) This answer defines functions at the global scope. Be extremely careful, you could encounter a conflict name with other plugins and ICN code. Instead use callbacks function, or name "strongly" your functions.
I followed these steps to write the Plugin Action:
AddContentItemDialog
object and display it by passing to it the Entry Template.An Entry Template is described by an EntryTemplate
object. It has a key propertiesOptions
which reference an array. Each element of this array represents a Document Property. Each element contains an key named defaultValue
:
EntryTemplate {
addClassDescription: "An Entry Template",
addClassLabel: "An Entry Template",
addClassName: "AnEntryTemplate",
// [...]
propertiesOptions: [
{
dataType: "xs:string",
id: "a_prop",
name: "A Property",
// [...]
defaultValue: "A default value",
// [...]
},
// [...]
],
// [...]
}
String values are passed as (obviously) strings, Dates as ISO8601 formatted strings (yyyy-MM-ddTHH:mm:ss
) and Lists as arrays.
For example, given n1
, n2
, n3
propertyOption
entries :
// "xs:string" dataType
entryTemplate.propertiesOptions[n1].defaultValue = "Duck";
// "xs:timestamp" dataType
entryTemplate.propertiesOptions[n2].defaultValue = "1938-04-15T00:00:00";
// "xs:string" dataType, "LIST" cardinality
entryTemplate.propertiesOptions[n3].defaultValue = ["Huey", "Dewey", "Louie"];
Below the implementation of the Javascript client-side code of the PluginAction. I didn't provide the service implementation nor the code to fill the Entry Template because it's a bit off topic. (For more informations about writing an ICN plugin, you can refer to the ICN Redbook Customizing and Extending IBM Content Navigator.)
Please note also I don't consider this answer as to be the best way to design the Action Plugin, don't hesitate to suggest optimizations/good practices related editions. I just found painfull to imbricate callbacks function, so I aimed to defined most of them at top-level, I don't like monolithic code.
First, the main block part :
require(["dojo/_base/declare",
"dojo/_base/lang",
"dojo/aspect",
"ecm/model/Request",
"ecm/widget/dialog/AddContentItemDialog"],
function(declare, lang, aspect, Request, AddContentItemDialog) {
/**
* Use this function to add any global JavaScript methods your plug-in requires.
*/
lang.setObject("openFilledCreateDocumentFormAction",
function(repository, items, callback, teamspace, resultSet, parameterMap) {
// Parameters passed to the service as HttpServletRequest
var serviceParams = new Object();
serviceParams.server = items[0].repository.id;
serviceParams.serverType = items[0].repository.type;
serviceParams.id = items[0].id;
// Object store and parentFolder retrieving (needed below)
var objectStore = items[0].objectStore;
var parentFolder = items[0].parent;
var entryTemplateClassName = null;
// Service call. "FormService" fetch the source document
// properties, then put them as JSON in the response.
// The response will be passed to the function
// requestCompleteCallback (see below)
Request.invokePluginService(
"FormPlugin",
"FormService", {
requestParams: serviceParams,
// Parameters below are for response callback
etClassName:"AnEntryTemplate",
repository:repository,
objectStore:objectStore,
parentFolder:parentFolder,
AddContentItemDialog:AddContentItemDialog,
aspect:aspect,
requestCompleteCallback: processRetrievalResponse
});
});
});
FormService
calls processRetrievalResponse()
when completed. In this one, we'll start by retrieving the template we want.
function processRetrievalResponse(response) {
// Some data passed to the parent object of this callback (see above)
var etClassName = this.etClassName;
var repository = this.repository;
var objectStore = this.objectStore;
var parentFolder = this.parentFolder;
var AddContentItemDialog = this.AddContentItemDialog;
var aspect = this.aspect;
// First we'll retrieve all the templates
repository.retrieveEntryTemplates(
function (entryTemplates, document_ET_count, folder_ET_count) {
var entryTemplate = null;
// Then we'll search for the one that we want
for (var i = 0; i < entryTemplates.length; i++) {
if (entryTemplates[i] &&
entryTemplates[i].addClassName == etClassName) {
entryTemplate = entryTemplates[i];
break;
}
}
// No Entry Template = abort.
if (!entryTemplate) {
alert("The Entry Template " +
"\"" + etClassName + "\" " +
"was not found. Please contact the administrators");
return;
}
// Now we got the Entry Template, time to retrieve its content
// First, we design a "waiter" object.
// We assume here the PluginService returns the values in
// the "properties" entry of the object response
retrievalWaiter =
new RetrievalWaiter (repository,
objectStore,
parentFolder,
entryTemplate,
response.properties,
AddContentItemDialog,
aspect);
// Then a call to retrieve its content
entryTemplate.retrieveEntryTemplate(null, false, true);
// We ignite the waiter. When the retrieval will be performed,
// It will fill its default values and use it to display
// the creation document dialog.
retrievalWaiter.wait();
}, "Document", parentFolder.id, null, objectStore);
}
The RetrievalWaiter
code. Here, no while
loop, cause it will be as consuming as disgusting.
This object simply relies on setTimeOut() to to periodically check the retrieval of the Entry Template content.
function RetrievalWaiter(repository, objectStore, parentFolder,
entryTemplate, properties,
AddContentItemDialog, aspect) {
this.repository = repository;
this.objectStore = objectStore;
this.parentFolder = parentFolder;
this.entryTemplate = entryTemplate;
this.properties = properties;
this.aspect = aspect;
this.wait =
function() {
// If the Entry Template is not yet loaded, wait 500 ms
// and recheck
if (!this.entryTemplate.isRetrieved) {
var _this = this;
setTimeout(function() {_this.wait();}, 500);
return;
}
// Fill the Entry Template with defaults value
// (got from the PluginServer response, see above)
fillEntryTemplate(this.entryTemplate, this.properties);
// Show the document creation dialog with the
showDialog(AddContentItemDialog,
this.aspect,
this.repository, this.objectStore,
this.parentFolder, this.entryTemplate);
}
}
Now, time to show the dialog.
function showDialog(AddContentItemDialog, aspect,
repository, objectStore,
parentFolder,
entryTemplate) {
var addContentItemDialog = new AddContentItemDialog();
var addContentItemPropertiesPane =
addContentItemDialog.addContentItemPropertiesPane;
var addContentItemGeneralPane =
addContentItemDialog.addContentItemGeneralPane;
addContentItemDialog.show(
repository,
parentFolder, // parent folder
true, // document being added
false, // not virtual
null, // no callback function
null, // no teamspace
true, // Use an Entry Template
entryTemplate, // Entry template
true // don't allow choosing directory
// from another repository
);
// Use aspect to set the value *after* the complete rendering
// of the properties pane
aspect.after(
addContentItemPropertiesPane,
"onCompleteRendering",
function() {
addContentItemDialog.setTitle("Duplicate a document");
addContentItemDialog.setIntroText(
"This form is loaded from a right-clicked document.");
// Removing the help link to make the form looks like
// the document creation one
addContentItemDialog.setIntroTextRef("","");
// Set parent folder and prevent it from being edited.
addContentItemGeneralPane.folderSelector.setRoot(parentFolder, objectStore);
addContentItemGeneralPane.folderSelector.setDisabled(true);
}, true);
}
// This function relies on the PluginService response.
// It takes the Entry Template, a JSON formatted response
function fillEntryTemplate(entryTemplate, jsonResponse) {
// My mission ends here. :-)
}