I've moved my blog to!. Please update your links. This blog is no longer in use--you can find all posts and comments at my new blog; I will no longer be posting to this site and comments have been disabled.

Tuesday, September 25, 2007

My Site Settings

This command replaces the gl-setmysitesnamingformat command as it includes the same functionality along with additional capabilities.

I had originally created the gl-setmysitesnamingformat command to address a need to set the naming format of personal sites and I originally didn't think I'd have to worry about the other settings on the My Sites Settings page but turns out that I did need to set one other field. So rather than create another command for just one field I decided to create a command that would allow me to set any value on that particular page.

I was originally thinking I would exclude the naming format as I already had a command for that but then decided that I'd just include it so that this command became a one-stop-shop and the other could be deprecated (I'll leave it in but there's not much need for it now that this command exists).

Like the original command I had to use reflection to set the internal properties (I just don't understand why Microsoft chose not to make the UserProfileApplication object public). Beyond using the internal UserProfileApplication class the command also uses an internal method called CommitPersonalSiteSettings which is part of the UserProfileManager class (I had to use reflection to call this method but at least I'm not accessing the database directly).

I'm hoping that Microsoft will either make these methods and objects public or will create the ability to set these values via the existing properties. The core code is shown below (if using reflection scares you I'd suggest clicking away):

   1: /// <summary>
   2: /// Runs the specified command.
   3: /// </summary>
   4: /// <param name="command">The command.</param>
   5: /// <param name="keyValues">The key values.</param>
   6: /// <param name="output">The output.</param>
   7: /// <returns></returns>
   8: public override int Run(string command, StringDictionary keyValues, out string output)
   9: {
  10:  output = string.Empty;
  12:  InitParameters(keyValues);
  14:  string sspname = Params["sspname"].Value;
  16:  ServerContext current = ServerContext.GetContext(sspname);
  17:  UserProfileManager upm = new UserProfileManager(current);
  19:  SiteNameFormat nameFormat = upm.PersonalSiteFormat;
  20:  if (Params["nameformat"].UserTypedIn)
  21:   nameFormat = (SiteNameFormat)Enum.Parse(typeof(SiteNameFormat), Params["nameformat"].Value, true);
  23:  string location = upm.PersonalSiteInclusion;
  24:  if (Params["location"].UserTypedIn)
  25:   location = Params["location"].Value;
  27:  bool chooseLanguage = upm.IsPersonalSiteMultipleLanguage;
  28:  if (Params["chooselanguage"].UserTypedIn)
  29:   chooseLanguage = (Params["chooselanguage"].Value == "enable" ? true : false);
  31:  string readers = upm.PersonalSiteReaders;
  32:  if (Params["readers"].UserTypedIn)
  33:   readers = Params["readers"].Value;
  35:  CommitPersonalSiteSettings(upm, chooseLanguage, location, nameFormat, readers);
  37:  bool modified = false;
  39:  // UserProfileApplication userProfileApplication = current.UserProfileApplication;
  40:  System.Reflection.PropertyInfo userProfileApplicationProp = current.GetType().GetProperty("UserProfileApplication",
  41:             BindingFlags.NonPublic |
  42:             BindingFlags.Instance |
  43:             BindingFlags.InvokeMethod |
  44:             BindingFlags.GetProperty);
  45:  object userProfileApplication = userProfileApplicationProp.GetValue(current, null);
  47:  // Set the search center url
  48:  if (Params["searchcenter"].UserTypedIn)
  49:  {
  50:   // userProfileApplication.SearchCenterUrl = Params["searchcenter"].Value;
  51:   System.Reflection.PropertyInfo searchCenterProp = userProfileApplication.GetType().GetProperty("SearchCenterUrl",
  52:                      BindingFlags.NonPublic |
  53:                      BindingFlags.Public |
  54:                      BindingFlags.Instance |
  55:                      BindingFlags.SetProperty |
  56:                      BindingFlags.FlattenHierarchy);
  57:   searchCenterProp.SetValue(userProfileApplication, Params["searchcenter"].Value, null);
  58:   modified = true;
  59:  }
  60:  if (Params["remotemysites"].UserTypedIn)
  61:  {
  62:   //userProfileApplication.EnablePersonalFeaturesForMultipleDeployments = Params["remotemysites"].Value;
  63:   System.Reflection.PropertyInfo remoteProp = userProfileApplication.GetType().GetProperty("EnablePersonalFeaturesForMultipleDeployments",
  64:                      BindingFlags.NonPublic |
  65:                      BindingFlags.Public |
  66:                      BindingFlags.Instance |
  67:                      BindingFlags.SetProperty |
  68:                      BindingFlags.FlattenHierarchy);
  69:   bool remoteMySites = (Params["remotemysites"].Value == "enable" ? true : false);
  70:   remoteProp.SetValue(userProfileApplication, remoteMySites, null);
  71:   modified = true;
  72:  }
  74:  if (Params["provider"].UserTypedIn)
  75:  {
  76:   Uri uri = new Uri(Params["provider"].Value);
  77:   //userProfileApplication.MySitePortalUrl = uri.AbsoluteUri;
  78:   System.Reflection.PropertyInfo searchCenterProp = userProfileApplication.GetType().GetProperty("MySitePortalUrl",
  79:                    BindingFlags.NonPublic |
  80:                    BindingFlags.Public |
  81:                    BindingFlags.Instance |
  82:                    BindingFlags.SetProperty |
  83:                    BindingFlags.FlattenHierarchy);
  84:   searchCenterProp.SetValue(userProfileApplication, uri.AbsoluteUri, null);
  85:   modified = true;
  86:  }
  88:  if (modified)
  89:  {
  90:   MethodInfo update =
  91:    userProfileApplication.GetType().GetMethod("Update",
  92:              BindingFlags.NonPublic |
  93:              BindingFlags.Public |
  94:              BindingFlags.Instance |
  95:              BindingFlags.InvokeMethod |
  96:              BindingFlags.FlattenHierarchy,
  97:              null,
  98:              new Type[] { }, null);
  99:   // userProfileApplication.Update(true);
 100:   update.Invoke(userProfileApplication, null);
 102:   Console.WriteLine("The settings updated may require an iisreset before the changes are visible.");
 103:  }
 105:  return 1;
 106: }
 109: #endregion
 111: /// <summary>
 112: /// Commits the personal site settings.
 113: /// </summary>
 114: /// <param name="upm">The user profile manager.</param>
 115: /// <param name="chooseLanguage">if set to <c>true</c> the user may choose a language for their personal site.</param>
 116: /// <param name="location">The personal site location.</param>
 117: /// <param name="nameFormat">The site name format.</param>
 118: /// <param name="readers">The readers site groups (comma separated).</param>
 119: internal static void CommitPersonalSiteSettings(UserProfileManager upm, bool chooseLanguage, string location, SiteNameFormat nameFormat, string readers)
 120: {
 121:  MethodInfo commitPersonalSiteSettings =
 122:   upm.GetType().GetMethod("CommitPersonalSiteSettings",
 123:         BindingFlags.NonPublic | BindingFlags.Public |
 124:         BindingFlags.Instance | BindingFlags.InvokeMethod);
 126:  commitPersonalSiteSettings.Invoke(upm,
 127:            new object[]
 128:             {
 129:              location, nameFormat, readers, chooseLanguage
 130:             });
 131: }
