Access denied when attempting to move SharePoint documents


The other day at work, I had to help solve a problem for a client. They were unable to move documents from one folder to another, using the modern move functionality within SharePoint Libraries.

Note: To be able to move a document, you need to have minimum of contribute permission on both the source and destination locations. You need to be able to add a new document to the destination and delete at the source.

The client I was helping was trying to move a document from inside one folder to another folder within the same document library. The client had contribute permission. I had checked their permission at the library, folder and document level, just in case there was broken inheritance. Nothing had broken inheritance. When the client attempted to move the library, they encountered the error message:

Access denied. You do not have permissions to perform this action or access this resource.

This had me stumped for a bit. I was able to move the documents, but I was Site Collection administrator. It was only when I was looking at the views on the list, I noticed a column called Approval Status. This was unusual, as we have a default setup for all the libraries within SharePoint and approval isn’t one of them.

After further investigation, it turned out that Content Approval was turned on within versioning settings of the library.

The client didn’t have the ‘Approve Items – Approve a minor version of a list item or document’ permission in this library. It was agreed that I could turn the Content Approval status off as it shouldn’t been turned on in the first place. Soon as this was turned off, the client was able to move documents from one folder to another successfully. I didn’t test it, but I would assume that if I added the client to a group that had the Approve Items permission, they would have also been able to move the documents with Content Approval status turned on.

Reference: https://mydigitalworkplace.wordpress.com/2018/05/20/10-things-about-copy-and-move-sharepoint-online/

Advertisements

Programmatically change the New Menu in SharePoint Online using PowerShell


Back in October 2018 Microsoft changed the way the New menu on a document library worked. You can now edit what is shown in the New menu directly from the library.

I needed to know how to do this in code, and it looked like PNP automatically does this for you. After doing a get-pnpprovisioningtemplate, when you look at the list you will see a XML element called NewDocumentTemplates, which has a JSON formatted string. The property NewDocumentTemplates exists on the view of the list. This made me think that you could have different menu items for different views, but this doesn’t seem to be the case.

<NewDocumentTemplates>[
{"templateId":"NewFolder","title":"Folder","visible":true},
{"contentTypeId":"0x0101007ADED896313A4943AFE7F07133B1339E","isContentType":true,"templateId":"0x0101007ADED896313A4943AFE7F07133B1339E","title":"Document","visible":false},
{"contentTypeId":"0x0101009348E1CE5767914598D327EAE7EB97D500245281855DF00844BF9BD64ADD983B7D","isContentType":true,"templateId":"0x0101009348E1CE5767914598D327EAE7EB97D500245281855DF00844BF9BD64ADD983B7D","title":"CF New Request","visible":true},
{"templateId":"NewDOC","title":"Word document","visible":true},
{"templateId":"NewXSL","title":"Excel workbook","visible":true},
{"templateId":"NewPPT","title":"PowerPoint presentation","visible":true},
{"templateId":"NewONE","title":"OneNote notebook","visible":true},
{"templateId":"NewXSLForm","title":"Forms for Excel","visible":true},
{"templateId":"Link","title":"Link","visible":true}]
</NewDocumentTemplates>

Now this seems to work, when applying it back to the same list. However, I found that if I set my own content types with visible to false, and then tried on a different list that did have that content type, it didn’t hide it.

So why not?

The reason is because the contentTypeId it is expecting, is the ListContentTypeId, not the actual ContentTypeId. For each list, the ListContentTypeId will be different.

When you add a site content type to a list:

“SharePoint list makes a local copy of the site content type and adds the copy to the content type collection on the list. The new list content type is a child of the site content type. The value of the Id property for the list content type is different from the value of the Id property for its parent site content type, but otherwise the two content types are initially the same.”

https://docs.microsoft.com/en-us/previous-versions/office/developer/sharepoint-2010/ms463016(v%3Doffice.14)#effects-from-adding-a-site-content-type-to-a-list

I’m sure PNP will pick up on this and update the code at some point.

