Unable to change Content Type of document in Library

I was trying to remove a content type from a document library which was associated with multiple content types. As a first action, I identified all documents tagged with the content type to be removed and tried to update them with another content type. However some documents were still showing the old content type despite saving it with the new content type by updating the [Content Type] field.

contenttypecolumn

I tried removing the document and adding the document back to the document library, unfortunately it was still referencing the old content type.

The solution that worked for me was to open the document in desktop office and open the “Advanced Properties”.

advancedproperties

Find and select the property ContentTypeId and click on Delete.

contenttypeiddelete

After deleting the ContentTypeId property I was able to update the content type property of the document and eventually remove the old content type from the document library.

Update Search Result Web Part: Query Text and Result Source

I had to automate update of result source and query of a search result web part in a search page as part of deployment script to move changes from DEV to TEST, UAT and PROD.

The XML definition of a search result web part is

<webParts>
<webPart xmlns=”http://schemas.microsoft.com/WebPart/v3″&gt;
<metaData>
<type name=”Microsoft.Office.Server.Search.WebControls.ResultScriptWebPart, Microsoft.Office.Server.Search, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c” />
<importErrorMessage>Cannot import this Web Part.</importErrorMessage>
</metaData>
<data>
<properties>
<property name=”MaxPagesBeforeCurrent” type=”int”>4</property>
<property name=”ShowBestBets” type=”bool”>True</property>
<property name=”Height” type=”string” />
<property name=”AdvancedSearchPageAddress” type=”string”>advanced.aspx</property>
<property name=”AllowZoneChange” type=”bool”>True</property>
<property name=”UseSharedDataProvider” type=”bool”>False</property>
<property name=”ShowPreferencesLink” type=”bool”>True</property>
<property name=”EmptyMessage” type=”string” />
<property name=”ScrollToTopOnRedraw” type=”bool”>True</property>
<property name=”ShowDefinitions” type=”bool”>True</property>
<property name=”SelectedPropertiesJson” type=”string”>null</property>
<property name=”ShowViewDuplicates” type=”bool”>False</property>
<property name=”CatalogIconImageUrl” type=”string” />
<property name=”RepositionLanguageDropDown” type=”bool”>True</property>
<property name=”PreloadedItemTemplateIdsJson” type=”string”>[“~sitecollection/_catalogs/masterpage/Display Templates/Search/Group_Default.js”,”~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Default.js”,”~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Site.js”,”~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Word.js”,”~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_PowerPoint.js”,”~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_Person_CompactHorizontal.js”,”~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_BestBet.js”,”~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_WebPage.js”]</property>
<property name=”ResultsPerPage” type=”int”>10</property>
<property name=”EmitStyleReference” type=”bool”>True</property>
<property name=”ShowPaging” type=”bool”>True</property>
<property name=”ResultTypeId” type=”string” />
<property name=”ShowUpScopeMessage” type=”bool”>False</property>
<property name=”AllowMinimize” type=”bool”>True</property>
<property name=”AllowClose” type=”bool”>True</property>
<property name=”HelpMode” type=”helpmode”>Modeless</property>
<property name=”Title” type=”string”>Search Results</property>
<property name=”ShowResults” type=”bool”>True</property>
<property name=”MaxPagesAfterCurrent” type=”int”>1</property>
<property name=”ShowResultCount” type=”bool”>True</property>
<property name=”Hidden” type=”bool”>False</property>
<property name=”ItemTemplateId” type=”string” />
<property name=”ItemBodyTemplateId” type=”string”>~sitecollection/_catalogs/masterpage/Display Templates/Search/Item_CommonItem_Body.js</property>
<property name=”UseSimplifiedQueryBuilder” type=”bool”>False</property>
<property name=”HitHighlightedPropertiesJson” type=”string”>[“Title”,”Path”,”Author”,”SectionNames”,”SiteDescription”]</property>
<property name=”AvailableSortsJson” type=”string”>[{“name”:”Relevance”,”sorts”:[]},{“name”:”Date(Newest)”,”sorts”:[{“p”:”Write”,”d”:1}]},{“name”:”Date(Oldest)”,”sorts”:[{“p”:”Write”,”d”:0}]},{“name”:”Lifetime Views”,”sorts”:[{“p”:”ViewsLifeTime”,”d”:1}]},{“name”:”Recent Views”,”sorts”:[{“p”:”ViewsRecent”,”d”:1}]}]</property>
<property name=”DataProviderJSON” type=”string”>{“QueryGroupName”:”Default”,”QueryPropertiesTemplateUrl”:”sitesearch://webroot”,”IgnoreQueryPropertiesTemplateUrl”:false,”SourceID”:null,”SourceName”:null,”SourceLevel”:null,”CollapseSpecification”:””,”QueryTemplate”:”{searchboxquery}”,”FallbackSort”:null,”FallbackSortJson”:”null”,”RankRules”:null,”RankRulesJson”:”null”,”AsynchronousResultRetrieval”:false,”SendContentBeforeQuery”:true,”BatchClientQuery”:true,”FallbackLanguage”:-1,”FallbackRankingModelID”:””,”EnableStemming”:true,”EnablePhonetic”:false,”EnableNicknames”:false,”EnableInterleaving”:true,”EnableQueryRules”:true,”EnableOrderingHitHighlightedProperty”:false,”HitHighlightedMultivaluePropertyLimit”:-1,”IgnoreContextualScope”:false,”ScopeResultsToCurrentSite”:false,”TrimDuplicates”:true,”Properties”:{},”PropertiesJson”:”{}”,”ClientType”:”AllResultsQuery”,”UpdateAjaxNavigate”:true,”SummaryLength”:180,”DesiredSnippetLength”:90,”PersonalizedQuery”:false,”FallbackRefinementFilters”:null,”IgnoreStaleServerQuery”:true,”RenderTemplateId”:””,”AlternateErrorMessage”:null,”Title”:””}</property>
<property name=”ChromeState” type=”chromestate”>Normal</property>
<property name=”AllowHide” type=”bool”>True</property>
<property name=”AllowEdit” type=”bool”>True</property>
<property name=”StatesJson” type=”string”>{}</property>
<property name=”RenderTemplateId” type=”string” />
<property name=”ShowPersonalFavorites” type=”bool”>True</property>
<property name=”ShowSortOptions” type=”bool”>False</property>
<property name=”ChromeType” type=”chrometype”>None</property>
<property name=”AllowConnect” type=”bool”>True</property>
<property name=”HelpUrl” type=”string” />
<property name=”ShowLanguageOptions” type=”bool”>True</property>
<property name=”Description” type=”string”>Displays the search results and the properties associated with them.</property>
<property name=”ServerIncludeScriptsJson” type=”string”>null</property>
<property name=”TitleUrl” type=”string” />
<property name=”AlternateErrorMessage” type=”string” null=”true” />
<property name=”ShowAlertMe” type=”bool”>True</property>
<property name=”ShowDidYouMean” type=”bool”>True</property>
<property name=”QueryGroupName” type=”string”>Default</property>
<property name=”Width” type=”string” />
<property name=”ShowAdvancedLink” type=”bool”>True</property>
<property name=”ExportMode” type=”exportmode”>All</property>
<property name=”Direction” type=”direction”>NotSet</property>
<property name=”BypassResultTypes” type=”bool”>False</property>
<property name=”GroupTemplateId” type=”string” />
<property name=”TitleIconImageUrl” type=”string” />
<property name=”MissingAssembly” type=”string”>Cannot import this Web Part.</property>
</properties>
</data>
</webPart>
</webParts>

 

The webpart property DataProviderJSON needs to be updated. The property is  in Json format which is converted to JSON using PowerShell ConvertFrom-Json method. The PowerShell ConvertTo-Json method is used to convert back to JSON before updating the “DataProviderJSON ” property.

The properties SourceId and SourceName refer to the result source.

On SharePoint On Premises environment , the SourceId can be found from navigating to central administration>Manage Service Applications>Search Service application>Result Sources.editresultsource

The format of the URL is http:///_admin/search/EditResultSourceSSA.aspx?level=service&appid={c21b4115-4e78-4d93-a4e7-24a9472c9b19}&sourceid=886883b4%2Db44c%2D4870%2Da0ff%2D1c0f81b2d9c8

In SharePoint Online, navigate to SharePoint Admin>Search>Manage Result sources and click on the result source you wish to find Id. The URL format is

https://-admin.sharepoint.com/_layouts/15/searchadmin/EditResultSource.aspx?level=tenant&sourceid=203fba36-2763-4060-9931-911ac8c0583b&view=1

Extract and decode the sourceid parameter from the url.

To update the query text, the QueryTemplate  needs to be changed. In the example below it is set to value “contentclass:STS_Web contentclass:STS_Site -SPSiteURL:personal {searchboxquery}” which means returns all sites or web based on the search criteria.

Referring to my post “Add Refiners to Refinement web part“, I had to write a script in PowerShell using CSOM SharePoint assemblies to update the result web part property DataProviderJSON.

searchresultsquerytextselectaquery

Use the following script to call function UpdateSearchResultsDataProviderWebPart .

$ctx=New-Object Microsoft.SharePoint.Client.ClientContext($SiteCollectionUrl);
$ctx.Credentials = New-Object System.Net.NetworkCredential($Username, $password);
$rootWeb = $ctx.Web
$ctx.Load($rootWeb)
$ctx.ExecuteQuery();
UpdateSearchResultsDataProviderWebPart -ctx $ctx -pageUrl "/Search/Pages/SiteDirectory.aspx"

Create Indexed(Queryable via Search) Web PropertyBag using PnP and PowerShell

PropertyBag set against SharePoint web object can be indexed which means a corresponding crawled property is created by Search. If search managed properties are created against the crawled property (property bag values), custom search display templates can be created to provide a site directory solution.

The post by Phil Harding Set a PropertyBag Property as Indexed (Queryable via Search) using CSOM + Powershell   describes how to create an indexed property on premises using the collection “IndexedPropertyKeys”.

$web.IndexedPropertyKeys.Add("MyPropertyName")

However with CSOM, the collection “IndexedPropertyKeys” is not present. Indexed property can be added to another property instead “vti_indexedpropertykeys”.  Phil provided the following code how to add an indexed property.

Fortunately with SharePoint PnP, the indexed property can be added using one line of code.

Set-SPOPropertyBagValue -Key "EDRMS_Usage" -Value "Collaboration" -Indexed

 

Apply Theme and Create Composed Look CSOM and PowerShell

There are several ways a theme can be applied to a SharePoint site.

I tried the PnP command  SetSPOTheme  as follows on SharePoint 2013 On Premises instance.

Set-SPOTheme -ColorPaletteUrl $PathThemeColor -FontSchemeUrl  $PathThemeFont

Unfortunately I was getting message ‘Field or property “AlternateCssUrl” does not exist.’ SetSPOThemeError_AlternateCssUrlDoesNotExist

The error is due not having latest patch installed. Unfortunately the latest SharePoint CU can’t be installed as we have heavily customised legacy applications migrated from SharePoint 2007 and SharePoint 2010 running in compatibility mode.

The other option was to use the add-in “DeployCustomTheme” from PnP samples. However it meant deploying the add-in before applying the theme and add-in catalog has not been enabled on the SharePoint farm.

Using PnP PowerShell Commands the custom theme and master page files can be uploaded to folder “/_catalogs/theme/15” and “/_catalogs/masterpage” respectively.

Deploy Custom Color

Add-SPOFile -Path ".\Files\Themes\Palette_EDRMS.spcolor" -Folder "/_catalogs/theme/15"

Deploy Custom Font

Add-SPOFile -Path ".\Files\Themes\fontscheme_EDRMS.spfont" -Folder "/_catalogs/theme/15"

Deploy Custom Master Page html and preview

Add-SPOFile -Path ".\Files\Themes\MasterPage\seattle_edrms.html" -Folder "/_catalogs/masterpage"


Add-SPOFile -Path ".\Files\Themes\MasterPage\seattle_edrms.preview" -Folder "/_catalogs/masterpage"

After deploying the files, the custom theme can be applied using the PowerShell script below. The custom composed look is created before the theme is applied using the CSOM method “ApplyTheme”

$web.ApplyTheme($themeUrl, $FontSchemeUrl,$BckImageUrl,$true)

After running script, the theme on the site is updated.

AfterRunningCSOMScript.PNG

Nintex SharePoint Online Create SharePoint Group with Contribute permissions

Nintex Workflow has actions “Web Request” and “Call HTTP Web Service” which can be used to make calls to the REST API of SharePoint Online/2013 to automate creation of SharePoint groups in a site collection.

Add the following steps to the workflow

  1. Create a Text variable GroupName and Use “Set Workflow Variable” to assign it to a value.GroupNameVariable
  2. Get request digest from targeted site collection. Please note that workflow app permissions need to amended to give site collection full control access.        App Step to get request digest
    • Add “App Step” action
    • Add “Web Request” action. Fill in the following fields
      • URL: <siteURL> /_api/ContextInfo
      • Method : POST
      • Content type : application/x-www-form-urlencoded
      • Header Name (Key) : Accept     Header value: application/json;odata=verbose

        WebRequestToGetRequestDigest

      • Body : Choose “Content” option  and enter “{}” in text field
      • UserName: Credentials who has access to targeted site
      • Password: password of above UserName
      • Store response content in : Create a variable “digest token”WebRequestToGetRequestDigest_2
    • Add “Query XML” action. Fill in the following fields
      • XML source: Choose “Content” and pick variable “digest token”
      • XPath query: /d:GetContextWebInformation/d:FormDigestValue
      • Return result as :Text
      • Query result in : create a text variable strDigestInfoQueryXMLRequestDigest
    • Add “Log to History” action. Print the strDigestInfo to make sure valid request digest are used  QueryXMLRequestDigest
  3. Create SharePoint Group using REST API ‍/_api/web/sitegroups  AppStepToCreateSpGroup
    • Add “App Step” action. Optionally add “Log to History List” action to log status of workflow.
    • Add “Build Dictionary” action to build “Request Headers”.
      •  Add the following key value pair as Text- content-type:  application/json; odata=verbose
        – X-RequestDigest : {Variable:strDigestInfo}
      • Save Output into RequestHeadersRequestHeaders
    • Add “Build Dictionary” action to build “Metadata”
      •  Add the following key/value pair- key: type    Type:Text  Value: SP.Group
        BuildMetadataForGroup
    • Add “Build Dictionary” action to build “Parameters”
      •  Add the following key/value pair- Key: __metadata   Type:Dictionary   Value: Workflow Variable GroupMetadata
        – Key: Description  Type: Text  Value: Group created automatically from Nintex workflow
        – Key: Title  Type:Text  Value:{Variable:GroupName}
      • Assign Output to a dictionary variable GroupParameters
        BuildDictionaryGroupParameters
    • Add “HTTP Web Service”action to create group
      • Fill in Address : <SiteURL>/_api/web/sitegroups
      • Set Request Type to “HTTP Post”
      • Set Request Headers to dictionary variable RequestHeaders
      • Set Request Content to dictionary variable GroupParameters
      • Set Response Headers to new dictionary variable GroupResponseHeaders
      • Set Response Content to new dictionary variable GroupResponseContent
      • Set Response Status Code to new text variable GroupResponseStatusCodeWebRequestToCreateGroup
    • Log to History list the responseLogResponseOfGroupCreated
  4. Retrieve SharePoint Group using REST API ‍ ‍/_api/web/sitegroups/getbyname AppStepToGetGroupCreated
    • Add “Add Step” action
    • Optionally add “Log History List” action to log workflow status.
    • Add a “Web Request” actionWebRequestToGetGroupCreated
      • Set URL to <SiteURL>/_api/web/sitegroups/getbyname(‘‍{Variable:GroupName}‍’)?$select=Id
      • Choose  Get for Method
      • Click Add Header
      • Set Header Key to “Accept”
      • Set Header Value to “application/json;odata=verbose”
      • Set UserName to user who has access to site collection
      • Set Password to password of specified user
      • Set Store response content in a Text variable GroupRequestIDXMLAdd
    • Add “Log History List” action to log response GroupRequestIDXMLAdd from web request. The response is in JSON format. Nintex does not have a JSON parser action to retrieve value of property. Use “Regular Expression” action as described in the next step is used to retrieve Id value
    • Add “Regular Expression” actionRegularExpressionToGetID
      • Set String to ‍{Variable:GroupRequestIDXML}
      • Set String Operation to Extract
      • Set pattern to (?<=”Id”:).*?(?=})
      • Save Output to text variable strGroupIDCol
    • Add “Get Item from Collection” action.GetGroupIDFromCollection
      • Set Target Collection to variable strGroupIDCol
      • Set Index to 0
      • Set Output to SPGroupId
    •  Add “Log History List” action to log strGroupIDCol
  5. Add Contribute Role to new Group
    • Add App Step action  AppStepToAddContributeRoleToGroup
    • Add “Call HTTP Web Service” Action WebRequestToAddContributeRole
      • Set URL to <siteURL>‍/_api/web/roleassignments/addroleassignment(principalid=‍{Variable:SPGroupId}‍,roleDefId=1073741827)
      • Set Request Type to “HTTP Post”
      • Set Request Headers to dictionary “RequestHeaders”
      • Set Response Headers to new dictionary variable RoleAssignResponseHeaders
      • Set Response Content to new dictionary variable RoleAssignmResponseContent
      • Set Response Status Code to new text variable RoleAssignmResponseStatusCode
    • Add “Log to History List” action to log response of web request

Save and Publish the workflow. If the workflow runs succesfully

WorkflowHistory

 

 

SharePoint JSOM to update Document ID Settings

The Document ID Service feature is useful to tag unique Id to documents across site collection to make it easier to retrieve. A prefix can be specified to use when the unique Id is generated.

The document id settings is stored in the web property bag “docid_settings_ui” after the feature the activated.

The web property bag can be updated using JSOM to assign a prefix with XML snippet like below.

var docIdValue = ‘<?xml version=”1.0″ encoding=”utf-16″?><DocIdUiSettings xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance&#8221; xmlns:xsd=”http://www.w3.org/2001/XMLSchema”&gt; <Prefix>’ + docIDPrefix + ‘</Prefix> <AssignmentEnabled>true</AssignmentEnabled></DocIdUiSettings>’

webProperties.set_item(“docid_settings_ui”, docIdValue);

The full script can be copied from below and tested in a content editor web part.

After the script is loaded on a content editor webpart, the [Site Collection URL] and [Doc ID Prefix] needs to be filled before clicking on “Configure Doc ID Settings”.

ConfigureDocIdSettings_UI

If successful, the message will appear.

TagYourSite

Navigate to site settings> Document ID Settings to verify the prefix

DocIdSettingsPrefix