Resolved: [Not a] Bug with user mail settings and receiveMails?
Is there possibly a backend issue with this field? I'm going to be making a bunch of updates soon, and to avoid flooding mailboxes I'm modifying many users in the same manner, and don't want to entirely lose email updates to all developers.
Some sample code (though I do a little abstracting of the RTC API, so I can't post the whole thing):
private static final String MAIL_SETTINGS = "com.ibm.team.workitem.mail.Configuration";
IContributorDetails details = (IContributorDetails)getContributorDetails(contributorWC).getWorkingCopy();
String settings = details.getLargeStringExtension(MAIL_SETTINGS);
settings = settings.replace("<receiveMails value=\"false\"/>", "<receiveMails value=\"true\"/>");
details.setLargeStringExtension(MAIL_SETTINGS, settings);
mgr.getContributorManager().setContributorDetails(getContributorHandle(), details, null);
Accepted answer
One other answer
takes a file of userids (1 per line)
or an xml file (you might have one, but have to update the format).
the code figures out what kind of source file it is.
| updated to use product provided constants instead of hard coded literals.
| added support for decypting password
| see the second answer here https://jazz.net/forum/questions/156705/is-there-support-for-password-files-in-the-plain-java-client-api
| for info on encrypting the password
<code>
package com.sd.test;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import javax.xml.parsers.DocumentBuilderFactory;
import org.eclipse.core.runtime.CoreException;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import com.ibm.team.foundation.common.util.IMemento;
import com.ibm.team.foundation.common.util.XMLMemento;
import com.ibm.team.repository.client.IContributorManager;
import com.ibm.team.repository.client.IItemManager;
import com.ibm.team.repository.client.ITeamRepository;
import com.ibm.team.repository.client.TeamPlatform;
import com.ibm.team.repository.client.ITeamRepository.ILoginHandler;
import com.ibm.team.repository.client.ITeamRepository.ILoginHandler.ILoginInfo;
import com.ibm.team.repository.common.IContributor;
import com.ibm.team.repository.common.IContributorDetails;
import com.ibm.team.repository.common.IContributorDetailsHandle;
import com.ibm.team.repository.common.IExtensibleItem;
import com.ibm.team.repository.common.ItemNotFoundException;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.workitem.common.internal.mailconfig.IMailConfigurationConstants;
import com.ibm.team.repository.common.util.ObfuscationHelper;
public class BulkConfigureContributorEmail implements IMailConfigurationConstants
{
// our hash key to save the content
private static final String CONFIG_KEY_ORIGINAL = CONFIG_KEY+".original";
// the default string, cut/pasted
private static final String DefaultSettings="default";
private static final String DefaultSettingsString=
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<mailconfiguration version=\"0.6\">\r\n<"+CONFIG_SCOPE_GLOBAL+">\r\n<"+CONFIG_DOMAIN_WORK_ITEMS+">\r\n<"+CONFIG_RECEIVE_MAILS+" value=\"true\"/>\r\n<"+CONFIG_MAIL_WHEN_SUBSCRIBED+" value=\"true\"/>\r\n<filter>\r\n<CREATOR>\r\n<COMMENT_ADDED/>\r\n<SUMMARY_DESCRIPTION_CHANGED/>\r\n<SEVERITY_PRIORITY_TARGET_CHANGED/>\r\n<OWNER_CHANGED/>\r\n<TAG_CHANGED/>\r\n<LINKS_ADDED_REMOVED/>\r\n<WORK_ITEM_CREATED_REOPENED/>\r\n<WORK_ITEM_COMPLETED/>\r\n<OTHER_STATE_CHANGE/>\r\n<OTHER_CHANGE/>\r\n</CREATOR>\r\n<OWNER>\r\n<COMMENT_ADDED/>\r\n<SUMMARY_DESCRIPTION_CHANGED/>\r\n<SEVERITY_PRIORITY_TARGET_CHANGED/>\r\n<OWNER_CHANGED/>\r\n<TAG_CHANGED/>\r\n<LINKS_ADDED_REMOVED/>\r\n<WORK_ITEM_CREATED_REOPENED/>\r\n<WORK_ITEM_COMPLETED/>\r\n<OTHER_STATE_CHANGE/>\r\n<OTHER_CHANGE/>\r\n</OWNER>\r\n<SUBSCRIBER>\r\n<COMMENT_ADDED/>\r\n<SUMMARY_DESCRIPTION_CHANGED/>\r\n<SEVERITY_PRIORITY_TARGET_CHANGED/>\r\n<OWNER_CHANGED/>\r\n<TAG_CHANGED/>\r\n<LINKS_ADDED_REMOVED/>\r\n<WORK_ITEM_CREATED_REOPENED/>\r\n<WORK_ITEM_COMPLETED/>\r\n<OTHER_STATE_CHANGE/>\r\n<OTHER_CHANGE/>\r\n</SUBSCRIBER>\r\n</filter>\r\n</"+CONFIG_DOMAIN_WORK_ITEMS+">\r\n<"+CONFIG_DOMAIN_MESSAGES_AT_ME+">\r\n<"+CONFIG_MAIL_MESSAGES_AT_ME+" value=\"true\"/>\r\n</"+CONFIG_DOMAIN_MESSAGES_AT_ME+">\r\n<approvals>\r\n<"+CONFIG_RECEIVE_MAILS+" value=\"true\"/>\r\n<receiveOwnChanges value=\"false\"/>\r\n</approvals>\r\n<format>\r\n<htmlMail value=\"false\"/>\r\n</format>\r\n</"+CONFIG_SCOPE_GLOBAL+">\r\n</mailconfiguration>";
// the identifier for the xml content to get to a user entry
private static final String XMLElementName = "Row";
private static final String UserIDElementName ="ID";
private static final String operationDisable = "disable";
private static final String operationRestore = "restore";
private static class LoginHandler implements ILoginHandler, ILoginInfo
{
private String fUserId;
private String fPassword;
private LoginHandler(String userId, String password)
{
fUserId = userId;
fPassword = password;
}
public String getUserId()
{
return fUserId;
}
public String getPassword()
{
return fPassword;
}
public ILoginInfo challenge(ITeamRepository repository)
{
return this;
}
}
/**
* @param args
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
String repositoryURI = args[0];
String AdminUserId = args[1];
String password = args[2];
String UserlistFile = args[3];
String operation = args[4];
TeamPlatform.startup();
ITeamRepository teamRepository = TeamPlatform
.getTeamRepositoryService().getTeamRepository(repositoryURI);
try
{
// decrypt the user password
password = ObfuscationHelper.decryptString(password);
}
catch(Exception ex)
{
// nothin to do, variable not overlayed on error
}
teamRepository.registerLoginHandler(new LoginHandler(AdminUserId, password));
// get the list of users whose email config needs to be changed
// might be flat file of ids, or xml doc Rows><Row <id>name</id>
// get the list ready here, if that fails. don't continue
ArrayList<String> userids = getUseridList(UserlistFile);
if(userids.size()>0)
{
try
{
// login
teamRepository.login(null);
// if successful
if(teamRepository.loggedIn())
{
// get the two workers
IContributorManager icm;
icm = teamRepository.contributorManager();
IItemManager iim;
iim = teamRepository.itemManager();
// loop thru the userid array
for(String userid: userids)
{
try
{
// get the user record using the ID string (not the name)
IContributor contributor = icm.fetchContributorByUserId(userid, null);
// if we found the user in the system
if(contributor!=null)
{
// get the read/write copy of the contributor record
contributor = (IContributor) contributor.getWorkingCopy();
// set the mail config
// either disabled, all
// or restored to whatever it was
setMailConfiguration(contributor,icm, iim, operation);
}
}
catch(ItemNotFoundException ex)
{
continue;
}
}
}
}
catch(Exception ex)
{
System.out.println("exception="+ex.toString());
}
}
TeamPlatform.shutdown();
}
public static ArrayList<String> getUseridList(String filename)
{
ArrayList<String> userlist = new ArrayList<String>();
try
{
String line;
BufferedReader br = new BufferedReader(new FileReader(filename));
// read the first line
line= br.readLine();
// if not an xml file
if(!line.startsWith("<?xml"))
{
// save the line to the array
userlist.add(line);
// read the rest of the user id list
while((line = br.readLine()) != null)
userlist.add(line);
// close the file
br.close();
}
else
{
// close the file
br.close();
// xml file processor
// document handler will reopen and parse
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File(filename));;
// normalize text representation
doc.getDocumentElement().normalize();
// get the list of elements that represent users
NodeList listOfUsers = doc.getElementsByTagName(XMLElementName);
//System.out.println("Total no of people : " + listOfUsers.getLength());
// loop thru the nodes
for(int i=0;i<listOfUsers.getLength(); i++)
{
NodeList userentry = (NodeList) listOfUsers.item(i);
//System.out.println("there are "+ userentry.getLength() + " nodes for this entry");
for (int q=0; q<userentry.getLength();q++)
{
//System.out.println("node "+ q+ " name="+userentry.item(q).getNodeName()+ " value="+userentry.item(q).getFirstChild().getTextContent());
// find the specific element that contains the userid data
if(userentry.item(q).getNodeName().equalsIgnoreCase(UserIDElementName))
{
// get the actual user data.. in the Text node
// and save it to the array
userlist.add(userentry.item(q).getFirstChild().getTextContent());
break;
}
}
}
}
}
catch (Exception ex)
{
// TODO Auto-generated catch block
ex.printStackTrace();
}
return userlist;
}
private static IMemento setMailConfiguration(IContributor contributor,IContributorManager icm, IItemManager iim, String operation) throws TeamRepositoryException
{
IContributorDetailsHandle detailsHandle= contributor.getDetails();
if (detailsHandle != null)
{
IContributorDetails details=
(IContributorDetails) iim.fetchPartialItem(detailsHandle, IItemManager.REFRESH, Collections.singleton(IExtensibleItem.ALL_STATE_EXTENSIONS_PROPERTY),null);
// get the writeable copy of the details
details=(IContributorDetails) details.getWorkingCopy();
// if we are restoring the previously set values
if(operation.equalsIgnoreCase(operationRestore))
{
// get the saved value, if any
String configVal= details.getLargeStringExtension(CONFIG_KEY_ORIGINAL);
if(configVal!=null)
{
// if the email value was actually specified
if(!configVal.equals(DefaultSettings))
// restore it
// overlay the current settings with the original saved value
details.setLargeStringExtension(CONFIG_KEY, configVal);
else
// clear the string, system uses the default
details.unsetLargeStringExtension(CONFIG_KEY);
// and remove the saved value
details.unsetLargeStringExtension(CONFIG_KEY_ORIGINAL);
// save the details info back;
icm.setContributorDetails(contributor, details,null);
}
return null;
}
else if(operation.equalsIgnoreCase(operationDisable))
{
// get the saved value, if any
String configVal= details.getLargeStringExtension(CONFIG_KEY_ORIGINAL);
// do we have a previously saved setup
// no continue on
if(configVal==null)
{
// get the RTC data for the mail settings
configVal= details.getLargeStringExtension(CONFIG_KEY);
// if not set
if(configVal==null)
{
// indicate the defaults were used
details.setLargeStringExtension(CONFIG_KEY_ORIGINAL, DefaultSettings);
// get the original string copy
configVal = DefaultSettingsString;
}
else
{
// save the original settings
details.setLargeStringExtension(CONFIG_KEY_ORIGINAL, configVal);
}
try
{
XMLMemento imx = XMLMemento.createReadRoot(new StringReader(configVal));
// turn off the global emails
imx.getChild(CONFIG_SCOPE_GLOBAL).getChild(CONFIG_DOMAIN_WORK_ITEMS).getChild(CONFIG_RECEIVE_MAILS).putString("value", "false");
// and the subscriptions
imx.getChild(CONFIG_SCOPE_GLOBAL).getChild(CONFIG_DOMAIN_WORK_ITEMS).getChild(CONFIG_MAIL_WHEN_SUBSCRIBED).putString("value", "false");
// and the mentions
imx.getChild(CONFIG_SCOPE_GLOBAL).getChild(CONFIG_DOMAIN_MESSAGES_AT_ME).getChild(CONFIG_MAIL_MESSAGES_AT_ME).putString("value", "false");
// and the approvals
imx.getChild(CONFIG_SCOPE_GLOBAL).getChild(CONFIG_DOMAIN_APPROVALS).getChild(CONFIG_RECEIVE_MAILS).putString("value", "false");
StringWriter writer= new StringWriter();
// flush into writer
imx.save(writer);
// save the new settings
details.setLargeStringExtension(CONFIG_KEY, writer.toString());
// and write back to the contributor
icm.setContributorDetails(contributor, details,null);
}
catch (CoreException e)
{
System.out.println("unable to get contrinutor details");
return (IMemento)null;
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return (IMemento)null;
}
return (IMemento)null;
}
}
</code>
Comments
Thanks Sam, though the code I have does work. It turns out that it might be a repo-wide issue at the server (vacations are heavy at this time so we didn't find out very quickly) and I'll report back when we know for sure.
Hi Sam,
How can we change the filter options (Creator, Owner, Modifier, Subscriber) using the API?
sorry, not sure what you mean..
you mean the values under those headings for each message type?
each checkbox is an XML element, so you have to set its value to true for checked, and false for unchecked.
you will have to search thru the xml document once you find its structure.
imx.getChild(CONFIG_SCOPE_GLOBAL).getChild(CONFIG_DOMAIN_WORK_ITEMS).getChild(CONFIG_RECEIVE_MAILS).putString("value", "false");
this is not documented for us externals.
Sam,
I found this structure and set the check box values, but the user profile didn't change. I can change only the
CONFIG_RECEIVE_MAILS value.
imx.getChild(CONFIG_SCOPE_GLOBAL).getChild(CONFIG_DOMAIN_WORK_ITEMS).getChild(CONFIG_WORK_ITEM_FILTER).getChild(creator).getChild(comment_added).putString("value", "true");
Sam,
Sorry. The xml structure is different. I used this code and it work. Thanks.
imx.getChild(CONFIG_SCOPE_GLOBAL).getChild(CONFIG_DOMAIN_WORK_ITEMS).getChild(CONFIG_WORK_ITEM_FILTER).getChild(CREATOR).getChild(COMMENT_ADDED).putString("value", "true");
I was posting at the same time.. yes.. you found it..
the per change type are in the CONFIG_WORK_ITEM_FILTER structure,
and each column is a xml element structure, <CREATOR></CREATOR><OWNER>>/OWNER> etc..
it looks like if you want the setting ON you include an element, otherwise not.
for example "<CREATOR><COMMENT_ADDED/><SUMMARY_DESCRIPTION_CHANGED/>...</CREATOR>
you can see all this in the default xml structure above
Please see Skip Mail Save Parameter since RTC 6.0 iFix03.
while interesting, this doesn't really help in the use cases I see.. bulk import of workitems.. none of my code is involved.
I hooked your code answer directly to my blog in one case. Want to make sure they see it if they go here.
Comments
Matthew Owenby
May 21 '14, 11:59 a.m.Sterling,
VK L
Feb 24 '18, 5:21 p.m.I have written a code to update mail format settings - it fails to fetch the contributor details and throuws null exception on the mailconfig retrieval:
*It works after i open the particular user in the editor and perform a save.
user = icm.fetchContributorByUserId(userid, null);
contributorWorkingCopy = (IContributor) user.getWorkingCopy();
icm.saveContributor(contributorWorkingCopy, null);
IContributorDetailsHandle detailsHandle = contributorWorkingCopy.getDetails();
details = (IContributorDetails) iim.fetchCompleteItem(detailsHandle,IItemManager.DEFAULT, null);
details=(IContributorDetails) details.getWorkingCopy();
String mailconfig = details.getLargeStringExtension("com.ibm.team.workitem.mail.Configuration");
mailconfig value is retrieved as null.Please advise.
Mohammed Sardar
Feb 24 '18, 3:13 p.m.Thanks. So, in RTC how can we this feature usage ? Is it a contribution to development? Please help.