Réutilisation composant MyObjects Webtop
Il est fréquent que nos client nous demandent un affichage de documents en fonction d'une requête avec toutes les fonctionnalités offertes par Webtop:
- Choix des colonnes
- Tris
- Filtres
Le composant MyObjects proposent l'ensemble de ces demandes et cet article se propose de démontrer qu'il est très simple et rapide d'y répondre. A titre d'exemple, le code présente l'ensemble des traces des Jobs, dm_document
sauvegardés sous /Temp/Jobs/<JOB NAME>
.
Le composant myfiles
se présente ainsi dans Webtop:
L'objectif est d'obtenir l'affichage suivant:
Les extraits de code respectent les bonnes pratiques présentées sur la page Bonnes_Pratiques_Webtop.
Sommaire
Votre avis
Nobody voted on this yet
|
|
Version 6.7 SP1
Le code est disponible à l'adresse http://www.jouvinio.net/svn/study/branches/documentum/6.7_SP1/Webtop/MyObjects%20Extension/
Fichier XML du composant
L'extension est donc basée sur la définition de myfiles_classic
dont la définition se trouve dans /webtop/config/myfiles_classic_component.xml
.
L'extension est alors définie à l'emplacement custom/config/webtop/searchex_component.xml
afin de spécifier une nouvelle classe d'instance:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<config version="1.0">
<scope>
<component id="mylogs_classic" extends="myfiles_classic:/webtop/config/myfiles_classic_component.xml">
<desc>
Lists job logs.
</desc>
<pages>
<start>/custom/webtop/classic/myfiles/mylogs.jsp</start>
</pages>
<class>fr.amexio.ejn.webtop.webcomponent.myfiles.MyLogsClassicView</class>
<nlsbundle>fr.amexio.ejn.webcomponent.library.myobjects.MyLogsNlsProp</nlsbundle>
<!--
<helpcontextid>myfilesclassic</helpcontextid>
-->
</component>
</scope>
</config>
Cette nouvelle définition permet de spécifier:
- Une page JSP, dans laquelle une liste de choix sera ajoutée au composant standard.
- Une classe d'instance, dans laquelle la requête DQL sera construite.
- Un fichier properties, dans lequel seront placés les messages spécifiques.
Classe d'instance
Construction requête
Après analyse du code standard, classe MyObjects
dans le package com.documentum.webcomponent.library.myobjects
, la requête est construite à partir d'une instance de MyObjectsService
dans le package com.documentum.webcomponent.library.myobjects
. Cette classe expose quatre fonctions:
- getMyObjectQueryString
- getMyObjectFindByIDQuery
- getFolderIncludedObjectQueryString
- getFolderIncludedObjectFindByIDQuery
Il y a dans ces fonctions pas mal de notion historique. En effet, il est possible de chercher les objets en incluant ou non les répertoires. A noter que cela n'est pas utilisé en standard, car il est impossible de choisir le type d'objets à rechercher. Toutefois, toutes les fonctions seront modifiées afin de ne pas avoir d'effet de bords.
En premier lieu, il faut écrire une extension de ce service.
package fr.amexio.ejn.webcomponent.library.myobjects;
import java.util.List;
import org.apache.log4j.Logger;
import com.documentum.fc.common.DfDocbaseConstants;
import com.documentum.web.formext.docbase.FolderUtil;
import com.documentum.webcomponent.library.myobjects.MyObjectsService;
/**
* <pre>
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* </pre>
*
* Use a custom "service" to build query on logs files. Because we use the myObjects component, we have to use the modifiedDaysCount in arguments. But we will not used it. We must
* extend the MyObjectsService, because this is what the original component is waiting for.
*
* @author Etienne Jouvin
*
*/
public class MyLogsService extends MyObjectsService {
private static final Logger LOGGER = Logger.getLogger(MyLogsService.class);
private String folderId;
/**
* Constructor.
*
* @param modifiedDaysCount Argument defined by parent constructor.
* @param folderId Folder id used to filter.
*/
public MyLogsService(int modifiedDaysCount, String folderId) {
super(modifiedDaysCount);
this.folderId = folderId;
}
/**
* Build the specific query for the MyLogs service.
*
* @param strAttributeNames Attributes names to put in the select clause.
* @param findById Internal flag to know what to put in where clause.
* @return Built query.
*/
private String buildQuery(String strAttributeNames[], boolean findById) {
StringBuffer strbuf = new StringBuffer();
/* Build the select clause with parent class. */
/* This will return a list will all attributes used in the select clause. */
/* This list is used after to build the order clause. */
List<?> orderedList = super.buildDqlSelectAttributesString(strbuf, strAttributeNames, null);
/* Here we want to request on dm_document, and we add the upper(object_name). */
/* In parent class, this upper is also insert and used as the default filter. */
/* Not a bad idea, so we keep it. */
strbuf.append(", upper(object_name) from ").append(DfDocbaseConstants.DM_DOCUMENT).append(" where ");
/* Use the FIND_BY_ID_QUERY_TOKEN flag or the real filter for the service. */
if (findById) {
strbuf.append("FIND_BY_ID_QUERY_TOKEN");
} else if (null != folderId && FolderUtil.isFolderType(folderId)) {
/* A folder id is given. Build the filter according this one. */
strbuf.append("folder(id('").append(folderId).append("'), descend)");
} else {
/* Here is the filter for logs, we go into /Temp/Jobs folder and all sub folders. */
/* No folder id given for filter. */
strbuf.append("folder('/Temp/Jobs', descend)");
}
strbuf.append(" order by ")
/* The first filter is on the object_name, case insensitive. */
/* We keep what it is done in parent class. */
/* A list was built for the select clause, */
/* then upper(object_name) is added to the select clause. */
/* So the position in select clause, for upper(obejct_name) is equals to the list size. */
.append(orderedList.size() + 1)
/* */
.append(",")
/* Add order on r_object_id. */
.append(orderedList.indexOf("r_object_id") + 1);
int iVersionLabel = orderedList.indexOf("r_version_label");
if (iVersionLabel > -1) {
strbuf.append(",").append(iVersionLabel + 1);
}
/* TODO, faire une critique sur les requêtes générées... c'est juste n'importe quoi. Avec les stringbuffer, les replace que l'on pourrait modifier en format etc etc. */
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("MyLogService query: " + strbuf.toString());
}
return strbuf.toString();
}
/** {@inheritDoc} */
@Override
public String getFolderIncludedObjectFindByIDQuery(String strObjectType, String strAttributeNames[]) {
/* The request produced by this function is something like: */
/* select 1, strAttributeNames[0], strAttributeNames[X], r_object_type, r_object_id, object_name, upper(object_name), '1' as isfolder */
/* from dm_cabinet */
/* where FIND_BY_ID_QUERY_TOKEN */
/* union */
/* select 2, strAttributeNames[0], strAttributeNames[X], r_object_type, r_object_id, object_name, upper(object_name), '1' as isfolder */
/* from dm_folder */
/* where FIND_BY_ID_QUERY_TOKEN */
/* union */
/* select 3, strAttributeNames[0], strAttributeNames[X], r_object_type, r_object_id, object_name, upper(object_name), '1' as isfolder */
/* from dm_document (all) */
/* where FIND_BY_ID_QUERY_TOKEN */
/* order by 1,7,5 */
/* The request can be different if r_version_label is in the array strAttributeNames. Then the order clause will use this attribute also. */
/* It also depends on the type requested. If dm_cabinet or dm_folder is set, the third query part is not built. */
/* This service was created to list logs. We do not take into account the folder argument. */
return this.buildQuery(strAttributeNames, true);
}
/** {@inheritDoc} */
@Override
public String getFolderIncludedObjectQueryString(String strObjectType, String strAttributeNames[]) {
/* The request produced by this function is something like: */
/* select 1, strAttributeNames[0], strAttributeNames[X], r_object_type, r_object_id, object_name, upper(object_name), '1' as isfolder */
/* from dm_cabinet */
/* where r_modifier=USER and r_modify_date >= DATE('11/03/2013', 'mm/dd/yyyy') */
/* union */
/* select 2, strAttributeNames[0], strAttributeNames[X], r_object_type, r_object_id, object_name, upper(object_name), '1' as isfolder */
/* from dm_folder */
/* where where r_modifier=USER and r_modify_date >= DATE('11/03/2013', 'mm/dd/yyyy') and not type(dm_cabinet) */
/* union */
/* select 3, strAttributeNames[0], strAttributeNames[X], r_object_type, r_object_id, object_name, upper(object_name), '1' as isfolder */
/* from dm_document (all) */
/* where ( */
/* r_lock_owner=USER or */
/* (r_modifier=USER and r_modify_date >= DATE('11/03/2013', 'mm/dd/yyyy')) */
/* ) and */
/* not type(dm_folder) */
/* order by 1,7,5 */
/* The request can be different if r_version_label is in the array strAttributeNames. Then the order clause will use this attribute also. */
/* It also depends on the type requested. If dm_cabinet or dm_folder is set, the third query part is not built. */
/* This service was created to list logs. We do not take into account the folder argument. */
return this.buildQuery(strAttributeNames, false);
}
/** {@inheritDoc} */
@Override
public String getMyObjectFindByIDQuery(String strObjectType, String strAttributeNames[]) {
/* The request produced by this function is something like: */
/* select strAttributeNames[0], strAttributeNames[X], r_object_type, r_object_id, object_name, upper(object_name), '0' as isfolder */
/* from dm_sysobject (all) */
/* where FIND_BY_ID_QUERY_TOKEN */
/* order by 6,4 */
/* The request can be different if r_version_label is in the array strAttributeNames. Then the order clause will use this attribute also. */
return this.buildQuery(strAttributeNames, true);
}
/** {@inheritDoc} */
@Override
public String getMyObjectQueryString(String strObjectType, String strAttributeNames[]) {
/* The request produced by this function is something like: */
/* select strAttributeNames[0], strAttributeNames[X], r_object_type, r_object_id, object_name, upper(object_name), '0' as isfolder */
/* from dm_sysobject (all) */
/* where r_lock_owner=USER or */
/* ((owner_name=USER or r_modifier=USER) and any r_version_label = 'CURRENT' and r_modify_date >= DATE('11/03/2013', 'mm/dd/yyyy')) */
/* order by 6,4 */
/* The request can be different if r_version_label is in the array strAttributeNames. Then the order clause will use this attribute also. */
return this.buildQuery(strAttributeNames, false);
}
}
A noter que le constructeur est modifié afin d'accepter un nouvel argument folderId
qui permettra de filtrer l'emplacement des logs. L'argument modifiedDaysCount
est conservé même si il n'est pas utilisé.
Les quatres fonctions appelent la construction d'une requête de type.
- Quand aucun filtre sur le job n'est spécifié:
SELECT attr1, attr2, ...
FROM dm_document
WHERE folder('/Temp/Jobs', descend)
ORDER BY ...
- Quand un job est spécifié:
SELECT attr1, attr2, ...
FROM dm_document
WHERE folder(id('<dm_folder> id'), descend)
ORDER BY ...
Utilisation du nouveau service
Le nouveau composant mylogs_classic
a été défini dans le fichier XML en spécifiant une nouvelle classe d'instance MyLogsClassicView
dans le package fr.amexio.ejn.webtop.webcomponent.myfiles
. Afin d'utiliser le nouveau service, seule la fonction getMyObjectsService
est étendue. Ceci permet de construire une instance du nouveau service en lieu et place du standard.
/** {@inheritDoc} */
@Override
protected MyObjectsService getMyObjectsService() {
/* During the function updateControl in MyObjects parent class, */
/* this function is called to have the "service" used to build the query. */
/* So here, we used a custom "service" in order to build the expected query. */
/* Send 0 as argument because we will not used it. Can do everything we want, but just keep it simple. */
DropDownList filterCtrl = (DropDownList) getControl(CTRL_JOBS_FILTER, com.documentum.web.form.control.DropDownList.class);
return new MyLogsService(0, filterCtrl.getValue());
}
L'utilisation de la DropDownList est étudié en fin d'article.
A partir de ce moment, l'extension est quasiment terminée. Il ne reste plus que la cosmétique à mettre en place.
Titre affiché
Le composant standard affiche un titre du type My Files (<DOCBASE_NAME>)
qu'il est possible de modifier. Pour cet exemple, le label My Logs
est souhaité. Ce titre est construit à partir de la fonction buildHeaderlabel
qui est en standard:
protected String buildHeaderlabel()
{
StringBuffer header = new StringBuffer(64);
header.append(getString("MSG_MYOBJECTS")).append(" (").append(getCurrentDocbase()).append(')');
return header.toString();
}
Il suffit d'ajouter un nouveau label dans le fichier properties déclaré dans le XML. La clé MSG_MYOBJECTS
est réutilisé au cas où celui-ci soit utilisé dans un autre mécanisme.
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
NLS_INCLUDES=com.documentum.webcomponent.library.myobjects.MyObjectsNlsProp
# Extends the MSG_MYOBJECTS for the component title, even if there is some code in the class
# But this string is used in multiple functions, so this is easiest to use the same name.
MSG_MYOBJECTS=My Logs
# Label for the job filter set to all jobs.
MSG_FILTER_ALL=All
MSG_JOB_FILTER=Filter by job
Ce nouveau label est ensuite récupéré dans la fonction buildHeaderlabel
/** {@inheritDoc} */
@Override
protected String buildHeaderlabel() {
/* In parent class, the label for MSG_MYOBJECTS is concatenated with the current docbase name. */
/* Here, we just want to display the label. */
/* This function is used during the method updateControl in MyObjects parent class. */
return super.getString(MSG_MYOBJECTS);
}
Composant browsertree
Le composant myfiles
est accessible depuis l'arbre de navigation, ce qu'il faut reproduire.
Fichier XML du browsertree
Le noeud est ajouté directement sous celui de myfiles
avec :
- Identifiant du composant
mylogs_classic
correspondant au nouveau composant. - Le label identifié par la clé
MSG_MYLOGS
. - une nouvelle icône, qui est placée sous
custom/theme/documentum/icons/browsertree/mylogs.png
.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<config version="1.0">
<scope>
<component modifies="browsertree:webtop/config/browsertreeex_component.xml">
<insertafter path='nodes.docbasenodes.node[componentid=myfiles_classic]'>
<node componentid="mylogs_classic">
<!-- Set a specific icon for the new node. -->
<icon>mylogs.png</icon>
<!-- The node label is set here for the browserTree. -->
<!-- Do not forget to modify the label in the component nlsbundle. -->
<label>
<nlsid>MSG_MYLOGS</nlsid>
</label>
<streamlinecomponent>mylogs_streamline</streamlinecomponent>
</node>
</insertafter>
<replace path='nlsbundle'>
<nlsbundle>fr.amexio.ejn.webtop.webcomponent.browsertree.BrowserTreeNlsProp</nlsbundle>
</replace>
</component>
</scope>
</config>
Comme un nouveau label est défini, une extension du fichier properties est mise en place sous custom/strings/fr/amexio/ejn/webtop/webcomponent/browsertree/BrowserTreeNlsProp.properties
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
NLS_INCLUDES=com.documentum.webtop.webcomponent.browsertree.BrowserTreeNlsProp
# New label for the new node.
MSG_MYLOGS=My logs
Cela suffit à rendre disponible le composant dans l'interface.
Composant display_preferences
Le composant myfiles
référence un identifiant de sélection de colonne. Cette modification va permettre de mettre en place une sélection spécifique au niveau composant.
Dans la classe MyFilesClassicView
, package com.documentum.webtop.webcomponent.myfiles
, la méthode setColumnsPreferenceId
est appelée avec l'argument application.display.classic_myfiles_columns
lors de son initialisation:
public void onInit(ArgumentList args)
{
setColumnsPreferenceId("application.display.classic_myfiles_columns");
super.onInit(args);
updateRootContext();
setObjectTypeFilter(m_strQueryObjectType, m_fIncludedFolders);
}
Il est donc nécessaire de surcharger cette fonctionnalité, en forçant l'identifiant. Pour ce faire, il suffit de surcharger la méthode setColumnsPreferenceId
dans la classe d'instance du nouveau composant:
private static final String PREF_CLASSIC_MYLOGS = "application.display.classic_mylogs_columns";
/** {@inheritDoc} */
@Override
protected void setColumnsPreferenceId(String strPreferenceId) {
/* We force a new preference id, to be able to do something different than myObject. */
/* But in this case, we must modify the display_preferences_ex_component. */
super.setColumnsPreferenceId(PREF_CLASSIC_MYLOGS);
}
Ainsi, il est possible de spécifier les colonnes à afficher directement depuis le composant mylogs
:
et cela sans que ça ait un impact sur le composant étendu myfiles
:
Il reste à définir cette nouvelle référence au niveau de display_preferences
.
Fichier XML
Il faut rajouter une préférence avec l'identifiant application.display.classic_mylogs_columns
, et de le rendre disponible en rajoutant un groupe mylogs
, placé sous le groupe myfiles
.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Licensed to the Apache Software Foundation (ASF) under one or more -->
<!-- contributor license agreements. See the NOTICE file distributed with -->
<!-- this work for additional information regarding copyright ownership. -->
<!-- The ASF licenses this file to You under the Apache License, Version 2.0 -->
<!-- (the "License"); you may not use this file except in compliance with -->
<!-- the License. You may obtain a copy of the License at -->
<!-- -->
<!-- http://www.apache.org/licenses/LICENSE-2.0 -->
<!-- -->
<!-- Unless required by applicable law or agreed to in writing, software -->
<!-- distributed under the License is distributed on an "AS IS" BASIS, -->
<!-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -->
<!-- See the License for the specific language governing permissions and -->
<!-- limitations under the License. -->
<config version="1.0">
<scope>
<component modifies="display_preferences:webtop/config/display_preferences_ex_component.xml">
<!--
does not work because the id contains .
<insertafter path='preferences.preference[id=application.display.classic_myfiles_columns]'>
</insertafter>
-->
<!-- Add a new preference columns. -->
<!-- The id is linked to the one set in the mylogs component. -->
<insert path='preferences'>
<preference id="application.display.classic_mylogs_columns">
<label><nlsid>CLASSIC_MYLOGS_COLUMNS_LABEL</nlsid></label>
<!-- We keep this label to do like standard selection in Webtop. -->
<columnlabel><nlsid>CLASSIC_COLUMNS</nlsid></columnlabel>
<type>columnlist</type>
<display_hint>hidden</display_hint>
<!-- We keep the myobjects_list, because nothing specific in component mylogs_classic. -->
<value>component[id=myobjects_list].columns</value>
<!-- Do exactly the same than for classic_myfiles_columns -->
<inherits>application.display.classic_cabinets_columns</inherits>
<editcomponent>columnselector</editcomponent>
<editcontainer>columnselectorcontainer</editcontainer>
</preference>
<!-- Do it only for the tutorial. Because the streamline component is registered in browsertree component. -->
<!-- We created a new extension with a specific column selector. -->
<!-- But we will not display it. -->
<preference id="application.display.streamline_myfiles_columns">
<label>
<nlsid>STREAMLINE_MYLOGS_COLUMNS_LABEL</nlsid>
</label>
<columnlabel>
<nlsid>STREAMLINE_COLUMNS</nlsid>
</columnlabel>
<type>columnlist</type>
<display_hint>hidden</display_hint>
<!-- We keep the myobjects_drilldown, because nothing specific in component mylogs_streamline. -->
<value>component[id=myobjects_drilldown].columns</value>
<inherits>application.display.streamline_cabinets_columns</inherits>
<editcomponent>columnselector</editcomponent>
<editcontainer>columnselectorcontainer</editcontainer>
</preference>
</insert>
<!-- Insert a new display. -->
<insertafter path='preferencedisplaygroups.group[id=myfiles]'>
<group id="mylogs">
<members>
<preference-ref id="application.display.classic_mylogs_columns" />
</members>
<label><nlsid>MYLOGS_LABEL</nlsid></label>
</group>
</insertafter>
<replace path='nlsbundle'>
<nlsbundle>fr.amexio.ejn.webcomponent.environment.preferences.display.DisplayPreferencesNlsProp</nlsbundle>
</replace>
</component>
</scope>
</config>
Comme de nouveaux labels sont définis, une extension du fichier properties est mise en place sous custom/strings/fr/amexio/ejn/webcomponent/environment/preferences/display/DisplayPreferencesNlsProp.properties
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
NLS_INCLUDES=com.documentum.webcomponent.environment.preferences.display.DisplayPreferencesNlsProp
# New label for the new node.
CLASSIC_MYLOGS_COLUMNS_LABEL=Classic Columns for My logs
# Just for the tutorial. Streamline view is not used anymore.
STREAMLINE_MYFILES_COLUMNS_LABEL=Streamline Columns for My logs
MYLOGS_LABEL=My logs
Ainsi, il est possible de sélectionner des colonnes à afficher sans que cela n'est d'impact sur la sélection faite pour myfiles
:
Filtre par nom des jobs
Jusqu'ici, l'article présente une simple réutilisation du composant standard. Afin d'agrémenter la fonctionnalité, une possibilité de filtre est mis en place pour n'afficher que les logs d'un job identifié. Pour cela, la possibilité de filtre sur le type d'objet, au niveau de la navigation, est reprise. Une liste de choix, avec le nom des jobs, est placée juste à côté de la zone de filtre sur le nom:
Fichier XML du composant
La page jsp
a donc été modifiée au niveau de la définition du composant mylogs_classic
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<config version="1.0">
<scope>
<component id="mylogs_classic" extends="myfiles_classic:/webtop/config/myfiles_classic_component.xml">
<desc>
Lists job logs.
</desc>
<pages>
<start>/custom/webtop/classic/myfiles/mylogs.jsp</start>
</pages>
<class>fr.amexio.ejn.webtop.webcomponent.myfiles.MyLogsClassicView</class>
<nlsbundle>fr.amexio.ejn.webcomponent.library.myobjects.MyLogsNlsProp</nlsbundle>
<!--
<helpcontextid>myfilesclassic</helpcontextid>
-->
</component>
</scope>
</config>
Page JSP du composant
La page est une simple copie du standard. Dans un premier temps, une copie de webtop/classic/myfiles/myfiles.jsp
est mise en place sous custom/webtop/classic/myfiles/mylogs.jsp
:
<%
//
%>
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ page errorPage="/wdk/errorhandler.jsp" %>
<%@ taglib uri="/WEB-INF/tlds/dmform_1_0.tld" prefix="dmf" %>
<%@ taglib uri="/WEB-INF/tlds/dmformext_1_0.tld" prefix="dmfx" %>
<%@ page import="com.documentum.web.form.Form" %>
<html>
<head>
<dmf:webform/>
<script language='JavaScript1.2' src='<%=Form.makeUrl(request, "/wdk/include/dynamicAction.js")%>'></script>
<script language='JavaScript1.2' src='<%=Form.makeUrl(request, "/webcomponent/navigation/navigation.js")%>'></script>
<title><dmf:label nlsid="MSG_MYOBJECTS"/></title>
</head>
<body class='contentBackground myfilesBodyClassicMargin' marginheight='0' marginwidth='0'
topmargin='0' bottommargin='0'>
<dmf:form>
<%@ include file='/custom/webcomponent/library/myobjects/mylogs_list_body.jsp' %>
</dmf:form>
</body>
</html>
En fait, cette page ne fait que inclure une autre, que nous devons recopier sous /custom/webcomponent/library/myobjects/mylogs_list_body.jsp
.
<%@ page import="com.documentum.webcomponent.library.myobjects.MyObjects,
com.documentum.web.form.Form"%>
<%@ page import="com.documentum.web.common.AccessibilityService"%>
<%@ page import="com.documentum.web.dragdrop.IDropTarget" %>
<%@ page import="fr.amexio.ejn.webtop.webcomponent.myfiles.MyLogsClassicView" %>
<%
//
%>
<%@ taglib uri="/WEB-INF/tlds/dmform_1_0.tld" prefix="dmf" %>
<%@ taglib uri="/WEB-INF/tlds/dmformext_1_0.tld" prefix="dmfx" %>
<dmfx:dragdrop/>
<dmfx:actionmultiselect name='multi'>
<dmfx:dragdropregion name='namedtopdragdropregion' enableddroppositions="<%=IDropTarget.DROP_POSITION_OVER%>" ondrop='onDrop' isbackground='true'>
<dmfx:argument name='objectId' contextvalue='objectId'/>
<dmf:datagrid name='<%=MyObjects.MYOBJECTS_GRID%>' paged='true' preservesort='false'
width='100%' cellspacing='0' cellpadding='0' bordersize='0' rowselection="true" fixedheaders="true" focus="true" >
<tr>
<td colspan=20 class='headerBackground'>
<table width="100%" cellspacing=0 cellpadding=0 border=0>
<tr>
<td class="leftAlignment" width=100%>
<dmf:label name='<%=MyObjects.MYOBJECTS_HEADER%>' cssclass='webcomponentTitle'/>
</td>
<td class="rightAlignment" nowrap>
<dmf:datacolumnbeginswith name='namecolumnbeginswith' column='object_name' autocompleteid='ac_namecolumnbeginswith' size="24" nlsid="MSG_BEGINSWITH_FILTER"/>
</td>
<% /* Added to have a filter on jobs */ %>
<td width="5" nowrap class="spacer"> </td>
<td class="rightAlignment" nowrap>
<dmf:dropdownlist name='<%=MyLogsClassicView.CTRL_JOBS_FILTER%>' onselect='onSelectJobFilter' tooltipnlsid="MSG_JOB_FILTER">
</dmf:dropdownlist>
</td>
<% /* End added */ %>
</tr>
</table>
</td>
</tr>
<tr class=pagerBackground>
<td colspan=20 align=center height=24>
<table width=100% border=0 cellspacing=0 cellpadding=0>
<tr>
<td align=center width=100%><dmf:datapaging name='pager1' gotopageclass='doclistPager'/></td>
<td class="rightAlignment" nowrap><dmf:label nlsid='MSG_SHOW_ITEMS'/> </td>
<td valign="middle" nowrap><dmf:datapagesize name='sizer' preference='application.display.classic' tooltipnlsid='MSG_SHOW_ITEMS'/> </td>
</tr>
</table>
</td>
</tr>
<tr>
<td colspan='23' height='1' class='spacer'> </td>
</tr>
<% boolean accessibilityEnabled = AccessibilityService.isAllAccessibilitiesEnabled();
if (accessibilityEnabled)
{
%>
<table width=100% border=0 cellspacing=0 cellpadding=0>
<%
}
%>
<tr class='colHeaderBackground'>
<dmf:datagridTh scope='col' width="16" height="16" cssclass='doclistcheckbox leftAlignment'>
<dmfx:actionmultiselectcheckall/>
</dmf:datagridTh>
<dmf:datagridTh scope='col' cssclass='doclistlocicon leftAlignment'>
<b><dmf:datasortimage name='sort1' datafield='r_lock_owner' cssclass='doclistbodyDatasortlink' reversesort='true' image='icons/sort/sortByLock.gif'/></b>
</dmf:datagridTh>
<dmf:celllist>
<dmf:celltemplate field='topic_status'>
<dmf:datagridTh scope='col' cssclass='doclistfilenamedatagrid leftAlignment'>
<dmf:datasortimage name='sorttopic' datafield='topic_status' cssclass='doclistbodyDatasortlink' image='icons/sort/sortByDisc.gif'/>
</dmf:datagridTh>
</dmf:celltemplate>
<dmf:celltemplate field='room_status'>
<dmf:datagridTh scope='col' cssclass='doclistfilenamedatagrid leftAlignment'>
<dmf:datasortimage name='sortroom' datafield='room_status' cssclass='doclistbodyDatasortlink' image='icons/sort/sortByRoom.gif'/>
</dmf:datagridTh>
</dmf:celltemplate>
<dmf:celltemplate field='object_name'>
<dmf:datagridTh scope='col' cssclass='doclistfilenamedatagrid objectlistheaderspacing leftAlignment' resizable="true">
<b><dmf:datasortlink name='sort2' datafield='object_name' cssclass='doclistbodyDatasortlink'/></b>
</dmf:datagridTh>
<dmf:datagridRowModifier>
<dmf:datagridTh scope='col' align='center' nowrap="true" cssclass='spacer'>
<dmf:image name='prop' nlsid='MSG_PROPERTIES' src='images/space.gif'/>
</dmf:datagridTh>
</dmf:datagridRowModifier>
</dmf:celltemplate>
<dmf:celltemplate field='title'>
<dmf:datagridTh scope='col' cssclass='doclistfilenamedatagrid leftAlignment' resizable="true">
<b><dmf:datasortlink name='sort3' datafield='title' cssclass='doclistbodyDatasortlink'/></b>
</dmf:datagridTh>
</dmf:celltemplate>
<dmf:celltemplate field='authors'>
<dmf:datagridTh scope='col' cssclass='doclistfilenamedatagrid leftAlignment' resizable="true">
<b><dmf:datasortlink name='sort4' datafield='authors' cssclass='doclistbodyDatasortlink'/></b>
</dmf:datagridTh>
</dmf:celltemplate>
<dmf:celltemplate type='number'>
<dmf:datagridTh scope='col' cssclass='doclistfilenamedatagrid leftAlignment' resizable="true">
<b><nobr><dmf:datasortlink name='sort5' datafield='CURRENT' mode='numeric' cssclass='doclistbodyDatasortlink'/></nobr></b>
</dmf:datagridTh>
</dmf:celltemplate>
<dmf:celltemplate type='date'>
<dmf:datagridTh scope='col' cssclass='doclistfilenamedatagrid leftAlignment' resizable="true">
<b><nobr><dmf:datasortlink name='sort6' datafield='CURRENT' mode='numeric' cssclass='doclistbodyDatasortlink'/></nobr></b>
</dmf:datagridTh>
</dmf:celltemplate>
<dmf:celltemplate>
<dmf:datagridTh scope='col' cssclass='doclistfilenamedatagrid leftAlignment' resizable="true">
<b><nobr><dmf:datasortlink name='sort7' datafield='CURRENT' cssclass='doclistbodyDatasortlink'/></nobr></b>
</dmf:datagridTh>
</dmf:celltemplate>
</dmf:celllist>
<dmf:datagridTh valign="middle" cssclass="colprefsicon">
<dmf:image src="icons/columnprefs_16.gif" nlsid="MSG_COLUMN_PREFERENCES" onclick="onClickColumnsPrefs">
<dmf:argument name="usemodalpopup" value="true"/>
<dmf:argument name="modalpopupwindowsize" value="large"/>
<dmf:argument name="refreshparentwindow" value="onok"/>
</dmf:image>
</dmf:datagridTh>
<dmf:datagridTh width="99%"> </dmf:datagridTh>
</tr>
<dmf:datagridRow cssclass='defaultDatagridRowStyle' altclass="defaultDatagridRowAltStyle">
<dmf:datagridRowTd height="24" cssclass="doclistcheckbox">
<dmfx:actionmultiselectcheckbox name='check' value='false'>
<dmf:argument name='objectId' datafield='r_object_id'/>
<dmf:argument name='type' datafield='r_object_type'/>
<dmf:argument name='lockOwner' datafield='r_lock_owner'/>
<dmfx:argument name='folderId' contextvalue='objectId'/>
<dmf:argument name='ownerName' datafield='owner_name'/>
<dmf:argument name='contentSize' datafield='r_full_content_size'/>
<dmf:argument name='contentType' datafield='a_content_type'/>
<dmf:argument name="isVirtualDoc" datafield='r_is_virtual_doc'/>
<dmf:argument name="linkCount" datafield='r_link_cnt'/>
<dmf:argument name='startworkflowId' value='startworkflow'/>
<dmf:argument name='workflowRuntimeState' value='-1'/>
<dmf:argument name='isReference' datafield='i_is_reference'/>
<dmf:argument name='isReplica' datafield='i_is_replica'/>
<dmf:argument name='assembledFromId' datafield='r_assembled_from_id'/>
<dmf:argument name='isFrozenAssembly' datafield='r_has_frzn_assembly'/>
<dmf:argument name='compoundArchitecture' datafield='a_compound_architecture'/>
<dmf:argument name='topicStatus' datafield='topic_status'/>
<dmf:argument name='events' datafield='events'/>
<dmf:argument name='notificationStatus' datafield='notification_status'/>
</dmfx:actionmultiselectcheckbox>
</dmf:datagridRowTd>
<dmf:datagridRowTd nowrap="true" cssclass='doclistlocicon'>
<dmfx:docbaselockicon datafield='r_lock_owner' size='16'/>
</dmf:datagridRowTd>
<dmf:celllist>
<dmf:celltemplate field="topic_status">
<dmf:datagridRowTd cssclass='doclistfilenamedatagrid'>
<dmfx:topicstatus name='status' nlsid='MSG_NO_COMMENTS' action='showtopicaction' src='icons/none.gif' height='16' width='16' showifdisabled='false' >
<dmf:argument name='objectId' datafield='r_object_id'/>
<dmf:argument name='type' datafield='r_object_type'/>
<dmf:argument name='topicStatus' datafield='topic_status'/>
<dmfx:argument name='folderId' contextvalue='objectId'/>
</dmfx:topicstatus>
</dmf:datagridRowTd>
</dmf:celltemplate>
<dmf:celltemplate field='room_status' >
<dmf:datagridRowTd cssclass='doclistfilenamedatagrid'>
<dmfx:governedicon name='room' action='view' src='icons/none.gif' height='16' width='16' >
<dmf:argument name='objectId' datafield='r_object_id'/>
<dmf:argument name='governing' datafield='room_status'/>
<dmf:argument name='type' value='dmc_room'/>
<dmfx:argument name='folderId' contextvalue='objectId'/>
</dmfx:governedicon>
</dmf:datagridRowTd>
</dmf:celltemplate>
<dmf:celltemplate field='object_name'>
<dmf:datagridRowTd cssclass='doclistfilenamedatagrid' nowrap="true" scope='row'>
<dmf:stringlengthformatter maxlen='32'>
<dmfx:dragdropregion datafield='object_name' enableddroppositions="<%=IDropTarget.DROP_POSITION_OVER%>" dragenabled='true' ondrop="onDrop" isbackground='true'>
<dmf:argument name='objectId' datafield='r_object_id'/>
<dmf:argument name='lockOwner' datafield='r_lock_owner'/>
<dmfx:docbaseicon formatdatafield='a_content_type' typedatafield='r_object_type' linkcntdatafield='r_link_cnt' isvirtualdocdatafield='r_is_virtual_doc' assembledfromdatafield='r_assembled_from_id' isfrozenassemblydatafield='r_has_frzn_assembly' isreplicadatafield='i_is_replica' isreferencedatafield='i_is_reference' size='16'/>
<dmf:datagridRowEvent eventname="dblclick">
<dmf:link name='lblObjName' onclick='onClickMyObject' datafield='object_name' runatclient='true'>
<dmf:argument name='objectId' datafield='r_object_id'/>
<dmf:argument name='type' datafield='r_object_type'/>
<dmf:argument name='isFolder' datafield='isfolder'/>
<dmf:argument name="isVirtualDoc" datafield='r_is_virtual_doc'/>
<dmf:argument name='assembledFromId' datafield='r_assembled_from_id'/>
<dmf:argument name="linkCount" datafield='r_link_cnt'/>
</dmf:link>
</dmf:datagridRowEvent>
</dmfx:dragdropregion>
</dmf:stringlengthformatter>
</dmf:datagridRowTd>
<dmf:datagridRowModifier>
<dmf:datagridRowTd cssclass='doclistfilenamedatagrid'>
<dmfx:actionimage name='propact' nlsid='MSG_PROPERTIES' action='properties' src='icons/info.gif' showifdisabled='false'>
<dmf:argument name='objectId' datafield='r_object_id'/>
<dmf:argument name='type' datafield='r_object_type'/>
</dmfx:actionimage>
</dmf:datagridRowTd>
</dmf:datagridRowModifier>
</dmf:celltemplate>
<dmf:celltemplate field='title'>
<dmf:datagridRowTd cssclass='doclistfilenamedatagrid'>
<dmfx:folderexclusionformatter datafield='r_object_type'>
<dmf:stringlengthformatter maxlen='14'>
<dmf:label datafield='title'/>
</dmf:stringlengthformatter>
</dmfx:folderexclusionformatter>
</dmf:datagridRowTd>
</dmf:celltemplate>
<dmf:celltemplate field='authors'>
<dmf:datagridRowTd cssclass='doclistfilenamedatagrid'>
<dmfx:folderexclusionformatter datafield='r_object_type'>
<dmf:htmlsafetextformatter>
<dmf:label datafield='authors'/>
</dmf:htmlsafetextformatter>
</dmfx:folderexclusionformatter>
</dmf:datagridRowTd>
</dmf:celltemplate>
<dmf:celltemplate field='r_version_label'>
<dmf:datagridRowTd cssclass='doclistfilenamedatagrid'>
<dmfx:folderexclusionformatter datafield='r_object_type'>
<dmf:htmlsafetextformatter>
<dmf:label datafield='r_version_label'/>
</dmf:htmlsafetextformatter>
</dmfx:folderexclusionformatter>
</dmf:datagridRowTd>
</dmf:celltemplate>
<dmf:celltemplate field='a_content_type'>
<dmf:datagridRowTd nowrap="true" cssclass='doclistfilenamedatagrid'>
<dmf:stringlengthformatter maxlen='14'>
<dmfx:docformatvalueformatter>
<dmf:label datafield='CURRENT'/>
</dmfx:docformatvalueformatter>
</dmf:stringlengthformatter>
</dmf:datagridRowTd>
</dmf:celltemplate>
<dmf:celltemplate field='r_full_content_size'>
<dmf:datagridRowTd nowrap="true" cssclass='doclistfilenamedatagrid'>
<dmfx:docsizevalueformatter datafield='r_object_type'>
<bdo dir="ltr">
<dmf:label datafield='r_full_content_size'/>
</bdo>
</dmfx:docsizevalueformatter>
</dmf:datagridRowTd>
</dmf:celltemplate>
<dmf:celltemplate field='r_current_state'>
<dmf:datagridRowTd nowrap="true" cssclass='doclistfilenamedatagrid'>
<dmfx:policystatenameformatter datafield='r_policy_id'>
<dmf:label datafield='r_current_state'/>
</dmfx:policystatenameformatter>
</dmf:datagridRowTd>
</dmf:celltemplate>
<dmf:celltemplate type='date'>
<dmf:datagridRowTd nowrap="true" cssclass='doclistfilenamedatagrid'>
<dmf:datevalueformatter>
<dmf:label datafield='CURRENT'/>
</dmf:datevalueformatter>
</dmf:datagridRowTd>
</dmf:celltemplate>
<dmf:celltemplate>
<dmf:datagridRowTd nowrap="true" cssclass='doclistfilenamedatagrid'>
<dmf:label datafield='CURRENT'/>
</dmf:datagridRowTd>
</dmf:celltemplate>
</dmf:celllist>
<dmf:datagridRowTd> </dmf:datagridRowTd>
<dmf:datagridRowTd width="80%"> </dmf:datagridRowTd>
</dmf:datagridRow>
<dmf:nodataRow>
<td colspan=20 height=24>
<dmf:label nlsid='MSG_NO_DOCUMENTS'/>
</td>
</dmf:nodataRow>
<% if (accessibilityEnabled)
{
%>
</table>
<%
}
%>
</dmf:datagrid>
</dmfx:dragdropregion>
</dmfx:actionmultiselect>
La modification permet d'afficher une liste de choix avec :
- le nom défini dans la constante
MyLogsClassicView.CTRL_JOBS_FILTER
. - l'action
onSelectJobFilter
associée sur modification de la valeur. - Une aide définie dans le libellé
MSG_JOB_FILTER
Code java associé
La modification Java comporte deux parties :
- Initialisation de la liste de choix.
- Traiter le changement de sélection.
Afin de peupler la liste de choix, ceci s'effectue à l'initialisation afin de récupérer l'ensemble des sous répertoire sous /Temp/Jobs
private static final String QRY_JOBS_TRACE = "select r_object_id, object_name from dm_folder where folder('/Temp/Jobs')";
/**
* Add an option into a dropdownlist.
*
* @param dropDownList List to complete.
* @param value Option value.
* @param label Option label.
*/
private void addOption(DropDownList dropDownList, String value, String label) {
/* Build the option. */
Option option = new Option();
option.setValue(value);
option.setLabel(label);
/* Add it */
dropDownList.addOption(option);
}
/**
* Set job jobs name on the list.
*/
private void buildJobsFilter() {
DropDownList filterCtrl = (DropDownList) getControl(CTRL_JOBS_FILTER, com.documentum.web.form.control.DropDownList.class);
filterCtrl.setMutable(true);
filterCtrl.clearOptions();
this.addOption(filterCtrl, OPT_VAL_ALL, super.getString(MSG_FILTER_ALL));
IDfSession idfSession = super.getDfSession();
DfQuery dfQuery = new DfQuery();
dfQuery.setDQL(QRY_JOBS_TRACE);
IDfCollection idfCollection = null;
try {
idfCollection = dfQuery.execute(idfSession, IDfQuery.DF_EXECREAD_QUERY);
while (idfCollection.next()) {
this.addOption(filterCtrl, idfCollection.getString(DfDocbaseConstants.R_OBJECT_ID), idfCollection.getString(DfDocbaseConstants.OBJECT_NAME));
}
} catch (DfException dfException) {
LOGGER.error(LOG_FAILED_RUN_QRY, dfException);
} finally {
try {
idfCollection.close();
} catch (DfException dfException) {
LOGGER.error(LOG_FAILED_CLOSE_QRY, dfException);
}
}
}
/** {@inheritDoc} */
@Override
public void onInit(ArgumentList args) {
super.onInit(args);
/* Build the list for all jobs names, used to filter traces to display. */
this.buildJobsFilter();
}
A noter, ce n'est pas la plus belle de façon de construire la liste, mais cela fonctionne pas trop mal dans le cadre de l'article et pour le temps imparti. Les options sont construites avec :
- 1 entrée dont la valeur est
*
, et le label placé dans le fichier properties avec la cléMSG_FILTER_ALL
. - Les identifiants des sous répertoires, et le label le nom de ceux-ci.
Lors de la sélection d'une valeur, la méthode onSelectJobFilter
est appelée. Celle-ci ne fait que exécuter la méthode updateControl
en standard. En effet, celle-ci entraîne l'appel de la fonction getMyObjectsService
, durant laquelle la valeur de la liste est récupérée pour être fourni au service.
Conclusion
Des références au mode streamline sont disponibles dans l'exemple. Cela n'est qu'à but informatif et d'homogénéité avec le code standard. Ce mode n'étant plus affiché en standard, les modifications mises en place n'ont pas été testée.
La mise ne place du code source n'a nécessité qu'une demi journée de travail, prouvant ainsi les capacités élevées pour réutiliser ce composant et faire un affichage pratique. C'est sans aucun doute, un composant le plus simple et puissant qu'il puisse exister dans la nébuleuse Webtop.