October 8, 2015

How to know which lists uses a specific Template ID

Suppose you are in a situation where you see the following xml schema for a list item event receiver:


<Receivers ListTemplateId="16016">
<Receiver>
<Name>CustomAlertEventReceiver</Name>
<Type>ItemUpdating</Type>
<Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
<Class>CustomAlertEventReceiver.ItemEventReceiver.ItemEventReceiver</Class>
<SequenceNumber>10000</SequenceNumber>
</Receiver>
</Receivers>


How do you figure which list or lists the event receiver applies to?

Well, we can know if this Template ID is OOB from this list.

Also, we can use the browser if we want to check only one or 2 specific lists that we already know they exist. This post describes how to get the template ID of from list settings page using the 'g_wsaListtemplateId' variable.
http://sharepoint.stackexchange.com/questions/89030/2010-custom-list-template-id

But the stuff above do not really answer the question as the id '16016' is custom.. "I want to know all -if any- lists that uses this template ID across the whole farm"?


My answer: Let's write some code!
I wrote a simple console app to get a collection of list names that are based on a specific template ID, and their web url.
Make sure you run this code in a SharePoint server machine because it will access the local farm and iterate through web applications, site collections & webs.


Hope this helps someone!


public struct TemplateIDResult
    {
        private string _ListName;
        private string _WebUrl;
        public string ListName
        {get { return _ListName; } set { _ListName = value; }}
        public string WebUrl
        {get { return _WebUrl; } set { _WebUrl = value; }}
    }
class Program
    {
        static void Main(string[] args)
        {
            int TargetTemplateTypeVal = 16016;// a custom id
            List<TemplateIDResult> ResultsFound = new List<TemplateIDResult>();
            ResultsFound.AddRange(getTemplateTypeFoundInFarm(TargetTemplateTypeVal));
            if (ResultsFound.Count > 0)
            {
                Console.WriteLine("Results count: " + ResultsFound.Count.ToString());           
                foreach (TemplateIDResult result in ResultsFound)
                {
                    Console.WriteLine(string.Format("List name: '{0}' in Web: {1}" ,result.ListName, result.WebUrl));
                } 
            }
            Console.WriteLine();
            Console.WriteLine("Press any key to exit");
            Console.ReadKey();
        }
        private static List<TemplateIDResult> getTemplateTypeFoundInFarm(int TemplateTypeValue)
        {
            List<TemplateIDResult> result = new List<TemplateIDResult>();
            SPWebService service = SPFarm.Local.Services.GetValue<SPWebService>(string.Empty);
            SPWebApplicationCollection WebAppColl = service.WebApplications;
            foreach (SPWebApplication WA in WebAppColl)
            {
                result.AddRange(getTemplateTypeFoundInWebApp(WA, TemplateTypeValue));
            }
            return result;     
        }
        private static List<TemplateIDResult> getTemplateTypeFoundInWebApp(SPWebApplication WA, int TemplateTypeValue)
        {
            List<TemplateIDResult> result = new List<TemplateIDResult>();
            SPSiteCollection AllSiteColl = WA.Sites;           
            foreach (SPSite site in AllSiteColl)
            {
                #region Site
                SPSecurity.RunWithElevatedPrivileges(delegate
                {
                    using (SPSite s = new SPSite(site.ID))
                    {
                        try
                        {
                            SPWebCollection AllWebs = site.AllWebs;
                            foreach (SPWeb web in AllWebs)
                            {
                                #region Web
                                int TemplateTypeVal;
                                foreach (SPListTemplate template in web.ListTemplates)
                                {
                                    TemplateTypeVal = (int)(template.Type);
                                    if (TemplateTypeVal == TemplateTypeValue)
                                    {
                                        TemplateIDResult r = new TemplateIDResult();
                                        r.ListName = template.Name;
                                        r.WebUrl = web.Url;
                                        result.Add(r);
                                    }
                                }
                                web.Dispose();
                                #endregion
                            }
                        }
                        catch (Exception e){                            Console.WriteLine("Web access error inside: " + site.Url);}
                    }
                });
                #endregion
            }           
            return result;
        }
    }

March 3, 2015

Solving Junk Output During Project Build in Visual Studio 2013

Symptoms


I had this weird situation a while ago. A developer can build a project successfully and show the normal details (couple of lines) in output window. However, when I access the same machine, the same Visual Studio instance, and builds the same project then the project builds okay, but output window will display a lot of weird &  junk details, contains inconsistencies with .NET assemblies. This behavior will occur for all projects, even for empty projects coming from VS templates before any custom code added..

I tried the following actions but did not fix it:
  • Clean the build &rebuild
  • Close project & re-open
  • Restarting Visual Studio
  • log-out from machine then log back in


