Pages

July 14, 2013

Asp:HyperLinkField Gives Undesirable URL

Problem:

When using the ASP.NET HyperLinkField to display the SharePoint Hyperlink field, the HyperLinkField will concatenate the web address (URL) with a comma, a space and then the description.

<asp:HyperLinkField HeaderText="Detail" DataNavigateUrlFields="Detail" Text="Detail" />

The redundant portions are highlighted in Yellow.

Example1:

Result link: http://sptest/Site1/Pages/MyRequest.aspx?RequestID=2/22/2013 2:35:06 PM, View



Example2:

Result link on GridView:   http://www.google.com,%20http//www.google.com



Solution:

Replace the HyperLinkField with a TemplateField control. The TemplateField control will host a hyperlink control where it manipulate the NavigateUrl parameter to extract only the actual URL before the comma ‘,’ from the SharePoint Hyperlink field.

Example:
<asp:TemplateField HeaderText="Detail">
<ItemTemplate>
<asp:HyperLink ID="HyperLink2" runat="server" NavigateUrl='<%# ((string)Eval("Detail")).Split(new string[]{","}, StringSplitOptions.None)[0] %>' Text="View" Target="_blank" />
</ItemTemplate>
</asp:TemplateField>


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 17, 2013

Performance problem: SharePoint site is very slow


Performance Problem:
SharePoint 2010 Server site and Central Admin are unrealistically too slow (16 seconds or more) to open in browser, without showing any error. You added much more server hardware resources (CPU, RAM and HDD) but this solution fails to improve performance.


Analysis:
In IE, hit F12 key and found that the site home page took 73 seconds to load!


In Central Admin, click on the Health Analyzer to find an interesting item in the performance section: “Application pools recycle when memory limits are exceeded.”





According to this Microsoft article here:
Application pools recycle because memory limits have been enabled and exceeded. Recycling based on memory limits is not usually necessary in a 64-bit environment, and therefore recycling should not be enabled. Unnecessary recycling can result in dropped requests from the recycled worker process and slow performance for end users who are making requests to the new worker process.

In faulty server -> IIS Manager -> Application Pools -> select the ‘SharePoint – 80' -> right-click -> click Recycling. We see the following 2 values:
·         Virtual memory usage (in KB): is checked to use 1024 KB
·         Private memory usage (in KB): is checked to use 1024 KB


Cause:
Apparently the application pool (SharePoint – 80) is recycling each time it reaches the memory limit as set above to 1024 KB. This happens many times during page loading so the total time needed to load the page is too long.


Solution:
Follow this procedure:
1.   Current user account must be a member of the Farm Administrators group.
2.   Identify the failing server(s) and repeat the following steps on each failing server
[Central Administration -> Monitoring -> click Review problems and solutions, and then find the name of the server in the Failing Servers column, example: SharePoint - 80].
3.   Current user account must be a member of the Local Administrators group on each server in step 2 above.
4.   In faulty server -> IIS Manager -> Application Pools -> select the ‘SharePoint – 80' -> right-click -> click Recycling. Clear the 2 check boxes of Virtual memory usage (in KB) and Private memory usage (in KB) -> click Next -> click Finish. (Repeat this step for all AppPools of slow WebApps).




Open the site again, and now it’s really fast!


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!







May 4, 2013

Custom values on Choice column - Part 2


In Part 1 here of this article, we understood how the default choices work in OTB Create Column page (FldNew.aspx) in SharePoint 2010.
Now, its time to start the fun. Let's see examples of how we can show custom choice values:

First, remember to always make a backup copy of any OTB file before customizing it.

Example 1: Change static text values in resource file
Open the wss.en-US.resx (located in C:\inetpub\wwwroot\wss\VirtualDirectories\80\App_GlobalResources directory) using notepad or Visual Studio. Search for the keys, and replace the default values with your custom values such as (X, Y, and Z).


And this is how it will look like when creating new column:


There are only 3 keys to be used here. The downside of changing values for these 3 keys is that this will impact other pages, for example these keys are used in qstedit.aspx page used in survey lists. 
You can create new keys with custom values inside wss.en-US.resx and reference them through a a GetGlobalResourceObject()  in FldNew.aspx page.


