System.MissingMethodException: Method not found Connect-PnPOnline

Using Visual Studio Code and SharePoint PNP I was trying to make some updates to a list but I wasn’t able to connect to a site.

Connect-PnPOnline -Url "https://taco.sharepoint.com/" -Credentials $creds

Error I was receiving:
System.MissingMethodException: Method not found: ‘System.Runtime.Remoting.ObjectHandle System.Activator.CreateInstance(System.String, System.String)’. at SharePointPnP.PowerShell.Commands.Base.ConnectOnline.ProcessRecord() at System.Management.Automation.CommandProcessor.ProcessRecord()

I tried uninstalling VScode, removed all traces of SharePoint from my laptop, and cleared the GAC. Nothing worked.

Here is what did work:
In VScode:

  1. Open the Command Palette on Windows or Linux with Ctrl+Shift+P. On macOS, use Cmd+Shift+P.
  2. Search for Session.
  3. Click on PowerShell: Show Session Menu.
  4. Choose one of the ___ (x86) options

Not sure how, but I was using an x64 session and SharePoint PNP clearly didn’t like that.

Edit: Updated VScode to the latest version and it managed to reset my session settings. When this happened, it caused my CSOM scripts to report a The remote server returned an error: (400) Bad Request error. The fix above will resolve the error.

How to use an iFrame in a modern SharePoint Online page

Using the Embed web part I was trying to paste in a site URL when I should have been using the iFrame HTML tag.

example:

<iframe src="https://sharepointed.com" height="200" width="300"></iframe>

If you encounter this error: This website doesn’t support embedding using just the address ….

You will need to update the HTML Field Security settings in the Site Settings area of your site. In my case, I simply added sharepointed.com to the allow iframes from this domain list, then updated the web part again.

Connect to SharePoint Online Using PowerShell

Update and a much better way to approach this:
Use a SharePoint App Only Client Id and Secret to access the site, list, or library.

Microsoft documentation:
https://docs.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azureacs
You can create an app principle that is limited to a single site, list, library, or a combination of them:
https://piyushksingh.com/2018/12/26/register-app-in-sharepoint/

 $sampleConnect = Connect-PnPOnline -Url "https://YOURsite.sharepoint.com/sites/parent/child" -AppId "12345-94c3-4149-bda5-abcedffadsf" -AppSecret "643r4er5sfdadsfadsfdsf=" -ReturnConnection

Write-Host  $sampleConnect.Url
In this example, I’m connecting to a Site Collection on my tenant.

Assumptions:
1) You have created a token in your o365 site
1.1) https://portal.office.com/account/
1.2) On the left site of the page click Security & privacy, then click Create and manage app passwords
1.3) In the app password page click the create button and give it a name.
1.4) Save the password to a secure location.
1.5) There is a better way of doing this that I will cover in a future post.
2) You have downloaded to CSOM DLL(s) from Nuget

Clear-Host

$userName = "me@sharepointed.com"
$pw = "abc123taco"  # I"M USING AN APP PASSWORD 
$siteCollectionUrl = "https://sharepointed.sharepoint.com/sites/taco"

#Secure the password
$securePassword = ConvertTo-SecureString $pw -AsPlainText -Force

Add-Type -Path "C:\Code\DLL\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Code\DLL\Microsoft.SharePoint.Client.Runtime.dll"

#Create Context
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteCollectionUrl)

#Authorise
$ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($userName, $securePassword)

$web = $ctx.Web
$properties = $web.AllProperties
$ctx.Load($web)
$ctx.Load($properties)
$ctx.ExecuteQuery()

Write-Host " Site Collectione URL: $($web.Url)"
Write-Host "Properties are "

foreach ($prop in $properties) {
    $prop.FieldValues
}

Use Flow to Update SharePoint Hyperlink Field

If you want to update a hyperlink field in SharePoint Online using Flow, you will need to use the Send an HTTP request to SharePoint action (for now). I first tried using the Update item Flow action, but it would not correctly set the URL and Description values. Another issue I had was using the Post method instead of Patch. Every blog/forum post was suggesting the use of the Post method, but the Patch method is what I ended up using.

For testing, create a list titled Contractors. In the list, add a Hyperlink field titled website. In your Flow, add a Send HTTP request to SharePoint action.

Site Address: select the site you created the Contractors list in.
Method: PATCH
Uri: _api/web/lists/getbytitle(‘Contractors’)/Items(‘ID From above Action’)
Headers
Accept application/json
Content-Type application/json; odata=verbose
X-HTTP-TYPE MERGE
IF-MATCH *
Body

{
'__metadata': { 'type': 'SP.Data.ContractorsListItem' },
   'website':
   {
          '__metadata': {'type':'SP.FieldUrlValue'},
               'Description':'Title from above Action',
                'Url': 'http://www.bbb.com'
  }
}

Peek code:

