SharePoint: The usage of SPUtility.GetLocalizedString

May 12th, 2011 No comments

     Sometimes when I parse the Schema.xml of some list I need to resolve such attribute values as:

  • “$Resources:core,lists_Folder;/ONEOFYOURLISTNAME” – often can be found in Lookup-field definitions;
  • “$Resources:groupExpColl;” – can be found in View definition;
  • “$Resources:core,moreItemsParen;” – can be found in View definition;
  • “$ResourcesNoEncode:core,noXinviewofY_LIST;” – can be found in View definition;
  • “$Resources:spscore,FeaturePushdownTaskTitle;”;
  • “$Resources:cmscore,PagesListDisplayName”;
  • and so on.

The method SPUtility.GetLocalizedString(string source, string defaultResourceFile, uint language) is dedicated for retrieving the value for a named resource string from the resource file for a specified language. In example above, a number of strings already contains indications what resource file should be used for searching the localized value, i.e. core, spscore and cmscore. That is why, in most cases, null for defaultResourceFile-parameter can be passed into SPUtility.GetLocalizedString.

If resource file name isn’t included into string (e.g. “$Resources:groupExpColl;”) and defaultResourceFile-parameter is null, I believe, SPUtility.GetLocalizedString searchs in core.resx or in its language-specific variant, e.g. core.en-US.resx (according to language-parameter).

I’ve wrapped the usage of SPUtility.GetLocalizedString into small method, which takes it upon itself to detect the current language.

public static string GetLocalizedString(SPWeb spWeb, string source)
{
    if (string.IsNullOrEmpty(source) || !source.StartsWith("$Resources", StringComparison.OrdinalIgnoreCase))
        return source;
    uint language = spWeb != null ? spWeb.Language : (HttpContext.Current != null ? (uint)Thread.CurrentThread.CurrentUICulture.LCID : 1033);
    return SPUtility.GetLocalizedString(source, null, language);
}

public static string GetLocalizedString(string source)
{
    return GetLocalizedString(null, source);
}

Below the results of GetLocalizedString in respect to each string from the above-mentioned example

String

Localized string

“$Resources:core,lists_Folder;/ONEOFYOURLISTNAME” “Lists/ONEOFYOURLISTNAME”
“$Resources:groupExpColl;” “Expand/Collapse”
“$Resources:core,moreItemsParen;” “(More Items…)”
“$ResourcesNoEncode:core,noXinviewofY_LIST;” “<HTML>There are no items to show in this view of the \”</HTML><ListProperty Select=\”Title\” HTMLEncode=\”TRUE\”><HTML>\” list.</HTML>”
“$Resources:spscore,FeaturePushdownTaskTitle;” “Enable new features on existing sites”
“$Resources:cmscore,PagesListDisplayName” “Pages”
Related posts:

SharePoint: Wrapper over EnsureUser

May 3rd, 2011 No comments

     When I needed to get SPUser-object I always utilized SPWeb.EnsureUser method. EnsureUser looks for the specified user login inside SPWeb.SiteUsers collection, and if the login isn’t found, turns to ActiveDirectory for the purpose of retrieving the user information from there. If such information is found, it will be added to SPWeb.SiteUsers and for the next time it will be returned directly from SPWeb.SiteUsers.

So, we have the fact that when EnsureUser is called, SPWeb-object potentially can be changed (changes affect SPWeb.SiteUsers collection, to be more precise). Each time we change SPWeb-object, it’s highly recommended to make sure that SPWeb.AllowUnsafeUpdates = true. It’s especially topical, when EnsureUser is called from Page during GET-request (Page.IsPostback = false). Otherwise, “Updates are currently disallowed on GET requests. To allow updates on a GET, set the ‘AllowUnsafeUpdates’ property on SPWeb” is thrown.

I’ve implemented some wrapper over the standard SPWeb.EnsureUser:

public static SPUser SafeEnsureUser(SPWeb web, string loginName)
{
    SPUser res = null;
    if (!web.AllowUnsafeUpdates)
    {
        bool oldAllowUnsafeUpdate = web.AllowUnsafeUpdates;
        try
        {
            web.AllowUnsafeUpdates = true;
            res = web.EnsureUser(loginName);
        }
        catch (Exception ex)
        {
            // write to log
        }
        finally
        {
            web.AllowUnsafeUpdates = oldAllowUnsafeUpdate;
        }
    }
    else
        try
        {
            res = web.EnsureUser(loginName);
        }
        catch(Exception ex)
        {
           // write to log
        }

    return res;
}

SafeEnsureUser checks whether web.AllowUnsafeUpdates is true. If yes, it just invoke standard EnsureUser. Otherwise, it preset web.AllowUnsafeUpdates to true and then invoke standard EnsureUser. Nowadays, everywhere I use this method instead of standard one.

If you need an extension for SPWeb-object, try this

public static SPUser SafeEnsureUser(this SPWeb web, string loginName)
{
	// the same method body
}
Related posts:

SharePoint: The specified SPContentDatabase has been upgraded to a newer version

April 6th, 2011 No comments

     Yesterday I was trying to restore the Web-application from fresh SQL-backup of database of production server. Doing that I used the algorithm described in one of my previous post “How to Restore a SharePoint Site from a Database Backup”. I restored backup into a new database, then I tried to create a new Web-application connected to the restored database, however, the undertaking failed, because I received the following error:

The specified SPContentDatabase Name=WSS_CONTENT_PROD Parent=SPDatabaseServiceInstance has been upgraded to a newer version of SharePoint. Please upgrade this SharePoint application server before attempting to access this object.