Example 2: Custom static choices on page:
You can define your static strings directly without the need to resources file.
Edit FldNew.aspx, and replace the whole original inline code (highlighted yellow portion in Part 1) with this:

SPHttpUtility.HtmlEncode(
            "January" + "\r\n" + "Febraury" + "\r\n" + "March"+ "\r\n" +
            "April" + "\r\n" + "May" + "\r\n" + "June" + "\r\n" +
            "July" + "\r\n" + "August" + "\r\n" + "September" + "\r\n" +
            "October" + "\r\n" + "November" + "\r\n" + "December"
            ,Response.Output);


And this is how it will look like:



Example 3: Custom dynamic strings:
We can provide list of numbers from 0 to 9. Put the following code instead of the inline code (highlighted yellow portion in Part 1) in page. Set the rows parameter inside the textarea tag to expand the choices box to show more values as required.

        string ChoicesTxt = "0";
       
        for (int i=1; i<10; i++)
            ChoicesTxt += "\r\n" + i.ToString();
       
       // write the choices string into idChoices control on page
       SPHttpUtility.HtmlEncode(ChoicesTxt,Response.Output);

 And it will like this:


Example 4: Custom data from SharePoint list:
Create a custom list named "MyChoices" and add your choice values in Title field. Adjust the permissions to make it readable by all farm users or elevate the privileges through code. Replace the idChoices inline code (highlighted yellow portion) with the following:


        string ChoicesTxt = "";
        bool  _CustomChoicesError = false;
       
        try
        {
            SPWeb web = SPContext.Current.Web;
            SPList MyChoicesList = web.Lists.TryGetList("MyChoices");
            if (MyChoicesList != null)
            {
                // if lists exists, read all the items
                SPListItemCollection MyChoices = MyChoicesList.Items;
                foreach (SPListItem choice in MyChoices)
                {
                    ChoicesTxt += choice.Title + "\r\n";  
                }
            }
            else
                _CustomChoicesError = true; // list does not exist
        }
        catch (Exception ex)
        {
            _CustomChoicesError = true;
        }
       
        // in case of error, revert back to original choices
        if (_CustomChoicesError)
            ChoicesTxt = "Enter Choice #1\r\nEnter Choice #2\r\nEnter Choice #3\r\n";
       
        // write the choices string into idChoices control on page
        SPHttpUtility.HtmlEncode(ChoicesTxt,Response.Output);

The list (left), and how the values will look like (right):



If the 'MyChoices' list is deleted or an error occurred during data retrieval, then the page will display the original values.
Note: When you create a column using custom choice values, change value of any of the items temporarily and then undo the change. This is to trigger clearOutDefaultWithCheck() for the default value control to show the first value (its default behavior is to take its value from the resources files). Also you can do this by adding more code to handle this.


I hope you enjoyed with these ideas about customizing the choice values across your farm.



April 30, 2013

Custom values on Choice column - Part 1

Couple of weeks ago I was playing around with SharePoint columns, and thought it would be nice to have a custom set of choices to show always when we create a choice column in SharePoint 2010.


Out of The Box functionality:
In SharePoint 2010, go to any list/library, and click 'Create Column' button. When you select a choice column then 3 dummy choice items will display initially ('Enter Choice #1', 'Enter Choice #2' and 'Enter Choice #3'). If you delete these and entered your custom list of items instead, you will notice that the default value field shows the first item value automatically.

My objective is to show useful data each time you create a choice column across your SharePoint farm. Examples could be: numbers from 1 to 10, or list of all months, or just a bunch of names..and the custom values can be static, calculated or coming from a SharePoint list!

I find manipulating SP pages an amusing task, and it is helpful to give you an inside view of how everything works. However, keep in mind that customizing OTB pages is not recommended because all your work can be flushed when patches got installed.
If you do things carefully and you are not going to install any update then continue reading!

First let's have a look on how the choices works in the OTB page.
The page we just saw in column creation is: 'FldNew.aspx' and you can find it in _Layouts folder.
Hit F12 key to view the JavaScript code. If you select the arrow icon and click on the box that shows the three dummy values then we can get to the control and its id is 'idChoices' as follows:

<tr>
                <td colspan="2"></td>
                <td class="ms-authoringcontrols">&#160;</td>
                    <td class="ms-authoringcontrols" id="onetidEnterChoice"><label for="idChoices">Type each choice on a separate line</label>:<font size="3">&#160;</font><br />
                        <table border="0" cellspacing="1">
                                <tr>
                                        <td>
<textarea class="ms-input" name="Choices" id="idChoices" rows="4" cols="40" wrap="off"  onchange="clearOutDefaultWithCheck()" >
Enter Choice #1
Enter Choice #2
Enter Choice #3
</textarea>
                                        </td>
                                </tr>
                        </table>
                </td>
</tr>

The default value control has the id:'onetidIODefChoiceValue'. This is how it works:
Initial value is 'Enter Choice #1'. When user edits and put custom choices, the method clearOutDefaultWithCheck() triggers and will call the other method clearOutDefault() when Calculated Value radio button was not selected. Method clearOutDefault() will set the first choice as the value of the text box of the Choice radio button.



Let's switch to Visual Studio and open the 'FldNew.aspx' and search for 'idChoice'. 
Caution: make backup copy for each OTB file before editing.

<tr>
<td colspan="2"></td>
<td class="ms-authoringcontrols">&#160;</td>
<td class="ms-authoringcontrols" id="onetidEnterChoice"><label for="idChoices"> <SharePoint:EncodedLiteral runat="server" text="<%$Resources:wss,fldedit_typeeachchoiceonseparate%>" EncodeMethod='HtmlEncode'/></label>:<font size="3">&#160;</font><br />
<table border="0" cellspacing="1">
<tr>
<td>
<textarea class="ms-input" name="Choices" id="idChoices" rows="4" cols="40" wrap="off"  onchange="clearOutDefaultWithCheck()" >
<%
SPHttpUtility.HtmlEncode((string)(this.GetGlobalResourceObject("wss", "fldedit_L_strDefaultChoice_Text")) + "\r\n" + (string)(this.GetGlobalResourceObject("wss", "fldedit_L_strChoice2_Text")) + "\r\n" + 
(string)(this.GetGlobalResourceObject("wss", "fldedit_L_strChoice3_Text")),Response.Output);
%></textarea>
</td>
</tr>
</table>
</td>
</tr>

Yellow portion is the where you need to focus, because this tells us how the control is getting the default values. This is an inline C# code that reads static text strings from a resource file using GetGlobalResourceObject() and encode the combined text string to be displayed within the control.
The method GetGlobalResourceObject() reads the a string value of a resource key stored in Resources file named 'wss'. The actual resource file name is 'wss.en-US.resx'. This is an ASP.NET application level resources file and available in the App_GlobalResources directory for every web application in SharePoint. 
For example: for my web application that resides on default port 80, the file path is:
C:\inetpub\wwwroot\wss\VirtualDirectories\80\App_GlobalResources\wss.en-US.resx

For idChoice control the keys are: "fldedit_L_strDefaultChoice_Text", "fldedit_L_strChoice2_Text" and  "fldedit_L_strChoice3_Text".
And for the the onetidIODefChoiceValue control the key is: "fldedit_L_strDefaultChoice_Text".

You can open this wss.en-US.resx file by notepad or Visual Studio. Search for the keys, and you will find the default choice values!



Now we finished understanding what's going on...the following article (Part 2) is about setting our desired custom choice values!


February 2, 2013

Hosted SharePoint 2013 for free

          


Thanks for SharePoint Power for this. This a nice gesture to supply a way to construct a a free hosted SharePoint 2013 tenant for non-commercial purposes.

According to their website, you can get the following:

  • Free unlimited SharePoint Site Collections and Subsites
  • Total 1GB Storage Space
  • Upto 10 Users to collaborate with
  • 200 Alerts
  • SharePoint 2013 Enterprise Features
  • Available to you in 20 Languages
All you have to do is to visit their website here, and click on Register button to fill an information. You can select a prefix as a site url for your SP site in the form of: prefix.sharepointpower.com
Anything else?...Nope, you are finished, and in a matter of minutes you will receive an email from SharePoint Power indicating the link and credentials for your new site!

After some editing to home page, here is my new hosted SP 2013 site....voilĂ !