{
    "inputs": {
        "host": {
            "connection": {
                "name": "@parameters('$connections')['shared_sharepointonline']['connectionId']"
            }
        },
        "method": "post",
        "body": {
            "method": "PATCH",
            "uri": "_api/web/lists/getbytitle('Contractors')/Items('@{triggerBody()?['ID']}')",
            "headers": {
                "Accept": "application/json",
                "Content-Type": "application/json; odata=verbose",
                "X-HTTP-TYPE": "MERGE",
                "IF-MATCH": "*"
            },
            "body": "{\n'__metadata': { 'type': 'SP.Data.ContractorsListItem' },\n   'website':\n   {\n          '__metadata': {'type':'SP.FieldUrlValue'},\n               'Description':'@{triggerBody()?['Title']}',\n                'Url': 'http://www.bbb.com'\n  }\n}"
        },
        "path": "/datasets/@{encodeURIComponent(encodeURIComponent('https://EnterYourURL.sharepoint.com/sites/spdev'))}/httprequest",
        "authentication": "@parameters('$authentication')"
    },
    "metadata": {
        "flowSystemMetadata": {
            "swaggerOperationId": "HttpRequest"
        }
    }
}

Get 5 Most Recently Update Items From ALL Lists in a FARM

A wise man recently asked me how I’d go about retrieving the five most recent modified items from every list in a farm. Fun question, and there are a couple of ways of going about this, but here is what I came up with.

Things to note: 1) on prem farm with more than one web app. 2) if you are dealing with a large farm, I’d suggest chunking out the Get-SPSite -limit all and the $site.allwebs into to smaller data sets. 3) script needs comments and error handling or reporting

Clear-Host

if ($null -eq (Get-PSSnapin -Name Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue)) {
    Add-PsSnapin Microsoft.SharePoint.PowerShell
}

$sites = Get-SPWebApplication | Get-SPSite -limit all 

foreach ($site in $sites) {
    foreach ($web in $site.AllWebs) {
        $lists = $web.Lists
        foreach ($list in $lists) {
            $query = New-Object Microsoft.SharePoint.SPQuery 
            $query.Query =  
            "<Query>
                <OrderBy>
                  <FieldRef Name='Modified' Ascending='False' />
                </OrderBy>
            </Query>" 
            $query.RowLimit = 5
            $listItems = $list.GetItems($query)
            
            if ($listItems) {
                foreach ($lIem in $listItems) {
                    Write-Host "Web: $web -- List: $list -- Item: $($lIem.Name) "
                }
            }
        }
        if ($web) {
            $web.Dispose()
        }
    }
}

Using SharePoint Keyword Query to Search Across Site Collections

Quick and easy way to search for an item across site collections. I would suggest using one of the Keyword query tools to fine-tune your queries. Also note that SharePoint will limit your search results to 10,000 items, but you can page your results and cycle through them. In the example below, I’m searching across all the site collections off of the /sites/ managed path. With the returned dataset, I’m looping through the rows getting the SPFile of each row.

$site = New-Object Microsoft.SharePoint.SPSite "https://example.site.com"

$keywordQuery = New-Object Microsoft.office.Server.Search.Query.KeywordQuery $site

$queryText = "SomeField:Taco AND Path:https://example.site.com/sites/*"
$keywordQuery.QueryText = $queryText
$keywordQuery.TrimDuplicates = $false
$searchExec = New-Object Microsoft.Office.Server.Search.Query.SearchExecutor
$searchResults = $searchExec.ExecuteQuery($keywordQuery)

$dTable = $searchResults.Table[000].Table.Rows

foreach($row in $searchResults.Table[000].Table.Rows)
{
      $web = Get-SPWeb $row.SPWebUrl
      $file = $web.GetFile($row.Path)
      Write-Host $file.ServerRelativeUrl
}

Use Selenium to Upload a File to SharePoint

I created a Visual Studio console app.
Added the Selenium WebDriver and the Selenium Chome WebDriver Nuget packages

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;

namespace SeleniumUpload
{
    class Program
    {
        static void Main(string[] args)
        {
            //set span to 20 seconds
            System.TimeSpan span = new System.TimeSpan(0, 0, 0, 20, 0);
            
            //load chrome and the sharepoint library you want to upload to
            IWebDriver driver = new ChromeDriver();           
            driver.Url = "https://yoursite/yourlibrary/Forms/AllItems.aspx";

            //click the upload button
            driver.FindElement(By.Id("QCB1_Button2")).Click();

            //switch the driver focus to the file upload modal
            driver.SwitchTo().Frame(driver.FindElement(By.ClassName("ms-dlgFrame")));

            //allow some time for the modal to load 
            WebDriverWait wait = new WebDriverWait(driver, span);

            //get the upload field from the sharepoint modal
            IWebElement uploadElement = driver.FindElement(By.Id("ctl00_PlaceHolderMain_ctl02_ctl04_InputFile"));

            //use sendkeys to insert the path to the file you want to upload
            uploadElement.SendKeys(@"C:\taco.txt");

            //click the ok button on the modal
            driver.FindElement(By.Id("ctl00_PlaceHolderMain_ctl01_RptControls_btnOK")).Click();            
        }
    }
}

