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.

confusing RA approval by non-reviewers

Peter_MiklianPeter_Miklian Customer Advanced IT Monkey ✭✭✭

Any end-user visiting approval URL (https://portal/ReviewActivity/Approval/RAID) sees active Approve/Reject buttons and can click them getting 'RA has been approved' notice but if he's not reviewer in given RA and he doesn't have permissions to approve/reject, nothing happens. The notice is very confusing.

Non-reviewer user comming to approval page should be informed somehow that he can't vote, e.g. non clickable greyed-out buttons or some notice 'you can't vote'.

Cireson support adviced me to create community feature request describing this situation.

7
7 votes

In Development · Last Updated

This is in development now

Comments

  • Nick_FlintNick_Flint Customer Advanced IT Monkey ✭✭✭
    edited May 2019
    This almost gets you there. I threw it together quickly and couldn't get a mutation observer to find the div since it doesn't have an ID (couldn't get the target using the class to work); so it's using a timeout which isn't ideal especially for a production environment. Without the timeout it tries to hide the buttons before they're drawn. Anyway, put this into your custom.js file however you like to do that.

    if(url.indexOf("/ReviewActivity/Approval") > -1){
    $(document).ready(function() {
    	setTimeout(function(){			
    		userCanVote = false;
    			
    		reviewers = pageForm.viewModel.Reviewer;
    			
    		function checkReviewer(value, index, arr)
    		{
    			if(session.user.UserName == value.User.UserName)
    			{
    				userCanVote = true;	
    			}
    		}
    
    		reviewers.forEach(checkReviewer);
    
    		if(userCanVote == false)
    		{
    			$('.btn-success').css('visibility', 'hidden');
    			$('.btn-danger').css('visibility', 'hidden');
    		}
    	},1000);
    });
    }
    
  • Peter_MiklianPeter_Miklian Customer Advanced IT Monkey ✭✭✭
     @Nick_Flint thank you for your code, we'll definitely try to implement it! ;)
  • Nick_FlintNick_Flint Customer Advanced IT Monkey ✭✭✭
    This is better. It gets rid of the arbitrary wait time and hides the buttons as when div containing then is drawn:
    $(document).ready(function() {
    	
    	//The navigation node doesn't load immediately. Get the main div that definitely exists.
    	var mainPageNode = document.getElementById('main_wrapper');
    
    	// create an observer instance
    	var observer = new MutationObserver(function(mutations) {
    
    		//The page changed. See if our element exists yet.
    		if(document.getElementsByClassName("form-control-picker input-sm ra-input").length > 0){
    						
    			userCanVote = false;
    				
    			reviewers = pageForm.viewModel.Reviewer;
    				
    			function checkReviewer(value, index, arr)
    			{
    				if(session.user.UserName == value.User.UserName)
    				{
    					userCanVote = true;
    						
    				}
    			}
    
    			reviewers.forEach(checkReviewer);
    				
    			if(userCanVote == false)
    			{
    				$('.btn-success').css('visibility', 'hidden');
    				$('.btn-danger').css('visibility', 'hidden');
    			}
    		
    		}
    	});
    	
    	// configure the observer and start the instance.
    	var observerConfig = { attributes: true, childList: true, subtree: true, characterData: true };
    	observer.observe(mainPageNode, observerConfig);
    });
    
  • Konstantin_Slavin-BoKonstantin_Slavin-Bo Customer Ninja IT Monkey ✭✭✭✭

    Heavily inspired by @Nick_Flint's code, I've updated it a bit to check whether the user is a member of an AD group which is set as Reviewer:

    // Customize Approval page
    $(document).ready(function() {
    
     if (document.URL.indexOf("ReviewActivity/Approval") > -1) { // Only worry about Approval pages
    
       //The navigation node doesn't load immediately. Get the main div that definitely exists.
       var mainPageNode = document.getElementById('main_wrapper');
    
       // create an observer instance
       var observer = new MutationObserver(function(mutations) {
    
         //The page changed. See if our element exists yet.
         if(document.getElementsByClassName("form-control-picker input-sm ra-input").length > 0){
    
           // Stop listening
           observer.disconnect();
    
           userIsReviewer = pageForm.viewModel.Reviewer.some(item => item.User.UserName == session.user.UserName);
    
           if(!userIsReviewer) {
             console.log("not direct reviewer, checking groups");
             $.get('/api/V3/User/GetUsersGroups', {id: session.user.Id}, function(data) {
               userIsReviewer = data.some(group => 
                                 pageForm.viewModel.Reviewer.some(rev => 
                                   group.UserName == rev.User.UserName
                               ));
    
               if(!userIsReviewer)
               {
                 $('.btn[data-bind$="isReviewActivity"]:visible').attr('disabled', true).parent().attr({'data-toggle': 'tooltip', 'data-placement': 'bottom', 'title': 'You are not reviewer on this activity'});
                 $('[data-toggle="tooltip"]').tooltip();
               }           
             });
           }
         }
       });
    
       // configure the observer and start the instance.
       var observerConfig = { childList: true, subtree: true };
       observer.observe(mainPageNode, observerConfig);
     }
    });
    
  • Nick_FlintNick_Flint Customer Advanced IT Monkey ✭✭✭

    @Konstantin_Slavin-Bo Nice! I like the tool tip with the explanation!

    After updating to your code, I noticed that it breaks if the RA has a 'blank' reviewer (created by SCSM if the RA is created without a reviewer). Error: "Cannot read property UserName". Makes sense since there isn't a UserName in that case. I haven't tested it with both a named reviewer and a blank reviewer.

  • Konstantin_Slavin-BoKonstantin_Slavin-Bo Customer Ninja IT Monkey ✭✭✭✭

    @Nick_Flint Ah yes, good catch, I didn't test that scenario. Thanks for the heads-up! Here's the updated code, which takes into account, if there's no actual reviewers on the RA (also updated to support legacy javascript, ie. Internet Explorer):

    // Customize Approval page
    $(document).ready(function () {
      if (document.URL.indexOf("ReviewActivity/Approval") > -1) { // Only worry about Approval pages
     
        //The navigation node doesn't load immediately, get the main div that definitely exists
        var mainPageNode = document.getElementById('main_wrapper'); // create an observer instance
        
        var cleanupPage = false;
    
        var observer = new MutationObserver(function (mutations) {
    
          //The page changed. See if our element exists yet.
          if (document.getElementsByClassName("form-control-picker input-sm ra-input").length > 0) {
            // Stop listening
            observer.disconnect();
            
            // Make sure that a Reviewer object exists
            if(pageForm.viewModel.Reviewer !== undefined) {
              userIsReviewer = pageForm.viewModel.Reviewer.some(function (item) {
                return item.User !== undefined && item.User.UserName !== undefined ? item.User.UserName == session.user.UserName : false;
              });
    
              if (!userIsReviewer) {
                console.log("not direct reviewer, checking groups");
                $.get('/api/V3/User/GetUsersGroups', {
                  id: session.user.Id
                }, function (data) {
                  userIsReviewer = data.some(function (group) {
                    return pageForm.viewModel.Reviewer.some(function (rev) {
                      return rev.User !== undefined && rev.User.UserName !== undefined ? group.UserName == rev.User.UserName : false;
    
                    });
                  });
    
                  if (!userIsReviewer) {
                    DisableButtons();
                  }
                });
              }
            } else {
              console.log("no reviewers");
              DisableButtons();
            }
    
            if(cleanupPage) {
              $('#ra-workitem-info').hide();
              $($('#ra-activity-info').children('.row')[0]).hide();
              $($('#ra-activity-info').children('.row')[1]).hide();
              $('#ra-activity-info').find('.ra-text-title').css({
                'color': 'black',
                'font-size': '1.2rem'
              });
              $('#ra-activity-info').find('.ra-text-desc').css({
                'color': 'black',
                'white-space': 'pre-wrap',
                'width': '100%'
              });
              $('#ra-activity-info').find('.ra-text-desc').parent().removeClass('col-md-12').addClass('col-md-6 col-xs-12');
              $($('#ra-activity-info').children('.row')[2]).after($($('.ra-user-info')[1]).hide().parent());
            }
            
            function DisableButtons() {
              $('.btn[data-bind$="isReviewActivity"]:visible').attr('disabled', true).parent().attr({
                'data-toggle': 'tooltip',
                'data-placement': 'bottom',
                'title': 'You are not reviewer on this activity'
              });
              $('[data-toggle="tooltip"]').tooltip();
            }
    
          }
    
        });
    
        // configure the observer and start the instance
        var observerConfig = {
          childList: true,
          subtree: true
        };
    
        observer.observe(mainPageNode, observerConfig);
      }
    });
    

    I also included the code we use to cleanup the approval page a bit, which removes anything related to the SR, the links and shows the full RA description. As we target the approval page to endusers, all that additional info is unnecessary. Simply set the cleanupPage variable on line 8 to true or false to enable/disable this. Example:

    cleanupPage = false

    cleanupPage = true

  • Konstantin_Slavin-BoKonstantin_Slavin-Bo Customer Ninja IT Monkey ✭✭✭✭

    And now that we are in this FR thread, I just wanted to add, that the Approvals page should fail gracefully, when the object requested is not found. Right now, if you enter a non-existing RA or MA, it will simply hang on the "load" animation and throw an error in the console, that ClassName was undefined. This is because the object returned "does not exists or you do not have access to it". This should of course be handled and displayed properly to the user.

Sign In or Register to comment.