Pages

Showing posts with label feature. Show all posts
Showing posts with label feature. Show all posts

November 29, 2016

How To Show Solution Name For Each Feature In SharePoint





The objective:
Ever occurred to you that you loose track of what solutions relates to what features enabled at site or site collection level? Yeah I've been in that situation too.
The objective is to add new field to Site features & Site Collection features page (/_layouts/ManageFeatures.aspx) showing a link of Solution name for each feature.

Introduction:
First, let's have a look for on urls of different pages:
Site features:         /_layouts/ManageFeatures.aspx
Site Coll. features: /_layouts/ManageFeatures.aspx?Scope=Site
WebApp features: /_admin/ManageWebAppFeatures.aspx?WebApplicationId={Id}&IsDlg=1
 (from CA -> select WebApp -> Manage Features)
Farm features:       /_admin/ManageFarmFeatures.aspx
 (from CA -> System Settings -> Manage farm features)

In this post, I am going to customize 'ManageFeatures.aspx' page only. However, the same procedure below can be applied to other pages. (Note: influenced by code from following post , I decided that I should start customizing my solution http://blogs.msdn.com/b/varun_malhotra/archive/2012/01/31/sharepoint-2010-get-solution-file-name-from-feature-activated.aspx).


OOB Page Details:
Let's first understand contents of ManageFeatures.aspx page and how it works:


- Fields: FeatureIcon(image), FeatureTitleDescription(Text), FeatureActiveButton(button), FeatureActiveState(text)


- Page calls a user control (~/_controltemplates/FeatureActivator.ascx) to render the table of features
<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">
 <wssuc:FeatureActivator runat="server" ID="featact" />
 <SharePoint:FormDigest runat=server />
</asp:Content>


- Page also calls another control ( ~/_controltemplates/FeatureActivatorItem.ascx) to do the feature logic
<AlternatingItemTemplate>
  <wssuc:FeatureActivatorItem
   DataItem='<%# Container.DataItem %>'
   AlternatingItem="true"
   runat="server"
/>


The feature logic is at code files:
- Microsoft.SharePoint.WebControls.FeatureActivator.cs
- Microsoft.SharePoint.WebControls.FeatureActivatorItem.cs


Customization Steps for SP2010 & SP2013:

1. First, make sure you backup the original manageFeatures.aspx page & user controls files (FeatureActivator.ascx & FeatureActivatorItem.ascx).


2. Make a copy for the following: page & user controls, then rename all with postfix with "Ex":
 ManageFeaturesEx.aspx, FeatureActivatorEx.ascx, FeatureActivatorItemEx.ascx

3. Do the following changes to edit 3 files:

- In ManageFeaturesEx.aspx file:
 Set the Src parameter to refer to FeatureActivatorEx.ascx
 <%@ Register TagPrefix="wssuc" TagName="FeatureActivator" src="~/_controltemplates/FeatureActivatorEx.ascx" %>

- In FeatureActivator.ascx file:

    1. Set the Src parameter to refer to FeatureActivatoritemEx.ascx
    <%@ Register TagPrefix="wssuc" TagName="FeatureActivatorItem" src="~/_controltemplates/FeatureActivatorItemEx.ascx"     %>

    2. Add new column header ("Solution") under <colgroup> add: <col width="10%" />

    3. Add new column content:
    *** For SP2010 ***
    Under <tr> add:       
      <th scope="col" id="FeatureSolution" class="ms-vh2" style="padding-bottom: 4px"><SharePoint:EncodedLiteral             runat="server" text="From Solution" EncodeMethod='HtmlEncode'/></th>

    *** For SP2013 ***
    Under <tr> add:       
      <th scope="col" id="FeatureSolution" class="ms-vh2-nofilter" style="padding-bottom: 4px"><SharePoint:EncodedLiteral         runat="server" text="From Solution" EncodeMethod='HtmlEncode'/></th>

- In FeatureActivatorItem.ascx file:
    1. Only for SP2013: First Add these 2 lines to import SharePoint namespaces
    <%@ Import Namespace="Microsoft.SharePoint" %>
    <%@ Import Namespace="Microsoft.SharePoint.Administration" %>

    2. insert the following code in page after the header tags & before first <td> tag:
    <script runat = "server">
        SPSite Site = SPContext.Current.Site;
        SPSolutionCollection FarmSolutions = SPContext.Current.Site.WebApplication.Farm.Solutions;
       
        private string getSolutionNameFromFeatureDef(SPFeatureDefinition featDef)
        {
        string result = string.Empty;
        Guid FeatureSolutionID = featDef.SolutionId;
        if (FeatureSolutionID != Guid.Empty)
        {
            SPSolution solution = FarmSolutions[FeatureSolutionID];
            if (solution != null)
            {
                result = solution.SolutionFile.DisplayName;
            }
        }
        return (result);
        }

        public string getSolutionName()
        {
        string result = string.Empty;
        try
        {
            Guid FeatureID = new Guid(this.FeatureId);
            SPFeatureDefinition CurrentFeatureDefinition;
            SPFeature CurrentFeature = Site.Features[FeatureID];
            if (CurrentFeature != null)
            {
                // feature is activated at site collection
                CurrentFeatureDefinition = CurrentFeature.Definition;
            }
            else
            {
                // feature is activated at Farm or other level
                CurrentFeatureDefinition = SPFarm.Local.FeatureDefinitions[FeatureID];
            }
            result = getSolutionNameFromFeatureDef(CurrentFeatureDefinition);
        }
        catch (System.Exception)
        {
            // do nothing when exception
        }       
        return (result);
       }
    </script>

    3. Then add a new <td> cell at the end of file:
      <td class="<% Response.Write(this.CssClass); %>" style="padding-top: 4px; padding-bottom: 4px; ">
            <table width="100%" cellpadding="0" cellspacing="0" border="0">
            <tr><td class="ms-vb2"><% Response.Write(getSolutionName());%></td></tr>
            </table>
    </td>


    Note: for SP2013 make sure to postfix any '_layouts/' with '15' to target the new hive


4. Copy the 3 files to hive (14 for SP2010 or 15 for SP2013) in all related WFE servers:
 - Copy controls FeatureActivatorEx.ascx, FeatureActivatorItemEx.ascx to \TEMPLATE\CONTROLTEMPLATES folder
- Copy page ManageFeaturesEx.aspx to \TEMPLATE\LAYOUTS folder

That's it! no need for IIS reset. Now you can enjoy having the solution info across all sites collection & site features across the farm.

Note: the solutions column will be empty when no wsp found for target feature (example: for OOB MS features that were stapled in with site definition). Also, solutions column will not support user solutions (sandboxed solutions).



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.

June 10, 2013

Error: site template requires that the Feature be activated in the site collection

Scenario:
You want to create a site template and use it across your environments such as different site collections. In source Site Collection, you create a site and add content such as web parts, lists..etc. You save that site as a template. In destination Site Collection, you upload that template into Solution Gallery and activate it. Then you try to create a site based on the template, but it fails with the error:

The site template requires that the Feature {FeatureID} be activated in the site collection


The quick solution is go to Site Settings -> Site Collection Features and activate the missing feature then try again. Easy huh?
It depends!.. The problem arise when the missing features are not available in your destination environment such as a 3rd party solution that was installed only in source environment.
An alternative is to abandon this site template, and create a fresh site on the destination site collection to ensure that all features are available before creating the template to be used on the destination. This is only good when the site template is not complex to re-create such as when only small amount of customization had applied to source site. But recreating a full customized site that contains a lot of custom lists and web parts will take long hours or days. Another alternative is to go low-level, fix the template and make it work!


Error Analysis:

Let's first see what's happening. Digging for error details in ULS logs show us this:


SPException thrown: Message: The site template requires that the Feature {eb657559-be37-4b91-a369-1c201183c779} be activated in the site collection.. Stack:    
at Microsoft.SharePoint.Utilities.SPUtility.ThrowSPExceptionWithTraceTag(UInt32 tagId, ULSCat traceCategory, Str
ing resourceId, Object[] resourceArgs)     
at Microsoft.SharePoint.SPWebTemplateElement.VerifyFeatures(XmlNodeList xmlNodeFeatures, SPWe
b applyTemplateToThisWeb, Boolean checkIsFeatureActivatedInSiteCollection)     
at Microsoft.SharePoint.SPWebTemplateElement.VerifyFeaturesInWebTemplate(SPWeb applyTemplateToThisWeb)     
at Microsoft.SharePoint.SPWeb.LoadFeatureWebTemplateContent(SPFeatureWebTemplate featureWebTemplate)     
at Microsoft.SharePoint.SPWeb.ApplyWebTemplate(String strWebTemplate)     
at Microsoft.SharePoint.Solutions.AddGallery.AddGalleryWebPart.CreateSite()
..

Let's have an insider look at the Microsoft.SharePoint.DLL. The following is the code for VerifyFeatures() method in SPWebTemplateElement.cs class file:

private void VerifyFeatures(XmlNodeList xmlNodeFeatures, SPWeb applyTemplateToThisWeb, bool checkIsFeatureActivatedInSiteCollection)
        {
            if ((xmlNodeFeatures != null) && (xmlNodeFeatures.Count != 0))
            {
                foreach (System.Xml.XmlNode node in xmlNodeFeatures)
                {
                    XmlElement element = (XmlElement) node;
                    XmlAttributeCollection attributes = element.Attributes;
                    if (attributes != null)
                    {
                        System.Xml.XmlAttribute attribute = attributes["ID"];
                        if (attribute != null)
                        {
                            Guid guid = new Guid(attribute.Value);
                            SPFeatureDefinition definition = SPFarm.Local.FeatureDefinitions[guid];
                            if (null == definition)
                            {
                                definition = applyTemplateToThisWeb.Site.FeatureDefinitions[guid];
                            }
                            if (null == definition)
                            {
                                SPUtility.ThrowSPExceptionWithTraceTag(0x67323467, ULSCat.msoulscat_WSS_FeaturesInfrastructure, "CannotFindFeatureInstalledSpecifiedInWebTemplate", new object[] { guid.ToString("B") });
                            }
                            if ((checkIsFeatureActivatedInSiteCollection && !applyTemplateToThisWeb.IsRootWeb) && (applyTemplateToThisWeb.Site.Features[guid] == null))
                            {
                                SPUtility.ThrowSPExceptionWithTraceTag(0x67323468, ULSCat.msoulscat_WSS_FeaturesInfrastructure, "CannotFindFeatureActivatedSpecifiedInWebTemplate", new object[] { guid.ToString("B") });
                            }
                        }
                    }
                }
            }
        }


The code reveals that it iterates against all features in the template, and is checks if each feature  is installed then checks if it's activated. The highlighted code in Yellow shows where the error message is being thrown.
In our case we are missing a feature whose ID is: eb657559-be37-4b91-a369-1c201183c779, but  are there other missing features I should worry about? Let's open the template itself.

1. Either import the web template .wsp file using Visual Studio (using the ‘Import SharePoint Solution Package’ project option), or convert the .WSP into .CAB then extract the solution folder.
2. Go to ONet.xml (in Web templates -> Name Template)
3. In <Configurations> section, search inside <SiteFeatures> for missing features in your environment


The Nintex Worklfow solution was installed in my test source Site Collection (but not on destination).
I activated two 3rd party features on Site Collection level. The SiteFeatures section in ONet.xml has the following:
  • "Nintex Workflow 2010" with an ID: 0561d315-d5db-4736-929e-26da142812c5,
  • "Nintex Workflow 2010 Web Parts" has ID: eb657559-be37-4b91-a369-1c201183c779, 
  • and "NintexWorkflowContentTypeUpgrade" with ID: 86c83d16-605d-41b4-bfdd-c75947899ac7
The Nintex Workflow solution is not installed on the destination site collection, and hence these features cannot be installed/activated there.

Note: to know if the missing feature belongs to SP2010 OTB, then check if it exists in this list here.

So here is what's happening:

- the template .wsp file comes with a number of feature IDs that were part of the site collection and site when template created on source environment.
- When the template is installed & activated on destination environment, it tries to reference these features. If one feature does not exist (not installed) or not activated then an SPException error will be thrown.
- As we saw in verifyFeatures() method, the code throws an exception at the first feature missing and abort the site creation but will not give a list of other missing features. This is a drawback in design because it makes it difficult for the Administrator to fix it.


Solution:
Comment out or delete the entries of missing features.

  1. Open the template's ONet.xml to comment or delete every missing feature whether in SiteFeatures or in WebFeatures sections that you cannot activate in your environment, until the resulting template is workable.
  2. Build the project in VS to produce new web template, or repackage the folder into .CAB then save as .WSP file.

Note: If we comment only the first feature, save the template file and upload to Solutions Gallery then create the site then we see a similar error but this time it is regarding a different feature with ID: 86c83d16-605d-41b4-bfdd-c75947899ac7
Which is the next missing feature in line as you can see in the ONet.xml image above.


After you complete the 'cleaning work', your template can now work as charm and the new site was created successfully on destination site collection :)



May 8, 2013

Site creation error: the folder does not exist



I have a SharePoint 2010 team site which has a doc lib called "Process Deliverables" and it contains folders with numbered names:


'0.SOW – Charter'
'1.Review Decision Report'
'2.Final Deliverables'

I saved this site into a template with its content successfully. When trying to create a site based on the template, it throws an error:
"Adding properties to the folder with url {..} failed while activating feature, the folder does not exist."

The url here is: ‘Process Deliverables/0.SOW – Charter’


Analysis:
I renamed the first folder '0.SOW – Charter' into '0_SOW – Charter' (without quotes). Then saved the site as a template and tried to create a site based on it. It threw a different error with similar message:


Here the url is: 'Process Deliverables/1.Review Decision Report'. This means the first folder passed successfully but the next folder item in the library fails.


Cause & Solution:
Special characters such as " # % & * : < > ? \ / { | } ~ . generally cannot be used in folder names. 
A complete list of these characters here.
In our case: the dot (.) is allowed but must be not consecutively used neither at the start nor the end of name. However, for some reason it is appearing to be causing the error here. Rename your folder if they have any of the above including the dot, example:

'0_SOW – Charter'
'1-Review Decision Report'
'2) Final Deliverables'


Now save your site as a template and create sites happily!