Working with documents

Retrieving the ecm.model.ContentItem from the ecm.model.item

repository.retrieveItem(documentOrFolderId, function (contentItem) {
    // Do something with the ContentItem
});
repository.retrieveItem(folderPath, function (contentItem) {
    // Do something with the ContentItem
});

Note: Unlike the documentation says, that does not work with an document path (see this), only with a folder path. However that works with both Document and Folder ID.

Example:

repository.retrieveItem(item.id, function (contentItem) {
    // Do something with the ContentItem
});
repository.retrieveItem('/Folder/MySubFolder', function (contentItem) {
    // Do something with the ContentItem
});

JSDoc.

Get a document by path

Since it is not possible to use the retrieveItem function to retrieve an item by path, here is a work around. However a more efficient way would be to implement your own service calling Factory.DocumentfetchInstance and return it. But this way does not require any extra work:

getItemByPath: function (itemPath, repository) {
    var def = new Deferred();
    var parentPath = itemPath.substring(0, itemPath.lastIndexOf("/"));
    var itemName = itemPath.substring(itemPath.lastIndexOf("/") + 1, itemPath.length);
    repository.retrieveItem(parentPath, function (item) {
        item.retrieveFolderContents(false, function (resultSet) {
            if (!resultSet.items || resultSet.items.length == 0) {
                def.resolve(null);
            } else {
                def.resolve(resultSet.items[0]);
            }
        }, null, null, true, null, "", item, {type: "AND", conditions: [
            {name: "{NAME}", condition: "endWith", value: itemName},
            {name: "{NAME}", condition: "startWith", value: itemName}
        ]});
    }, 'Folder');
    return def.promise;
}

Retrieve a document absolute path

The way to do that is the following, but it can get a bit tricky and I’ll explain why just after the code.

getAbsolutePath: function (item) {
    var res = "", current = item;
    while (current && !current.rootFolder) {
        res = "/" + current.name + res;
        current = current.parent;
    }
    if (res == "") {
        res = "/";
    }
    return res;
}

Now the problem is that in order for this to work, the item needs to be connected to a parent, meaning it needs to come from the content view or the tree. This will be true if you are using the item in a context action, it won’t if you are retrieving the document from the repository (retrieveItem). That makes sense because in FileNet, a document doesn’t have to be filed in a folder or can be in several so what would be the parent? You can get all of them by retrieving the FoldersFiledIn though. So if you are retrieving an item from the server, you will have to set the parent manually (item.parent=aFolderContentItem) after retrieval to set a context for your item.

 

Test if an item is document or a folder

var isFolder = item.isFolder();

JSDoc, item can be an ecm.model.Item, it doesn’t have to be an ecm.model.ContentItem.

Test if a document is locked

var isLocked = item.locked;
var lockedUser  = item.lockedUser

JSDoc and JSDoc, item can be an ecm.model.Item, it doesn’t have to be an ecm.model.ContentItem.

Get the class of a document

item.getContentClass()

JSDoc, item needs to be an ecm.model.ContentItem.

Get the content of the document into a JavaScript variable

For obvious readon, document’s content are not fetch in the ContentItem objects. However, some times when working with small text document, it can be nice to get the content to analyze it or display it to the user. You can do this using the following code:

xhr(item.getContentUrl(), {
	handleAs: "text"
}).then(function (data) {
	// Do something with the handled data
}, function (err) {
	// Handle the error condition
}, function (evt) {
	// Handle a progress event from the request if the
	// browser supports XHR2
});

Get attributes (properties) of a document

To get the attributes (properties) of a document, use:

item.retrieveAttributes(function (resItem) {
    // It actually returns the item, but with the attributes member fully fetched
    // You can now access your property via resItem.attributes.propName
}, false, false, function () {
    // What to do if an error occurred
});

The attributes member is not an array, but an object looking like this:

icn_debug_doc_attributes

Add/Check In a document.

There is two ways to add/check in a document with ICN, using a pure JS model, or using the FileTracker applet. Since applets are not the most portable things, I would advice against using the applet. However if you are using the applet to locate the file (which I have to say is pretty convenient for the user, especially if you are writing a multi check in wizard), then you will have to use the File Tracker applet to check in the documents since it does not provide a W3C File object. For the add however, I don’t really see the point, except if you like the Java File Chooser better of the Browser one or the D&D…

Using the JS model
ecm.model.Repository.addDocumentItem(parentFolder, objectStore, templateName, criterias, contentSourceType, mimeType, filename, content, childComponentValues, permissions, securityPolicyId, addAsMinorVersion, autoClassify, allowDuplicateFileNames, setSecurityParent, teamspaceId, callback, isBackgroundRequest, onError, compoundDocument)
ecm.model.ContentItem.checkIn(templateName, criteria, contentSourceType, mimeType, filename, content, childComponentValues, permissions, securityPolicyId, newVersion, checkInAsMinorVersion, autoClassify, callback)

Add and CheckIn JSDoc, but the doc miss some information so let’s detail that together:

That adds/checks in this item to the repository using the content stream. Parameters are quite the same for both method so let’s detail them all together

Parameters:

  • parentFolder (add only): the ContentItem of the folder where to file the new document
  • objectStore (add only): the object store to use to add the document. You can use repository.objectStore to get it.
  • templateName: The item’s content class name.
    Be careful there, despite the name, this is NOT the entry template name, but as it is said here it’s the class’ id. If you want to checkIn using an entry template, you’ll have to set the property EntryTemplateId in the criterias.
  • criteria: An array of property values to be set on checkin.
    Properties you want to set, if property exists for the item but is not in this array, it will be left untouch. Meaning you can send an empty array if you don’t want to change any property. Here what this property looks like. This is an array of Criteria item (this is not a class, just an object):
    icn_debug_criteria
  • contentSourceType: A string value holding the content source type.
    • “Folder”
    • “Document”
    • “Item” (no content)
    • “ExternalURL” (FileNet P8 only)
  • mimeType: A string value holding the MIME Type of the content.
    You can also leave this empty (“”) and the content engine will figure out the correct value if it can.
  • filename: A string value holding the file name of the content.
  • content: The file content stream.This a W3C File object, that looks like this:icn_debug_content
    You can also use the BLOB straight if you need, we’ll see this in the section how to check in a JavaScript variable.
  • childComponentValues: An array of child component values. (Content Manager only). If you don’t care about that, just send an empty array ([]).
  • permissions: The permissions of the object.
    This is an array of permission object. If you leave it null on check in, it will keep the current version’s permissions, you have to provide it on add, you can use either the entry template permissions if you are using one, or the user choices.This array is usually produced by the AddDocumentPropertiesPane or a PropertiesPane you’re using in your custom wizard. In case you need at some point to set this, here what the permission array looks like:
    icn_debug_permission
  • securityPolicyId: A string holding a security policy id. (FileNet P8 only. This value can be null.).
    Security policies are rules to automatically change permissions when creating new versions (release, minor, deprecated…). You can just leave this null.
  • newVersion: A boolean value indicating whether to create a new version. (Content Manager only).
    Not for P8, P8 will create a new version anyway. Leave it null.
  • checkInAsMinorVersion: A boolean value indicating whether the item should be checked in as a minor version. (FileNet P8 only).
    Tells P8 to create a minor a major version. It depends of your need. Usually it is good to use the repository configuration on this (repository.checkinAsMajorVersion) or at least pre check the checkbox (or not) if you are letting the user set this.
  • autoClassify: A boolean value indicating whether the item should be auto-classified. (FileNet P8 only)
    Autoclassify is used to set class and properties automatically when addig document. This is really important when adding document, I’m not sure it really matters on CheckIn since it has already been done, but in doubt you can set it to true.
  • allowDuplicateFileNames (add only): if true, the Content Engine will throw an error if a document with the same DocumentTitle property already exists in the ‘parentFolder
  • callback: A callback function to be called after the item has been checked in.
    What you want to do when check in is done. The signature of the method is callback(item, fieldErrors). However this method will be call as follow:

    • If no error: callback(item) where item is a ecm.model.ContentItem
    • If errors: callback(undefined, fieldErrors)

    That means you’ll never have both filled at the same time.

  • compoundDocument:P8 only, indicates if the document is a compound document or not

Here is an example of checking in using the JavaScript model:

_checkInUsingJS: function (contentItem) {
    contentItem.checkIn(
        this._currentItem.classId, // ex: 'document'
        this._currentItem.criterias, // can be [] if you are not changing any property but just updating content
        this._currentItem.contentSourceType, // ex: 'Document'
        this._currentItem.mimetype, // ex: text/xml
        this._currentItem.filename, // ex myFile.xml
        this._currentItem.content, // ex: the File object provided by your browser from the D&D
        this._currentItem.childComponentValues, // usually null for P8
        this._currentItem.permissions, // null if you want to keep all current permissions and just update content
        this._currentItem.securityPolicyId, // a security policy id or null if you don't use one
        this._currentItem.newVersion, // Not used by P8, using false won't just update the content 🙂
        this._currentItem.checkInAsMinorVersion, // if false the reservation version will be converted into a major one
        this._currentItem.autoClassify, // If you want to run the autoclassify module if you have one configured for this MIME Type
        lang.hitch(this, this._processCurrentItemComplete) // What to do after
    );
}

And here is an example of adding using the JavaScript model:

this.repository.addDocumentItem(
    this._parentFolder, // The ecm.modelContentItem of the folder where to file the new document
    this.repository.objectStore, // The object store, you can use the repository to get here like I do here
    this._currentItem.classId, // The class id of the new document, for instance 'document'
    this._currentItem.criterias, // The array of criterias, see note after this code
    this._currentItem.contentSourceType, // ex: 'Document'
    this._currentItem.mimetype, // The MIME Type, or empty if you want to let the CE figure it out
    this._currentItem.filename, // The filename
    this._currentItem.content, // This is also a W3C File object, or the BLOB object
    this._currentItem.childComponentValues, // can be null
    this._currentItem.permissions, // Can't be null or [] for an add, you have to provide some permissions. Usually you can use the SecurityPane widget to do that, or use the permissions for the entry template if you are using one.
    this._currentItem.securityPolicyId, // The security policy if you want to use one, or null
    this._currentItem.addAsMinorVersion, // if false it will create a major version (1.0), otherwise a minor version (0.1)
    this._currentItem.autoClassify, // if true, the CE will run the autoclassify module on this document if the its MIME Type has one
    this._currentItem.allowDuplicateFileNames, // true if you want to allow several documents with the same name in the folder
    this._currentItem.setSecurityParent, // This is deprecated since FileNet P8 4, avoid using this, we will prefer security proxy or SecurityFolder instead
    this._currentItem.teamspaceId, // can be null if you are not adding this document from a teamspace, if you are set its id here
    lang.hitch(this, this._processCurrentItemComplete), // what to do after the add is done
    isBackgroundRequest, // only for the ui
    lang.hitch(this, this._onError), // what to do if your CE burns because of your document (more seriously if you use the allowDuplicateFileNames to false, you'll get the exception here)
    this._currentItem.compoundDocument // Tell P8 (only) if this is a compound document
);

