This blog post is a contribution from Aaron Miao, an engineer with the SharePoint Developer Support team.
This is not a new issue. Ronald Widha’s blog post addresses this issue for SharePoint 2010. But with SharePoint 2012 and Visual Studio 2012 releases, the issue still remains and gets more complicated.
Problem
If you create a SharePoint 2013 application page project with Visual Studio 2012 by following the steps below, then drag and drop a user control to your application page Test.aspx, it will generate a mark up shown below.
The steps
1. Create SharePoint 2013 Farm solution.
2. Add a new User Control called TestControl.ascx with a public string property called Name.
3. Add an application page called Test.aspx. Your project will look like this.
And the mark up
<%@ Register Src="~/_controltemplates/15/TestSharePointProject1/TestControl.ascx" TagPrefix="uc1" TagName="TestControl" %>
<uc1:TestControl runat="server" id="TestControl" />
In Test.aspx.cs, if you access the user control object like this:
You will run into 2 issues.
First, VS intellisense does not work.
Second, you cannot compile the project because VS 2012 does not recognize the Name property (as seen marked with red waved line).
Casting the user control explicitly could do the trick. But you do not want to do that every time!
Cause
If you check the Test.aspx.designer.cs file, your user control will be declared like this:
protected global::System.Web.UI.UserControl TestControl;
The TestControl is declared as base type of System.Web.UI.UserControl. This is the problem with SharePoint 2013 Visual Studio templates and VS2012. Look closer at your Test.aspx page, your control is registered like this:
Note that the source is _controltemplates rather than ControlTemplates. In addition, it has folder called “15”. This should be mapped to SharePoint 2013 installation 15 hive folder. However, VS2012 generated project does not even have 15 folder. You would hope that VS2012 understands what SharePoint 2013 means here.
If you look closer and hover mouse over the Name property, VS2012 does not seem to like your user control registration.
The user control registration does not match the project folder structure. It’s suggesting that it cannot find the control and decided to put the base System.Web.UI.UserControl in the page designer class (*.ascx.designer.cs). This is the culprit as to why we cannot get the public properties/methods without explicit casting.
It seems that this problem is caused by VS2012 not having the same assumptions as SharePoint 2013. It happens for VS2010 with SharePoint 2010 and still true for VS2012 with SharePoint 2013.
VS2012 should have generated \_controltemplates\15 in the first place when we create user control for SharePoint 2013. Please be aware that SharePoint 2013 installation now includes both 14 and 15 hives. Alternatively, VS2010 should simply accept that controltemplates as a mere string token that will be converted to _controltemplates/15 by SharePoint 2013 runtime and take this into account when generating the designer classes.
Solution
The solution is simple. But what I figured is that it depends on the site URL property of the project in question. Typically, site URL could either be http://yourserverurl or http://yourserverurl/sites/yoursiteurl. The key is to make the project folder structure that matches the URL path.
Here’s what we need to do. Continue the steps of creating the project.
4. Add a new folder to the project called “_controltemplates”.
If your site URL looks like http://yourserverurl/sites/yoursiteurl, you need to add folder “sites” and then add folder “yoursiteurl” inside of the folder “sites”. Then add folder “_controltemplates” under “yoursiteurl” folder.
5. Add a new folder called “15” inside of “_controltemplates” folder.
6. In file explorer, copy all files from “ControlTemplates” to “15” folder.
7. In Solution, include all files in “15” folder.
8. Delete “ControlTemplates” from solution.
9. Then you could drag and drop the user control to the application page and starting coding.
As long as you do this, VS2012 behaves properly and casts the user controls to the proper type in designer class (*.ascx.designer.cs).
After all this changes, your project should look like this.
Your control registration now looks like this:
If your project references the site like this: http://yourserverurl/sites/yoursiteurl, it should look like the below. Note that the folder structure matches the site URL.
And your control registration looks like this:
<%@ Register Src="~/sites/devsite/_controltemplates/15/TestSharePointProject1/TestControl.ascx" TagPrefix="uc1" TagName="TestControl" %>
This blog post is a contribution from Deepak Kumar, an Engineer with the SharePoint Developer Support team.
The “Inherits” attribute of the custom content type when set to “False” indicates that the custom content type will only inherit Fields that were in the parent content type when SharePoint Foundation was installed. Fields that were added to the parent content type by users will not be inherited. This causes a problem when the parent content type is “Document” and the document id service is enabled.
When the custom content type is set as default on the Document Library, you will see that there is no document ID generated when a document is uploaded to the library. If the default content type is set to “Document”, the Document ID is generated successfully.
When the Document ID service is enabled, SharePoint Server 2010 adds new columns and event receivers to the “Document” and “Document Set” content types. These event receivers assign document IDs which are then stored in these columns.
Document IDs are not generated with the custom content type because this content type does not inherit from the site collection “Document” content type. As a result, it does not get a copy of the event receivers which are responsible for generating the document Ids.
To overcome this, the event receivers will need to be associated manually with the custom content type. To associate the event receives, edit the content type’s elements.xml file and add <XmlDocuments> to specify the event receivers that generate Document IDs.
<?xml version="1.0" encoding="utf-8"?> <Elements xmlns="http://schemas.microsoft.com/sharepoint/"> <!-- Parent ContentType: Document (0x0101) --> <ContentType ID="0x010100093177ad58664d71831d2b1453421530" Name="DocContentType" Group="Custom Group" Description="My Content Type" Inherits="FALSE" Version="0"> <FieldRefs> </FieldRefs> <XmlDocuments> <XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/events"> <spe:Receivers xmlns:spe="http://schemas.microsoft.com/sharepoint/events"> <Receiver> <Name>Document ID Generator</Name> <Synchronization>Synchronous</Synchronization> <Type>10001</Type> <SequenceNumber>1000</SequenceNumber> <Assembly>Microsoft.Office.DocumentManagement, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly> <Class>Microsoft.Office.DocumentManagement.Internal.DocIdHandler</Class> <Data> </Data> <Filter> </Filter> </Receiver> <Receiver> <Name>Document ID Generator</Name> <Synchronization>Synchronous</Synchronization> <Type>10002</Type> <SequenceNumber>1001</SequenceNumber> <Assembly>Microsoft.Office.DocumentManagement, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly> <Class>Microsoft.Office.DocumentManagement.Internal.DocIdHandler</Class> <Data> </Data> <Filter> </Filter> </Receiver> <Receiver> <Name>Document ID Generator</Name> <Synchronization>Synchronous</Synchronization> <Type>10004</Type> <SequenceNumber>1002</SequenceNumber> <Assembly>Microsoft.Office.DocumentManagement, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly> <Class>Microsoft.Office.DocumentManagement.Internal.DocIdHandler</Class> <Data> </Data> <Filter> </Filter> </Receiver> <Receiver> <Name>Document ID Generator</Name> <Synchronization>Synchronous</Synchronization> <Type>10006</Type> <SequenceNumber>1003</SequenceNumber> <Assembly>Microsoft.Office.DocumentManagement, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly> <Class>Microsoft.Office.DocumentManagement.Internal.DocIdHandler</Class> <Data> </Data> <Filter> </Filter> </Receiver> </spe:Receivers> </XmlDocument> </XmlDocuments> </ContentType> </Elements>
This blog post is a contribution from Mustaq Patel, an engineer with the SharePoint Developer Support team.
This blog post demonstrates the use of SharePoint 2013 REST from AppWeb to issue HTTP POST on HostWeb. This does not require cross domain calls or the use of SP.RequestExecutor.js. Cross-site collection calls are demonstrated in Solving cross-domain problems in apps for SharePoint.
So let’s dive into the App. This App is an OnPrem SharePoint hosted app which will create a sub-site in the hostweb, create a custom List within that sub-site and create list item in the custom list.
This can be further enhanced to do the full CRUD operations using REST as demonstrated in http://msdn.microsoft.com/en-us/library/jj164022.aspx
Prerequisite
You will need a SharePoint 2013 environment that is configured to develop and deploy SharePoint Apps. If not please follow below MSDN to configure your environment for SharePoint Apps :How to: Set up an on-premises development environment for apps for SharePoint
Steps
1. Create a SharePoint Hosted App using Visual Studio 2012 using your on premise Site Url to deploy the App.
2. Edit default.aspx markup and add the following to PlaceHolderMain
<asp:Content ContentPlaceHolderID="PlaceHolderMain" runat="server"> <div> <p id="message"></p> </div> <br /><br /> <input id="btncreatespweb" type="button" value="Create SPWeb on HostWeb" onclick="createSPWeb();" /> <input id="btncreatesplist" type="button" value="Create SPList on HostWeb" onclick="createSPList();" /> <input id="btncreatesplistitem" type="button" value="Create SPListItem" onclick="createSPListItem();" /> </asp:Content>
3. Edit App.js and replace all the code with the following :
'use strict'; var hostweburl; var appweburl; // This code runs when the DOM is ready and creates a context object which is needed to use the SharePoint object model $(document).ready(function () { hostweburl= decodeURIComponent(getQueryStringParameter("SPHostUrl")); appweburl = decodeURIComponent(getQueryStringParameter("SPAppWebUrl")); }); function createSPWeb() { $.ajax( { url: appweburl + "/_api/SP.AppContextSite(@target)/web/webinfos/add?@target='" + hostweburl + "'", type: "POST", data: JSON.stringify( { 'parameters': { '__metadata': { 'type': 'SP.WebInfoCreationInformation' }, 'Url': 'RestSubWeb', 'Title': 'RestSubWeb', 'Description': 'REST created web', 'Language': 1033, 'WebTemplate': 'sts', 'UseUniquePermissions': false } } ), headers: { "accept": "application/json;odata=verbose", "content-type": "application/json;odata=verbose", "X-RequestDigest": $("#__REQUESTDIGEST").val() }, success: successHandler, error: errorHandler }); } function createSPList() { $.ajax( { url: appweburl + "/_api/SP.AppContextSite(@target)/web/lists?@target='" + hostweburl + "/RestSubWeb'", type: "POST", data: JSON.stringify({ '__metadata': { 'type': 'SP.List' }, 'AllowContentTypes': true, 'BaseTemplate': 100, 'ContentTypesEnabled': true, 'Description': 'My TestCustomList description', 'Title': 'TestCustomList' }), headers: { "accept": "application/json;odata=verbose", "content-type": "application/json;odata=verbose", "X-RequestDigest": $("#__REQUESTDIGEST").val() }, success: successHandler, error: errorHandler }); } function createSPListItem() { $.ajax( { url: appweburl + "/_api/SP.AppContextSite(@target)/web/lists/GetByTitle('TestCustomList')/items?@target='" + hostweburl + "/RestSubWeb'", type: "POST", data: JSON.stringify({ '__metadata': { 'type': 'SP.Data.TestCustomListListItem' }, 'Title': 'Added from RESTSPApp' }), headers: { "accept": "application/json;odata=verbose", "content-type": "application/json;odata=verbose", "X-RequestDigest": $("#__REQUESTDIGEST").val() }, success: successHandler, error: errorHandler }); } function successHandler() { $('#message').text('Success'); } function errorHandler(data, errorCode, errorMessage) { $('#message').text('Error ' + errorMessage); } function getQueryStringParameter(paramToRetrieve) { var params = document.URL.split("?")[1].split("&"); var strParams = ""; for (var i = 0; i < params.length; i = i + 1) { var singleParam = params[i].split("="); if (singleParam[0] == paramToRetrieve) return singleParam[1]; } }
4. Next navigate to AppManifest –> Permissions. Select Scope=Site Collection and Permission=Full Control
5. Deploy the App. Once deployed, the default page should load
6. Click “Create SPWeb on HostWeb”. Once you see the text “Success” above the button, go to the Host web where you will see the new sub-site “RestSubWeb” created
7. Create a List and List item using “Create SPList on HostWeb” and “Create SPListIiem” buttons.
Here’s how the code works without using cross-domain JavaScript library. The AppContextSite object will create appropriate context based on @target parameter and call the appropriate REST endpoint to do POST to SharePoint. REST url syntax used here is appWebUrl/_api/SP.AppContextSite(@target)/web?@target='http://devsite'
Where appWebUrl/_api/SP.AppContextSite(@target)/ does not change but the parameters web? is the based the object that you’re accessing and @target value is the url of the site from where you are trying to access it.
For example to access list on the host web use appWebUrl/_api/SP.AppContextSite(@target)/web/lists?@target='” +hostWebUrl+ “’”
To create custom field for a list in host web
appWebUrl/_api/SP.AppContextSite(@target)/web/lists(guid ‘list GUID’)/Fields?@target='” +hostWebUrl+ “’”
I hope this simple demo helps writing robust Apps for SharePoint using REST API.
This blog post is a contribution from Charls Tom Jacob, an engineer with the SharePoint Developer Support team.
By default, new events added to SharePoint calendar list are not set to be recurring. If all the events users create are going to be recurring, it would be convenient to have this checkbox checked by default, along with some default values populated for the recurrence details.
This can be easily achieved by doing some customization to the list form using SharePoint Designer and JavaScript.
Follow the steps below:
1. Open the list in SharePoint designer
2. Clicks Forms section and click New Item Form from the ribbon
3. Set it as the default form
4. Edit this file in advance mode. Place the following JavaScript after the PlaceHolderMain:
<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">
JavaScript code to set the Recurrence checkbox checked and to set some default value for recurrence.
<script language="javascript" type="text/javascript"> // Push these methods for execution after the page load _spBodyOnLoadFunctionNames.push("EnableRecurrence"); _spBodyOnLoadFunctionNames.push("ChangeRecurrenceSettings"); // Utility method to get the HTML element for a sever side control based on a partially matching name function findHtmlControl(ctrlName) { var inputs = document.getElementsByTagName("input"); for (var i = 0; i < inputs.length; i++) { if(inputs[i].id.indexOf(ctrlName) > 0) return inputs[i] } } function EnableRecurrence() { // Server generated Id for the checkbox would look like this : // ctl00$m$g_86f2fc9c_f288_4ea5_8967_96c53e5a8aa5$ff71$ctl00$ctl00$RecurrenceField var RecurrenceField= findHtmlControl("RecurrenceField"); if(RecurrenceField.checked == false) { // Set the checkbox to checked RecurrenceField.checked = true; // Fire the click event which initiates a post-back which will display the recurrence details section RecurrenceField.onclick(); } } function ChangeRecurrenceSettings() { var repeatInstances= findHtmlControl("repeatInstances"); var endDateRangeType= findHtmlControl("endDateRangeType1"); // Select the "End after" option button if(endDateRangeType != null) { endDateRangeType.checked = true; } // Set number of occurrences to 12 instead of the default 10 if(repeatInstances != null) { repeatInstances.value = 12; } } </script>
5. Save the changes and navigate to the SharePoint site, create a new Event. The form would open with recurrence checkbox checked and “End after” set to 12
This blog post is a contribution from David Wilborn, an engineer with the SharePoint Developer Support team.
I recently worked a project where we needed to add a custom field to blog posts and use the new Client Side Rendering in SharePoint 2013 to display the field. My first thought was to create a new custom template to render the entire post item. This would allow us to place the new field wherever we liked. While this may be the logical choice in some cases, it practice it takes a lot of work if you want your blog post to look like the out-of-the-box version. There is a lot of customization and custom logic in the blog post rendering.
A simpler option is to set up your custom field to be rendered along with another field. This allows you to keep the default look of the post without having to replicate all of the existing layout and logic yourself.
Note: Custom Client-Side Rendering scripts can sometimes have issues with the Minimal Download Strategy feature. See the following blog post for more information: http://blogs.msdn.com/b/sridhara/archive/2013/02/08/register-csr-override-on-mds-enabled-sharepoint-2013-site.aspx
In my example, I will be adding a “Subtitle” field that I want to be displayed above the main blog post Body text. The first step is to add the Subtitle field to the Post list for my blog site. I can do this through the GUI by going to Settings -> SiteContent -> Posts.
In this case I am adding a simple text field named “Subtitle.” Select the “LIST” tab in the upper right corner, and go to “List Settings” on the ribbon:
Toward the bottom of the page above the “Views” section, click “Create column.” Enter “Subtitle” for the column name. You can leave the remainder of the settings at their defaults, and click “OK.” Be sure to enter some values into the Subtitle fields in your Posts list so that we can see them once we’ve modified the template.
The next step is to add my new field to the current view that displays the blog post. I navigate to the main page of my blog site and edit the page (Page tab, select “Edit Page” from the ribbon). Next, I edit the “Posts” web part:
In the “List Views” section, click “Edit the current view” (underneath “Selected View”):
I select the checkbox for the field I created and click “OK” to add it:
The field is now available to be rendered as part of the current view. Note that this custom view is saved as part of the web part page, and is not saved to the views that are part of the Posts list itself. Next, I create the custom JavaScript file that will render the new field. I’ll be adding the “Subtitle” rendering to the renderer for the post Body, since I am displaying the Subtitle over the body. The first part of the code does the work to associate our custom rendering code with the Body field.
Note the “(function() { …” syntax which causes the JavaScript to be executed immediately. This code creates a template for the Body field when it is displayed in a View form, and associates it with our custom rendering function, named CBody. The template is then registered with the Template Manager.
(function () {
var overrideCtx = {};
overrideCtx.Templates = {};
overrideCtx.Templates.Fields = {'Body':{'View':CBody}};
SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideCtx);
})();
Next we’ll implement the “CBody” function, which is our custom client-side rendering function. This is simply JavaScript code that outputs the HTML we want displayed when the field is rendered by the web part.
function CBody(ctx) {
var ret = "<b>" + ctx.CurrentItem.Subtitle + "</b><hr/>" + ctx.CurrentItem.Body;
return ret; }
The ctx parameter passed in to our custom function gives us access to the current item and its fields. More on this shortly. You can see that the code renders the Subtitle field in bold, followed by a horizontal rule, and then the Body field. So our final JavaScript file contains both of these functions:
overrideCtx.Templates.Fields = {'Body':{'View': CBody}};
return ret;
}
Save the file as formatblogpost.js in your C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\TEMPLATE\LAYOUTS directory (or equivalent location in your 15 hive).
The final step is to tell your Posts web part to use the custom JavaScript code. Edit your blog page, and edit the Posts web part. In the Miscellaneous section at the bottom of the web part, specify “formatblogpost.js” (without the quotes) in the JS Link field:
Save your web part and stop editing the page. Do an IISRESET to ensure that the new template is used. If everything has worked correctly, you should see your custom field rendering in your blog posts:
Let’s take a closer look at the available fields using the JavaScript debugger:
When you hit the breakpoint, click the “Watch” tab in the right plane. Click on the Click to add… text and type “ctx.CurrentItem” in the line.
Expand the ctx.CurrentItem node by clicking the plus sign to the left:
You can see the fields that are available to your JavaScript code, including the new “Subtitle” field that we created. Since the displayed field names can vary from the internal names, the JavaScript debugger can be invaluable for debugging any issues you have with Client Side Rendering.