The specified SPContentDatabase has been upgraded to a newer version of SharePoint. Please upgrade this SharePoint application server before attempting to access this object

     According to the error some update has been deployed on the production server and has led to the version increase. The obvious solution was to upgrade the local environment. First of all, I went to Central Administration->Operations->Servers in Farm and found out the local current version of SharePoint, it was 12.0.0.6504.

Where to take look at version

     In the same time the current version on production was 12.0.0.6510. By means of the page “How To find the SharePoint version” I discovered the cumulative update I should have set up to bring local version to production version. I downloaded the required update and installed. Unfortunately, despite the successful installation, the local version didn’t change at all and the error still remained during a new Web-application creation. When the standard steps don’t help, we need a trick. I tried to find where the restored database contains the information about version. The table Versions struck my eye:

SELECT [VersionId]
      ,[Version]
      ,[Id]
      ,[UserName]
      ,[TimeStamp]
      ,[FinalizeTimeStamp]
      ,[Mode]
      ,[ModeStack]
      ,[Updates]
      ,[Notes]
  FROM [WSS_CONTENT_PROD].[dbo].[Versions]

Table Versions

     As you can see, we have one record where version is greater than local version (12.0.0.6510 > 12.0.0.6504). I replaced 12.0.0.6510 with 12.0.0.6504 using the following sql-command:

Update [WSS_CONTENT_PROD].[dbo].[Versions] 
SET [Version] = '12.0.0.6504' 
where [Version] = '12.0.0.6510'

     After this trick I was able to create a new Web-application connected to the restored database without errors. Of course, Microsoft doesn’t welcome any handmade changes in content database and you should avoid doing them, but if you don’t have a choice you can attempt at your own risk 🙂 In my case it has saved much time and life energy for me 🙂

Related posts:

SharePoint: How to get SystemAccount token

March 25th, 2011 No comments

     As you probably know RunWithElevatedPrivileges allows to run code under the Application Pool identity, which has site collection administrator privileges on all site collections hosted by that application pool. In my opinion RunWithElevatedPrivileges provides too many rights for code, which usually operates in the bounds of only one certain site collection. That is why instead of using RunWithElevatedPrivileges I prefer dealing with an elevated SPSite-object created with SPUserToken of SystemAccount of the target site collection. SystemAccount or SHAREPOINT\system is an alias for the site collection administrators and it has full control over all content in the site collection. We can get it using property SystemAccount.UserToken of SPSite-object. However sometimes the current user doesn’t have permissions to access that property, in this case we will have to use RunWithElevatedPrivileges I’ve criticized above :). The method retrieving the token of SystemAccount can look like the following:

protected static SPUserToken GetSystemToken(SPSite spSite)
{
    SPUserToken res = null;
    bool oldCatchAccessDeniedException = spSite.CatchAccessDeniedException;
    try
    {
        spSite.CatchAccessDeniedException = false;
        res = spSite.SystemAccount.UserToken;
    }
    catch (UnauthorizedAccessException)
    {
        SPSecurity.RunWithElevatedPrivileges(delegate()
        {
            using (SPSite elevatedSPSite = new SPSite(spSite.ID))
                res = elevatedSPSite.SystemAccount.UserToken;      // (***)
        });
    }
    finally
    {
        spSite.CatchAccessDeniedException = oldCatchAccessDeniedException;
    }
    return res;
}

     Here spSite.CatchAccessDeniedException=false allows us to catch and handle UnauthorizedAccessException by ourself, and if the exception happens, we elevates privileges to read the user token by means of RunWithElevatedPrivileges. As we know the most of SharePoint objects somehow refers to SPSite and SPWeb objects, in context of which these SP-objects were created. From this viewpoint the GetSystemToken can look potentially buggy in the line marked with (***), because method returns SPUserToken while its parent elevatedSPSite is being disposed. To justify this code we should take a look at SPUserToken class kindly provided by .Net Reflector:

public sealed class SPUserToken
{
    // Fields
    private byte[] m_token;

    // Methods
    public SPUserToken(byte[] token);
    public bool CompareUser(SPUserToken userTokenCheck);

    // Properties
    public byte[] BinaryToken { get; }
}

     As you can see SPUserToken is just an array of bytes, it doesn’t contain any references to any parental SharePoint objects, therefore we can return it from the method freely. Once SPUserToken is received, it can be cached for a while and reused.

     If you don’t have any original SPSite-object (for example, you have a simple console application) you should create it, e.g.

protected static SPUserToken GetSystemToken(string siteUrl)
{
    SPUserToken res = null;
    using (SPSite spSite = new SPSite(siteUrl))
        res = GetSystemToken(spSite);
    return res;
}

Thanks!

Related posts:

SharePoint: How to Restore a SharePoint Site from a Database Backup

March 22nd, 2011 No comments

     From time to time I restore the Web-application I’m working on from the SQL-backup of database of production server. The main reason for that is to have the actual local environment and to make sure that a new functionality I’ve implemented will work correctly on production server with actual data. Our production server and developer machines are in one domain and for this case the usual sequence of actions is:

  1. Restore backup into a new database using Microsoft SQL Server Management Studio;
  2. Create a new Web-application (Central Administration->Application Management->Create or extend Web application->Create a new Web application):
    • scroll to “Database Name and Authentication”-section and replace Database Name (generated by default) with the name of just created database;
    • scroll to “Application Pool”-section and make sure that a security account for a new application pool has the required permissions on the content database you’ve recovered;
  3. Go to restored Site Collection Administrators (Central Administration->Application Management->Site collection administrators) of just created Web-application and check that they are valid in the new environment, if not, replace them with valid ones;
  4. [Optional] Here some people suggest doing stsadm –o upgrade –inplace –url http://<just created web application url>, but I don’t, because everything works fine in my case;

Happy restoring!

Related posts: