IBM Content Navigator - Advanced Development Course

advertisement

 

 

 

 

 

 

 

 

IBM  Content  Navigator  Plug-­‐in  Development  

Advanced  Topics  

 

 

 

 

 

 

 

 

Brett  Morris,  IBM  

 

 

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   1  

Table  of  Contents  

Introduction  ..............................................................................................................................  3

 

1.  Plug-­‐in  Repository  Types  .................................................................................................  4

 

1.1  Creating  the  repository  type  ..................................................................................................  4

1.2  Enabling  authentication  with  the  repository  type  .........................................................  5

1.3  Add  additional  actions  to  the  repository  type  ...............................................................  18

 

1.4  Enabling  the  repository  configuration  panels  ...............................................................  32

1.5  Optional  Exercise  .....................................................................................................................  37

 

 

 

 

2.  Creating  a  Plug-­‐in  API  .....................................................................................................  38

 

2.1  Creating  a  Plug-­‐in  API  ............................................................................................................  38

2.2  Using  a  Plug-­‐in  API  ..................................................................................................................  40

 

 

3.  Open  Actions  ......................................................................................................................  54

 

3.1  Creating  an  open  action  .........................................................................................................  54

3.2  Enabling  the  custom  open  action  .......................................................................................  58  

 

 

 

4.  Asynchronous  tasks  .........................................................................................................  59

 

4.1  IBM  ECM  Task  Manager  .........................................................................................................  59

4.2  Custom  Asynchronous  Tasks  ...............................................................................................  59

4.3  Creating  a  Custom  Asynchronous  Task  ............................................................................  60

4.4  Registering  the  Custom  Asynchronous  Task  ..................................................................  64

 

 

 

4.5  Creating  a  Custom  Scheduling  Pane  ..................................................................................  65

4.6  Scheduling  Your  Custom  Task  .............................................................................................  68  

 

 

 

2   IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

 

 

Introduction  

 

In  this  course  you  will  complete  exercises  focused  on  advanced  IBM  Content  

Navigator  Plug-­‐in  development  topics.  The  course  builds  upon  the  techniques  you   learned  in  the  earlier  development  course.  

 

 

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   3  

 

1.  Plug-­‐in  Repository  Types  

 

There  may  be  cases  where  you  encounter  a  custom  content  source  or  legacy  system   that  you  need  to  integrate  into  your  Content  Navigator  user  interface.  Generally  it  is   recommended  you  first  consider  creating  a  Content  Management  Interoperability  

Services  (CMIS)  provider  for  this  type  of  source.  That  allows  you  to  use  many  of  the   existing  Content  Navigator  features,  without  requiring  any  user  interface   development,  and  provides  a  standard  mechanism  for  accessing  the  source.  

However,  it  is  sometimes  a  challenge  to  define  a  CMIS  provider  for  a  specific  source   type  (perhaps  because  it  is  not  really  an  enterprise  content  management  system)  or   the  situation  requires  custom  interaction  with  the  source  and  very  little  of  the  

Content  Navigator  existing  features  will  be  used.  In  that  case,  you  may  only  want   custom  authentication  and  a  small  number  of  the  existing  services  in  the  Content  

Navigator  framework  for  the  custom  content  source  and  custom  panels  and  services   for  everything  else.  To  accommodate  this  rare  use  case,  Content  Navigator  provides   the  ability  to  define  a  custom  repository  type  in  a  plug-­‐in.  In  this  exercise,  you  will   create  a  simple  plug-­‐in  repository  type.  

1.1  Creating  the  repository  type  

 

Right-­‐click  on  the  “

com.ibm.ecm.extension

”  package  in  the  eclipse  project  you   created  in  the  base  “

Content  Navigator  Development  Course

”  and  use  the  “

IBM  

Content  Navigator

”  menu  to  create  a  “

New  Repository  Type

”.  In  the  new  

  repository  type  wizard  set  the  class  name  to  “

IBMDevCourseRepository

”.  

4   IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

 

 

 

Figure  1.1  

Click  “Ok”  to  create  the  new  repository  type.  The  wizard  generates  five  files,  the   main  Java  class,  “

IBMDevCourseRepository.java

”,  two  JavaScript  classes,  

IBMDevCourseRepositoryGeneralConfigurationPane.js

”  and  

IBMDevCourseRepositoryConfigurationParametersPane.js

”,  and  two  HTML   files,  which  will  serve  as  the  HTML  templates  for  the  JavaScript  classes.  Like  other   plug-­‐in  types,  the  main  Java  class  defines  the  base  repository  type.  The  wizard   generates  a  java.util.Map,  which  will  be  used  to  map  the  various  actions  the   repository  type  will  support.  Initially  no  actions  are  mapped,  but  you  will  create   several  to  enable  this  repository  type.  Similar  to  other  plug-­‐in  types,  the  repository   type  has  both  an  identifier  and  a  name  used  to  represent  the  repository  type  in  the  

Content  Navigator  administration.  The  “

performAction

”  method  is  responsible  to   handling  an  action  call  to  your  repository  type.  When  the  user  initiates  an  action  on   your  repository  type  in  the  user  interface,  such  as  a  call  to  log  in,  Content  Navigator   will  route  the  call  to  your  “

performAction

”  method.  There  is  no  need  for  you  to   implement  any  user  interface  source  code  for  this  to  happen.  The  Content  Navigator  

  framework  handles  it  automatically.  

In  addition,  to  the  identifier,  name  and  perform  action  methods,  the  repository  type   definition  defines  two  widget  classes  with  the  methods,  

getConfigurationDijitClass

”  and  “

getConnectedConfigurationDijitClass

”.  These   allow  you  to  specify  custom  panels  for  the  repository  configuration  in  the  Content  

Navigator  administration.  Typically  a  repository  definition  will  have  a  general  panel,   which  is  used  to  collect  the  basic  information  necessary  to  connect  to  the  repository.  

Additional  panels  are  available  after  the  administrator  has  connected  to  the   repository,  allowing  the  administration  to  add  any  additional  configuration   necessary  to  enable  the  repository.  A  plug-­‐in  provided  repository  type  has  the  same   capability  and  the  wizard  generated  the  shell  for  the  two  user  interface  widgets  that  

  will  be  used  in  the  repository  configuration.  

The  final  methods  in  the  repository  type  class  are,  “

logon

”,  “

logoff

”,  

getRepositoryModelClass

”  and  “

getIconClass

”.  The  “

getIconClass

”  method   allows  you  to  provide  a  custom  icon  to  use  in  the  Content  Navigator  administration   to  represent  the  repository.  You  can  also  optionally  provide  a  custom  repository  

JavaScript  model  extension  for  your  repository  type,  using  the  

getRepositoryModelClass

”  method.  The  “

logon

”  and  “

logoff

”  methods  are  used  to   handle  the  authentication  and  disconnect  from  the  repository.  The  “logon”  method   returns  a  “

PluginRepositoryConnection

”  type,  which  will  represent  your  

  repository  type  on  the  server-­‐side.  You  must  implement  this  in  order  to  enable  your   repository  type.  

 

1.2  Enabling  authentication  with  the  repository  type  

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   5  

In  order  to  enable  the  repository  type  for  use  within  Content  Navigator,  you  will   need  to  create  a  new  class  that  extends  the  “

PluginRepositoryConnection

”  and   enable  the  log  in  and  log  out  methods  in  your  repository  type.  Right-­‐click  on  the  

com.ibm.ecm.extension

”  package  and  create  a  new  package  called  

 

repositoryType

”.  You  will  use  this  new  package  to  isolate  the  classes  you  create   for  the  repository  type.  

 

Figure  1.2  

Right-­‐click  on  the  newly  created  package  and  create  a  new  Java  class  called  

RepositoryConnection

”,  with  the  superclass  

 

com.ibm.ecm.extension.PluginRepositoryConnection

”.    

 

6   IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

 

 

Figure  1.3  

From  the  resources  directory  in  the  course  materials,  copy  the  “repository.json”  file   to  your  “

com.ibm.ecm.extension.data

”  package.  For  the  remaining  exercises,  you  

  will  be  using  this  JSON  file  to  simulate  the  presence  of  a  real  source.  

Open  the  newly  created  “

RepositoryConnection.java

”  file  and  make  the  following   changes:  

  package com.ibm.ecm.extension.repositoryType;

 

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   7  

import java.io.InputStream; import com.ibm.ecm.configuration.RepositoryConfig; import com.ibm.ecm.extension.PluginRepositoryConnection; import com.ibm.json.java.JSONObject; public class RepositoryConnection extends PluginRepositoryConnection { private JSONObject repositoryJSON; public RepositoryConnection(String type, String userId,

RepositoryConfig repositoryConfig ) throws Exception { super (type, userId, userId );

// TODO Auto-generated constructor stub

InputStream inStream = null; try {

// Load the repository JSON inStream = this.getClass().getClassLoader().getResourceAsStream("com/ibm/ecm/exten sion/data/repository.json"); repositoryJSON = JSONObject.

parse (inStream);

} finally {

} if (inStream != null) {

} inStream.close();

} public JSONObject getRepositoryJSON() {

} return repositoryJSON;

}

The  changes  above  simply  update  the  constructor  to  read  the  repository  JSON   definition  and  provide  a  method  that  can  be  used  to  obtain  the  parsed  JSON.  The   next  step  is  to  enable  the  “

logon

”  and  “

logout

”  methods  in  the  main  repository  type  

Java  class.  To  do  that,  you  will  create  action  handler  classes  for  logon  and  logout.  

Right-­‐click  on  the  “

com.ibm.ecm.extension.repositoryType

”  package  and  create  a   new  Java  class  called  “

RepositoryLogon

”  and  make  the  following  changes  to  the   newly  created  file:  

  package com.ibm.ecm.extension.repositoryType; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.ibm.ecm.configuration.RepositoryConfig; import com.ibm.ecm.extension.PluginRepositoryConnection; import com.ibm.ecm.extension.PluginRepositoryLogonException; import com.ibm.ecm.extension.PluginServiceCallbacks;

8   IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

  import com.ibm.ecm.json.JSONResponse; import com.ibm.json.java.JSONArray; import com.ibm.json.java.JSONObject; public class RepositoryLogon { public

}

RepositoryLogon() {

// TODO Auto-generated constructor stub

@SuppressWarnings("unchecked") public void performAction(PluginServiceCallbacks callbacks,

RepositoryConfig repositoryConfig, HttpServletRequest request,

HttpServletResponse response) throws Exception {

String loginRepoId = request.getParameter("repositoryId");

String serverName = request.getParameter("serverName");

String userid = request.getParameter("userid");

String password = request.getParameter("password");

JSONResponse jsonResponse = new JSONResponse(); if (repositoryConfig.isEmpty() && serverName != null){ repositoryConfig.setServerName(serverName);

}

PluginRepositoryConnection connection = logon(callbacks, request, userid, password, repositoryConfig); if (connection != null) {

Map<String, PluginRepositoryConnection> pluginRepositoryConnectionList = (Map<String,

PluginRepositoryConnection>)

(request.getSession((true)).getAttribute("plugin_repositories")); if (pluginRepositoryConnectionList == null) { pluginRepositoryConnectionList = new

HashMap<String, PluginRepositoryConnection>(); pluginRepositoryConnectionList); request.getSession(true).setAttribute("plugin_repositories",

} pluginRepositoryConnectionList.put(loginRepoId, connection);

JSONArray servers = new JSONArray(); jsonResponse.put("servers", servers);

JSONObject server = new JSONObject(); server.put("connected", true); server.put("id", loginRepoId); servers.add(server);

}

} callbacks.writeJSONResponse(jsonResponse, response); public PluginRepositoryConnection logon(PluginServiceCallbacks callbacks, HttpServletRequest request, String userid, String password,

RepositoryConfig repositoryConfig) throws

PluginRepositoryLogonException {

// For this example, user1, user2 and ibm will valid with any password

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   9  

  if ("user1".equalsIgnoreCase(userid) ||

"user2".equalsIgnoreCase(userid) || "ibm".equalsIgnoreCase(userid)) { try {

RepositoryConnection connection = new

RepositoryConnection("IBMDevCourseRepository", userid, repositoryConfig); return connection;

} catch (Exception e) { throw new PluginRepositoryLogonException(e);

} else {

} throw new

PluginRepositoryLogonException(PluginRepositoryLogonException.

LOGON_FAI

LURE_BAD_USERID_OR_PASSWORD );

}

}

}