The syntax of the command I created to set the various properties can be seen below.

C:\>stsadm -help gl-mysitesettings

stsadm -o gl-mysitesettings

Sets the my site settings for the My Sites web application.

        -sspname <SSP name>
        [-nameformat <Username_CollisionError | Username_CollisionDomain | Domain_Username>]
        [-location <personal site location>]
        [-chooselanguage <enable | disable>]
        [-readers <comma separated list of site group readers>]
        [-searchcenter <preferred search center URL>]
        [-remotemysites <enable | disable>]
        [-provider <personal site provider URL (ex: "http://mysites")>]

Here’s an example of how to set all the properties:

stsadm -o gl-mysitesettings -sspname "SSP1" -nameformat username_collisiondomain -location "personal" -chooselanguage disable -readers "NT AUTHORITY\authenticated users" -searchcenter "http://intranet/searchcenter/pages" -remotemysites disable -provider "http://mysites"

Please note that because this command uses internal only classes and methods directly it could, according to Microsoft, put your environment into an un-supported state (though this is Microsoft's recommended approach over directly manipulating the database). Please make sure you understand what the command is doing and what your support options with Microsoft are.


kaushal said...

Hi Gary,
i used your mysitesettings command to modify the location of mysite for my organizations as ssp is not working for us in gui mode. While hitting the mysite link from any site page its going to the new location. while clicking the profile link on mysite also its going to the new location. But, while searching for a person and then clicking on the result it is still going to the old mysite location. so.. we are getting the page not found error . inserting the new path of location in the url we are able to go to the persons profile. thanks in advance and surely would appreciate your help..

Gary Lapointe said...

It takes a while for profiles to be synched up with SSP changes. There's a job that's run (I think it's profsynch but I honestly don't remember) which synchronizes all the content databases with various changes - this will need to run before you'll notice any change.

kaushal said...

Thanks Gary,
There is a job definition called Profile Synchronization which is scheduled to run hourly.But last time it ran on 12/06/07. Any clue why is it so..?? Is there any way i can force a job to run now ??

Gary Lapointe said...

You can try the runtimerjob command. My guess is that if your ssp is not working that your jobs probably aren't running either but honestly without error details I can't be much more help than that.