Réutilisation composant MyObjects Webtop

De EjnTricks

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.

Hand-icon.png Votre avis

Nobody voted on this yet

 You need to enable JavaScript to vote


Version 6.7 SP1

Le code est disponible à l'adresse http://www.jouvinio.net/svn/study/branches/documentum/6.7_SP1/Webtop/MyObjects%20Extension/

XML format icon.png 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:

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.


Java format icon.png 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.

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);
	}


Webtop Component Icon.png Composant browsertree

Le composant myfiles est accessible depuis l'arbre de navigation, ce qu'il faut reproduire.

XML format icon.png 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.

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.

Webtop Component Icon.png 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.

XML format icon.png 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.

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:


XML format icon.png Fichier XML du composant

La page jsp a donc été modifiée au niveau de la définition du composant mylogs_classic

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 :

En fait, cette page ne fait que inclure une autre, que nous devons recopier sous /custom/webcomponent/library/myobjects/mylogs_list_body.jsp.

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

Java format icon.png 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.


Warning-icon.png 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.