In  this  case,  since  this  is  merely  an  example  of  how  to  create  a  repository  type,  the   actual  log  in  method  will  be  hardcoded  to  accept  three  user  ids  with  any  password   and  it  will  generate  an  instance  of  the  RepositoryConnection  class  you  already   created.  If  the  user  id  entered  by  the  user  does  not  match,  you  throw  a  standard   invalid  credentials  exception.  The  “

performAction

”  method,  which  will  be  called  by   the  main  repository  type  Java  class,  is  checking  for  a  cache  of  plug-­‐in  provided   repository  types  in  the  session  first.  If  none  is  found,  it  will  create  a  new  HashMap,   to  store  a  handle  to  the  repository  connection,  making  it  available  to  other  actions   that  will  be  initiated  after  log  in.  Finally  the  method  uses  the  PluginServiceCallbacks  

  to  stream  a  standard  Content  Navigator  repository  JSON  payload  back  to  the  client.  

Now,  create  another  class  in  the  “

com.ibm.ecm.extension.repositoryType

”   package  called  “

RepositoryLogoff

”  and  make  the  following  changes  to  the  class:  

  package com.ibm.ecm.extension.repositoryType; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import com.ibm.ecm.configuration.RepositoryConfig; import com.ibm.ecm.extension.PluginRepositoryConnection; import com.ibm.ecm.extension.PluginServiceCallbacks; public class RepositoryLogoff { public RepositoryLogoff() {

// TODO Auto-generated constructor stub

} public void performAction(PluginServiceCallbacks callbacks,

RepositoryConfig repositoryConfig, HttpServletRequest request,

HttpServletResponse response) throws Exception {

String repositoryId = request.getParameter("repositoryId");

RepositoryConnection connection =

1

0    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

(RepositoryConnection)callbacks.getPluginRepositoryConnection(repositor yId);

} logoff(callbacks, request.getSession(false), connection); public void logoff(PluginServiceCallbacks callbacks, HttpSession session, PluginRepositoryConnection connection) throws Exception { callbacks.getLogger().logInfo(this, "logoff", "Logged off");

// Since the connection is a dummy connection there is no actual work to do in this method

}

}

In  this  case,  your  plug-­‐in  repository  type  is  not  actually  creating  any  resources  or   establishing  any  handles  that  need  to  be  disconnected,  so  the  log  off  does  not  need   to  do  anything.  However,  if  this  were  a  real  data  source,  it  is  extremely  likely  this   method  would  be  necessary  and  failing  to  implement  anything  here  could  result  in  

  runtime  issues.  

Now  that  you  have  log  on  and  log  off  action  classes,  it  is  time  to  enable  log  on  and  log   off  from  your  main  repository  type  Java  class.  Open  

IBMDevCourseRepository.java

”  from  the  

com.ibm.ecm.extension.repositoryType

”  package  and  make  the  following  

  changes:   import java.lang.reflect.Method; import java.util.HashMap; import java.util.Locale; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import com.ibm.ecm.configuration.RepositoryConfig; import com.ibm.ecm.extension.PluginRepositoryConnection; import com.ibm.ecm.extension.PluginRepositoryType; import com.ibm.ecm.extension.PluginServiceCallbacks; import com.ibm.ecm.extension.repositoryType.RepositoryLogoff; import com.ibm.ecm.extension.repositoryType.RepositoryLogon; import com.ibm.ecm.json.JSONMessage; import com.ibm.ecm.json.JSONResponse;

/**

* Provides an abstract class that is extended to create a class defining a custom repository type provided by the

* plug-in. Additional PluginService classes are implemented to provide the different services of the repository type.

* <p>

* For enterprise content servers providing Content Management

Interoperability Services (CMIS), the CMIS repository

* type can be used to access the server. A plug-in provided repository type can be used to extend IBM Content Navigator

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   1

1  

* to support access other data sources not traditionally considered enterprise content servers.

* <p>

* Note that additional configuration is needed by the Content

Navigator administrator to define the actual repository

* of the plug-in provided repository type and add it to a desktop.

*/ public class IBMDevCourseRepository extends PluginRepositoryType { private static final Map<String, String>

IBMDevCourseRepositoryActionMap = new HashMap<String, String>() { private static final long serialVersionUID =

7212659949366854547L;

{ put("logon",

"com.ibm.ecm.extension.repositoryType.RepositoryLogon"); put("logoff",

"com.ibm.ecm.extension.repositoryType.RepositoryLogoff");

}

}; public void performAction(PluginServiceCallbacks callbacks,

RepositoryConfig repositoryConfig, String action, HttpServletRequest request, HttpServletResponse response) throws Exception { if ( IBMDevCourseRepositoryActionMap .containsKey(action)) { callbacks.getLogger().logDebug( this , "performAction",

"Executing \"" + action + "\"...");

Object obj =

Class.

forName ( IBMDevCourseRepositoryActionMap .get(action)).newInstance(

);

Method m = obj.getClass().getMethod("performAction",

PluginServiceCallbacks.

class , RepositoryConfig.

class ,

HttpServletRequest.

class , HttpServletResponse.

class ); response);

} else m.invoke(obj, callbacks, repositoryConfig, request,

{ callbacks.getLogger().logError( this , "performAction",

"${PluginClassName} does not support the repository action \"" + action

+ "\".");

// return JSON with an error as well for the client

JSONResponse jsonResponse = new JSONResponse(); jsonResponse.addErrorMessage( new JSONMessage(20005,

"${PluginClassName} does not support the action", "The repository action \""+action+"\" is not supported.", null , null , null )); response.getWriter().write(jsonResponse.serialize());

}

}

@Override public String getId() {

} public return

@Override

"IBMDevCourseRepository";

String getName(Locale locale) { return "IBMDevCourseRepository";

1

2    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

;

} return "IBM Development Course Repository";

@Override public String getConfigurationDijitClass() { return

"iBMDevCoursePluginDojo.IBMDevCourseRepositoryGeneralConfigurationPane"

} public String getConnectedConfigurationDijitClass() { return

"iBMDevCoursePluginDojo.IBMDevCourseRepositoryConfigurationParametersPa

ne";

}

@Override public PluginRepositoryConnection logon(PluginServiceCallbacks callbacks, HttpServletRequest request, String userid, String password,

RepositoryConfig repositoryConfig) throws Exception {

PluginRepositoryConnection connection =

RepositoryLogon.class.newInstance().logon(callbacks, request, userid, password, repositoryConfig); return connection;

}

@Override public void logoff(PluginServiceCallbacks callbacks, HttpSession session, PluginRepositoryConnection connection) throws Exception {

RepositoryLogoff.class.newInstance().logoff(callbacks, session, connection);

}

@Override public String getRepositoryModelClass() {

} return "";

} public String getIconClass() { return "";

}

 

Both  the  log  on  and  log  off  actions  need  to  be  added  to  your  action  map,  they  will  be   available  in  the  performAction  method.  Likewise,  you  changed  the  “

logon

”  and  

logoff

”  methods  in  the  repository  type,  so  that  they  now  call  your  new  action  

  handler  classes  to  handle  the  log  on  and  logoff.  

At  this  point,  you  are  ready  to  test  your  new  repository  type  in  Content  Navigator.  

Open  up  the  Content  Navigator  administration  and  open  your  plug-­‐in  in  the  plug-­‐in  

  administration.  It  should  now  list  the  new  repository  type.  

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   1

3  

Figure  1.4  

 

Go  to  the  repository  configuration  pane  and  you  should  now  be  able  to  create  a  new  

 

IBM  Development  Course  Repository.  

 

 

Figure  1.5  

1

4    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

Create  a  new  “

IBM  Development  Course  Repository

”.  Since  you  have  not  add   anything  to  the  configuration  widgets  created  by  the  wizard  you  will  be  presented   with  the  default  ID  and  display  name.  All  repository  types  in  Content  Navigator  must   have  a  unique  identifier  and  a  display  name  to  use  when  showing  the  repository  to  

  the  user.  Enter  the  display  name  “

Development  Course  Plugin  Demo

”  and  the  

connect

”  button  will  be  enabled.  

 

Figure  1.6  

Click  “Connect…”  and  you  will  be  prompted  for  credentials.  You’re  repository  type   should  only  accept  the  user  ids  “user1”,  “user2”  and  “ibm”.  Enter  a  valid  user  ID  and  

  you  can  now  access  the  configuration  tab.  

 

 

 

Figure  1.8  

Save  the  repository  type  definition  and  go  to  the  desktops  configuration  pane,  create   a  new  desktop  called  “

Dev  Course  Desktop  3

”,  with  an  ID  of  “

devcourse3

”.  Set  the  

 

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   1

5  

  newly  created  repository  type  as  the  authenticating  repository  and  on  the  layout   tab,  select  the  “

browse

”  feature  as  the  only  available  feature.    

 

Figure  1.9  

 

1

6    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

Figure  1.10  

Save  the  desktop  and  switch  to  the  “

devcourse3

”  desktop  to  test  your  new  plug-­‐in   repository  type.  After  logging  in  you  will  see  an  error  indicating  the  repository  type  

  does  not  support  the  necessary  actions  to  populate  the  browse  pane.  

 

 

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   1

7  

 

Figure  1.11  

 

1.3  Add  additional  actions  to  the  repository  type  

The  next  step  is  to  add  the  actions  required  to  populate  a  folder  tree  and  list  view  in  

Content  Navigator.  In  order  to  do  that,  you  will  need  to  add  several  actions  to  your   repository  type.  However,  first  you  are  going  to  create  several  new  model  classes  to   represent  the  objects  used  by  the  user  interface.  Right-­‐click  on  the  

com.ibm.ecm.extension.repositoryType

”  package  and  create  a  new  class  called  

AttributeDefintion

”  and  make  the  following  changes  to  the  class:  

  package com.ibm.ecm.extension.repositoryType; import com.ibm.json.java.JSONObject;