Cause
It appeared that something went wrong with my profile cache within Visual Studio.


Solution
The solution I found is to delete the Visual Studio cache. Not a perfect solution but it works!

Important: Please understand that deleting the cache will make you lose all of your personal settings in VS. You can export the settings first so you can have a backup before you proceed.

Steps
1. Delete the contents from the following folders:
  • C:\Users\<<Your Alias>>\AppData\Local\Microsoft\Team Foundation\5.0\Cache
  • C:\Users\<<Your Alias>>\AppData\Local\Microsoft\VisualStudio \12.0\ComponentModelCache
  • C:\Users\<<Your Alias>>\AppData\Local\Microsoft\VSCommon
2. From command prompt run the "devenv /resetuserdata" command:
  • For 64 bit: C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE
  • For 32 bit: C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE





January 31, 2015

Get Actual Url of Master Page For Specific Page

I needed to know the full url of the actual master page for each page in my SharePoint farm, so I wrote a piece of code that will do all the magic for me: Read html contents from page file, find the master page tag, get the actual master page from master token, then construct the full url string.
Here is the useful section of the code with 2 methods below:

HtmlContent = web.GetFileAsString(PageUrl);
Match MasterMatch = Regex.Match(HtmlContent, "MasterPageFile=\".*.master", RegexOptions.IgnoreCase | RegexOptions.Multiline);

MasterTag = MasterMatch.Groups[0].Value;// Example: "MasterPageFile=\"~masterurl/default.master"
if (!String.IsNullOrEmpty(MasterTag))
{
// remove 'MasterPageFile=\"' portion
MasterPageValue = MasterTag.Remove(0, 16);

MasterPageUrl = getMasterPageUrlFromToken(web, MasterPageValue);
MasterPageFullUrl = getMasterPageFullUrl(web, MasterPageUrl);
}



/// Convert Master Page token to url
private static string getMasterPageUrlFromToken(SPWeb web, string MasterPageToken)
{string Url = String.Empty;
if (MasterPageToken.Equals("~masterurl/default.master", StringComparison.CurrentCultureIgnoreCase))
{
MasterPageToken = web.MasterUrl;
}
else if (MasterPageToken.Equals("~masterurl/custom.master", StringComparison.CurrentCultureIgnoreCase))
{
MasterPageToken = web.CustomMasterUrl;
}
else if (MasterPageToken.Equals("~site/default.master", StringComparison.CurrentCultureIgnoreCase))
{// Will use "default.master" file in Web Master pages gallery
MasterPageToken = web.CustomMasterUrl;
}
else if (MasterPageToken.Equals("~sitecollection/default.master", StringComparison.CurrentCultureIgnoreCase))
{// Will use "default.master" file in SiteCollection Master pages gallery                     
MasterPageToken = web.CustomMasterUrl;
}
return (MasterPageToken);
}

  
private static string getMasterPageFullUrl(SPWeb web, string MasterUrl)
{// Input Examples:
// "/sname/_catalogs/masterpage/seattle.master"
// "../_catalogs/masterpage/seattle.master"
// "../../_catalogs/masterpage/default_noimage.master"
// "~site/_catalogs/masterpage/default_copy1.master"
// "sites/Office_Viewing_Service_Cache/_catalogs/masterpage/v4.master"

string FullUrl = String.Empty;
string tempUrl = String.Empty;
if (SPUrlUtility.IsUrlRelative(MasterUrl))
{tempUrl = MasterUrl.Substring(MasterUrl.IndexOf("_catalogs"));
FullUrl = String.Format("{0}/{1}", web.Url, tempUrl);
}
else

FullUrl = MasterUrl;
return (FullUrl);
}




January 18, 2015

Fixing deploymet error: The feature is not a Farm Level feature and is not found in a Site level defined by the Url

If you have a SharePoint 2013 site that was migrated from SP2010 but the visual upgrade was not performed, then it will be on SP2010 UI mode as you can see in Fig #1 below (unlike the case of a brand new SP2013 or after the visual upgrade as in Fig #2).


Figure #1 -  Sharepoint 2013 with SP2010 UI



Figure #2 – SharePoint 2013 with SP2013 UI





So in SharePoint 2013 site with SP2010 UI mode, if you are deploying a feature (for example a visual web part) through Visual Studio or PowerShell then the following occurs:
  • The feature is scoped to "Site", and Activate on Default is set to true.
  • Active deployment configuration is default\
  • Solution will be added to farm
  • Feature will be installed on farm, and feature files will be copied to features folder in 15 hive (\15\TEMPLATE\FEATURES).
  • .Dll files will be deployed to GAC or local bin folder
  • User controls will be copied to control templates folder in 15 hive (\15\TEMPLATE\CONTROLTEMPLATES).
  • Feature is not added to site collection features page.
  • Enable-SPFeature command will fail with error message:
    The feature is not a Farm Level feature and is not found in a Site level defined by the Url [Site Collection Url].
  • The .webpart file will not be copied to Web Parts Gallery (_catalogs/wp) in target site collection.
  • Web part cannot be added to any page.