Note: be careful about the criterias. You HAVE to define the DocumentTitle even is you want to use the FileName. The Content Engine won’t do this for you and the client side has to set it even when using the file name. If you don’t the document will have a blank name.

Using the FileTracker applet

Checking in a document:

_checkInUsingFT: function (contentItem) {
    var contentURL = ecm.model.Request.getServiceRequestUrl("checkIn", this.repository.type);
    var contentUrl1 = contentURL;
    if (contentUrl1.charAt(0) == ".") {
        contentUrl1 = contentUrl1.substr(1, contentUrl1.length);
        contentUrl1 = ecm.model.desktop._cServicesUrl + contentUrl1;
    }
    var documentInfo = [];
    var documentInfoInstance = contentItem._makeCheckinParams(
        this._currentItem.classId, // The class ID, ex 'document'
        this._currentItem.criterias, // The arrays of property values, can be [] if you are not changing anything
        this._currentItem.contentSourceType, // The type, ex 'Document'
        this._currentItem.mimetype, // The MIME Type, ex text/plain
        this._currentItem.filename, // The file name
        {
            filePath: item.filePath, // The content is not a File anymore, but an object with the filename and the path on the system
            fileName: item.fileName  // This is usually provided by the applet, and the applet will use the path to upload the file
        },
        this._currentItem.childComponentValues, // can be null on P8
        this._currentItem.permissions, // can be null if you want to keep current permission, or an array of Permissions
        this._currentItem.securityPolicyId, // the security policy id or null if you don't use one
        this._currentItem.newVersion, // not use by P8
        this._currentItem.checkInAsMinorVersion, // if false the reservation version will be converted into a major one
        this._currentItem.autoClassify, // If you want to run the autoclassify module if you have one configured for this MIME Type
        null, // the call back method, but with the FT that's not used, we will used the FT callback when checking in
        true // true to add form parameters to the request for compatibility with older browser. I won't focus on that here,
    );
    this._buildFTParameters(documentInfoInstance, this._currentItem); // Add security token to the request
    documentInfo.push(documentInfoInstance);
    this._currentItem.repositoryId = [this.repository.id];
    this._currentItem.docid = [contentItem.id];
    this._currentItem.ObjectStoreName = [this.repository.objectStoreName];
    this._currentItem.vsId = [contentItem.vsId];
    var inputParameters = {
        contentUrl: contentUrl1,
        documentInfo: documentInfo,
        deleteFile: false,
        isBackgroundRequest: true,
        onError: lang.hitch(this, this._onError),
        progressMessage: (Messages["progress_message_" + this.repository.type + "_" + "checkIn"] || Messages["progress_message_" + "checkIn"] || Messages.progress_message_general)
    };
    // This is where we actually check in the document using the File Tracker applet
    // It is good practice and highly recommended to call _checkInCompleted to update the item after the checkin
    FileTracker.uploadFiles(inputParameters, lang.hitch(this, function (response) {
        contentItem._checkInCompleted(response, lang.hitch(this, "_processCurrentItemComplete"));
    }));
},
_buildFTParameters: function (documentInfoInstance, documentItem) {
    documentInfoInstance.security_token = Request._security_token;
    documentInfoInstance.filePath = documentItem.filePath;
    return documentInfoInstance;
}

Adding a document (since I don’t really see the point of adding using the applet, I won’t comment this but you still have it if you need):

// Building the service URL
var contentURL = ecm.model.Request.getServiceRequestUrl("addItem", this.repository.type);
var contentUrl1 = contentURL;
if (contentUrl1.charAt(0) == ".") {
    contentUrl1 = contentUrl1.substr(1, contentUrl1.length);
    contentUrl1 = ecm.model.desktop._cServicesUrl + contentUrl1;
}
// Building params needed for the add
var documentInfo = [];
var args = this.repository._makeAddDocumentItemArgs(
    this._parentFolder,
    this.repository.objectStore,
    this._currentItem.templateName,
    this._currentItem.criterias,
    this._currentItem.contentSourceType,
    this._currentItem.mimetype,
    this._currentItem.filename,
    this._currentItem.content,
    this._currentItem.childComponentValues,
    this._currentItem.permissions,
    this._currentItem.securityPolicyId,
    this._currentItem.addAsMinorVersion,
    this._currentItem.autoClassify,
    this._currentItem.allowDuplicateFileNames,
    this._currentItem.setSecurityParent,
    this._currentItem.teamspaceId,
    lang.hitch(this, this._processCurrentItemComplete),
    isBackgroundRequest,
    lang.hitch(this, this._onError),
    this._currentItem.compoundDocument,
    true
);
var documentInfoInstance = this._buildFTParameters(args, this._currentItem);
documentInfo.push(documentInfoInstance);
// Bulding params needed for the FileTracker applet
var inputParameters = {
    contentUrl: contentUrl1,
    documentInfo: documentInfo,
    isBackgroundRequest: isBackgroundRequest,
    deleteFile: false,
    onError: this._onError,
    progressMessage: (Messages["progress_message_" + this.repository.type + "_" + "addItem"] || Messages["progress_message_" + "addItem"] || Messages.progress_message_general)
};