PowerShell and CSOM Bulk Delete List Items

From my desktop, I wanted to delete all the items in a list. The list had 10,000+ items and loop through the items one at a time is slow and not efficient. This is where the batch process comes in and will help to quickly delete all the items in a large list.

In this example, I commented-out the SharePoint Online Credentials because I’m working with an on-prem environment.

The batch size is set to 1,000

#Load SharePoint CSOM Assemblies (you will need to grab these from a SharePoint server)
Add-Type -Path "C:\SharePoint DLL\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\SharePoint DLL\Microsoft.SharePoint.Client.Runtime.dll"

$SiteURL = "https://mylocalsite/sites/taco/"
$ListName = "GiantList"
$BatchSize = "1000"

Try {
    #$Cred= Get-Credential
    #$Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Cred.Username, $Cred.Password)

    #Setup the context
    $Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
    #$Ctx.Credentials = $Credentials

    #Get the List
    $List = $Ctx.Web.Lists.GetByTitle($ListName)
    $Ctx.Load($List)
    $Ctx.ExecuteQuery()

    #Define Query to get List Items in the batch
    $Query = New-Object Microsoft.SharePoint.Client.CamlQuery
    $Query.ViewXml = @"

$BatchSize

"@

    #Get List Items in Batch
    Do {
        $ListItems = $List.GetItems($Query)
        $Ctx.Load($ListItems)
        $Ctx.ExecuteQuery()

        if ($ListItems.Count -gt 0) {
            for ($i = $ListItems.Count - 1; $i -ge 0; $i--) {
                $ListItems[$i].DeleteObject()
            }
            $clientContext.ExecuteQuery()
        }
        else {
            Write-Host "." -foregroundcolor black -backgroundcolor yellow
            $continue = $false;
        }

        $Query.ListItemCollectionPosition = $ListItems.ListItemCollectionPosition
    }
    While ($Query.ListItemCollectionPosition -ne $null)

}
Catch {
    write-host -f Red "Error Adding Items to List!" $_.Exception.Message
}

Limiting the number of rows returned in each query will help to avoid this error:

Exception calling "ExecuteQuery" with "0" argument(s): "The attempted operation is prohibited because it exceeds the list view threshold enforced by the administrator."

Some of the scripts were borrowed from:
http://www.sharepointdiary.com/2016/12/sharepoint-online-get-all-items-from-large-lists-powershell-csom.html

Note:
I plan to revisit this and batch processing where you combine all the delete statements into a large XML string and pass it to SharePoint for processing.

Copy or Move Folder Structure in SharePoint Using CSOM

This post is intended for SharePoint Online and 2016+ using CSOM.

In previous version of SharePoint, moving a folder structure to a different library or a different library in another site collection required a good amount of code and effort. With the introduction of Microsoft.SharePoint.Client.MoveCopyUtil we now have the ability to use methods like MoveFolder to perform a cut and paste of a folder structure. There are other methods that are worth exploring like FolderCopy that is a copy and paste of a structure.

More methods can be found here: MoveCopyUtil members

Name Description
Public method Static member CopyFile
Public method Static member CopyFileByPath
Public method Static member CopyFolder
Public method Static member CopyFolderByPath
Public method Static member MoveFile
Public method Static member MoveFileByPath
Public method Static member MoveFolder
Public method Static member MoveFolderByPath

Example of moving a folder between site collections.


string dstUrl = "https://sharepoint.com/sites/B/";

using (ClientContext srcContext = new ClientContext(dstUrl))
{
  string sourceFolder = "https://sharepoint.com/sites/A/libraryname/foldername";
  string destFolder = dstUrl + "libraryname/foldername";

  MoveCopyUtil.MoveFolder(srcContext, sourceFolder, destFolder);
  srcContext.ExecuteQuery();
}

Again, this is using CSOM with the latest version of the NuGet package (16.0.4351.1000).

Move OneNote Notebooks to New SharePoint Library or Server

Problem:
My company recently completed a SharePoint 2010 to 2016 migration. With the migration came the use of HTTPS security, so my OneNote notebooks stored in SharePoint would no longer sync. All of my notebooks displayed an error of Not syncing.

Here is how I fixed the issue:
First, make sure all of your notebooks are in SharePoint.
In OneNote, click on the File tab.
Locate the first notebook you want to update.
Next to the notebook name click Settings, then Properties.
In the Properties window click on Change Location…
Copy the URL of your SharePoint document library.
Paste the URL into the OneNote Chose a sync location… window.
Select the folder you want to sync OneNote with.
Click OK, a message box should appear saying the item is syncing.
Give it a minute and you should be set.