<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Frank Marasco</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/frank_marasco/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/frank_marasco/feed.xml</link>
    <description />
    <lastBuildDate>Wed, 17 Apr 2019 03:23:14 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>AutoTagging Content using CSOM</title>
      <link>https://docs.microsoft.com/archive/blogs/frank_marasco/autotagging-content-using-csom</link>
      <pubDate>Sat, 16 Aug 2014 07:03:00 GMT</pubDate>
      <dc:creator><![CDATA[Frank Marasco MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/frank_marasco/2014/08/16/autotagging-content-using-csom/</guid>
      <description><![CDATA[Overview
&nbsp;
I have been asked a few times how to implement an auto-tagging solution using...]]></description>
      <content:encoded><![CDATA[<h2><span style="font-size: 1.5em;">Overview</span></h2>
<p>&nbsp;</p>
<p>I have been asked a few times how to implement an auto-tagging solution using CSOM, how can I set Taxonomy fields programmatically? &nbsp;This sample demonstrates how to auto-tag&nbsp; a document that will populate meta-data at the time the document is upload to SharePoint. This solution will enable more refined search results and assist with classification of content. A Remote Event Receiver (ReR) will be used to retrieve the properties from the users profile and update the meta-data of the document at the time of document upload. The user will will have option of override those auto populated properties.</p>
<p>This samples demonstrates the following:</p>
<ul>
<li>Adding a Fields and Content Types to the host web</li>
<li>Creation of Taxonomy Fields programmatically</li>
<li>Creating Two Libraries with the associated content type that are deployed to the host web</li>
<li>Register a ItemAdding &amp; ItemAdded Remote Event Receiver in the host web</li>
<li>Removal of the Remote Event Receiver</li>
<li>Retrieval of User Profile Properties.</li>
<li>Setting Taxonomy Fields programmatically</li>
</ul>
<h2>Permissions</h2>
<p>AppOnly Permissions are used in this solution</p>
<ul>
<li>Tenant: FullControl</li>
<li>Taxonomy: Read</li>
<li>Social: Read</li>
</ul>
<h2>Adding Fields and Content Types</h2>
<p>To create the fields and content types the below code leverages <a href="https://github.com/OfficeDev/PnP/tree/master/OfficeDevPnP.Core">OfficeDevPnP.Core</a>.&nbsp; We chose to create the fields and content types programmatically and this is the preferred approach. This gives you greater control for future mainteanance, gives you away to&nbsp; implement localized versions of your fields and content types.</p>
<pre>        //Check the fields<br />        if (!ctx.Web.FieldExistsById(FLD_CLASSIFICATION_ID))<br />        {<br />                ctx.Web.CreateTaxonomyField(FLD_CLASSIFICATION_ID,<br />                                            FLD_CLASSIFICATION_INTERNAL_NAME,<br />                                            FLD_CLASSIFICATION_DISPLAY_NAME,<br />                                            FIELDS_GROUP_NAME,<br />                                            TAXONOMY_GROUP,<br />                                            TAXONOMY_TERMSET_CLASSIFICATION_NAME);<br />        }<br /><br />        //check the content type<br />        if (!ctx.Web.ContentTypeExistsById(CONTOSODOCUMENT_CT_ID))<br />        {<br />                ctx.Web.CreateContentType(CONTOSODOCUMENT_CT_NAME,<br />                                          CT_DESC, CONTOSODOCUMENT_CT_ID,<br />                                          CT_GROUP);<br />        }<br /><br />        //associate fields to content types<br />        if (!ctx.Web.FieldExistsByNameInContentType(CONTOSODOCUMENT_CT_NAME, FLD_CLASSIFICATION_INTERNAL_NAME))<br />        {<br />                ctx.Web.AddFieldToContentTypeById(CONTOSODOCUMENT_CT_ID,<br />                                                  FLD_CLASSIFICATION_ID.ToString(),<br />                                                  false);<br />        }</pre>
<p>&nbsp;</p>
<p><span style="font-size: 1.5em;">Create Document Library</span></p>
<p>To create a document library we use the following code. We are again, leveraging core to provide this functionality. The following code will create the library, enable versioning and remove the default Document content type.&nbsp;</p>
<pre>            if (!ctx.Web.ListExists(library.Title))<br />            {<br />                ctx.Web.AddList(ListTemplateType.DocumentLibrary, library.Title, false);<br />                List _list = ctx.Web.GetListByTitle(library.Title);<br />                if (!string.IsNullOrEmpty(library.Description))<br />                {<br />                    _list.Description = library.Description;<br />                }<br /><br />                if (library.VerisioningEnabled)<br />                {<br />                    _list.EnableVersioning = true;<br />                }<br /><br />                _list.ContentTypesEnabled = true;<br />                _list.RemoveContentTypeByName("Document");<br />                _list.Update();<br />                <br />                ctx.Web.AddContentTypeToListById(library.Title, associateContentTypeID, true);<br />                ctx.Web.Context.ExecuteQuery();<br />               <br />            }<br />            else<br />            {<br />                throw new Exception("A list, survey, discussion board, or document library with the specified title already exists in this Web site.  Please choose another title.");<br />            }</pre>
<h2>Registering the Remote Event Receiver in the host web</h2>
<p>We will register two remote event receivers, which are ItemAdding and ItemAdded to two separate libraries.&nbsp;</p>
<pre>       public static void AddEventReceiver(ClientContext ctx, List list, EventReceiverDefinitionCreationInformation eventReceiverInfo)<br />        {<br />            if (!DoesEventReceiverExistByName(ctx, list, eventReceiverInfo.ReceiverName))<br />            {<br />                list.EventReceivers.Add(eventReceiverInfo);<br />                ctx.ExecuteQuery();<br />            }<br />        }</pre>
<p>We want to make sure that when we add the event receiver that one doesn't already exist. The following code demonstrates how to check if the receiver exists by name.</p>
<pre>       public static bool DoesEventReceiverExistByName(ClientContext ctx, List list, string eventReceiverName )<br />        {<br />            bool _doesExist = false;<br />            ctx.Load(list, lib =&gt; lib.EventReceivers);<br />            ctx.ExecuteQuery();<br /><br />            var _rer = list.EventReceivers.Where(e =&gt; e.ReceiverName == eventReceiverName).FirstOrDefault();<br />            if (_rer != null) {<br />                _doesExist = true;<br />            }<br /><br />            return _doesExist;<br />        }</pre>
<p>The following code is used to help us create a new EventReceiverDefinitionCreationInformation object. Make sure you change your ReceiverUrl to match your environment.</p>
<pre>       public static EventReceiverDefinitionCreationInformation CreateEventReciever(string receiverName, EventReceiverType type)<br />        {<br /><br />            EventReceiverDefinitionCreationInformation _rer = new EventReceiverDefinitionCreationInformation();<br />            _rer.EventType = type;<br />            _rer.ReceiverName = receiverName;<br />            _rer.ReceiverClass = "ECM.AutoTaggingWeb.Services.AutoTaggingService";<br />            _rer.ReceiverUrl = "https://amsecm.azurewebsites.net/Services/AutoTaggingService.svc";<br />            _rer.Synchronization = EventReceiverSynchronization.Synchronous;<br />            return _rer;<br />        }</pre>
<h2>Removing the Remote Event Receiver in the host web</h2>
<p>The following code is use to remove the event receiver from the list.</p>
<pre> public static void RemoveEventReceiver(ClientContext ctx, List list, string receiverName)<br />        {<br />            ctx.Load(list, lib =&gt; lib.EventReceivers);<br />            ctx.ExecuteQuery();<br /><br />            var _rer = list.EventReceivers.Where(e =&gt; e.ReceiverName == receiverName).FirstOrDefault();<br />            if(_rer != null)<br />            {<br />                _rer.DeleteObject();<br />                ctx.ExecuteQuery();<br />            }<br />        }</pre>
<h2>ItemAdding Remote Event Receiver</h2>
<p>Implementation class for ItemAdding. The ItemAdding member uses the result ChangeItemProperties to update the taxonomy field. &nbsp;We need to check if the the document already contains the properties for the Taxonomy Field, in this scenario we don't want to update the field. When debugging the code you will notice that if the property is already supplied the format is&nbsp;<strong>2;#MYTERNNAME|74972ac9-3183-4775-b232-cd6de3569c65</strong>&nbsp;This is the WssID, the value of the term and the GUID.&nbsp;</p>
<pre>        public void HandleAutoTaggingItemAdding(SPRemoteEventProperties properties,SPRemoteEventResult result)<br />        {<br />            using (ClientContext ctx = TokenHelper.CreateRemoteEventReceiverClientContext(properties))<br />            {<br />                if (ctx != null)<br />                {<br />                    var itemProperties = properties.ItemEventProperties;<br />                    var _userLoginName = properties.ItemEventProperties.UserLoginName;<br />                    var _afterProperites = itemProperties.AfterProperties;<br />                    if(!_afterProperites.ContainsKey(ScenarioHandler.FLD_CLASSIFICATION_INTERNAL_NAME))<br />                    {<br />                        string _classficationToSet = ProfileHelper.GetProfilePropertyFor(ctx, _userLoginName, Constants.UPA_CLASSIFICATION_PROPERTY);<br />                        if(!string.IsNullOrEmpty(_classficationToSet))<br />                        { <br />                            var _formatTaxonomy = AutoTaggingHelper.GetTaxonomyFormat(ctx, _classficationToSet);<br />                            result.ChangedItemProperties.Add(ScenarioHandler.FLD_CLASSIFICATION_INTERNAL_NAME, _formatTaxonomy);<br />                        }<br />                    }<br />                }<br />            }<br />        }</pre>
&nbsp;
<p>To get the term value in this format, we are going to use the below code that queries the TaxonomyHiddenList. Warning, DO NOT modify this list, you can read. Remember, you can look but don't touch.</p>
<pre>        public static string GetTaxonomyFormat(ClientContext ctx, string term)<br />        { <br />            if(string.IsNullOrEmpty(term))<br />            {<br />                throw new ArgumentException(string.Format(EXCEPTION_MSG_INVALID_ARG, "term"));<br />            }<br />            string _result = string.Empty;<br />            var _list = ctx.Web.Lists.GetByTitle(TAXONOMY_HIDDEN_LIST_NAME);<br />            CamlQuery _caml = new CamlQuery();<br /><br />            _caml.ViewXml = string.Format(TAXONOMY_CAML_QRY, term);<br />            var _listItemCollection = _list.GetItems(_caml);<br /><br />            ctx.Load(_listItemCollection,<br />                eachItem =&gt; eachItem.Include(<br />                    item =&gt; item,<br />                    item =&gt; item.Id,<br />                    item =&gt; item[TAXONOMY_FIELDS_IDFORTERM]));<br />            ctx.ExecuteQuery();<br /><br />            if (_listItemCollection.Count &gt; 0)<br />            {<br />                var _item = _listItemCollection.FirstOrDefault();<br />                var _wssId = _item.Id;<br />                var _termId = _item[TAXONOMY_FIELDS_IDFORTERM].ToString(); ;<br />                _result = string.Format(TAXONOMY_FORMATED_STRING, _wssId, term, _termId);<br />            }<br /><br />            return _result;<br />        }</pre>
<h2>ItemAdded Remote Event Receiver</h2>
<p>Implementation class for ItemAdded. The ItemAdded event receiver is implemented Synchronously. This implementation queries the list and updates the Taxonomy field in the list.</p>
<pre>        public void HandleAutoTaggingItemAdded(SPRemoteEventProperties properties)<br />        {<br />            using (ClientContext ctx = TokenHelper.CreateRemoteEventReceiverClientContext(properties))<br />            {<br />                if (ctx != null)<br />                {<br />                    string _userLoginName = properties.ItemEventProperties.UserLoginName;<br />                    List _library = ctx.Web.Lists.GetById(properties.ItemEventProperties.ListId);<br />                    var _itemToUpdate = _library.GetItemById(properties.ItemEventProperties.ListItemId);<br />                    ctx.Load(_itemToUpdate);<br />                    ctx.ExecuteQuery();<br /><br />                    Hashtable _model = new Hashtable();<br />                    string _classficationToSet = ProfileHelper.GetProfilePropertyFor(ctx, _userLoginName, Constants.UPA_CLASSIFICATION_PROPERTY);<br />                    if (!string.IsNullOrEmpty(_classficationToSet))<br />                    {<br />                        _model.Add(ScenarioHandler.FLD_CLASSIFICATION_INTERNAL_NAME, _classficationToSet);<br />                        AutoTaggingHelper.SetTaxonomyField(ctx, _itemToUpdate, _model);<br />                    }<br />                }<br />            }<br />        }</pre>
<p>The AutoTagginghelper.SetTaxonomyField implementation uses the following code. The code is more expensive since we have to query the FieldCollection, get the Taxonomy Field that you want to update and then finally search for the Term by name to retrieve the Term Id.</p>
<pre>public static void SetTaxonomyField(ClientContext ctx, ListItem listItem, Hashtable model)<br />        {<br />          <br />            FieldCollection _fields = listItem.ParentList.Fields;<br />            ctx.Load(_fields);<br />            ctx.ExecuteQuery();<br /><br />            foreach(var _key in model.Keys)<br />            {<br />               var _termName = model[_key].ToString();<br />               TaxonomyField _field = ctx.CastTo&lt;TaxonomyField&gt;(_fields.GetByInternalNameOrTitle(_key.ToString()));<br />               ctx.Load(_field);<br />               ctx.ExecuteQuery();<br />               Guid _id = _field.TermSetId;<br />               string _termID = AutoTaggingHelper.GetTermIdByName(ctx, _termName, _id );<br />               var _termValue = new TaxonomyFieldValue()<br />               {<br />                   Label = _termName,<br />                   TermGuid = _termID,<br />                   WssId = -1<br />               };<br /><br />               _field.SetFieldValueByValue(listItem, _termValue);<br />               listItem.Update();<br />               ctx.ExecuteQuery();<br />            }<br />        }</pre>
<p><code class="csharp"><br /></code></p>
<h2>Recommendations</h2>
<p>While your testing the two scenarios, you will noticed that the ItemAdding implementation is more responsive, this is due to how we are getting the term, the guid and its wssId, as well query int the list item. We have one call vs four in the ItemAdded implementation.&nbsp;&nbsp;If your use case matches this scenario, then I would recommend that you use ItemAdding instead of ItemAdded (Synchronously).&nbsp;&nbsp;You should also make sure you code is as efficient as possible. Another possible solution which is more efficient is to use ItemAdded asynchronously and queue the actions so that we are not blocking the user in the UI.</p>
<h2>Dependencies</h2>
<ul>
<li>Microsoft.SharePoint.Client</li>
<li>Microsoft.SharePoint.Client.Runtime</li>
<li>Microsoft.SharePoint.Client.Taxonomy</li>
<li>Microsoft.SharePoint.Client.UserProfiles</li>
</ul>
<h2>References</h2>
<ul>
<li><a href="https://blogs.msdn.com/b/vesku/archive/2013/11/25/setting-up-provider-hosted-app-to-windows-azure-for-office365-tenant.aspx">Setting up provider hosted app to Windows Azure for Office365 tenant</a></li>
<li><a href="https://github.com/OfficeDev/PnP">Office 365 PnP</a></li>
<li>The <a href="https://github.com/OfficeDev/PnP/tree/master/Scenarios/ECM.AutoTagging">code </a>sample used in this blog.</li>
<li><a href="https://blogs.msdn.com/b/kaevans/archive/2013/02/23/sharepoint-2013-app-only-policy-made-easy.aspx">Using App-Only Tokens</a></li>
<li><a href="https://blogs.msdn.com/b/vesku/archive/2013/11/06/ftc-to-cam-stop-creating-content-types-and-site-columns-declaratively.aspx">FTC to CAM &ndash; Stop creating content types and site columns declaratively</a></li>
</ul>]]></content:encoded>
    </item>
    <item>
      <title>How to upload and activate sandbox solutions using CSOM</title>
      <link>https://docs.microsoft.com/archive/blogs/frank_marasco/how-to-upload-and-activate-sandbox-solutions-using-csom</link>
      <pubDate>Sun, 10 Aug 2014 11:39:00 GMT</pubDate>
      <dc:creator><![CDATA[Frank Marasco MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/frank_marasco/2014/08/10/how-to-upload-and-activate-sandbox-solutions-using-csom/</guid>
      <description><![CDATA[I was working with one my friends, Suman (shoe) Chakrabarti on a Patterns &amp; Practice solution...]]></description>
      <content:encoded><![CDATA[<p>I was working with one my friends, Suman (shoe) Chakrabarti on a Patterns &amp; Practice solution that&nbsp;prevents deletion of sites using partial code (Sandbox). Well he wrote the code for that, but we decided to add an extra bonus item. We thought it would be nice to show how to add the solution to the gallery and active the solution in the sample.&nbsp;</p>
<div>
<h2>Solution Deployment/Removal</h2>
</div>
<p>In order to upload the wsp to the Solution Gallery, we are going to use &nbsp;the FileCreationInformation class to upload the file to the gallery. Removing the solution is handled by finding the uploaded file and calling the DeleteObject member.</p>
<p>// get the file from the server path in the provider site</p>
<p>var filePath = Server.MapPath("~/PreventDeleteSites.wsp");</p>
<p>var file = new FileStream(filePath, FileMode.Open);</p>
<p>// create the FileCreationInformation object and prepare</p>
<p>// to upload it to the solution gallery</p>
<p>var fileCI = new FileCreationInformation() {</p>
<p>&nbsp;&nbsp;&nbsp; ContentStream = file,</p>
<p>&nbsp;&nbsp;&nbsp; Url = "PreventDeleteSites.wsp",</p>
<p>&nbsp;&nbsp;&nbsp; Overwrite = true</p>
<p>};</p>
<p>// upload the solution to the gallery</p>
<p>var uploadedFile = solutionGallery.RootFolder.Files.Add(fileCI);</p>
<p>clientContext.Load(uploadedFile);</p>
<p>clientContext.ExecuteQuery();</p>
<div>
<h2>Solution Activation/Deactivation</h2>
</div>
<p>Solution activation is done using the <a href="https://msdn.microsoft.com/en-us/library/microsoft.sharepoint.client.publishing.designpackage.aspx">DesignPackage </a>class in the <a href="https://msdn.microsoft.com/en-us/library/jj164782.aspx">Microsoft.SharePoint.Client.Publishing&nbsp;</a>assembly. &nbsp;We are going to use the <a href="https://msdn.microsoft.com/en-us/library/microsoft.sharepoint.client.publishing.designpackage.install.aspx">DesignPackage.Install&nbsp;</a>and <a href="https://msdn.microsoft.com/en-us/library/microsoft.sharepoint.client.publishing.designpackage.uninstall.aspx">DesignPackage.Uninstall</a> members to activate and deactivate the solution for the site collection.</p>
<p>// get the DesignPackageInfo (which is the same name for a sandbox solution)</p>
<p>var wsp = new DesignPackageInfo(){</p>
<p>&nbsp;&nbsp;&nbsp; // during deployment, the solution ID is not necessary but we need the Guid during removal</p>
<p>&nbsp;&nbsp;&nbsp; PackageGuid = Guid.Empty, // 4c16c0b9-0162-43ad-a8e9-a4b810e58a56</p>
<p>&nbsp;&nbsp;&nbsp; PackageName = "PreventDeleteSites"</p>
<p>};</p>
<p>// install the solution from the file url</p>
<p>var filerelativeurl = solutionGallery.RootFolder.ServerRelativeUrl + "/PreventDeleteSites.wsp";</p>
<p>DesignPackage.Install(clientContext, clientContext.Site, wsp, filerelativeurl);</p>
<p>clientContext.ExecuteQuery();</p>
<h2>References</h2>
<p>The <a href="https://github.com/OfficeDev/PnP/tree/master/Samples/Provisioning.PreventDeleteSites">Prevent Site Deletion</a>&nbsp;sample that shows this code in action</p>
<p><a href="https://github.com/OfficeDev/PnP">Office 365 Patterns and Practices</a></p>
<p><a href="https://msdn.microsoft.com/en-us/library/jj164782.aspx">Microsoft.SharePoint.Client.Publishing</a>&nbsp;</p>
<h1 class="title">&nbsp;</h1>]]></content:encoded>
    </item>
    <item>
      <title>SideLoading Guideance</title>
      <link>https://docs.microsoft.com/archive/blogs/frank_marasco/sideloading-guideance</link>
      <pubDate>Sat, 26 Jul 2014 12:16:00 GMT</pubDate>
      <dc:creator><![CDATA[Frank Marasco MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/frank_marasco/2014/07/26/sideloading-guideance/</guid>
      <description><![CDATA[This scenario shows how one can use Side Loading to install a SharePoint Provider Hosted Application...]]></description>
      <content:encoded><![CDATA[<p>This scenario shows how one can use Side Loading to install a SharePoint Provider Hosted Application to a site collection.&nbsp;</p>
<p>SharePoint Administrators can deploy apps to their tenancy basically two different ways. Deploy from the app catalog (&ldquo;app stapling&rdquo;) or via sideloading.</p>
<p>What is sideloading? App sideloading, from a SharePoint context, is the ability to install a SharePoint app directly into a site to explicitly bypass the regular governance controls of SharePoint. The main reason for blocking sideloading by default on non-developer sites is the risk that faulty apps pose to their host web/host site collection. Apps have the potential to destroy data and make sites or, given enough permissions, can even make site collections unusable. Therefore, apps should only be sideloaded in dev/test environments and in production only when deploying from the AppCatalog does not meet your needs. It is <strong>NOT</strong> recommended to sideload SharePoint Hosted-Applications, because of the risk of data loss.</p>
<p><em>Note</em>:</p>
<ul>
<li>Enabling the app sideloading features requires tenant admin permissions (in a multi-tenant environment) or farm admin permissions (in a single tenant environment).</li>
<li>You must have a user context when sideloading the application. App-only permission is not available.</li>
<li>Sideloading does not suppress the security check or compensate existing security requirements. It does however enable the programmatic installation of an app</li>
<li>You must still register and app principal for SharePoint Provider hosted applications</li>
<li>You should deactivate the sideloading feature immediately once the app is in installed. Site Collections administrators can install apps using CSOM which could circumvent your governance practices.</li>
</ul>
<div>
<h2>Centrally deployed apps vs side loading comparison</h2>
<p>&nbsp;</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="604">
<div>
<h1><strong>App Stapling (Deploy from App Catalog)</strong></h1>
</div>
</td>
<td valign="top" width="604">
<div>
<h1><strong>Sideloading</strong></h1>
</div>
</td>
</tr>
<tr>
<td valign="top" width="604">
<p>Custom actions and app parts are not supported</p>
</td>
<td valign="top" width="604">
<p>Custom actions and app parts are supported</p>
</td>
</tr>
<tr>
<td valign="top" width="604">
<p>App Install, Uninstall and upgrade event receivers cannot be handled</p>
</td>
<td valign="top" width="604">
<p>App Install, Uninstall and upgrade event receivers do fire and can be handled</p>
</td>
</tr>
<tr>
<td valign="top" width="604">
<p>Site Collection Administrators cannot uninstall the application</p>
</td>
<td valign="top" width="604">
<p>Site Collection Administrators can uninstall the application</p>
</td>
</tr>
<tr>
<td valign="top" width="604">
<p>Applied to new and existing site collections</p>
</td>
<td valign="top" width="604">
<p>Custom code must be used to install the application</p>
</td>
</tr>
<tr>
<td valign="top" width="604">
<p>There is metadata about the app and updates are applied</p>
</td>
<td valign="top" width="604">
<p>Tenant Administrators must enable the sideloading feature prior to install the application and should be disabled after the application is installed</p>
</td>
</tr>
<tr>
<td valign="top" width="604">
<p>&nbsp;</p>
</td>
<td valign="top" width="604">
<p>There is no metadata about the app and updates have to be managed manually</p>
</td>
</tr>
</tbody>
</table>
</div>
<div>
<div>
<h1>How to use against Office 365 Multi-tenant</h1>
</div>
<p>Since this solution is based on the using a provider hosted application, the following should be taken in account:</p>
<ul>
<li>The user must be a tenant administrator in order to enable the SideLoading Feature</li>
<li>The Provider hosted application has already been registered by the tenant administrator</li>
<li>The Provider hosted application has been deployed to your hosting platform</li>
</ul>
<p>&nbsp;</p>
<p>Guid _sideloadingFeature = new Guid("AE3A1339-61F5-4f8f-81A7-ABD2DA956A7D");</p>
<p>string _url = GetUserInput("Please Supply the SharePoint Online Site Collection URL: ");</p>
<p>/* Prompt for Credentials */</p>
<p>Console.WriteLine("Enter Credentials for {0}", _url);</p>
<p>string _userName = GetUserInput("SharePoint Username: ");</p>
<p>SecureString _pwd = GetPassword();</p>
<p>ClientContext _ctx = new ClientContext(_url);</p>
<p>_ctx.ApplicationName = "AMS SIDELOADING SAMPLE";</p>
<p>_ctx.AuthenticationMode = ClientAuthenticationMode.Default;</p>
<p>//For SharePoint Online</p>
<p>_ctx.Credentials = new SharePointOnlineCredentials(_userName, _pwd);</p>
<p>string _path = GetUserInput("Please supply path to your app package:");</p>
<p>Site _site = _ctx.Site;</p>
<p>Web _web = _ctx.Web;</p>
<p>try</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; _ctx.Load(_web);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; _ctx.ExecuteQuery();</p>
<p>//Make sure we have side loading enabled. You must be a tenant admin to activate or you will get an exception! The ProcessFeature is an extension method,</p>
<p>//see Office365 Development Patterns and Practices (<a href="https://github.com/OfficeDev/PnP">https://github.com/OfficeDev/PnP</a>) OfficeDevPnp.Core sample</p>
<p>&nbsp;&nbsp; &nbsp;&nbsp; _site.ProcessFeature(_sideloadingFeature, true);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; try</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var _appstream = System.IO.File.OpenRead(_path);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;AppInstance _app = _web.LoadAndInstallApp(_appstream);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _ctx.Load(_app);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _ctx.ExecuteQuery();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch{</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; throw;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //we should ensure that the side loading feature is disable when we are done or if an exception occurs</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _site.ProcessFeature(_sideloadingFeature, false);</p>
<p>}</p>
<p>catch (Exception _ex)</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; Console.ForegroundColor = ConsoleColor.Red;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine(string.Format("Exception!"), _ex.ToString());</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("Press any key to continue.");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; Console.Read();</p>
<p>}</p>
<h2>References</h2>
<p class="title"><a href="https://www.microsoft.com/en-us/download/confirmation.aspx?id=42030p;">SharePoint 2013 and SharePoint Online solution pack for branding and site provisioning&nbsp;</a></p>
<p><a href="https://github.com/OfficeDev/PnP">Code Sample in the blog</a> &nbsp;See the&nbsp;Core.SideLoading sample</p>
</div>]]></content:encoded>
    </item>
    <item>
      <title>Synchronize term sets using CSOM</title>
      <link>https://docs.microsoft.com/archive/blogs/frank_marasco/synchronize-term-sets-using-csom</link>
      <pubDate>Sun, 29 Jun 2014 07:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Frank Marasco MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/frank_marasco/2014/06/29/synchronize-term-sets-using-csom/</guid>
      <description><![CDATA[Have you ever wanted to move Taxonomy items from one term store to another? With on-premises...]]></description>
      <content:encoded><![CDATA[<p>Have you ever wanted to move Taxonomy items from one term store to another? With on-premises implementations you can move your MMS database, but I think this takes to long. What about SharePoint Online? Obviously, we cannot move our database to Office 365 this way. There is an OfficeAMS sample (Core.MMS) &nbsp;that demonstrates how to perform basic taxonomy operations with the term store however, what if you have use case where you only want to synchronize your changes. This sample takes the Core.MMS sample a step further, by using the ChangeInformation class in the Microsoft.SharePoint.Client.Taxonomy assembly to synchronize your term stores.</p>
<p>This sample will use the ChangeInformation class to return all the changes in the source term store.&nbsp;</p>
<p>First, create a TaxonomySession object.&nbsp;</p>
<pre class="brush: csharp; ruler: true; auto-links: false;">DateTime _startFrom = DateTime.Now.AddYears(-1);<br /><br />Console.WriteLine("Opening the taxonomy session");<br />TaxonomySession _sourceTaxonomySession = TaxonomySession.GetTaxonomySession(sourceClientContext);<br />TermStore sourceTermStore = _sourceTaxonomySession.GetDefaultKeywordsTermStore();<br />sourceClientContext.Load(sourceTermStore);<br />sourceClientContext.ExecuteQuery();</pre>
<p>Once you have created the TaxonomySession object we need to get the changes, we get the changes by creating a new Instance of ChangeInformation and set the start date. In this case, I&rsquo;m getting all the changes from 1 year ago.&nbsp;</p>
<pre class="brush: csharp; ruler: true; auto-links: false;">Console.WriteLine("Reading the changes");<br />ChangeInformation _ci = new ChangeInformation(sourceClientContext);<br />_ci.StartTime = _startFrom;<br />ChangedItemCollection _cic = sourceTermStore.GetChanges(_ci);<br />sourceClientContext.Load(_cic);<br />sourceClientContext.ExecuteQuery();<br /><br /></pre>
<p>Once we invoke the GetChanges member this will return a ChangeItemCollection that can be used to enumerate the changes that have occurred in term store. You can then perform an operation on the target term store like add, edit or delete like we do in the downloadable code sample.</p>
<pre class="brush: csharp; ruler: true; auto-links: false;">foreach (ChangedItem _changeItem in _cic) {</pre>
<pre class="brush: csharp; ruler: true; auto-links: false;">///ENUMERATE YOU&rsquo;RE CHANGES<br />if (_changeItem.ItemType == ChangedItemType.Group){<br />///PROCESS YOU&rsquo;RE CHANGES<br />}<br />}</pre>
<p>&nbsp;</p>
<h2>Running the code.</h2>
<p>&nbsp;</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/62/31/3678.MMSSAmple.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-62-31/3678.MMSSAmple.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/62/31/3678.MMSSAmple.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-62-31/3678.MMSSAmple.png" alt="" border="0" /></a></p>
<p>&nbsp;</p>
<p>Ensure, that the user has the appropriate permissions to the term store in both the source and target term stores, or you will get an exception.</p>
<p>&nbsp;</p>
<h1 class="brush: csharp; ruler: true; auto-links: false;">FAQ</h1>
<p><em><strong>Question - "Does this work with on-premises SharePoint implementations?"&nbsp;</strong></em></p>
<p>Sure does, in the code sample change&nbsp;SharePointOnlineCredentials to NetworkCredential</p>
<p><em><strong>Question - "I need my Guid in the target Termstore, does this preserve the Guid?"&nbsp;</strong></em></p>
<p>Yes, the code sample will retain you Guid</p>
<p><em><strong>Question - "Why should i use this pattern to sync my terms?"</strong></em></p>
<p>This performs better &nbsp;than just GetAllTerms and enumerating every time you want to sync.&nbsp;<em><strong><br /></strong></em></p>
<p>&nbsp;<em><strong>Question - "Does this work with SharePoint 2010?"</strong></em></p>
<p>In theory yes, however you should only use SharePoint 2010 as the source and not the target. &nbsp;We are unable to set the TermId in CSOM for 2010.</p>
<h1 class="brush: csharp; ruler: true; auto-links: false;">References</h1>
<p>Few useful links and resources related on this blog post</p>
<p><a href="https://1drv.ms/1vkjU5H">Code example from this blog post.</a> - "Core.MMSSync.zip"&nbsp;</p>
<p>&nbsp;</p>
<h4>Special thanks to&nbsp;Kimmo Forss&nbsp;</h4>]]></content:encoded>
    </item>
    <item>
      <title>External Sharing, OneDrive, Team Sites, O'my</title>
      <link>https://docs.microsoft.com/archive/blogs/frank_marasco/external-sharing-onedrive-team-sites-omy</link>
      <pubDate>Tue, 27 May 2014 18:00:00 GMT</pubDate>
      <dc:creator><![CDATA[Frank Marasco MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/frank_marasco/2014/05/27/external-sharing-onedrive-team-sites-omy/</guid>
      <description><![CDATA[I get asked a few times on how to address a scenario where want to enable external sharing for team...]]></description>
      <content:encoded><![CDATA[<p>I get asked a few times on how to address a scenario where want to enable external sharing for team collaboration sites, however we do not want to enable external sharing for our OneDrive for Business Sites.</p>
<p><br />First off, External sharing for OneDrive for Business is an all or nothing approach and is enabled by default.&nbsp;To disable External Sharing you would navigate to your SharePoint Admin Tenant&nbsp;URL, Site Collection view and choose Sharing from the ribbon. You will be presented with a similar view as depicted below where you can adjust your settings.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/62/31/0451.ExternalSharing.PNG" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-62-31/0451.ExternalSharing.PNG"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/62/31/0451.ExternalSharing.PNG" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-62-31/0451.ExternalSharing.PNG" alt="" border="0" /></a></p>
<p>Once you disable sharing users will no longer be able to share outside your organization. Now you can disable Sharing at the tenant level but that will work but we still have that&nbsp;scenario&nbsp;that we want to allow Sharing for our collaboration sites.</p>
<p>Now, I'm a developer at heart and I cant have a blog post without sharing some&nbsp;code. So lets say for your team collaboration sites, you have a custom site provisioning solution like the one at <a href="https://officeams.codeplex.com/">https://officeams.codeplex.com/</a> and you wanted to enable External Sharing in these sites during the provisioning process and you don't want to have to navigate and enable Sharing for each one of your sites&nbsp;You can easy update the solution to include the below code. Now, don't try this one a OneDrive site you will receive an error.</p>
<p><code class="cplusplus"> /// &lt;summary&gt;<br /> /// Sets the Site Collection External Sharing Setting using the SharePoint Tenant API<br /> /// &lt;/summary&gt;<br /> /// &lt;param name="adminCC"&gt;&lt;/param&gt;<br /> /// &lt;param name="siteCollectionURl"&gt;&lt;/param&gt;<br /> /// &lt;param name="shareSettings"&gt;&lt;/param&gt;<br /> public static void SetSiteSharing(ClientContext adminCC, string siteCollectionURl, SharingCapabilities shareSettings)<br /> {<br /> var _tenantAdmin = new Tenant(adminCC);<br /> SiteProperties _siteprops = _tenantAdmin.GetSitePropertiesByUrl(siteCollectionURl, true);<br /> adminCC.Load(_tenantAdmin);<br /> adminCC.Load(_siteprops);<br /> adminCC.ExecuteQuery();<br /> <br /> SharingCapabilities _tenantSharing = _tenantAdmin.SharingCapability;<br /> var _currentShareSettings = _siteprops.SharingCapability;<br /> bool _isUpdatable = false;<br /> <br /> if(_tenantSharing == SharingCapabilities.Disabled)<br /> {<br /> Console.WriteLine("Sharing is currently disabled in your tenant! I am unable to work on it.");<br /> }<br /> else<br /> { <br /> if(shareSettings == SharingCapabilities.Disabled)<br /> { _isUpdatable = true; }<br /> else if(shareSettings == SharingCapabilities.ExternalUserSharingOnly)<br /> {<br /> _isUpdatable = true; <br /> }<br /> else if (shareSettings == SharingCapabilities.ExternalUserAndGuestSharing)<br /> {<br /> if (_tenantSharing == SharingCapabilities.ExternalUserAndGuestSharing)<br /> {<br /> _isUpdatable = true;<br /> }<br /> else<br /> {<br /> Console.WriteLine("ExternalUserAndGuestSharing is currently disabled in your tenant! I am unable to work on it.");<br /> }<br /> }<br /> }<br /> if (_currentShareSettings != shareSettings &amp;&amp; _isUpdatable)<br /> {<br /> _siteprops.SharingCapability = shareSettings;<br /> _siteprops.Update();<br /> adminCC.ExecuteQuery();<br /> Console.WriteLine("Set Sharing on site {0} to {1}.", siteCollectionURl, shareSettings);<br /> }<br /> }</code></p>
<p>You can get the sample solution <a href="https://onedrive.live.com/redir.aspx?cid=8b9effe3e881eca2&amp;resid=8B9EFFE3E881ECA2!20635&amp;parId=8B9EFFE3E881ECA2!136&amp;authkey=!AJyNub6lsHWnJO8">here</a>.</p>
<p>Sharing is caring!</p>]]></content:encoded>
    </item>
    <item>
      <title>So you want to programmatically provision Personal Sites (One Drive for Business) in Office 365?</title>
      <link>https://docs.microsoft.com/archive/blogs/frank_marasco/so-you-want-to-programmatically-provision-personal-sites-one-drive-for-business-in-office-365</link>
      <pubDate>Tue, 25 Mar 2014 15:58:00 GMT</pubDate>
      <dc:creator><![CDATA[Frank Marasco MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/frank_marasco/2014/03/25/so-you-want-to-programmatically-provision-personal-sites-one-drive-for-business-in-office-365/</guid>
      <description><![CDATA[Overview
As part of the new Client Side Object Model (CSOM) assemblies released in the SharePoint...]]></description>
      <content:encoded><![CDATA[<h1>Overview</h1>
<p>As part of the new Client Side Object Model (CSOM) assemblies released in the <a href="https://www.microsoft.com/en-us/download/details.aspx?id=42038">SharePoint Online Client Components SDK</a>, we now have many new capabilities and improvements. One specifically is the capability to programmatically provision One Drive for Business sites in Office 365</p>
<p>There is basically two ways to provision a users One Drive for Business site, the first way is user initiated that is when the user navigates to their newsfeed, site or One Drive links that are within the suite bar, the other is by pre-provisioning leveraging CSOM. In some cases it might not be feasible to have the user initiate the provision process. Say for example, you are migrating from an on-premises SharePoint farm or other repositories, and you don&rsquo;t want to wait for the user to click a link before you start your migration. The AMS team is in the process of publishing this sample at <a href="https://officeams.codeplex.com/">https://officeams.codeplex.com/</a>, but if you need it now and don&rsquo;t want to wait here is a sample to get you going.</p>
<h1>Use Cases</h1>
<ul>
<li>You have an custom on-boarding process and you want to create One Drive Sites when you on-board new employees</li>
<li>You are performing an on-premises migration to Office 365</li>
<li>You are migrating from the G word or other repositories</li>
</ul>
<h1>Dependencies Components<span style="font-size: 12px;">&nbsp;</span></h1>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="623">
<p><strong>SharePoint Online Client Redistributable</strong></p>
</td>
<td valign="top" width="623">
<p><a href="https://www.microsoft.com/en-us/download/details.aspx?id=42038">https://www.microsoft.com/en-us/download/details.aspx?id=42038</a>)</p>
</td>
</tr>
</tbody>
</table>
<h1>References</h1>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="623">
<p><strong>Description </strong></p>
</td>
<td valign="top" width="623">
<p><strong>Version</strong></p>
</td>
</tr>
<tr>
<td valign="top" width="623">
<p><strong>Microsoft.Online.SharePoint.Client.Tenant</strong></p>
</td>
<td valign="top" width="623">
<p>16.0.2617.1200</p>
</td>
</tr>
<tr>
<td valign="top" width="623">
<p><strong>Microsoft.SharePoint.Client</strong></p>
</td>
<td valign="top" width="623">
<p>16.0.2617.1200</p>
</td>
</tr>
<tr>
<td valign="top" width="623">
<p><strong>Microsoft.SharePoint.Client.Runtime</strong></p>
</td>
<td valign="top" width="623">
<p>16.0.2617.1200</p>
</td>
</tr>
<tr>
<td valign="top" width="623">
<p><strong>Microsoft.SharePoint.Client.UserProfiles</strong></p>
</td>
<td valign="top" width="623">
<p>16.0.2617.1200</p>
</td>
</tr>
</tbody>
</table>
<h1>Requirements</h1>
<ul>
<li>You must be a tenant administrator to provision One Drive for Business sites programmatically</li>
<li>Your code must have a context to your tenant admin URL. For example https://microsoftacs-admin.sharepoint.com</li>
</ul>
<h1>Code Samples</h1>
<h1>Rest API Implementation</h1>
<p><code class="csharp"> public void CreatePersonalSiteUsingRest(string tenantAdminUrl , string userName, string password , string emailIDs)<br /> {<br /> //Validate Arguments<br /> Uri _uri = new Uri(tenantAdminUrl);<br /> string _emailData = emailIDs;<br /> if (_creds == null)<br /> {<br /> _creds = new SharePointOnlineCredentials(userName, Utilities.StringToSecure(password));<br /> string authCookie = _creds.GetAuthenticationCookie(_uri);<br /> _cookies = new CookieContainer();<br /> _cookies.Add(new Cookie("FedAuth", authCookie.TrimStart("SPOIDCRL=".ToCharArray()), "", _uri.Authority));<br /> <br /> Uri _apiContextInfo = new Uri(_uri, API_CONTEXTINFO);<br /> <br /> HttpWebRequest _endpointRequest = (HttpWebRequest)HttpWebRequest.Create(_apiContextInfo.OriginalString);<br /> _endpointRequest.Method = "POST";<br /> _endpointRequest.Accept = "text/xml;charset=utf-8";<br /> _endpointRequest.CookieContainer = _cookies;<br /> _endpointRequest.ContentLength = 0;<br /> _endpointRequest.Credentials = _creds;<br /> <br /> //We need to get the form digest since we are submitting or writing data via rest.<br /> //We need to make a call to _api/contextinnfo to retreive the digestvalue and attach the form digest value to all <br /> //requests. https://msdn.microsoft.com/en-us/library/office/jj164022(v=office.15).aspx<br /> <br /> HttpWebResponse _endpointResponse = (HttpWebResponse)_endpointRequest.GetResponse();<br /> XmlNamespaceManager _xmlnspm = new XmlNamespaceManager(new NameTable());<br /> _xmlnspm.AddNamespace("d", "https://schemas.microsoft.com/ado/2007/08/dataservices");<br /> StreamReader _contextinfoReader = new StreamReader(_endpointResponse.GetResponseStream(), System.Text.Encoding.UTF8);<br /> var _formDigestXML = new XmlDocument();<br /> _formDigestXML.LoadXml(_contextinfoReader.ReadToEnd());<br /> var _formDigestNode = _formDigestXML.SelectSingleNode("//d:FormDigestValue", _xmlnspm);<br /> _formDigest = _formDigestNode.InnerXml;<br /> }<br /> <br /> Byte[] _payLoad = System.Text.Encoding.ASCII.GetBytes(_emailData);<br /> HttpWebRequest _spoMySiteRequest = (HttpWebRequest)HttpWebRequest.Create(new Uri(_uri, API_CREATEPERSONALSITE));<br /> _spoMySiteRequest.Method = "POST";<br /> _spoMySiteRequest.ContentType = "application/json;odata=verbose";<br /> _spoMySiteRequest.Accept = "application/json;odata=verbose";<br /> _spoMySiteRequest.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");<br /> _spoMySiteRequest.Headers.Add("X-RequestDigest", _formDigest);<br /> _spoMySiteRequest.CookieContainer = _cookies;<br /> _spoMySiteRequest.ContentLength = _emailData.Length;<br /> _spoMySiteRequest.Credentials = _creds;<br /> <br /> Stream _itemRequestStream = _spoMySiteRequest.GetRequestStream();<br /> _itemRequestStream.Write(_payLoad, 0, _payLoad.Length);<br /> _itemRequestStream.Close();<br /> <br /> HttpWebResponse _mysiteCreateResponse = (HttpWebResponse)_spoMySiteRequest.GetResponse();<br /> <br /> }</code></p>
<p>&nbsp;</p>
<h1>Invocation</h1>
<p><code class="csharp"> PersonalSiteCreator _spoPersonalSiteCreator = new PersonalSiteCreator();<br /> String _adminTenantUrl = "https://youradmin.sharepoint.com";<br /> String _userName = "myAdmin@yourdomain.onmicrosoft.com";<br /> String _passWord = "myAdminPassword";<br /> <br /> //You dont want to provision more than 200 users personal sites during a single request. If you have a large amount of users consider<br /> //waiting for the last users site to be provisioned. The reason behind this is not to bombard the service with requests.<br /> <br /> // One Users<br /> string _restPayload = "{'emailIDs':['user1@MicrosoftACS.onmicrosoft.com']}";<br /> <br /> // Multiple Users<br /> string _restPayload = "{'emailIDs':['user1@MicrosoftACS.onmicrosoft.com', 'user2@MicrosoftACS.onmicrosoft.com']}";<br /> _spoPersonalSiteCreator.CreatePersonalSiteUsingRest(_adminTenantUrl, _userName, _passWord, _restPayload);</code></p>
<p>&nbsp;</p>
<h1>CSOM Implementation</h1>
<p><code class="csharp"> /// &lt;summary&gt;<br /> /// Sample Member that provisions personal sites leveraging CSOM<br /> /// &lt;/summary&gt;<br /> /// &lt;param name="tenantAdminUrl"&gt;The Tenanat Admin URL for your SharePoint Online Subscription&lt;/param&gt;<br /> /// &lt;param name="userName"&gt;The User name, who has tenant admin permssions&lt;/param&gt;<br /> /// &lt;param name="password"&gt;The password for the user who has tenant admin permissions&lt;/param&gt;<br /> /// &lt;param name="emailIDs"&gt;The email ids for users who personal site you want to create&lt;/param&gt;<br /> public void CreatePersonalSiteUsingCSOM(string tenantAdminUrl, string userName, string password, string[] emailIDs)<br /> {<br /> using (ClientContext _context = new ClientContext(tenantAdminUrl))<br /> {<br /> try<br /> {<br /> SharePointOnlineCredentials _creds = new SharePointOnlineCredentials(userName, Utilities.StringToSecure(password));<br /> _context.Credentials = _creds;<br /> _context.ExecuteQuery();<br /> <br /> ProfileLoader _profileLoader = ProfileLoader.GetProfileLoader(_context);<br /> _profileLoader.CreatePersonalSiteEnqueueBulk(emailIDs);<br /> _profileLoader.Context.ExecuteQuery();<br /> }<br /> catch(Exception _ex)<br /> {<br /> throw;<br /> }<br /> }<br /> }</code></p>
<h1>Invocation</h1>
<p><code class="csharp"> PersonalSiteCreator _spoPersonalSiteCreator = new PersonalSiteCreator();<br /> String _adminTenantUrl = "https://youradmin.sharepoint.com";<br /> String _userName = "myAdmin@yourdomain.onmicrosoft.com";<br /> String _passWord = "myAdminPassword";<br /> <br /> //You dont want to provision more than 200 users personal sites during a single request. If you have a large amount of users consider<br /> //waiting for the last users site to be provisioned. The reason behind this is not to bombard the service with requests.<br /> <br /> //Create Personal Sites using CSOM<br /> string[] _usersToCreate = { "User3@MicrosoftACS.onmicrosoft.com" , "User4@MicrosoftACS.onmicrosoft.com"};<br /> _spoPersonalSiteCreator.CreatePersonalSiteUsingCSOM(_adminTenantUrl, _userName, _passWord, _usersToCreate);</code></p>
<p>&nbsp;</p>
<p>During execution of the above code you might notice in the User Profiles a similar view, that the users personal site is displaying a SPSSITEERROR. This is expected during provisioning and will later be updated once the back-end processes have completed.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/62/31/3487.Provision1.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-62-31/3487.Provision1.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/62/31/3487.Provision1.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x500/__key/communityserver-blogs-components-weblogfiles/00-00-01-62-31/3487.Provision1.png" alt="" border="0" /></a></p>
<p>&nbsp;</p>
<p><br /> After this service has provisioned the site you will see a similar screen below which displays the users personal site.</p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/62/31/0576.provision2.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-62-31/0576.provision2.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/62/31/0576.provision2.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x500/__key/communityserver-blogs-components-weblogfiles/00-00-01-62-31/0576.provision2.png" alt="" border="0" /></a></p>
<p>&nbsp;</p>
<h1>FAQ</h1>
<p><em>Q: Do we have an API to provision on-premises One Drive Sites?</em></p>
<p>I&rsquo;m sorry at this time no.<em> </em></p>
<p><em>Q: Should I use the Rest API or CSOM?</em></p>
<p>That depends, use what you are comfortable with however, our opinion is that CSOM is more intuitive in this scenario.&nbsp;</p>
<p><em>Q: Can I batch more than 200 users at a time?</em></p>
<p>No, there is limit of 200 users per request.</p>
<p><em>Q: I have more than 200 users and I will need to submit multiple requests can I do that?</em></p>
<p>Sure you can, but you must batch the requests. Before you submit the next batch of users you should validate that the last user in your batch has had their One Drive site created. In the background all requests are queued up to the maximum 200.</p>
<p><em>Q: What happens, if we don&rsquo;t listen to the above guidance?</em></p>
<p>You will receive an exception. We told you so.</p>
<p><em>Q: How long can does it take to provision a One Drive Site?</em></p>
<p>That depends we have seen it take up to 5 minutes per site during the day. During testing we provisioned 4,000 site collections and it took sometime to provision all the users. We are always improving the service.</p>
<p>Q - <em>Can I use this code and material within any project I'm involved?</em></p>
<p>Feel free to use this on your engagements or solutions, but remember we told you no more than 200 users per requests</p>
<p>Q &ndash; <em>Where can we provide feedback or we have other comments?</em></p>
<p><a href="https://www.facebook.com/officeams">https://www.facebook.com/officeams</a> or <a href="https://officeams.codeplex.com/">https://officeams.codeplex.com/</a></p>
<p>Q &ndash; <em>These code snippets are nice, but can you point me to the Visual Studio Solution?</em></p>
<p><strong>EDITED</strong>: The solution has been released and is&nbsp;available at <a href="https://officeams.codeplex.com/">https://officeams.codeplex.com/</a></p>
<p>Q &ndash; <em>Do you have a PowerShell example?</em></p>
<p>Glad you asked. Here you go.</p>
<p>Open up SharePoint PowerShell console and run the following commands:</p>
<p><code class="cplusplus"> $webUrl = "https://yoursharepointadmin.SharePoint.com"<br /> $ctx = New-Object Microsoft.SharePoint.Client.ClientContext($webUrl)<br /> <br /> $web = $ctx.Web<br /> $username = "admin@myadmin.sharepoint.com"<br /> $password = read-host -AsSecureString<br /> <br /> $ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username,$password )<br /> <br /> $ctx.Load($web)<br /> $ctx.ExecuteQuery()<br /> <br /> [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.UserProfiles")<br /> <br /> $loader =[Microsoft.SharePoint.Client.UserProfiles.ProfileLoader]::GetProfileLoader($ctx)<br /> <br /> #To Get Profile<br /> $profile = $loader.GetUserProfile()<br /> $ctx.Load($profile)<br /> $ctx.ExecuteQuery()<br /> $profile</code> <code class="cplusplus"><br /> #To enqueue Profile<br /> $loader.CreatePersonalSiteEnqueueBulk(@("user1@domain.com")) <br /> $loader.Context.ExecuteQuery()</code></p>
<p>Q &ndash; W<em>hat if I submit a user who already has a One Drive Site?</em></p>
<p>We will process the request within the service and we will handle gracefully, however you should validate the user doesn&rsquo;t have a site already to reduce the burden. See the AMS samples on how to determine if a user has One Drive site.</p>
<p>Q &ndash;&nbsp; <em>&nbsp;I want to add additional users or apply custom branding to the One Drive site how can I do that using this API?</em></p>
<p>The CreatePersonalSiteEnqueueBulk method only provisions the Site Collection, you can use the AMS sample Deploy branding to personal SkyDrive Pro sites automatically for possible options.</p>
<h1>Special Thanks to</h1>
<p>Kimmo Forss, Bob Fox, Vesa Juvonen, Rob Howard,&nbsp;Kirk Daues, Steve Walker,&nbsp;Ali Mazaheri, Bert Jansen, Jeremy Mazner, Phil Newman,&nbsp;Luca Bandinelli, Neil Hodgkinson, Bill Baer,&nbsp;Microsoft&nbsp;America Cloud Services &amp; Office 365 CoE&nbsp;</p>
<p>&nbsp;&nbsp;</p>]]></content:encoded>
    </item>
  </channel>
</rss>