JSONObject attributeDefinitionJSON) { public class AttributeDefinition { private RepositoryConnection connection = null; private JSONObject attributeDefinitionJSON = null; public AttributeDefinition(RepositoryConnection connection,

} this.connection = connection; this.attributeDefinitionJSON = attributeDefinitionJSON; public String getId() {

 

1

8    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

} return (String)attributeDefinitionJSON.get("id"); public String getName() { return (String)attributeDefinitionJSON.get("name");

} public String getType() {

} public RepositoryConnection getRepositoryConnection() { return connection;

} public int getMaxLength() { if (attributeDefinitionJSON.containsKey("maxLength")) { return

((Long)attributeDefinitionJSON.get("maxLength")).intValue();

} return 0;

} if (attributeDefinitionJSON.containsKey("type")) {

} return (String)attributeDefinitionJSON.get("type"); return "xs:string"; public boolean isSystem() { if (attributeDefinitionJSON.containsKey("system")) { return

(Boolean)attributeDefinitionJSON.get("system");

}

} return false;

 

}

This  class  is  used  to  model  the  attribute  definitions  in  the  repository.json  file  you   imported  into  your  project  earlier.  You  will  need  to  create  two  more  to  represent   content  items  and  content  classes.  Create  a  new  Java  class  called  “

ContentItem

”  and  

  make  the  following  changes  to  the  new  file:   package com.ibm.ecm.extension.repositoryType; import java.io.InputStream; import com.ibm.json.java.JSONArray; import com.ibm.json.java.JSONObject; public class ContentItem { private RepositoryConnection connection; private JSONObject contentItemJSON; public ContentItem(RepositoryConnection connection, JSONObject contentItemJSON) { this.connection = connection;

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   1

9  

} this.contentItemJSON = contentItemJSON; public String getContentClassId() { return (String)contentItemJSON.get("contentClassId");

} public ContentClass getContentClass() { return connection.getContentClass(getContentClassId());

} public String getId() { return (String)contentItemJSON.get("id");

} public String getName() {

// By convention we'll use the first defined attribute as the name

AttributeDefinition nameAttributeDef = getContentClass().getAttributeDefinitions()[0]; return getAttributeValue(nameAttributeDef.getId()).toString();

} public int getAttributeCount() {

JSONObject attributes =

(JSONObject)contentItemJSON.get("attributes"); return attributes.size();

}

@SuppressWarnings("unchecked") public String[] getAttributeIds() {

JSONObject attributes =

(JSONObject)contentItemJSON.get("attributes");

String[] attributeIds = new String[attributes.size()]; attributes.keySet().toArray(attributeIds); return attributeIds;

} public Object getAttributeValue(String attributeId) {

JSONObject attributes =

(JSONObject)contentItemJSON.get("attributes"); return attributes.get(attributeId);

} public void setAttribute(String attributeId, Object value) {

// Note: This is not a permanent change to the json file, just temporary to the in-memory copy

JSONObject attributes =

(JSONObject)contentItemJSON.get("attributes"); attributes.put(attributeId, value);

}

/**

* If the item is a folder, this returns folder contents.

* @return

*/

2

0    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

}

@SuppressWarnings("unchecked") public String[] getContentIds() {

JSONArray contentItemIdsJSON =

(JSONArray)contentItemJSON.get("contentItemIds"); if (contentItemIdsJSON == null) { return new String[0];

}

String[] contentItemIds = new

String[contentItemIdsJSON.size()]; contentItemIdsJSON.toArray(contentItemIds); return contentItemIds;

} public boolean isFolder() {

} public boolean isDocument() {

} public String getContentType() {

} return getContentClass().isFolderClass(); return getContentClass().isDocumentClass(); if (isFolder()) { return "folder";

} return (String)contentItemJSON.get("contentType");

 

Please  note  the  above  code  will  not  compile  yet.  There  are  additional  changes  you   will  need  to  make  to  the  RepositoryConnection  class  in  order  to  fully  enable  the   model  objects.  

 

Right-­‐click  on  the  “com.ibm.ecm.extension.repositoryType”  package  and  create  the  

  last  model  object,  “

ContentClass

”  and  make  the  following  changes  to  the  new  file:   package com.ibm.ecm.extension.repositoryType; import com.ibm.json.java.JSONArray; import com.ibm.json.java.JSONObject; public class ContentClass { private RepositoryConnection connection; private JSONObject contentClassJSON; private String[] attributeDefinitionIds; private AttributeDefinition[] attributeDefinitions; public ContentClass(RepositoryConnection connection, JSONObject contentClassJSON) {

} this.connection = connection; this.contentClassJSON = contentClassJSON; public String getId() {

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   2

1  

} return (String)contentClassJSON.get("id"); public String getName() {

} return (String)contentClassJSON.get("name");

@SuppressWarnings("unchecked") public String[] getAttributeDefinitionIds() { if (attributeDefinitionIds == null) {

JSONArray attributeDefinitionIdsJSON = (JSONArray) contentClassJSON.get("attributeDefinitionIds"); attributeDefinitionIds = new

String[attributeDefinitionIdsJSON.size()]; attributeDefinitionIdsJSON.toArray(attributeDefinitionIds);

}

} return attributeDefinitionIds; public AttributeDefinition[] getAttributeDefinitions() { if (attributeDefinitions == null) {

String[] attributeDefinitionIds = getAttributeDefinitionIds(); attributeDefinitions = new

AttributeDefinition[attributeDefinitionIds.length]; for (int i = 0; i < attributeDefinitionIds.length; i++) { attributeDefinitions[i] = connection.getAttributeDefinition(attributeDefinitionIds[i]);

}

} return attributeDefinitions;

} public boolean isFolderClass() { return contentClassJSON.containsKey("semanticType") && contentClassJSON.get("semanticType").equals("folder");

} public boolean isDocumentClass() { return contentClassJSON.containsKey("semanticType") && contentClassJSON.get("semanticType").equals("document");

}

}

 

Open  the  “

RepositoryConnection.java

”  file  and  make  the  following  changes:   package com.ibm.ecm.extension.repositoryType; import java.io.InputStream; import java.util.Collection; import java.util.Hashtable; import com.ibm.ecm.configuration.RepositoryConfig; import com.ibm.ecm.extension.PluginRepositoryConnection;

2

2    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

  import com.ibm.json.java.JSONArray; import com.ibm.json.java.JSONObject; public class RepositoryConnection extends PluginRepositoryConnection { private JSONObject repositoryJSON; private Hashtable<String, AttributeDefinition> attributeDefinitions; private Hashtable<String, ContentClass> contentClasses; public RepositoryConnection(String type, String userId,

RepositoryConfig repositoryConfig) throws Exception { super try

(type, userId, userId);

InputStream inStream =

{ null ;

// Load the repository JSON inStream = this .getClass().getClassLoader().getResourceAsStream("com/ibm/ecm/exten sion/data/repository.json"); repositoryJSON = JSONObject.

parse (inStream);

// Cache the attribute definitions attributeDefinitions = new Hashtable<String,

AttributeDefinition>();

JSONArray attributeDefinitionsJSON = (JSONArray) repositoryJSON.get("attributeDefinitions"); for (int i = 0; i < attributeDefinitionsJSON.size(); i++) {

JSONObject attributeDefinitionJSON =

(JSONObject) attributeDefinitionsJSON.get(i);

String attributeDefinitionId = (String) attributeDefinitionJSON.get("id");

AttributeDefinition attributeDefinition = new

AttributeDefinition(this, attributeDefinitionJSON); attributeDefinition);

} attributeDefinitions.put(attributeDefinitionId,

ContentClass>();

// Cache the content classes contentClasses = new Hashtable<String,

JSONArray contentClassesJSON = (JSONArray) repositoryJSON.get("contentClasses"); for (int i = 0; i < contentClassesJSON.size(); i++) {

JSONObject contentClassJSON = (JSONObject) contentClassesJSON.get(i);

String contentClassId = (String) contentClassJSON.get("id");

ContentClass contentClass = new

ContentClass(this, contentClassJSON); contentClasses.put(contentClassId, contentClass);

}

} finally { if (inStream != null ) {

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   2

3  

} public

}

}

} inStream.close();

JSONObject getRepositoryJSON() { return repositoryJSON; public ContentClass[] getContentClasses() {

ContentClass[] contentClassArray = new

ContentClass[contentClasses.size()];

Collection<ContentClass> contentClassCollection = contentClasses.values(); contentClassArray = contentClassCollection.toArray(contentClassArray); return contentClassArray;

} public AttributeDefinition getAttributeDefinition(String attributeDefinitionId) { if

(attributeDefinitions.containsKey(attributeDefinitionId)) { return attributeDefinitions.get(attributeDefinitionId);

} throw new RuntimeException("Attribute definition not found with id " + attributeDefinitionId the repository JSON.");

+ ". There is likely an error in the format of

} public ContentClass getContentClass(String contentClassId) { if (contentClasses.containsKey(contentClassId)) { return contentClasses.get(contentClassId);

} throw new RuntimeException("Content class not found with id

" + contentClassId the repository JSON.");

+ ". There is likely an error in the format of

} public ContentItem getRootItem() throws Exception {

JSONArray contentItemsJSON = (JSONArray) repositoryJSON.get("contentItems");

// By convention, the first item is the root

JSONObject contentItemJSON = (JSONObject) contentItemsJSON.get(0);

} return new ContentItem(this, contentItemJSON); public ContentItem getContentItem(String contentItemId) throws

Exception {

JSONArray contentItemsJSON = (JSONArray) repositoryJSON.get("contentItems");

// Scan the list looking for the first item with the id for (int i = 0; i < contentItemsJSON.size(); i++) {

2

4    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

JSONObject contentItemJSON = (JSONObject) contentItemsJSON.get(i);

{ if (contentItemId.equals(contentItemJSON.get("id")))

} return new ContentItem(this, contentItemJSON);

} throw new Exception("Content item not found with id: " + contentItemId);

}

}

The  above  changes  will  allow  the  other  objects  you  created  to  compile.  The  

RepositoryConnection  class  is  now  caching  attribute  definitions  and  content  class   definitions  found  in  the  repository.json  file  and  provided  methods  to  retrieve   attributes,  content  classes  and  content  items.  Next  you’re  going  to  add  one  more   utility  class,  which  will  contain  a  method  you  will  use  multiple  times  in  two  separate   actions.  Right-­‐click  on  “

com.ibm.ecm.extension.repositoryType

”  package  and   create  a  new  class  called,  “

RepositoryUtils

”  with  the  following  source:  

  package com.ibm.ecm.extension.repositoryType; import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; import com.ibm.ecm.extension.PluginServiceCallbacks; import com.ibm.ecm.json.JSONMessage; import com.ibm.ecm.json.JSONResultSetColumn; import com.ibm.ecm.json.JSONResultSetResponse; import com.ibm.ecm.json.JSONResultSetRow; import com.ibm.json.java.JSONArray; import com.ibm.json.java.JSONObject; public class RepositoryUtils { public static JSONResultSetResponse buildResultSetResponse(PluginServiceCallbacks callbacks,

RepositoryConnection connection, ArrayList<ContentItem> contentItemList) {

JSONResultSetResponse resultSetResponse = new

JSONResultSetResponse(); try { columns that are

// Iterate through all the items and determine the

// needed to present all of the item attributes

Hashtable<String, ContentClass> contentClasses = new

Hashtable<String, ContentClass>(); for (ContentItem contentItem : contentItemList) {

String contentClassId = contentItem.getContentClassId(); if

(!contentClasses.containsKey(contentClassId)) { contentClasses.put(contentClassId,

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   2

5  

connection.getContentClass(contentClassId));

}

}

ArrayList<String> joinedAttributeDefIds = new

ArrayList<String>();

Enumeration<ContentClass> elements = contentClasses.elements(); while (elements.hasMoreElements()) { elements.nextElement();

ContentClass contentClass =

String[] attributeStrings = contentClass.getAttributeDefinitionIds(); i++) { for (int i = 0; i < attributeStrings.length; if

(!joinedAttributeDefIds.contains(attributeStrings[i])) { joinedAttributeDefIds.add(attributeStrings[i]);

}

}

}

// Add the columns to the result response resultSetResponse.addColumn(new

JSONResultSetColumn("&nbsp;", "multiStateIcon", false, new String[0])); resultSetResponse.addColumn(new

JSONResultSetColumn("&nbsp;", "17px", "mimeTypeIcon", null, false)); for (int i = 0; i < joinedAttributeDefIds.size(); i++) {

String attributeDefinitionId = joinedAttributeDefIds.get(i);

AttributeDefinition attributeDef = connection.getAttributeDefinition(attributeDefinitionId);

JSONResultSetColumn(); if (!attributeDef.isSystem()) {

JSONResultSetColumn resultSetColumn = new resultSetColumn.setName(attributeDef.getName()); resultSetColumn.setField(attributeDef.getId()); resultSetColumn.setWidth(Integer.

toString (Math.

min (attributeDef.g

etMaxLength(), 10)) + "em"); resultSetResponse.addColumn(resultSetColumn);

}

} the sample, just

// Add magazine columns for the magazine view. For

// a thumbnail and the first attribute of the class(es) is displayed. Usually

// this would be configurable and likely provide important timestamps resultSetResponse.addMagazineColumn(new

JSONResultSetColumn("thumbnail", "60px", "thumbnail", null, null));

JSONArray fieldsToDisplay = new JSONArray();

2

6    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

  if (joinedAttributeDefIds.size() > 0) {

String attributeDefinitionId = joinedAttributeDefIds.get(0);

AttributeDefinition attributeDef = connection.getAttributeDefinition(attributeDefinitionId);

JSONObject jsonObj = new JSONObject(); jsonObj.put("field", attributeDef.getId()); jsonObj.put("displayName", attributeDef.getName()); fieldsToDisplay.add(jsonObj);

} resultSetResponse.addMagazineColumn(new

JSONResultSetColumn("content", "100%", "content", fieldsToDisplay, null));

// Add the rows to the result response for (ContentItem contentItem : contentItemList) {

JSONResultSetRow resultSetRow = new

JSONResultSetRow(contentItem.getContentClassId(), contentItem.getId(), contentItem.getName(), contentItem.getContentType(),

JSONResultSetRow.

PRIV_VIEWDOC | JSONResultSetRow.

PRIV_EXPORT |

JSONResultSetRow.

PRIV_EMAILDOC | JSONResultSetRow.

PRIV_PRINTDOC |

JSONResultSetRow.

PRIV_EDITPROPERTIES ); resultSetRow.setName(contentItem.getName());

String[] attributeIds = contentItem.getAttributeIds(); for (int j = 0; j < attributeIds.length; j++) {

String attributeId = attributeIds[j];

Object attributeValue = contentItem.getAttributeValue(attributeId); resultSetRow.addAttribute(attributeId, attributeValue, null, null, null);

}

} resultSetResponse.addRow(resultSetRow);

} catch (Exception e) { callbacks.getLogger().logError(RepositoryUtils.class,

"buildResultSetResponse", e);

JSONMessage message = new JSONMessage(30000, "The content server is not available.",

"The content server cannot return content items.", "",

"See the server logs for more information on this error: " + e.getLocalizedMessage(), null); resultSetResponse.addErrorMessage(message);

}

}

} return resultSetResponse;

 

