Get the real JID of user in a room - xmpp

I have a code to discover room occupants like this :
public List<String> getRoomOccupants() {
List<String> occupants = new ArrayList<>();
DiscoverItems discoItems = null;
DiscoverItems.Item item = null;
try {
ServiceDiscoveryManager discoManager = ServiceDiscoveryManager.getInstanceFor(connection);
discoItems = discoManager.discoverItems(zipWhipNumber+"#"+roomService);
Iterator<DiscoverItems.Item> occupantsItems = discoItems.getItems().iterator();
while(occupantsItems.hasNext())
{
item = occupantsItems.next();
occupants.add(item.getName());
System.out.println(" EntityID : " + item.getEntityID() + "\n EntityName : " + item.getName());
}
} catch (NoResponseException | XMPPErrorException
| NotConnectedException e1) {
e1.printStackTrace();
}
return occupants;
}
But the EntityID is the name of the User in the room Not the full JID. So if the user by any chance changes the name to another nickname I won't be able to find his full JID.
Can anyone tell me how to get the full JID from the room ?
Thanks.

Discovery service must to be enabled to show such infos, and depends by implementations and and MultiUserChat configuration.
Probably you are not able to get this informations with a standard Discovery Operation.
You can try to pass through MultiUserChat item like this:
RoomInfo ri = MultiUserChatManager.getInstanceFor(connection).getRoomInfo("zipWhipNumber+"#"+roomService");
List<String> jidUsers = ri.getContactJids());
or like this:
MultiUserChat muc = MultiUserChatManager.getInstance().getMultiUserChat("zipWhipNumber+"#"+roomService");
List<String> jidUsers = muc.getOccupants();
*RoomInformation* it's more or less a DiscoveryService sized on MultiUserChat. Without right configuration, getContactJids() will return null.
*getOccupants()* of a MultiUserChat reply with a List of jids, but it's possible it sends a bad-request if user didn't previously joined the given *MultiUserChat*

Related

Leverage Groups for Workflow Approvals

We're implementing a new workflow (combined with staging task sync) on an existing website where we would like to notify all members that "own" that particular section/content to approve changes.
One of the options is to have multiple roles and their corresponding workflows configured for their role and scope, but this seems like overkill - at least for us, as currently one single role is set for approvals (and another for editors)
However I've recently come across this new page property:
And have a couple of questions:
Can regular CMS users (without membership) be part of a group?
Would we be able to leverage this group for the workflow's email notifications instead of the roles? E.g. email to everyone in the owner group when a page was sent for approval.
Is this option by default inherited from the parent page when a new one is created or does it need to be set individually for each page?
We have a Kentico 11 EMS license and working on an advanced workflow, therefore custom code is possible.
Can regular CMS users (without membership) be part of a group?
- why don't you use roles here?
Would we be able to leverage this group for the workflow's email
notifications instead of the roles? E.g. email to everyone in the
owner group when a page was sent for approval.
- you'll need to customize workflow manager class, but in general yes, it is possible. You could find an inspiration in this post
Is this option by default inherited from the parent page when a new
one is created or does it need to be set individually for each page?
- Use a macro to default the field. If you populate it with anything else then the new values will be saved.
Sample code snippet for Custom Global Event Handler for Workflow steps i.e., Reject and Approve steps.
using CMS;
using CMS.Base;
using CMS.DataEngine;
using CMS.DocumentEngine;
using CMS.EmailEngine;
using CMS.EventLog;
using CMS.Helpers;
using CMS.MacroEngine;
using CMS.SiteProvider;
using CMS.WorkflowEngine;
using System;
// Registers the custom module into the system
[assembly: RegisterModule(typeof(CustomWorkflowEvent))]
public class CustomWorkflowEvent : CMSModuleLoader
{
// Module class constructor, the system registers the module under the name "CustomInit"
public CustomWorkflowEvent()
: base("CustomInit")
{
}
// Contains initialization code that is executed when the application starts
protected override void OnInit()
{
base.OnInit();
// Assigns custom handlers to events
// WorkflowEvents.Approve.After += WorkFlow_Event_After();
WorkflowEvents.Reject.After += WorkFlow_Event_After;
WorkflowEvents.Approve.After += Approve_After;
// WorkflowEvents.Action.After += WorkFlowAction_Event_After;
}
private void Approve_After(object sender, WorkflowEventArgs e)
{
try
{
WorkflowStepInfo wsi = e.PreviousStep;
if (wsi != null)
{
CMS.WorkflowEngine.Definitions.SourcePoint s = wsi.GetSourcePoint(Guid.NewGuid());
//Make sure it was an approval (standard) step
var approvers = WorkflowStepInfoProvider.GetUsersWhoCanApprove(wsi, null, SiteContext.CurrentSiteID, "UserID = " + CMSActionContext.CurrentUser.UserID, "UserID", 0, "Email, FullName, Username");
EventLogProvider.LogInformation("Approvers Data", "Approvers Data", approvers.ToString());
if (approvers != null)
{
//Loop through the approvers
string siteName = null;
SiteInfo si = SiteInfoProvider.GetSiteInfo(SiteContext.CurrentSiteID);
if (si != null)
{
siteName = si.SiteName;
}
EmailTemplateInfo eti = EmailTemplateProvider.GetEmailTemplate("Workflow.Rejected", SiteContext.CurrentSiteName);
MacroResolver mcr = MacroResolver.GetInstance();
EmailMessage message = new EmailMessage();
// Get sender from settings
message.EmailFormat = EmailFormatEnum.Both;
message.From = eti.TemplateFrom;
// Do not send the e-mail if there is no sender specified
if (message.From != "")
{
// Initialize message
// message.Recipients = strRecipientEmail;
message.Subject = eti.TemplateSubject;
// Send email via Email engine API
// EmailSender.SendEmailWithTemplateText(SiteContext.CurrentSiteName, message, eti, mcr, true);
}
}
}
}
catch (Exception ex)
{
throw;
}
}
private void WorkFlow_Event_After(object sender, WorkflowEventArgs e)
{
try
{
WorkflowStepInfo wsi = e.PreviousStep;
if (wsi != null)
{
CMS.WorkflowEngine.Definitions.SourcePoint s = wsi.GetSourcePoint(Guid.NewGuid());
//Make sure it was an approval (standard) step
var approvers = WorkflowStepInfoProvider.GetUsersWhoCanApprove(wsi, null, SiteContext.CurrentSiteID, "UserID = " + CMSActionContext.CurrentUser.UserID, "UserID", 0, "Email, FullName, Username");
EventLogProvider.LogInformation("Approvers Data", "Approvers Data", approvers.ToString());
if (approvers != null)
{
//Loop through the approvers
string siteName = null;
SiteInfo si = SiteInfoProvider.GetSiteInfo(SiteContext.CurrentSiteID);
if (si != null)
{
siteName = si.SiteName;
}
EmailTemplateInfo eti = EmailTemplateProvider.GetEmailTemplate("Workflow.Rejected", SiteContext.CurrentSiteName);
MacroResolver mcr = MacroResolver.GetInstance();
EmailMessage message = new EmailMessage();
// Get sender from settings
message.EmailFormat = EmailFormatEnum.Both;
message.From = eti.TemplateFrom;
// Do not send the e-mail if there is no sender specified
if (message.From != "")
{
// Initialize message
// message.Recipients = strRecipientEmail;
message.Subject = eti.TemplateSubject;
// Send email via Email engine API
// EmailSender.SendEmailWithTemplateText(SiteContext.CurrentSiteName, message, eti, mcr, true);
}
}
}
}
catch (Exception ex)
{
throw;
}
}
}
Hope Helps you.
Can regular CMS users (without membership) be part of a group?
It is not part of CMS users. Groups are coming from Groups Application.
GROUP: Allows you to manage user groups. Groups are a social networking
feature enabling users to find information and communicate according
to shared interests.
Would we be able to leverage this group for the workflow's email notifications instead of the roles? E.g. email to everyone in the owner group when a page was sent for approval.
No
Is this option by default inherited from the parent page when a new one is created or does it need to be set individually for each page?
No

Smack - get entry starts with specific letter

I'm using Smack to build simple XMPP android client, I need to retrieve users only starts with letter "a" I've tried to use roster and UserSearchManager both need specific user id I couldn't use wild card for e.g.
UserSearch userSearch = new UserSearch();
answerForm.setAnswer("user", "a*");
or
RosterEntry entry = roster.getEntry("a*");
Any help?
This is a working snippet:
public static List<Row> searchOnServerForUsers(String searchString, XmppManager xmppManager) throws Exception
{
UserSearchManager usersearchManager = new UserSearchManager(abstractXMPPConnection.getConnection());
String searchServiceName = XmppConstants.USER_SERVICE_NAME_PREFIX + abstractXMPPConnection.getConnection().getServiceName();
Form form = usersearchManager.getSearchForm( searchServiceName );
Form answers = form.createAnswerForm();
answers.setAnswer("Name", false);
answers.setAnswer("Email", false);
answers.setAnswer("Username", true);
// answers.setAnswer("search", "*"); //search for all registered users
answers.setAnswer("search", "a*"); //your search string
ReportedData data = usersearchManager.getSearchResults(answers, searchServiceName);
List<Row> rows = data.getRows();
return rows;
}
So you have to use UserSearch,
on each column (like Username, pay attention to case!)
you have to set if search (true) or to not search (false)
your search string (in your usecase: "a*")
Don't use smack version 4.4.0 alpha1 it has some bugs. After reverting it to 4.1.1 . I got all the users. from UserSearchManager

How to check the user is registered in ejabberd server or not using asmack

The following is the code that I used to search the given user registered in xmpp server or not using asmack.
UserSearchManager userSearchManager = new UserSearchManager(
connection);
Form searchForm;
try {
searchForm = userSearchManager.getSearchForm("vjud."
+ connection.getServiceName());
Form answerForm = searchForm.createAnswerForm();
answerForm.setAnswer("user", true);
answerForm.setAnswer("search", "test"); // here i'm passsing the
// Text value to search
ReportedData resultData;
resultData = userSearchManager.getSearchResults(answerForm,
"vjud." + connection.getServiceName());
Iterator<Row> it = resultData.getRows();
Row row = null;
while (it.hasNext()) {
String value = it.next().toString();
Log.i("Iteartor values......", " " + value);
System.out.println("Jabber_id :"
+ row.getValues("jid").next().toString());
System.out.println("Name :"
+ row.getValues("Name").next().toString());
row = it.next();
}
} catch (XMPPException e) {
e.printStackTrace();
}
Form answerForm = searchForm.createAnswerForm(); getting NullPointerException.Anyone please help thanks in advance.
You cannot use Jabber User Directory to know if a user is registered or not. Jabber User Directory (vjud) is based on the info user publish in their Vcard. A registered user that do not have a Vcard will not be matched by the search.

Service Unavailable 503 Error during File Transfer with Openfire using Smack

I am trying to send a file through chat using openfire on the server and the smack java library.
This is the output I get:
Status :: Error Error :: null Exception :: service-unavailable(503) Is
it done? true
Here are my sender and receiver functions:
public void fileTransfer(String fileName, String destination) throws XMPPException {
// Create the file transfer manager
FileTransferManager manager = new FileTransferManager(connection);
FileTransferNegotiator.setServiceEnabled(connection,true);
// Create the outgoing file transfer
OutgoingFileTransfer transfer = manager.createOutgoingFileTransfer(destination);
// Send the file
transfer.sendFile(new File(fileName), "You won't believe this!");
try {
Thread.sleep(10000);
}
catch(Exception e){}
System.out.println("Status :: " + transfer.getStatus() + " Error :: " + transfer.getError() + " Exception :: " + transfer.getException());
System.out.println("Is it done? " + transfer.isDone());
}
public void fileReceiver(final boolean accept, final String fileName) {
// Create the file transfer manager
final FileTransferManager manager = new FileTransferManager(connection);
// Create the listener
manager.addFileTransferListener(new FileTransferListener() {
public void fileTransferRequest(FileTransferRequest request) {
// broadcast something here. Wheather users want to accept file
// Check to see if the request should be accepted
if(accept) {
// Accept it
IncomingFileTransfer transfer = request.accept();
try {
transfer.recieveFile(new File(fileName));
System.out.println("File " + fileName + "Received Successfully");
//InputStream input = transfer.recieveFile();
} catch (XMPPException ex) {
Logger.getLogger(XmppManager.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
// Reject it
request.reject();
}
}
});
}
I had same problem, I investigated the stanza and solved it this way.
Many people use "/Smack" or "/Resource" as resource part in jid, but that can be done another way.
Resource path is changing with every presence changed of user. Lets say we want to send image to this user:
"user1#mydomain"
You must add "/Resource" part to this jid and it become this:
user1#mydomain/Resource
But /Resource path is changing with presence so you must follow every presence change to update resource path.
Best way is to get user presence is in roster listener and in presencheChanged() method you get last user resource part like this:
Roster roster=getRoster();
roster.addRosterListener(new RosterListener() {
#Override
public void entriesAdded(Collection<Jid> addresses) {
Log.d("entriesAdded", "ug");
context.sendBroadcast(new Intent("ENTRIES_ADDED"));
}
#Override
public void entriesUpdated(Collection<Jid> addresses) {
Log.d("entriesUpdated", "ug");
}
#Override
public void entriesDeleted(Collection<Jid> addresses) {
Log.d("entriesDeleted", "ug");
}
#Override
public void presenceChanged(Presence presence) {
Log.d("presenceChanged", "ug");
//Resource from presence
String resource = presence.getFrom().getResourceOrEmpty().toString();
//Update resource part for user in DB or preferences
//...
}
});
}
Resource string will be some generated string like "6u1613j3kv" and jid will become:
user1#mydomain/6u1613j3kv
That means that you must create your outgoing transfer like this:
EntityFullJid jid = JidCreate.entityFullFrom("user1#mydomain/6u1613j3kv");
OutgoingFileTransfer transfer = manager.createOutgoingFileTransfer(jid)
transfer.sendFile(new File("DirectoryPath"), "Description");
And that is how i have solved my problem with file transfer on smack and Openfire.
In your case jid is destination.
Also to mention you must add following properties in your Openfire server:
xmpp.proxy.enabled - true
xmpp.proxy.externalip - MY_IP_ADDRESS
xmpp.proxy.port - 7777
Just to mention, I am using Openfire 4.0.2 and Smack 4.2.2.
Also this can be configured the easy way, just set the resource on
XMPPTCPConnectionConfiguration.Builder .
like
XMPPTCPConnectionConfiguration.Builder configurationBuilder =
XMPPTCPConnectionConfiguration.builder();
configurationBuilder.setResource("yourResourceName");

Custom contact has invalid ContactListId on Windows Universal

I am experiencing some strange behaviour in the Windows Universal Contacts API. Consider the following method:
public async Task TestContactStore()
{
//Fetch store and create custom contact list
ContactStore localStore = await ContactManager.RequestStoreAsync(ContactStoreAccessType.AppContactsReadWrite);
var list = await localStore.CreateContactListAsync("Contact Store Test");
//Create new contact in custom contact list
await list.SaveContactAsync(new Contact() { Name = "Test", LastName = "Contact"});
ContactStore allAccessStore = await ContactManager.RequestStoreAsync(ContactStoreAccessType.AllContactsReadOnly);
//Print all available contact lists
Debug.WriteLine("All Contact Lists");
var contactLists = await allAccessStore.FindContactListsAsync();
foreach (var contactList in contactLists)
{
Debug.WriteLine(contactList.DisplayName + ": " + contactList.Id);
}
//Print all available contacts
Debug.WriteLine("All Contacts");
var contacts = await allAccessStore.FindContactsAsync();
foreach (var contact in contacts)
{
Debug.WriteLine(contact.Name + " " + contact.LastName + ": " + contact.ContactListId);
}
}
It creates a contact list for my app ("Contact Store Test"). Then it saves a new Contact to this list ("Test Contact"). After this it prints all available contact lists and then all contacts.
The output is:
All Contact Lists
Hotmail: 24,5,5
Contact Store Test: 24,11,11
All Contacts
Hotmail Contact: 24,5,5
Test Contact: 24,5,5
Why does the Test Contact show up with the same ContactListId as the Hotmail-list? In the contacts app on the phone it shows up as belonging to the Contact Store Test-list. What am I missing?
I have now done some further research regarding this behavior. In the latest build 10.0.10512.1000, this behavior has changed. All ContactListId return empty string now.
When fetching contacts the way I did in my example above
var contactLists = await allAccessStore.FindContactListsAsync();
foreach (var contact in contactLists)
{
Debug.WriteLine(contactList.DisplayName + ": " + contactList.Id);
}
It seems as though the contacts in the list are always aggregated contacts (even if they are not linked in the address book). Aggregated contacts (apparently) always has empty ContactListId and AggregateId. The Id-property always matches the AggregateId-property of the corresponding raw contacts.
To get the corresponding contact list id, the raw contacts for that aggregate needs to be fetched:
var contactLists = await allAccessStore.FindContactListsAsync();
foreach (var contact in contactLists)
{
var rawContacts = await allAccessStore.AggregateContactManager.FindRawContactsAsync(aggregateContact);
foreach (var raw in rawContacts)
{
Debug.WriteLine(contactList.DisplayName + ": " + contactList.Id);
}
}
This gives expected ContactListId.