PowerShell script

I decided to write my own PowerShell script to solve this issue. First, I needed to understand the JSON format. For the built in Microsoft menu items the JSON looks as follow:

{
  "title": "Word Document",
  "templateId": "NewDOC",
  "visible": true
}

There are quite a few built in ones, and from using PNP to export out, I was able to find what they were called. (If you find out any more, that I don’t have listed here please let me know.)

  • TemplateId – Name
  • NewFolder – Folder
  • NewDOC – Word Document
  • NewXSL – Excel workbook
  • NewPPT – PowerPoint presentation
  • NewONE – OneNote notebook
  • NewVSDX – Visio drawing
  • NewXSLForm – Forms for Excel
  • Link – Link

Note: If you don’t have a license for Visio, or do not have Forms enabled for your E3 license, then the menu item will not show up anyway.

The JSON format for content types are the following:

{
  "title": "<Content Type Name>",
  "templateId": "<ListContentTypeID>",
  "visible": <true/false>,
  "contentTypeId": "<ListContentTypeID>",
  "isContentType": <true/false>
}

Below is my function script. Set-ListNewMenuItems.ps1 With this script you can hide any content types/default menu items by using the titles.

-Url:’https://tenant.sharepoint.com/sites/demo&#8217; -ListTitle:’Documents’ -ContentTypesToHide:’OneNote notebook’,’PowerPoint’,’Custom CT Name’

You can also hide all default menu items, and let your content types show. It will still show links and folder.

-Url:’https://tenant.sharepoint.com/sites/demo&#8217; -ListTitle:’Documents’ -HideDefault:$true

If you want to hide link and folder too, you would need to include their names in the ContentTypesToHide.

-Url:’https://tenant.sharepoint.com/sites/demo&#8217; -ListTitle:’Documents’ -ContentTypesToHide:’Folder’,’Link’ -HideDefault:$true

After running my script to remove Document, OneNote notebook, Word Document and Forms For Excel

-Url:’https://tenant.sharepoint.com/sites/demo&#8217; -ListTitle:’Documents’ -ContentTypesToHide:’Document’,’OneNote notebook’,’Word Document’,’Forms For Excel’ -HideDefault:$false

As you can see from above, it states that it is Showing Visio drawing. However, on my tenant I do not have a license for it. Below shows you how my New menu has been updated.

The code isn’t perfect, and could do with improvements such as ordering. Please feel free to use it.

SharePoint Online Custom Format View issue with @now and UK Date format


Before you read all of this, I do not have a solution. I currently have an open premier support call with Microsoft regarding this, and when this is fixed, I will update the post.

View formatting is a way to change the look of your Lists/Libraries. Using JSON you apply to the list will change the way it looks. The following link takes you to Microsoft Page about Custom Views. https://docs.microsoft.com/en-us/sharepoint/dev/declarative-customization/view-formatting.

There are two ways to write the JSON, one way is using Abstract Tree Syntax (AST) or you can write in the Excel-style. I will be using the Excel-style.

I have recently started using them for a project I was working on, it was a simple request to change the colour of the row depending on a date column. The rules were:

  • Date equals to today – Leave the row white.
  • If Date is before today – Make the row red.
  • If Date is in the next 7 days – Make the row Orange.
  • If Date is between 7 and 28 days away – Make the row Yellow.
  • More than 28 days – Make the row green.

However, I was noticing that the colours didn’t reflect the correct values. It was only when date changed from the 28th February to the 1st March, the colours changed (still incorrect though) when I realised that the custom formatting didn’t understand UK date formats. Soon as I flipped my Regional Settings for the Locale to be English (United States) the colours displayed correctly.

Example

(Please note I am doing this example on the 8th March 2019)

For this example, I have created a SharePoint List and added an additional Date column. For the purpose of understanding what is going wrong, I have created 2 more text columns called “Date In UK” and “Date in US” where I have put the date values in text for everyone to understand. (e.g A date written like: 03/04/2019, an English person would read this as 3rd April 2019, where an American person would read this as 4th March 2019)