This  above  static  utility  method  takes  an  array  of  ContentItems  and  creates  a   response  that  is  suitable  for  consumption  by  the  Content  Navigator  FolderTree  and  

ContentList  widgets.  Using  JSONResultSetResponse  class  the  code  creates  a  

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   2

7  

structure  for  both  details  and  magazine  views  of  the  ContentList  and  adds  the  items   to  a  list  within  the  JSON  structure.  With  this  utility  method  you  are  now  ready  to   create  the  actions  necessary  to  populate  a  folder  tree  and  list  view.  Right-­‐click  on  

com.ibm.ecm.extension.repositoryType

”  package  and  create  a  new  class  called,  

 

OpenFolder

”  with  the  following  source:   package com.ibm.ecm.extension.repositoryType; import java.util.ArrayList; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.ibm.ecm.configuration.RepositoryConfig; import com.ibm.ecm.extension.PluginServiceCallbacks; import com.ibm.ecm.json.JSONResultSetResponse; public class OpenFolder { public void performAction(PluginServiceCallbacks callbacks,

RepositoryConfig repositoryConfig, HttpServletRequest request,

HttpServletResponse response) throws Exception {

String repositoryId = request.getParameter("repositoryId");

RepositoryConnection connection = (RepositoryConnection) callbacks.getPluginRepositoryConnection(repositoryId);

String docid = request.getParameter("docid"); // id of folder boolean foldersOnly = false; if

("folderSearch".equals(request.getParameter("filter_type"))) { foldersOnly = true; // only nested folders should be returned

}

JSONResultSetResponse jsonResponse = new

JSONResultSetResponse();

ContentItem folder; if (docid.equals("/")) { folder = connection.getRootItem();

} else { folder = connection.getContentItem(docid);

}

String[] contentItemIds = folder.getContentIds();

ArrayList<ContentItem> contentItemList = new

ArrayList<ContentItem>(); for (int i = 0; i < contentItemIds.length; i++) {

ContentItem contentItem = connection.getContentItem(contentItemIds[i]);

} if (!foldersOnly || contentItem.isFolder()) {

} contentItemList.add(contentItem); jsonResponse =

2

8    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

}

RepositoryUtils.

buildResultSetResponse (callbacks, connection, contentItemList); callbacks.writeJSONResponse(jsonResponse, response);

}

The  open  folder  action  uses  the  folder  id  passed  by  the  client  to  retrieve  the   appropriate  folder  from  the  respository.json  file.  If  the  client  passes  a  folder  id  of  

/

”,  it  represents  the  root,  so  the  open  folder  action  obtains  the  root  item  in  the  tree   from  the  RepositoryConnection  class.  Finally  the  utility  method  you  created  earlier  

  is  used  to  create  the  JSON  response  for  the  client.  

One  more  action  is  required  to  enable  the  folder  browsing  capability.  Right-­‐click  on   the  “

com.ibm.ecm.extension.repositoryType

”  package  again  and  create  a  class   called  “

GetContentItems

”  and  add  the  following  source  code  to  the  new  file:  

  package com.ibm.ecm.extension.repositoryType; import java.util.ArrayList; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.ibm.ecm.configuration.RepositoryConfig; import com.ibm.ecm.extension.PluginServiceCallbacks; import com.ibm.ecm.json.JSONResultSetResponse; public class GetContentItems { public void performAction(PluginServiceCallbacks callbacks,

RepositoryConfig repositoryConfig, HttpServletRequest request,

HttpServletResponse response) throws Exception { yId);

String repositoryId = request.getParameter("repositoryId");

RepositoryConnection connection =

(RepositoryConnection)callbacks.getPluginRepositoryConnection(repositor

String docid = request.getParameter("docid"); // id of first document

JSONResultSetResponse jsonResponse = new

JSONResultSetResponse();

ArrayList<ContentItem> contentItemList = new

ArrayList<ContentItem>(); if (docid.equals("/")) { contentItemList.add(connection.getRootItem()); jsonResponse =

RepositoryUtils.

buildResultSetResponse (callbacks, connection, contentItemList);

} else {

String[] docids = request.getParameterValues("docid"); for (int i = 0; i < docids.length; i++) {

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   2

9  

} contentItemList.add(connection.getContentItem(docids[i]));

} jsonResponse =

RepositoryUtils.

buildResultSetResponse (callbacks, connection, contentItemList);

}

} callbacks.writeJSONResponse(jsonResponse, response);

 

This  action  simply  retrieves  the  content  items  associated  with  the  document  id   passed  from  the  client.  The  last  change  you  need  to  make  is  to  enable  the  

OpenFolder

”  and  “

GetContentItems

”  actions  in  the  main  repository  type  Java  

  class.  Open  the  “

IBMDevCoursePlugin.java

”  file  and  make  the  following  change:   public class IBMDevCourseRepository extends PluginRepositoryType { private static final Map<String, String>

IBMDevCourseRepositoryActionMap = new HashMap<String, String>() { private static final long serialVersionUID =

7212659949366854547L;

{ put("logon",

"com.ibm.ecm.extension.repositoryType.RepositoryLogon"); put("logoff",

"com.ibm.ecm.extension.repositoryType.RepositoryLogoff"); put("openFolder",

"com.ibm.ecm.extension.repositoryType.OpenFolder"); put("getContentItems",

"com.ibm.ecm.extension.repositoryType.GetContentItems");

}

}; public void performAction(PluginServiceCallbacks callbacks,

RepositoryConfig repositoryConfig, String action, HttpServletRequest request, HttpServletResponse response) throws Exception { if ( IBMDevCourseRepositoryActionMap .containsKey(action)) { callbacks.getLogger().logDebug( this , "performAction",

"Executing \"" + action + "\"...");

Object obj =

Class.

forName ( IBMDevCourseRepositoryActionMap .get(action)).newInstance(

);

Method m = obj.getClass().getMethod("performAction",

PluginServiceCallbacks.

class , RepositoryConfig.

class ,

HttpServletRequest.

class , HttpServletResponse.

class ); response); m.invoke(obj, callbacks, repositoryConfig, request,

} else { callbacks.getLogger().logError( this , "performAction",

"${PluginClassName} does not support the repository action \"" + action

+ "\".");

// return JSON with an error as well for the client

3

0    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

JSONResponse jsonResponse = new JSONResponse(); jsonResponse.addErrorMessage( new JSONMessage(20005,

"${PluginClassName} does not support the action", "The repository action \""+action+"\" is not supported.", null , null , null )); response.getWriter().write(jsonResponse.serialize());

}

}

@Override public String getId() {

} return

@Override

"IBMDevCourseRepository"; public String getName(Locale locale) {

} return "IBM Development Course Repository";

;

@Override public String getConfigurationDijitClass() { return

"iBMDevCoursePluginDojo.IBMDevCourseRepositoryGeneralConfigurationPane"

} public String getConnectedConfigurationDijitClass() { return

"iBMDevCoursePluginDojo.IBMDevCourseRepositoryConfigurationParametersPa

ne";

}

@Override public PluginRepositoryConnection logon(PluginServiceCallbacks callbacks, HttpServletRequest request, String userid, String password,

RepositoryConfig repositoryConfig) throws Exception {

PluginRepositoryConnection connection =

RepositoryLogon.

class .newInstance().logon(callbacks, request, userid, password, repositoryConfig);

} return connection;

@Override public void logoff(PluginServiceCallbacks callbacks, HttpSession

} session, PluginRepositoryConnection connection) throws Exception {

RepositoryLogoff.

class .newInstance().logoff(callbacks, session, connection);

}

@Override public String getRepositoryModelClass() { return "";

} public

}

String getIconClass() { return "";

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   3

1  

 

With  the  above  changes  you  should  now  be  able  to  log  on  to  your  repository  type  

  from  the  browse  feature  and  populate  the  tree  and  list  view  with  contents  of  folders.  

Figure  1.12  

1.4  Enabling  the  repository  configuration  panels  

 

Although  you  do  not  need  any  additional  configuration  data  for  this  repository  type,   it  is  a  useful  exercise  to  create  some  dummy  values  for  the  repository  configuration   to  help  you  understand  how  you  would  go  about  prompting  the  administrator  for   custom  properties  in  a  real  repository  type.  Open  

IBMDevCourseRepositoryGeneralConfigurationPane.html

”  located  under  your  

com.ibm.ecm.extension.iBMDevCoursePluginDojo.templates

”  package  and  

  make  the  following  changes:  

<div>

<table class= "propertyTable" role= "presentation" >

<tr>

<td class=

Name:</label>&nbsp;

"propertyRowLabel" >

<span class= "required" >*</span>

<label for= "${id}_serverName" >Server

</td>

<td class= "propertyRowValue" >

<div id= "${id}_serverName" data-dojo-attach-point= data-dojo-attach-event=

"serverNameField"

"onKeyUp:

_onParamChange" data-dojotype= "ecm/widget/ValidationTextBox" data-dojo-props= " required: true, trim: true, propercase: false, maxLength: 64 " ></div>

</table>

</div>

</tr>

</td>

This  adds  a  required  field  to  the  widget  template,  prompting  the  administrator  for  a  

“server  name”  parameter.  This  is  a  parameter  provided  by  default  in  the  standard   repository  configuration  object  in  Content  Navigator  and  a  typical  field  required  for  

 

3

2    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

  repositories.  Open  “

IBMDevCourseRepositoryGeneralConfigurationPane.js

”  and   make  the  following  changes:  

  define([

"dojo/_base/declare",

"dijit/_TemplatedMixin",

"dijit/_WidgetsInTemplateMixin",

"ecm/widget/ValidationTextBox",

"ecm/widget/admin/PluginRepositoryGeneralConfigurationPane",

"dojo/text!./templates/IBMDevCourseRepositoryGeneralConfiguration

Pane.html"

], function (declare, _TemplatedMixin, _WidgetsInTemplateMixin,

ValidationTextBox, PluginRepositoryGeneralConfigurationPane, template)

{

/**

* @name iBMDevCoursePluginDojo.IBMDevCourseRepositoryGeneralConfigurationPane

* @class Provides a configuration panel for general repository configuration for the repository type. This panel appears

* on the general tab of the repository configuration page in administration when creating or editing a repository

* of the defined repository type.

* @augments ecm.widget.admin.PluginRepositoryGeneralConfigurationPane

*/ return declare("iBMDevCoursePluginDojo.IBMDevCourseRepositoryGeneralConfigurat

ionPane", [ PluginRepositoryGeneralConfigurationPane, _TemplatedMixin,

_WidgetsInTemplateMixin ], {

/** @lends iBMDevCoursePluginDojo.IBMDevCourseRepositoryGeneralConfigurationPane.p

rototype */ templateString: template, widgetsInTemplate: true , load: function (repositoryConfig) { if (repositoryConfig) { this.serverNameField.set('value', repositoryConfig.getServerName());

}

},

_onParamChange: function() {

}, this.onSaveNeeded(true); validate: function () {

}, if(!this.serverNameField.isValid()) {

} return false; return true ;

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   3

3  

; save: function (repositoryConfig) { if (repositoryConfig) { repositoryConfig.setServerName(this.serverNameField.get("value")) params.serverName = this.serverNameField.get("value");

}

});

});

} ,

} getLogonParams: function(params) {

 

These  changes  will  enable  the  server  name  prompt,  saving  the  value  in  the  default  

serverName

”  property  of  the  Content  Navigator  repository  configuration.  The  

getLogonParams

”  method  is  called  by  the  container  to  obtain  additional   information  that  should  be  passed  to  the  log  in  action  call  to  the  repository  type.  

Open  the  repository  configuration  for  your  repository  type  and  there  should  be  a  

  new  required  parameter  on  the  general  configuration  tab.  

Figure  1.13  

 

It  is  also  possible  to  add  custom  properties  that  are  not  available  in  the  standard  

Content  Navigator  repository  configuration.  To  illustrate  that  you  will  add  some   custom  configuration  parameters  to  the  configuration  tab  that  is  enabled  after  you   log  in  to  the  repository  type  in  the  repository  configuration.  Open  

“IBMDevCourseRepositoryConfigurationParametersPane.html”  and  add  the   following:  

 

3

4    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

 

 

<div>

<tr>

1:</label>&nbsp;

<table class= "propertyTable" role= "presentation" >

_onParamChange"

<td class=

</td>

"propertyRowLabel"

<span class=

<label for=

<div id=

"required"

"${id}_param1" data-dojotype= "ecm/widget/ValidationTextBox"

>

>*</span>

"${id}_param1"

<td class= "propertyRowValue" >

>Config Param data-dojo-attach-point= data-dojo-attach-event=

"param1Field"

"onKeyUp: data-dojo-props= " required: true, trim: true, propercase: false, maxLength: 64 " ></div>

</tr>

</td>

<tr>

<td class= "propertyRowLabel"

<label for=

>

"${id}_param2" >Config Param

2:</label>&nbsp;

_onParamChange"

</td>

<td class= "propertyRowValue" >

<div id= "${id}_param2" data-dojotype= "ecm/widget/ValidationTextBox" data-dojo-attach-point= data-dojo-attach-event=

"param2Field"

"onKeyUp: data-dojo-props= " required: true, trim: true, propercase: false, maxLength: 64 " ></div>

</tr>

</td>

</table>

</div>

This  change  will  enable  two  new  prompts  for  the  administrator,  for  dummy   parameters  you  will  store  in  custom  configuration  parameters  on  your  repository   configuration.  Open  “IBMDevCourseRepositoryConfigurationParametersPane.js”   and  make  the  following  changes:  

  define([

"dojo/_base/declare",

"dojo/_base/json",

"dijit/_TemplatedMixin",

"dijit/_WidgetsInTemplateMixin",

"ecm/widget/ValidationTextBox",

"ecm/widget/admin/PluginRepositoryConfigurationParametersPane",

"dojo/text!./templates/IBMDevCourseRepositoryConfigurationParamet ersPane.html"

], function (declare, dojojson, _TemplatedMixin,

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   3

5  

_WidgetsInTemplateMixin, ValidationTextBox,

PluginRepositoryConfigurationParametersPane, template) {

/**

* @name iBMDevCoursePluginDojo.IBMDevCourseRepository

* @class Provides a configuration panel for general repository configuration for the repository type. This panel appears

* on the general tab of the repository configuration page in administration when creating or editing a repository

*

* @augments

of the defined repository type. ecm.widget.admin.PluginRepositoryGeneralConfigurationPane

*/ return declare("iBMDevCoursePluginDojo.IBMDevCourseRepositoryConfigurationPara

metersPane", [ PluginRepositoryConfigurationParametersPane,

_TemplatedMixin, _WidgetsInTemplateMixin], {

/** @lends iBMDevCoursePluginDojo.IBMDevCourseRepositoryConfigurationParametersPan

e.prototype */ templateString: template, widgetsInTemplate: true , load: function (repositoryConfig) { var customProperties = repositoryConfig.getCustomProperties(); if (customProperties) { var jsonConfig = dojojson.fromJson(customProperties); this.param1Field.set('value',jsonConfig.configuration[0].value); this.param2Field.set('value',jsonConfig.configuration[1].value);

}

},

_onParamChange: function() {

}, this.onSaveNeeded(true); validate: function () {

}, if(!this.param1Field.isValid()) return false; return true; save: function (repositoryConfig) { var configArray = []; var configString = { name: "param1Field", value: this.param1Field.get('value')

}; configArray.push(configString); configString = {

3

6    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

;

}); name: "param2Field", value: this.param2Field.get('value')

}; configArray.push(configString); var configJson = {

"configuration" : configArray

}; repositoryConfig.setCustomProperties(dojojson.toJson(configJson))

});

}

 

