SubscribeOthers avec groupes Webtop
Suite à une question sur le forum powerlink, cette extension a été réalisée afin de pouvoir sélectionner des groupes lors de l'abonnement à un objet pour d'autres utilisateurs. Par défaut, il n'est possible de ne sélectionner que des utilisateurs, autre que l'utilisateur connecté, dans ce composant.
L'objectif de cette extension est donc de pouvoir sélectionner un(des) groupe(s) et d'abonner l'ensemble des utilisateurs de la sélection.
A noter que l'ajout ou suppression d'un utilisateur, dans un des groupes, n'est pas pris en compte. En effet, il serait beaucoup plus compliqué de mettre en place l'abonnement au niveau des groupes. Il faudrait alors modifier le service d'abonnement. De plus, la fonctionnalité de notifications sur les groupes ne fonctionne pas. En effet, ces notifications envoient un mail à l'adresse de l'utilisateur (soit le groupe) abonné sur les objets. Enfin, une personne du groupe pourrait supprimer l'abonnement, qui serait donc plus visible pour tous les autres utilisateurs.
Sommaire
Votre avis
Current user rating: 100/100 (1 votes)
|
|
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/Groups%20selection%20on%20subscribeothers/
Analyse
Définition action
L'action subscribeothers
est définie dans le fichier webtop/webcomponent/config/actions/dm_sysobject_actions.xml
avec la configuration suivante:
<action id="subscribeothers">
<params>
<param name="objectId" required="true"></param>
<param name="folderPath" required="false"></param>
</params>
<preconditions>
<precondition class="com.documentum.webcomponent.library.actions.SubscribeOthersPrecondition">
</precondition>
</preconditions>
<execution class="com.documentum.web.formext.action.RedirectActionExecution">
<dynamicfilter class="com.documentum.web.formext.action.LaunchComponentFilter">
<option>
<criteria>
<criterion name='isreference' value='true' evaluatorclass='com.documentum.webcomponent.library.actions.ReferenceEvaluator'/>
</criteria>
<selection>
<execution class="com.documentum.web.formext.action.LaunchComponent">
<component>informinvalidactionforreference</component>
</execution>
</selection>
</option>
<option>
<criteria>
</criteria>
<selection>
<execution class="com.documentum.web.formext.action.LaunchComponent">
<component>subscribeotherslocator</component>
<container>subscribeotherslocatorcontainer</container>
</execution>
</selection>
</option>
</dynamicfilter>
</execution>
<invocation>
<modalpopup>
<windowsize>large</windowsize>
<refreshparentwindow>onok</refreshparentwindow>
</modalpopup>
</invocation>
<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Developer's Comments
# Parameters
/* folderPath (Optional) Path to object
*/
/* objectId (Required) ID of the selected object
*/
#
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
</action>
L'action entraîne donc l'affichage du composant subscribeotherslocator
au sein du container subscribeotherslocatorcontainer
.
Container subscribeotherslocatorcontainer
Le container subscribeotherslocatorcontainer
est défini dans le fichier webtop/webcomponent/config/library/subscription/subscribeotherslocator_component.xml
avec la configuration suivante:
<component id="subscribeotherslocatorcontainer" extends="useronlylocatorcontainer:webcomponent/config/library/locator/useronlylocator_component.xml">
<desc>
Container used to display the component "subscribeotherslocator."
</desc>
<params>
<param name="componentArgs" required="true"/>
</params>
<contains>
<component>subscribeotherslocator</component>
</contains>
<class>com.documentum.webcomponent.library.subscription.SubscribeOthersLocatorContainer</class>
<nlsbundle>com.documentum.webcomponent.library.subscription.SubscribeOthersLocatorContainerNlsProp</nlsbundle>
</component>
Ce container est une simple extension du container useronlylocatorcontainer
utilisé pour la sélection d'utilisateur uniquement. Il n'y a rien de particulier dans ce container. Il est juste nécessaire de comprendre la prise en compte de la sélection pour la création des abonnements. Ceci s'effectue dans la méthode onOk
dont le code est:
public void onOk(Control button, ArgumentList args)
{
if(canCommitChanges() && onCommitChanges())
{
ArrayList components = getContainedComponents();
SubscribeOthersLocator subscribeOthers = (SubscribeOthersLocator)components.get(0);
LocatorItemResultSet selectedItems = subscribeOthers.getSelections();
if(selectedItems.getResultsCount() > 0)
processSelectionResults(subscribeOthers.getSelections());
if(getTopForm().getCallerForm() != null)
setComponentReturn();
}
}
A noter le cast du composant inclus en SubscribeOthersLocator
. Lors de l'extension de ce composant, il faudra donc hérité de cette classe.
Composant subscribeotherslocator
Le composant subscribeotherslocator
est défini dans le fichier webtop/webcomponent/config/library/subscription/subscribeotherslocator_component.xml
avec la configuration suivante:
<component id="subscribeotherslocator" extends="useronlylocator:webcomponent/config/library/locator/useronlylocator_component.xml">
<desc>
Locator that displays all users except the current user; used
by a user to subscribe other users to objects.
</desc>
<!-- set a value of "true" to give a flat list of all selectable objects -->
<flatlist>true</flatlist>
<views>
<view applyto="5.0">
<queryfiltersets>
<queryfilterset>
<queryfilter>
<!-- show just the users (i.e. no groups, no roles)-->
<displayname>
<nlsid>MSG_HIDE_GROUPS</nlsid>
</displayname>
<excludetypes>dm_group</excludetypes>
</queryfilter>
</queryfilterset>
</queryfiltersets>
</view>
<view applyto="4.x">
<queryfiltersets>
<queryfilterset>
<queryfilter>
<!-- show just the users (i.e. no groups, no roles)-->
<displayname>
<nlsid>MSG_HIDE_GROUPS</nlsid>
</displayname>
<excludetypes>dm_group</excludetypes>
</queryfilter>
</queryfilterset>
</queryfiltersets>
</view>
</views>
<class>com.documentum.webcomponent.library.subscription.SubscribeOthersLocator</class>
<helpcontextid>subscribeotherslocator</helpcontextid>
</component>
Il n'y a rien de particulier au niveau de cette configuration. Les filtres sont définis afin de n'afficher que les utilisateurs, configuration du noeud views
. La configuration de flatlist
est utilisée afin d'empêcher la navigation dans les éléments, lorsque ceux-ci contiennent des fils.
A noter l'extension du composant useronlylocator
, qui entraîne l'impossibilité de sélectionner les éléments "container", soit les groupes.
<component id="useronlylocator" extends="userorgrouplocator:webcomponent/config/library/locator/userorgrouplocator_component.xml">
<desc>
Locates users in a repository. The component allows users to
navigate from user groups to find a user. This component extends
the component userorgrouplocator.
</desc>
<class>com.documentum.webcomponent.library.locator.UserOrGroupLocator</class>
<nlsbundle>com.documentum.webcomponent.library.locator.UserOrGroupLocatorNlsProp</nlsbundle>
<objecttype>dm_user</objecttype>
<containerselectable>false</containerselectable>
<privategroupvisible>false</privategroupvisible>
<helpcontextid>useronlylocator</helpcontextid>
</component>
L'analyse de la classe com.documentum.webcomponent.library.subscription.SubscribeOthersLocator
va permettre d'identifier des points à ne pas oublier. Le contenu de la classe est très simple:
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: SubscribeOthersLocator.java
package com.documentum.webcomponent.library.subscription;
import com.documentum.webcomponent.library.locator.LocatorQuery;
import com.documentum.webcomponent.library.locator.UserOrGroupLocator;
// Referenced classes of package com.documentum.webcomponent.library.subscription:
// SubscribeOthersLocatorQuery
public class SubscribeOthersLocator extends UserOrGroupLocator
{
public SubscribeOthersLocator()
{
}
protected LocatorQuery createQuery()
{
SubscribeOthersLocatorQuery query = new SubscribeOthersLocatorQuery();
query.setPrivateGroupVisible(getIsPrivateGroupVisible());
query.setWorkQueueGroupVisible(getIsWorkQueueGroupVisible());
return query;
}
}
Il n'y a donc pas grand chose dans celle-ci, mise à part l'utilisation d'une classe particulière pour la génération de la requête, qui est aussi très simple.
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: SubscribeOthersLocatorQuery.java
package com.documentum.webcomponent.library.subscription;
import com.documentum.web.form.query.Clause;
import com.documentum.web.form.query.ParsedExpression;
import com.documentum.webcomponent.library.locator.UserOrGroupLocatorQuery;
import java.util.ArrayList;
public class SubscribeOthersLocatorQuery extends UserOrGroupLocatorQuery
{
public SubscribeOthersLocatorQuery()
{
}
protected String prepareWhereClauseSnippet()
{
ParsedExpression expression = new ParsedExpression("a.user_name != USER");
m_vectWhereClauses.add(new Clause(expression, "AND"));
return super.prepareWhereClauseSnippet();
}
}
Le code parle de lui même. Lors de la génération de la requête un filtre est ajouté pour ne pas retourner l'utilisation dont la valeur de user_name
est égale à USER
, soit l'utilisateur connecté.
Ce point est particulièrement important car l'extension doit afficher les groupes également. Le filtre serait donc invalide, puisque le type dm_group
ne contient pas l'attribut user_name
.
Mise en place
Extension composant subscribeotherslocator
Une fois l'analyse réalisée, l'extension est assez simple.
La configuration du composant doit être modifiée ainsi:
Elément configuration | Valeur | Description |
---|---|---|
class | fr.amexio.ejn.webcomponent.library.subscription.SubscribeOthersWithGroupsLocator | Il est nécessaire de modifier la classe d'instance pour mettre en place une requête spécifique. |
containerselectable | true | Il faut permettre la sélection des éléments "container", soit les groupes. |
flatlist | false | Il faut pouvoir entrer dans la composition des groupes. |
views | ... | Configuration des filtres du locator pour afficher les utilisateurs et les groupes. |
Ce type de configuration est déjà disponible au sein de Webtop, pour le composant userorgrouplocator
dans le fichier webtop/webcomponent/config/library/locator/userorgrouplocator_component.xml
. Il va donc suffire de recopier cette configuration.
La configuration est placée dans le fichier webtop/custom/config/webcomponent/library/subscription/subscribeotherslocator_component.xml
. Il n'est pas possible d'utiliser la directive component modifies
car les différentes modifications à mettre en place se situent dans l'ensemble de la hierarchie de configuration. La configuration devient donc:
<component id="subscribeotherslocator" extends="subscribeotherslocator:webcomponent/config/library/subscription/subscribeotherslocator_component.xml">
<!-- Need to replace the component class. -->
<!-- Because in com.documentum.webcomponent.library.subscription.SubscribeOthersLocator, defined in parent XML, -->
<!-- a filter is add in the query (SubscribeOthersLocatorQuery instance) on the user_name different to USER, -->
<!-- in order to filter connected user. -->
<!-- But the instance must extends this class, because there is a casting in the container -->
<class>fr.amexio.ejn.webcomponent.library.subscription.SubscribeOthersWithGroupsLocator</class>
<!-- Revert definition in useronlylocator component. We want to be able to select groups, container object. -->
<containerselectable>true</containerselectable>
<!-- Revert definition in subscribeotherslocator component. We want to be able to go into groups. -->
<flatlist>false</flatlist>
<views>
<view applyto="5.0">
<queryfiltersets>
<queryfilterset>
<queryfilter>
<displayname>
<nlsid>MSG_SHOW_ALL_50</nlsid>
</displayname>
<containertypes>dm_group</containertypes>
</queryfilter>
<queryfilter>
<displayname>
<nlsid>MSG_HIDE_GROUPS</nlsid>
</displayname>
<excludetypes>dm_group</excludetypes>
</queryfilter>
<queryfilter>
<displayname>
<nlsid>MSG_HIDE_USERS</nlsid>
</displayname>
<containertypes>dm_group</containertypes>
<includetypes>dm_group</includetypes>
<attributefilters>
<attributefilter>
<attribute>group_class</attribute>
<predicate>ne</predicate>
<value dqlformatted="false">role</value>
</attributefilter>
</attributefilters>
</queryfilter>
<queryfilter>
<displayname>
<nlsid>MSG_SHOW_ROLES</nlsid>
</displayname>
<containertypes>dm_group</containertypes>
<includetypes>dm_group</includetypes>
<attributefilters>
<attributefilter>
<attribute>group_class</attribute>
<predicate>eq</predicate>
<value dqlformatted="false">role</value>
</attributefilter>
</attributefilters>
</queryfilter>
</queryfilterset>
</queryfiltersets>
</view>
<view applyto="4.x">
<queryfiltersets>
<queryfilterset>
<queryfilter>
<displayname>
<nlsid>MSG_SHOW_ALL_4X</nlsid>
</displayname>
<containertypes>dm_group</containertypes>
</queryfilter>
<queryfilter>
<displayname>
<nlsid>MSG_HIDE_GROUPS</nlsid>
</displayname>
<excludetypes>dm_group</excludetypes>
</queryfilter>
<queryfilter>
<displayname>
<nlsid>MSG_HIDE_USERS</nlsid>
</displayname>
<!-- show groups before users, groups are navigatable -->
<containertypes>dm_group</containertypes>
<includetypes>dm_group</includetypes>
</queryfilter>
</queryfilterset>
</queryfiltersets>
</view>
</views>
</component>
La classe d'instance est modifiée afin de modifier la classe utilisée pour générer la requête. Il ne faut ajouter le filtre sur le nom des utilisateurs uniquement lorsque la clause from
de la sous requête est dm_user
. L'extension est donc aussi simple que la classe originale.
/*
* 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.
*/
package fr.amexio.ejn.webcomponent.library.subscription;
import com.documentum.webcomponent.library.locator.LocatorQuery;
import com.documentum.webcomponent.library.subscription.SubscribeOthersLocator;
/**
* @author Etienne Jouvin (amexio)
*
*/
public class SubscribeOthersWithGroupsLocator extends SubscribeOthersLocator {
private static final long serialVersionUID = 1991616416006992854L;
/**
* Default constructor.
*/
public SubscribeOthersWithGroupsLocator() {
}
/** {@inheritDoc} */
@Override
protected LocatorQuery createQuery() {
SubscribeOthersWithGroupsLocatorQuery query = new SubscribeOthersWithGroupsLocatorQuery();
query.setPrivateGroupVisible(getIsPrivateGroupVisible());
query.setWorkQueueGroupVisible(getIsWorkQueueGroupVisible());
return query;
}
}
L'utilisation est exactement la même que le standard, seule la classe utilisée est modifiée.
/*
* 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.
*/
package fr.amexio.ejn.webcomponent.library.subscription;
import com.documentum.web.form.query.Clause;
import com.documentum.web.form.query.LogicalOperator;
import com.documentum.web.form.query.ParsedExpression;
import com.documentum.webcomponent.library.locator.UserOrGroupLocatorQuery;
/**
* @author Etienne Jouvin (amexio)
*
*/
public class SubscribeOthersWithGroupsLocatorQuery extends UserOrGroupLocatorQuery {
private static final String FILTER_CONNECTED_USER = "a.user_name != USER";
private static final String FROM_CLAUSE_USER = "dm_user a";
private static final long serialVersionUID = -8993053515216601870L;
/**
* Default constructor.
*/
public SubscribeOthersWithGroupsLocatorQuery() {
}
/** {@inheritDoc} */
@SuppressWarnings("unchecked")
@Override
protected String prepareWhereClauseSnippet() {
String fromClause = super.getFromClause();
if (FROM_CLAUSE_USER.equals(fromClause)) {
/* From clause is about user. Add filer on user name. */
/* Do same thing than in com.documentum.webcomponent.library.subscription.SubscribeOthersLocatorQuery. */
/* But the filter is added in any case. */
/* Because the component is updated in order to be able to select groups, */
/* the code will generate an invalid query. */
ParsedExpression expression = new ParsedExpression(FILTER_CONNECTED_USER);
m_vectWhereClauses.add(new Clause(expression, LogicalOperator.AND));
}
return super.prepareWhereClauseSnippet();
}
}
Par rapport au standard, le filtre n'est ajouté que si la valeur de la clause from
est dm_user a
. Cette valeur est mise à jour lors de la construction des requêtes dans les fonctions getContainerViewStatement
et getRootViewStatement
de la classe com.documentum.webcomponent.library.locator.UserOrGroupLocatorQuery
. Ces fonctions construisent les deux parties de la requête afin de retourner les groupes et / ou les utilisateurs.
protected String getRootViewStatement()
{
boolean fUnion = false;
StringBuffer bufStatement = new StringBuffer(512);
if(!isTypeExcluded("dm_group"))
{
fUnion = true;
setFromClause("dm_group a");
ExpressionSet predicate = new ExpressionSet();
if(!isPrivateGroupVisible())
predicate.addExpression("AND", new ParsedExpression("(a.is_private=0 or a.owner_name=user)"));
if(m_fIs53Docbase)
{
predicate.addExpression("AND", new ParsedExpression("(a.group_native_room_id = '0000000000000000')"));
if(!isWorkQueueGroupVisible())
predicate.addExpression("AND", new ParsedExpression("(a.group_class IS NULL OR a.group_class!='queue')"));
if(!getShowUnlistedUsers())
predicate.addExpression("AND", new ParsedExpression("(a.group_name!='dce_hidden_users')"));
}
if(predicate.getExpressionSet().size() == 0)
setBaseViewExpression(null);
else
setBaseViewExpression(predicate);
processNameStartsWith("a.group_name");
setGroupSelectAttributes();
bufStatement.append("SELECT").append(" ").append(getSelectForPermitSnippet()).append(" ").append(getSelectType()).append(" ");
bufStatement.append(prepareSelectValuesSnippet());
bufStatement.append(" ").append("FROM").append(" ");
bufStatement.append(getFromClause());
bufStatement.append(" ");
bufStatement.append(prepareWhereClauseSnippet());
String appendQry = getPresetEntriesAsQueryString();
if(appendQry != null && appendQry.length() > 0)
{
bufStatement.append(" ");
bufStatement.append("AND");
bufStatement.append(" ");
bufStatement.append("a.group_name in");
bufStatement.append(" ");
bufStatement.append(appendQry);
}
}
if(getViewDocbaseType().equals("dm_user"))
{
setFromClause("dm_user a");
setBaseViewExpression(new ParsedExpression("a.r_is_group=0"));
processNameStartsWith("a.user_name");
if(fUnion)
bufStatement.append(" union ");
setUserSelectAttributes();
bufStatement.append("SELECT").append(" ").append(getSelectForPermitSnippet()).append(" ").append(getSelectType()).append(" ");
bufStatement.append(prepareSelectValuesSnippet());
bufStatement.append(" ").append("FROM").append(" ");
bufStatement.append(getFromClause());
bufStatement.append(" ");
bufStatement.append(prepareWhereClauseSnippet());
String appendQry = getPresetEntriesAsQueryString();
if(appendQry != null && appendQry.length() > 0)
{
bufStatement.append(" ");
bufStatement.append("AND");
bufStatement.append(" ");
bufStatement.append("a.user_name IN (Select i_all_users_names from dm_group where group_name in ");
bufStatement.append(appendQry);
bufStatement.append(")");
}
if(!getShowUnlistedUsers())
bufStatement.append(" AND a.user_name NOT IN (Select i_all_users_names from dm_group where group_name='dce_hidden_users')");
}
removeAllOrderByAttributes();
addOrderByAttribute("idunion", "ASC");
addOrderByAttribute("objname", "ASC");
bufStatement.append(" ");
bufStatement.append(prepareOrderBySnippet());
return bufStatement.toString();
}
protected String getContainerViewStatement()
{
boolean fUnion = false;
StringBuffer bufStatement = new StringBuffer(512);
if(!isTypeExcluded("dm_group"))
{
fUnion = true;
setFromClause("dm_group a");
String strBaseExpression = "";
if(!isPrivateGroupVisible())
strBaseExpression = "(a.is_private=0 or a.owner_name=user) and ";
if(m_fIs53Docbase && !isWorkQueueGroupVisible())
strBaseExpression = (new StringBuilder()).append(strBaseExpression).append("(a.group_class IS NULL OR a.group_class!='queue') and ").toString();
strBaseExpression = (new StringBuilder()).append(strBaseExpression).append(getGroupBaseExpression()).toString();
setBaseViewExpression(new ParsedExpression(strBaseExpression));
processNameStartsWith("a.group_name");
setGroupSelectAttributes();
bufStatement.append("SELECT").append(" ").append(getSelectForPermitSnippet()).append(" ").append(getSelectType()).append(" ");
bufStatement.append(prepareSelectValuesSnippet());
bufStatement.append(" ").append("FROM").append(" ");
bufStatement.append(getFromClause());
bufStatement.append(" ");
bufStatement.append(prepareWhereClauseSnippet());
}
if(getViewDocbaseType().equals("dm_user"))
{
setFromClause("dm_user a");
String strBaseExpression = "";
if(!isPrivateGroupVisible())
strBaseExpression = "a.r_is_group=0 and ";
strBaseExpression = (new StringBuilder()).append(strBaseExpression).append(getUserBaseExpression()).toString();
setBaseViewExpression(new ParsedExpression(strBaseExpression));
processNameStartsWith("a.user_name");
if(fUnion)
bufStatement.append(" union ");
setUserSelectAttributes();
bufStatement.append("SELECT").append(" ").append(getSelectForPermitSnippet()).append(" ").append(getSelectType()).append(" ");
bufStatement.append(prepareSelectValuesSnippet());
bufStatement.append(" ").append("FROM").append(" ");
bufStatement.append(getFromClause());
bufStatement.append(" ");
bufStatement.append(prepareWhereClauseSnippet());
}
removeAllOrderByAttributes();
addOrderByAttribute("idunion", "ASC");
addOrderByAttribute("objname", "ASC");
bufStatement.append(" ");
bufStatement.append(prepareOrderBySnippet());
return bufStatement.toString();
}
Extension container subscribeotherslocatorcontainer
La dernière modification a mettre en place concerne le container, afin qu'il soit capable de transformer les identifiants des groupes sélectionnés par l'identifiant des utilisateurs contenus dans ceux-ci.
La configuration du composant doit être modifiée ainsi:
Elément configuration | Valeur | Description |
---|---|---|
class | fr.amexio.ejn.webcomponent.library.subscription.SubscribeOthersWithGroupsLocatorContainer | Seul la modification de la classe d'instance est nécessaire. |
Contrairement au composant, il est possible d'utiliser la directive component modifies
, car les modifications ne concernent des noeuds dans une unique définition.
La configuration devient donc:
<component modifies="subscribeotherslocatorcontainer:webcomponent/config/library/subscription/subscribeotherslocator_component.xml">
<replace path='class'>
<!-- Change the container class in order to manage group selection. -->
<class>fr.amexio.ejn.webcomponent.library.subscription.SubscribeOthersWithGroupsLocatorContainer</class>
</replace>
</component>
La classe d'instance est modifiée afin de convertir les identifiants de groupes en identifiant d'utilisateurs. Ceci doit être réalisé avant la mise en place des abonnements, et donc durant l'exécution de la fonction onOk
. Pour cela, le composant contenu est récupéré, comme dans la classe parente, mais avec un casting suffisant pour utiliser les fonctions nécessaires.
La fonction convertGroups
est appelée avec le composant. L'ensemble des identifiants sélectionnés, retournés par la fonction getSelections
, est parcouru afin d'extraire ceux concernant des groupes et en construisant la valeur pour un filtre dans la clause where
avec l'opérateur in
.
Une fois le filtre construit, la méthode convertGroupsToUsers
est appelée pour exécutée une requête DQL retournant les utilisateurs des groupes, en se basant sur l'attribut i_all_users_names
. La requête est du type
select dm_user.r_object_id
from dm_user, dm_group
where any dm_group.i_all_users_names = dm_user.user_name and
dm_group.r_object_id in ( ... ) and
dm_user.user_name != user
A noter l'utilisation du filtre dm_user.user_name != user
afin de ne pas récupérer l'identifiant de l'utilisateur connecté si il se trouve dans un des groupes sélectionnés.
Attention Du fait de l'utilisation de cette requête, il existe une limitation sur le nombre de groupes sélectionnés. L'opérateur in possède une restriction à 1000 éléments, ce qui est acceptable dans ce cas. Qui va sélectionner 1000 groupes ?
Chacun des identifiants retournés est ajouté ensuite dans la sélection du composant.
/*
* 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.
*/
package fr.amexio.ejn.webcomponent.library.subscription;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import com.documentum.fc.client.DfQuery;
import com.documentum.fc.client.IDfCollection;
import com.documentum.fc.client.IDfQuery;
import com.documentum.fc.client.IDfSession;
import com.documentum.fc.common.DfDocbaseConstants;
import com.documentum.fc.common.DfException;
import com.documentum.fc.common.DfId;
import com.documentum.fc.impl.util.StringUtil;
import com.documentum.web.common.ArgumentList;
import com.documentum.web.form.Control;
import com.documentum.webcomponent.library.locator.LocatorItemResultSet;
import com.documentum.webcomponent.library.locator.ObjectLocator;
import com.documentum.webcomponent.library.subscription.SubscribeOthersLocatorContainer;
/**
* @author Etienne Jouvin (amexio)
*
*/
public class SubscribeOthersWithGroupsLocatorContainer extends SubscribeOthersLocatorContainer {
private static final String LOG_WARN_CLOSE_QUERY = "Failed to close query";
private static final String LOG_WARN_RUN_QUERY = "Failed to run query";
private static final Logger LOGGER = Logger.getLogger(SubscribeOthersWithGroupsLocatorContainer.class);
private static final String QUERY_ALL_USERS = "select dm_user.r_object_id "
/* Build from clause. */
+ "from dm_user, dm_group "
/* Build where clause. Link all users names from group to user_name from user. */
+ "where any dm_group.i_all_users_names = dm_user.user_name and "
/* Filter groups by the id. */
+ "dm_group.r_object_id in ({0}) "
/* Component used to subscribe other users. Need to remove the connected user from the result. */
+ "and dm_user.user_name != user";
private static final long serialVersionUID = -6256477084651474924L;
/**
* Extract groups id from the object selector. Remove each group id and build a clause for filter r_object_id in (...).
*
* @param objectLocator Source component.
* @return Groups ids filter for query.
*/
private String convertGroups(ObjectLocator objectLocator) {
StringBuffer groupsFilter = new StringBuffer();
/* Get selected items. */
LocatorItemResultSet selectedItems = objectLocator.getSelections();
int selectedCnt = null == selectedItems ? 0 : selectedItems.getResultsCount();
if (selectedCnt > 0) {
/* There is some selection. Need to parse all selected item to get dm_group object_id. */
List<String> groupIds = new ArrayList<String>(selectedCnt);
/* Like done in parent class, put the index in resultset to first position. */
selectedItems.first();
String objectId;
do {
/* Get the object id. */
objectId = selectedItems.getPersistentObjectId();
if (DfId.isObjectId(objectId) && objectId.charAt(0) == '1' && objectId.charAt(1) == '2') {
/* Just in case... but this is a valid DCTM object id. */
/* And it is a group r_object_id. */
if (groupsFilter.length() != 0) {
/* Not first time, must put a comma. */
groupsFilter.append(',');
}
/* Build the filter on the group id. */
groupsFilter.append('\'').append(objectId).append('\'');
groupIds.add(objectId);
}
} while (selectedItems.next());
/* Remove all group ids from the selectedItems. */
for (String groupId : groupIds) {
selectedItems.remove(groupId);
}
}
return groupsFilter.toString();
}
/**
* Run a query to get all users ids from groups, identified by a query filter. For each user, add them in the object locator selected items.
*
* @param objectLocator
* @param groupsFilter
*/
private void convertGroupsToUsers(ObjectLocator objectLocator, String groupsFilter) {
if (!StringUtil.isEmptyOrNull(groupsFilter)) {
/* The built filter is not empty. Must run the query to get users ids. */
/* Now build the query to get groups. */
String query = MessageFormat.format(QUERY_ALL_USERS, groupsFilter.toString());
LocatorItemResultSet selectedItems = objectLocator.getSelections();
/* Initialize the query. */
IDfQuery idfQuery = new DfQuery();
idfQuery.setDQL(query);
IDfSession idfSession = super.getDfSession();
IDfCollection idfCollection = null;
try {
String userId;
for (idfCollection = idfQuery.execute(idfSession, IDfQuery.DF_EXECREAD_QUERY); idfCollection.next();) {
/* For each result, get the user id and store it in the map if not already inside. */
userId = idfCollection.getString(DfDocbaseConstants.R_OBJECT_ID);
/* Add the user id in the list. If already selected, the add function will check it and do not add if already selected. */
selectedItems.add(userId);
}
} catch (DfException dfException) {
LOGGER.warn(LOG_WARN_RUN_QUERY, dfException);
} finally {
try {
idfCollection.close();
} catch (DfException dfException) {
LOGGER.warn(LOG_WARN_CLOSE_QUERY, dfException);
}
}
}
}
/** {@inheritDoc} */
@Override
public void onOk(Control button, ArgumentList args) {
/* Get contained component. */
ArrayList<?> components = this.getContainedComponents();
Object component = null == components || components.size() == 0 ? null : components.get(0);
if (component instanceof ObjectLocator) {
/* Correct instance. cast it. */
ObjectLocator objectLocator = (ObjectLocator) component;
/* Call groups id extraction. The function return all groups id for query filter. */
String groupsFilter = this.convertGroups(objectLocator);
/* Convert groups ids to users in the selected item. */
this.convertGroupsToUsers(objectLocator, groupsFilter);
}
super.onOk(button, args);
}
}