Tri des types lors de l'import Webtop
Suite à une question sur le forum powerlink, cette extension a été réalisée afin de pouvoir trier les listes des types sélectionnables lors de l'importation de documents.
L'objectif de cette extension est donc de trier la liste, construite à partir du dictionnaire de données, en fonction du label du type. Cependant, cette extension ne permet pas de choisir les modes de tri, attribut et ordre, ce qui pourrait être réalisée dans une autre étude.
Sommaire
Votre avis
Nobody voted on this yet
|
|
Extension version 6.6
Le code explicité dans ce chapitre s'applique pour la version 6.6
de Webtop. Le code est disponible à l'adresse http://www.jouvinio.net/svn/study/branches/documentum/6.6/Webtop/Object%20types%20list%20ordered%20on%20import/
Analyse
Définition composant
Le composant import
est défini dans le fichier webcomponent/config/library/contenttransfer/importcontent/import_component.xml
avec la configuration suivante:
<component id="import">
<desc>
This is the WDK import component. Imports objects from the
client file system to the repository. The component must run
within the importcontainer or httpimportcontainer.
</desc>
<ucfrequired/>
<params>
<param name="objectId" required="true"/>
<param name="filenameWithPath" required="true"/>
<param name="localFilePath" required="false"/>
<param name="parentPath" required="false"/>
<param name="docbaseType" required="false"/>
<param name="isDirectory" required="false"/>
<param name="baseDocbaseType" required="false"/>
<param name="format" required="false"/>
<param name="defaultAttributesValues" required="false"/>
<param name="oleScanEnable" required="false"/>
</params>
<ignoreNullAttributeValue>true</ignoreNullAttributeValue>
<pages>
<start>/webcomponent/library/contenttransfer/importcontent/importContent.jsp</start>
<folder>/webcomponent/library/contenttransfer/importcontent/importFolder.jsp</folder>
</pages>
<class>com.documentum.webcomponent.library.contenttransfer.importcontent.ImportContent</class>
<nlsbundle>com.documentum.webcomponent.library.contenttransfer.importcontent.ImportContentNlsProp</nlsbundle>
<helpcontextid>import</helpcontextid>
<service>
<processor-class>com.documentum.webcomponent.library.contenttransfer.importcontent.ImportProcessor</processor-class>
</service>
<init-controls>
<control name="xmlCategoryListLabel" type="com.documentum.web.form.control.Label">
<init-property>
<property-name>enabled</property-name>
<property-value>true</property-value>
</init-property>
<init-property>
<property-name>visible</property-name>
<property-value>true</property-value>
</init-property>
</control>
<control name="xmlCategoryList" type="com.documentum.web.form.control.databound.DataDropDownList">
<init-property>
<property-name>enabled</property-name>
<property-value>false</property-value>
</init-property>
<init-property>
<property-name>visible</property-name>
<property-value>false</property-value>
</init-property>
</control>
<control name="bocswriteoption" type="com.documentum.web.form.control.Panel">
</control>
</init-controls>
<document-docbase-base-type>dm_document</document-docbase-base-type>
<document-docbase-type>dm_document</document-docbase-type>
<docbase-type-mappings>
<docbase-type-mapping>
<format>msg</format>
<type>dm_email_message</type>
</docbase-type-mapping>
</docbase-type-mappings>
<folder-docbase-type>dm_folder</folder-docbase-type>
<document_filter>
<preset_item id="type_filter">
<selection>
<entries>
<entry></entry>
</entries>
</selection>
</preset_item>
</document_filter>
<folder_filter>
<preset_item id="type_filter">
<selection>
<entries>
<entry></entry>
</entries>
</selection>
</preset_item>
</folder_filter>
<preset_item id="format_filter">
<selection>
<entries>
<entry></entry>
</entries>
</selection>
</preset_item>
<preset_item id='autoattributes'>
<attributes>
<attribute>
<name></name>
<values>
<value></value>
</values>
</attribute>
</attributes>
</preset_item>
<preset_item id="acl_filter">
<selection>
<entries>
<entry></entry>
</entries>
</selection>
</preset_item>
<preserve-file-extension>true</preserve-file-extension>
</component>
Les contrôles des pages sont construits au niveau de la classe d'instance. Il est donc nécessaire de l'étudier afin de comprendre comment la liste des types est peuplées.
Classe instance
Comme pour tout composant, la méthode onInit
est exécutée, et contient généralement les appels pour construire les composants inclus. Le contenu de cette méthode contient un appel à la méthode initControls
qui paraît intéressante de part son nom.
public void onInit(ArgumentList arg)
{
super.onInit(arg);
setFolderId(arg.get("objectId"));
setFilePath(arg.get("filenameWithPath"));
setParentPath(arg.get("parentPath"));
setObjectName(arg.get("objectName"));
setDocbaseType(arg.get("docbaseType"));
setBaseDocbaseType(arg.get("baseDocbaseType"));
setDirectory(Boolean.valueOf(arg.get("isDirectory")).booleanValue());
setDefaultFormat(arg.get("format"));
setDefaultOLEScan(Boolean.valueOf(arg.get("oleScanEnable")).booleanValue());
String strFormat = arg.get("format");
if(strFormat != null && strFormat.length() > 0)
{
DocbaseIcon icon = (DocbaseIcon)getControl("obj_icon", com/documentum/web/formext/control/docbase/DocbaseIcon);
if(icon != null)
{
icon.setFormat(strFormat);
icon.setType("dm_document");
}
}
String defaultAttrValues = arg.get("defaultAttributesValues");
if(defaultAttrValues != null)
setDefaultAttributesValues(ArgumentList.decode(defaultAttrValues));
String localFilePath = arg.get("localFilePath");
if(localFilePath != null && localFilePath.length() > 0)
{
File f = new File(localFilePath);
if(f.exists())
setLocalFilePath(f.getAbsolutePath());
}
if(isDirectory())
setComponentPage("folder");
setIgnoreNullAttributeValue();
initContext();
initControls();
}
Le contenu de la méthode initControls
n'est rien d'autre qu'un enchaînement d'exécution de méthodes. Encore une fois, le nom des méthodes va orienté l'analyse, et initTypeListControl
semble être à étudier.
protected void initControls()
{
initFormatWarningLabelControl();
initTypeListControl();
initDocbaseType();
initDropDownListControl();
initFormatControl();
initFilenameLabelControl();
initSelectedFormat();
initBocsWriteOptionsControls();
initDocbaseObjectControl();
initOLEScanPanel();
}
Enfin, la méthode initTypeListControl
est utilisée afin de construire une instance de ScrollableResultSet
qui est affectée à la liste des types disponibles, récupérée depuis la fonction getTypeListControl
.
protected void initTypeListControl()
{
m_typesResultset = createTypesResultSet();
DataDropDownList typeList = getTypeListControl(true);
typeList.getDataProvider().setScrollableResultSet(m_typesResultset);
}
A ce stade, le point d'entrée pour l'extension est identifié. La fonction createTypesResultSet
va appeler la fonction getTypes
en fonction du contexte de navigation, de la configuration et du type en cours de création, puisqu'il est possible d'importer un répertoire.
protected ScrollableResultSet createTypesResultSet()
{
try
{
String baseObjectType = getBaseDocbaseType();
String excludedSuperTypes[] = {
"dm_folder", "dmc_rm_formal_record", "dmc_rm_formal_rec_structure", "dmc_prm_physical_document"
};
String excludedFolderSuperTypes[] = {
"dmc_rm_formal_rec_cabinet", "dmc_rm_formal_rec_folder", "dmc_prm_physical_container", "dmc_rmc_thesaurus_category", "dmc_rmc_file", "dmc_rmc_file_part"
};
ScrollableResultSet resultSet;
if(baseObjectType != null && baseObjectType.length() > 0)
{
if(isDirectory())
resultSet = getTypes("folder_filter", baseObjectType, excludedFolderSuperTypes);
else
resultSet = getTypes("document_filter", baseObjectType, excludedSuperTypes);
} else
if(isDirectory())
resultSet = getTypes("folder_filter", "dm_folder", excludedFolderSuperTypes);
else
resultSet = getTypes("document_filter", getDocumentBaseType(), excludedSuperTypes);
return resultSet;
}
catch(DfException e)
{
throw new WrapperRuntimeException("Unable to query types from docbase!", e);
}
}
La fonction getTypes
est beaucoup plus complexe. La liste des types est mis dans un cache au niveau de la session HTTP, ce qui est déjà une amélioration par rapport aux versions 5 (au moins), car cela économise des requêtes. Si l'utilisateur affiche ce composant pour la première fois, le cache ne contient pas cette liste, et une requête est exécutée sur les types dmi_type_info
et dmi_dd_type_info
, à l'aide de la classe com.documentum.web.formext.docbase.TypeUtil
, qui ne sera pas étudiée dans le cadre de cet article. Une fois les types récupérés, ils sont stockés dans une instance de com.documentum.web.form.control.databound.TableResultSet
. Cette dernière est ensuite mise dans une instance de la classe privée CachedResultSet
implémentant l'interface com.documentum.web.form.control.databound.ScrollableResultSet
.
private ScrollableResultSet getTypes(String filterElementName, String supertype, String notSupertypes[])
throws DfException
{
CachedResultSet resultSet = null;
StringBuffer buf = new StringBuffer(255);
buf.append(supertype);
if(notSupertypes != null)
{
if(notSupertypes.length != 0)
buf.append(",");
int i = 0;
do
{
if(i >= notSupertypes.length)
break;
buf.append(notSupertypes[i++]);
if(i != notSupertypes.length)
buf.append(",");
} while(true);
}
String typeKey = buf.toString();
ServletRequest req = getPageContext().getRequest();
TransientObjectWrapper tw = (TransientObjectWrapper)req.getAttribute(TYPES_RESULTSET_REQ_CACHE);
Map typesCache;
if(tw != null && (typesCache = (Map)tw.get()) != null)
{
resultSet = (CachedResultSet)typesCache.get(typeKey);
} else
{
typesCache = new HashMap();
tw = new TransientObjectWrapper(typesCache);
req.setAttribute(TYPES_RESULTSET_REQ_CACHE, tw);
}
if(resultSet == null)
{
TableResultSet tmpResultSet = new TableResultSet(new String[] {
"type_name", "description", "label_text"
});
TypeSelector typeSelector = new TypeSelector(supertype, notSupertypes, getConfigBasePath(), filterElementName, "type_filter", getContext());
List typeItemList = TypeUtil.getTypeList(supertype, notSupertypes);
if(typeItemList.size() < 1)
typeItemList = TypeUtil.getTypeList(supertype, notSupertypes, "en");
List entries = new ArrayList();
com.documentum.web.formext.docbase.TypeUtil.TypeItem typeItem;
for(Iterator i$ = typeItemList.iterator(); i$.hasNext(); entries.add(new Entry(typeItem.getName(), typeItem.getDescription())))
typeItem = (com.documentum.web.formext.docbase.TypeUtil.TypeItem)i$.next();
typeSelector.setDefaultList(entries);
List typeLists = typeSelector.getSelection();
String typeName = null;
String description;
String labelText;
for(Iterator i$ = typeLists.iterator(); i$.hasNext(); tmpResultSet.add(new String[] {
typeName, description, labelText
}))
{
Entry entry = (Entry)i$.next();
typeName = entry.getName();
description = entry.getDescription();
labelText = (new StringBuilder()).append(description).append(" (").append(typeName).append(')').toString();
}
if(typeLists.size() == 1)
setDocbaseType(typeName);
resultSet = new CachedResultSet(tmpResultSet);
typesCache.put(typeKey, resultSet);
} else
{
resultSet = (CachedResultSet)resultSet.clone();
}
return resultSet;
}
Mise en place
Extension composant import
Une fois l'analyse réalisée, l'extension est particulièrement simple.
La configuration du composant doit être modifiée ainsi:
Elément configuration | Valeur | Description |
---|---|---|
class | fr.amexio.ejn.webcomponent.library.contenttransfer.importcontent.ImportContentSortTypes | Il est nécessaire de modifier la classe d'instance pour mettre en place le tri. |
La configuration est placée dans le fichier webtop/custom/config/webcomponent/library/contenttransfer/importcontent/import_component.xml
.
<component modifies="import:webcomponent/config/library/contenttransfer/importcontent/import_component.xml">
<replace path='class'>
<!-- Change class in order to sort object types list. -->
<class>fr.amexio.ejn.webcomponent.library.contenttransfer.importcontent.ImportContentSortTypes</class>
</replace>
</component>
La classe d'instance est modifiée afin de modifier l'instance
ScrollableResultSet
retournée. Comme son instance est une classe privée, il sera nécessaire de construire une nouvelle instance avec une classe publique, afin d'éviter d'en créer une nouvelle. De plus, il est souhaité de trier les informations, ce qui est disponible dans la classe com.documentum.web.form.control.databound.TableResultSet
.
Le fonctionnement est donc aussi simple que:
- Récupérer la position des différentes informations, label, nom et description. Ces positions seront utilisées afin de récupérer les valeur depuis la liste construite en standard.
- Parcourir la liste afin de convertir les valeurs dans un tableau d'objet. Attention, il faut que les informations soit placées exactement dans le même ordre que la définition des colonnes.
- Puis construction d'une instance de
TableResultSet</code, sur laquelle la fonction <code>sort
est exécutée pour trier les données dans l'ordre ascendant, argumentiSortDirection
égal à 0, sur la valeur elabel_text
et utilisant le mode de comparaison pour les chaînes de caractères, argumentiSortMode
égal à 0.
package fr.amexio.ejn.webcomponent.library.contenttransfer.importcontent;
import com.documentum.web.form.control.databound.ScrollableResultSet;
import com.documentum.web.form.control.databound.TableResultSet;
import com.documentum.webcomponent.library.contenttransfer.importcontent.ImportContent;
/**
* @author Etienne Jouvin (amexio)
*
*/
public class ImportContentSortTypes extends ImportContent {
private static final String COL_DESCRIPTION = "description";
private static final String COL_LABEL = "label_text";
private static final String COL_NAME = "type_name";
private static final long serialVersionUID = -8015469932944932339L;
/**
* Default constructor.
*/
public ImportContentSortTypes() {
super();
}
/** {@inheritDoc} */
@Override
protected ScrollableResultSet createTypesResultSet() {
/* Let parent class works. */
ScrollableResultSet scrollableResultSet = super.createTypesResultSet();
/* And now, we want to sort elements inside the result set. */
if (null != scrollableResultSet) {
/* Initialize the cursor position. */
scrollableResultSet.setCursor(-1);
/* Need to get column position, just in case something changed. */
/* Must add 1 in the position */
/* Positions will be used in function getObject with the column position, the value is decrease by one. */
int descriptionPos = scrollableResultSet.findColumn(COL_DESCRIPTION) + 1;
int labelTextPos = scrollableResultSet.findColumn(COL_LABEL) + 1;
int typeNamePos = scrollableResultSet.findColumn(COL_NAME) + 1;
/* And now parse each items */
String[] columns = new String[] { COL_NAME, COL_DESCRIPTION, COL_LABEL };
Object[][] items = new Object[scrollableResultSet.getResultsCount()][];
Object[] item;
int cpt = 0;
while (scrollableResultSet.next()) {
item = new Object[3];
/* Even if we know values are String, use String.valueOf just to not have to cast values. */
item[0] = scrollableResultSet.getObject(typeNamePos);
item[1] = scrollableResultSet.getObject(descriptionPos);
item[2] = scrollableResultSet.getObject(labelTextPos);
items[cpt] = item;
cpt++;
}
/* Build the result set. */
scrollableResultSet = new TableResultSet(items, columns);
/* Call the sort. */
/* iSortDirection (second argument) set to 0 to do sort ASC. */
/* iSortMode (third argument) set to 0 to do sort on string value. */
scrollableResultSet.sort(COL_LABEL, 0, 0);
}
return scrollableResultSet;
}
}
Attention, pour cette extension il n'y a pas de mise en place de cache. Ainsi le tri est effectué à chaque exécution de la fonction. Il serait possible de reproduire le mécanisme standard, mais cela implique la compréhension de code placé dans des classes et variables privées.