These  changes  load  and  save  the  custom  configuration  parameters  in  a  JSON  string  

  on  the  repository  configuration.  After  these  changes  you  should  now  find  the  new   prompts  on  the  repository  configuration  tab  after  you  log  in  to  your  repository  type.  

 

Figure  1.14  

1.5  Optional  Exercise  

 

Add  new  actions  to  handle  the  other  aspects  of  the  browse  pane.  These  actions   include  “openItem”,  “openContentClass”,  “getContentClasses”,  “getContentItems”,  

“editAttributes”  and  “getDocument”.    

 

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   3

7  

 

 

2.  Creating  a  Plug-­‐in  API  

 

In  addition  to  creating  extensions  for  features  contained  within  your  plug-­‐in  you   may  find  the  need  to  expose  server-­‐side  APIs  that  other  plug-­‐ins  can  leverage.  A   good  example,  is  the  External  Data  Services  (EDS)  plug-­‐in  provided  by  Content  

Navigator.  Although  this  plug-­‐in  is  primarily  used  to  augment  the  standard  services   available  within  Content  Navigator,  it  contains  an  API  that  is  used  to  handle  a  call  to   an  external  service,  which  is  configured  as  part  of  the  plug-­‐in  configuration  during   deployment.  Other  plug-­‐ins  may  want  to  leverage  this  same  external  service,  so  it  is   beneficial  for  the  EDS  plug-­‐in  to  provide  a  mechanism  for  other  plug-­‐ins  to  leverage   this  configured  external  source.  For  this  capability,  Content  Navigator’s  plug-­‐in   framework  provides  the  option  to  create  a  Plug-­‐in  API.  A  Plug-­‐in  API  is  server-­‐side   extension  that  exposes  a  method  call  for  another  plug-­‐in.  In  this  exercise,  you  will   create  a  plug-­‐in  API  that  allows  another  plug-­‐in  to  leverage  the  configuration  stored   by  your  existing  plug-­‐in.  You  will  then  create  a  new  plug-­‐in  to  read  these  values  and   display  them  in  the  plug-­‐in  configuration  of  the  new  plug-­‐in.  

2.1  Creating  a  Plug-­‐in  API  

 

Right-­‐click  on  the  “

com.ibm.ecm.extension

”  package  in  your  plug-­‐in  project  and  

  use  the  Content  Navigator  menu  to  create  a  new  “

API

”.  

 

Figure  2.1  

3

8    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

 

The  Plug-­‐in  API  Java  class,  like  other  server  extensions,  is  a  simple  class  structure.  It   includes  only  an  identifier  and  an  execute  method.  Make  the  following  changes  to  

  the  execute  method  of  the  Plug-­‐in  API  Java  class:   package com.ibm.ecm.extension; import javax.servlet.http.HttpServletRequest; import com.ibm.json.java.JSONObject;

/**

* Provides an abstract class that is extended to create a class implementing an API provided by the plug-in. A plug-in

* API can be invoked from other plug-ins, allowing one plug-in to provide services that another plug-in service can

* use. A plug-in API is similar to a plug-in service, receiving an instance of PluginServiceCallbacks and the original

* HttpServletRequest object, allowing access to any session state and underlying APIs. </p> Follow best practices for

* servlets when implementing a plug-in API. In particular, always assume multi-threaded use and do not keep unshared

* information in instance variables.

*/ public class DevCourseConfigAPI extends PluginAPI { public

}

String getId() { return "DevCourseConfigAPI";

/**

* Performs the action of this API.

*

* @param callbacks

* An instance of the

<code>PluginServiceCallbacks</code> class that contains several functions that can

* be used by the plug-in API. These functions provide access to the plug-in configuration and content

* server APIs.

* @param request

* The <code>HttpServletRequest</code> from the current request being processed. The service can access

* the invocation parameters from the request as well as session state.

* @param arguments

* An object array containing the input arguments to the API. The particular structure of these object is

* defined by the plug-in API writer. The classes of the objects used as arguments should be J2SE classes

* or instances of com.ibm.ecm.json as any plug-in specific classes will be loaded by different

* classloaders causing class cast exceptions when attempting to pass instances from one plug-in to

* another.

* @returns An object containing the response the API. This object can be of any structure and is defined by the

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   3

9  

}

* plug-in API writer. The class of the object should be a J2SE class or instance of com.ibm.ecm.json as

* any plug-in specific classes will be loaded by different classloaders causing class cast exceptions when

* attempting to use the object by the invoker.

* @throws Exception

* For exceptions that occur when the API is running.

If the logging level is high enough to log errors,

* information about the exception is logged by IBM

Content Navigator.

*/ public Object execute(PluginServiceCallbacks callbacks,

HttpServletRequest request, Object[] arguments) throws Exception {

JSONObject jsonObj = new JSONObject();

String[] configData = callbacks.loadConfigurations(new

String[] {"1"}); if (configData != null) { jsonObj = JSONObject.

parse (configData[0]);

} return jsonObj;

}

The  plug-­‐in  API  is  going  to  do  nothing  more  than  retrieve  the  custom  configuration   from  our  plug-­‐in  and  return  it  as  a  JSONObject.    

2.2  Using  a  Plug-­‐in  API  

 

In  order  to  use  the  Plug-­‐in  API,  you  need  to  create  a  new  Plug-­‐in  to  leverage  the  API.  

 

In  eclipse,  create  a  new  “IBM  Content  Navigator”  Plug-­‐in  project  called  

IBMDevCoursePlugin2

”  and  use  descriptive  name  “

IBM  Development  Course  

Plugin  2

”,  the  package  name  “

com.ibm.ecm.extension.devcourse

”  and  the  class   name  “

IBMDevCoursePlugin2

”.  

4

0    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

 

Figure  2.2  

Since  this  plug-­‐in  will  leverage  a  Plug-­‐in  API  provided  by  our  first  plug-­‐in,  it  must   register  the  IBMDevCoursePlugin  as  a  dependency.  Make  the  following  changes  to  

  your  newly  created  plug-­‐ins  main  plug-­‐in  Java  class,  “

IBMDevCoursePlugin2.java

”:   public class IBMDevCoursePlugin2 extends Plugin { private PluginAction[] pluginActions = new PluginAction[0];

  private PluginOpenAction[] pluginOpenActions = new

PluginOpenAction[0]; private PluginRequestFilter[] pluginRequestFilters = new

PluginRequestFilter[0]; private PluginResponseFilter[] pluginResponseFilters = new

PluginResponseFilter[0]; null ; private PluginService[] pluginServices = new PluginService[0]; private PluginODAuthenticationService odAuthenticationService = private PluginViewerDef[] pluginViewerDefs = new

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   4

1  

PluginViewerDef[0]; private PluginLayout[] pluginLayouts = new PluginLayout[0]; private PluginFeature[] pluginFeatures = new PluginFeature[0]; private PluginMenuType[] pluginMenuTypes = new PluginMenuType[0]; private PluginMenu[] pluginMenus = new PluginMenu[0]; private PluginRepositoryType[] pluginRepositoryTypes = new

PluginRepositoryType[0]; private PluginAPI[] pluginAPIs = new PluginAPI[0]; private String[] pluginDependencies = new String[0];

/**

* Initializes this plug-in in the web client. A plug-in can perform

* initialization that would be shared for all users of the application in

* the method. This method differs from the constructor because this method

* is called only when the plug-in is used within the web application. This

* method is invoked when the application is initializing. This is an optional

* method and may be removed from the plug-in if no applicationInit is required.

*/ public void applicationInit(HttpServletRequest request,

PluginServiceCallbacks callbacks) throws Exception {

}

/**

* Provides an identifier for the plug-in.

* <p>

* <strong>Important:</strong> This identifier is used in path names and

* URLs so it must contain only alphanumeric characters.

* </p>

*/ public String getId() { return "IBMDevCoursePlugin2";

}

/**

* Provides a descriptive name for this plug-in. The name identifies the

* plug-in in the IBM Content Navigator administration tool

*

* @parm locale The locale currently in use. The name should be returned in

* the language for this locale if it is translated.

*/ public String getName(Locale locale) {

} return "IBM Development Course Plugin 2";

/**

* Provides a version indicator for this plug-in. The version information is

* displayed in the administration tool to help the administrator

4

2    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

  validate

* the version of the plug-in that is being used. The plug-in writer needs

* to update this indicator when redistributing a modified version of the

* plug-in.

*

* @return A <code>String</code> representation of the version indicator for

* the plug-in.

*/ public

}

String getVersion() { return "2.0.3";

/**

* Provides the copyright license for this plug-in. The information is

* displayed in the About dialog Plugins tab. The license is provided by the

* plugin creator. This is an optional method and may be removed from the plug-in

* if no copyright is specified.

*

* @return A <code>String</code> representation of the license for the

* plug-in.

* @since 2.0.2

*/ public String getCopyright() {

} return "Optionally add a Copyright statement here";

/**

* Returns the name of a JavaScript file provided by this plugin. This file

* is downloaded to the web browser during the initialization of the web

* client, before login. A script can be used to perform customization that

* cannot be performed by other extension mechanisms, such as features or

* layouts. However, it is preferable to use these other extension

* mechanisms to provide more flexibility to the administrator when

* configuring desktops.

*/ public

}

/**

String getScript() { return "IBMDevCoursePlugin2.js";

* Returns the name of a debug version of the JavaScript file provided by

* getScript(). The default implementation invokes getScript().

*

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   4

3  

*

*/ public

}