Solution:
Use CompatibilityLevel parameter in Install-SPSolution PowerShell cmdlet to deploy the solution in either or both 14 & 15 hives:

Install-SPSolution -identity SolutionName.wsp -GACDeployment -CompatibilityLevel{14,15} -WebApplication https://ServerName.Domain.com
Also, you can apply the CompatibilityLevel parameter to Enable-SPFeature cmdlet to activate individual features in either or both hives.

January 4, 2015

Cannot Edit And Save Documents In Client Program or Open From Explorer SharePoint 2013

The users complain from 2 problems, so let's describe the situation:

Problem 1: Document Edit/Save in client doesn't work
When user clicks on a document link in document library to open in edit mode (through a browser popup or from client program), the client program (for example MS Word) opens the document. However, user will not be able to save back to server, because when he clicks on ‘Save’ button, it will display the ‘Save As’ dialog box where user can only select local destination to save the file. Also, if it was a Microsoft Office document, user can click on File tab and sees that the document is in ‘Read Only mode’ even though he opened it with edit mode.
Problem 2: Open With Explorer doesn't work either!


Here are the environment details:
  • This is a fresh SharePoint 2013 installation (Out-of-box)
  • Some Web Applications are on port 80 (http) while others are on port 443 (https)
  • WFE Servers configurations: Installed Desktop Experience feature on WFE servers, enabled WebClient service to run automatically, library contribute permissions are set correctly. Users tested as site members and as site collection administrator, removed all Office 2013 products from WFE servers including SharePoint Designer 2013.
  • Client machine configurations: Office 2010 installed, added site to Trusted Sites zone, installed Add-ons for IE


Errors in logs:

Digging into ULS resulted in finding the following errors for w3wp.exe process:
Failed to get document content data. System.ComponentModel.Win32Exception (0x80004005): Cannot complete this function     at Microsoft.SharePoint.SPSqlClient.GetDocumentContentRow(Int32 rowOrd, Object ospFileStmMgr, SPDocumentBindRequest& dbreq, SPDocumentBindResults& dbres)     cda7da9c-4e98-509a-9976-8129b3f554cb
Could not get DocumentContent row: 0x80004005.
Failure when fetching document. 0x80070012


Root Cause:

Actually this is is really about one thing: the SharePoint WebDav. Both features 'Document Edit/Save'  'Open In Exporer' depend on it.
Document edit mode uses SharePoint WebDav http calls to upload the document back to target location (i.e. Document Library) when document is saved in client program (such as MS Word). 
When user clicks 'Save' button in client program, the client program has to discover if server supports required protocols including WebDav service (this is called Office Protocol Discovery). The client program checks first for cached info (called Office Protocol Discovery Cache) in client machine. This depends if there was a prior successful call to the same resource (i.e. the Doc Library). The cache is a windows registry key: HKEY_CURRENT_USER\Software\Microsoft\Office\[Version]\Common\Internet\Server Cache

If there is no value for the target location folder in that key , then browser will try to initiate an http OPTIONS request to ask for folder info, and will require separate authentication. Otherwise, if there is a sub-key value for the target location folder, then no need to initiate OPTIONS request and automatic logon will proceed automatically. 

When client program initiates an OPTIONS request (and in some cases it needed to initiate PROPFIND requests too), The OPTIONS request is getting rejected. This is because of an IIS settings coming from IIS-lockdown script that disallow the ‘OPTIONS’ http verb from being processed. As a result, SharePoint never received the call for edit and assumes the default Read-Only mode. The client program will get an HTTP 404 (not found error).

Refer to the following useful links for more info:


 Solution:

IIS must allow the ‘OPTIONS’ http verb to be used.
For a specific web application, check if IIS is blocking ‘OPTIONS’ verb (the ‘Allowed’ value is set to false).  To fix this, remove the ‘OPTIONS’ entry from Request Filtering section in IIS, this will affect web.config file for target web app only. You must do this for all WFE servers where the web applications are available.
The same change can be applied for the entire farm, and this will affect applicationhost.config file.
Now both saving from client and 'Open with Explorer' features work!

Example:
I used Fiddler to show the inner data of what's going on for a sample document inside Shared Documents library: /sites/IIbraheemTestSite/Shared%20Documents/TestDoc.docx

Before Change After Change
OPTIONS request

Failed
Succeed
Registry folder sub-key in client machine No data stored
Data stored

Hope that helps!