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.

SharePoint SPFx PNP gulp trust-dev-cert Error

Trying to setup my dev environment to play around with SPFx and ran into this error:

[08:33:19] Error - [trust-cert] Error: root "Trusted Root Certification Authorities"
Signature matches Public Key
CertUtil: -addstore command FAILED: 0x80070005 (WIN32: 5 ERROR_ACCESS_DENIED)
CertUtil: Access is denied.

[08:33:19] Error – [trust-cert] Certificate trust failed with an unknown error.
[08:33:19] Error – ‘trust-cert’ sub task errored after 5.61 s
“Error trusting development certificate.”
[08:33:19] ‘trust-dev-cert’ errored after 5.62 s
[08:33:19]
[08:33:19] ==================[ Finished ]==================
Error – [trust-cert] Error: root “Trusted Root Certification Authorities”
Signature matches Public Key
CertUtil: -addstore command FAILED: 0x80070005 (WIN32: 5 ERROR_ACCESS_DENIED)
CertUtil: Access is denied.

Error – [trust-cert] Certificate trust failed with an unknown error.
Error – ‘trust-cert’ sub task errored after 5.61 s
“Error trusting development certificate.”

The only way I could manage to get around it was to manually create a cert using the OOB Windows tools.

Creating your .cer file

  1. Start by locating makecert.exe. You can typically find it in and C:\Program Files (x86)\Windows Kits\8.1\bin\x64 on Windows Server 2012 R2.
  2. Browse to that location with Command Prompt with Administrative privileges.
  3. You can actually create a certificate to run against your machine name and you can also have it work against localhost. To create a certificate for your machine name, use the following command:

makecert -n “CN=%ComputerName%, CN=localhost” -ss MY -sr LocalMachine -b 08/09/2016 -e 09/09/2028 -a sha256 -sky exchange -r -pe myCert.cer

Return to where you tried to run trust-dev-cert and move to the next step.  We now have a dev cert so there is no use in creating one using gulp.

Thanks to Santi Murtagh for the instructions on setting up the cert.

Creating a Self-Signed SSL Certificate for Developing and Testing Against IIS

The Web application at X could not be found.

Error: The Web application at https://sharepoint.sharepointed.com could not be found. Verify that you have typed the URL correctly. If the URL should be serving existing content, the system administrator may need to add a new request URL mapping to the intended application.

I created a .net console app to update some stuff in SharePoint.  I received the above error when executing the .exe file with a new service account. 

First, I tried granting Shell access to the content db I was working with, but that didn’t solve the problem.

$cDb = Get-SPContentDatabase -site "https://taco.sharepointed.com/" Add-SPShellAdmin -UserName "domain\userAccount -database $cDb

Running the same command without the database switch fixed my problem.

Add-SPShellAdmin -UserName "domain\userAccount"

SharePoint CSOM Upload File Access Denied Error

Using a .Net Web App, a simple process is to create a local file and upload it to SharePoint. I thought the service account I was using had ample permissions to the Site, but it didn’t… For testing, I granted the service account Full Control at the Web App level (user policy), site collection admin, and more. Nothing worked.

Sample of the code I was using:

ClientContext ctxH = new ClientContext(hURL); 
Web siteH = ctxH.Web; 
ctxH.Load(siteH); 
ctxH.ExecuteQuery(); 

List _library = siteH.Lists.GetByTitle("Drop Off Library"); 
Folder _oFolder = siteH.GetFolderByServerRelativeUrl(siteH.ServerRelativeUrl.TrimEnd('/') + "/" + "DropOffLibrary"); 
ctxH.Load(_oFolder); 
ctxH.ExecuteQuery(); 

FileStream fileStream = System.IO.File.OpenRead(fileName); FileCreationInformation fileInfo = new FileCreationInformation(); fileInfo.ContentStream = fileStream; fileInfo.Overwrite = true; fileInfo.Url = destFileName; Microsoft.SharePoint.Client.File _oFile = _oFolder.Files.Add(fileInfo); 

ctxHub.Load(_oFile); 
ctxHub.ExecuteQuery();

Use PowerShell to Execute SharePoint Search Queries

In this example, I’m narrowing my search to one library and a search term.
At a high level, the script searches the FoodSite for the word GoodTaco.

function Query-SPSearch {
    param(
        [Parameter(Mandatory=$true)][String]$WebApplicationPath,
        [Parameter(Mandatory=$true)][String]$KeywordQuery,
        [Parameter()][Int32]$Count = 10
    )
 
    $QueryXml = @"
 
<QueryPacket xmlns="urn:Microsoft.Search.Query" >
    <Query>
        <Context>
            <QueryText type="STRING">$KeywordQuery</QueryText>
        </Context>
        <Range>
            <Count>$Count</Count>
        </Range>    
        <IncludeSpecialTermResults>false</IncludeSpecialTermResults>
        <PreQuerySuggestions>false</PreQuerySuggestions>
        <HighlightQuerySuggestions>false</HighlightQuerySuggestions>
        <IncludeRelevantResults>true</IncludeRelevantResults>
        <IncludeHighConfidenceResults>false</IncludeHighConfidenceResults>
    </Query>
</QueryPacket>
"@
    $ServicePath = "/_vti_bin/search.asmx"
    $SearchWS = New-WebServiceProxy -Uri ($WebApplicationPath + $ServicePath) -UseDefaultCredential
    $Results = $SearchWS.QueryEx( $QueryXml )
    # we excluded all other result sets, but just in case get the one we want:
    $Results.Tables["RelevantResults"]
}
 
Query-SPSearch -WebApplicationPath "https://sharepointed.com/sites/foodsite" -KeywordQuery "GoodTaco AND path:https://sharepointed.com/sites/foodsite/tacos" -Count 20 | Format-Table Title, Author, Path

Create a console app to query userprofileservice.asmx

In Visual Studio, create a console application.
Create a web reference using the following inputs:
URL: https://yourSite.url/_vti_bin/userprofileservice.asmx?WSDL
Name: UPS1

In the program.cs class, replace the Main method with the following:

static void Main(string[] args)
{
UPS1.UserProfileService ups1 = new UPS1.UserProfileService();

ups1.UseDefaultCredentials = true;

UPS1.PropertyData[] info = ups1.GetUserProfileByName(@"domain\username");

for (int i = 0; i < info.Length; i++) { Console.WriteLine(info[i].Name); for (int j = 0; j < info[i].Values.Length; j++) { Console.WriteLine(info[i].Values[j].Value); } } Console.ReadLine(); } 

Run the app.