@since 2.0.2

String getDebugScript() { return getScript(); in the

/**

* Returns the name of a Dojo module or widget that is contained

* resources for this plug-in. IBM Content Navigator performs the necessary

* <code>dojo.registerModulePath</code> mapping to allow modules or widgets

* with mapped path names to be loaded by using the

* <code>dojo.require</code> method. A specified module can be the directory

* or package name for a set of Dojo modules or widgets.

*/ public

}

String getDojoModule() { return "iBMDevCoursePlugin2Dojo";

/**

* Returns the name of a CSS file that contains styles for this plug-in. IBM

* Content Navigator generates the necessary style tag to pull in this file

* when IBM Content Navigator loads the plug-in.

*/ public String getCSSFileName() {

} return "IBMDevCoursePlugin2.css"; getCSSFileName. The

/**

* Returns a debug version of the CSS file returned by

* default implementation invokes getCSSFileName.

*

* @since 2.0.2

* @return

*/ public String getDebugCSSFileName() { return getCSSFileName();

}

/**

* Returns the name of a Dojo <code>dijit</code> class that provides a

* configuration interface widget for this plug-in. The widget must extend

* the <code>ecm.widget.admin.PluginConfigurationPane</code> widget. An

* instance of the widget is created and displayed in the IBM

Content

* Navigator administration tool for configuration that is specific to the

* plug-in.

4

4    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

* <p>

* Refer to the documentation on

* {@link ecm.widget.admin.PluginConfigurationPane

PluginConfigurationPane}

* for more information on what is required for a plug-in configuration user

* interface.

* </p>

*/ public String getConfigurationDijitClass() {

} return "iBMDevCoursePlugin2Dojo.ConfigurationPane";

/**

* Provides a list of actions that this plug-in adds to the main toolbar of

* the web client.

*

* @return An array of

* <code>{@link com.ibm.ecm.extension.PluginAction

PluginAction}</code>

* objects. The plug-in should return the same set of objects on

* every call.

*/ public

}

PluginAction[] getActions() { return pluginActions;

/** supported

* Provides a list of open actions that this plug-in provides for

* items.

*

* @since 2.0.2

* @return An array of

* <code>{@link com.ibm.ecm.extension.PluginOpenAction

PluginOpenAction}</code>

* objects. The plug-in should return the same set of objects on

* every call.

*/ public PluginOpenAction[] getOpenActions() {

}

/** return pluginOpenActions;

* Provides a list of filters that are run before a requested service. This

* method can be used to modify the request or block the request.

*

* @return An array of

* <code>{@link com.ibm.ecm.extension.PluginRequestFilter

PluginRequestFilter}</code>

* objects.

*/ public PluginRequestFilter[] getRequestFilters() {

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   4

5  

} return pluginRequestFilters;

/**

* Provides a list of filters that are run after a requested service. This

* list of filters can be used to modify the response that is returned.

*

* @return An array of

* <code>{@link com.ibm.ecm.extension.PluginResponseFilter PluginResponseFilter}</code>

* objects.

*/ public

}

PluginResponseFilter[] getResponseFilters() { return pluginResponseFilters;

/**

* Provides a list of services that are provided by this plug-in.

The

* services run on the web server, and can be called by the web browser

* logic component of the plug-in.

*

* @return An array of {@link com.ibm.ecm.extension.PluginService

* PluginService} objects. The plug-in should return the same set of

* objects on every call. If there are no services defined by the

* plug-in, the call should return an empty array.

*/ public PluginService[] getServices() {

}

/** return pluginServices;

* Provides a custom service used for Content Manager OnDemand single

* sign-on (SSO). This is an optional service that will be called when SSO

* is enabled on a Content Manager OnDemand server. The result of the

* service will be the information passed through the Content

Manager

* OnDemand Web Enablement Kit "passThru" API.

*

* @since 2.0.2

{

* @return A {@link com.ibm.ecm.extension.PluginODAuthenticationService

* PluginODAuthenticationService} object used as an authentication

* exit for Content Manager OnDemand single sign-on.

*/ public PluginODAuthenticationService getODAuthenticationService() return odAuthenticationService;

4

6    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

}

/**

* Provides a list of viewers that are provided by this plug-in.

The viewers

* become available in the viewer configuration area of the IBM

Content

* Navigator administration tool. The viewers can be mapped to be used to

* view certain document types.

* <p>

* <strong>Note:</strong> Typically, a plug-in does not define multiple and an

* viewers. However, this method can be used to provide multiple

* configurations of the same viewer, such as a view-only version

* editing mode version of the same viewer.

* </p>

*

* @return An array of {@link ecm.widget.admin.PluginViewerDef

* PluginViewerDef} objects describing the viewers provided by the

* plug-in.

*/ public

}

PluginViewerDef[] getViewers() { return pluginViewerDefs;

/**

* Specifies one or more custom layouts that are provided by this plug-in.

* Custom layouts can display the features and other user interface

* components of IBM Content Navigator in a different arrangement. The IBM

* Content Navigator administration tool is used to select the layout to use

* for a desktop.

*

* @return

*/ public

An array of plug-in layout objects.

PluginLayout[] getLayouts() { return pluginLayouts;

}

/**

* Specifies custom features that are provided by this plug-in.

Features are the major user interface sections,

* which appear as icons on the left side of the user interface in the default layout. Examples of features include

* Search, Favorites, and Teamspaces.

*

* @return An array of custom plug-in feature objects.

*/ public PluginFeature[] getFeatures() {

} return pluginFeatures;

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   4

7  

/**

* Provides a list of new menu types defined by the plug-in.

*

*

}

@return

*/

An array of new menu type objects. public PluginMenuType[] getMenuTypes() { return pluginMenuTypes;

/**

* Provides a list of menus defined by the plug-in.

*

* @return An array of plug-in menu objects.

*/ public PluginMenu[] getMenus() { return pluginMenus;

}

/**

* Provides a list of one or more custom repositories that are provided by this plug-in.

*

* @return An array of plug-in repository types.

*/ public PluginRepositoryType[] getRepositoryTypes() {

} return pluginRepositoryTypes;

/**

* Provides a list of one or more custom APIs that are provided by this plug-in.

*

*

*/ public

}

@return An array of plug-in APIs.

PluginAPI[] getPluginAPIs() { return pluginAPIs;

/**

* Provides a list of identifiers (the plug-in Ids) of all the plug-ins that this plug-in depends on.

*

* @return An array of plug-in identifier strings.

* @since 2.0.3

*/ public String[] getPluginDependencies() { if (pluginDependencies.length == 0) { pluginDependencies = new String[] {

"IBMDevCoursePlugin" };

}

} return pluginDependencies;

 

}

4

8    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

Now  that  you  have  registered  the  dependency  the  next  step  is  to  create  a  

PluginService  that  will  leverage  the  Plugin  API  from  the  first  plug-­‐in.  Right-­‐click  on   the  “

com.ibm.ecm.extension.devcourse

”  package  in  your  new  plug-­‐in  and  create  a   new  Service  called  “

ConfigService

”.  

 

 

Figure  2.3  

Make  the  following  changes  to  the  newly  created  ConfigService  class:  

  it must public class ConfigService extends PluginService {

/**

* Returns the unique identifier for this service.

* <p>

* <strong>Important:</strong> This identifier is used in URLs so

* contain only alphanumeric characters.

* </p>

*

* @return A <code>String</code> that is used to identify the service.

*/ public String getId() {

} return "ConfigService";

/**

* Returns the name of the IBM Content Navigator service that this service

* overrides. If this service does not override an IBM Content

Navigator

* service, this method returns <code>null</code>.

*

 

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   4

9  

*

*/ public

}

@returns The name of the service.

String getOverriddenService() { return null ;

/**

* Performs the action of this service.

*

* @param callbacks

* An instance of the

<code>PluginServiceCallbacks</code> class

* that contains several functions that can be used by the

* service. These functions provide access to the plug-in

* configuration and content server APIs.

* @param request

* The <code>HttpServletRequest</code> object that provides the

* request. The service can access the invocation parameters from

* the request.

* @param response

* The <code>HttpServletResponse</code> object that is generated

* by the service. The service can get the output stream and

* write the response. The response must be in JSON format.

* @throws Exception

* For exceptions that occur when the service is running. If the

* logging level is high enough to log errors, information about

* the exception is logged by IBM Content Navigator.

*/ public void execute(PluginServiceCallbacks callbacks,

HttpServletRequest request, HttpServletResponse response) throws

Exception {

PluginLogger logger = callbacks.getLogger(); logger.logEntry(this, "execute");

JSONResponse jsonResponse = new JSONResponse();

Object obj = callbacks.executePluginAPI("IBMDevCoursePlugin", "DevCourseConfigAPI", null); try { if (obj != null) { jsonResponse.put("data", obj);

}

} catch (Exception e) { logger.logError(this, "execute", e); jsonResponse.addErrorMessage(new JSONMessage(20002,

"Error occurred attempting to process a request to the ConfigService in plug-in IBMDevCoursePlugin2", null, null, null, null));

} finally {

5

0    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

}

PluginResponseUtil.

writeJSONResponse (request, response, jsonResponse, callbacks, "ConfigurationService");

} logger.logExit(this, "execute");

// Send response to the client

}

The  following  changes  leverage  the  PluginServiceCallbacks  method  

“executePluginAPI”  to  call  the  Plugin  API  provided  by  your  first  plugin.  The  method   takes  the  plug-­‐in  ID  and  the  ID  of  its  Plugin  API  as  parameters.  The  third  parameter   is  an  array  of  objects,  which  allows  you  to  pass  arguments  to  the  Plugin  API.  In  this   case,  you’re  API  does  not  require  any  arguments,  so  it  is  ok  to  pass  null  for  the  third   parameter.  

 

The  last  step  is  to  update  the  configuration  widget  for  this  new  plugin,  to  enable  it  to   display  the  configuration  parameters  from  the  first  plugin.  Open  the  

ConfigurationPane.html

”  file  located  under  the  

com.ibm.ecm.extension.devcourse.WebContent.iBMDevCoursePlugin2Dojo.te

mplates

”  package  and  make  the  following  changes:  

 

<div>

<!-- Please add your configuration pane -->

<!-- For example

<table class="propertyTable" role="presentation">

<tr>

<td class="propertyRowLabel">

<span class="required">*</span>

<label for="${id}_ecServerURI">Server:</label>&nbsp;

<div data-dojo-type="ecm/widget/HoverHelp" data-dojoattach-point="ecServerURIHoverHelp" message="Specify a server address."></div>

</td>

<td class="propertyRowValue">

<div id="${id}_ecServerURI" data-dojo-attachpoint="ecServerURI" data-dojo-attach-event="onKeyUp: _onFieldChange" data-dojo-type="ecm/widget/ValidationTextBox" required="true" trim="true" propercase="false"></div>

</td>

</tr>

</table>

-->

<h1>Parameters from IBM Development Course Plug-in</h1>

<table class= "propertyTable" role= "presentation" >

<tr>

<td class= "propertyRowLabel" >

<label>Parameter 1:</label>&nbsp;

</td>

<td class= "propertyRowValue" data-dojo-attachpoint= "param1Value" >

</td>

</tr>

<tr>

<td class= "propertyRowLabel" >

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   5

1  

<label>Parameter 2:</label>&nbsp;

</td>

<td class= "propertyRowValue" data-dojo-attachpoint= "param2Value" >

</td>

</tr>

</table>

</div>

 

 

Now,  update  the  “

ConfigurationPane.js

”  file  under  

 

com.ibm.ecm.extension.devcourse.WebContent.iBMDevCoursePlugin2Dojo

”:  

}); define([

],

"dojo/_base/declare",

"dojo/_base/lang",

"dijit/_TemplatedMixin",

"dijit/_WidgetsInTemplateMixin",

"ecm/model/Request",

"ecm/widget/admin/PluginConfigurationPane",

"dojo/text!./templates/ConfigurationPane.html" function (declare, lang, _TemplatedMixin, _WidgetsInTemplateMixin,

Request, PluginConfigurationPane, template) { return declare("IBMDevCoursePlugin2Dojo.ConfigurationPane",

[ PluginConfigurationPane, _TemplatedMixin, _WidgetsInTemplateMixin], { templateString: template, widgetsInTemplate: load:

"ConfigService", function true ,

(callback) {

Request.invokePluginService("IBMDevCoursePlugin2", function(response) { response.data.param1) {

{ requestCompleteCallback: lang.hitch(this,

// success if (response.data && this.param1Value.innerHTML = response.data.param1; response.data.param2) {

} if (response.data && this.param2Value.innerHTML = response.data.param2;

},

);

}); validate:

}

} function return true

})

() {

;

}

5

2    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

 

This  change  will  cause  the  configuration  to  call  your  newly  created  service   immediately  when  it  loads  and  display  the  configuration  parameters  saved  by  the   other  plug-­‐in.  

Figure  2.4    

 

 

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   5

3  

 

3.  Open  Actions  

 

There  may  be  cases  where  you  may  need  to  override  the  default  behavior  of  the   open  action,  avoiding  the  standard  path  associated  with  opening  a  particular   document  type.  For  example,  Content  Navigator  stores  search  templates  as  JSON   files  in  the  repository.  However,  when  the  user  clicks  on  a  search  template  in  a  list,   they  do  not  want  to  open  the  JSON  file  in  the  viewer.  Instead,  they  expect  the  search   itself  to  open  on  the  search  feature  in  ICN.  You  may  encounter  similar  cases,  where   you  want  to  add  custom  logic  when  the  user  opens  a  particular  type.  

 

3.1  Creating  an  open  action  

 

For  this  exercise  you  will  add  a  JSON  file  containing  search  criteria  to  the  Invoice   class  and  create  a  custom  open  action  to  run  a  search  based  on  the  defined  criteria.  

The  development  course  materials  “

resources

”  directory  contains  a  file  name  

openActionSearch.json

”,  which  you  will  use  for  this  exercise.  Create  a  new  

Invoice

”  document  using  the  “

openActionSearch.json

”  file  as  the  content  for  the   document.  You  can  use  Content  Navigator  to  create  the  file  by  clicking  on  the  “

Add  

Document

”  action  on  the  “

browse  feature

”  in  the  Content  Navigator  application.  

 

Figure  3.1  

5

4    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

After  you  create  the  new  file  you  are  ready  to  enable  your  open  action.  Right-­‐click   on  the  “

com.ibm.ecm.extension

”  package  in  your  eclipse  project  and  use  the  

Content  Navigator

”  menu  to  access  the  “

Open  Action

”  wizard.  In  the  wizard,   specify  the  name  “

DevCourseOpenAction

”,  adding  the  “

application/json

”  content  

  type  and  indicate  the  open  action  only  supports  the  “

IBM  FileNet  P8

”  repository   type:  

 

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   5

5  

 

Figure  3.2  

The  wizard  creates  a  new  file,  “

DevCourseOpenAction.java

”  and  adds  a  new  

JavaScript  method  for  your  open  action  to  the  “

IBMDevCoursePlugin.js

”  file.  As   with  other  plug-­‐in  types  you  have  created  in  previous  exercises,  the  

5

6    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

 

“DevCourseOpenAction.java”  file  defines  your  open  action  for  the  Content  Navigator   plug-­‐in  framework.  It  includes  both  the  typical  methods  to  return  the  identifier  and   name  of  your  open  action.  Additionally  it  contains  the  methods  

getOpenActionFunction

”,  “

getContentTypes

”  and  “

getServerTypes

”.    The  

“getOpenActionFunction”  returns  the  name  of  the  JavaScript  function  to  run  when   the  Content  Navigator  framework  invokes  the  open  action.  The  “getContentTypes”   method  returns  a  String  array  containing  the  mime  types  supported  by  this  open   action.  In  this  case,  you  specified  the  action  only  supports  the  “

application/json

”   mime  type.  Finally,  the  “getServerTypes”  method  returns  a  String  array  containing   the  list  of  repository  types  supported  by  the  open  action.  Since  you  specified  the   open  action  applies  only  to  the  FileNet  P8  repository  type,  the  array  contains  only   one  entry,  “p8”.  

 

Open  the  “IBMDevCoursePlugin.js”  file  and  add  the  following  to  the  newly  created  

 

devCourseOpenAction

”  function:   require(["dojo/_base/declare",

"dojo/_base/lang",

"ecm/model/Request",

"iBMDevCoursePluginDojo/Decorators",

"iBMDevCoursePluginDojo/ResultSet",

"iBMDevCoursePluginDojo/CurrencyPropertyFormatter",

"iBMDevCoursePluginDojo/DevCourseFeature",

"iBMDevCoursePluginDojo/FeatureConfigurationPane",

"iBMDevCoursePluginDojo/ConfigurationPane",

"iBMDevCoursePluginDojo/VideoViewer"], function (declare, lang, Request, Decorators, ResultSet, messages) {

/**

* Use this function to add any global JavaScript methods your plug-in requires.

*/ lang.setObject("customListViewAction", function (repository, items, callback, teamspace, resultSet, parameterMap) {

ResultSet(response);

Request.invokePluginService("IBMDevCoursePlugin",

"ListViewService",

{ requestCompleteCallback: function (response) { if (parameterMap.widget && parameterMap.widget.isInstanceOf(ecm.widget.listView.ContentList)) { response.repository = repository; var resultSet = new parameterMap.widget.setResultSet(resultSet);

}

}

}

);

}); lang.setObject("devCourseOpenAction", function (repository, contentItems) {

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   5

7  

 

/*

* Add custom code for your open action here. For example, your action might launch a custom document view in a dialog.

*/

}); console.debug("Open action called");

});

 

Now,  re-­‐load  your  plug-­‐in  and  find  the  invoice  document  you  created  earlier  in  this   exercise.  Open  the  document,  with  the  browser  developer  tools  enabled  in  your  

  browser,  and  the  print  statement  you  added  above  should  display  in  the  console.  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

3.2  Enabling  the  custom  open  action  

 

 

 

Now  that  you  have  a  working  open  action,  the  next  step  is  to  enable  it  to  run  the  

  search  criteria  contained  within  the  JSON  file  you  added  earlier  in  this  chapter.  

5

8    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

4.  Asynchronous  tasks  

 

 

An  asynchronous  task  is  basically  a  long  running  process  that  operates  in  the   background  without  blocking  the  user  interface.    There  are  many  use  cases  for  this   type  of  tasks.    Some  common  scenarios  include:  

1) A  long-­‐running  search  or  report    

 