The JSON format I have applied is below, this uses the Excel-Style formatting of using a nested if statement. An excel if statement looks like : =if(logic_test, true, false) When you are doing a nested If statement, the false value, is another if statement. Meaning if 1st logic_test is false, then run the second if statement instead. =if(1st_logic_test, true, if(2nd_logic_test, true, false))

To do the comparison against the current date, you use the @now keyword. If you need to calculate a week date from now you need to add 7 day worth of milliseconds, (7 * 24*60*60*1000 = 604,800,000) @now + 604800000

{
  "schema": "https://developer.microsoft.com/json-schemas/sp/view-formatting.schema.json",
  "additionalRowClass": "=if([$Date] == @now,'',if([$Date] < @now, 'ms-fontColor-neutralSecondary ms-fontColor-redDark ms-fontSize-mPlus ms-fontWeight-bold sp-field-severity--blocked',if([$Date] <= @now + 604800000,'sp-field-severity--severeWarning ms-fontColor-neutralSecondary ms-fontWeight-bold',if([$Date] <= @now + 2419000000,'sp-field-severity--warning','sp-field-severity--good ms-fontColor-neutralSecondary'))))"
}

When this JSON is applied to the list, my list looks like the following:

As you can see from the picture above (if the colours come out right on your screen) the formatting is incorrect.

Item 1 – is Red

Item 2 – is Yellow

Items 3 to 7 are Green.

Remember I’m doing this demo on the 8th March 2019, which should show:

Items 1 to 3 – in Red (Before today)

Items 4 – White (as it’s today’s date)

Item 5 – Orange (In next 7 days)

Item 6 – Yellow (In next 7 to 28 days)

Item 7 – Green (Over 28 days)

So why do I have Item 1 red, and Item 2 yellow, but the rest green. This is because the date is being read in by the formula the wrong way round as an American date. The 4th column “Date in US” is the way the date is being read in.

Item 1 – being read in as the 3rd January, which is before the 8th March – Red

Item 2 – being read in as the 3rd April, which is more than 7 days after the 8th March – Yellow

Items 3 to 5 – being read in as the 3rd July onwards, which is more than 28 days after the 8th March – Green.

Items 6 to 7 – Would have invalid dates in American date format because there is not a 20
or 18 month. It is green because of the last if statement that says If less than 28 days, (true) display yellow, else (false) display green.

Changing the Regional Settings Locale.

I will now go to my Regional Settings and change the Locale. This can be found in site Settings, and under Site Administration select Regional Settings.

Change the Local from English (United Kingdom) to English (United States)

When I go back to my list, the colour formatting looks very different, but correct. (Well almost)

Apart from Item 4 the 8th March 2019 which should be white because it is today’s date, all the colours are now correct according to the JSON formula.

Conclusion

I have tested this same formula on 3 different tenants, and the same happens every time. Therefore, I’m certain it isn’t a problem with my tenant. It doesn’t seem to be just the UK date format either, it affects any date when the format isn’t in the American format of MM/DD/YYYY.

After searching on the Web trying to find a solution, I did come across this github issue https://github.com/SharePoint/sp-dev-list-formatting/issues/92 which has been opened since 5th October 2018. This issue is around calculated columns using dates but reading through the comments it does seem that people are saying they can get it working when using the US date format.

Until Microsoft addresses this bug, using conditional formatting on date columns is pointless, unless you are using the US Date format.

I have yet to try to write the Formula using the Abstract Tree Syntax(AST) way.

Azure function failing with “Object reference not set to an instance of an object”


I have an Azure application that uses an Office 365 App only token. Followed similar instructions to the one here: https://docs.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azuread

I’m using OfficeDevPnP to connect to SharePoint with my Office 365 App only token. Demo code shown below.


