SPServices Stories #20 – Modify User Profile Properties on SharePoint Online 2013 using SPServices

This entry is part 20 of 21 in the series SPServices Stories

Introduction

Sometimes people ask me why I’m still bothering with the crufty old SOAP Web Services in SPServices. After all, there are REST and CSOM to play with and Microsoft has decided to deprecate the SOAP Web Services.

Well in some cases, the Shiny New Toys don’t let you get the job done. In cases where you’re implementing on Office365 and simply can’t deploy server side code, SPServices can sometimes be just the right tool. It’s easy to use and it gets stuff done that you need. What more can I say?

I found this nice story from Gary Arora about updating User Profile data a few weeks back. In it, Gary shows us how to easily make those updates on Office365 using SPServices. Gary calls himself “your friendly neighborhood SharePointMan” and he is happy to be have his story be one of my stories.

The Use Case

SharePoint 2013 users need to modify (specific) user-profile-properties client-side without having to navigate away to their ‘MySite’ site and swift through rows of user properties.
(Following is an mock-up showing a simple interface to update a user profile property via CEWP)

Modify_User_Profile_Properties_SharePoint

Simple interface to update Fax number from a CEWP. (Basic demo)

The Usual Solution

In the SharePoint 2013 universe there are 2 ways to read/write data client-side. CSOM and REST. Unfortunately CSOM and REST are not fully there yet when it comes to matching the server side functionality.

In this specific case, one could use CSOM or REST to retrieve (read) User Profile Properties but there is no way to modify (update) these properties from client-side. Here’s Microsoft’s official position.

Not all functionality that you find in the Microsoft.Office.Server.UserProfiles assembly is available from client APIs. For example, you have to use the server object model to create or change user profiles because they’re read-only from client APIs (except the user profile picture)

Hence The Dirty Workaround

So our workaround is SOAP, the forgotten granddaddy of Web services. The User Profile Service web service, from the SharePoint 2007 days, has a method called ModifyUserPropertyByAccountName which functions exactly as it sounds. But since SOAP can be a bit intimidating & ugly to write, we’ll use SPServices, “a jQuery library which abstracts SharePoint’s Web Services and makes them easier to use”

So here’s how we’ll use SPServices to modify User Profile Properties. The method is applicable to SharePoint 2013 & 2010 (online & on-prem) versions.

1. Reference SPServices library

You have two options here. You can either download the SPService library and reference it locally or reference it from its CDN:

<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery.SPServices/2013.01/jquery.SPServices-2013.01.min.js"></script>

2. Create updateUserProfile() Function

The following function simulates the ModifyUserPropertyByAccountName method on the User Profile Service web service.

function updateUserProfile(userId, propertyName, propertyValue) {

  var propertyData = "<PropertyData>" +
  "<IsPrivacyChanged>false</IsPrivacyChanged>" +
  "<IsValueChanged>true</IsValueChanged>" +
  "<Name>" + propertyName + "</Name>" +
  "<Privacy>NotSet</Privacy>" +
  "<Values><ValueData><Value xsi:type=\"xsd:string\">" + propertyValue + "</Value></ValueData></Values>" +
  "</PropertyData>";
  
  $().SPServices({
    operation: "ModifyUserPropertyByAccountName",
    async: false,
    webURL: "/",
    accountName: userId,
    newData: propertyData,
    completefunc: function (xData, Status) {
      var result = $(xData.responseXML);
    }
  });

}

3. Invoke updateUserProfile() Function

This function takes 3 parameters.

  • userId: Your userID. The format is “domain\userId” for on-prem and “i:0#.f|membership|<federated ID>” for SharePoint Online.
  • propertyName: The user profile property that needs to be changed
  • propertyValue: The new user profile property value

Example:

updateUserProfile(
  "i:0#.f|membership|garya@aroragary.onmicrosoft.com",
  "Fax", "555 555 5555");

Note: The above code works but notice that you are passing a hardcoded userId.

To pass the current userId dynamically, we can use CSOM’s get_currentUser(). But since that’s based on the successful execution of ClientContext query, we need to “defer” invoking “updateUserProfile()” until we have received current userId. Therefore we’ll create a Deferred object as follows:

function getUserLogin() {
  var userLogin = $.Deferred(function () {
    var clientContext = new SP.ClientContext.get_current();
    var user = clientContext.get_web().get_currentUser();
    clientContext.load(user);
    clientContext.executeQueryAsync(
      function () {
        userLogin.resolve(user.get_loginName());
      }
      ,
      function () {
        userLogin.reject(args.get_message());
      }
      );
  });
  return userLogin.promise();
}

Now when we invoke updateUserProfile(), it will execute getUserLogin() first, implying that the ClientContext query was successful :

getUserLogin().done(function (userId) {
  updateUserProfile(userId, "Fax", "555 555 5555");
});

4. Full working code

Steps to replicate the demo

  1. Copy the following code snippet and save it as a text file (.txt)
  2. Upload this text file anywhere on your SharePoint site (e.g. Site Assets). Remember its path.
  3. On the same SharePoint site, add/edit a page and insert a CEWP (Content Editor Web Part)
  4. On the web part properties of CEWP, add the path to the text file under “Content Link” section
  5. Click Ok, and save the page.
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery.SPServices/2013.01/jquery.SPServices-2013.01.min.js"></script>
<script type="text/javascript">