2) Any  sweep-­‐like  jobs  that  are  CPU  intensive  and  may  take  hours  to   complete.  

3) Batched  operations  such  as  large  uploads  or  multi-­‐deletes  that  require  a   bit  of  time  to  finish.  

 

In  IBM  Content  Navigator,  asynchronous  tasks  are  used  to  perform  teamspace   decommissioning,  which  deletes  all  data  in  a  teamspace.    Other  products  such  as  

IBM  Enterprise  Records  use  asynchronous  tasks  to  do  their  retention  sweeps  which   can  take  hours  or  days  to  finish.  

4.1  IBM  ECM  Task  Manager  

The  IBM  ECM  Task  manager  is  the  task  execution  framework  used  to  manage   asynchronous  tasks.    It  is  bundled  along  with  an  IBM  Content  Navigator  installation   and  deployment  and  can  be  enabled  or  disabled  in  the  Navigator’s  administration  

  console.      

Some  additional  features  of  the  IBM  ECM  Task  manager  include:  

1) Flexible  scheduling  capabilities  such  as  single,  recurring,  and  calendar   scheduling  

2) Batch  and  parallel  processing  support  for  performance  and  scale  of  heavy   tasks  

3) Clustered  deployment  for  fail-­‐over  safety  

4) Full  auditing  capabilities  

5) Completely  extensible  with  custom  tasks  

 

The  IBM  ECM  Task  manager  is  repository  agnostic  and  can  be  used  to  run  against  

  any  ECM  repositories  or  any  other  external  repositories  as  well.  

 

4.2  Custom  Asynchronous  Tasks  

As  stated  above,  the  IBM  ECM  Task  Manager  supports  custom  asynchronous  task   development.    Users  are  able  to  create  custom  tasks  and  use  the  task  manager  to  

  schedule  and  run  their  tasks.  

There  are  a  full  set  of  APIs  (javascript,  plugin,  and  Task  manager)  and  UIs  (task   panes  and  schedulers)  available  for  use.  

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   5

9  

 

In  this  exercise,  we  will  focus  on  creating  a  custom  task.    This  exercise  will  walk  you   through  the  following:  

1) Creating  a  custom  asynchronous  task  to  perform  stimulated  batched   operation.    Usually  something  more  substantial  will  be  done  in  a  custom  task   but  focus  on  something  smaller  just  so  it  will  finish  within  a  reasonable  time   frame.  

2) Registering  the  custom  task  with  IBM  Content  Navigator  so  it  can  be  used   within  the  default  Navigator  Task  Pane.  

3) Creating  a  custom  scheduler  that  prompts  the  user  for  custom  fields  and   scheduling  information  

4.3  Creating  a  Custom  Asynchronous  Task  

Creating  a  custom  asynchronous  task  requires  extending  from  the  base  task  called  

com.ibm.ecm.task.commonj.work.BaseTask.”

 –  the  foundational  class  for  all   asynchronous  tasks.    You  will  need  to  implement  the  “

performTask()

”  method  and   provide  what  your  custom  task  is  supposed  to  do.  

 

Let’s  get  started  by  opening  your  Eclipse  and  pointing  it  to  the  last  working  copy  of   the  

IBMECMDevCoursePlugin

 project.    If  you  don’t  have  a  previous  working  copy,   you  can  take  the  completed  solution  from  another  chapter.      

 

 

 

In  order  to  create  your  custom  take,  you  will  need  to  first  bring  in  some  task  

 

 

 

  manager  libraries.    There  are  a  few  other  libraries  that  are  needed  (JPA  persistence   and  WINK)  and  typically  not  provided  with  IBM  Content  Navigator.  All  the  libraries   are  located  under  :  

C:\ICN  Development  Course  Materials\Advanced  Course  

Materials\Libs

.    Make  a  copy  of  all  the  libraries.  

 

 

 

 

 

 

 

 

 

Go  back  to  your  Eclipse  and  paste  all  the  library  files  under  the  lib  folder.    You   should  have  this  now.  

6

0    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

 

 

 

Right  click  

IBMECMDevCourseProject

 and  choose  Properties  and  then  Libraries   tab.    Click  on  Add  JARs  and  add  all  the  libraries  that  are  under  the  lib  folder.        

 

With  all  the  libraries  loaded,  you  are  now  ready  to  create  your  custom  task.      Create  

  a  class  file  under  

src/com/ibm/ecm/extension

 and  name  it  “

IBMDevAsyncTask

”.    

Add  to  the  class  and  make  it  extend  from  BaseTask.    At  the  same  time,  add  the   method  stubs  that  your  custom  task  will  need  to  implement  as  well.    In  the  end,  you   should  have  something  similar  to  this:  

 

} package com.ibm.ecm.extension; import java.io.File; import java.io.IOException; import com.ibm.ecm.task.commonj.work.BaseTask; import com.ibm.ecm.task.entities.Task; public class IBMDevAsyncTask extends BaseTask { public IBMDevAsyncTask(Task task, File logDirectory) throws

IOException { super (task, logDirectory);

// TODO Auto-generated constructor stub

}

@Override public void performTask() {

}

// TODO Auto-generated method stub

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   6

1  

 

Inside  the  BaseTask,  there  are  a  few  variables  that  will  provide  valuable  information   that  will  help  you  in  implementing  your  task.      

1) Task  –  This  variable  holds  all  the  information  about  the  current  Task  which  is   serialized  and  stored  into  the  tasks  database.    This  includes  the  start  time  of   your  task,  who  the  creator  is,  run  time  status  (failed,  errored  out,  paused,  or   not  yet  started),    the  end  time,  the  task’s  schedule,  and  any  custom  task’s   information  (stored  into  the  task’s  info  as  a  JSON  object).    