// Calling FileTracker to add the document
FileTracker.uploadFiles(inputParameters, lang.hitch(this, function (response) {
    this.repository._addDocumentItemCompleted(response, this._parentFolder, lang.hitch(this, this._processCurrentItemComplete));
}));

Check in a JavaScript variable

Sometime it can be useful to check in a variable into a document instead of a File. ICN JavaScript model does not offer such a function, but you can make it work by building your own BLOB object from your variable and use it to check in the document. Here is an example:

var blob = new Blob(['This is some content'], {type: 'text/plain'});
// You can use the blob as content in the check in function but in case you need the File object,
// here is how to construct it
var file = new File(blob, 'test.txt');

Note: this doesn’t work with Firefox 27, it says this is a restricted operation, it has been fixed in Firefox 28 and above.

38 thoughts on “Working with documents

  1. Bhavik

    Hi,
    I want to extend ecm.widget.dialog.CheckInDialog to create custom checkin

    Can you help me how can I do that?

    Thank you

    Reply
    1. Guillaume Post author

      Hi Bhavik, I was going to write about extending built-in UI stuffs but I guess it’s not ready yet :). It depends what you want to do, changing small visual thing,behavior or he whole interface. In any case the first step would be create a new dialog extending ecm.widget.dialog.CheckInDialog. Code should look like:

      define([ "dojo/_base/declare", "dojo/_base/lang", "ecm/widget/dialog/CheckInDialog" ], function (
          declare, lang, CheckInDialog) {
          return declare(CheckInDialog, {
              // Here you can overwrite function
          });
      });
      

      Then you’ll have to tell me a little bit more about what you want to do. If this is small behavior change you can overwrite postCreate and do your thing in it, like aspect and small dom changes. Example:

      postCreate: function() {
          this.inherited(arguments);
          // Do your JavaScript thing here like aspect and domChanges
      },
      

      If you want change the whole UI, you might want to copy the template and overwrite it with

      "dojo/text!./templates/YourTemplatehtml"

      assign it to a variable then use it as

      contentString: template,

      I would rather use the first solution if you have small changes. Again, tell me what you want and I may help you more.

      Hope that helps you!

      Reply
    1. Guillaume Post author

      Hi Prabu,

      You’ll have to tell me a little bit more about what you’re trying to do. The answer depends on where you need the id.

      In an action, if you need it to set the visibility, just uses the array of items provided as parameter (isVisible : function (repository, items, repositoryTypes, teamspace)), for instance items[i].id.

      You have the same in the performAction function.

      If not, please clarify where you need the information.

      Reply
      1. Prabu

        Thank you Guillaume,

        I am trying to add a context menu item…
        In which I Created a plugin with pluginAction and in action model class js I am trying to get the documentId which are selected by user. when they click on that menu item it has to show the document ids. thats my first step further in that i got to add other functions.

        Reply
        1. Guillaume Post author

          Thank you for clarifying 🙂

          Basically, to do this, you need to:

          • Create a PluginAction class (which you already did)
          • return the JavaScript class of your action. For instance, if your plugin name is TestPlugin and you action is ShowId, you should return in the getActionModelClass method “testPluginDojo.ShowId”
          • Do not forget you modify your Plugin Class to add your new Action in the return statement of the getActions method.
          • Then create a the JavaScript class according what you return in the Java class In our example, create a file WebContent/testPluginDojo/ShowId.js

          In this JavaScript class, which should extend ecm/model/Action, you can overwrite only the performAction method, to display the Id (with an alert to do this dirty or using the ecm/widget/dialog/MessageDialog class.

          Here is a quick snippet of the class. Of course, don’t forget to create a new custom menu for documents and add your action to it, then assign your menu to the “Document context menu” in your desktop.

          WebContent/testPluginDojo/ShowId.js:

          define([ "dojo/_base/declare", "dojo/_base/lang", "ecm/model/Action"], function (declare, lang, Action) {
            return declare(Action, {
              performAction : function (repository, itemList, callback, teamspace, resultSet, parameterMap) {
                var items = [], i;
                // Get real items if favorites
                if (itemList && itemList[0] && itemList[0].isInstanceOf && itemList[0].isInstanceOf(ecm.model.Favorite) && this.id != "RenameFavorite" && this.id != "EnableSync" && this.id != "DisableSync" && this.id != "DeleteFavorite" && this.id != "OpenFolder") {
                  for (i in itemList) {
                    items.push(itemList[i].item);
                  }
                } else {
                  items = itemList;
                }
                // Now show your id
                if (items && items.length > 0) {
                  alert(itemList[0].id);
                }
              }
            });
          });
          

          Hope that’s enough to get you started.

          Reply
  2. Prabu

    Hi Guillaume,
    I need to invoke the service class with docid as one of the service parameter.
    For which I am trying to get the item id from dojo class to action model class but its says “ContextMenuDojo is not a constructor”
    .
    var documentItems = new ContextMenuDojo();
    documentItems.performAction(repository, itemList, callback, teamspace, resultSet, parameterMap);

    Please tell me how to solve it.

    Thanks.

    Reply
    1. Guillaume Post author

      Hi Prabu,
      I’m not sure you are understanding how the Action works, you are not supposed to instantiate Context Menu, you are only creating a new Action class and ICN will take care of using it. In the performAction that you are overwriting (not using), you have access to the items. ICN will call your function, when clicking on the the context menu item, that’s not to you to call it.

      Now for the service, it looks like this. But you should take a look to the RedBook it is great.

      var serviceParams = {
        "docId": items[0].id
      };
      
      // Calling service
      Request.invokePluginService("TestPlugin", "YourServiceID", {
          requestParams : serviceParams,
          requestCompleteCallback : lang.hitch(this, function (response) {
              // Do something with the response you returned in Java (JSON object)
          })
      });
      
      Reply
  3. Prabu

    Hi Guillaume,
    How to use MessageDialog for getting the input value from user in action model class and pass it to service class

    Thank you.

    Reply
  4. Guillaume Post author

    Hi Prabu,
    You can’t. You should create your own dialog extending ecm/widget/dialog/BaseDialog and call your service in the method you registered with your Valid button. For example:
    this.addButton(“Valid”, “_execute”, false, true);
    and call your service in your _execute method.
    Let me know if you need more help.

    Reply
  5. Prabu

    Yes Guillaume, I need your help.
    Here I am not adding any button. Like how alert gets displayed I need to prompt a Dialog box to get two input values. Below is the sample one.

    ======

    <link rel="stylesheet" href="../_static/js/dojo/../dijit/themes/claro/claro.css">

    <script>dojoConfig = {parseOnLoad: true}</script>
    <script src='../_static/js/dojo/dojo.js'></script>

    <script>

    dojo.require("dijit.form.Button");
    dojo.require("dijit.form.TextBox");

    function checkData(){
    var data = formDlg.get('value');
    console.log(data);
    if(data.sdate > data.edate){
    alert("Start date must be before end date");
    return false;
    }else{
    return true;
    }
    }
    </script>

    Total Number of Page:

    Number pages in each to split:

    Submit
    Cancel

    When pressing this button the dialog will popup:

    Show me!

    dijit.byId(“formDialog”).show();

    But i need to execute this for one of my document context menu action model class

    require([“dojo/_base/declare”,
    “dojo/_base/lang”,
    “ecm/model/Request”, “ContextMenuPluginDojo/ContextMenuDojo”],
    function(declare, lang, Request, items, ContextMenuDojo) {
    /**
    * Use this function to add any global JavaScript methods your plug-in requires.
    */

    lang.setObject(“contextMenuSplit”, function(repository, itemList, callback, teamspace, resultSet, parameterMap) {

    }
    })

    How can i achieve that… Please tell me.

    Thank you.

    Reply
    1. Guillaume Post author

      Hi Prabu,

      As I told you, you need to create a new dialog. Please refer to the Dojo documentation to know how to create a new widget. To help you, here is what your code should look like but you should try to understand what’s going on here. You have two files for the widget/dialog, one for the template (InputDialog.html usually in a templates sub-folder), one for the class itself (InputDialog.js). They should be:
      The template:

      <div>
          <div class="field">
              <label for="${id}_inputOne">Please enter something</label>
              <input
              data-dojo-type="ecm.widget.ValidationTextBox" data-dojo-attach-point="inputField1" name="${id}_inputOne" id="${id}_inputOne" />
          </div>
          <div class="field">
              <label for="${id}_inputTwo">Please enter something again</label>
              <input
              data-dojo-type="ecm.widget.ValidationTextBox" data-dojo-attach-point="inputField2" name="${id}_inputTwo" id="${id}_inputTwo" />
          </div>
      </div>
      

      The class:

      define([
          "dojo/_base/declare",
          "dojo/_base/lang",
          "ecm/widget/dialog/BaseDialog",
          "ecm/widget/ValidationTextBox",
          "dojo/text!./templates/InputDialog.html"
      ], function (
          declare,
          lang,
          BaseDialog,
          ValidationTextBox,
          template
      ) {
      
          return declare(BaseDialog, {
      
              contentString : template,
              loginButton: null,
      
              /**
               * Finish to create the dialog
               */
              postCreate : function () {
                  var methodName = "postCreate";
                  this.logEntry(methodName);
                  this.inherited(arguments);
                  this.setTitle("Give me two inputs");
                  this.setResizable(true);
                  this.loginButton = this.addButton("Valid", "_execute", false, true);
                  this.logExit(methodName);
              },
              _execute: function () {
              var value1 = this.inputField1.get('value');
              var value2 = this.inputField2.get('value');
              // Do something here when your user valid wit the value
              }
      
          });
      
      });
      

      Then you can use your widget like:

      var dialog = new InputDialog();
      dialog.show();
      

      Of course don’t forget to import your widget in the define statement (testPlugin/InputDialog).

      Reply
    1. Guillaume Post author

      Hi Prabu,

      I think you are missing basic Dojo knowledge. You should follow some tutorials on Dojo first. Especially on how to create a class/widget, how to import it and how to instantiate it.

      In your case, I would say you InputDialog.js file isn’t a proper Dojo class or you import isn’t correct. You can post your code if you wish, I’ll take a look.

      Reply
  6. tryprabu

    Dojo class

    define([
        "dojo/_base/declare",
        "dojo/_base/lang",
        "ecm/widget/dialog/BaseDialog",
        "ecm/widget/ValidationTextBox",
        "dojo/text!./templates/inputDialog.html"
    ], function (
        declare,
        lang,
        BaseDialog,
        ValidationTextBox,
        template
    ) {
    
    return declare(BaseDialog, {
    
        contentString : template,
        loginButton: null,
    
        /**
         * Finish to create the dialog
         */
        postCreate : function () {
            var methodName = "postCreate";
            this.logEntry(methodName);
            this.inherited(arguments);
            this.setTitle("Give me two inputs");
            this.setResizable(true);
            this.loginButton = this.addButton("Valid", "_execute", false, true);
            this.logExit(methodName);
        },
        _execute: function () {
        var value1 = this.inputField1.get('value');
        var value2 = this.inputField2.get('value');
        // Do something here when your user valid wit the value
        }
    
    });
    
    
    });
    

    html template

        Please provide the page number for split1
    
    
    
        Please provide the page number for split2
    

    action model class

    require(["dojo/_base/declare",
             "dojo/_base/lang",
             "ecm/model/Request", 
             "ecm/widget/dialog/BaseDialog", 
             "ecm/widget/dialog/AddContentItemDialog", 
             "ContextMenuPluginDojo/ContextMenuDojo", 
             "ContextMenuPluginDojo/splitDialog"], 
    function(declare, lang, Request, items,  AddContentItemDialog, ContextMenuDojo, BaseDialog, splitDialog) {
        /**
         * Use this function to add any global JavaScript methods your plug-in requires.
         */
    lang.setObject("menuitem" , function(repository, itemList, callback, teamspace, resultSet, parameterMap) {
    
    var dialog = new InputDialog();
    dialog.show();
    }
    

    Error

    InputDialog is not a constructor.

    Reply
    1. Guillaume Post author

      Hi Prabu,
      You really need to look at the Dojo documentation to see how the loader works. In your case you are importing “ContextMenuPluginDojo/splitDialog” as the variable splitDialog so that’s the name you have to use to create a new instance:

      var dialog = new splitDialog();
      dialog.show();
      
      Reply
  7. tryprabu

    Sorry I forgot to edit that while posting it here… But I had given exactly as how you mentioned here.

    But then why its throwing saying splitDialog() is not a constrcutor it should have thown me the error as splitDialog is not defined right??

    Thanks & Regards,
    Prabu

    Reply
    1. Guillaume Post author

      Indeed my bad, I haven’t looked close enough :). Your parameters do not match, you have 6 for 7 mapping:

      • “dojo/_base/declare”: declare
      • “dojo/_base/lang”: lang
      • “ecm/model/Request”: Request
      • “ecm/widget/dialog/BaseDialog”: items ???
      • “ecm/widget/dialog/AddContentItemDialog”: AddContentItemDialog
      • “ContextMenuPluginDojo/ContextMenuDojo”: ContextMenuDojo
      • “ContextMenuPluginDojo/splitDialog”: BaseDialog
      • nothing: splitDialog

      Move “BaseDialog” to “items” and try again and let me know.

      Reply
  8. tryprabu

    I removed the BaseDialog but the plugin is not getting loaded….

    “dojo/_base/declare”: declare
    “dojo/_base/lang”: lang
    “ecm/model/Request”: Request
    nothing : items ???
    “ecm/widget/dialog/AddContentItemDialog”: AddContentItemDialog
    “ContextMenuPluginDojo/ContextMenuDojo”: ContextMenuDojo
    “ContextMenuPluginDojo/splitDialog”: splitDialog

    and its says that same error splitDialog is not a constructor
    I am not sure whether its following the serier or matching with names. But without items plugin is not getting loaded itself

    Reply
    1. Guillaume Post author

      I don’t know what your code is, but it should start with:

      require(["dojo/_base/declare",
               "dojo/_base/lang",
               "ecm/model/Request", 
               "ecm/widget/dialog/BaseDialog", 
               "ecm/widget/dialog/AddContentItemDialog", 
               "ContextMenuPluginDojo/ContextMenuDojo", 
               "ContextMenuPluginDojo/splitDialog"], 
      function(declare, lang, Request, BaseDialog,  AddContentItemDialog, ContextMenuDojo, splitDialog) {
      

      What do you have. And what error do you get in the console if any?

      Reply
  9. tryprabu

    I got dojo popup worked by using it in another plugin project.
    But I was looking to get the value1 & value2 from the dojo class to action class. But I couldn’t get it. Can you help me please.

    Thank you,
    Prabu

    Reply
    1. Guillaume Post author

      If you want to use the value you will need to wait the user validate the dialog. If you’re execute action is in the dialog class, just access the value of the field:

      var value1 = this.inputField1.get('value');
      var value2 = this.inputField2.get('value');
      

      If you function is in the action class, and assuming you can access your dialog with the dialog variable, just access the fields from the dialog:

      var value1 = dialog.inputField1.get('value');
      var value2 = dialog.inputField2.get('value');
      

      However you can’t call this right after calling the dialog.show() function. You need to wait for the user to validate the dialog, in a callback function, or using a deferred object.

      Reply
  10. Ram Medam

    Hi Guillaume,

    First Of all, This is good documentation. Thank you.

    I need your help…

    I am trying to update the permissions on a existing document. Below is the snippet i am using, i am getting null pointer exception…. Not sure how to proceed. Any Idea?

    docObj.retrievePermissions(lang.hitch(this,function(permissions){
    var acc1 = ({
    accessMask: 131201,
    accessType: 1,
    granteeName: “rmedam”,
    granteeType:2001,
    inheritableDepth:0
    });
    permissions.push(acc1);
    docObj.saveAttributes([], null, null, permissions, false, lang.hitch(this, function(response) {
    console.log(‘DocObject Saved With New Permissions’);
    }),false,lang.hitch(this,function(er){
    console.log(er);
    }));

    Reply
    1. Ram

      Below Code Worked………

      docObj.retrievePermissions(lang.hitch(this,function(res){

      permissions = [];
      dojo_array.forEach(res, function(permission) {
      var acc = {
      accessMask: permission.accessMask,
      accessType: permission.accessType,
      granteeType: permission.granteeType,
      inheritableDepth: permission.inheritableDepth,
      granteeName: permission.name
      }
      permissions.push(acc);
      });

      var acc1 = ({
      accessMask: 131201,
      accessType: 1,
      granteeName: "rmedam",
      granteeType:2001,
      inheritableDepth:0
      });
      permissions.push(acc1);

      docObj.saveAttributes([], null, null, permissions, false, lang.hitch(this, function(response) {
      console.log('DocObject Saved With New Permissions');
      }),false,lang.hitch(this,function(er){
      console.log(er);
      }));
      }))

      Reply
  11. Deep Thakkar

    Hi,

    I have a requirement, where I need to download the documents selected from the search results along with the metadata of each documents in an excel as a zip file.
    I am able to get the vsID of each documents and can call a service method to perform the download action.
    But, I am not able to find a way to download the document in a temp location in server and send the content(zip file) to the browser as OutStream.

    here is my dojo code:

    var reqParams = new Object();
    reqParams = serviceParams;
    var request = ecm.model.Request.invokePluginService(“DateFormatter”,”DownloadExportService”,{
    requestParams : serviceParams,
    requestCompleteCallback : function(response) {
    // success
    console.log(“DownloadExportService called”);
    console.log(“The sample plugin service completed, returning:\n”+ dojo.toJson(response,true,” “));
    }
    }

    Can I transform the byte[] of the zip file as json and send it back the above javascript call, and somehow export the file to the browser?
    Any kind of help will be appreciated, related to downloading the document to a temp location in server and stream out the document/zip content to the browser.

    Thanks,
    Deep Thakkar

    Reply
    1. Guillaume Post author

      Hi Deep,

      I took a look at the Request class and I think there is no way it can handle file result sent by writing in the response.getOutputStream(), even if we do some aspect tweak in the Request class. I will eventually try again to overwrite the whole process to see if I can make it work, but meanwhile what I would do is to:

      • Create the zip in your service and put it in a accessible space of your server
      • Send back the exact link in your response
      • In the requestCompleteCallback function, open a new window with the link and the browser should offer the download

      Sending the byte array in the json is a terrible idea 🙂 And I doubt that would work anyway because of binary issue.

      Hope that helps.

      Reply
    2. Guillaume Post author

      I just thought, an easier way could be to call your service in a new popup. That way:

      • No need to return a json object, write your file as byte in the response’s outputstream
      • No need for an accessible space

      Just open the popup with the URL http://host:port/navigator/jaxrs/plugin?plugin=PluginId&action=ServiceId&security_token=7917535885080325046&someparam=value, token being from ecm.model.Request._security_token.

      I can’t come up with a better way right now but this one is pretty nice, only one service, no extra space.

      Reply
  12. George Bredis

    Hi!
    Great article!
    I’m new to all this ECM (especially ICN) stuff, and I have a problem. Could You, please, help me?
    I need to create an action in ICN, which will create a new version of existing ContentItem and I’m confused with setting all the parameters for JS checkIn function. For example, the content parameter, as well as others. Could You provide me with the sample, which will just create a new version of existing CintentItem?

    Reply
    1. Guillaume Post author

      Hi George,

      I’m really sorry about the delay. I was travelling in the previous month and I didn’t get a chance to answer any of the comments. I hope that doesn’t come to late.

      Here is a simple example on how to check out/in a document. Hee I’m using hard coded content for this example, but you can replace the “new File” by the File object provided by your browser (meaning the user), that would be the content of your new version.


      var item = itemList[0];
      repository.lockItems([item], function () {
        // Ok here the item is checked out (locked, same meaning :))
        // Let's check it back
        item.checkIn(
          item.template, // templateName, we want to keep the same class
          [], // criteria, I don't change any property here, hence the empty array
          "Document", // contentSourceType
          "", // mimeType, leaving it empty will let the CE figure it out
          "test.txt", // filename, if you're using a File object, get its name
          new File([new Blob(['This is some content'], {type: 'text/plain'})], 'test.txt'), // Creating the file on the fly, but if you have a File object provided by the user use it here
          [], // childComponentValues, not a compound doc here
          null, // permissions, leave it null will keep previous version permissions
          null, // securityPolicyId, no security policy
          null, // you don;t care for P8, this is a CM feature
          false, // checkInAsMinorVersion, we want a major version
          false, // autoClassify, I don't want to autoclassify on check it, maybe you do
          function () {
            // callback when check in is done
            // I want to refresh the folder so user instantly see the new version number
            // in the grid
            item.parent.refresh();
          });
      });

      Hope that helps.

      Reply
  13. George Bredis

    Hi, Guillaume! Thanks a lot for such a detailed reply!
    In principle, I’ve already implemented something like this, but in any case I’m very grateful to You! Would You be so kind to answer some more questions?
    Another task of mine is to develop an action which will create a new ContentItem with the special standard “blank” (containing some text) PDF-file as its content. For now, my intermediate solution is to use HTML (instead of PDF) file (contained within the plugin), which is read as text inside the action. Then the BLOB is created from this text and used as new item file content (the same way as shown in Your example). By the way, I recognized that Internet Explorer doesn’t allow “new File([new Blob…” (Chrome – does!), but the workaround is using BLOB itself, without wrapping with the File…. 🙂
    This works fine, but the final requirement is that the file should be PDF, and I still didn’t find a solution on how to work with binary data for explained scenario… The main point is that the user shouldn’t select file manually – everything have to be automated.
    Could You, please, help me?

    As well, I have some questions regarding EDS. I use it with success, but some points are not clear for me… Maybe I should ask this in some other thread here?

    Thanks in advance!

    George

    Reply
    1. Guillaume Post author

      Hi George,

      Well, generating PDF using JavaScript is not really an ICN issue. I’m sure you can find some documentation on this easily, first Google link being this.

      But one suggestion: isn’t it enough to store files as text, and let users use the “Download as PDF” action to get a PDF from it? ICN already has a PDF engine, so why not use it?

      Hope that helps.

      Reply
      1. George Bredis

        Hi, Guillaume!
        Thanks a lot for Your reply! Well, for the moment the customer is satisfied with the solution of storing text files for initial “blanks”.
        However, I have another question, and it’s related exactly the ICN – custom editors, etc. Would You be so kind to answer it, if I post it to the proper topic (Custom editors)?

        Reply
  14. Abhishek Kumar

    Hi guillaume

    I’m new to for ICN . and I have a problem. Could You, please, help me?
    I need to create in ICN, which will auto generate ID No like (Invoice1234) that when adding document i will select Entry template like (invoice ) then all properties is coming in there one properties should be auto generate number like Invoice1234….., can you suggest me how i need to step by step

    Reply
  15. George Bredis

    Hello, Guillaume!
    I have a simple question. Hopefully, You can help me.
    My item class contains a reference attribute (with dataType=”xs:reference”). What I need is to set it’s value via ecm JS, having a PID String (I’m using CM8.4.3). The question is how to achieve this, if it’s possible? I was trying the following simple code in my Action:

            performAction: function (repository, items, callback, teamspace, resultSet, parameterMap) {
                var item=items[0];
                item.retrieveAttributes(lang.hitch(this, function(attrs) {
                    var attrs = [];
                    var attr = {};
                    attr.name="kadastrAzsRef";
                    attr.label="kadastrAzsRef";
                    attr.dataType="xs:reference";
                    attr.value="88 3 ICM6 ...."; //my PID string
                    attrs.push(attr);
                    repository.lockItems([item], lang.hitch(this, function () {
                        item.saveAttributes(attrs, null, null, null, true, lang.hitch(this, function (resp, fieldErrors) {
                            repository.retrieveItem(resp.rows[0].id, lang.hitch(this, function (itm) {
                                if (itm.locked) {
                                    repository.unlockItems([itm], lang.hitch(this, function () {
                                        console.log("Item unlocked!");
                                    }));
                                }
                            }));
                        }));
                    }));
    
                }));
    

    And during the execution of saveAttributes method I get an error on server side:
    [1/23/17 16:06:37:516 MSK] 000000e9 SystemErr R CIWEB Error: [admin(unknown) @ fe80:0:0:0:fcd0:34ae:b4e:2fd7%11] com.ibm.ecm.struts.actions.cm.CMEditAttributesAction.executeAction()
    java.lang.NullPointerException
    at com.ibm.ecm.struts.actions.cm.CMEditAttributesAction.updateItem(CMEditAttributesAction.java:700)
    at com.ibm.ecm.struts.actions.cm.CMEditAttributesAction.executeAction(CMEditAttributesAction.java:215)
    ….

    So, is it possible in principle, and if Yes – then How?

    Thanks in advance!
    George Bredis

    Reply
    1. Guillaume Post author

      Hi George,
      I apologize for the really late answer, but I changed my hosting in January and I just noticed emails weren’t working anymore so I missed all the comments.

      To answer your question, yes it’s definitely possible. First it’s not necessary to check out (lock) an item before updating a property. Check out is only necessary to update the content of a document.
      So you can get rid of your lockItems call and unlockItems call. Since you don’t do the unlock anymore, you can also get rid of retrieveItem 🙂

      This leaves you with this code, that I just made work on my VM to make sure 🙂

      
      var attrs = [{
          name: "ObjectProperty",
          label: "ObjectProperty",
          dataType: "xs:reference",
          value: "{5045465A-0000-C412-8EF1-38EE8F3DA2F4}"
      }];
      item.saveAttributes(attrs, null, null, null, true, lang.hitch(this, function (resp, fieldErrors) {
          console.log(resp);
      }));
      

      I don’t have CM and I’m using FileNet P8 but this should basically be the same for you with a CM ID.

      Now looking at your NullPointerException, it’s possible that the item your trying to update does not have the property so are you sure it’s the correct class?

      Hope that helps.

      Reply
      1. George Bredis

        Hello, Guillaume!
        Thanks a lot for Your reply! I’ll check the behavior once more, since the code You provided me with is actually almost the same as mine (except of lock / unlock item). For now I implemented the requirement via the call to ICN service and updating the item in backend, but that’s not what I like 🙂

        By the way, looks like new hosting makes Your site much faster, than before! 🙂
        Once again, thank You for Your replies and help!

        Reply
  16. Andrew

    How can I fetch user role from response filter for p8/search filter service? I could get login user with the help of callback.getuserId

    Reply

Leave a Reply