//updateUserProfilePreFlight defers until getUserId() is "done"
//then it invokes updateUserProfile
function updateUserProfilePreFlight(){
  getUserId().done(function (userId) {
    var propertyName = "Fax"
    var propertyValue =  $("#Fax").val();
    updateUserProfile(userId, propertyName, propertyValue);
  });
}

//getUserLogin() uses CSOM to retrive current userId.
function getUserId() {
  var userLogin = $.Deferred(function () {
    var clientContext = new SP.ClientContext.get_current();
    var user = clientContext.get_web().get_currentUser();
    clientContext.load(user);
    clientContext.executeQueryAsync(
      function () {
        userLogin.resolve(user.get_loginName());
      }
      ,
      function () {
        userLogin.reject(args.get_message());
      }
      );
  });
  return userLogin.promise();
}

//updateUserProfile updates the userprofile property 
function updateUserProfile(userId, propertyName, propertyValue) {

  var propertyData = "<PropertyData>" +
  "<IsPrivacyChanged>false</IsPrivacyChanged>" +
  "<IsValueChanged>true</IsValueChanged>" +
  "<Name>" + propertyName + "</Name>" +
  "<Privacy>NotSet</Privacy>" +
  "<Values><ValueData><Value xsi:type=\"xsd:string\">" + propertyValue + "</Value></ValueData></Values>" +
  "</PropertyData>";

  $().SPServices({
    operation: "ModifyUserPropertyByAccountName",
    async: false,
    webURL: "/",
    accountName: userId,
    newData: propertyData,
    completefunc: function (xData, Status) {
      var result = $(xData.responseXML);
    }
  });

}


</script>

<input id="Fax" type="text" placeholder="Update Fax" />
<input onclick="updateUserProfilePreFlight()" type="button" value="Update" />

Note

  • You can only edit the user profile properties that are editable (unlocked) on your MySite. Certain fields like department are usually locked for editing as per company policy
Series NavigationSPServices Stories #22 : SPServices SharePoint Attachments in Internet Explorer 9 >>

26 Comments

  1. Is it possible to change other’s user profile ? I tried it, but I am getting an “Attempted to perform an unauthorized operation.” exception.
    Is there a way to sort this out ?

    I also tried this using .NET code (connecting to SharePoint Online) and passing global admin credentials. The GetUserPropertyByAccountName works for different users, but the ModifyUserPropertyByAccountName only works for the current user

    Thanks,

    Reply
  2. Thanks for your prompt response.

    If you don’t mind I have another question. I am trying this code in a sharepoint auto-hosted app using jquery (in a sharepoint-hosted app it works), but I am getting “Transport Error”, then what I do is to add this line:
    jQuery.support.cors = true;
    but still does not work, now I get “access denied”
    Is there any way to work around this?

    Reply
  3. Hey, sorry to bother you again. The above scenario works when calling the service via .net code and passing the fedauth cookies of the O365 global admin, and also when referencing the service via the Tenant Admin site collection. I will be trying to resolve this but using SPServices and jquery

    Thanks,
    Ricardo

    Reply
      • Dear Anderson,

        The below part of the code isn’t executing,pls help.

        $().SPServices({

        operation: “ModifyUserPropertyByAccountName”,

        async: false,

        webURL: “/”,

        accountName: userId,

        newData: propertyData,

        completefunc: function (xData, Status) {

        var result = $(xData.responseXML);

        }

        });

        Thanks,
        Amar

        Reply
          • was getting error in status
            completefunc: function (xData, Status)
            when i was refering jquery.SPServices-2014.01.js

            but succeeded when i referred jquery.SPServices-2013.01.min.js

            2013 version is working

            Reply
            • I’d be interested to know what was causing the problem if you were able to spot it. Best to post questions about SPServices on the SPServices site at Codeplex, as it’s easier to share code there.

              M.

              Reply
              • Dear Marc,

                Thanks a lot for this wonderful workaround.

                couldn’t find the bug exactly! but when i tried the operation “ModifyUserPropertyByAccountName” through 2014 version, the property was not updated.
                but when i tried with 2013 version the property got updated.

                Reply
  4. Hello,
    I have a custom user profile property of type – String (Multi Value)
    I have a termset mapped to this property.

    Now, I want to update this user profile property.
    If I have a single term selected, then it works. But, if returns error – 500 if I try to update property value with multiple terms.

    I tried below options:
    Term1,Term2

    Term2;Term2

    [{ “Id”: “6d953327-2026-4f25-b4a6-fdb8f61523be”, “Name”: “Term1” }, { “Id”: “f9a09db7-998d-42e8-a369-91c9ed0fa68b”, “Name”: “Term2” }]

    But, no luck.
    Could you please suggest?

    Reply
  5. Hey Marc,
    I’m a big an of your SPServices because I realize the value it brought to MOSS when there were no CSOM or REST.
    Now I’m using it in O365 to read certain user properties like Office Location,JobTitle etc. for the current logged in user.
    But it returns an empty string for Job Title and Office Location properties.
    I think I’m using wrong Internal names for these properties.
    Anyway you could help ?

    Reply
  6. I Have this error :

    “Server was unable to process request. —> Attempted to perform an unauthorized operation.” when trying to update a field (I have tried Fax,SPS-MUILanguages and SPS-ContentLanguages) Any idea why ? Thanks

    Reply

Have a thought or opinion?