2) TaskExecutionRecord  –  When  a  single  task  runs  on  a  recurring  basis  or  is   repeated,  there  are  no  subsequent  tasks  that  are  created  for  each  run.    What   will  happen  is  that  a  single  task  will  be  created  and  then  for  each  subsequent   runs,  a  task  execution  record  will  be  instantiated  to  represent  that  next  run.    

A  task  execution  record  contains  similar  but  a  smaller  subset  of  a  task’s   information.    It  will  always  contain  a  taskId  which  points  back  to  the   originating  task.  

3) LogDirectory  –  This  is  the  directory  where  any  logs  for  the  task  will  be  logged   into.    By  default,  this  log  directory  is  set  as  “taskManager”  and  located  as  a   child  of  the  log  directory  of  IBM  Content  Navigator.  

4) Email  notifications  –  A  task  is  able  to  notify  users  when  a  task  completes,   fails,  or  errors  out.    There  are  methods  to  set  the  content  and  subject  of  how   the  email  will  be  presented  when  one  of  these  statuses  of  the  task  occur.  

 

 

 

As  noted  above,  we  are  going  to  be  writing  a  simple  stimulated  task.    Change  

 

IBMDevAsyncTask  to  look  like  this:  

  package com.ibm.ecm.extension; import java.io.File; import java.io.IOException; import com.ibm.ecm.task.Constants; import com.ibm.ecm.task.TaskLogger; import com.ibm.ecm.task.commonj.work.BaseTask; import com.ibm.ecm.task.entities.Task; import com.ibm.ecm.task.utils.Utils; import com.ibm.json.java.JSONObject; public class IBMDevAsyncTask extends BaseTask { public IBMDevAsyncTask(Task task, File logDirectory) throws

IOException {

} super

//

(task, logDirectory);

TODO Auto-generated constructor stub public void performTask() { try {

6

2    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

TaskLogger.

fine (IBMDevAsyncTask.

class .toString(),

"performTask" , "Starting task." );

//grabbing information from the task info

JSONObject taskInfo =

JSONObject.

parse ( task .getTaskInfo());

Thread.

sleep (5000); successfully!" );

JSONObject results = results.put( new JSONObject();

"default_label" , "This task completed

} saveTaskInfo(results, taskInfo); catch (Exception exp){ this .addError( new Long(0),

Utils.

captureStackTrace (exp)); setTaskStatus(Constants.

TASK_STATUS_FAILED );

}

} public void saveTaskInfo(JSONObject results, JSONObject taskInfo) throws Exception {

.

String resultsString = results.serialize(); if ( this .

taskExecutionRecord != null &&

( this .

task .getTaskMode() == Constants.

TASK_RECURRING || this .

task .getTaskMode() == Constants.

TASK_CALENDAR_ACTION )){

); this .

taskExecutionRecord .setTaskExecutionInfo(results.serialize() this .updateTaskExecutionRecord();

} else this .

taskInfo.put( task

"results" , resultsString);

.setTaskInfo(taskInfo.serialize());

}

} this .updateTask();

 

What  this  task  is  doing  is  that  it’s  deserializing  the  task’s  info.    It  will  then  simulate   some  type  of  batched  operations  by  sleeping  for  5  seconds.    At  the  end,  it  will  save  a   json  value  back  into  the  task’s  info  to  indicate  that  it  has  successfully.    In  the  save,  it   checks  to  see  if  this  is  a  recurring  task,  if  it  is,  it  will  save  to  the  task  execution   record  instead.      

 

Once  you  have  finished  creating  your  task,  you  are  now  required  to  add  this  class   into  the  IBM  ECM  Task  Manager  for  it  to  process.    Use  Windows  Explorer  and   navigate  to  where  your  IBMDevCoursePlugin  is  located.    Browse  into  and  make  a   copy  of  

IBMDevCoursePlugin\bin\com\ibm\ecm\extension\IBMDevAsyncTask.class

 

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   6

3  

 

With  the  class  file,  using  Windows  Explorer  again,  browse  to  

C:\IBM\ECMClient\configure\explodedformat\taskManager\taskManagerWe b\WEB-­‐INF\dropins  

and  paste  in  the  class  file.    

 

Once  the  class  added  ,  now  you  will  need  to  restart  Websphere  by  going  into  

Windows  Start  Icon  and  choosing  

Stop  AppSrv01

 and  then  

Start  AppSrv01.  

Once  

Websphere  restarts,  your  custom  task  is  now  ready  to  be  used.  

4.4  Registering  the  Custom  Asynchronous  Task  

 

In  order  for  Navigator  to  recognize  the  custom  asynchronous  task  and  display  it   within  the  Navigator’s  Task  Pane,  you  need  to  register  it  in  your  plugin  by  creating  a  

PluginAsyncTaskType  which  represents  every  unique  custom  task  you  have.  

   

Navigate  to  Eclipse  and  create  a  new  java  class  called  

 

IBMDevCoursePluginAsyncTaskType

”  under  “

com/ibm/ecm/extension

”.    Put   the  following  content  in:  

  package  com.ibm.ecm.extension;  

  import  java.util.Locale;  

 

 

 

 

  import  com.ibm.ecm.extension.PluginAsyncTaskType;  

  public  class  IBMDevAsyncTaskType  extends  PluginAsyncTaskType  {  

  public  String  getClassHandlerName(){   return  "com.ibm.ecm.extension.IBMDevAsyncTask";  

}  

 

 

 

 

 

 

 

 

 

 

 

  public  String  getName(Locale  locale){  

}   return  "Dev  Async  Task";   public  String  getTaskCreationDialogDijitClass()  {  

}   return  "iBMDevCoursePluginDojo/TaskCreationDialog";  

  public  String  getIconClass()  {   return  "ftWordProcessing";  

 

}  

}  

 

In  IBMDevAsyncType,  you  are  returning  the  full  classpath  name  of  your  custom   async  task  created  previously  in  the  getClassHandlerName().      You  are  also  

6

4    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

  returning  a  custom  task  name  that  you  want  to  display  to  the  user.    For  the  

TaskCreationDialogDijitClass,  you  are  presenting  a  custom  user  interface  that  will   be  displayed  to  the  user  when  scheduling  your  task.    

4.5  Creating  a  Custom  Scheduling  Pane  

 

Navigate  to  Eclipse  and  create  these  two  new  javascript  files  under  

  com\ibm\ecm\extension\WebContent\iBMDevCoursePluginDojo  

 

1) TaskCreationDialog.js  

define([  

"dojo/_base/declare",  

 

 

 

 

 

  "dojo/_base/lang",  

"ecm/widget/taskManager/BaseTaskCreationDialog",  

"iBMDevCoursePluginDojo/TaskCreationPane",  

"dojo/text!./templates/TaskCreationDialog.html",  

 

 

 

],  function(declare,  lang,  BaseTaskCreationDialog,  TaskCreationPane,  contentString)  

{   return  declare("iBMDevCoursePluginDojo.TaskCreationDialog",  [  

         BaseTaskCreationDialog  

 

],  {   contentString:  contentString,  

 

 

 

 

 

});  

 

 

 

 

 

 

 

 

 

 

   

 

 

 

 

 

 

 

  widgetsInTemplate:  true,   postCreate:  function()  {  

},   this.inherited(arguments);   this.taskCreationPane  =  new  TaskCreationPane();   this.taskSchedulerPane.addTitlePaneSection("General",   this.taskCreationPane,  0);  

 

 

 

 

 

 

});  

 

 

 

  onSchedule:  function()  {  

}  

  var  valid  =  this.taskCreationPane.validate();   if  (valid  ==  true)  {  

}   this.inherited(arguments);  

 

 

2) TaskCreationPane.js  

  define([  

"dojo/_base/declare",  

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   6

5  

 

 

 

  "dojo/_base/lang",  

"dijit/layout/ContentPane",  

"dijit/_TemplatedMixin",  

"dijit/_WidgetsInTemplateMixin",  

  "dojo/text!./templates/TaskCreationPane.html"  

],  function(declare,  lang,  ContentPane,  TemplatedMixin,  WidgetsInTemplateMixin,  

  contentString)  {  

 

 

 

 

 

 

 

  return  declare("iBMDevCoursePluginDojo.TaskCreationPane",  [ContentPane,  

TemplatedMixin,  WidgetsInTemplateMixin  ],  {  

 

 

 

 

 

 

 

 

  templateString:  contentString,   widgetsInTemplate:  true,  

/**  

 *  Returns  true  if  this  pane  contains  all  valid  values.  

 */  

 

 

 

 

 

 

 

 

 

 

 

});  

 

 

 

});  

  validate:  function()  {  

}   return  true;  

 

 

 

 

 

 

 

 

In  the  end,  you  should  have  this:  

6

6    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

 

 

 

You  will  also  need  to  create  two  template  files  to  correspond  with  the  two  panes   under  

 

com/ibm/ecm/extension/WebContent/iBMDevCoursePluginDojo/templates  

1) TaskCreationDialog.html  

 

<div  class="ecmCommonPropertiesPane"  data-­‐dojo-­‐attach-­‐ point="contentContainerNode"  data-­‐dojo-­‐type="dijit/layout/ContentPane"   style="width:100%;  height:100%;">  

</div>  

 

2) TaskCreationPane.html  

 

 

<table  class="propertyTable">  

 

 

 

 

 

 

 

 

 

<tbody>  

 

 

 

 

 

 

 

  property:</label>  

 

 

 

 

<tr>  

 

<td  class="propertyRowLabel">  

</td>  

<label  for="${id}_property">Sample  

<td  class="propertyRowValue">  

 

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   6

7  

 

 

 

          <div  id="${id}_property"  data-­‐dojo-­‐ type="dijit/form/TextBox"  data-­‐dojo-­‐attach-­‐point="number"    

            data-­‐dojo-­‐props="value:'abc',  trim:   true,  intermediateChanges:  true,  required:  true"   style="width:150px;"></div>  

 

 

 

 

   

</tr>  

</tbody>  

</td>  

 

  </table>  

 

In  the  end  you  should  see  this:  

   

What  you  are  doing  is  creating  a  custom  task  creation  dialog  which  extends  from  the  

 

BaseTaskCreationDialog

 widget.    Inside  the  dialog,  you  will  present  a  custom  pane   that  will  include  a  single  textbox  that  will  prompt  the  user  for  values.    The  value   from  these  prompts  can  then  be  passed  along  into  your  custom  task  for  processing.      

 

4.6  Scheduling  Your  Custom  Task  

 

Once  you  have  all  the  files  created,  let’s  start  getting  the  Navigator  Task  Pane   running  and  scheduling  some  tasks.  

6

8    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

Launch  Navigator  in  your  browser.    Click  on  the  administration  feature  icon.    Click   on  Desktops  and  then  edit  your  default  desktop  (Demo)  .    Go  to  the  Layout  tab  and  

  in  the  feature  list,  enable  the  Asynchronous  Tasks  feature  and  then  Save  and  Close.      

 

 

 

Refresh  your  browser.      Once  Navigator  has  loaded,  click  on  the  Asynchronous  Tasks  

Feature  Pane  on  the  left-­‐hand  side.  

 

 

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   6

9  

 

 

 

This  Asynchronous  Feature  Pane  displays  all  the  asynchronous  tasks  you  have  in   your  system.    This  is  also  the  location  where  you  can  schedule  new  async  tasks  as   well.    Since  you  have  your  AsyncTaskType  registered,  you  should  see  your  

Dev  

Async  Task

 under  the  Schedule  button.  

     

 

 

Click  on  schedule  task  action  and  it  should  launch  the  custom  task  creation  dijits  and   panes  that  you  added.      

7

0    

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT  

 

 

Enter  a  name  and  description  for  your  task.    You  can  also  change  the  schedule  as   well.    Once  you  are  done,  click  on  the  Schedule  button.      Once  your  task  has  been   scheduled,  you  should  see  a  new  field  in  async  task  list.    The  new  task  should  be  in  a   temporary  

Scheduled

 status.    Refresh  the  list  again  and  it  should  be  

In-­‐Processing

  status  and  then  finally  in  a  

Completed

 status.      

 

Click  on  your  completed  task  and  you  will  see  a  detail  tab  that  contains  more  

 

  information  about  your  task.      

 

 

IBM  CONTENT  NAVIGATOR  PLUG-­‐IN  DEVELOPMENT   7

1  

Download