Corey Roth [MVP] : SharePoint Online, SharePoint, Office 365http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/tags/SharePoint+Online/SharePoint/Office+365/default.aspxTags: SharePoint Online, SharePoint, Office 365enCommunityServer 2007.1 (Build: 20917.1142)Creating an Azure Logic app that connects with SharePoint Onlinehttp://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2015/03/26/creating-an-azure-logic-app-that-connects-with-sharepoint-online.aspxThu, 26 Mar 2015 17:51:13 GMTceb7fe2a-c56b-4d85-99e6-8dd548580538:7180CoreyRoth3http://www.dotnetmafia.com/blogs/dotnettipoftheday/rsscomments.aspx?PostID=7180http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2015/03/26/creating-an-azure-logic-app-that-connects-with-sharepoint-online.aspx#comments<p>Logic apps are a great new feature in Microsoft Azure that lets you connect to a variety of systems and create triggers and events.&#160; This allows you to reproduce some interesting light workflow functionality in an easy manner.&#160; It comes with connectors for common Microsoft products like SharePoint Online, Yammer, and BizTalk.&#160; However, it also includes connects to services like Twitter, SalesForce, SAP, and Dropbox.&#160; It even includes a connector for on-premises SharePoint 2013 servers although it requires some additional configuration.</p> <p>As Logic apps are part of Azure, this is definitely a developer focused feature.&#160; I don’t see end users working with these directly.</p> <p>Today, we’re going to look at expanding the <a href="http://azure.microsoft.com/en-us/documentation/articles/app-service-logic-create-a-logic-app/">Twitter / Dropbox example</a> and expand that for use into SharePoint.&#160; That example stopped at only inserting the first tweet into a file on Dropbox.&#160; This example, will insert all tweets that have been returned from a user’s Mention timeline into a list on SharePoint Online.&#160; </p> <p>First, I started by creating a simple custom list on my SharePoint Online (Office 365) site.&#160; I am just using the Title column as well as a custom column named TwitterAccount to capture who sent the tweet.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppSharePointListColumns_0049D352.png"><img title="AzureLogicAppSharePointListColumns" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="AzureLogicAppSharePointListColumns" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppSharePointListColumns_thumb_62D78245.png" width="454" height="178" /></a></p> <p>Like in the Twitter / Dropbox example, we need to create our connectors first.&#160; We’ll create the Twitter Connector first. Start by logging into the new Azure portal at <a href="http://portal.azure.com">http://portal.azure.com</a>.&#160; Then click <strong>New</strong>, <strong>Developer Services</strong>, <strong>Azure Marketplace</strong>, <strong>API Apps</strong>.</p> <a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppNewConnector_3B9D3910.png"><img title="AzureLogicAppNewConnector" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="AzureLogicAppNewConnector" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppNewConnector_thumb_4BAC07C7.png" width="504" height="298" /></a> <p>Select <strong>Twitter Service </strong>and then click <strong>Create</strong>.&#160; Here you will be presented with some standard Azure settings that you will need to specify such as the App Service Plan, Resource Group, Subscription, and Location.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppNewConnectorTwitterDetails_19483448.png"><img title="AzureLogicAppNewConnectorTwitterDetails" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="AzureLogicAppNewConnectorTwitterDetails" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppNewConnectorTwitterDetails_thumb_549B9A06.png" width="504" height="460" /></a></p> <p>Now, we need to repeat the process for the SharePoint Online connector.&#160; Select the SharePoint Online connector out of the API Services list.&#160; Here you need to also specify the URL to your site as well as the relative URL to the list.&#160; For example, for my Tweets list I created, I specified a relative URL of <em>Lists/Tweets</em>.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/LogicAppsSPOConnector_465D2116.png"><img title="LogicAppsSPOConnector" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="LogicAppsSPOConnector" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/LogicAppsSPOConnector_thumb_68B4B68F.png" width="504" height="329" /></a></p> <p>Now that we have created our connectors, we can get started with our Logic apps.&#160; Create a new one by choosing Logic app under the API Services list.&#160; If you can’t find any of these connectors or apps you can always just search for them.&#160; After you click <strong>Create</strong>, you will be prompted for some settings.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppNewWithSettings_6F67C012.png"><img title="AzureLogicAppNewWithSettings" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="AzureLogicAppNewWithSettings" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppNewWithSettings_thumb_55FFBCD8.png" width="504" height="456" /></a></p> <p>Give your app a name and then click <strong>Triggers and Actions</strong> to begin designing your app.&#160; The canvas will appear and you can begin designing your app.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppCanvas_388D6BCC.png"><img title="AzureLogicAppCanvas" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="AzureLogicAppCanvas" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppCanvas_thumb_1F256892.png" width="504" height="316" /></a></p> <p>To get started you simply click on the connectors you want to use.&#160; The Twitter / Dropbox example starts with a recurrence connector to execute the task once an hour.&#160; I didn’t want to accidently leave this app running so I checked the box <strong>Run this logic manually</strong>.&#160; </p> <p>Now we’ll click on the Twitter connector to pull data out of Twitter.&#160; The first time you do this you will be prompted for authentication by Twitter.&#160; Once you login, it will show a list of common actions.&#160; However, you can click the “…” to see more.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppCanvasTwitterDefault_25D87215.png"><img title="AzureLogicAppCanvasTwitterDefault" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="AzureLogicAppCanvasTwitterDefault" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppCanvasTwitterDefault_thumb_417CFE0B.png" width="504" height="435" /></a></p> <p>In our case, we want to select <strong>Get Mentions Timeline</strong>.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppCanvasTwitterGetMentions_4830078E.png"><img title="AzureLogicAppCanvasTwitterGetMentions" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="AzureLogicAppCanvasTwitterGetMentions" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppCanvasTwitterGetMentions_thumb_7CD063C9.png" width="354" height="604" /></a></p> <p>Now, we need to add our SharePoint Online Connector by clicking on it.&#160; It will also prompt you for authentication as well.&#160; It will then present you with common actions that you can preform on your list.&#160; It will mention your list directly by name.&#160; Choose <strong>Insert into &lt;listname&gt;</strong> to continue.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppSharePointConnectorDefault_6E91EAD9.png"><img title="AzureLogicAppSharePointConnectorDefault" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="AzureLogicAppSharePointConnectorDefault" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppSharePointConnectorDefault_thumb_0A3676D0.png" width="354" height="613" /></a></p> <p>We’ll be able to start mapping our data to the columns in the SharePoint list.&#160; You can find the other fields by clicking on the “…” button.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppSharePointConnectorInsertItem_0553C314.png"><img title="AzureLogicAppSharePointConnectorInsertItem" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="AzureLogicAppSharePointConnectorInsertItem" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppSharePointConnectorInsertItem_thumb_5A0F2C0C.png" width="404" height="282" /></a></p> <p>Don’t do the mapping yet though.&#160; In the online example, it only inserted the first list item.&#160; We’re going to implement repeating functionality to insert all of the list items.&#160; Click on the cogwheel and choose <strong>Repeat over a list</strong>. </p> <p>&#160;<a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppSharePointConnectorRepeaterMenu_04E7901F.png"><img title="AzureLogicAppSharePointConnectorRepeaterMenu" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="AzureLogicAppSharePointConnectorRepeaterMenu" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppSharePointConnectorRepeaterMenu_thumb_07904BD0.png" width="404" height="264" /></a></p> <p>You can also specify conditions from this menu but we’ll cover that in the future.&#160; Now we want to specify the fields to use in our repeater.&#160; In the <strong>Repeat </strong>Field click the … icon next to it and choose <strong>Get Mentions Timeline</strong>.&#160; This will add the text <em>@body(‘twitterconnector’)</em> which is an internal syntax used by Logic apps.&#160; Now we want to write the Tweet Text to the Title field and the Tweeted By field to the TwitterAccount field.&#160; You can look at the options specified in the “…” menu but they don’t quite work right for repeaters.&#160; You can start by picking <strong>This Action</strong> but we still have a bit of work to do.&#160; It will add <em>@repeatItem()</em> to the box when you click it.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppSharePointConnectorRepeatItem_7951D2DF.png"><img title="AzureLogicAppSharePointConnectorRepeatItem" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="AzureLogicAppSharePointConnectorRepeatItem" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppSharePointConnectorRepeatItem_thumb_072418DB.png" width="404" height="86" /></a></p> <p>Now we need to specify the field name.&#160; We can actually infer this by looking at the Twitter Connector or by looking at the syntax it uses when we select a field from the “…” icon.&#160; Since this is a preview release, the selectors for these fields don’t always give the same results.&#160; Sometime if you select a field name it will do it correctly.&#160; Other times it gives us the syntax that only returns the value from the first item returned.&#160; Ultimately, I want to end up with a value of <em>@repeatItem().TweetText</em> for the <strong>Title</strong> field and a value of @repeatItem().Tweeted_By for the <strong>TwitterAccount</strong> field.&#160; Here’s what the final settings look like.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppSharePointConnectorRepeatingSettings_5BDF81D3.png"><img title="AzureLogicAppSharePointConnectorRepeatingSettings" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="AzureLogicAppSharePointConnectorRepeatingSettings" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppSharePointConnectorRepeatingSettings_thumb_62928B56.png" width="404" height="436" /></a></p> <p>Click the checkbox to accept the values and then click Save at the top.&#160; Finally you want to click <strong>Create</strong> to begin creation of your Logic app.&#160; If you are going to make a lot of triggers and actions, I recommend you click Create first before you create them.&#160; This will allow you save your progress as you work on things.&#160; Nothing will be saved until you have created the Logic app itself first.</p> <p>Now are ready to manually run the Logic app manually.&#160; Click the Run Now button at the top to begin.&#160; You’ll see the progress in the Operations section at the bottom.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppSettings_492A881C.png"><img title="AzureLogicAppSettings" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="AzureLogicAppSettings" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppSettings_thumb_56909B22.png" width="504" height="613" /></a></p> <p>Go back to the list on your SharePoint site to verify that the functionality worked.&#160; You can see the list of tweets that mentioned me have been populated.&#160; </p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppTweetList_19033D59.png"><img title="AzureLogicAppTweetList" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="AzureLogicAppTweetList" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/AzureLogicAppTweetList_thumb_5F802D61.png" width="504" height="281" /></a></p> <p>I noticed they didn’t necessarily come in the right order so it might be a good idea to bring in the date column as well.&#160; When I made a schema change to my list earlier I had to recreate the SharePoint Online Connector for it to show up.&#160; There might be a way to refresh it though.</p> <p>Logic Apps are an interesting technology with a lot of promise.&#160; It makes some scenarios rather easy to implement in a short time.&#160; I’ll continue to work with the technology to get a feel for what it can do and what the limitations are.&#160; Try it out for yourself and see what you can do.</p><img src="http://www.dotnetmafia.com/aggbug.aspx?PostID=7180" width="1" height="1">SharePointSharePoint OnlineOffice 365Office 365 GridAzureLogic appTroubleshooting errors with the client-side People Picker (SPClientPeoplePicker)http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2015/03/02/troubleshooting-errors-with-the-client-side-people-picker-spclientpeoplepicker.aspxMon, 02 Mar 2015 20:41:12 GMTceb7fe2a-c56b-4d85-99e6-8dd548580538:7151CoreyRoth0http://www.dotnetmafia.com/blogs/dotnettipoftheday/rsscomments.aspx?PostID=7151http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2015/03/02/troubleshooting-errors-with-the-client-side-people-picker-spclientpeoplepicker.aspx#comments<p>I’ve done quite a bit of work with the <a href="https://msdn.microsoft.com/en-us/library/jj713593.aspx">client-side People Picker</a> in SharePoint-hosted apps lately.&#160; I find that while it is relatively easy to get set up, I found it was quite easy to break.&#160; I have found that it tended to work better in Office 365 than it does in on-premises SharePoint 2013.&#160; I ran into two common issues. First, I found that after you did a query for users, you could not actually select the user.&#160; The second related issue is that you could select the user, but the textbox would remain empty and not show the user’s name.</p> <p>In my troubleshooting of the issue, I found that the behavior differed based on a variety of things including what user was using the page, how you authenticated, and which browser you were using.&#160; When I was experiencing issues, I found that administrator users never had a problem.&#160; That figures.&#160; Low-privilege users would not be able to get the control to work.&#160; Then I discovered that it would work for these users if they were not auto logged into the site with Internet Explorer.&#160; Here, I am referring to the setting <strong>Automatic logon with current user name and password</strong>.&#160; If the user typed in their credentials manually, it would actually work.&#160; Quite strange.</p> <p>After further investigation, I discovered that access to the <strong>spMgr</strong> object deep in <strong>clientpeoplepicker.js</strong> was coming back as undefined.&#160; This turns out to be in a block of code that shows presence information for the user.&#160; I found that if I disabled presence, it fixed the issue for users in Internet Explorer.&#160; To disable presence, set <strong>ShowUserPresence</strong> to false on the <strong>SPClientPeoplePicker</strong> object.&#160; Do this right before your call to <strong>SPClientPeoplePicker_InitStandaloneControlWrapper</strong>.</p> <div style="background:white;"> <p style="margin:0px;"><font face="Consolas"><font color="#000000"><font style="font-size:10pt;">SPClientPeoplePicker.ShowUserPresence = </font></font><font style="font-size:10pt;"><span><font color="#0000ff">false</font></span><font color="#000000">;</font></font></font></p> </div> <p>This resolved my issues in Internet Explorer.&#160; However, Chrome was displaying some erratic behavior.&#160; Sometimes the People Picker would work, but most of the time it still wouldn’t.&#160; Although this technique worked for some browsers, I don’t think it was the final solution.&#160; I found the final solution also in the debugger.&#160; I reported the following error in <strong>clienttemplates.js:</strong></p> <p><em>Strings is not defined.</em></p> <p>The odd thing is that the minified version of <strong>strings.js</strong> was actually included on the page automatically but it didn’t work for some reason.&#160; Strings.js is the JavaScript file that contains all of the multilingual string resources for your current culture.&#160; I included a hard reference to the US English version of the strings.js file in the 1033 folder.&#160; </p> <div style="background:white;"> <p style="margin:0px;"><font face="Consolas"><span><font color="#0000ff"><font style="font-size:10pt;">&lt;</font></font></span><font style="font-size:10pt;"><span><font color="#800000">script</font></span><font color="#000000"> </font><span><font color="#ff0000">type</font></span><span><font color="#0000ff">=&quot;text/javascript&quot;</font></span><font color="#000000"> </font><span><font color="#ff0000">src</font></span><span><font color="#0000ff">=&quot;/_layouts/15/1033/strings.js&quot;&gt;&lt;/</font></span><span><font color="#800000">script</font></span></font><span><font style="font-size:10pt;" color="#0000ff">&gt;</font></span></font></p> </div> <p>Once I did that, all of my People Picker issues were resolved.&#160; You may want to correct culture code for the languages you use.</p><img src="http://www.dotnetmafia.com/aggbug.aspx?PostID=7151" width="1" height="1">SharePointErrorSharePoint OnlineOffice 365SharePoint 2013AppsJavaScriptConfiguring Information Rights Management (IRM) on a document library in Office 365http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2015/02/03/configuring-information-rights-management-irm-on-a-document-library-in-office-365.aspxTue, 03 Feb 2015 22:57:58 GMTceb7fe2a-c56b-4d85-99e6-8dd548580538:7130CoreyRoth2http://www.dotnetmafia.com/blogs/dotnettipoftheday/rsscomments.aspx?PostID=7130http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2015/02/03/configuring-information-rights-management-irm-on-a-document-library-in-office-365.aspx#comments<p>Last year, I wrote a post on how to get started using <a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2013/03/28/configuring-and-using-irm-with-office-365-and-sharepoint-online.aspx">Information Rights Management in Office 365</a>.&#160; That post covered what you need to get started as well as how to enable IRM at an individual file level.&#160; This post is a follow-up to that one and covers how to configure IRM on a document library.</p> <p>If you haven’t enabled Rights Management in your Office 365 subscription, you’ll need to do that first.&#160; My previous <a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2013/03/28/configuring-and-using-irm-with-office-365-and-sharepoint-online.aspx">post</a> can walk you through the steps.&#160; You’ll find <strong>Rights Management </strong>under <strong>Service Settings</strong> now on the administration site.&#160; Once IRM is configured on you subscription, go to the document library and view the <strong>Library Settings</strong>.&#160; From there, click the <strong>Information Rights Management </strong>link.&#160; When you configure IRM for the first time on a library, it will prompt you for a policy name and description.&#160; This message here will be visible to users when viewing the document.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibraryConfigureDefault_1FCC0E20.png"><img title="IRMDocumentLibraryConfigureDefault" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="IRMDocumentLibraryConfigureDefault" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibraryConfigureDefault_thumb_2927D354.png" width="504" height="235" /></a></p> <p>Click the <strong>SHOW OPTIONS</strong> link to view the settings we can configure on the document library.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibraryAdvancedSettings_48D6AD1C.png"><img title="IRMDocumentLibraryAdvancedSettings" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="IRMDocumentLibraryAdvancedSettings" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibraryAdvancedSettings_thumb_640F061D.png" width="554" height="559" /></a></p> <p>You have quite a bit of flexibility to configure what users can do to your files from here.&#160; However, you don’t quite have the level of granularity on permissions that you do when configuring Rights Management from within Office.&#160; In the first section, you can prevent users from allowing documents that don’t support IRM.&#160; For example, if you upload an XML file (at least I am pretty sure you can’t do IRM on that), the file upload will be blocked.&#160; </p> <p>You can also turn IRM after a period of time.&#160; This setting is good if you know that the documents are only sensitive for a period of time.&#160; For example, maybe an acquisition is occurring on a certain date and then the documents are no longer sensitive.</p> <p>You can also prevent users from viewing the documents in the browser.&#160; You may have various reasons for why you would want to do this.&#160; It’s not enabled by default, but you can turn it on.&#160; When you do have it enabled, the Document Preview panel will not show in the document library and clicking on the document opens it directly in Office.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibraryOWADisabled_0183E7DB.png"><img title="IRMDocumentLibraryOWADisabled" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="IRMDocumentLibraryOWADisabled" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibraryOWADisabled_thumb_4F20145B.png" width="504" height="323" /></a></p> <p>If you do allow your users to view documents in the browser, they will get an error message when using the document preview functionality.&#160; However, they can still view the document in the browser using Office Online (formerly Office Web Apps) when clicking on the <strong>OPEN </strong>link.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibraryPreviewDenied_710B76DF.png"><img title="IRMDocumentLibraryPreviewDenied" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="IRMDocumentLibraryPreviewDenied" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibraryPreviewDenied_thumb_5EC2B01D.png" width="504" height="364" /></a></p> <p>In the <em>Configure document access rights</em> section you can prevent users from printing the document.&#160; This simply disables the print dialog inside Office.&#160; Users could still find ways to get around this if they tried hard enough.&#160; Remember that the settings in this section affect viewers and not users with full control permissions.</p> <p>You can also set how long the user’s rights are valid before they expire on the document.&#160; This setting is important.&#160; If a user downloads the document and copies it to a flash drive, he or she will be prompted once to get rights to the document.&#160; Now, if the user left the company but still had the file they won’t be able to open it once the rights expire.&#160; You just need to pick the number of days before the license expires.&#160; The higher the value, the less often the user has to login to get rights again.&#160; However, if the value is too small, you may annoy the user by prompting them more often to get rights.</p> <p>In the <em>set group protection and credentials </em>settings, you can specify how often users must verify their credentials.&#160; You can also specify a security group where users are allowed to share the documents with other members of the group.&#160; This is completely different from the security settings on the document library.&#160; For example, a user might not have access to the document library, but they received a file from it on a flash drive.&#160; If the user is in the security group you specify here, he or she will be able to open the file.</p> <p>Once you have configured, IRM on your document library you can test it.&#160; Clicking on the document will show it in Office Online.&#160; The message you configured for the policy at the top.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibraryOfficeOnlineWithMessage_7347FF9B.png"><img title="IRMDocumentLibraryOfficeOnlineWithMessage" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="IRMDocumentLibraryOfficeOnlineWithMessage" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibraryOfficeOnlineWithMessage_thumb_40E42C1C.png" width="554" height="229" /></a></p> <p>When you click on <strong>OPEN IN WORD</strong>, we’ll see the document with additional settings in the tool tip.&#160; Since I have full control of the document library, I have permissions to edit the rights management policy from within Word using the <strong>Change Settings</strong> button.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibraryWordMessage_4797359F.png"><img title="IRMDocumentLibraryWordMessage" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="IRMDocumentLibraryWordMessage" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibraryWordMessage_thumb_472B02AA.png" width="554" height="263" /></a></p> <p>To really verify functionality, we will want to login with another user account.&#160; In my example, I logged in with a user that has Viewer permissions.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibraryWordMessageViewOnly_14C72F2B.png"><img title="IRMDocumentLibraryWordMessageViewOnly" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="IRMDocumentLibraryWordMessageViewOnly" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibraryWordMessageViewOnly_thumb_093171EC.png" width="554" height="210" /></a></p> <p>Clicking <strong>View Permissions</strong>, I can see what permissions the user has exactly.&#160; It also tells you when your permissions expire.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibraryWordPermissionsView_4BA41422.png"><img title="IRMDocumentLibraryWordPermissionsView" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="IRMDocumentLibraryWordPermissionsView" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibraryWordPermissionsView_thumb_2469CAED.png" width="304" height="406" /></a></p> <p>When you look at the back stage in Office, you’ll see that many of the links are disabled such as <strong>Print</strong>.&#160; </p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibraryBackStagePermissionsDenied_38EF1A6B.png"><img title="IRMDocumentLibraryBackStagePermissionsDenied" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="IRMDocumentLibraryBackStagePermissionsDenied" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibraryBackStagePermissionsDenied_thumb_6FCBFF62.png" width="554" height="311" /></a></p> <p>If your expiration date is getting near, you’ll see a message inside Office notifying you.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibraryExpiration_2393F5B4.png"><img title="IRMDocumentLibraryExpiration" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="IRMDocumentLibraryExpiration" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibraryExpiration_thumb_2CEFBAE8.png" width="554" height="201" /></a></p> <p>The settings for Information Rights Management have matured quite a bit since SharePoint 2010.&#160; You have a lot more control of what you can specify as defaults in your document library.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibrary2010Mode_61901723.png"><img title="IRMDocumentLibrary2010Mode" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="IRMDocumentLibrary2010Mode" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/IRMDocumentLibrary2010Mode_thumb_2F2C43A4.png" width="554" height="304" /></a></p> <p>Information Rights Management is a great feature in Office 365 and easy to set up.&#160; I find it is still highly under utilized by most organizations.</p><img src="http://www.dotnetmafia.com/aggbug.aspx?PostID=7130" width="1" height="1">SharePointSharePoint OnlineOffice 365OfficeA quick way to get to list settings in SharePoint-hosted appshttp://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/12/16/a-quick-way-to-get-to-list-settings-in-sharepoint-hosted-apps.aspxTue, 16 Dec 2014 20:53:38 GMTceb7fe2a-c56b-4d85-99e6-8dd548580538:7094CoreyRoth0http://www.dotnetmafia.com/blogs/dotnettipoftheday/rsscomments.aspx?PostID=7094http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/12/16/a-quick-way-to-get-to-list-settings-in-sharepoint-hosted-apps.aspx#comments<p>If you are a developer working with the SharePoint app model and have deployed a list, you might have noticed that the list settings button is missing from the ribbon.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SharePointAppListRibbonNoListSettings_2D7F9249.png"><img title="SharePointAppListRibbonNoListSettings" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SharePointAppListRibbonNoListSettings" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SharePointAppListRibbonNoListSettings_thumb_29091182.png" width="542" height="139" /></a></p> <p>Now, you typically probably don’t want to adjust list settings inside an app through the UI.&#160; However, it’s not uncommon that you want to look at your list settings in the development process to confirm you did everything right.&#160; For example, maybe you want to check the permissions on the list.&#160; </p> <p>If you know the List Id, you can always just go directly to ListEdit.aspx, but I find that’s a lot of work.&#160; Instead, I found the easiest way is go to active the <strong>LIST</strong> ribbon and then click <strong>Modify View</strong>.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SharePointAppListRibbonModifyView_5DA96DBD.png"><img title="SharePointAppListRibbonModifyView" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SharePointAppListRibbonModifyView" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SharePointAppListRibbonModifyView_thumb_645C7740.png" width="539" height="172" /></a></p> <p>That will take you to a page where you can edit a view.&#160; However, it also gives you a breadcrumb back to <strong>Settings</strong>.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SharePointAppModifyListViewSettingsBreadcrumb_1D07214E.png"><img title="SharePointAppModifyListViewSettingsBreadcrumb" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SharePointAppModifyListViewSettingsBreadcrumb" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SharePointAppModifyListViewSettingsBreadcrumb_thumb_1FAFDCFF.png" width="541" height="167" /></a></p> <p>You’ll now be on your list settings page.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SharePointAppListSettings2_2662E682.png"><img title="SharePointAppListSettings2" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SharePointAppListSettings2" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SharePointAppListSettings2_thumb_2D15F005.png" width="539" height="294" /></a></p> <p>This is a great way to manually set permissions on your list when needed.&#160; You can also adjust a number of other settings.&#160; Just be warned that some settings don’t work such as <em>Information Management Policy Settings</em>.&#160; You will get a 500 error when you do that.</p> <p>This is a simple tip but maybe some of you developers out there will find it useful.</p><img src="http://www.dotnetmafia.com/aggbug.aspx?PostID=7094" width="1" height="1">SharePointSharePoint OnlineOffice 365AppsVisual Studio 2013Removing links from the Office 365 suite barhttp://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/06/16/removing-links-from-the-office-365-suite-bar.aspxMon, 16 Jun 2014 16:38:25 GMTceb7fe2a-c56b-4d85-99e6-8dd548580538:6801CoreyRoth0http://www.dotnetmafia.com/blogs/dotnettipoftheday/rsscomments.aspx?PostID=6801http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/06/16/removing-links-from-the-office-365-suite-bar.aspx#comments<p>When it comes to Office 365, you may not want to have some of the links enabled at the top such as Yammer, OneDrive, and Sites.&#160; Although, I don&#39;t recommend disabling them.&#160; Sometimes you just aren&#39;t ready or there is a business reason preventing you from showing those links. These settings creeped into Office 365 a while back, but I still have people ask me about them from time to time, so I thought it warranted a quick blog post.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/Office365SuiteBar_5FCB9C61.png"><img title="Office365SuiteBar" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="Office365SuiteBar" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/Office365SuiteBar_thumb_7450EBDF.png" width="524" height="47" /></a></p> <p>These settings can be changed by your Office 365 Global Administrator by going to the SharePoint tenant administration site.&#160; Click on the <em>Settings</em> link and you&#39;ll find the options to show and hide the OneDrive for Business, Yammer / Newsfeed, and Sites links.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/TenantAdminSuiteBarHideLinks_41ED1860.png"><img title="TenantAdminSuiteBarHideLinks" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="TenantAdminSuiteBarHideLinks" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/TenantAdminSuiteBarHideLinks_thumb_33AE9F70.png" width="525" height="84" /></a></p> <p>Hide the links that you don&#39;t want to show and then click <em>Save</em> at the bottom of the page.&#160; Once you save your changes, you won&#39;t see the navigation change right away.&#160; It seems to take about five minutes.&#160; It seems to be cached per user as well.&#160; Just be patient and the changes should be made.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/Office365SuiteBarCustomized_2F381EA9.png"><img title="Office365SuiteBarCustomized" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="Office365SuiteBarCustomized" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/Office365SuiteBarCustomized_thumb_1CEF57E7.png" width="468" height="45" /></a></p> <p>If you want to customize any of the other links in SharePoint (or add your own), you&#39;ll have to revert back to customizing the master page.&#160; It&#39;s not ideal, but it&#39;s possible.</p><img src="http://www.dotnetmafia.com/aggbug.aspx?PostID=6801" width="1" height="1">SharePointSharePoint OnlineOffice 365Office 365 GridNew branding and publishing content on Office 365 demo siteshttp://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/06/02/new-branding-and-publishing-content-on-office-365-demo-sites.aspxMon, 02 Jun 2014 14:25:17 GMTceb7fe2a-c56b-4d85-99e6-8dd548580538:6791CoreyRoth3http://www.dotnetmafia.com/blogs/dotnettipoftheday/rsscomments.aspx?PostID=6791http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/06/02/new-branding-and-publishing-content-on-office-365-demo-sites.aspx#comments<p>As a partner, you have access to <a href="http://www.microsoftofficedemos.com">MicrosoftOfficeDemos.com</a>, where you can provision a free Office 365 tenant good for 90 days.&#160; If you aren&#39;t familiar with these demo sites in SharePoint Online, they come packed with demo content to demonstrate various Intranet scenarios.&#160; All you need is a Live ID associated with your organization&#39;s partner record.&#160; If you are a Microsoft partner and don&#39;t have your Live ID associated, the person that manages your partnership in your company can help get you associated.</p> <p>What I love about these demo sites is that they are constantly being updated.&#160; The latest revisions brings us a new home page demonstrating a carousel feature for showing off articles using display templates.&#160; You can click on the image in the carousel, however, I find getting to the article to be not very user friendly.&#160; You have to click on the item in the carousel and then click on the large image above.&#160; This might be better implemented using a hover effect.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosNewHomePageTop_097EFE90.png"><img title="OfficeDemosNewHomePageTop" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="OfficeDemosNewHomePageTop" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosNewHomePageTop_thumb_6D072E11.png" width="695" height="344" /></a></p> <p>Scrolling down, you&#39;ll see some familiar content from previous Contoso demo sites, but now they actually have real content behind them.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosNewHomePageBottom_6A610311.png"><img title="OfficeDemosNewHomePageBottom" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="OfficeDemosNewHomePageBottom" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosNewHomePageBottom_thumb_22386897.png" width="692" height="434" /></a></p> <p>If you don&#39;t remember the previous home page, take a look at the screenshot below.&#160; It demonstrated content roll-up but you couldn&#39;t actually click on any of the links.&#160; </p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosOldHomePage_2CF8509D.png"><img title="OfficeDemosOldHomePage" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="OfficeDemosOldHomePage" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosOldHomePage_thumb_31716215.png" width="553" height="418" /></a></p> <p>Now you&#39;ll be happy to know that you can finally read the message from the president of Contoso!</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosArticlePage_60C2D79F.png"><img title="OfficeDemosArticlePage" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="OfficeDemosArticlePage" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosArticlePage_thumb_326BE2A3.png" width="700" height="335" /></a></p> <p>Unfortunately, the news page is still static.&#160; I would like to see this page updated with a Content Search web part to do the news roll-up.&#160; I could see this coming in the future, but in the meantime if you need to demonstrate the concept, it&#39;s easy for you to implement.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosNewsSection_7B255B67.png"><img title="OfficeDemosNewsSection" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="OfficeDemosNewsSection" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosNewsSection_thumb_61BD582D.png" width="578" height="341" /></a></p> <p>Going back to the home page, there&#39;s a nice video slider.&#160; </p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosVideoCarousel_0414EDA7.png"><img title="OfficeDemosVideoCarousel" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="OfficeDemosVideoCarousel" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosVideoCarousel_thumb_3EFC2070.png" width="706" height="83" /></a></p> <p>Clicking on the image of any of them, takes you a video article page.&#160; Unfortunately, I could never get any of them to play even though there is a video player control there.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosVideoArticle_7A4F862E.png"><img title="OfficeDemosVideoArticle" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="OfficeDemosVideoArticle" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosVideoArticle_thumb_3A1BFD65.png" width="575" height="425" /></a></p> <p>At the bottom of the home page, it features some sales metrics.&#160; Unfortunately, this is just a content editor web part with a static link but it does help to demonstrate that you can bring in metrics.&#160; I would love to see this get wired up to some data.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosSalesMetrics_3BEC532C.png"><img title="OfficeDemosSalesMetrics" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="OfficeDemosSalesMetrics" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosSalesMetrics_thumb_09887FAD.png" width="340" height="220" /></a></p> <p>The web part in the center at the bottom is supposed to show events.&#160; However, there must not be any data loaded for them.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosEventsWebPart_16EE92B3.png"><img title="OfficeDemosEventsWebPart" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="OfficeDemosEventsWebPart" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosEventsWebPart_thumb_369D6C7B.png" width="244" height="228" /></a></p> <p>Lastly, the web part zone in the bottom right shows documents that belong to the user using Content Search.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosMyContent_4B22BBF9.png"><img title="OfficeDemosMyContent" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="OfficeDemosMyContent" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosMyContent_thumb_66C747EF.png" width="281" height="174" /></a></p> <p>This site really is a great example of the kind of things you can do with Content Search web parts and Display Templates.&#160; You can see a list of them that they used when you edit the page.&#160; If you like them enough, you could probably even &quot;borrow&quot; them to make your own.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/ContentSearchDisplayTemplates_067621B8.png"><img title="ContentSearchDisplayTemplates" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="ContentSearchDisplayTemplates" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/ContentSearchDisplayTemplates_thumb_6903D0AB.png" width="199" height="372" /></a></p> <p>As far as the rest of the subsites, I didn&#39;t see a lot of differences.&#160; They have new branding but the content appears to be the same.</p> <p>Mark Kashman (<a href="https://twitter.com/mkashman">@mkashman</a>) pointed out that the design is responsive too.&#160; When viewed on a mobile device, the navigation collapses to a &quot;hamburger&quot; menu. You can click on an article and see that content too formatted nicely for your mobile device.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosResponsiveHamburgerMenu_06E29891.png"><img title="OfficeDemosResponsiveHamburgerMenu" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="OfficeDemosResponsiveHamburgerMenu" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosResponsiveHamburgerMenu_thumb_642160D3.png" width="244" height="302" /></a></p> <p>The Yammer feed is also available using the floating &quot;Y&quot; icon.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosResponsiveYammer_31515A5F.png"><img title="OfficeDemosResponsiveYammer" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="OfficeDemosResponsiveYammer" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/OfficeDemosResponsiveYammer_thumb_1A9212D6.png" width="250" height="308" /></a></p> <p>However, when I tried clicking on the Yammer link on my phone it prompted me to get the Yammer App for Windows Phone.&#160; I already had it so I said &quot;no thanks&quot; and it gave me an error trying to render it.</p> <p>I think the Office team did a great job with this new home page.&#160; It does a great job showing how you can roll-up content in SharePoint 2013.&#160; It actually demonstrated article publishing and these are the kinds of things that I am guessing a lot of you out there have implemented for clients.&#160; This is going to be absolutely great for demos.&#160; If you don&#39;t have a demo Office 365 tenant, go <a href="http://www.microsoftofficedemos.com">provision</a> one today!</p><img src="http://www.dotnetmafia.com/aggbug.aspx?PostID=6791" width="1" height="1">SharePointSharePoint OnlineOffice 365Office 365 GridSharePoint 2013How to: Use PowerShell to Create and Manage Users and Groups in SharePoint Onlinehttp://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/05/06/how-to-use-powershell-to-create-and-manage-users-and-groups-in-sharepoint-online.aspxTue, 06 May 2014 16:14:29 GMTceb7fe2a-c56b-4d85-99e6-8dd548580538:6764CoreyRoth9http://www.dotnetmafia.com/blogs/dotnettipoftheday/rsscomments.aspx?PostID=6764http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/05/06/how-to-use-powershell-to-create-and-manage-users-and-groups-in-sharepoint-online.aspx#comments<p>I have an upcoming <a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/04/23/speaking-at-teched-north-america-2014.aspx">talk at TechEd 2014</a> and part of it will be spent showing you how you can manage users with PowerShell in SharePoint Online.&#160; I thought, I would give a little preview and show you some of the ways you can manage users.&#160; If you are using Office 365, this post will get you started, creating, deleting, and adding users to groups.&#160;&#160; You&#39;ll also learn how to set permissions on groups as well as promote site collection administrators.&#160; You&#39;ll need to install <a href="http://www.microsoft.com/en-us/download/details.aspx?id=35588">SharePoint Online Management Shell</a>, if you haven&#39;t already.&#160; </p> <p>Get started by opening a session using <a href="http://technet.microsoft.com/en-us/library/fp161392(v=office.15).aspx">Connect-SPOService</a> and the full URL to the admin site of your tenant.&#160; You can see my first <a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2012/11/29/how-to-use-powershell-with-sharepoint-online-preview.aspx">SharePoint Online PowerShell post</a> for the syntax.</p> <p>You&#39;ll need to know the URL to the site collection you are working with for all of these commands.&#160; </p> <h1></h1> <h3>Getting a list of users</h3> <p>We can start by getting a simple list of all users on a site using <a href="http://technet.microsoft.com/en-us/library/fp161389(v=office.15).aspx">Get-SPOUser</a>.</p> <p><strong>Get-SPOUser -Site https://mytenant.sharepoint.com/sites/mysitecollection</strong></p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPOwerShellGetSPOUser_216DE51C.png"><img title="SPOPOwerShellGetSPOUser" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPOPOwerShellGetSPOUser" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPOwerShellGetSPOUser_thumb_7CDC5797.png" width="514" height="298" /></a></p> <p>This will give you a list of all users, the login name, and what groups each user belongs to.&#160; If you look all the way down the list, you&#39;ll even notice some internal hidden users such as the cache, crawl, and system accounts.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPOwerShellGetSPOUser2_7F18E053.png"><img title="SPOPOwerShellGetSPOUser2" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPOPOwerShellGetSPOUser2" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPOwerShellGetSPOUser2_thumb_65B0DD19.png" width="515" height="225" /></a></p> <p>Even with a small list, you&#39;ll notice that this cmdlet takes a while to execute.&#160; You can filter it by specifying a specific user or group.&#160; For example, to retrieve the user information for our user, Sara Davis, we use the same cmdlet with the <strong>-LoginName</strong> parameter.&#160; Keep in mind you have to specify the full login name.</p> <p><strong>Get-SPOUser -Site https://mytenant.sharepoint.com/sites/mysitecollection -LoginName user@mytenant.onmicrosoft.com</strong></p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPOwerShellGetSPOUserLoginName_45299D67.png"><img title="SPOPOwerShellGetSPOUserLoginName" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPOPOwerShellGetSPOUserLoginName" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPOwerShellGetSPOUserLoginName_thumb_0EBB7C16.png" width="519" height="94" /></a></p> <p>To view all of the users in a group, use the <strong>-Group</strong> parameter.</p> <p><strong>Get-SPOUser -Site https://mytenant.sharepoint.com/sites/mysitecollection - Group &quot;My Group&quot;</strong></p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPOwerShellGetSPOUserGroup_47662623.png"><img title="SPOPOwerShellGetSPOUserGroup" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPOPOwerShellGetSPOUserGroup" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPOwerShellGetSPOUserGroup_thumb_09D8C85A.png" width="515" height="300" /></a></p> <p>You&#39;ll notice there are not any commands to add new users.&#160; Those are handles at the Office 365 level.&#160; If the user requires a brand new account, you create the user there and then add them to the appropriate groups.</p> <h3>Getting a list of groups</h3> <p>We can retrieve a list of groups on a given site using <a href="http://technet.microsoft.com/en-us/library/fp161385(v=office.15).aspx">Get-SPOSiteGroup</a>.</p> <p><strong>Get-SPOSiteGroup -SiteName https://mytenant.sharepoint.com/sites/mysitecollection</strong></p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShelGetSPOSiteGroup_452C2E18.png"><img title="SPOPowerShelGetSPOSiteGroup" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPOPowerShelGetSPOSiteGroup" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShelGetSPOSiteGroup_thumb_3DA0BEAB.png" width="516" height="300" /></a></p> <p>This will show you the name of the group, the roles of the group, and what users are in it.&#160; You may want to use FormatTable to make the results easier to read.</p> <p><strong>Get-SPOSiteGroup -SiteName https://mytenant.sharepoint.com/sites/mysitecollection | FT Title, Roles -AutoSize</strong></p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShelGetSPOSiteGroupFormatTable_320B016C.png"><img title="SPOPowerShelGetSPOSiteGroupFormatTable" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPOPowerShelGetSPOSiteGroupFormatTable" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShelGetSPOSiteGroupFormatTable_thumb_1498B060.png" width="519" height="216" /></a></p> <p>You can also request a specific group by name, with the <strong>-Group</strong> parameter</p> <p><strong>Get-SPOSiteGroup -SiteName https://mytenant.sharepoint.com/sites/mysitecollection -Group &quot;My Group&quot;</strong></p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShellGetSPOSiteGroupByName_7B30AD25.png"><img title="SPOPowerShellGetSPOSiteGroupByName" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPOPowerShellGetSPOSiteGroupByName" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShellGetSPOSiteGroupByName_thumb_48CCD9A6.png" width="517" height="139" /></a></p> <h3></h3> <h3>Creating a group</h3> <p>To create a Group, we use the <a href="http://technet.microsoft.com/en-us/library/fp161384(v=office.15).aspx">New-SPOSiteGroup</a> cmdlet.&#160; You will need to pass the name of the group using the <strong>-Group</strong> parameter as well as the site collection.&#160; In the <strong>-PermissionLevels</strong> attribute, you pass the name of a known permission level such as <em>Contribute</em>, <em>Design</em>, or <em>Full Control</em>.</p> <p><strong>New-SPOSiteGroup -Site https://mytenant.sharepoint.com/sites/mysitecollection -Group &quot;Group Name&quot; -PermissionLevels &quot;Contribute&quot;</strong></p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShellNewSPOSiteGroup_2F64D66C.png"><img title="SPOPowerShellNewSPOSiteGroup" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPOPowerShellNewSPOSiteGroup" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShellNewSPOSiteGroup_thumb_640532A7.png" width="517" height="139" /></a></p> <p>This cmdlet tends to take a while.&#160; Once it&#39;s done, it will return information about your group.&#160; If you assign the return value of this cmdlet to a variable, you can then pass it to <strong>Add-SPOUser</strong> to add a user to the group.</p> <h3>Adding a user to a group</h3> <p>Once you create a group, you will probably want to add a user to it.&#160; We can do that with <a href="http://technet.microsoft.com/en-us/library/fp161371(v=office.15).aspx">Add-SPOUser</a>.</p> <p><strong>Add-SPOUser -Site https://mytenant.sharepoint.com/sites/mysitecollection -Group &quot;Group Name&quot; -LoginName <a href="mailto:user@mytenant.onmicrosoft.com">user@mytenant.onmicrosoft.com</a></strong></p> <p><strong><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShellAddSPOUser_7414015E.png"><img title="SPOPowerShellAddSPOUser" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPOPowerShellAddSPOUser" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShellAddSPOUser_thumb_4CD9B829.png" width="518" height="92" /></a></strong></p> <p>Using <strong>Get-SPOUser, </strong>like we showed earlier, we can verify our new users is in the group.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShellGetSPOUserGroup2_5CE886E0.png"><img title="SPOPowerShellGetSPOUserGroup2" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPOPowerShellGetSPOUserGroup2" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShellGetSPOUserGroup2_thumb_35AE3DAB.png" width="518" height="96" /></a></p> <h3>Removing a user from a group</h3> <p>As you might guess, removing a user from a group takes the same three parameters with the <a href="http://technet.microsoft.com/en-us/library/fp161381(v=office.15).aspx">Remove-SPOUser</a> cmdlet.&#160; However, the Group is optional.&#160; Include it to remove the user from a specific group or omit it to remove the user from the site entirely.</p> <p><strong>Remove-SPOUser -Site https://mytenant.sharepoint.com/sites/mysitecollection -Group &quot;Group Name&quot; -LoginName user@mytenant.onmicrosoft.com</strong></p> <p><strong><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShellRemoveSPOUserGroup_555D1773.png"><img title="SPOPowerShellRemoveSPOUserGroup" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPOPowerShellRemoveSPOUserGroup" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShellRemoveSPOUserGroup_thumb_7101A369.png" width="518" height="62" /></a></strong></p> <p> <p>When successful, no output will be returned.</p> <h3>Adding a permission level to a group</h3> <p>To change permissions on a group, we use the <a href="http://technet.microsoft.com/en-us/library/fp161387(v=office.15).aspx">Set-SPOSiteGroup</a>&#160; To add a permission level to a group, use the <strong>-PermissionLevelsToAdd</strong> parameter.&#160; Note this cmdlet uses the <strong>-Identity </strong>parameter instead of -Group.</p> <p><strong>Set-SPOSiteGroup SPOUser -Site https://mytenant.sharepoint.com/sites/mysitecollection -Identity &quot;Group Name&quot; -PermissionLevelsToAdd &quot;Design&quot;</strong></p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShellSetSPOSiteGroupAddPermissions_10B07D32.png"><img title="SPOPowerShellSetSPOSiteGroupAddPermissions" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPOPowerShellSetSPOSiteGroupAddPermissions" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShellSetSPOSiteGroupAddPermissions_thumb_4550D96D.png" width="521" height="148" /></a></p> <h3></h3> <h3>Removing a permission level from a group</h3> <p>We can also use <strong>Set-SPOSiteGroup </strong>to remove a permission level as well using the <strong>-PermissionLevelsToRemove</strong> parameter.</p> <p><strong>Set-SPOSiteGroup SPOUser -Site https://mytenant.sharepoint.com/sites/mysitecollection -Identity &quot;Group Name&quot; -PermissionLevelsToRemove &quot;Contribute&quot;</strong></p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShellSetSPOSiteGroupRemovePermissions_40DA58A6.png"><img title="SPOPowerShellSetSPOSiteGroupRemovePermissions" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPOPowerShellSetSPOSiteGroupRemovePermissions" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShellSetSPOSiteGroupRemovePermissions_thumb_478D6229.png" width="519" height="139" /></a></p> <p>You can also use <strong>Set-SPOSiteGroup</strong> to rename it with the <strong>-Name</strong> parameter as well as change the owner with the <strong>-Owner</strong> parameter.</p> <h3>Removing a group</h3> <p>To remove the group, use the <a href="http://technet.microsoft.com/en-us/library/fp161373(v=office.15).aspx">Remove-SPOSiteGroup</a>.&#160; For some reason, this cmdlet uses the <strong>-Identity</strong> parameter instead of -Group so pass the name there. </p> <p><strong>Remove-SPOSiteGroup SPOUser -Site https://mytenant.sharepoint.com/sites/mysitecollection -Identity &quot;Group Name&quot;</strong></p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShellRemoveSPOSiteGroup_5C12B1A7.png"><img title="SPOPowerShellRemoveSPOSiteGroup" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPOPowerShellRemoveSPOSiteGroup" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShellRemoveSPOSiteGroup_thumb_49C9EAE5.png" width="517" height="60" /></a></p> <h3>Make a user a site collection administrator</h3> <p>We can give users site collection administrator rights, but the functionality is buried in <a href="http://technet.microsoft.com/en-us/library/fp161375(v=office.15).aspx">Set-SPOUser</a>.&#160; Set the <strong>-IsSiteCollectionAdmin</strong> command to <strong>$true </strong>to make the user a site collection administrator.</p> <p><strong>Set-SPOUser -Site https://mytenant.sharepoint.com/sites/mysitecollection -Group &quot;Group Name&quot; -LoginName user@mytenant.onmicrosoft.com -IsSiteCollectionAdmin $true</strong></p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShellSetSPOUser_3B8B71F5.png"><img title="SPOPowerShellSetSPOUser" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPOPowerShellSetSPOUser" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShellSetSPOUser_thumb_572FFDEB.png" width="516" height="96" /></a></p> <p>To remove site collection administrator rights, simple set <strong>IsSiteCollectionAdmin</strong> to <strong>$false</strong>.</p> </p> <h3>Get a list of site collection administrators</h3> <p>If you want to see who all of the site collection administrators are, you can find the value on the <strong>IsSiteAdmin</strong> property of the user object returned from <strong>Get-SPOUser</strong>.&#160; You just have to display it.&#160; In the example below, we select the column using Format-Table (ft).</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShellGetSPOUsersSiteCollectionAdmin_24CC2A6C.png"><img title="SPOPowerShellGetSPOUsersSiteCollectionAdmin" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPOPowerShellGetSPOUsersSiteCollectionAdmin" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOPowerShellGetSPOUsersSiteCollectionAdmin_thumb_5D76D479.png" width="518" height="162" /></a></p> <p> <p>We&#39;ll be covering this and a lot more at my <a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/04/23/speaking-at-teched-north-america-2014.aspx">PowerShell talk at TechEd</a> next week.&#160; If you are there, be sure and attend.</p></p><img src="http://www.dotnetmafia.com/aggbug.aspx?PostID=6764" width="1" height="1">SharePointPowerShellSharePoint OnlineOffice 365Office 365 GridHow to: Create and Manage sites in SharePoint Online using PowerShell and CSOMhttp://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/04/30/how-to-create-and-manage-sites-in-sharepoint-online-using-powershell-and-csom.aspxWed, 30 Apr 2014 16:45:59 GMTceb7fe2a-c56b-4d85-99e6-8dd548580538:6751CoreyRoth4http://www.dotnetmafia.com/blogs/dotnettipoftheday/rsscomments.aspx?PostID=6751http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/04/30/how-to-create-and-manage-sites-in-sharepoint-online-using-powershell-and-csom.aspx#comments<p>In case you missed it last week, be sure and check out my guest post on the <a href="http://blogs.technet.com/b/heyscriptingguy/archive/2014/04/24/create-and-manage-sharepoint-online-sites-by-using-powershell.aspx">Hey, Scripting Guy blog</a>.&#160; There, I show you how to create sites, get sites, and delete sites from SharePoint Online using PowerShell and CSOM.&#160; SharePoint Online Management Shell doesn&#39;t have this functionality, but you can use CSOM instead.&#160; If you are using Office 365, this can be handy to meet your site provisioning needs.</p> <p><a href="http://blogs.technet.com/b/heyscriptingguy/archive/2014/04/24/create-and-manage-sharepoint-online-sites-by-using-powershell.aspx">Hey, Scripting Guy Blog Post</a></p> <p>I also posted the complete PowerShell scripts to the TechNet script center.</p> <ul> <li><a href="http://gallery.technet.microsoft.com/scriptcenter/SharePoint-Online-Create-a-90cf1880">Create a new site in SharePoint Online using PowerShell and CSOM</a></li> <li><a href="http://gallery.technet.microsoft.com/scriptcenter/SharePoint-Online-Get-a-19cfb449">Get a list of sites in SharePoint Online using PowerShell and CSOM</a></li> <li><a href="http://gallery.technet.microsoft.com/scriptcenter/SharePoint-Online-Delete-a-e0aabe36">Deleting a site in SharePoint Online using PowerShell and CSOM</a></li> </ul> <p>I hope you find these scripts useful.&#160; Let me know if you have any questions.</p><img src="http://www.dotnetmafia.com/aggbug.aspx?PostID=6751" width="1" height="1">SharePointPowerShellSharePoint OnlineOffice 365Office 365 GridNew Document Library Headers in SharePoint Online Siteshttp://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/04/23/new-document-library-headers-in-sharepoint-online-sites.aspxWed, 23 Apr 2014 22:33:36 GMTceb7fe2a-c56b-4d85-99e6-8dd548580538:6738CoreyRoth5http://www.dotnetmafia.com/blogs/dotnettipoftheday/rsscomments.aspx?PostID=6738http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/04/23/new-document-library-headers-in-sharepoint-online-sites.aspx#comments<p>If you are using SharePoint Online (Office 365), you might notice some changes rolling out this week to your document libraries.&#160; Previously, the header of your document library probably looked something like this.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOOldDocumentLibraryHeader_318CC599.png"><img title="SPOOldDocumentLibraryHeader" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPOOldDocumentLibraryHeader" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOOldDocumentLibraryHeader_thumb_0CFB3815.png" width="340" height="129" /></a></p> <p>However, now, you will notice a new set of links where the <em>new document</em> link was.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPONewDocumentLibraryHeader_01657AD6.png"><img title="SPONewDocumentLibraryHeader" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPONewDocumentLibraryHeader" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPONewDocumentLibraryHeader_thumb_7CEEFA0E.png" width="442" height="143" /></a></p> <p>You can still drag and drop files into your document library like before.&#160; However, there are some other links to get you started.&#160; What&#39;s nice about this is that it makes it more obvious about how to create a new document or upload it without having to mess with the ribbon.</p> <p>Clicking the <em>new</em> button brings up a menu showing you common Office file types.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPONewDocumentLibraryHeaderNewDocument_4A1EF39A.png"><img title="SPONewDocumentLibraryHeaderNewDocument" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPONewDocumentLibraryHeaderNewDocument" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPONewDocumentLibraryHeaderNewDocument_thumb_258D6616.png" width="428" height="312" /></a></p> <p>A <em>sync </em>button is also present which promotes the use of OneDrive for Business for synchronizing document libraries.</p> <p>You&#39;ll notice that <em>edit</em>, <em>manage</em>, and <em>share</em> are disabled.&#160; That&#39;s because these links are only available if you have a document selected.&#160; Once you select a document, these options will be available.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPONewDocumentLibraryHeaderDocumentSelected_79DC9C19.png"><img title="SPONewDocumentLibraryHeaderDocumentSelected" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPONewDocumentLibraryHeaderDocumentSelected" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPONewDocumentLibraryHeaderDocumentSelected_thumb_554B0E95.png" width="435" height="150" /></a></p> <p>Clicking <em>edit</em> will open the document for editing.&#160; </p> <p>Clicking <em>manage</em> actually brings up the ECB content menu.&#160; This makes it a bit easier to access than before.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPONewDocumentLibraryHeaderEditMenu_109E7454.png"><img title="SPONewDocumentLibraryHeaderEditMenu" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPONewDocumentLibraryHeaderEditMenu" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPONewDocumentLibraryHeaderEditMenu_thumb_6C0CE6CF.png" width="322" height="322" /></a></p> <p>Lastly, clicking <em>share</em> brings up the document sharing dialog.&#160; I think these are nice changes that make things easier to work with.&#160; I wonder if these links are extensible like ribbon actions or ECB menu items.</p> <p>If you don&#39;t see these changes in your SharePoint Online tenant, sit tight as they should be showing up before very long.</p><img src="http://www.dotnetmafia.com/aggbug.aspx?PostID=6738" width="1" height="1">SharePointSharePoint OnlineOffice 365Office 365 GridSharePoint 2013If SharePoint forms are a priority to your business, then maybe Office 365 should be toohttp://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/03/17/if-sharepoint-forms-is-a-priority-to-your-business-then-maybe-office-365-should-be-too.aspxMon, 17 Mar 2014 16:57:28 GMTceb7fe2a-c56b-4d85-99e6-8dd548580538:6714CoreyRoth2http://www.dotnetmafia.com/blogs/dotnettipoftheday/rsscomments.aspx?PostID=6714http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/03/17/if-sharepoint-forms-is-a-priority-to-your-business-then-maybe-office-365-should-be-too.aspx#comments<p>There has been a lot of talk about forms in the last year.&#160; Your business users need it and your developers want it too.&#160; The current version of SharePoint doesn&#39;t offer much in this space right now.&#160; At SharePoint Conference 2014, Microsoft announced the <a href="http://channel9.msdn.com/Events/SharePoint-Conference/2014/SPC348">forms roadmap for SharePoint</a>.&#160; Since at least last year, Microsoft has been telling you that SharePoint is now a &quot;service-first&quot; offering.&#160; That means if you want the newest features, you need to be in Office 365.&#160; <a href="http://office.microsoft.com/en-us/office-online-help/surveys-in-excel-hosted-online-HA103171060.aspx">Excel Surveys</a> was one of the first examples of this.&#160; If you don&#39;t mind waiting three years to get something new, by all means stay on-premises.&#160; </p> <p>That being said, we&#39;re going to start seeing iterative releases for forms technology based on Access showing up as soon as this summer in Office 365.&#160; This is the Forms on SharePoint Lists &quot;FoSL&quot; technology mentioned in the session.&#160; Microsoft is focusing on an iterative process for continuous improvement here.&#160; That means, we&#39;ll get a simplified version of the forms technology soon that includes some of the following features (mentioned in SPC348):</p> <ul> <li>Auto form layout based on list schema</li> <li>Lookup between lists</li> <li>Cascading combo boxes</li> <li>SharePoint list workflows</li> </ul> <p>This tool is designed around the information worker so that they can easily build their forms around SharePoint lists.&#160; What is particularly exciting is that it supports workflows.&#160; This was a key reason that information workers couldn&#39;t build business solutions using Access Services 2013.&#160; Cascading combo boxes have always been particularly troublesome in the past as well.&#160; All of these features are subject to change, but these are some great features targeted for launch.&#160; Will it have everything you need?&#160; Probably not, but they have more features targeted such as hiding / showing sections, profile information, and business logic coming in the next year.&#160; Want to have a say what is going into the product?&#160; Vote for it on <a href="http://officeforms.uservoice.com/">User Voice</a>.&#160; Here you can suggest ideas and vote for the features that matter most to you.</p> <p>I&#39;ve had this forms discussion with a lot of clients in the last year.&#160; It&#39;s been a hard conversation, because there really was no good answer.&#160; For clients that just needed a handful of forms built, many of them found it was cheaper and more effective to just engage a developer.&#160; For clients that wanted to empower end users, the discussion usually turned to third party tools.&#160; Both Nintex and K2 make some great forms products, but they both tend to be highly cost prohibitive.&#160; Even some of my clients with the largest budgets, just couldn&#39;t pull the trigger due to cost.&#160; If you have the money, these third party tools can probably meet your immediate needs.&#160; However, if you can wait a bit longer, maybe it makes sense to put those dollars towards migration to Office 365.&#160; By the time you are done, Microsoft will more than likely will have a forms project that can meet a lot of your needs.&#160; That being said, the third party tools are more mature products at this point.&#160; They will offer more features to you now, than FoSL will at launch, so you really have to weight in what makes sense for your business.&#160; </p> <p>Now, I could be totally wrong on this since all I have seen is the same demo many of you have at SharePoint Conference.&#160; Many ISVs have built good businesses around the gaps in SharePoint.&#160; They can charge a premium for a feature you need now.&#160; However, those gaps tend to get filled over time.&#160; Social is a great example.&#160; Back in 2011, lots of companies were considering NewsGator.&#160; Many opted out, again due to cost.&#160; Then Microsoft bought Yammer and now the company formerly known as NewsGator is now <a href="http://www.sitrion.com/blog/newsgator-is-now-sitrion">known as Sitrion</a> and is focusing on HR solutions.&#160; I&#39;m not saying that I see K2 or Nintex going out of business any time soon, but I hope they are working on something great to fill that next gap.&#160; While you may argue that the Microsoft forms solution may never be as mature as what the ISVs offer, it may be good enough.&#160; Customers will opt to get by with what it provides rather than sign a PO with an ISV to get more.</p> <p>Now is forms the tipping point to get you to make the jump to the cloud?&#160; Maybe not, but this is only the first of many things that you will see online years before it gets to on-premises.&#160; Think about it, the next version of SharePoint on-premises isn&#39;t coming until the end of next year.&#160; That means SharePoint Online users will have access to the forms technology a year or more before on-premises users.&#160; That&#39;s assuming you migrated to SharePoint vNExt as soon as it came out too.&#160; It&#39;s hard to shift from a reactive to a pro-active IT organization when you have to tell your users sorry, you need to wait two more years for that.</p> <p>As I said at the end of last year, <a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2013/12/30/year-in-review-13-sharepoint-highlights-for-2013.aspx">2014 is the year of the hybrid</a>.&#160; You&#39;ll see more organizations this putting one foot in the cloud.&#160; Whether they start with OneDrive for Business or they go all in, you&#39;ll see new organizations looking at the cloud this year when previously they swore they would never go.&#160; Never say never.&#160; When companies look at spending 500k, 1 million, or even more on doing an upgrade every couple of years, you have to ask yourself why?&#160; Wouldn&#39;t you rather just do one last migration to SharePoint Online and then never have to worry about it again?&#160; If I was a CIO, that&#39;s certainly where I would be looking.&#160; Of course, moving to the cloud has it caveats, concerns, downtime, performance issues, and risks.&#160; I understand that it&#39;s not right for everyone.&#160; However, if Office 365 is viable for your business though, why wouldn&#39;t you do it and never have to worry about another migration again?</p> <p>After the forms session, I had a conversation on <a href="http://channel9.msdn.com/Events/SharePoint-Conference/2014/SPCtv-Insights-into-the-Future-of-Forms">SPCtv</a> with Greg Lindhorst, <a href="https://twitter.com/SonyaKoptyev">Sonya Koptyev</a>, and <a href="https://twitter.com/karuana">Karuana</a> about the future of forms.&#160; Be sure and watch it for more information.</p> <p><a href="http://channel9.msdn.com/Events/SharePoint-Conference/2014/SPCtv-Insights-into-the-Future-of-Forms">SPCtv Insights into the Future of Forms</a></p> <p>Follow me on twitter: <a href="https://twitter.com/coreyroth">@coreyroth</a></p><img src="http://www.dotnetmafia.com/aggbug.aspx?PostID=6714" width="1" height="1">SharePointEnd UserSharePoint OnlineOffice 365Office 365 GridSharePoint 2013AccessSPC14FormsNew SharePoint Online Tenants feature OneDrive brandinghttp://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/02/27/new-sharepoint-online-tenants-feature-onedrive-branding.aspxFri, 28 Feb 2014 00:26:42 GMTceb7fe2a-c56b-4d85-99e6-8dd548580538:6687CoreyRoth1http://www.dotnetmafia.com/blogs/dotnettipoftheday/rsscomments.aspx?PostID=6687http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/02/27/new-sharepoint-online-tenants-feature-onedrive-branding.aspx#comments<p>After Service Pack 1 went live this week, I decided to provision a new Office 365 tenant to see if the OneDrive changes are present there as well.&#160; It turns out that they are.&#160; When you login, you&#39;ll see <strong>OneDrive</strong> at the top.&#160; I really wish this would say OneDrive for Business to avoid confusion with the consumer offering.&#160; However, I think this is a screen real estate issue.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOHomePageOneDrive_0B8D09A4.png"><img title="SPOHomePageOneDrive" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPOHomePageOneDrive" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOHomePageOneDrive_thumb_4AED4DE5.png" width="646" height="381" /></a></p> <p>Tenant administration has the naming right.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOOneDriveForBusinessTenantAdmin_41941962.png"><img title="SPOOneDriveForBusinessTenantAdmin" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPOOneDriveForBusinessTenantAdmin" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOOneDriveForBusinessTenantAdmin_thumb_5CCC7263.png" width="649" height="337" /></a></p> <p>Clicking on the OneDrive link, looks similar to the old SkyDrive Pro experience.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOOneDriveForBusiness_383AE4DF.png"><img title="SPOOneDriveForBusiness" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="SPOOneDriveForBusiness" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/SPOOneDriveForBusiness_thumb_2585EB28.png" width="658" height="200" /></a></p> <p>My existing tenants still say SkyDrive.&#160; I suspect they will stay that way for quite a while, but maybe we&#39;ll hear more at SharePoint Conference.&#160; If you happen to hear anything on the timeline of this during a session, please let me know with a comment or tweet!&#160; </p><img src="http://www.dotnetmafia.com/aggbug.aspx?PostID=6687" width="1" height="1">SharePointSharePoint OnlineOffice 365Office 365 GridSPC14OneDrive for BusinessUseful JavaScript to know when working with SharePoint Display Templates (#SPC3000)http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/02/26/useful-javascript-for-working-with-sharepoint-display-templates-spc3000-spc14.aspxWed, 26 Feb 2014 16:58:14 GMTceb7fe2a-c56b-4d85-99e6-8dd548580538:6678CoreyRoth25http://www.dotnetmafia.com/blogs/dotnettipoftheday/rsscomments.aspx?PostID=6678http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/02/26/useful-javascript-for-working-with-sharepoint-display-templates-spc3000-spc14.aspx#comments<p>With the display templates feature in SharePoint 2013, you can highly customize the look of search using HTML, JavaScript, and jQuery.&#160; It turns out there are a lot of helper functions to make your life easier in the out-of-the-box scripts.&#160; Unfortunately, there is not a lot of documentation out there on how to get started aside from looking at the code of OOTB display templates.&#160; You can also find quite a bit by looking at the debug versions of the scripts <strong>Search.cbs.debug.js</strong> and <strong>Search.clientcontrols.js</strong>.&#160; This post today serves as a guide to go along with my upcoming <a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/02/10/speaking-at-sharepoint-conference-2014-spc14.aspx">Display Templates</a> talk (<a href="https://twitter.com/search?q=%23SPC3000">#SPC3000</a>) at SharePoint Conference 2014.</p> <h4>Meet ctx</h4> <p>When it comes to display templates, <strong>ctx</strong> is your content object for your display template.&#160; With this object, you can get access to managed properties, the number of results, and DOM elements that make up your display template.&#160; Your display template will have a reference to <strong>ctx</strong> automatically and you will need to pass it to many of the methods to get access.</p> <h4>Accessing the Current Item</h4> <p>Now that you have met <strong>ctx</strong>, you can use it to do a number of actions.&#160; Use this in your item display templates and hover panels.</p> <p><strong>ctx.CurrentItem</strong></p> <p>This gives you access to all of the managed properties that have been set in the <strong>ManagedPropertyMapping</strong> element.&#160; See below for the syntax to request the values.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/DisplayTemplateDebuggerCtxCurrentItem_6486C122.png"><img title="DisplayTemplateDebuggerCtxCurrentItem" style="border-left-width:0px;border-right-width:0px;background-image:none;border-bottom-width:0px;padding-top:0px;padding-left:0px;display:inline;padding-right:0px;border-top-width:0px;" border="0" alt="DisplayTemplateDebuggerCtxCurrentItem" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/DisplayTemplateDebuggerCtxCurrentItem_thumb_18BAEA69.png" width="411" height="197" /></a></p> <p><strong>Get the index of the current item</strong></p> <div style="font-size:10pt;font-family:consolas;background:white;color:black;"> <p style="margin:0px;"><span style="color:blue;">var</span> index = ctx.CurrentItemIdx;</p> </div> <p>An item display template gets called for each result you have.&#160; This property is useful to determine if you are on the first or last result.</p> <h4>Accessing Managed Properties</h4> <p>The basics of retrieving values of managed properties is accessing managed properties.&#160; You can do this in two ways by using the managed property name or the display name in the <strong>ManagedPropertyMapping</strong> element of the display template.&#160; You’ll find your managed properties typically in a format like the one below with the display property on the left and the managed property name on the right.&#160; The value in brackets is optional and is used by the Content Search web part and it&#39;s what the web part shows in the property mapping section as a label.</p> <pre>&#39;Line 1&#39;{Line 1}:&#39;Title&#39;</pre>
<pre><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/ContentSearchPropertyMappings_463C0A2C.png"><img title="ContentSearchPropertyMappings" style="border-top:0px;border-right:0px;background-image:none;border-bottom:0px;padding-top:0px;padding-left:0px;border-left:0px;display:inline;padding-right:0px;" border="0" alt="ContentSearchPropertyMappings" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/ContentSearchPropertyMappings_thumb_08AEAC63.png" width="238" height="335" /></a></pre>
<p><strong>Get Property Value by Managed Property Name</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;">ctx.CurrentItem.LastModifiedTime</p>
</div>
</div>
<p>Your managed properties that you define in the <strong>ManagedPropertyMappings </strong>will show up as typed properties that you can access directly from the <strong>ctx</strong> object.&#160; The example above display the last modified time.&#160; Remember you can display a field from inside the HTML markup with the _#= =#_ markup.</p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;">_#= ctx.CurrentItem.LastModifiedTime =#_</p>
</div>
<p><strong>Get Property Value by Display Name</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;"><span style="color:blue;">var</span> line4value = $getItemValue(ctx, <span style="color:#a31515;">&quot;Line 4&quot;</span>);</p>
</div>
</div>
<p>You can also use <strong>$getItemValue</strong> to get the value of a property using the web part display name (the value in the brackets).&#160; This is good for the content search web part where you don&#39;t know what managed property the user has mapped to your column.&#160; For example, in the screenshot above if I request Line 4, it will provide me the value of the <em>Path </em>managed property.</p>
<h4></h4>
<h4>Useful Properties</h4>
<p>There are a number of properties that might be useful to you if you plan on working with the JavaScript object model.&#160; For example, add the following to your <strong>ManagedPropertyMapping</strong> element to get the id of the list, list item id, and site collection URL.</p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;"><span style="color:#006400;">&#39;ListItemID&#39;:&#39;ListItemID&#39;,&#39;ListID&#39;:&#39;ListID&#39;,&#39;SPSiteUrl&#39;:&#39;SPSiteUrl&#39;</span></p>
</div>
<p><strong>Get the host list id</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;"><span style="color:blue;">var</span> listId = ctx.CurrentItem.ListID;</p>
</div>
</div>
<p>This will retrieve the GUID of the list containing the item.</p>
<p><strong>Get the list item id</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;"><span style="color:blue;">var</span> listItemId = ctx.CurrentItem.ListItemID;</p>
</div>
</div>
<p>This will return the numeric list item id of the list item.&#160; This returns the integer value as opposed to the GUID.</p>
<p><strong>Get the site collection URL</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;"><span style="color:blue;">var</span> siteCollectionUrl = ctx.CurrentItem.SPSiteUrl;</p>
</div>
</div>
<p>The site collection URL is available with the <strong>SPSiteUrl</strong> managed property.&#160; If you need access to the site (web) instead of the site collection, the process is a little bit more involved.</p>
<p><strong>Get the file extension</strong></p>
<pre><pre style="font-size:13px;font-family:consolas;background:white;color:black;">ctx.CurrentItem.FileExtension
</pre></pre>
<p>This lets you get the file extension of the current item.&#160; Many of the helper functions use this to determine the type of document.</p>
<h4>Including External Scripts and Style sheets</h4>
<p>Since display templates are just HTML and JavaScript, you may want to reference external script files such as jQuery.&#160; You can also reference CSS files as well.&#160; You need to put these references in a <strong>&lt;script&gt;</strong> element block of your display template.</p>
<p><strong>Include an external script</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p>$includeScript(this.url, &quot;~sitecollection/_layouts/15/reputation.js&quot;);</p>
</div>
<p>You can reference external scripts using a relative URL as shown above.&#160; This example pulls the script out of the current site collection.&#160; The first parameter to <strong>$includeScript</strong> is always <strong>this.url</strong>.</p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;">$includeScript(<span style="color:blue;">this</span>.url, <span style="color:#a31515;">https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.0.min.js</span>);</p>
</div>
<p>You can also specify an absolute URL including those residing externally on a CDN.&#160; </p>
<p>You can also load scripts using <strong>SP.SOD</strong> as well if you prefer.&#160; Some recommend this approach over <strong>$includeScript</strong> as it is supposed to be synchronous where $includeScript is not.</p>
<p><strong>Including a style sheet (CSS)</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;">$includeCSS(<span style="color:blue;">this</span>.url, <span style="color:#a31515;">&quot;~sitecollection/site/library/mycustom.css&quot;</span>);</p>
</div>
</div>
<p>Referencing a style sheet looks similar to referencing a script.</p>
<h4>Key Building Blocks</h4>
<p>When it comes to working with display templates, many of the things that you need to do must be executed after the rest of the script has rendered.&#160; This especially applies for anything that modifies the DOM.&#160; Refer to the <a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/02/13/for-tips-for-using-jquery-with-sharepoint-display-templates.aspx">four tips for using jQuery</a> post for more details.&#160;&#160; We use Post Render Callbacks to execute code after the display template is done rendering.&#160; Think of these as your jQuery equivalent to $(document).ready().&#160; There are two ways to make this happen.&#160; The first is <strong>ctx.OnPostRender.push()</strong>.</p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;">ctx.OnPostRender = [];</p>
<p style="margin:0px;">ctx.OnPostRender.push(ctx, <span style="color:blue;">function</span> () {</p>
<p style="margin:0px;">&#160;&#160;&#160; <span style="color:green;">// do something</span></p>
<p style="margin:0px;">});</p>
</div>
<p>The first statement clears the array and the second one pushes your function onto it.&#160; This means you can have multiple OnPostRender event handlers. Instead, you may want to use AddPostRenderCallback</p>
<pre><pre style="font-size:13px;font-family:consolas;background:white;color:black;">AddPostRenderCallback(ctx, <span style="color:blue;">function</span> () {&#160;&#160;&#160;&#160; <span style="color:green;">// do something</span>
});</pre></pre>
<p>If you look at the out-of-the-box templates, you&#39;ll see them executed both ways.&#160; I tend to prefer the latter technique.</p>
<p><strong>Get a ClientContext object</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;">Srch.ScriptApplicationManager.get_clientRuntimeContext();</p>
</div>
<p>This will return a ClientContext object for the site hosting the search component.&#160; If you are looking to get a ClientContext object for the site hosting the search result, see this <a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/02/12/how-to-get-a-clientcontext-for-a-site-given-a-full-url.aspx">post</a>.</p>
<h4>Working with the DOM</h4>
<p>You&#39;ll often need to keep track of some element Ids when working with display templates.&#160; A common practice is to generate a unique base id for the containing div element and then appending strings to those ids for child elements.&#160; This often happens at the begging of every display template and then the markup references these ids.&#160; Let&#39;s look at some examples.</p>
<p><strong>Creating a unique element id for a child element</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;"><span style="color:blue;">var</span> id = ctx.ClientControl.get_nextUniqueId();</p>
</div>
<p>One of the first lines of script you will find in a display template is this one.&#160; It generates a unique id that will be assigned to the first div element.&#160; You&#39;ll then see another line such as this when is actually used by the first div element.</p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;"><span style="color:blue;">var</span> itemId = id + Srch.U.Ids.item;</p>
</div>
<p>The div element uses <strong>$htmlEncode</strong> (which we&#39;ll cover below) to html encode the id.</p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;"><span style="color:blue;">&lt;</span><span style="color:maroon;">div</span> <span style="color:red;">id</span><span style="color:blue;">=&quot;_#= $htmlEncode(itemId) =#_&quot;</span> <span style="color:red;">name</span><span style="color:blue;">=&quot;Item&quot;</span> <span style="color:red;">data-displaytemplate</span><span style="color:blue;">=&quot;DefaultItem&quot;</span> <span style="color:red;">class</span><span style="color:blue;">=&quot;ms-srch-item&quot;</span> <span style="color:red;">onmouseover</span><span style="color:blue;">=&quot;</span>_#= ctx.currentItem_ShowHoverPanelCallback =#_<span style="color:blue;">&quot;</span> <span style="color:red;">onmouseout</span><span style="color:blue;">=&quot;</span>_#= ctx.currentItem_HideHoverPanelCallback =#_<span style="color:blue;">&quot;&gt;</span></p>
</div>
<p>What do these ids look like?&#160; Here is an example.</p>
<p><em>ctl00_ctl53_g_f59f896a_d82a_4957_acea_90abc7a9b86e_csr1</em></p>
<p><strong>Getting the id of the div of the display template</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;">ctx.ClientControl.get_id();</p>
</div>
<p>This will return the base id of the display template.</p>
<h4>Utility Functions</h4>
<p>You&#39;ll find a number of useful functions in the Srch.U object for everything from checking for null to manipulation of strings.&#160; A lot of this overlaps with jQuery but there are some other search specific functions.&#160; In the out-of-the-box display templates you&#39;ll sometimes see these methods referenced with Srch.U or with an alias using the $ sign.&#160; Take a look at our first example.</p>
<p><strong>Checking for null</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;"><span style="color:blue;">if</span> (!$isNull(ctx.ClientControl))</p>
<p style="margin:0px;">{</p>
<p style="margin:0px;">&#160;&#160;&#160; <span style="color:green;">// do something</span></p>
<p style="margin:0px;">}</p>
</div>
<p>The above use of <strong>$isNull</strong> will execute the code in the if statement if the object is not null.&#160; The code snippet below using <strong>Srch.U.n</strong> will also do the same thing.</p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;"><span style="color:blue;">if</span> (!Srch.U.n(ctx.ClientControl))</p>
<p style="margin:0px;">{</p>
<p style="margin:0px;">&#160;&#160;&#160; <span style="color:green;">// do something</span></p>
<p style="margin:0px;">}</p>
</div>
<p><strong>Checking for an empty string</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;"><span style="color:blue;">if</span> (!$isEmptyString(ctx.CurrentItem.DisplayAuthor)) { }</p>
</div>
<p>You can use <strong>$isEmptyString</strong> or <strong>Srch.U.e</strong> to check if a string is empty.</p>
<p><strong>Checking if an array is empty</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;"><span style="color:blue;">if</span> ($isEmptyArray(myArray)) { }</p>
</div>
<p>To determine if an array is empty <strong>$isEmptyArray</strong>.</p>
<p><strong>Getting an array from multi-value fields such as author</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;"><span style="color:blue;">var</span> authors = Srch.U.getArray(ctx.CurrentItem.DisplayAuthor);</p>
</div>
<p>The <strong>Srch.U.getArray</strong> method is handy because it knows how to split common multi-value fields such as author.&#160; It has a list of known delimeters such as &quot;,#&quot;, &quot;|&quot;, and &quot;;&quot;.</p>
<p><strong>Check if the item is a web page</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;">ctx.CurrentItem.csr_ShowViewLibrary = !Srch.U.isWebPage(ctx.CurrentItem.FileExtension);</p>
</div>
</div>
<p>This method knows extensions for common page types and will return true if the extension is a page (i.e.: .aspx or .html).</p>
<p><strong>Get a friendly name for a file extension</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;">Srch.U.getFriendlyNameForFileExtension(ctx.CurrentItem.FileExtension);</p>
</div>
<p>This method is how search displays the word PowerPoint instead of PPTX.</p>
<p><strong>Get the Display Name from an author field</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;"><span style="color:blue;">var</span> author = Srch.U.getDisplayNameFromAuthorField(ctx.CurrentItem.AuthorOWSUSER);</p>
</div>
<p>This function will display the author&#39;s full name properly.</p>
<p><strong>Setting the icon</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;">ctx.CurrentItem.csr_Icon = Srch.U.getIconUrlByFileExtension(ctx.CurrentItem);</p>
</div>
<p>This allows you to override the icon for a given item result.&#160; You can call <strong>Srch.U.getIconUrlByFileExtension()</strong> if you want to get the icon for the given file type.</p>
<p><strong>Get the number of items</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;">ctx.ClientControl.get_numberOfItems();</p>
</div>
<p>You can get the number of items returned using <strong>ctx.CurrentItem.get_numberOfItems()</strong>.</p>
<p><strong>Determine if the host is SharePoint Foundation</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;"><span style="color:blue;">if</span> (!Srch.U.isSPFSKU()) { }</p>
</div>
<p>Some functionality isn&#39;t available in SPF, use this method to determine your version.</p>
<h4>Formatting</h4>
<p>The <strong>Srch.U</strong> class has a number of methods that make formatting values easier.&#160; Besides just the basic formatting, it has formatting methods for numbers, dates, and file sizes that are more human readable such as display &quot;5 MB&quot; instead of &quot;5,000,000 bytes&quot;.&#160; These method are locale aware so they should display your data in the correct format for the user.</p>
<p><strong>Formatting the date</strong> <strong>with a short pattern</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;">Srch.U.toFormattedDate(ctx.CurrentItem.Write, <span style="color:#a31515;">&#39;ShortDatePattern&#39;</span>);</p>
</div>
<p>By default, this will use <em>LongDatePattern </em>if you don&#39;t specify a date pattern.&#160; </p>
<p><strong>Formatting numbers</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;">Srch.U.toFormattedNumber(value, 3);</p>
</div>
<p>This formats the number with three decimal places if you are passing it a decimal number.</p>
<p><strong>Format the number using approximations</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;">Srch.U.toFriendlyNumber(value);</p>
</div>
<p>This is useful for numbers in the 1,000 range and greater.&#160; It rounds down to the nearest thousand.&#160; For example, 52,343 will be returned as &quot;52,000+&quot;.&#160; If the value is greater than 100,000, it will return a value of 100,000+.</p>
<p><strong>Format file sizes</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;">Srch.U.toFileSizeDisplay(ctx.CurrentItem.FileSize, false);</p>
</div>
<p>This will return the file size in GB, MB, or KB.&#160; You can optionally have it show the decimal part with the second parameter. </p>
<p><strong>Formatting a string</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;"><span style="color:blue;">var</span> myString = String.format(<span style="color:#a31515;">&#39;Hello World {0} {1}&#39;</span>, value1, value2);</p>
</div>
<p>String formatting is available just like you would use in C#.</p>
<p><strong>Truncate a URL</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;">Srch.U.truncateUrl(ctx.CurrentItem.Path, 40);</p>
</div>
<p>Want to display a truncated path like you see in the search results.&#160; This method is how it is done.&#160; You can specify the number of characters in length you want it with the second parameter.</p>
<p><strong>Trim a string</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;">Srch.U.getTrimmedString(string, 40);</p>
</div>
<p>Use this to trim a long string.&#160; An ellipsis (...) will be added to the end of the string past the cutoff (second parameter)</p>
<p><strong>Trim Spaces</strong></p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;">Srch.U.trimExtraSpaces(string);</p>
</div>
<p>This method is handy to trim spaces from a string.</p>
<h4>Localization</h4>
<p>Display templates have built-in support for providing localization.&#160; It uses a system of resource dictionary stored in JavaScript files located in culture specific folders.&#160; Each culture specific folder contains a file <strong>CustomStrings.js</strong>.&#160; You can use this file or include your own.&#160;&#160; </p>
<p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/DisplayTemplateLanguageFiles_4F2B9C6B.png"><img title="DisplayTemplateLanguageFiles" style="border-left-width:0px;border-right-width:0px;background-image:none;border-bottom-width:0px;padding-top:0px;padding-left:0px;display:inline;padding-right:0px;border-top-width:0px;" border="0" alt="DisplayTemplateLanguageFiles" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/DisplayTemplateLanguageFiles_thumb_119E3EA2.png" width="386" height="314" /></a>&#160;</p>
<p><strong>Registering a resource dictionary</strong></p>
<p>If you look inside <strong>CustomStrings.js</strong> you will see the syntax of adding resources using a simple array with <strong>$registerResourceDictionary</strong>.</p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;">$registerResourceDictionary(<span style="color:#a31515;">&quot;en-us&quot;</span>, {</p>
<p style="margin:0px;">&#160;&#160;&#160; <span style="color:#a31515;">&quot;sampleCustomStringId&quot;</span>: <span style="color:#a31515;">&quot;Sample custom string&quot;</span>,</p>
<p style="margin:0px;">&#160;&#160;&#160; <span style="color:#a31515;">&quot;rf_RefinementTitle_ManagedPropertyName&quot;</span>: <span style="color:#a31515;">&quot;Sample Refinement Title for ManagedPropertyName&quot;</span></p>
<p style="margin:0px;">});</p>
</div>
<p><strong>Loading a resource file</strong></p>
<p>Many out-of-the-box templates load a resource file automatically using the statement below.&#160; If not, you can add it yourself or modify it if you are using a different resource dictionary.</p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;">$includeLanguageScript(<span style="color:blue;">this</span>.url, <span style="color:#a31515;">&quot;~sitecollection/_catalogs/masterpage/Display Templates/Language Files/{Locale}/CustomStrings.js&quot;</span>);</p>
</div>
<p><strong>Using a resource</strong></p>
<p>Using a resource in your display template is quite easy using <strong>$resource</strong>.&#160; It&#39;s a good idea to make use of <strong>$htmlEncode</strong> to encode it before writing it.</p>
<div style="font-size:10pt;font-family:consolas;background:white;color:black;">
<p style="margin:0px;">_#= $htmlEncode($resource(<span style="color:#a31515;">&quot;sampleCustomStringId&quot;</span>)) =#_</p>
</div>
<p><strong></strong></p>
<h4>See more at #SPC14</h4>
<p>If you are interested in Display Templates, be sure and come see my <a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/02/10/speaking-at-sharepoint-conference-2014-spc14.aspx">talk</a> (<a href="https://twitter.com/search?q=%23SPC3000">#SPC3000</a>) at SharePoint Conference 2014.&#160; We&#39;ll be using a lot of what you see in this post in the demos.</p>
<p>Follow me on twitter: <a href="https://twitter.com/coreyroth">@coreyroth</a></p><img src="http://www.dotnetmafia.com/aggbug.aspx?PostID=6678" width="1" height="1">SharePointEnterprise SearchPresentationsSharePoint OnlineOffice 365Office 365 GridSharePoint 2013SPC14Display Templates#SPC14 Post - Using Display Templates to format search results as a gridhttp://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/02/20/spc14-post-using-display-templates-to-format-search-results-as-a-grid.aspxThu, 20 Feb 2014 15:57:00 GMTceb7fe2a-c56b-4d85-99e6-8dd548580538:6656CoreyRoth19http://www.dotnetmafia.com/blogs/dotnettipoftheday/rsscomments.aspx?PostID=6656http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/02/20/spc14-post-using-display-templates-to-format-search-results-as-a-grid.aspx#comments<p>Today at SharePoint Conference 2014 in session <a href="https://twitter.com/search?q=%23SPC3000">#SPC3000</a> on <a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/02/10/speaking-at-sharepoint-conference-2014-spc14.aspx">Display Templates</a>, I showed how to format your search results as a grid when using the Content Search web part.&nbsp; As promised, this blog post has gone live that walks you through the exact same steps we performed in the demo.&nbsp; I&#39;ve also attached the display templates in this post so that you can get started immediately.</p> <p>To create a grid with display templates, we need to create a new Control display template and a new Item display template.&nbsp; The control wraps the items so this is where the start of our table will begin.&nbsp; You can build your display templates with design manager, the master page gallery, or with SharePoint Designer.</p> <p><b>Building the Control Display Template</b></p> <p>I don&#39;t like to build display templates from scratch, so we&#39;ll start by taking a copy of <b>Control_List.html</b> and call it <b>Control_Grid.html</b>.&nbsp; Edit the file and start by changing the title tag.&nbsp; This is what is shown in the Content Search web part.</p> <div style="background:white;color:black;font-family:consolas;font-size:10pt;"> <p style="margin:0px;"><span style="color:blue;">&lt;</span><span style="color:maroon;">title</span><span style="color:blue;">&gt;</span>Grid<span style="color:blue;">&lt;/</span><span style="color:maroon;">title</span><span style="color:blue;">&gt;</span></p> </div> <p>You can optionally change the description if you would like.</p> <div style="background:white;color:black;font-family:consolas;font-size:10pt;"> <p style="margin:0px;"><span>&lt;mso:MasterPageDescription msdt:dt=&quot;string&quot;&gt;This is a grid.&lt;/mso:MasterPageDescription&gt;</span></p> </div> <p>Next give the root div a new id.</p> <div style="background:white;color:black;font-family:consolas;font-size:10pt;"> <p style="margin:0px;"><span style="color:blue;">&lt;</span><span style="color:maroon;">div</span> <span style="color:red;">id</span><span style="color:blue;">=&quot;Control_Grid&quot;&gt;</span></p> </div> <p>Ultimately, we are going to use the jQuery DataTables library to make our tables look pretty.&nbsp; We&#39;ll go ahead and include all of the necessary scripts now.&nbsp; I am pulling them in via CDN.&nbsp; You can tweak the version numbers as desired.&nbsp; Add these into the <b>script</b> element.</p> <div style="background:white;color:black;font-family:consolas;font-size:10pt;"> <p style="margin:0px;">$includeScript(<span style="color:blue;">this</span>.url, <span>&quot;https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.0.min.js&quot;</span>);</p> <p style="margin:0px;">$includeScript(<span style="color:blue;">this</span>.url, <span>&quot;https://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/jquery.dataTables.min.js&quot;</span>);</p> <p style="margin:0px;">$includeCSS(<span style="color:blue;">this</span>.url, <span>&quot;https://ajax.aspnetcdn.com/ajax/jquery.dataTables/1.9.4/css/jquery.dataTables.css&quot;</span>);</p> </div> <p>The <b>ListRenderRenderWrapper </b>wraps each item template rendered with an <b>li</b> tag.&nbsp; We need these so remove the two push statements.</p> <p>To work with our table and table header, we need to generate some Ids.&nbsp; These Ids we&#39;ll use with jQuery code later to add elements.&nbsp; We do this in the same manner as you see in other display templates used in the search center.&nbsp; We use <b>ctx.ClientControl.get_nextUniqueid() </b>to generate a random identifier.&nbsp; We then append strings onto it for our own use.&nbsp; You can add the following statements anywhere in the first JavaScript block (just not inside the wrapper). </p> <div style="background:white;color:black;font-family:consolas;font-size:10pt;"> <p style="margin:0px;"><span>var encodedId = $htmlEncode(ctx.ClientControl.get_nextUniqueId() + &quot;_Table_&quot;);</span></p> <p style="margin:0px;"><span>var headerRowId = $htmlEncode(encodedId + &quot;_HeaderRow_&quot;);</span></p> </div> <p>We now need to remove our <b>ul </b>elements and replace them with a table.&nbsp; We&#39;re going to use the <i>encodedId</i> from above as our id.</p> <div style="background:white;color:black;font-family:consolas;font-size:10pt;"> <p style="margin:0px;"><span style="color:blue;">&lt;</span><span style="color:maroon;">table</span>&nbsp; <span style="color:red;">id</span><span style="color:blue;">=&quot;_#= encodedId =#_&quot;</span> <span style="color:red;">class</span><span style="color:blue;">=&quot;resultsTable&quot;&gt;</span></p> </div> <p>We will also want to close the table tag after <b>ctx.RenderGroups</b>.<b>&nbsp; </b>Here&#39;s what the section looks like.</p> <div style="background:white;color:black;font-family:consolas;font-size:10pt;"> <p style="margin:0px;"><span>var noResultsClassName = &quot;ms-srch-result-noResults&quot;;</span></p> <p style="margin:0px;">&nbsp;</p> <p style="margin:0px;"><span>var encodedId = $htmlEncode(ctx.ClientControl.get_nextUniqueId() + &quot;_Table_&quot;);</span></p> <p style="margin:0px;"><span>var headerRowId = $htmlEncode(encodedId + &quot;_HeaderRow_&quot;);</span></p> <p style="margin:0px;">&nbsp;</p> <p style="margin:0px;"><span>var ListRenderRenderWrapper = function(itemRenderResult, inCtx, tpl)</span></p> <p style="margin:0px;"><span>{</span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp; var iStr = [];</span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp; iStr.push(itemRenderResult);</span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp; return iStr.join(&#39;&#39;);</span></p> <p style="margin:0px;"><span>}</span></p> <p style="margin:0px;"><span>ctx[&#39;ItemRenderWrapper&#39;] = ListRenderRenderWrapper;</span></p> <p style="margin:0px;"><span>_#--&gt;</span></p> <p style="margin:0px;">&nbsp;&nbsp;&nbsp; <span style="color:blue;">&lt;</span><span style="color:maroon;">table</span>&nbsp; <span style="color:red;">id</span><span style="color:blue;">=&quot;_#= encodedId =#_&quot;</span> <span style="color:red;">class</span><span style="color:blue;">=&quot;resultsTable&quot;&gt;</span></p> <p style="margin:0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _#= ctx.RenderGroups(ctx) =#_</p> <p style="margin:0px;">&nbsp;&nbsp;&nbsp; <span style="color:blue;">&lt;/</span><span style="color:maroon;">table</span><span style="color:blue;">&gt;</span></p> </div> <p><b>Building the Item Display Template</b></p> <p>The Item Display Template gets called once for every search result.&nbsp; We need to create a display template that renders a table row as opposed to a list item.&nbsp; To do this, we&#39;re going to copy <b>Item_Diagnostic.html</b>.&nbsp; We&#39;ll call this new copy, <b>Item_GridRow.html</b>.&nbsp; Start by changing the title tag of this file too.</p> <div style="background:white;color:black;font-family:consolas;font-size:10pt;"> <p style="margin:0px;"><span style="color:blue;">&lt;</span><span style="color:maroon;">title</span><span style="color:blue;">&gt;</span>Grid Row<span style="color:blue;">&lt;/</span><span style="color:maroon;">title</span><span style="color:blue;">&gt;</span></p> </div> <p>You can optionally change the description if you would like.&nbsp; Next, give the root div a new name.</p> <div style="background:white;color:black;font-family:consolas;font-size:10pt;"> <p style="margin:0px;"><span style="color:blue;">&lt;</span><span style="color:maroon;">div</span> <span style="color:red;">id</span><span style="color:blue;">=&quot;Item_GridRow&quot;&gt;</span></p> </div> <p>Now, we want to remove the existing HTML markup as we don&#39;t need it.&nbsp; Remove everything between the <b>ul</b> tags.&nbsp; Then we are going to replace it with our logic to display the table row.&nbsp; If you remember, the diagnostic control, it has ten managed properties that you can map, <i>Line 1</i> through <i>Line 10</i>.&nbsp; This code simply writes out a new table row and then iterates through those lines 1 through 10.&nbsp; The managed property and it&#39;s value are assigned to the <i>lineValueInfo</i> object.&nbsp; If a managed property is mapped, it will write out the value.&nbsp; If, it&#39;s mapped but there is no value, it will write out an empty table cell.&nbsp; If a managed property hasn&#39;t been mapped, then it will not do anything.&nbsp; This allows for the grid to feature the exact number of mapped columns and not have any empty cells.</p> <div style="background:white;color:black;font-family:consolas;font-size:10pt;"> <p style="margin:0px;"><span style="color:blue;">&lt;</span><span style="color:maroon;">tr</span> <span style="color:red;">class</span><span style="color:blue;">=&quot;gridRow&quot;&gt;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p> <p style="margin:0px;"><span>&lt;!--#_</span></p> <p style="margin:0px;"><span>for(var lineNum = 1; lineNum &lt;= 10; lineNum++)</span></p> <p style="margin:0px;"><span>{</span></p> <p style="margin:0px;">&nbsp;</p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp; var lineValueInfo = $getItemValue(ctx, String.format(&quot;Line {0}&quot;, lineNum));</span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp; if(!$isNull(lineValueInfo) &amp;&amp; !$isEmptyString(cbsDiagnostic_RenderPropertyMappings(lineValueInfo)))</span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp; {</span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var lineId = String.format(&quot;{0}line{1}&quot;, encodedId, lineNum);</span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var slotName = String.format($resource(&quot;item_Diagnostic_SlotNameFormat&quot;), lineNum);</span></p> <p style="margin:0px;"><span>_#--&gt;</span></p> <p style="margin:0px;"><span>&lt;!--#_</span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(!lineValueInfo.isEmpty)</span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (ctx.CurrentItemIdx == 0)</span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ctx.ManagedPropertyNames.push(lineValueInfo.managedPropertyName);</span></p> <p style="margin:0px;"><span>_#--&gt;</span></p> <p style="margin:0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color:blue;">&lt;</span><span style="color:maroon;">td</span><span style="color:blue;">&gt;</span>_#= lineValueInfo =#_<span style="color:blue;">&lt;/</span><span style="color:maroon;">td</span><span style="color:blue;">&gt;</span></p> <p style="margin:0px;"><span>&lt;!--#_</span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else</span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _#--&gt;</span></p> <p style="margin:0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color:blue;">&lt;</span><span style="color:maroon;">td</span><span style="color:blue;">&gt;&lt;/</span><span style="color:maroon;">td</span><span style="color:blue;">&gt;</span></p> <p style="margin:0px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>&lt;!--#_</span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp; }</span></p> <p style="margin:0px;"><span>}</span></p> <p style="margin:0px;"><span>_#--&gt;</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p> <p style="margin:0px;"><span style="color:blue;">&lt;/</span><span style="color:maroon;">tr</span><span style="color:blue;">&gt;</span></p> </div> <p>If you are getting confused with the cutting and pasting.&nbsp; Don&#39;t worry because I&#39;ve attached the display templates to this post.</p> <p><b>Trying out the display templates</b></p> <p>Add a content search web part to a page and then set the <b>Control </b>template to <b>Grid</b> and the <b>Item</b> template to <b>Grid Row</b>.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/ContentSearchPropertiesGridSelected_6038BB69.png"><img width="210" height="259" title="ContentSearchPropertiesGridSelected" style="border:0px currentColor;border-image:none;padding-top:0px;padding-right:0px;padding-left:0px;display:inline;background-image:none;" alt="ContentSearchPropertiesGridSelected" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/ContentSearchPropertiesGridSelected_thumb_295E6723.png" border="0" /></a></p> <p>Your results should change to look like a grid now.&nbsp; It&#39;s not very pretty yet, but it&#39;s a start.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/DisplayTemplateGridNoHeader_5DFEC35E.png"><img width="519" height="161" title="DisplayTemplateGridNoHeader" style="border:0px currentColor;border-image:none;padding-top:0px;padding-right:0px;padding-left:0px;display:inline;background-image:none;" alt="DisplayTemplateGridNoHeader" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/DisplayTemplateGridNoHeader_thumb_7DAD9D26.png" border="0" /></a></p> <p>We can add a few fields to it using the property mapping.&nbsp; Start by checking the <i>Change the mapping...</i> checkbox.&nbsp; In my example, I am going to add Author, Write, and Path.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/ContentSearchPropertiesMapping_1232ECA5.png"><img width="192" height="261" title="ContentSearchPropertiesMapping" style="border:0px currentColor;border-image:none;padding-top:0px;padding-right:0px;padding-left:0px;display:inline;background-image:none;" alt="ContentSearchPropertiesMapping" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/ContentSearchPropertiesMapping_thumb_46D348E0.png" border="0" /></a></p> <p>Here is what our grid looks like now.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/DisplayTemplateGridNoHeaderMoreColumns_668222A8.png"><img width="528" height="244" title="DisplayTemplateGridNoHeaderMoreColumns" style="border:0px currentColor;border-image:none;padding-top:0px;padding-right:0px;padding-left:0px;display:inline;background-image:none;" alt="DisplayTemplateGridNoHeaderMoreColumns" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/DisplayTemplateGridNoHeaderMoreColumns_thumb_620BA1E1.png" border="0" /></a></p> <p><b>Adding a header row</b></p> <p>Unfortunately, something that seems as simple as adding a header row is quite complicated.&nbsp; The header row belongs in the control template.&nbsp; However, we don&#39;t know what the managed properties are inside of this template.&nbsp; We only have access to them from the item template.&nbsp; Our technique to work around this is to collect the information in an array when the item template is executed.&nbsp; Then we&#39;ll use an <b>OnPostRender</b> event to look at that array and build the header row.&nbsp; Think of OnPostRender as your equivalent to $(Document).Ready() in jQuery.&nbsp; We&#39;ll simply add this array to the <b>ctx</b> object.</p> <p>Go back to <b>Control_Grid.html</b> and add the following line after the headerRowId line.&nbsp; This initializes our array.&nbsp; Now we just need to add some values to it.</p> <div style="background:white;color:black;font-family:consolas;font-size:10pt;"> <p style="margin:0px;"><span>ctx.ManagedPropertyNames = [];</span></p> </div> <p>Now, we need to add a table header inside the table element we originally created.&nbsp; Notice the use of <b>headerRowId</b> that we defined before.</p> <div style="background:white;color:black;font-family:consolas;font-size:10pt;"> <p style="margin:0px;"><span style="color:blue;">&lt;</span><span style="color:maroon;">thead</span><span style="color:blue;">&gt;</span></p> <p style="margin:0px;">&nbsp;&nbsp;&nbsp; <span style="color:blue;">&lt;</span><span style="color:maroon;">tr</span> <span style="color:red;">id</span><span style="color:blue;">=&quot;_#= headerRowId =#_&quot;</span> <span style="color:red;">class</span><span style="color:blue;">=&quot;resultsTableHeader&quot;&gt;</span></p> <p style="margin:0px;">&nbsp;&nbsp;&nbsp; <span style="color:blue;">&lt;/</span><span style="color:maroon;">tr</span><span style="color:blue;">&gt;</span></p> <p style="margin:0px;"><span style="color:blue;">&lt;/</span><span style="color:maroon;">thead</span><span style="color:blue;">&gt;</span></p> </div> <p>Now switch to <b>Item_GridRow.html</b>.&nbsp; Here we are going to add some inside the loop.&nbsp; The <b>lineValueInfo</b> object has the name of the managed property.&nbsp; Add this snippet into the if statement checking if lineValueInfo is empty or not.</p> <div style="background:white;color:black;font-family:consolas;font-size:10pt;"> <p style="margin:0px;"><span>if (ctx.CurrentItemIdx == 0)</span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp; ctx.ManagedPropertyNames.push(lineValueInfo.managedPropertyName);</span></p> </div> <p>Since the item template gets executed multiple times, we only want to add this information once.&nbsp; Using <b>ctx.CurrentItemIdx</b>, we can check to see if this is the first iteration of this display template.</p> <p>Now we need to go back to <b>Control_Grid.html</b> and create the <b>OnPostRender</b> event.&nbsp; You can add this code after the line you just added.</p> <div style="background:white;color:black;font-family:consolas;font-size:10pt;"> <p style="margin:0px;"><span>ctx.OnPostRender = [];</span></p> <p style="margin:0px;"><span>ctx.OnPostRender.push(function () {</span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp; for(var i = 0; i &lt; ctx.ManagedPropertyNames.length; i++)</span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp; {</span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(&quot;.resultsTableHeader&quot;).append(&quot;&lt;th&gt;&quot; + ctx.ManagedPropertyNames[i] + &quot;&lt;/th&gt;&quot;);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></p> <p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp; }</span></p> <p style="margin:0px;"><span>});</span></p> </div> <p>This code iterates through the managed properties stored in the <b>ctx.ManagedPropertyNames</b> array.&nbsp; It then just appends a <b>th </b>element for each property name.</p> <p>Save your display templates and then refresh your page in the browser and now we should have the names of the managed properties above each column.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/DisplayTemplateGridHeader_7690F15F.png"><img width="611" height="300" title="DisplayTemplateGridHeader" style="border:0px currentColor;border-image:none;padding-top:0px;padding-right:0px;padding-left:0px;display:inline;background-image:none;" alt="DisplayTemplateGridHeader" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/DisplayTemplateGridHeader_thumb_48376BB2.png" border="0" /></a></p> <p><b>Using DataTables</b></p> <p>At this point, we have the makings of a good grid, but it&#39;s still quite easy.&nbsp; Now is a good time to enlist the help of the jQuery <a href="https://datatables.net/">DataTables plug-in</a>.&nbsp; The key to using this plug-in is having a well formatted table.&nbsp; That means no missing cells or anything like that.&nbsp; Luckily, I took all of the hard work out of that and our code accommodates for that.&nbsp; Since we have already registered the script, it only takes one line of code to take advantage of it.&nbsp; Just add this to the end of the <b>OnPostRender</b> function.&nbsp; We just need to use the <b>encodedId</b> to get a reference to our table and then apply the <b>dataTable()</b> method to it.&nbsp; I passed a few parameters to it as well such as disabling paging and sorting.&nbsp; You can look up additional parameters on the plug-in&#39;s website.&nbsp; </p> <pre style="background:white;color:black;font-family:consolas;font-size:13px;"><span style="color:darkgreen;">$(&quot;#&quot; + encodedId).dataTable({ &quot;bPaginate&quot;: false, &quot;bAutoWidth&quot;: true, &quot;bSort&quot;: false });</span></pre>
<p>The entire <b>OnPostRender </b>method now looks like this.</p>
<div style="background:white;color:black;font-family:consolas;font-size:10pt;">
<p style="margin:0px;"><span>ctx.OnPostRender = [];</span></p>
<p style="margin:0px;"><span>ctx.OnPostRender.push(function () {</span></p>
<p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp; for(var i = 0; i &lt; ctx.ManagedPropertyNames.length; i++)</span></p>
<p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp; {</span></p>
<p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(&quot;.resultsTableHeader&quot;).append(&quot;&lt;th&gt;&quot; + ctx.ManagedPropertyNames[i] + &quot;&lt;/th&gt;&quot;);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></p>
<p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp; }</span></p>
<p style="margin:0px;">&nbsp;</p>
<p style="margin:0px;"><span>&nbsp;&nbsp;&nbsp; $(&quot;#&quot; + encodedId).dataTable({ &quot;bPaginate&quot;: false, &quot;bAutoWidth&quot;: true, &quot;bSort&quot;: false });</span></p>
<p style="margin:0px;"><span>});</span></p>
</div>
<p>Now refresh your page, and you should have a nice grid that looks like this.</p>
<p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/DisplayTemplateGridWithDataTables_71AE3DA3.png"><img width="666" height="269" title="DisplayTemplateGridWithDataTables" style="border:0px currentColor;border-image:none;padding-top:0px;padding-right:0px;padding-left:0px;display:inline;background-image:none;" alt="DisplayTemplateGridWithDataTables" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/DisplayTemplateGridWithDataTables_thumb_5EF943EC.png" border="0" /></a></p>
<p>Now that looks a lot better.&nbsp; You can use your own CSS to apply your own colors of course.&nbsp; </p>
<p>As promised, I have attached the display templates to this post.&nbsp; Feel free to try them out, use them for yourself, and improve them.&nbsp; If you find them useful, feel free to leave a comment.</p><img src="http://www.dotnetmafia.com/aggbug.aspx?PostID=6656" width="1" height="1">SharePointEnterprise SearchPresentationsSharePoint DesignerSharePoint OnlineOffice 365Office 365 GridSharePoint 2013SPC144 tips for using jQuery with SharePoint Display Templateshttp://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/02/13/for-tips-for-using-jquery-with-sharepoint-display-templates.aspxThu, 13 Feb 2014 18:05:58 GMTceb7fe2a-c56b-4d85-99e6-8dd548580538:6651CoreyRoth6http://www.dotnetmafia.com/blogs/dotnettipoftheday/rsscomments.aspx?PostID=6651http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/02/13/for-tips-for-using-jquery-with-sharepoint-display-templates.aspx#comments<p>In preparing for my SharePoint <a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/02/10/speaking-at-sharepoint-conference-2014-spc14.aspx">Display Templates</a> talk (<a href="https://twitter.com/search?q=%23SPC3000">#SPC3000</a>) at SPC14, you&#39;ll be seeing a lot of posts from me in the coming weeks about the components that make up my demos.&#160; Today, I wanted to talk briefly about using jQuery with display templates.&#160; Display templates are really nothing more than JavaScript so using jQuery seems natural. However, in my experience with them, there are a few things to be aware of.</p> <p><strong>1) Don&#39;t assume jQuery is loaded already</strong></p> <p>The SharePoint product team has managed to do some amazing client-side things without using a single line of jQuery.&#160; Out-of-the-box, you might find it gets loaded on occasion by certain web parts.&#160; For the most part though, you shouldn&#39;t have any expectation of it being there.&#160; When you start working with display templates, you might notice $ is defined, but it won&#39;t be what you think it is.&#160; If you aren&#39;t sure it&#39;s loaded, you can always check the developer toolbar on your page.</p> <p><a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/DisplayTemplatejQueryDeveloperToolbar_37317A89.png"><img title="DisplayTemplatejQueryDeveloperToolbar" style="border-left-width:0px;border-right-width:0px;background-image:none;border-bottom-width:0px;padding-top:0px;padding-left:0px;display:inline;padding-right:0px;border-top-width:0px;" border="0" alt="DisplayTemplatejQueryDeveloperToolbar" src="http://www.dotnetmafia.com/blogs/dotnettipoftheday/DisplayTemplatejQueryDeveloperToolbar_thumb_04CDA70A.png" width="609" height="191" /></a></p> <p>That means you need to load it yourself.&#160; For most of you, your first option is to include it in your master page. However, if you want to make a display template that doesn&#39;t have any external dependencies on the branding, you can opt to load the script yourself.&#160; Display templates have a method called <strong>$includeScript</strong> to help you load external scripts.&#160; The first parameter is always <strong>this.url</strong>.&#160; In this example, I uploaded my jQuery file into the same folder as the rest of my display templates.</p> <div style="font-size:10pt;font-family:consolas;background:white;color:black;"> <p style="margin:0px;">$includeScript(<span style="color:blue;">this</span>.url, <span style="color:#a31515;">&quot;~sitecollection/_catalogs/masterpage/Display Templates/Content Web Parts/jQuery.2.0.min.js&quot;</span>);</p> </div> <p>You can also use this with a CDN if you prefer.</p> <div style="font-size:10pt;font-family:consolas;background:white;color:black;"> <p style="margin:0px;">$includeScript(<span style="color:blue;">this</span>.url, <span style="color:#a31515;">&quot;https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.0.min.js&quot;</span>);</p> </div> <p>If you prefer, you can also register scripts with <strong>SP.SOD</strong>.</p> <p><strong>2) Know how to get element IDs</strong></p> <p>When learning about display templates, I find that one of the easiest to understand is <strong>Item_TwoLines.html</strong>.<strong>&#160; </strong>This particular template does a good job of showing how IDs are generated and rendered.&#160; Most templates follow this basic premise.&#160; First generate a unique id for the display template using <strong>ctx.ClientControl.get_nextUniqueId()</strong> and append a meaningful string to it.&#160; In this case, it generates an Id and appends <em>_2Lines_</em>.</p> <div style="font-size:10pt;font-family:consolas;background:white;color:black;"> <p style="margin:0px;"><span style="color:blue;">var</span> encodedId = $htmlEncode(ctx.ClientControl.get_nextUniqueId() + <span style="color:#a31515;">&quot;_2lines_&quot;</span>);</p> </div> <p>That gives us the base id in which all subsequent controls will use.&#160; For example, the encodedId might look something like.</p> <p><em>ctl00_ctl53_g_4c262258_fbac_4368_83cf_626d56131ca1_csr1_2lines</em></p> <p>To generate the element ID for the div containing everything in the display template, it appends to encodedId with another string.</p> <div style="font-size:10pt;font-family:consolas;background:white;color:black;"> <p style="margin:0px;"><span style="color:blue;">var</span> containerId = encodedId + <span style="color:#a31515;">&quot;container&quot;</span>;</p> </div> <p>This gives us an id for the div like the following.</p> <p><em>ctl00_ctl53_g_4c262258_fbac_4368_83cf_626d56131ca1_csr1_2lines_container</em></p> <p>The display template then uses that generated id when it is rendering.</p> <div style="font-size:10pt;font-family:consolas;background:white;color:black;"> <p style="margin:0px;"><span style="color:blue;">&lt;</span><span style="color:maroon;">div</span> <span style="color:red;">class</span><span style="color:blue;">=&quot;cbs-Item&quot;</span> <span style="color:red;">id</span><span style="color:blue;">=&quot;_#= containerId =#_&quot;</span> <span style="color:red;">data-displaytemplate</span><span style="color:blue;">=&quot;Item2Lines&quot;&gt;</span></p> </div> <p>Now, you might be thinking, ok great, I can work with that Id and do something.&#160; Maybe something like this even.</p> <div style="font-size:10pt;font-family:consolas;background:white;color:black;"> <p style="margin:0px;">$(<span style="color:#a31515;">&#39;#&#39;</span> + containerId).append(<span style="color:#a31515;">&quot;&lt;p&gt;Really good search result!&lt;/p&gt;&quot;</span>);</p> </div> <p>You can do that but only at certain times.&#160; Read on...</p> <p><strong>3) Don&#39;t try to access the DOM in the middle of your display template</strong></p> <p>What do I mean by that?&#160; Let&#39;s look at the rest of <strong>Item_TwoLines.html</strong>.</p> <div style="font-size:10pt;font-family:consolas;background:white;color:black;"><span style="color:blue;"> <div style="font-size:10pt;font-family:consolas;background:white;color:black;"> <p style="margin:0px;"><span style="color:blue;">&lt;</span><span style="color:maroon;">div</span> <span style="color:red;">class</span><span style="color:blue;">=&quot;cbs-Item&quot;</span> <span style="color:red;">id</span><span style="color:blue;">=&quot;_#= containerId =#_&quot;</span> <span style="color:red;">data-displaytemplate</span><span style="color:blue;">=&quot;Item2Lines&quot;&gt;</span></p> <p style="margin:0px;">&#160;&#160;&#160; <span style="color:blue;">&lt;</span><span style="color:maroon;">a</span> <span style="color:red;">class</span><span style="color:blue;">=&quot;cbs-ItemLink&quot;</span> <span style="color:red;">title</span><span style="color:blue;">=&quot;_#= $htmlEncode(line1.defaultValueRenderer(line1)) =#_&quot;</span> <span style="color:red;">id</span><span style="color:blue;">=&quot;_#= pictureLinkId =#_&quot;&gt;</span></p> <p style="margin:0px;">&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color:blue;">&lt;</span><span style="color:maroon;">img</span> <span style="color:red;">class</span><span style="color:blue;">=&quot;cbs-Thumbnail&quot;</span> <span style="color:red;">src</span><span style="color:blue;">=&quot;_#= $urlHtmlEncode(iconURL) =#_&quot;</span> <span style="color:red;">alt</span><span style="color:blue;">=&quot;_#= $htmlEncode(line1.defaultValueRenderer(line1)) =#_&quot;</span> <span style="color:red;">id</span><span style="color:blue;">=&quot;_#= pictureId =#_&quot;</span> <span style="color:blue;">/&gt;</span></p> <p style="margin:0px;">&#160;&#160;&#160; <span style="color:blue;">&lt;/</span><span style="color:maroon;">a</span><span style="color:blue;">&gt;</span></p> <p style="margin:0px;">&#160;&#160;&#160; <span style="color:blue;">&lt;</span><span style="color:maroon;">div</span> <span style="color:red;">class</span><span style="color:blue;">=&quot;cbs-Detail&quot;</span> <span style="color:red;">id</span><span style="color:blue;">=&quot;_#= dataContainerId =#_&quot;&gt;</span></p> <p style="margin:0px;">&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color:blue;">&lt;</span><span style="color:maroon;">a</span> <span style="color:red;">class</span><span style="color:blue;">=&quot;cbs-Line1Link ms-noWrap ms-displayBlock&quot;</span> <span style="color:red;">href</span><span style="color:blue;">=&quot;_#= linkURL =#_&quot;</span> <span style="color:red;">title</span><span style="color:blue;">=&quot;_#= $htmlEncode(line1.defaultValueRenderer(line1)) =#_&quot;</span> <span style="color:red;">id</span><span style="color:blue;">=&quot;_#= line1LinkId =#_&quot;&gt;</span>_#= line1 =#_<span style="color:blue;">&lt;/</span><span style="color:maroon;">a</span><span style="color:blue;">&gt;</span></p> <p style="margin:0px;">&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color:#006400;">&lt;!--#_</span></p> <p style="margin:0px;"><span style="color:#006400;">&#160;&#160;&#160;&#160;&#160;&#160;&#160; if(!line2.isEmpty)</span></p> <p style="margin:0px;"><span style="color:#006400;">&#160;&#160;&#160;&#160;&#160;&#160;&#160; {</span></p> <p style="margin:0px;"><span style="color:#006400;">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; // don&#39;t use jQuery here!</span></p> <p style="margin:0px;"><span style="color:#006400;">&#160;&#160;&#160;&#160;&#160;&#160;&#160; _#--&gt;</span></p> <p style="margin:0px;">&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color:blue;">&lt;</span><span style="color:maroon;">div</span> <span style="color:red;">class</span><span style="color:blue;">=&quot;cbs-Line2 ms-noWrap&quot;</span> <span style="color:red;">title</span><span style="color:blue;">=&quot;_#= $htmlEncode(line2.defaultValueRenderer(line2)) =#_&quot;</span> <span style="color:red;">id</span><span style="color:blue;">=&quot;_#= line2Id =#_&quot;&gt;</span>_#= line2 =#_<span style="color:blue;">&lt;/</span><span style="color:maroon;">div</span><span style="color:blue;">&gt;</span></p> <p style="margin:0px;">&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color:#006400;">&lt;!--#_</span></p> <p style="margin:0px;"><span style="color:#006400;">&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</span></p> <p style="margin:0px;"><span style="color:#006400;">&#160;&#160;&#160;&#160;&#160;&#160;&#160; _#--&gt;</span></p> <p style="margin:0px;">&#160;&#160;&#160; <span style="color:blue;">&lt;/</span><span style="color:maroon;">div</span><span style="color:blue;">&gt;</span></p> <p style="margin:0px;">&#160;&#160;&#160; <span style="color:#006400;">&lt;!--#_</span></p> <p style="margin:0px;"><span style="color:#006400;">&#160;&#160;&#160;&#160;&#160;&#160;&#160; // don&#39;t use jQuery here either!</span></p> <p style="margin:0px;"><span style="color:#006400;">&#160;&#160;&#160; _#--&gt;</span></p> <p style="margin:0px;"><span style="color:blue;">&lt;/</span><span style="color:maroon;">div</span><span style="color:blue;">&gt;</span></p> </div> </span></div> <p>Display Templates let you do some old school classic ASP style spaghetti code.&#160; So you might think what if I try to reference an element inside that if statement or maybe you just add a new script block past the end of the last div.&#160; You can try it and your script will execute, but your jQuery won&#39;t find anything when you try to reference an element by id.&#160; That&#39;s because it&#39;s not on the page yet.&#160; If you look at the script that is generated for the display template, you will see that all of your HTML is pushed onto the <strong>ms_outHtml</strong> object.&#160; This allows all of your display template HTML to be pushed onto the DOM at once in a more efficient manner.&#160; How do we work around that?&#160; Read on...</p> <p><strong>4) Use jQuery inside of OnPostRender</strong></p> <p>Any jQuery manipulation you want to do really needs to occur in a post render event using <strong>AddPostRenderCallback</strong>.&#160; You just register a function and this will get executed when all of the HTML for the display template has hit the page.&#160; Think of this is as your <strong>$(document).ready()</strong> of display templates.&#160; Take a look at the code snippet below to see how we would have executed our previous example.</p> <div style="font-size:10pt;font-family:consolas;background:white;color:black;"> <p style="margin:0px;">AddPostRenderCallback(ctx, <span style="color:blue;">function</span> () {</p> <p style="margin:0px;">&#160;&#160;&#160; $(<span style="color:#a31515;">&#39;#&#39;</span> + containerId).append(<span style="color:#a31515;">&quot;&lt;p&gt;Something&lt;/p&gt;&quot;</span>);</p> <p style="margin:0px;">});</p> </div> <p>This will execute the line of code inside the function once the display template has rendered and then you will get the expected results.</p> <p>&#160;</p> <p>If you are just starting with display templates, hopefully you have found these quick tips useful.&#160; We&#39;ll be covering this in more detail at my session at SPC14.&#160; I hope to see you there.</p> <p>Follow me on twitter: <a href="https://twitter.com/coreyroth">@coreyroth</a>.</p><img src="http://www.dotnetmafia.com/aggbug.aspx?PostID=6651" width="1" height="1">SharePointEnterprise SearchSharePoint OnlineOffice 365Office 365 GridSharePoint 2013JavaScriptHow to: Get a ClientContext for a site given a full URLhttp://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/02/12/how-to-get-a-clientcontext-for-a-site-given-a-full-url.aspxWed, 12 Feb 2014 22:08:28 GMTceb7fe2a-c56b-4d85-99e6-8dd548580538:6649CoreyRoth2http://www.dotnetmafia.com/blogs/dotnettipoftheday/rsscomments.aspx?PostID=6649http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/02/12/how-to-get-a-clientcontext-for-a-site-given-a-full-url.aspx#comments<p>Last week, I was faced with the challenge of determining the URL to the site (web) when I knew the URL to a document in SharePoint.&#160; I also happened to know the URL to the site collection, but I wanted to reliably get the URL to a web so I could get a ClientContext object for it.&#160; Why would I want to do this?&#160; Come to my session (<a href="https://twitter.com/search?q=%23SPC3000">#SPC3000</a>) on <a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/02/10/speaking-at-sharepoint-conference-2014-spc14.aspx">Display Templates</a> at <a href="https://twitter.com/search?q=%23SPC14">#SPC14</a> to find out!&#160; I also had someone leave a comment on my blog in just the last week about this topic, so I thought now would be a good time to share it.&#160; <a href="https://twitter.com/jthake">@JThake</a> suggested that he would have just parsed the path, but given a complex URL, that&#39;s not going to work!</p> <p>With the <a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2008/02/14/how-to-get-a-spweb-object-given-a-full-url.aspx">server-side object model</a>, you can pass a full URL to the SPSite constructor and get a reference to an SPWeb object.&#160; This trick doesn&#39;t work though with JSOM.&#160; If you want a reference to a given subsite, you need to pass the exact URL.&#160; This technique wasn&#39;t obvious to me or a few others, but luckily <a href="https://twitter.com/path2sharepoint">@Path2SharePoint</a> shed some light on this when he mentioned I needed to use <strong>/_api/contextinfo</strong>.&#160; For those of you who have doing a lot with client side code you are probably familiar with this end point because you can get your form digest that way.&#160; Honestly though, there is not a lot of information out there on this particular endpoint.</p> <p>I did a search on the Internet to see if I could find a complete working example and things were pretty scarce.&#160; Luckily though, I found a snippet on the bottom of a post from <a href="https://twitter.com/wictor">@Wictor</a> that had just enough of what I needed.&#160; Let&#39;s put it all together.&#160; First let&#39;s assume we have a URL like the one below.&#160; This refers to a document in buried in a folder of a document library of a subsite.&#160; </p> <div style="font-size:10pt;font-family:consolas;background:white;color:black;"> <p style="margin:0px;"><span style="color:blue;">var</span> documentPath = <span style="color:#a31515;">&quot;http://server/sitecollection/site/documents/folder/mydocument.docx&quot;</span>;</p> </div> <p>We need to make a <strong>$.ajax()</strong> cal to the <strong>/_api/contextinfo</strong> end point, but first we need to assemble a URL that works.&#160; To do this, we need to strip off the filename.&#160; We&#39;ll then append our end point to the path.&#160; We&#39;ll just use the <strong>substring </strong>function and <strong>lastIndexOf</strong> to split the path off.&#160; If you have a library loaded to work with URLs, you can use one of those methods as well.</p> <div style="font-size:10pt;font-family:consolas;background:white;color:black;"> <p style="margin:0px;">documentPath = documentPath.substring(0, documentPath.lastIndexOf(<span style="color:#a31515;">&#39;/&#39;</span>)) + <span style="color:#a31515;">&quot;/_api/contextinfo&quot;</span>;</p> </div> <p>Now, we need to call <strong>$.ajax()</strong> to get the objects we need.&#160; One thing to know is that this endpoint requires you to use <strong>POST</strong> instead of <strong>GET</strong>.&#160; That means, you can&#39;t just try the URL in a web browser.&#160; Here is what the code looks like.</p> <div style="font-size:10pt;font-family:consolas;background:white;color:black;"> <p style="margin:0px;">$.ajax({</p> <p style="margin:0px;">&#160;&#160;&#160; url: docuemntPath,</p> <p style="margin:0px;">&#160;&#160;&#160; type: <span style="color:#a31515;">&quot;POST&quot;</span>,</p> <p style="margin:0px;">&#160;&#160;&#160; contentType: <span style="color:#a31515;">&quot;application/x-www-url-encoded&quot;</span>,</p> <p style="margin:0px;">&#160;&#160;&#160; dataType: <span style="color:#a31515;">&quot;json&quot;</span>,</p> <p style="margin:0px;">&#160;&#160;&#160; headers: {</p> <p style="margin:0px;">&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color:#a31515;">&quot;Accept&quot;</span>: <span style="color:#a31515;">&quot;application/json; odata=verbose&quot;</span>,</p> <p style="margin:0px;">&#160;&#160;&#160; },</p> <p style="margin:0px;">&#160;&#160;&#160; success: <span style="color:blue;">function</span> (data) {</p> <p style="margin:0px;">&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color:blue;">if</span> (data.d) {</p> <p style="margin:0px;">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color:blue;">var</span> webUrl = data.d.GetContextWebInformation.WebFullUrl;</p> <p style="margin:0px;">&#160;</p> <p style="margin:0px;">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; <span style="color:blue;">var</span> clientContext = <span style="color:blue;">new</span> SP.ClientContext(webUrl);</p> <p style="margin:0px;">&#160;&#160;&#160;&#160;&#160;&#160;&#160; }</p> <p style="margin:0px;">&#160;&#160;&#160; },</p> <p style="margin:0px;">&#160;&#160;&#160; error: <span style="color:blue;">function</span> (err) {</p> <p style="margin:0px;">&#160;&#160;&#160;&#160;&#160;&#160; alert(JSON.stringify(err));</p> <p style="margin:0px;">&#160;&#160;&#160; }</p> <p style="margin:0px;">);</p> </div> <p>In the success function, we can get the information we need from <strong>data.d.GetContextWebInformation.WebFullUrl</strong>.&#160; This has the full path to the site hosting the document.&#160; From there, we can just pass it to the constructor of <strong>SP.ClientContext</strong> to get a reference to that subsite.&#160; Now you can work directly with the objects on that site such as it&#39;s lists or whatever you need.</p> <p>We&#39;ll be using this snippet of code in my talk <a href="https://twitter.com/search?q=%23SPC3000">#SPC3000</a> at <a href="http://www.dotnetmafia.com/blogs/dotnettipoftheday/archive/2014/02/10/speaking-at-sharepoint-conference-2014-spc14.aspx">SharePoint Conference</a>.&#160; If you have an interest in display templates, be sure and attend!&#160; Thanks again to everyone that helped me with this!</p><img src="http://www.dotnetmafia.com/aggbug.aspx?PostID=6649" width="1" height="1">SharePointSharePoint OnlineOffice 365Office 365 GridSharePoint 2013Client Object ModelJavaScriptSPC14