Home Service Manager Portal Feature Requests
We appreciate you taking the time to vote and add your suggestions to make our products awesome! Your request will be submitted to the community for review and inclusion into the backlog.

We recommend reviewing what is submitted before posting, in case your idea has already been submitted by another community member. If it has been submitted, vote for that existing feature request (by clicking the up arrow) to increase its opportunity of being added to Cireson solutions.

For more information around feature requests in the Cireson Community click here.

"String" and "Display" DataTypes Automatically Refresh from Related "UserPicker" object

Adrian_PaechAdrian_Paech Customer Advanced IT Monkey ✭✭✭

We have a scenario where we want to display 5+ related fields about a user, immediately after selecting/changing the "Affected User". Info such as their contact number, mobile number, location, title and notes field (the Notes field is where we specify if they are a VIP).

We understand this can be partially achieved with the "ExtraProps" field on a UserPicker, but there is no way to format the output of ExtraProps (its just comma separated data). Alternatively it would be nice to be able to format the ExtraProps output somehow.

7 votes

Submitted · Last Updated

Comments

  • Geoff_RossGeoff_Ross Cireson Consultant O.G.
    edited May 2016

    You can do simple CSS styling to the extra props such as make it red and bold with the following.
    Use this CSS override in the custom.css file to change the ExtraProps label style.
    .help-block { 
    color:red; 
    font-weight:bold;
    }

    Alternatively, you can add any field from the User object into the Work Item form using the normal form customisation methods, but using property name  RequestedWorkItem.<property> Make sure you also the the field to read only with Disabled: true.

    eg

    { DataType: "String", PropertyDisplayName: "Job Title", PropertyName: "RequestedWorkItem.Title", Disabled: true }

  • Adrian_PaechAdrian_Paech Customer Advanced IT Monkey ✭✭✭
    Thanks Ross.
    That works great, but unfortionately the fields do not auto-refresh when you change the affected user (as they do in the console). :(
    The only way to get around this is to have the analyst save the form and then reload it. (then they can see the updated information).
    Is there any quick trick to get these fields to auto-refresh?
    Regards,
    Adrian
  • Amarjit_DhillonAmarjit_Dhillon Customer Adept IT Monkey ✭✭
    This is the exact same problem I am having, my extra fields on the Incident form is not auto updating when I'm changing the customer.
  • Tom_HendricksTom_Hendricks Customer Super IT Monkey ✭✭✭✭✭
    I had opened a bug ticket with Cireson about this--the only two properties that change when you modify a userpicker are Name and Id.  All the others are only populated when the form is loaded.  According to the discussion we are having, this is by design.

    This is causing some major headaches for me and my users, however.  We also use an AD extended property to show VIP status in the ExtraProps, among other things.  I also have a custom task that opens another tool, sending the affected user's username over as a param....except that it always sends the ID that was populated at page load....which might be nothing if it is a new ticket that has not been saved yet.

    So I have to re-write my task to make a separate web call on change....shouldn't displaying properties of the affected user object that are in the viewModel be an out-of-box feature, though?

    It is a shame that I can only give you one upvote.  I hope many more follow!
  • Amarjit_DhillonAmarjit_Dhillon Customer Adept IT Monkey ✭✭
    Hi Tom, thank you for the response. I would just like to say that I really appreciate your contribution to the community hub. I am new to this and would like in time to upload some scripts of my own to share.

    Is there any chance we can have a look at your VIP script and see how it works?
  • Tom_HendricksTom_Hendricks Customer Super IT Monkey ✭✭✭✭✭
    edited March 2017
    I have not written the script to work around this, yet.  I was waiting for Cireson to resolve the bug.  I am planning to, now that it appears that they do not consider it a bug, however.

    I am happy to share my thoughts on this publicly, both so that they can be critiqued and in case this is helpful on its own.

    The VIP status is stored in Active Directory (one of the extendedAttribute fields), which I use the Asset Import to sync to, since the stock AD Connector does not look at those attributes.  I had to extend the user class to have the extra property to sync this value into, also.

    With my user object class extended and all the object instances (users) synced, I merely type the name of this user property in ExtraProps to have it show below the affected user field....or so I thought before this bug was discovered.

    At a very high level, the script would take a very similar approach to the one that @john_doyle wrote for sending email to the affected user (reference: https://community.cireson.com/discussion/192/portal-tip-adding-an-email-link-for-the-affected-user-to-the-incident-form). 

    Step 1: It would need to look at the BaseId of the RequestedWorkItem (which is the relationship target for the Affected User) object and make an API call to get the other property/properties that did not get updated properly on the form.

    Edit: I had stated here that I was not sure what API call to make, but John had already chosen the right one for this in his code.  /api/V3/User/GetUserRelatedInfoByUserId?userId={GUID} will return the extended properties for the user object.

    Step 2: Using JQuery, wipe out the ExtraProps element if it exists, and append a div tag containing the desired properties.

  • Amarjit_DhillonAmarjit_Dhillon Customer Adept IT Monkey ✭✭

    Hi Guys, I just wanted to share some code with you based on what Tom supplied earlier, it uses some of John Doyles code.


    I have created the below fields within in my incident.js form

     { DataType: "String", PropertyDisplayName: "Business Phone", PropertyName: "RequestedWorkItem.BusinessPhone", Disabled: true },
    { DataType: "String", PropertyDisplayName: "Department", PropertyName: "RequestedWorkItem.Department", Disabled: true },
    { DataType: "String", PropertyDisplayName: "Office", PropertyName: "RequestedWorkItem.Office", Disabled: true },
    { DataType: "String", PropertyDisplayName: "Organizational Unit", PropertyName: "RequestedWorkItem.OrganizationalUnit", Disabled: true },



    And i'm trying to get these fields to change automatically when the userpicker (affecteduser) is invoked:



    app.custom.formTasks.add('Incident', null, function (formObj, viewModel) {
        formObj.boundReady(function () {
      
      $("label[for='RequestedWorkItem.BusinessPhone']").parent().append("<span id='affectedUserBusinessPhone'</span>");
      $("label[for='RequestedWorkItem.Department']").parent().append("<span id='affectedUserDepartment'</span>");
      $("label[for='RequestedWorkItem.Office']").parent().append("<span id='affectedUserOffice'</span>");
      $("label[for='RequestedWorkItem.OrganizationalUnit']").parent().append("<span id='affectedUserOrganizationalUnit'</span>");
      
      var fnLookUpUser = function(){
       var lookupUserId = (pageForm.viewModel.RequestedWorkItem.BaseId) ? pageForm.viewModel.RequestedWorkItem.BaseId : "";
                $.get("/api/V3/User/GetUserRelatedInfoByUserId", { userId: lookupUserId },
                    function (data) {
         var obj = jQuery.parseJSON( data);
         $("#affectedUserBusinessPhone").html(obj.BusinessPhone);
         $("#affectedUserDepartment").html(obj.Department);
         $("#affectedUserOffice").html(obj.Office);
         $("#affectedUserOrganizationalUnit").html(obj.OrganizationalUnit);
                    },"json");   
      }
      
      if (pageForm.viewModel.RequestedWorkItem.BaseId)
       fnLookUpUser(pageForm.viewModel.RequestedWorkItem.BaseId);
      boundObj["RequestedWorkItem"].bind("change",
       function(){
        fnLookUpUser();
            });
        });
    });


    Only problem I have is that it does that ExtraProp thing whereby it puts my data outside of the fields rather than replacing the text with the fields I created e.g. Label


    and I'm not sure what to change Label to.


    Is this of any help to you Tom?

  • Amarjit_DhillonAmarjit_Dhillon Customer Adept IT Monkey ✭✭

    ignore last one, below is my new code:

    app.custom.formTasks.add('Incident', null, function (formObj, viewModel) {
        formObj.boundReady(function () {
      
      var fnLookUpUser = function(){
       var lookupUserId = (pageForm.viewModel.RequestedWorkItem.BaseId) ? pageForm.viewModel.RequestedWorkItem.BaseId : "";
                $.get("/api/V3/User/GetUserRelatedInfoByUserId", { userId: lookupUserId },
                    function (data) {
         var obj = jQuery.parseJSON( data);
         
         $("input[name=RequestedWorkItem\\.BusinessPhone]").val(obj.BusinessPhone);
         $("input[name=RequestedWorkItem\\.Department").val(obj.Department);
         $("input[name=RequestedWorkItem\\.Office").val(obj.Office);
         $("input[name=RequestedWorkItem\\.OrganizationalUnit").val(obj.OrganizationalUnit);
                    },"json");   
      }
      
      if (pageForm.viewModel.RequestedWorkItem.BaseId)
       fnLookUpUser(pageForm.viewModel.RequestedWorkItem.BaseId);
      boundObj["RequestedWorkItem"].bind("change",
       function(){
        fnLookUpUser();
            });
        });
    });

  • Tom_HendricksTom_Hendricks Customer Super IT Monkey ✭✭✭✭✭
    edited March 2017
    Yes @Amarjit_Dhillon, this does help get the ball rolling.  I am a manager, not a developer, so I do not always have time (and more importantly, resources) to sit down and write out the code, even if I can already picture what it will look like.  Thank you for taking the time to get this started!  Today, I have some time to respond and work with you on this.

    I think the first thing to get out of the way is whether or not you want to have separate fields of data (I would recommend DataType: "Display" instead of "String", because this is not editable), or if you would rather populate ExtraProps instead.  It is a question more of how you want your form to look than functionality (other than the number of tabs to move through the fields, for those who prefer to use the mouse as little as possible).

    My users have shared that they value brevity and efficiency--they are primarily keyboard users when it comes to the work item forms.  They count the number of clicks everything takes and are annoyed by having to touch the mouse in the first place and also by a click count higher than 2 for any given task.

    So for me, I select the most critical information and display it as text below the user picker, since it cannot (and should not--this is not the system of record) be edited anyway.  This may vary for others, of course!  Here is my interpretation of your script, considering my different requirements:

    (Note: extendedProp1 and extendedProp2 are fictitious placeholders)
    { DataType: "UserPicker", PropertyDisplayName: "AffectedUser", PropertyName: "RequestedWorkItem", Required: true, ExtraProps: "extendedProp1,extendedProp2,Title,UserName,BusinessPhone" }
    
    app.custom.formTasks.add('Incident', null, function (formObj, viewModel) { <br>&nbsp;&nbsp;&nbsp; formObj.boundChange('RequestedWorkItem', function () {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; updateExtraProps('RequestedWorkItem');<br>&nbsp;&nbsp;&nbsp; }<br>});<br><br>/* ALTERNATE VERSION OF ABOVE (do not use both) ADAPTED FROM AMARJIT DHILLON'S CODE */<br>boundObj['RequestedWorkItem'].bind("change", function() {<br>   updateExtraProps('RequestedWorkItem');<br>});<br>

     if (userPropertyName && pageForm.viewModel[userPropertyName]) {
    var userObj = pageForm.viewModel[userPropertyName];
     $.get("/api/V3/User/GetUserRelatedInfoByUserId", { userId: userObj.BaseId }, function (data) { // Capture the return object, which may be empty if the field was cleared var newDataJson = $.parseJSON(data);
    // Merge the properties from the view model with the returned object (to preserve methods, etc.) $.extend(newDataJson, userObj, $.parseJSON(data)); // Set the viewModel = to the merged object, which has the properties of the new user pageForm.viewModel[userPropertyName] = newDataJson; }); } }function&nbsp;updateExtraProps(userPropertyName)&nbsp;{
    Your code takes the correct approach in cases where app.custom.formTasks is not available, such as when the ticket is in the "closed" status (we have another thread open about that one, link isn't handy at the moment).  It can be handled using the boundChange event the rest of the time, however.

    I would also prefer your code if I needed to update multiple field values, as in your example.  In my case, the ExtraProps update without any further binding or kendoAutoComplete.trigger("change") statements needed.  I also made this modular to accept userObj as a param because my ChangeRequest form, for example, has 4 user pickers, so I want to be able to add a boundChange() for each of them and reuse the same code.

    Edit: Removed the optional "true" param from $.extend() because it is not necessary to perform a deep merge to describe the user object--doing so brings in all their related tickets, assets, etc. too!
  • Amarjit_DhillonAmarjit_Dhillon Customer Adept IT Monkey ✭✭
    edited March 2017
    No problem, I'm going to try and use DataType: "Display" instead of "String" and see how that looks. I'm also going to try and adapt my code into your example above.

    I also have extended my User class to include a Boolean variable called "isVIP" and if this set to TRUE for a user, I would like to set this in the form somewhere, maybe ExtraProps.

    I'm not a good coder so I will try my best.
     
  • Amarjit_DhillonAmarjit_Dhillon Customer Adept IT Monkey ✭✭
    edited March 2017
    Tom, is there any chance that share some of your Cireson Asset Import LDAP Settings please (screenshots, etc). I'm having issues with mine.

    https://community.cireson.com/discussion/comment/6725#Comment_6725
  • Amarjit_DhillonAmarjit_Dhillon Customer Adept IT Monkey ✭✭
    edited March 2017

    Hi Guys, With some help I have been able to set the extra fields to Display and can now change the text on these labels using the below code when the affecteduser / RequestWorkItem is changed:


    Incident.js / ServiceRequest.js

    { DataType: "UserPicker", PropertyDisplayName: "AffectedUser", PropertyName: "RequestedWorkItem", ExtraProps:"Title" },


    { DataType: "Display", PropertyDisplayName: "Business Phone:", PropertyName: "RequestedWorkItem.BusinessPhone" },
    { DataType: "Display", PropertyDisplayName: "Department:", PropertyName: "RequestedWorkItem.Department" },
    { DataType: "Display", PropertyDisplayName: "Office:", PropertyName: "RequestedWorkItem.Office" },
    { DataType: "Display", PropertyDisplayName: "Organizational Unit:", PropertyName: "RequestedWorkItem.OrganizationalUnit" },


    Custom.js

    app.custom.formTasks.add('Incident', null, function (formObj, viewModel) {
            formObj.boundChange('RequestedWorkItem.DisplayName',function (formObj, viewModel) {
       fnUpdateFields();
            });
    });

    app.custom.formTasks.add('ServiceRequest', null, function (formObj, viewModel) {
            formObj.boundChange('RequestedWorkItem.DisplayName',function (formObj, viewModel) {
       fnUpdateFields();
            });
    });

      var fnUpdateFields = function(){
       var lookupUserId = (pageForm.viewModel.RequestedWorkItem.BaseId) ? pageForm.viewModel.RequestedWorkItem.BaseId : "";
            $.get("/api/V3/User/GetUserRelatedInfoByUserId", { userId: lookupUserId },
                function (data) {
        
       var obj = jQuery.parseJSON( data);
        
       $("label[for=RequestedWorkItem\\.BusinessPhone]").next().text(obj.BusinessPhone);
       $("label[for=RequestedWorkItem\\.Department]").next().text(obj.Department);
       $("label[for=RequestedWorkItem\\.Office]").next().text(obj.Office);
       $("label[for=RequestedWorkItem\\.OrganizationalUnit]").next().text(obj.OrganizationalUnit);
      },"json");  
      }

  • Amarjit_DhillonAmarjit_Dhillon Customer Adept IT Monkey ✭✭
    edited March 2017

    Last one guys, I now am pulling through the VIP Status from a custom variable from the SCSM CMDB and placing this into extraProps. I had extended it and created a boolean type variable called "isVIP":

    Incident/ServiceRequest .JS

    { DataType: "UserPicker", PropertyDisplayName: "AffectedUser", PropertyName: "RequestedWorkItem" },


    { DataType: "Display", PropertyDisplayName: "Business Phone:", PropertyName: "RequestedWorkItem.BusinessPhone" },
    { DataType: "Display", PropertyDisplayName: "Department:", PropertyName: "RequestedWorkItem.Department" },
    { DataType: "Display", PropertyDisplayName: "Office:", PropertyName: "RequestedWorkItem.Office" },
    { DataType: "Display", PropertyDisplayName: "Organizational Unit:", PropertyName: "RequestedWorkItem.OrganizationalUnit" },




    Custom.JS

    app.custom.formTasks.add('Incident', null, function (formObj, viewModel) {
     
      formObj.boundReady(function () {
       $("label[for='RequestedWorkItem']").parent().append("<span id='affectedUserTitle' class='help-block'></span>");
       fnUpdateFields();
      });
     
            formObj.boundChange('RequestedWorkItem.DisplayName',function (formObj, viewModel) {
       fnUpdateFields();
            });
    });

    app.custom.formTasks.add('ServiceRequest', null, function (formObj, viewModel) {
     
      formObj.boundReady(function () {
       $("label[for='RequestedWorkItem']").parent().append("<span id='affectedUserTitle' class='help-block'></span>");
       fnUpdateFields();
      });
     
            formObj.boundChange('RequestedWorkItem.DisplayName',function (formObj, viewModel) {
       fnUpdateFields();
            });
    });

      var fnUpdateFields = function(){
       var lookupUserId = (pageForm.viewModel.RequestedWorkItem.BaseId) ? pageForm.viewModel.RequestedWorkItem.BaseId : "";
            $.get("/api/V3/User/GetUserRelatedInfoByUserId", { userId: lookupUserId },
                function (data) {
        
       var obj = jQuery.parseJSON( data);
        
       $("label[for=RequestedWorkItem\\.BusinessPhone]").next().text(obj.BusinessPhone);
       $("label[for=RequestedWorkItem\\.Department]").next().text(obj.Department);
       $("label[for=RequestedWorkItem\\.Office]").next().text(obj.Office);
       $("label[for=RequestedWorkItem\\.OrganizationalUnit]").next().text(obj.OrganizationalUnit);
       
       if (obj.isVIP){
        $("#affectedUserTitle").html("VIP User  :  " + obj.Title);
       } else {
        $("#affectedUserTitle").html(obj.Title);
       }
      },"json");  
      }

  • Nick_FlintNick_Flint Customer Advanced IT Monkey ✭✭✭
    Thank you for sharing this. One more user request knocked off!
  • Brian_WiestBrian_Wiest Customer Super IT Monkey ✭✭✭✭✭
    Of note I discovered this causes a number of errors to appear on the webconsole.log. 

    2017-06-06 08:08:55,819, ERROR [ 153]:  An error occured while featching user info...
    System.Data.SqlClient.SqlException (0x80131904): Procedure or function 'spGet_UserById' expects parameter '@Id', which was not supplied.
       at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
       at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
       at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
       at System.Data.SqlClient.SqlDataReader.TryConsumeMetaData()
       at System.Data.SqlClient.SqlDataReader.get_MetaData()
       at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
       at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds, Boolean describeParameterEncryptionRequest)
       at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean asyncWrite)
       at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
       at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
       at System.Data.SqlClient.SqlCommand.ExecuteReader()
       at Cireson.ServiceManager.DAL.Database.<ExecuteStoredProcedureAsync>d__38.MoveNext()
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
       at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
       at Cireson.ServiceManager.Services.Implementations.ConsoleUserService.<GetUserRelatedInfoAsync>d__48.MoveNext()
    ClientConnectionId:77a9f177-6b3f-45be-8268-85434a4effe0
    Error Number:201,State:4,Class:16

    Can something be added to the script to handle Nulls?

  • Brian_WiestBrian_Wiest Customer Super IT Monkey ✭✭✭✭✭
    The Amazing @john_doyle provided a fix clearing the errors on the log but still keeping the function :)

    Replace
    var lookupUserId = (pageForm.viewModel.RequestedWorkItem.BaseId) ? pageForm.viewModel.RequestedWorkItem.BaseId : "";

    with:
    var lookupUserId = "";
    if (pageForm.viewModel.RequestedWorkItem.BaseId) {
       lookupUserId = pageForm.viewModel.RequestedWorkItem.BaseId;
    }
    else {
       return;
    }

  • Amarjit_DhillonAmarjit_Dhillon Customer Adept IT Monkey ✭✭
    nice one guys
  • Alex_MarshAlex_Marsh Premier Partner Advanced IT Monkey ✭✭✭
    Is there any way of replacing the API call with /Search/GetObjectProperties? I'm finding the /api/V3/User/GetUserRelatedInfoByUserId a bit slow when users have a large number of related objects when all we need from here really is the user data
  • Mark_GearyMark_Geary Customer IT Monkey ✭
    @Amarjit_Dhillon Hi i am trying  to add the VIP bit but i cant get it to show . we have added the VIP bit in the following AD Attribute Object Name is employeeType

    LDAP Search syntax is (objectcategory=person)(objectcategory=user)(employeeType=vip)

    has any one got an ideas or can help with this bit

  • Amarjit_DhillonAmarjit_Dhillon Customer Adept IT Monkey ✭✭

    Hi, have you extended your active directory class in scsm? as far as i'm aware, the attribute EmpoyeeType is not sync via the normal SCSM AD Connectors.


    I myself extended my Active Directory Class in SCSM to include boolean field called isVIP and this field is manually set in SCSM.

  • Tom_HendricksTom_Hendricks Customer Super IT Monkey ✭✭✭✭✭

    Hi, have you extended your active directory class in scsm? as far as i'm aware, the attribute EmpoyeeType is not sync via the normal SCSM AD Connectors.


    I myself extended my Active Directory Class in SCSM to include boolean field called isVIP and this field is manually set in SCSM.

    I was going to say the same thing.  Is it possible that "employeeType" is actually being stored in one of the extendedAttribute[1-15] properties in AD?
  • Mark_GearyMark_Geary Customer IT Monkey ✭
    Hi Tom yes as far as i know it is been set on the extended attribute
  • Tom_HendricksTom_Hendricks Customer Super IT Monkey ✭✭✭✭✭
    Then I would expect the LDAP query to look more like (using #1 as the example):

    (objectcategory=person)(objectcategory=user)(extensionAttribute1=vip)
Sign In or Register to comment.