string clientId = GetApplicationSetting("clientId");
string tenant = GetApplicationSetting("tenant");
string certificateFilename = GetApplicationSetting("certificateFilename");
string certificatePath = GetCertificatePath(exCtx.FunctionAppDirectory, certificateFilename);

SecureString certificatePassword = ConvertToSecureString(GetApplicationSetting("certificatePassword"));

string tenantAdminUrl = GetApplicationSetting("tenantAdminUrl");
log.Info("Got details");
OfficeDevPnP.Core.AuthenticationManager authManager = new OfficeDevPnP.Core.AuthenticationManager();

try{
   using (var clientContext = authManager.GetAzureADAppOnlyAuthenticatedContext(siteProperties.Url, clientId, tenant, certificatePath, certificatePassword))
   {
     Web web = clientContext.Web;
     clientContext.Load(web, w =&gt; w.Webs, w =&gt; w.Title, w =&gt; w.Url);
     clientContext.ExecuteQueryRetry();
   }
}catch(Exception ex){
log.Info("Failed! " + ex.Message);
}

The Azure function that has been deployed and running daily for a while now, when all of a sudden it stopped working last Wednesday. Just as it was authenticating with SharePoint it displayed the error message “Object reference not set to an instance of an object”

The stack trace is below:

2018-02-06T09:45:28.347 Failed! Object reference not set to an instance of an object.
2018-02-06T09:45:28.364 StackTrace:    at Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.RunAsyncTask[T](Task`1 task)
   at Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.AcquireToken(String resource, ClientAssertionCertificate clientCertificate)
   at OfficeDevPnP.Core.AuthenticationManager.<>c__DisplayClass36_0.<GetAzureADAppOnlyAuthenticatedContext>b__0(Object sender, WebRequestEventArgs args)
   at Microsoft.SharePoint.Client.ClientRuntimeContext.OnExecutingWebRequest(WebRequestEventArgs args)
   at Microsoft.SharePoint.Client.ClientContext.GetFormDigestInfoPrivate()
   at Microsoft.SharePoint.Client.ClientContext.EnsureFormDigest()
   at Microsoft.SharePoint.Client.ClientContext.ExecuteQuery()
   at AppFunctionDemo.Function2.GetTenantSiteProperties(AuthenticationManager authManager, String tenantAdminUrl, String clientId, String tenant, String certificatePath, SecureString certificatePassword, TraceWriter log)
   at AppFunctionDemo.Function2.Run(TimerInfo myTimer, ExecutionContext exCtx, TraceWriter log)

After a call with Microsoft, it turns out that the Azure Functions are slowly being updated throughout the tenants.

Last one was 11 days ago (at time of writing this) https://github.com/Azure/azure-functions-host/releases

If you go to your Azure functions and look in the Application Settings, you will see an application setting saying:


FUNCTIONS_EXTENSION_VERSION: ~1

This means it will keep itself up to the latest date. By changing the version “~1” to a previous version “1.0.11490” after saving the Application Settings, and re-running my Azure function, I no longer get an error message saying “Object Reference not set to an instance of an object” and the function runs to completion.

There is obviously a bigger bug issue here, but at least my Azure functions can continue running until then.

SPFX obtaining the URL Query Parameters


This is a very quick blog post, but I needed to obtain the URL parameters of the page for an SPFX webpart.

There is a built in method for obtaining URL parameters.

import { UrlQueryParameterCollection } from '@microsoft/sp-core-library';

...

var queryParms = new UrlQueryParameterCollection(window.location.href);
var myParm = queryParms.getValue("myParam");

I have found this very useful with Console Logs. I have a method called “logMessageToConsole” passing in the message. If I find a parameter called debug in my query string, then all the console logs are written out.

private logMessageToConsole(message:string)
{
var queryParms=new UrlQueryParameterCollection(window.location.href);
  if(queryParms.getValue("debug")){
    console.log(message);
 }
}

That way I only need to add ?debug=1 to the end of my URL or &debug=1 if there are already querystring parameters, and then I can see all my console messages. This is useful to quickly debug on production environments.

View Trace Logs with PnP Provisioning Templates using PowerShell


When running the Get-PnPProvisioningTemplate and the Apply-PnPProvisioningTemplate it is sometime necessary to see what is going on while the call is processing.

Just run the following command first before calling your Get/Apply -PNPProvisioningTemplate command.

Set-PnPTraceLog -On -Level:Debug

If you need to turn it off again

Set-PnPTraceLog -Off

PnP Provisioning Templates Adding – Everyone except external users


On a recent project of mine, I’ve been working with the PnP Provisioning engine using PowerShell. It’s really the first time I’ve really worked with it, and I must say I’m impressed at how easy it is to use.

If you have never used it before I recommend you checking out the following articles on MSDN.

https://msdn.microsoft.com/en-us/pnp_powershell/pnp-powershell-overview

https://msdn.microsoft.com/en-us/pnp_articles/pnp-provisioning-framework

https://msdn.microsoft.com/en-us/pnp_articles/pnp-remote-provisioning

Basic installation setup

If you have a Windows 10 device or have installed PowerShellGet, to check if you have the latest version installed in your PowerShell environment, run the following below

Get-Module SharePointPnPPowerShell* -ListAvailable | Select-Object Name, Version | Sort-Object Version -Descending

I already have the latest version at the time of writing this 2.17.1708.1.

You can install or Upgrade the SharePointPNPPowerShell with the following commands.

To Install

#SharePoint Online
Install-Module SharePointPnpPowerShellOnline

#SharePoint 2016
Install-Module SharePointPnPPowerShell2016

#SharePoint 2013
Install-Module SharePointPnPPowerShell2013

To Upgrade

Update-Module SharePointPnPPowerShell*

Everyone and Everyone Except External Users

The quickest way to create a PnP Provisioning template ready to use again, is to create your site within SharePoint using point and click.

I’ve created my site with Members set up to “Everyone except external users” and Visitors set up to “Everyone”.

After you have created your site, you use the following commands to connect and export the template.

Connect-PnpOnline -Url:https://mytenant.sharepoint.com/sites/pnpexamples -Credentials: (Get-Credential)
Get-PnPProvisioningTemplate -Out 'pnpExample.xml'

If you open the XML file, and look in the <pnp:Security> section, you will see that Additional Members has c:0-.f|rolemanager|spo-grid-all-users/{GUID} and Additional Visitors has c:0(.s|true. These represent Everyone except external users and Visitors respectively.

If you are only using this template to create more sites in the same tenant that you exported it from, then you are good. The GUID after ‘spo-grid-all-users’ will always be that GUID in your tenant. However, when you want to use this template in other tenants – for example you have a staging and production environment – then this GUID will not work and the importing of the template will not add Everyone except external users to your members group.

What is the GUID after spo-grid-all-users?

It turns out that the GUID relates to the Authentication Realm ID of your tenant, and luckily PnP have a PowerShell command

Get-PnPAuthenticationRealm

How does that help us?

You can pass parameters into the Apply-PnPTemplate. First we need to change the XML inside <pnp:AdditionalMembers>

From:

<pnp:User Name="c:0-.f|rolemanager|spo-grid-all-users/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx" />

To:

<pnp:User Name="c:0-.f|rolemanager|spo-grid-all-users/{parameter:AuthenticationRealm}" />

Save the XML template and now you can run the following commands to apply the template to a new site in a different tenant. (Please note my team site I’m applying this to has already been created)

Connect-PnpOnline -Url:https://myOtherTenant.sharepoint.com/sites/pnpexamples -Credentials: (Get-Credential)
$AuthenticationRealm = Get-PnPAuthenticationRealm
Apply-PnPProvisioningTemplate -Path:'pnpExample.xml' -Parameters:@{"AuthenticationRealm"=$AuthenticationRealm}

After applying the template to another tenant, you will see your “Everyone except external users” inserted correctly.