﻿<?xml version="1.0" encoding="utf-8"?>
<EXOconfigData format="Rwav" version="2023.1.100.35">
  <Object type="View">
    <Attribute name="Name">UnnamedLinkIcon_1</Attribute>
    <Attribute name="Width">640</Attribute>
    <Attribute name="Height">480</Attribute>
    <Attribute name="Zoom">100%</Attribute>
    <Attribute name="ScaleValue">1</Attribute>
    <Attribute name="OnOpen">//Save a reference to this view
const view = this.view;

//Call SSF and obtain user info
this.view.call('.noOfUsers').then(users=&gt;{
    //Put number of connected sessions to noOfUsers
    view.noOfUsers.value(users.length);    

    //Put users Titles in the multiline textbox.
    const userStr = users.reduce((current, entry)=&gt;{
        return `${current}\n${entry.Title}`
    }, "");
    view.users.value(userStr);
});</Attribute>
    <Attribute name="ServerSideFunctionRPC">accounts.%account%.services.ssf.execute</Attribute>
    <Attribute name="ServerSideJS">return {
 noOfUsers:async function(args, callInfo){
    const call = callInfo.context.call;
    //get all connected sessions
    const result = await call('wamp.session.list');
    if(! result)
        return [];
     
    const users = []
    const promises = result.map(async (sessionId)=&gt;{
        //for each session fetch sesison information
        const session = await call('wamp.session.get',[sessionId]);
        const authid = session.authid;
        const jwtClaims = authid.split('.')[1];
        if(jwtClaims){
            //use the token from each connected user and get the claims
            const jwtString = Buffer.from(jwtClaims,'base64').toString('utf-8');
            const claims = JSON.parse(jwtString);
            if(claims.isService===undefined)
            {
                //if the connected session is not a service, query the Arrigo Local domain controller
                //about information from the accessToken
                const info = await call('accounts.%account%.dc.info', [callInfo.path],{callmeta:{token: session.authid}});
                //extract and add the 'User' information and put it to list of active users
                users.push(info.User)
            }
        }
    });
    //wait for all async calls to finish
    await Promise.all(promises);
    
    //Return the list.
    return users;
 }
}</Attribute>
    <Object type="ArgumentsFolder">
      <Attribute name="Name">Arguments</Attribute>
      <Attribute name="Comment">This folder contains all arguments for the view. The arguments can be sent to the view when it is used in run-time.</Attribute>
    </Object>
    <Object type="ElementsFolder">
      <Attribute name="Name">Elements</Attribute>
      <Attribute name="Comment">This folder contains all visual elements for the view.</Attribute>
      <Object type="Text">
        <Attribute name="Name">noOfUsers</Attribute>
        <Attribute name="Title">Number of active users</Attribute>
        <Attribute name="Left">175</Attribute>
        <Attribute name="Top">25</Attribute>
        <Attribute name="Width">72</Attribute>
        <Attribute name="Height">20</Attribute>
      </Object>
      <Object type="Text">
        <Attribute name="Name">users</Attribute>
        <Attribute name="ShowTitle">No</Attribute>
        <Attribute name="Multiline">Yes</Attribute>
        <Attribute name="Left">30</Attribute>
        <Attribute name="Top">60</Attribute>
        <Attribute name="Width">220</Attribute>
        <Attribute name="Height">205</Attribute>
      </Object>
    </Object>
  </Object>
</EXOconfigData>