From 031340c0ce1d44e9acfa8a12940cd190dc52f804 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sun, 16 Jan 2022 15:33:05 +0200 Subject: [PATCH 001/458] Feature #529 - added cmdlet to retrieve list item permissions --- CHANGELOG.md | 1 + documentation/Get-PnPListItemPermission.md | 81 +++++++++++++++++++++ src/Commands/Lists/GetListItemPermission.cs | 52 +++++++++++++ src/Commands/Model/ListItemPermissions.cs | 12 +++ 4 files changed, 146 insertions(+) create mode 100644 documentation/Get-PnPListItemPermission.md create mode 100644 src/Commands/Lists/GetListItemPermission.cs create mode 100644 src/Commands/Model/ListItemPermissions.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index a09aeb71f..7f36375f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Added - Added `Add\Remove\Invoke-PnPListDesign` cmdlets to add a list design, remove a list design and apply the list design. +- Added `Get-PnPListItemPermission` cmdlet to retrieve permissions of a list item. ### Changed diff --git a/documentation/Get-PnPListItemPermission.md b/documentation/Get-PnPListItemPermission.md new file mode 100644 index 000000000..a1c9eea05 --- /dev/null +++ b/documentation/Get-PnPListItemPermission.md @@ -0,0 +1,81 @@ +--- +Module Name: PnP.PowerShell +title: Get-PnPListItemPermission +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Get-PnPListItemPermission.html +--- + +# Get-PnPListItemPermission + +## SYNOPSIS +Gets list item permissions. + +## SYNTAX + +```powershell + +Get-PnPListItemPermission [-List] -Identity + [-Connection ] [] + +``` + +## DESCRIPTION + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Get-PnPListItemPermission -List 'Documents' -Identity 1 +``` + +Get the permissions for listitem with id 1 in the list 'Documents' + +## PARAMETERS + +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Identity +The ID of the listitem, or actual ListItem object + +```yaml +Type: ListItemPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -List +The ID, Title or Url of the list. + +```yaml +Type: ListPipeBind +Parameter Sets: (All) + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) diff --git a/src/Commands/Lists/GetListItemPermission.cs b/src/Commands/Lists/GetListItemPermission.cs new file mode 100644 index 000000000..5d37091b6 --- /dev/null +++ b/src/Commands/Lists/GetListItemPermission.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.Management.Automation; +using PnP.Core.QueryModel; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Model; + +namespace PnP.PowerShell.Commands.Lists +{ + [Cmdlet(VerbsCommon.Get, "PnPListItemPermissions")] + public class GetListItemPermission : PnPWebCmdlet + { + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, ParameterSetName = ParameterAttribute.AllParameterSets)] + public ListPipeBind List; + + [Parameter(Mandatory = true, ValueFromPipeline = true, ParameterSetName = ParameterAttribute.AllParameterSets)] + public ListItemPipeBind Identity; + + protected override void ExecuteCmdlet() + { + var list = List.GetList(PnPContext); + + if (list == null) + { + throw new PSArgumentException($"Cannot find list provided through - {nameof(List)}", nameof(List)); + } + + var item = Identity.GetListItem(list); + + if (item == null) + { + throw new PSArgumentException($"Cannot find list item provided through -{nameof(Identity)}", nameof(Identity)); + } + + item.LoadAsync(w => w.RoleAssignments.QueryProperties(p => p.RoleDefinitions, p => p.PrincipalId)).GetAwaiter().GetResult(); + + var listItemPermissions = new List(); + + foreach (var roleAssignment in item.RoleAssignments.AsRequested()) + { + var listItemPermission = new ListItemPermissions + { + RoleDefinitions = roleAssignment.RoleDefinitions.AsRequested(), + PrincipalId = roleAssignment.PrincipalId + }; + + listItemPermissions.Add(listItemPermission); + } + + WriteObject(listItemPermissions, true); + } + } +} \ No newline at end of file diff --git a/src/Commands/Model/ListItemPermissions.cs b/src/Commands/Model/ListItemPermissions.cs new file mode 100644 index 000000000..108e4c782 --- /dev/null +++ b/src/Commands/Model/ListItemPermissions.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace PnP.PowerShell.Commands.Model +{ + public class ListItemPermissions + { + public IEnumerable RoleDefinitions { get; set; } + + public int PrincipalId { get; set; } + } +} From 8b47e789e24556c49490e4df7d4ebd50489d4d24 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sun, 16 Jan 2022 15:34:29 +0200 Subject: [PATCH 002/458] Updated docs --- CHANGELOG.md | 2 +- documentation/Get-PnPListItemPermission.md | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f36375f2..4f84734ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Added - Added `Add\Remove\Invoke-PnPListDesign` cmdlets to add a list design, remove a list design and apply the list design. -- Added `Get-PnPListItemPermission` cmdlet to retrieve permissions of a list item. +- Added `Get-PnPListItemPermissions` cmdlet to retrieve permissions of a list item. ### Changed diff --git a/documentation/Get-PnPListItemPermission.md b/documentation/Get-PnPListItemPermission.md index a1c9eea05..0650f3c0e 100644 --- a/documentation/Get-PnPListItemPermission.md +++ b/documentation/Get-PnPListItemPermission.md @@ -1,10 +1,10 @@ --- Module Name: PnP.PowerShell -title: Get-PnPListItemPermission +title: Get-PnPListItemPermissions schema: 2.0.0 applicable: SharePoint Online external help file: PnP.PowerShell.dll-Help.xml -online version: https://pnp.github.io/powershell/cmdlets/Get-PnPListItemPermission.html +online version: https://pnp.github.io/powershell/cmdlets/Get-PnPListItemPermissions.html --- # Get-PnPListItemPermission @@ -16,7 +16,7 @@ Gets list item permissions. ```powershell -Get-PnPListItemPermission [-List] -Identity +Get-PnPListItemPermissions [-List] -Identity [-Connection ] [] ``` @@ -27,7 +27,7 @@ Get-PnPListItemPermission [-List] -Identity ### EXAMPLE 1 ```powershell -Get-PnPListItemPermission -List 'Documents' -Identity 1 +Get-PnPListItemPermissions -List 'Documents' -Identity 1 ``` Get the permissions for listitem with id 1 in the list 'Documents' From 007ca8ff95a0afbc584ee1ee57711f2453fc3e7f Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sun, 16 Jan 2022 15:34:49 +0200 Subject: [PATCH 003/458] Updated URL --- ...Get-PnPListItemPermission.md => Get-PnPListItemPermissions.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename documentation/{Get-PnPListItemPermission.md => Get-PnPListItemPermissions.md} (100%) diff --git a/documentation/Get-PnPListItemPermission.md b/documentation/Get-PnPListItemPermissions.md similarity index 100% rename from documentation/Get-PnPListItemPermission.md rename to documentation/Get-PnPListItemPermissions.md From ecc8bee5ad5577bd848f50d88fb85a890382fd5d Mon Sep 17 00:00:00 2001 From: Aleks Date: Mon, 14 Mar 2022 00:37:08 +0100 Subject: [PATCH 004/458] building docker image in workflows --- .github/workflows/nightlyrelease.yml | 40 +++++----------------- docker/pnppowershell-prerelease.dockerFile | 6 ++++ 2 files changed, 15 insertions(+), 31 deletions(-) create mode 100644 docker/pnppowershell-prerelease.dockerFile diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index 0e01889e5..fb8c7d6c7 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -2,40 +2,18 @@ name: Nightly Release to PowerShell Gallery on: workflow_dispatch: - schedule: - - cron: '30 2 * * *' + push: + branches: + - dev + paths: + - 'docker/**' jobs: build: - runs-on: windows-latest + runs-on: ubuntu-latest steps: - - name: Setup .NET Core - uses: actions/setup-dotnet@v1 - with: - dotnet-version: | - 3.1.301 - 5.x - - uses: actions/checkout@v2 - with: - ref: dev - token: ${{ secrets.PAT }} - - name: Build and Publish Module - env: - POWERSHELLGALLERY_API_KEY: ${{ secrets.POWERSHELLGALLERY_API_KEY }} - shell: pwsh + - name: Build an image run: | - ./build/Build-Nightly.ps1 - - name: Set variables - shell: pwsh - run: | - $version = Get-Content version.txt -raw - "BUILDVERSION=$version" | Out-File $env:GITHUB_ENV -Encoding utf8 -Append - - name: Add & Commit - uses: EndBug/add-and-commit@v6 - with: - message: 'Nightly publish to PowerShell Gallery' - tag: '${{env.BUILDVERSION}}-nightly --force' - push: true - branch: dev - token: ${{ secrets.PAT }} + VERSION=$(cat ./version.txt) + docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag pnp-powershell:$VERSION diff --git a/docker/pnppowershell-prerelease.dockerFile b/docker/pnppowershell-prerelease.dockerFile new file mode 100644 index 000000000..333283f76 --- /dev/null +++ b/docker/pnppowershell-prerelease.dockerFile @@ -0,0 +1,6 @@ +FROM mcr.microsoft.com/powershell:7.2.1-alpine-3.14-20211215 + +SHELL ["pwsh", "-command"] +ARG PNP_MODULE_VERSION +RUN Write-Host $env:PNP_MODULE_VERSION +RUN Install-Module -Name PnP.PowerShell -RequiredVersion $env:PNP_MODULE_VERSION -Force -AllowPrerelease From a55bae383573b0cd09ced796ff19014c2f159d44 Mon Sep 17 00:00:00 2001 From: Aleks Date: Mon, 14 Mar 2022 00:42:02 +0100 Subject: [PATCH 005/458] ls --- .github/workflows/nightlyrelease.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index fb8c7d6c7..c1c8b70eb 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -15,5 +15,6 @@ jobs: steps: - name: Build an image run: | + ls VERSION=$(cat ./version.txt) docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag pnp-powershell:$VERSION From 0df806796b0833cf38cd6b410d2f4bc95b95ebb6 Mon Sep 17 00:00:00 2001 From: Aleks Date: Mon, 14 Mar 2022 00:45:00 +0100 Subject: [PATCH 006/458] cd dev --- .github/workflows/nightlyrelease.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index c1c8b70eb..7b18ca2f3 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -15,6 +15,7 @@ jobs: steps: - name: Build an image run: | + cd dev ls VERSION=$(cat ./version.txt) docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag pnp-powershell:$VERSION From 12d3e6f84bddd7dc3cb64d6823a7bbc627526301 Mon Sep 17 00:00:00 2001 From: Aleks Date: Mon, 14 Mar 2022 00:48:51 +0100 Subject: [PATCH 007/458] checkout --- .github/workflows/nightlyrelease.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index 7b18ca2f3..2217bb19d 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -13,8 +13,10 @@ jobs: runs-on: ubuntu-latest steps: + - uses: actions/checkout@v2 - name: Build an image run: | + ls cd dev ls VERSION=$(cat ./version.txt) From 18f75ca4dbf299868208e296b74fe5d2e7dd40e8 Mon Sep 17 00:00:00 2001 From: Aleks Date: Mon, 14 Mar 2022 00:50:24 +0100 Subject: [PATCH 008/458] removing debug output --- .github/workflows/nightlyrelease.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index 2217bb19d..c58c1cbe6 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -16,8 +16,5 @@ jobs: - uses: actions/checkout@v2 - name: Build an image run: | - ls - cd dev - ls VERSION=$(cat ./version.txt) docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag pnp-powershell:$VERSION From ac8acae6139fa651e86c9bdbb5d36f13eefaf60d Mon Sep 17 00:00:00 2001 From: Aleks Date: Mon, 14 Mar 2022 01:05:41 +0100 Subject: [PATCH 009/458] nightly version --- .github/workflows/nightlyrelease.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index c58c1cbe6..b2125cc48 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -16,5 +16,5 @@ jobs: - uses: actions/checkout@v2 - name: Build an image run: | - VERSION=$(cat ./version.txt) + VERSION=$(cat ./version.txt)-nightly docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag pnp-powershell:$VERSION From 99e27dc4fd849c8af91b0696e053cf461bbd160a Mon Sep 17 00:00:00 2001 From: Aleks Date: Mon, 14 Mar 2022 01:22:27 +0100 Subject: [PATCH 010/458] docker login --- .github/workflows/nightlyrelease.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index b2125cc48..86ff47a31 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -18,3 +18,4 @@ jobs: run: | VERSION=$(cat ./version.txt)-nightly docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag pnp-powershell:$VERSION + docker login -u asapozhkov -p ${{ secrets.GITHUB_TOKEN }} From 902f251ae7674a416f05ffb0fc1ee454168f4635 Mon Sep 17 00:00:00 2001 From: Aleks Date: Mon, 14 Mar 2022 01:23:40 +0100 Subject: [PATCH 011/458] docker secret --- .github/workflows/nightlyrelease.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index 86ff47a31..c7c56587e 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -18,4 +18,4 @@ jobs: run: | VERSION=$(cat ./version.txt)-nightly docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag pnp-powershell:$VERSION - docker login -u asapozhkov -p ${{ secrets.GITHUB_TOKEN }} + docker login -u asapozhkov -p ${{ secrets.DOCKER_TOKEN }} From 7777e912cd514fa455f4a3ffcf2f7242c0dcdd15 Mon Sep 17 00:00:00 2001 From: Aleks Date: Mon, 14 Mar 2022 01:33:34 +0100 Subject: [PATCH 012/458] pushing to docker hub --- .github/workflows/nightlyrelease.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index c7c56587e..39d84ffce 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -17,5 +17,6 @@ jobs: - name: Build an image run: | VERSION=$(cat ./version.txt)-nightly - docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag pnp-powershell:$VERSION + docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag asapozhkov/pnp-powershell:$VERSION docker login -u asapozhkov -p ${{ secrets.DOCKER_TOKEN }} + docker push asapozhkov/pnp-powershell:$VERSION \ No newline at end of file From 96e559c9d719ba5c8a455df7f0d20280377ff5b6 Mon Sep 17 00:00:00 2001 From: Aleks Date: Mon, 14 Mar 2022 01:44:57 +0100 Subject: [PATCH 013/458] docker readme --- docker/README.md | 26 +++++++++++ docker/build-module-in-linux.ps1 | 75 ++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 docker/README.md create mode 100644 docker/build-module-in-linux.ps1 diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 000000000..2f145eb84 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,26 @@ +# Build module in Docker in Linux + +```bash +docker run --rm -it -v $(pwd):/home/powershell mcr.microsoft.com/dotnet/sdk:6.0 pwsh +``` + +```powershell +/home/powershell/docker/build-module-in-linux.ps1 +``` + +# Publish with prereleases manually + +1. Set "DOCKER_TOKEN" variable in Github Actions Secrets + +2. Run + +```bash +VERSION=$(cat ./version.txt) +docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag asapozhkov/pnp-powershell:$VERSION +docker login -u asapozhkov -p $DOCKER_TOKEN +docker push asapozhkov/pnp-powershell:$VERSION +``` + +# Publish automatically with Github Actions + +Set "DOCKER_TOKEN" variable in Github Actions Secrets diff --git a/docker/build-module-in-linux.ps1 b/docker/build-module-in-linux.ps1 new file mode 100644 index 000000000..93e022f5f --- /dev/null +++ b/docker/build-module-in-linux.ps1 @@ -0,0 +1,75 @@ +cd /home/powershell + +$versionFileContents = (Get-Content ./version.txt -Raw).Trim() +$versionFileContents +$versionObject = [System.Management.Automation.SemanticVersion]::Parse($versionFileContents) +$buildVersion = $versionObject.Patch + 1; +$version = "$($versionObject.Major).$($versionObject.Minor).$buildVersion" +dotnet build ./src/Commands/PnP.PowerShell.csproj --nologo --configuration Release --no-incremental -p:VersionPrefix=$version -p:VersionSuffix=nightly + +$documentsFolder = [environment]::getfolderpath("mydocuments"); +$destinationFolder = "$documentsFolder/.local/share/powershell/Modules/PnP.PowerShell" +$corePath = "$destinationFolder/Core" +$commonPath = "$destinationFolder/Common" +$frameworkPath = "$destinationFolder/Framework" +$assemblyExceptions = @("System.Memory.dll"); +Write-Host "Creating target folders: $destinationFolder" -ForegroundColor Yellow +New-Item -Path $destinationFolder -ItemType Directory -Force | Out-Null +New-Item -Path "$destinationFolder\Core" -ItemType Directory -Force | Out-Null +New-Item -Path "$destinationFolder\Common" -ItemType Directory -Force | Out-Null +Write-Host "Copying files to $destinationFolder" -ForegroundColor Yellow + +$commonFiles = [System.Collections.Generic.Hashset[string]]::new() +Copy-Item -Path "./resources/*.ps1xml" -Destination "$destinationFolder" +Get-ChildItem -Path "./src/ALC/bin/Release/netstandard2.0" | Where-Object { $_.Extension -in '.dll', '.pdb' } | Foreach-Object { if (!$assemblyExceptions.Contains($_.Name)) { [void]$commonFiles.Add($_.Name) }; Copy-Item -LiteralPath $_.FullName -Destination $commonPath } +Get-ChildItem -Path "./src/Commands/bin/Release/netcoreapp3.1" | Where-Object { $_.Extension -in '.dll', '.pdb' -and -not $commonFiles.Contains($_.Name) } | Foreach-Object { Copy-Item -LiteralPath $_.FullName -Destination $corePath } + +Write-Host "Generating PnP.PowerShell.psd1" -ForegroundColor Yellow +# Load the Module in a new PowerShell session +$scriptBlock = { + $documentsFolder = [environment]::getfolderpath("mydocuments"); + $destinationFolder = "$documentsFolder/.local/share/powershell/Modules/PnP.PowerShell" + Import-Module -Name "$destinationFolder/Core/PnP.PowerShell.dll" -DisableNameChecking + Write-Host "Getting cmdlet info" -ForegroundColor Yellow + $cmdlets = Get-Command -Module PnP.PowerShell | ForEach-Object { "`"$_`"" } + $cmdlets -Join "," +} + +Write-Host "Starting job to retrieve cmdlet names" -ForegroundColor Yellow +$cmdletsString = Start-ThreadJob -ScriptBlock $scriptBlock | Receive-Job -Wait + +Write-Host "Writing PSD1" -ForegroundColor Yellow +$manifest = "@{ + NestedModules = if (`$PSEdition -eq 'Core') + { + 'Core/PnP.PowerShell.dll' + } + else + { + 'Framework/PnP.PowerShell.dll' + } + ModuleVersion = '$version' + Description = 'Microsoft 365 Patterns and Practices PowerShell Cmdlets' + GUID = '0b0430ce-d799-4f3b-a565-f0dca1f31e17' + Author = 'Microsoft 365 Patterns and Practices' + CompanyName = 'Microsoft 365 Patterns and Practices' + CompatiblePSEditions = @(`"Core`",`"Desktop`") + PowerShellVersion = '5.1' + DotNetFrameworkVersion = '4.6.1' + ProcessorArchitecture = 'None' + FunctionsToExport = '*' + CmdletsToExport = @($cmdletsString) + VariablesToExport = '*' + AliasesToExport = '*' + FormatsToProcess = 'PnP.PowerShell.Format.ps1xml' + PrivateData = @{ + PSData = @{ + Tags = 'SharePoint','PnP','Teams','Planner' + Prerelease = 'nightly' + ProjectUri = 'https://aka.ms/sppnp' + IconUri = 'https://raw.githubusercontent.com/pnp/media/40e7cd8952a9347ea44e5572bb0e49622a102a12/parker/ms/300w/parker-ms-300.png' + } + } +}" +$manifest | Out-File "$destinationFolder/PnP.PowerShell.psd1" -Force +Import-Module -Name PnP.PowerShell From dec85216857ea187c8031d470d83f28e3206767b Mon Sep 17 00:00:00 2001 From: Aleks Date: Mon, 14 Mar 2022 23:41:27 +0100 Subject: [PATCH 014/458] using DOCKER_USERNAME and DOCKER_PASSWORD --- .github/workflows/nightlyrelease.yml | 6 +++--- docker/README.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index 39d84ffce..a4713504b 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -17,6 +17,6 @@ jobs: - name: Build an image run: | VERSION=$(cat ./version.txt)-nightly - docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag asapozhkov/pnp-powershell:$VERSION - docker login -u asapozhkov -p ${{ secrets.DOCKER_TOKEN }} - docker push asapozhkov/pnp-powershell:$VERSION \ No newline at end of file + docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION + docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} + docker push ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION diff --git a/docker/README.md b/docker/README.md index 2f145eb84..fca1958ac 100644 --- a/docker/README.md +++ b/docker/README.md @@ -10,7 +10,7 @@ docker run --rm -it -v $(pwd):/home/powershell mcr.microsoft.com/dotnet/sdk:6.0 # Publish with prereleases manually -1. Set "DOCKER_TOKEN" variable in Github Actions Secrets +1. Set "DOCKER_USERNAME" and "DOCKER_PASSWORD" variables 2. Run From c0122b789ab6bd5107e0024d9a6d75838767e2b0 Mon Sep 17 00:00:00 2001 From: Aleks Date: Mon, 14 Mar 2022 23:43:59 +0100 Subject: [PATCH 015/458] using DOCKER_USERNAME and DOCKER_PASSWORD --- docker/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/README.md b/docker/README.md index fca1958ac..f57dce992 100644 --- a/docker/README.md +++ b/docker/README.md @@ -16,11 +16,11 @@ docker run --rm -it -v $(pwd):/home/powershell mcr.microsoft.com/dotnet/sdk:6.0 ```bash VERSION=$(cat ./version.txt) -docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag asapozhkov/pnp-powershell:$VERSION -docker login -u asapozhkov -p $DOCKER_TOKEN -docker push asapozhkov/pnp-powershell:$VERSION +docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag $DOCKER_USERNAME/powershell:$VERSION +docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD +docker push $DOCKER_USERNAME/powershell:$VERSION ``` # Publish automatically with Github Actions -Set "DOCKER_TOKEN" variable in Github Actions Secrets +Set "DOCKER_USERNAME" and "DOCKER_PASSWORD" variables in Github Actions Secrets From e1859ba5d3c5c34c4714b16cd8d32a63619bf40e Mon Sep 17 00:00:00 2001 From: Aleks Date: Mon, 14 Mar 2022 23:46:25 +0100 Subject: [PATCH 016/458] adding nightly tag --- .github/workflows/nightlyrelease.yml | 1 + docker/README.md | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index a4713504b..04606b231 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -20,3 +20,4 @@ jobs: docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} docker push ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION + docker push ${{ secrets.DOCKER_USERNAME }}/powershell:nightly diff --git a/docker/README.md b/docker/README.md index f57dce992..7857542d5 100644 --- a/docker/README.md +++ b/docker/README.md @@ -15,10 +15,11 @@ docker run --rm -it -v $(pwd):/home/powershell mcr.microsoft.com/dotnet/sdk:6.0 2. Run ```bash -VERSION=$(cat ./version.txt) +VERSION=$(cat ./version.txt)-nightly docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag $DOCKER_USERNAME/powershell:$VERSION docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD docker push $DOCKER_USERNAME/powershell:$VERSION +docker push $DOCKER_USERNAME/powershell:nightly ``` # Publish automatically with Github Actions From 3e896f73c3503a7a87d148899841844a1a07f251 Mon Sep 17 00:00:00 2001 From: Aleks Date: Mon, 14 Mar 2022 23:56:21 +0100 Subject: [PATCH 017/458] verified adding nightly tag --- .github/workflows/nightlyrelease.yml | 1 + docker/README.md | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index 04606b231..7f04663ea 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -18,6 +18,7 @@ jobs: run: | VERSION=$(cat ./version.txt)-nightly docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION + docker image tag $DOCKER_USERNAME/powershell:$VERSION $DOCKER_USERNAME/powershell:nightly docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} docker push ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION docker push ${{ secrets.DOCKER_USERNAME }}/powershell:nightly diff --git a/docker/README.md b/docker/README.md index 7857542d5..736fa32ff 100644 --- a/docker/README.md +++ b/docker/README.md @@ -17,6 +17,7 @@ docker run --rm -it -v $(pwd):/home/powershell mcr.microsoft.com/dotnet/sdk:6.0 ```bash VERSION=$(cat ./version.txt)-nightly docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag $DOCKER_USERNAME/powershell:$VERSION +docker image tag $DOCKER_USERNAME/powershell:$VERSION $DOCKER_USERNAME/powershell:nightly docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD docker push $DOCKER_USERNAME/powershell:$VERSION docker push $DOCKER_USERNAME/powershell:nightly From 9f877c244341b1bb65826d02827e57cff8ecb428 Mon Sep 17 00:00:00 2001 From: Aleks Date: Tue, 15 Mar 2022 00:00:13 +0100 Subject: [PATCH 018/458] corrected user name ref --- .github/workflows/nightlyrelease.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index 7f04663ea..965da7d8a 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -18,7 +18,7 @@ jobs: run: | VERSION=$(cat ./version.txt)-nightly docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION - docker image tag $DOCKER_USERNAME/powershell:$VERSION $DOCKER_USERNAME/powershell:nightly + docker image tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION ${{ secrets.DOCKER_USERNAME }}/powershell:nightly docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} docker push ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION docker push ${{ secrets.DOCKER_USERNAME }}/powershell:nightly From d2e2180a57042cf1cd902013b50714b5e7e323c1 Mon Sep 17 00:00:00 2001 From: Mikael Svenson Date: Sat, 26 Mar 2022 17:44:41 +0100 Subject: [PATCH 019/458] Refactor and support paging --- src/Commands/Search/GetSearchConfiguration.cs | 113 +++++++++++------- 1 file changed, 70 insertions(+), 43 deletions(-) diff --git a/src/Commands/Search/GetSearchConfiguration.cs b/src/Commands/Search/GetSearchConfiguration.cs index 34e986c66..db9f167d0 100644 --- a/src/Commands/Search/GetSearchConfiguration.cs +++ b/src/Commands/Search/GetSearchConfiguration.cs @@ -54,60 +54,73 @@ protected override void ExecuteCmdlet() { string output = string.Empty; - switch (Scope) + if (!PromotedResultsToBookmarkCSV.IsPresent) { - case SearchConfigurationScope.Web: - { - if (PromotedResultsToBookmarkCSV.IsPresent) - { - output = RestHelper.ExecuteGetRequest(ClientContext, "searchsetting/getpromotedresultqueryrules"); - } - else + switch (Scope) + { + case SearchConfigurationScope.Web: { + output = CurrentWeb.GetSearchConfiguration(); + + break; } - break; - } - case SearchConfigurationScope.Site: - { - if (PromotedResultsToBookmarkCSV.IsPresent) - { - output = RestHelper.ExecuteGetRequest(ClientContext, "searchsetting/getpromotedresultqueryrules?sitecollectionlevel=true"); - } - else + case SearchConfigurationScope.Site: { + output = ClientContext.Site.GetSearchConfiguration(); - } - break; - } - case SearchConfigurationScope.Subscription: - { - if (!ClientContext.Url.ToLower().Contains("-admin")) - { - throw new InvalidOperationException(Resources.CurrentSiteIsNoTenantAdminSite); - } - if (PromotedResultsToBookmarkCSV.IsPresent) - { - output = RestHelper.ExecuteGetRequest(ClientContext, "searchsetting/getpromotedresultqueryrules"); + break; } - else + case SearchConfigurationScope.Subscription: { + if (!ClientContext.Url.ToLower().Contains("-admin")) + { + throw new InvalidOperationException(Resources.CurrentSiteIsNoTenantAdminSite); + } + SearchObjectOwner owningScope = new SearchObjectOwner(ClientContext, SearchObjectLevel.SPSiteSubscription); var config = new SearchConfigurationPortability(ClientContext); ClientResult configuration = config.ExportSearchConfiguration(owningScope); ClientContext.ExecuteQueryRetry(); output = configuration.Value; } - } - break; + break; + } } - - if (PromotedResultsToBookmarkCSV.IsPresent) + else { - output = ConvertToCSV(output); - } + string promotedResultsBaseUrl = "searchsetting/getpromotedresultqueryrules?"; + if (Scope == SearchConfigurationScope.Site) + { + promotedResultsBaseUrl += "sitecollectionlevel=true&"; + } + int offset = 0; + const int numberOfRules = 50; + bool hasData; + List queryRuleResponses = new List(); + do + { + string runUrl = string.Format("{0}offset={1}&numberOfRules={2}", promotedResultsBaseUrl, offset, numberOfRules); + string response = RestHelper.ExecuteGetRequest(ClientContext, runUrl); + offset += numberOfRules; + var config = JsonSerializer.Deserialize(response); + hasData = config.Result != null && config.Result.Count > 0; + if(hasData) queryRuleResponses.Add(response); + } while (hasData); + + List bookmarks = new List(200); + foreach (var response in queryRuleResponses) + { + var result = PromotedResultsToBookmarks(response); + if (result != null && result.Count > 0) + { + bookmarks.AddRange(result); + } + } + output = BookmarksToString(bookmarks); + } if (Path != null) { @@ -144,7 +157,7 @@ protected override void ExecuteCmdlet() } } - private string ConvertToCSV(string json) + private List PromotedResultsToBookmarks(string json) { var bookmarks = new List(); var config = JsonSerializer.Deserialize(json); @@ -152,11 +165,16 @@ private string ConvertToCSV(string json) { foreach (var rule in config.Result) { - //if (!rule.IsPromotedResultsOnly) continue; - if (rule.QueryConditions == null || rule.PromotedResults == null) continue; + if (rule.QueryConditions == null || rule.PromotedResults == null) + { + continue; + } foreach (var promoResult in rule.PromotedResults) { - if (promoResult.IsVisual) continue; + if (promoResult.IsVisual) + { + continue; + } dynamic bookmark = new ExpandoObject(); bookmark.Title = promoResult.Title.Contains(" ") ? '"' + promoResult.Title + '"' : promoResult.Title; bookmark.Url = promoResult.Url; @@ -164,8 +182,10 @@ private string ConvertToCSV(string json) bool matchSimilar = false; foreach (var condition in rule.QueryConditions) { - if (condition.Terms == null) continue; - if (condition.QueryConditionType != "Keyword") continue; + if (condition.Terms == null || condition.QueryConditionType != "Keyword") + { + continue; + } if (condition.MatchingOptions.Contains("ProperPrefix") || condition.MatchingOptions.Contains("ProperSuffix")) { @@ -177,7 +197,10 @@ private string ConvertToCSV(string json) triggerTerms.AddRange(term.Split(';').Select(s => s.Replace("Keywords:", "").Trim()).ToList()); } } - if (triggerTerms.Count == 0) continue; + if (triggerTerms.Count == 0) + { + continue; + } var dict = bookmark as IDictionary; @@ -201,7 +224,11 @@ private string ConvertToCSV(string json) } } } + return bookmarks; + } + private static string BookmarksToString(List bookmarks) + { StringBuilder sb = new StringBuilder(); bool firstLine = true; foreach (var bookmark in bookmarks) From b39113d816dc21858454c7e3e27b4a76774cce41 Mon Sep 17 00:00:00 2001 From: Mikael Svenson Date: Tue, 29 Mar 2022 20:03:01 +0200 Subject: [PATCH 020/458] Added warning output for skipped promoted results --- src/Commands/Search/GetSearchConfiguration.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Commands/Search/GetSearchConfiguration.cs b/src/Commands/Search/GetSearchConfiguration.cs index db9f167d0..da3526c46 100644 --- a/src/Commands/Search/GetSearchConfiguration.cs +++ b/src/Commands/Search/GetSearchConfiguration.cs @@ -171,19 +171,23 @@ private List PromotedResultsToBookmarks(string json) } foreach (var promoResult in rule.PromotedResults) { + dynamic bookmark = new ExpandoObject(); + bookmark.Title = promoResult.Title.Contains(" ") ? '"' + promoResult.Title + '"' : promoResult.Title; + bookmark.Url = promoResult.Url; + if (promoResult.IsVisual) { + WriteWarning($"Skipping visual promoted result {bookmark.Title} ({bookmark.Url})"); continue; } - dynamic bookmark = new ExpandoObject(); - bookmark.Title = promoResult.Title.Contains(" ") ? '"' + promoResult.Title + '"' : promoResult.Title; - bookmark.Url = promoResult.Url; + List triggerTerms = new List(); bool matchSimilar = false; foreach (var condition in rule.QueryConditions) { if (condition.Terms == null || condition.QueryConditionType != "Keyword") { + WriteWarning($"Skipping {bookmark.Title} due to no trigger conditions"); continue; } @@ -199,6 +203,7 @@ private List PromotedResultsToBookmarks(string json) } if (triggerTerms.Count == 0) { + WriteWarning($"Skipping {bookmark.Title} due to no trigger terms"); continue; } From 2270edf3a461fe210135df09288f60d99f0e583f Mon Sep 17 00:00:00 2001 From: Milan Holemans Date: Thu, 31 Mar 2022 00:05:09 +0200 Subject: [PATCH 021/458] Created Add-PnpTeamsChannelUser cmdlet --- documentation/Add-PnpTeamsChannelUser.md | 105 ++++++++++++++++++ src/Commands/Model/Teams/TeamChannelMember.cs | 2 - src/Commands/Teams/AddTeamsChannelUser.cs | 50 +++++++++ src/Commands/Utilities/TeamsUtility.cs | 16 +++ 4 files changed, 171 insertions(+), 2 deletions(-) create mode 100644 documentation/Add-PnpTeamsChannelUser.md create mode 100644 src/Commands/Teams/AddTeamsChannelUser.cs diff --git a/documentation/Add-PnpTeamsChannelUser.md b/documentation/Add-PnpTeamsChannelUser.md new file mode 100644 index 000000000..1a58e6c28 --- /dev/null +++ b/documentation/Add-PnpTeamsChannelUser.md @@ -0,0 +1,105 @@ +--- +Module Name: PnP.PowerShell +schema: 2.0.0 +applicable: SharePoint Online +online version: https://pnp.github.io/powershell/cmdlets/Add-PnPTeamsChannelUser.html +external help file: PnP.PowerShell.dll-Help.xml +title: Add-PnPTeamsChannelUser +--- + +# Add-PnPTeamsChannelUser + +## SYNOPSIS + +**Required Permissions** + + * Microsoft Graph API: ChannelMember.ReadWrite.All + +Adds a user to an existing Microsoft Teams private channel. + +## SYNTAX + +```powershell +Add-PnPTeamsChannelUser -Team -Channel -User -Role [] +``` + +## DESCRIPTION + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Add-PnPTeamsChannelUser -Team 4efdf392-8225-4763-9e7f-4edeb7f721aa -Channel "19:796d063b63e34497aeaf092c8fb9b44e@thread.skype" -User john@doe.com -Role Owner +``` + +Adds user as an owner to the private channel. + +### EXAMPLE 2 +```powershell +Add-PnPTeamsChannelUser -Team "My Team" -Channel "My Private Channel" -User john@doe.com -Role Member +``` + +Adds user as a member to the private channel. + +## PARAMETERS + +### -Team +Specify the group id, mailNickname or display name of the team to use. + +```yaml +Type: TeamsTeamPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Channel +The id or name of the channel to retrieve. + +```yaml +Type: TeamsChannelPipeBind +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -User +Specify the UPN (e.g. john@doe.com) + +```yaml +Type: String +Parameter Sets: (User) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Role +Specify the role of the user + +```yaml +Type: String +Parameter Sets: (All) +Accepted values: Owner, Member + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/src/Commands/Model/Teams/TeamChannelMember.cs b/src/Commands/Model/Teams/TeamChannelMember.cs index 096a4b085..907406277 100644 --- a/src/Commands/Model/Teams/TeamChannelMember.cs +++ b/src/Commands/Model/Teams/TeamChannelMember.cs @@ -26,6 +26,4 @@ public class TeamChannelMember [JsonPropertyName("email")] public string email { get; set; } } - - } diff --git a/src/Commands/Teams/AddTeamsChannelUser.cs b/src/Commands/Teams/AddTeamsChannelUser.cs new file mode 100644 index 000000000..72e494882 --- /dev/null +++ b/src/Commands/Teams/AddTeamsChannelUser.cs @@ -0,0 +1,50 @@ +using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Model.Graph; +using PnP.PowerShell.Commands.Utilities; +using System.Management.Automation; + +namespace PnP.PowerShell.Commands.Teams +{ + [Cmdlet(VerbsCommon.Add, "PnPTeamsChannelUser")] + [RequiredMinimalApiPermissions("ChannelMember.ReadWrite.All")] + public class AddTeamsChannelUser : PnPGraphCmdlet + { + [Parameter(Mandatory = true)] + public TeamsTeamPipeBind Team; + + [Parameter(Mandatory = true)] + public TeamsChannelPipeBind Channel; + + [Parameter(Mandatory = true)] + public string User; + + [Parameter(Mandatory = true)] + [ValidateSet("Owner", "Member")] + public string Role; + + protected override void ExecuteCmdlet() + { + var groupId = Team.GetGroupId(HttpClient, AccessToken); + if (groupId == null) + throw new PSArgumentException("Group not found"); + + var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + if (channelId == null) + throw new PSArgumentException("Channel not found"); + + try + { + TeamsUtility.AddChannelUserAsync(HttpClient, AccessToken, groupId, channelId, User, Role).GetAwaiter().GetResult(); + } + catch (GraphException ex) + { + if (ex.Error != null) + throw new PSInvalidOperationException(ex.Error.Message); + + throw; + } + } + } +} diff --git a/src/Commands/Utilities/TeamsUtility.cs b/src/Commands/Utilities/TeamsUtility.cs index 5e8bc12a6..8b15b8ad1 100644 --- a/src/Commands/Utilities/TeamsUtility.cs +++ b/src/Commands/Utilities/TeamsUtility.cs @@ -11,7 +11,9 @@ using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; +using Microsoft.Graph; using Group = PnP.PowerShell.Commands.Model.Graph.Group; +using Team = PnP.PowerShell.Commands.Model.Teams.Team; using TeamChannel = PnP.PowerShell.Commands.Model.Teams.TeamChannel; using User = PnP.PowerShell.Commands.Model.Teams.User; @@ -609,6 +611,20 @@ public static async Task AddChannelAsync(string accessToken, HttpCl } } + public static async Task AddChannelUserAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string upn, string role) + { + var channelMember = new TeamChannelMember + { + UserIdentifier = $"https://graph.microsoft.com/v1.0/users('{upn}')", + }; + + // The role for the user. Must be owner or empty. + if (role.Equals("Owner")) + channelMember.Roles.Add("owner"); + + return await GraphHelper.PostAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/members", channelMember, accessToken); + } + public static async Task PostMessageAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, TeamChannelMessage message) { await GraphHelper.PostAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/messages", message, accessToken); From d0601341baa636de0c2bd238617924c67b0a9592 Mon Sep 17 00:00:00 2001 From: Milan Holemans Date: Thu, 31 Mar 2022 00:09:00 +0200 Subject: [PATCH 022/458] Removed unused using --- src/Commands/Utilities/TeamsUtility.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Commands/Utilities/TeamsUtility.cs b/src/Commands/Utilities/TeamsUtility.cs index 8b15b8ad1..8eb3ef7a1 100644 --- a/src/Commands/Utilities/TeamsUtility.cs +++ b/src/Commands/Utilities/TeamsUtility.cs @@ -11,7 +11,6 @@ using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; -using Microsoft.Graph; using Group = PnP.PowerShell.Commands.Model.Graph.Group; using Team = PnP.PowerShell.Commands.Model.Teams.Team; using TeamChannel = PnP.PowerShell.Commands.Model.Teams.TeamChannel; From f8924ce5735d94981956b947763a59736de11275 Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Thu, 31 Mar 2022 03:13:30 +0000 Subject: [PATCH 023/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index d55f2ea26..64fd6e48a 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -f646d46fd105fd06464e05d62fc4afc9c25c44f4 \ No newline at end of file +f4de8051073ca3b4a1a87cfaf84908f3ec892196 \ No newline at end of file diff --git a/version.txt b/version.txt index ed21137ee..e33692ab6 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.0 \ No newline at end of file +1.10.1 \ No newline at end of file From 433d55aeae807f47d49f75464e20fcfffb16efd9 Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 31 Mar 2022 16:47:25 +0200 Subject: [PATCH 024/458] run via schedule again --- .github/workflows/nightlyrelease.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index 965da7d8a..c872c4553 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -2,11 +2,8 @@ name: Nightly Release to PowerShell Gallery on: workflow_dispatch: - push: - branches: - - dev - paths: - - 'docker/**' + schedule: + - cron: '30 3 * * *' jobs: build: From bb6f9f2bc140e39c9ec162e3969d3aecab730a09 Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 31 Mar 2022 16:47:54 +0200 Subject: [PATCH 025/458] more specific job name --- .github/workflows/nightlyrelease.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index c872c4553..4d371e6ac 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -5,10 +5,8 @@ on: schedule: - cron: '30 3 * * *' jobs: - build: - + publish-docker: runs-on: ubuntu-latest - steps: - uses: actions/checkout@v2 - name: Build an image From eb6fd6b08b048a1a72c40bcf290b74ce7baa351d Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 31 Mar 2022 16:49:16 +0200 Subject: [PATCH 026/458] divided job into simpler steps --- .github/workflows/nightlyrelease.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index 4d371e6ac..4859543c8 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -13,7 +13,13 @@ jobs: run: | VERSION=$(cat ./version.txt)-nightly docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION + - name: Tag the image + run: | + VERSION=$(cat ./version.txt)-nightly docker image tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION ${{ secrets.DOCKER_USERNAME }}/powershell:nightly + - name: Push the image + run: | + VERSION=$(cat ./version.txt)-nightly docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} docker push ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION docker push ${{ secrets.DOCKER_USERNAME }}/powershell:nightly From dad1ba598e3c658c2390b47cdcdfee9ddef54ac1 Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 31 Mar 2022 17:10:52 +0200 Subject: [PATCH 027/458] testing pipeline for publishing --- .github/workflows/release.yml | 16 ++++++++++++++++ docker/publish.ps1 | 1 + 2 files changed, 17 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100644 docker/publish.ps1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..d969d216d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,16 @@ +name: Release to Docker Hub + +on: + workflow_dispatch: + push: + branches: + - dev + paths: + - 'docker/**' +jobs: + publish-docker: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Build an image + run: ./docker/publish.ps1 diff --git a/docker/publish.ps1 b/docker/publish.ps1 new file mode 100644 index 000000000..3b47ed418 --- /dev/null +++ b/docker/publish.ps1 @@ -0,0 +1 @@ +docker image list \ No newline at end of file From 3450cc284ca2e22b455ffae3b96fa04130e346a9 Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 31 Mar 2022 17:13:53 +0200 Subject: [PATCH 028/458] preparing for linux containers --- .github/workflows/release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d969d216d..5086523c7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,8 +9,9 @@ on: - 'docker/**' jobs: publish-docker: - runs-on: windows-latest + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Build an image + shell: pwsh run: ./docker/publish.ps1 From f5b96511f1e7329798a4f606fd51bfe7dc4b475d Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 31 Mar 2022 17:15:13 +0200 Subject: [PATCH 029/458] triggering on yaml change --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5086523c7..f2112331a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,6 +7,7 @@ on: - dev paths: - 'docker/**' + - '.github/workflows/release.yml' jobs: publish-docker: runs-on: ubuntu-latest From 46ec4f2f91af5717c763aadcd3de43c07701b7f8 Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 31 Mar 2022 22:23:34 +0200 Subject: [PATCH 030/458] password in quoutes --- .github/workflows/nightlyrelease.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index 4859543c8..47ee4bb16 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -20,6 +20,6 @@ jobs: - name: Push the image run: | VERSION=$(cat ./version.txt)-nightly - docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} + docker login -u ${{ secrets.DOCKER_USERNAME }} -p '${{ secrets.DOCKER_PASSWORD }}' docker push ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION docker push ${{ secrets.DOCKER_USERNAME }}/powershell:nightly From 19c8a63fa35aec61e6e6dd7e06eacac04e7ad4be Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 31 Mar 2022 22:24:41 +0200 Subject: [PATCH 031/458] testing publishing script with parameters --- .github/workflows/release.yml | 6 ++++-- docker/Publish-UnpublishedImage.ps1 | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 docker/Publish-UnpublishedImage.ps1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f2112331a..8187a5408 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,6 +13,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Build an image + - name: Build and Publish All shell: pwsh - run: ./docker/publish.ps1 + run: | + `$securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell `$securedPassword diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 new file mode 100644 index 000000000..7ca884c6e --- /dev/null +++ b/docker/Publish-UnpublishedImage.ps1 @@ -0,0 +1,25 @@ +Param( + [Parameter(Position = 0, + Mandatory = $true, + ValueFromPipeline = $false)] + [String] + $PS_MODULE_NAME, + [Parameter(Position = 1, + Mandatory = $true, + ValueFromPipeline = $false)] + [String] + $DOCKER_USERNAME, + [Parameter(Position = 2, + Mandatory = $true, + ValueFromPipeline = $false)] + [String] + $DOCKER_IMAGE_NAME, + [Parameter(Position = 3, + Mandatory = $true, + ValueFromPipeline = $false)] + [Security.SecureString] + $DOCKER_PASSWORD +) +$publishedImageVersions = docker image list $DOCKER_USERNAME/$DOCKER_IMAGE_NAME --format "{{.Tag}}" +Write-Host $publishedImageVersions.Count; +Find-Module $PS_MODULE_NAME -AllVersions \ No newline at end of file From e7579239eb0520d31fc67500615aa9e3e92a7b03 Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 31 Mar 2022 22:27:03 +0200 Subject: [PATCH 032/458] no ` needed for pwsh jobs --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8187a5408..306e9a86a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,5 +16,5 @@ jobs: - name: Build and Publish All shell: pwsh run: | - `$securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force - ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell `$securedPassword + $securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword From 80e54c3bd03c816a61429471551f3d326984709a Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 31 Mar 2022 22:48:01 +0200 Subject: [PATCH 033/458] Getting docker tags via Invoke-RestMethod --- docker/Publish-UnpublishedImage.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 index 7ca884c6e..e30451a66 100644 --- a/docker/Publish-UnpublishedImage.ps1 +++ b/docker/Publish-UnpublishedImage.ps1 @@ -20,6 +20,8 @@ Param( [Security.SecureString] $DOCKER_PASSWORD ) -$publishedImageVersions = docker image list $DOCKER_USERNAME/$DOCKER_IMAGE_NAME --format "{{.Tag}}" +$publishedImageVersions = (Invoke-RestMethod https://registry.hub.docker.com/v2/repositories/$DOCKER_USERNAME/$DOCKER_IMAGE_NAME/tags).results | % { + $_.name +} Write-Host $publishedImageVersions.Count; Find-Module $PS_MODULE_NAME -AllVersions \ No newline at end of file From 6a8e7143428ce038e7589dfcb499e056aa97ff71 Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 31 Mar 2022 22:52:56 +0200 Subject: [PATCH 034/458] show every unpublished image --- docker/Publish-UnpublishedImage.ps1 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 index e30451a66..fda04e076 100644 --- a/docker/Publish-UnpublishedImage.ps1 +++ b/docker/Publish-UnpublishedImage.ps1 @@ -23,5 +23,8 @@ Param( $publishedImageVersions = (Invoke-RestMethod https://registry.hub.docker.com/v2/repositories/$DOCKER_USERNAME/$DOCKER_IMAGE_NAME/tags).results | % { $_.name } -Write-Host $publishedImageVersions.Count; -Find-Module $PS_MODULE_NAME -AllVersions \ No newline at end of file +Find-Module $PS_MODULE_NAME -AllVersions -AllowPrerelease | % { + if ( !( $publishedImageVersions -contains $_ ) ) { + $_ + } +} \ No newline at end of file From 1d5ed1ec2aa588bd8c2814f2c126a75a5f4b8bf2 Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 31 Mar 2022 22:56:46 +0200 Subject: [PATCH 035/458] fixed version comparisons --- docker/Publish-UnpublishedImage.ps1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 index fda04e076..286f40bb8 100644 --- a/docker/Publish-UnpublishedImage.ps1 +++ b/docker/Publish-UnpublishedImage.ps1 @@ -24,7 +24,8 @@ $publishedImageVersions = (Invoke-RestMethod https://registry.hub.docker.com/v2/ $_.name } Find-Module $PS_MODULE_NAME -AllVersions -AllowPrerelease | % { - if ( !( $publishedImageVersions -contains $_ ) ) { - $_ + $moduleVersion = $_.Version; + if ( !( $publishedImageVersions -contains $moduleVersion ) ) { + $moduleVersion } } \ No newline at end of file From f8c886c94eccd0217412aa7eb5cb5482f28b6e37 Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 31 Mar 2022 23:35:08 +0200 Subject: [PATCH 036/458] publishin all not prereleases --- docker/Publish-UnpublishedImage.ps1 | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 index 286f40bb8..481c35c74 100644 --- a/docker/Publish-UnpublishedImage.ps1 +++ b/docker/Publish-UnpublishedImage.ps1 @@ -23,9 +23,15 @@ Param( $publishedImageVersions = (Invoke-RestMethod https://registry.hub.docker.com/v2/repositories/$DOCKER_USERNAME/$DOCKER_IMAGE_NAME/tags).results | % { $_.name } -Find-Module $PS_MODULE_NAME -AllVersions -AllowPrerelease | % { +$moduleVersions = Find-Module $PS_MODULE_NAME -AllVersions; +[array]::Reverse($moduleVersions); +$moduleVersions | % { $moduleVersion = $_.Version; if ( !( $publishedImageVersions -contains $moduleVersion ) ) { - $moduleVersion + docker build --build-arg "PNP_MODULE_VERSION=$moduleVersion" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME:$moduleVersion; + docker image tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME:$moduleVersion $DOCKER_USERNAME/$DOCKER_IMAGE_NAME:latest; + docker login -u $DOCKER_USERNAME -p "$([System.Net.NetworkCredential]::new("", $DOCKER_PASSWORD).Password)"; + docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME:$moduleVersion; + docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME:latest; } } \ No newline at end of file From 57fb24e457ee7722322857d7181e7fd817130d35 Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 31 Mar 2022 23:37:47 +0200 Subject: [PATCH 037/458] safer manipulating with : --- docker/Publish-UnpublishedImage.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 index 481c35c74..94746a886 100644 --- a/docker/Publish-UnpublishedImage.ps1 +++ b/docker/Publish-UnpublishedImage.ps1 @@ -28,10 +28,10 @@ $moduleVersions = Find-Module $PS_MODULE_NAME -AllVersions; $moduleVersions | % { $moduleVersion = $_.Version; if ( !( $publishedImageVersions -contains $moduleVersion ) ) { - docker build --build-arg "PNP_MODULE_VERSION=$moduleVersion" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME:$moduleVersion; - docker image tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME:$moduleVersion $DOCKER_USERNAME/$DOCKER_IMAGE_NAME:latest; + docker build --build-arg "PNP_MODULE_VERSION=$moduleVersion" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$moduleVersion; + docker image tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$moduleVersion $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; docker login -u $DOCKER_USERNAME -p "$([System.Net.NetworkCredential]::new("", $DOCKER_PASSWORD).Password)"; - docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME:$moduleVersion; - docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME:latest; + docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$moduleVersion; + docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; } } \ No newline at end of file From e10069a45ab9dc98bb554e066c1239b818933df2 Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 31 Mar 2022 23:48:11 +0200 Subject: [PATCH 038/458] replacing quotes --- docker/Publish-UnpublishedImage.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 index 94746a886..cd8db9d40 100644 --- a/docker/Publish-UnpublishedImage.ps1 +++ b/docker/Publish-UnpublishedImage.ps1 @@ -30,7 +30,8 @@ $moduleVersions | % { if ( !( $publishedImageVersions -contains $moduleVersion ) ) { docker build --build-arg "PNP_MODULE_VERSION=$moduleVersion" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$moduleVersion; docker image tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$moduleVersion $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; - docker login -u $DOCKER_USERNAME -p "$([System.Net.NetworkCredential]::new("", $DOCKER_PASSWORD).Password)"; + $plainStringPassword = [System.Net.NetworkCredential]::new("", $DOCKER_PASSWORD).Password; + docker login -u $DOCKER_USERNAME -p "$plainStringPassword"; docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$moduleVersion; docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; } From 70831454ec728e94b31483d3b6353a950cc0e6b3 Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 31 Mar 2022 23:57:29 +0200 Subject: [PATCH 039/458] universal docker file --- docker/pnppowershell.dockerFile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docker/pnppowershell.dockerFile b/docker/pnppowershell.dockerFile index 6771f01bc..ea3ba51b0 100644 --- a/docker/pnppowershell.dockerFile +++ b/docker/pnppowershell.dockerFile @@ -1,3 +1,5 @@ -FROM mcr.microsoft.com/powershell:lts-debian-10 -COPY ./powershell/ powershell -RUN /usr/bin/pwsh -File ./powershell/installModules.ps1 && rm -rf ./powershell \ No newline at end of file +FROM mcr.microsoft.com/powershell:7.2.1-alpine-3.14-20211215 + +SHELL ["pwsh", "-command"] +ARG PNP_MODULE_VERSION +RUN Install-Module -Name PnP.PowerShell -RequiredVersion $env:PNP_MODULE_VERSION -Force -AllowPrerelease From 5fe379fd3c3f0fc7f2ffc92693f70ebb2badbacb Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 31 Mar 2022 23:58:24 +0200 Subject: [PATCH 040/458] using universal dockerfile instead of special --- .github/workflows/nightlyrelease.yml | 2 +- docker/pnppowershell-prerelease.dockerFile | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 docker/pnppowershell-prerelease.dockerFile diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index 47ee4bb16..2a21abd41 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -12,7 +12,7 @@ jobs: - name: Build an image run: | VERSION=$(cat ./version.txt)-nightly - docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION + docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell.dockerFile --tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION - name: Tag the image run: | VERSION=$(cat ./version.txt)-nightly diff --git a/docker/pnppowershell-prerelease.dockerFile b/docker/pnppowershell-prerelease.dockerFile deleted file mode 100644 index 333283f76..000000000 --- a/docker/pnppowershell-prerelease.dockerFile +++ /dev/null @@ -1,6 +0,0 @@ -FROM mcr.microsoft.com/powershell:7.2.1-alpine-3.14-20211215 - -SHELL ["pwsh", "-command"] -ARG PNP_MODULE_VERSION -RUN Write-Host $env:PNP_MODULE_VERSION -RUN Install-Module -Name PnP.PowerShell -RequiredVersion $env:PNP_MODULE_VERSION -Force -AllowPrerelease From 5023a9ba4ee4ff345adca1f9f228729f39995ed8 Mon Sep 17 00:00:00 2001 From: Aleks Date: Fri, 1 Apr 2022 00:15:12 +0200 Subject: [PATCH 041/458] listing published images with 10k limit --- docker/Publish-UnpublishedImage.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 index cd8db9d40..55f1183ca 100644 --- a/docker/Publish-UnpublishedImage.ps1 +++ b/docker/Publish-UnpublishedImage.ps1 @@ -20,7 +20,7 @@ Param( [Security.SecureString] $DOCKER_PASSWORD ) -$publishedImageVersions = (Invoke-RestMethod https://registry.hub.docker.com/v2/repositories/$DOCKER_USERNAME/$DOCKER_IMAGE_NAME/tags).results | % { +$publishedImageVersions = (Invoke-RestMethod https://registry.hub.docker.com/v2/repositories/$DOCKER_USERNAME/$DOCKER_IMAGE_NAME/tags?page_size=10240).results | % { $_.name } $moduleVersions = Find-Module $PS_MODULE_NAME -AllVersions; From 6e1384fe37a85d6bb5914177cc2d5bf2ac36c99e Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Fri, 1 Apr 2022 03:36:59 +0000 Subject: [PATCH 042/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 3cfa87f12..321d3a539 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -6682eb4e2a18289a9175d94bdd641aecc35f0356 \ No newline at end of file +1676f309f3879d961cffe53ee1ceba3147a81370 \ No newline at end of file diff --git a/version.txt b/version.txt index e33692ab6..70ad429ec 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.1 \ No newline at end of file +1.10.2 \ No newline at end of file From f3df35703d818a965b921d69c09fed0b4a59ada2 Mon Sep 17 00:00:00 2001 From: Aleks Date: Fri, 1 Apr 2022 10:37:59 +0200 Subject: [PATCH 043/458] release only running via schedule --- .github/workflows/release.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 306e9a86a..8d2129030 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,12 +2,8 @@ name: Release to Docker Hub on: workflow_dispatch: - push: - branches: - - dev - paths: - - 'docker/**' - - '.github/workflows/release.yml' + schedule: + - cron: '30 3 * * *' jobs: publish-docker: runs-on: ubuntu-latest From 64bdca8eb2680ccf39f1bd66371cbbbff4d64d48 Mon Sep 17 00:00:00 2001 From: Aleks Date: Fri, 1 Apr 2022 10:40:56 +0200 Subject: [PATCH 044/458] updated base image version --- docker/pnppowershell.dockerFile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/pnppowershell.dockerFile b/docker/pnppowershell.dockerFile index ea3ba51b0..b46b7e6c9 100644 --- a/docker/pnppowershell.dockerFile +++ b/docker/pnppowershell.dockerFile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/powershell:7.2.1-alpine-3.14-20211215 +FROM mcr.microsoft.com/powershell:7.2.2-alpine-3.14-20220318 SHELL ["pwsh", "-command"] ARG PNP_MODULE_VERSION From fb57053a70144dee1fe3f0e1a31d0f286c3e750c Mon Sep 17 00:00:00 2001 From: Aleks Date: Fri, 1 Apr 2022 10:45:03 +0200 Subject: [PATCH 045/458] cleaning nightly release before merging to origin --- .github/workflows/nightlyrelease.yml | 40 ++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index 2a21abd41..fe37d37e4 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -1,12 +1,48 @@ -name: Nightly Release to PowerShell Gallery +name: Nightly Release to PowerShell Gallery and Docker Hub on: workflow_dispatch: schedule: - - cron: '30 3 * * *' + - cron: '30 2 * * *' jobs: + build: + + runs-on: windows-latest + + steps: + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: | + 3.1.301 + 5.x + - uses: actions/checkout@v2 + with: + ref: dev + token: ${{ secrets.PAT }} + - name: Build and Publish Module + env: + POWERSHELLGALLERY_API_KEY: ${{ secrets.POWERSHELLGALLERY_API_KEY }} + shell: pwsh + run: | + ./build/Build-Nightly.ps1 + - name: Set variables + shell: pwsh + run: | + $version = Get-Content version.txt -raw + "BUILDVERSION=$version" | Out-File $env:GITHUB_ENV -Encoding utf8 -Append + - name: Add & Commit + uses: EndBug/add-and-commit@v6 + with: + message: 'Nightly publish to PowerShell Gallery' + tag: '${{env.BUILDVERSION}}-nightly --force' + push: true + branch: dev + token: ${{ secrets.PAT }} + name: Nightly Release to PowerShell Gallery publish-docker: runs-on: ubuntu-latest + needs: [ build ] steps: - uses: actions/checkout@v2 - name: Build an image From c518b51127eb6c742429c97ee3615c6cc03a9227 Mon Sep 17 00:00:00 2001 From: Aleks Date: Fri, 1 Apr 2022 10:46:37 +0200 Subject: [PATCH 046/458] typo in yaml cleaning --- .github/workflows/nightlyrelease.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index fe37d37e4..f6c9493ee 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -39,7 +39,6 @@ jobs: push: true branch: dev token: ${{ secrets.PAT }} - name: Nightly Release to PowerShell Gallery publish-docker: runs-on: ubuntu-latest needs: [ build ] From 5dda0253af4f6d99d515ad4f764c73addbfbd6ef Mon Sep 17 00:00:00 2001 From: Aleks Date: Fri, 1 Apr 2022 10:58:13 +0200 Subject: [PATCH 047/458] removing unused file --- docker/publish.ps1 | 1 - 1 file changed, 1 deletion(-) delete mode 100644 docker/publish.ps1 diff --git a/docker/publish.ps1 b/docker/publish.ps1 deleted file mode 100644 index 3b47ed418..000000000 --- a/docker/publish.ps1 +++ /dev/null @@ -1 +0,0 @@ -docker image list \ No newline at end of file From 53ead61355c921c2f533416e65f7e5a764b0f3f6 Mon Sep 17 00:00:00 2001 From: Aleks Date: Fri, 1 Apr 2022 10:59:28 +0200 Subject: [PATCH 048/458] removing unused file --- docker/build-module-in-linux.ps1 | 75 -------------------------------- 1 file changed, 75 deletions(-) delete mode 100644 docker/build-module-in-linux.ps1 diff --git a/docker/build-module-in-linux.ps1 b/docker/build-module-in-linux.ps1 deleted file mode 100644 index 93e022f5f..000000000 --- a/docker/build-module-in-linux.ps1 +++ /dev/null @@ -1,75 +0,0 @@ -cd /home/powershell - -$versionFileContents = (Get-Content ./version.txt -Raw).Trim() -$versionFileContents -$versionObject = [System.Management.Automation.SemanticVersion]::Parse($versionFileContents) -$buildVersion = $versionObject.Patch + 1; -$version = "$($versionObject.Major).$($versionObject.Minor).$buildVersion" -dotnet build ./src/Commands/PnP.PowerShell.csproj --nologo --configuration Release --no-incremental -p:VersionPrefix=$version -p:VersionSuffix=nightly - -$documentsFolder = [environment]::getfolderpath("mydocuments"); -$destinationFolder = "$documentsFolder/.local/share/powershell/Modules/PnP.PowerShell" -$corePath = "$destinationFolder/Core" -$commonPath = "$destinationFolder/Common" -$frameworkPath = "$destinationFolder/Framework" -$assemblyExceptions = @("System.Memory.dll"); -Write-Host "Creating target folders: $destinationFolder" -ForegroundColor Yellow -New-Item -Path $destinationFolder -ItemType Directory -Force | Out-Null -New-Item -Path "$destinationFolder\Core" -ItemType Directory -Force | Out-Null -New-Item -Path "$destinationFolder\Common" -ItemType Directory -Force | Out-Null -Write-Host "Copying files to $destinationFolder" -ForegroundColor Yellow - -$commonFiles = [System.Collections.Generic.Hashset[string]]::new() -Copy-Item -Path "./resources/*.ps1xml" -Destination "$destinationFolder" -Get-ChildItem -Path "./src/ALC/bin/Release/netstandard2.0" | Where-Object { $_.Extension -in '.dll', '.pdb' } | Foreach-Object { if (!$assemblyExceptions.Contains($_.Name)) { [void]$commonFiles.Add($_.Name) }; Copy-Item -LiteralPath $_.FullName -Destination $commonPath } -Get-ChildItem -Path "./src/Commands/bin/Release/netcoreapp3.1" | Where-Object { $_.Extension -in '.dll', '.pdb' -and -not $commonFiles.Contains($_.Name) } | Foreach-Object { Copy-Item -LiteralPath $_.FullName -Destination $corePath } - -Write-Host "Generating PnP.PowerShell.psd1" -ForegroundColor Yellow -# Load the Module in a new PowerShell session -$scriptBlock = { - $documentsFolder = [environment]::getfolderpath("mydocuments"); - $destinationFolder = "$documentsFolder/.local/share/powershell/Modules/PnP.PowerShell" - Import-Module -Name "$destinationFolder/Core/PnP.PowerShell.dll" -DisableNameChecking - Write-Host "Getting cmdlet info" -ForegroundColor Yellow - $cmdlets = Get-Command -Module PnP.PowerShell | ForEach-Object { "`"$_`"" } - $cmdlets -Join "," -} - -Write-Host "Starting job to retrieve cmdlet names" -ForegroundColor Yellow -$cmdletsString = Start-ThreadJob -ScriptBlock $scriptBlock | Receive-Job -Wait - -Write-Host "Writing PSD1" -ForegroundColor Yellow -$manifest = "@{ - NestedModules = if (`$PSEdition -eq 'Core') - { - 'Core/PnP.PowerShell.dll' - } - else - { - 'Framework/PnP.PowerShell.dll' - } - ModuleVersion = '$version' - Description = 'Microsoft 365 Patterns and Practices PowerShell Cmdlets' - GUID = '0b0430ce-d799-4f3b-a565-f0dca1f31e17' - Author = 'Microsoft 365 Patterns and Practices' - CompanyName = 'Microsoft 365 Patterns and Practices' - CompatiblePSEditions = @(`"Core`",`"Desktop`") - PowerShellVersion = '5.1' - DotNetFrameworkVersion = '4.6.1' - ProcessorArchitecture = 'None' - FunctionsToExport = '*' - CmdletsToExport = @($cmdletsString) - VariablesToExport = '*' - AliasesToExport = '*' - FormatsToProcess = 'PnP.PowerShell.Format.ps1xml' - PrivateData = @{ - PSData = @{ - Tags = 'SharePoint','PnP','Teams','Planner' - Prerelease = 'nightly' - ProjectUri = 'https://aka.ms/sppnp' - IconUri = 'https://raw.githubusercontent.com/pnp/media/40e7cd8952a9347ea44e5572bb0e49622a102a12/parker/ms/300w/parker-ms-300.png' - } - } -}" -$manifest | Out-File "$destinationFolder/PnP.PowerShell.psd1" -Force -Import-Module -Name PnP.PowerShell From 7274566834c0363d3e8dd9500b9b1ed7658ad1a5 Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Sat, 2 Apr 2022 03:11:42 +0000 Subject: [PATCH 049/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 321d3a539..7caf49c8b 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -1676f309f3879d961cffe53ee1ceba3147a81370 \ No newline at end of file +57cba81dc35e67242356301d84e8c5b0f2685dec \ No newline at end of file diff --git a/version.txt b/version.txt index 70ad429ec..62321afdc 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.2 \ No newline at end of file +1.10.3 \ No newline at end of file From 45aa8c54b17370abe3552e4f62841f02f1993e0f Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Sun, 3 Apr 2022 03:12:09 +0000 Subject: [PATCH 050/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 7caf49c8b..cc93cdc01 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -57cba81dc35e67242356301d84e8c5b0f2685dec \ No newline at end of file +a54ed053d2c12227cad293dfcb2d27d5b5b2b6d4 \ No newline at end of file diff --git a/version.txt b/version.txt index 62321afdc..5a68790ef 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.3 \ No newline at end of file +1.10.4 \ No newline at end of file From eccc02842acdbcaee729a2b213a67f91bf00e0da Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Mon, 4 Apr 2022 03:25:02 +0000 Subject: [PATCH 051/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index cc93cdc01..2ab735f6e 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -a54ed053d2c12227cad293dfcb2d27d5b5b2b6d4 \ No newline at end of file +2792305c12f164813f8265016cb92b227ed97334 \ No newline at end of file diff --git a/version.txt b/version.txt index 5a68790ef..0ee7671d0 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.4 \ No newline at end of file +1.10.5 \ No newline at end of file From 9e49d792eed739bad8f626870af2315bdd3d3fb9 Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Mon, 4 Apr 2022 20:22:35 +0200 Subject: [PATCH 052/458] Update Add-PnPHubToHubAssociation.md --- documentation/Add-PnPHubToHubAssociation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Add-PnPHubToHubAssociation.md b/documentation/Add-PnPHubToHubAssociation.md index b65424f7a..f2e744d67 100644 --- a/documentation/Add-PnPHubToHubAssociation.md +++ b/documentation/Add-PnPHubToHubAssociation.md @@ -15,7 +15,7 @@ title: Add-PnPHubToHubAssociation * SharePoint: Access to the SharePoint Tenant Administration site -Associates a hub site to another hub site to build a hierarchy of hubs. The association only is a content association, meaning that only the content of the underlying associates sites will surface in webparts such as the news webpart. No other settings will be inherrited. At most 3 levels of hubs can be associated with each other. +Associates a hub site to another hub site to build a hierarchy of hubs. The association only is a content association, meaning that only the content of the underlying associates sites will surface in webparts such as the news webpart. No other settings will be inherited. At most 3 levels of hubs can be associated with each other. ## SYNTAX From c99bb08581e3efc90166bc9385aa50082291ce8a Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Mon, 4 Apr 2022 20:30:26 +0200 Subject: [PATCH 053/458] Update Add-PnPFile.md Suggested missing description --- documentation/Add-PnPFile.md | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/Add-PnPFile.md b/documentation/Add-PnPFile.md index fa132267f..f44d043bd 100644 --- a/documentation/Add-PnPFile.md +++ b/documentation/Add-PnPFile.md @@ -29,6 +29,7 @@ Add-PnPFile -Folder -FileName -Stream [-Check ``` ## DESCRIPTION +This cmdlet uploads a local file or a file from a stream to the specified folder. ## EXAMPLES From 8255fefcca6caa59846eacd2ad9a8a7b3ad27dca Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Mon, 4 Apr 2022 20:34:25 +0200 Subject: [PATCH 054/458] Update Add-PnPRoleDefinition.md --- documentation/Add-PnPRoleDefinition.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/Add-PnPRoleDefinition.md b/documentation/Add-PnPRoleDefinition.md index ea797ade5..e768b6136 100644 --- a/documentation/Add-PnPRoleDefinition.md +++ b/documentation/Add-PnPRoleDefinition.md @@ -98,7 +98,7 @@ Accept wildcard characters: False ``` ### -Exclude -Specifies permission flags(s) to disable. Please visit https://docs.microsoft.com/previous-versions/office/sharepoint-csom/ee536458(v%3Doffice.15) for the PermissionKind enum +Specifies permission flag(s) to disable. Please visit https://docs.microsoft.com/previous-versions/office/sharepoint-csom/ee536458(v%3Doffice.15) for the PermissionKind enum ```yaml Type: PermissionKind[] @@ -113,7 +113,7 @@ Accept wildcard characters: False ``` ### -Include -Specifies permission flags(s) to enable. Please visit https://docs.microsoft.com/previous-versions/office/sharepoint-csom/ee536458(v%3Doffice.15) for the PermissionKind enum +Specifies permission flag(s) to enable. Please visit https://docs.microsoft.com/previous-versions/office/sharepoint-csom/ee536458(v%3Doffice.15) for the PermissionKind enum ```yaml Type: PermissionKind[] From c9eff6f9edaf9dfab957126e680057f9cdd26a0c Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Mon, 4 Apr 2022 20:51:40 +0200 Subject: [PATCH 055/458] Update Add-PnPPage.md Suggested different description. Removed duplicate parameter --- documentation/Add-PnPPage.md | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/documentation/Add-PnPPage.md b/documentation/Add-PnPPage.md index f00934fb3..219cfac7f 100644 --- a/documentation/Add-PnPPage.md +++ b/documentation/Add-PnPPage.md @@ -10,7 +10,7 @@ title: Add-PnPPage # Add-PnPPage ## SYNOPSIS -Allows creation of a new page +Creates a new page ## SYNTAX @@ -23,7 +23,7 @@ Add-PnPPage [-Name] [-LayoutType ] ``` ## DESCRIPTION -Allows creation of a new page. The page will be located inside the Site Pages library of the cite currently connected to. +Creates a new page. The page will be located inside the Site Pages library of the cite currently connected to. ## EXAMPLES @@ -129,20 +129,6 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -Connection -Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. - -```yaml -Type: PnPConnection -Parameter Sets: (All) - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - ### -ContentType Specify either the name, ID or an actual content type. @@ -274,4 +260,4 @@ Accept wildcard characters: False ## RELATED LINKS -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) From efd13d9685e279eebbd882d8127c0fbc6dd113be Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Tue, 5 Apr 2022 03:12:54 +0000 Subject: [PATCH 056/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 2ab735f6e..8c4969370 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -2792305c12f164813f8265016cb92b227ed97334 \ No newline at end of file +8c45ebefdcf43f8af6b7c1a2619ee466af36ce87 \ No newline at end of file diff --git a/version.txt b/version.txt index 0ee7671d0..9919148bd 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.5 \ No newline at end of file +1.10.6 \ No newline at end of file From b75bab613193b94f3bfe6e6bc8c20731b66a1724 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 5 Apr 2022 15:18:59 +0200 Subject: [PATCH 057/458] Work in progress --- documentation/New-PnPUPABulkImportJob.md | 41 ++++++++++++++++++- ...intUserProfilesFromAzureActiveDirectory.md | 16 +++++++- .../UserProfiles/NewUPABulkImportJob.cs | 31 ++++++++++++++ ...intUserProfilesFromAzureActiveDirectory.cs | 5 ++- .../Utilities/SharePointUserProfileSync.cs | 26 ++++++++++-- 5 files changed, 111 insertions(+), 8 deletions(-) diff --git a/documentation/New-PnPUPABulkImportJob.md b/documentation/New-PnPUPABulkImportJob.md index 8748a044e..91c9a9818 100644 --- a/documentation/New-PnPUPABulkImportJob.md +++ b/documentation/New-PnPUPABulkImportJob.md @@ -21,13 +21,13 @@ Submit up a new user profile bulk import job. ```powershell New-PnPUPABulkImportJob [-Folder] [-Path] [-UserProfilePropertyMapping] - [-IdProperty] [[-IdType] ] [-Connection ] + [-IdProperty] [[-IdType] ] [-Wait] [-Verbose] [-Connection ] [] ``` ```powershell New-PnPUPABulkImportJob -Url [-UserProfilePropertyMapping] - [-IdProperty] [[-IdType] ] [-Connection ] + [-IdProperty] [[-IdType] ] [-Wait] [-Verbose] [-Connection ] [] ``` @@ -65,6 +65,13 @@ New-PnPUPABulkImportJob -Url "https://{tenant}.sharepoint.com/Shared Documents/p This will submit a new user profile bulk import job to SharePoint Online using an already uploaded file. +### EXAMPLE 3 +```powershell +New-PnPUPABulkImportJob -Url "https://{tenant}.sharepoint.com/sites/userprofilesync/Shared Documents/profiles.json" -IdProperty "IdName" -UserProfilePropertyMapping @{"Department"="Department"} -Wait -Verbose +``` + +This will submit a new user profile bulk import job to SharePoint Online using an already uploaded file and will wait until the import has finished. + ## PARAMETERS ### -Folder @@ -166,6 +173,36 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Wait +Adding this parameter will cause the script to start the user profile sync operation and wait with proceeding with the rest of the script until the user profiles have been imported into the SharePoint Online user profile. It can take a long time for the user profile sync operation to complete. It will check every 30 seconds for the current status of the job, to avoid getting throttled. This retry value is non configurable. + +Add `-Verbose` as well to be notified about the progress while waiting. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Verbose +When provided, additional debug statements will be shown while going through the user profile sync steps. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) diff --git a/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md b/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md index cd0ead621..1cedfb646 100644 --- a/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md +++ b/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md @@ -16,7 +16,7 @@ Synchronizes user profiles from Azure Active Directory into the SharePoint Onlin ### Upload file ```powershell -Sync-PnPSharePointUserProfilesFromAzureActiveDirectory -UserProfilePropertyMapping [-Users ] [-Folder ] [-Wait] [-Verbose] [-Connection ] [] +Sync-PnPSharePointUserProfilesFromAzureActiveDirectory -UserProfilePropertyMapping [-IdType ] [-Users ] [-Folder ] [-Wait] [-Verbose] [-Connection ] [] ``` ## DESCRIPTION @@ -130,6 +130,20 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -IdType +The type of profile identifier (Email/CloudId/PrincipalName). Defaults to CloudId. Ensure that if you use this in combination with `-Users` that all of the user objects you're passing in are having their Mail property populated when choosing IdType Email, Id property for CloudId or UserPrincipalName for PrincipalName. + +```yaml +Type: ImportProfilePropertiesUserIdType +Parameter Sets: (All) +Accepted values: Email, CloudId, PrincipalName + +Required: False +Default value: CloudId +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -WhatIf Will retrieve the users from Azure Active Directory and create and upload the mappings JSON file, but will not invoke a request to SharePoint Online to queue the import process. This way you can examine the mappings JSON file on SharePoint Online first to ensure the mappings are being done correctly. diff --git a/src/Commands/UserProfiles/NewUPABulkImportJob.cs b/src/Commands/UserProfiles/NewUPABulkImportJob.cs index f2a900465..33d0a8393 100644 --- a/src/Commands/UserProfiles/NewUPABulkImportJob.cs +++ b/src/Commands/UserProfiles/NewUPABulkImportJob.cs @@ -7,6 +7,7 @@ using Microsoft.SharePoint.Client; using PnP.PowerShell.Commands.Base; using PnP.Framework.Utilities; +using System.Threading; namespace PnP.PowerShell.Commands.UserProfiles { @@ -37,6 +38,10 @@ public class NewUPABulkImportJob : PnPAdminCmdlet [Parameter(Mandatory = false, Position = 3, ParameterSetName = ParameterSet_URL)] public ImportProfilePropertiesUserIdType IdType = ImportProfilePropertiesUserIdType.Email; + [Parameter(Mandatory = false, Position = 4, ParameterSetName = ParameterSet_UPLOADFILE)] + [Parameter(Mandatory = false, Position = 3, ParameterSetName = ParameterSet_URL)] + public SwitchParameter Wait; + protected override void ExecuteCmdlet() { if (string.IsNullOrWhiteSpace(IdProperty)) @@ -85,6 +90,32 @@ protected override void ExecuteCmdlet() var job = o365.GetImportProfilePropertyJob(id.Value); ClientContext.Load(job); ClientContext.ExecuteQueryRetry(); + + WriteVerbose($"Job initiated with Id {job.JobId} and status {job.State} for file {job.SourceUri}"); + + // Check if we should wait with finalzing this cmdlet execution until the user profile import operation has completed + if(Wait.ToBool()) + { + // Go into a loop to wait for the import to be successful or erroneous + ImportProfilePropertiesJobInfo jobStatus; + do + { + // Wait for 30 seconds before requesting its current state again to avoid running into throttling + Thread.Sleep((int)System.TimeSpan.FromSeconds(30).TotalMilliseconds); + + // Request the current status of the import job + jobStatus = o365.GetImportProfilePropertyJob(job.JobId); + ClientContext.Load(jobStatus); + ClientContext.ExecuteQueryRetry(); + + WriteVerbose($"Current status of job {job.JobId}: {jobStatus.State}"); + } + while (jobStatus.State != ImportProfilePropertiesJobState.Succeeded && jobStatus.State != ImportProfilePropertiesJobState.Error); + + // Import job either completed or failed + job = jobStatus; + } + WriteObject(job); } } diff --git a/src/Commands/UserProfiles/SyncSharePointUserProfilesFromAzureActiveDirectory.cs b/src/Commands/UserProfiles/SyncSharePointUserProfilesFromAzureActiveDirectory.cs index 73102a3c4..1aba0f725 100644 --- a/src/Commands/UserProfiles/SyncSharePointUserProfilesFromAzureActiveDirectory.cs +++ b/src/Commands/UserProfiles/SyncSharePointUserProfilesFromAzureActiveDirectory.cs @@ -24,6 +24,9 @@ public class SyncSharePointUserProfilesFromAzureActiveDirectory : PnPSharePointC [Parameter(Mandatory = false)] public SwitchParameter WhatIf; + [Parameter(Mandatory = false)] + public ImportProfilePropertiesUserIdType IdType = ImportProfilePropertiesUserIdType.PrincipalName; + [Parameter(Mandatory = false)] public SwitchParameter Wait; @@ -78,7 +81,7 @@ protected override void ExecuteCmdlet() // Perform the mapping and execute the sync operation WriteVerbose($"Creating mapping file{(WhatIf.ToBool() ? " and" : ",")} uploading it to SharePoint Online to folder '{Folder}'{(WhatIf.ToBool() ? "" : " and executing sync job")}"); - var job = PnP.PowerShell.Commands.Utilities.SharePointUserProfileSync.SyncFromAzureActiveDirectory(nonAdminClientContext, aadUsers, UserProfilePropertyMapping, Folder, ParameterSpecified(nameof(WhatIf))).GetAwaiter().GetResult(); + var job = PnP.PowerShell.Commands.Utilities.SharePointUserProfileSync.SyncFromAzureActiveDirectory(nonAdminClientContext, aadUsers, IdType, UserProfilePropertyMapping, Folder, ParameterSpecified(nameof(WhatIf))).GetAwaiter().GetResult(); WriteVerbose($"Job initiated with Id {job.JobId} and status {job.State} for file {job.SourceUri}"); diff --git a/src/Commands/Utilities/SharePointUserProfileSync.cs b/src/Commands/Utilities/SharePointUserProfileSync.cs index 4db0a5493..d840a2ef6 100644 --- a/src/Commands/Utilities/SharePointUserProfileSync.cs +++ b/src/Commands/Utilities/SharePointUserProfileSync.cs @@ -23,10 +23,11 @@ public static class SharePointUserProfileSync /// A ClientContext which can be used to interact with SharePoint Online /// Azure AD User objects that need to be synced /// Hashtable with the mapping from the Azure Active Directory property (the value) to the SharePoint Online User Profile Property (the key) + /// Type of identifier to map the user on to synchronize its user profile of (CloudId, PrincipalName, Email) /// Location in the currently connected to site where to upload the JSON file to with instructions to update the user profiles /// Boolean indicating if only the mappings file should be created and uploaded to SharePoint Online (true) or if the import job on that file should also be invoked (false) /// Information on the status of the import job that has been created because of this action - public static async Task SyncFromAzureActiveDirectory(ClientContext clientContext, IEnumerable users, Hashtable userProfilePropertyMappings, string sharePointFolder, bool onlyCreateAndUploadMappingsFile = false) + public static async Task SyncFromAzureActiveDirectory(ClientContext clientContext, IEnumerable users, ImportProfilePropertiesUserIdType idType, Hashtable userProfilePropertyMappings, string sharePointFolder, bool onlyCreateAndUploadMappingsFile = false) { var webServerRelativeUrl = clientContext.Web.EnsureProperty(w => w.ServerRelativeUrl); if (!sharePointFolder.ToLower().StartsWith(webServerRelativeUrl)) @@ -72,12 +73,29 @@ public static async Task SyncFromAzureActiveDir } } + // If there are properties to update for this user, add the IdName property for this user and the fields to update to the mapping output if(userUpdateBuilder.Length > 0) { bulkUpdateBuilder.Append(@"{""IdName"":"""); - bulkUpdateBuilder.Append(user.UserPrincipalName); + + // Map the proper IdType property based on the IdType that we need to use to identify the user by + switch(idType) + { + case ImportProfilePropertiesUserIdType.CloudId: + bulkUpdateBuilder.Append(user.Id); + break; + + case ImportProfilePropertiesUserIdType.Email: + bulkUpdateBuilder.Append(user.Mail); + break; + + case ImportProfilePropertiesUserIdType.PrincipalName: + bulkUpdateBuilder.Append(user.UserPrincipalName); + break; + } + bulkUpdateBuilder.Append(@""","); - bulkUpdateBuilder.Append(userUpdateBuilder.ToString().TrimEnd(',').Replace(@"\", @"\\")); + bulkUpdateBuilder.Append(userUpdateBuilder.ToString().TrimEnd(',')).Replace(@"\", @"\\"); bulkUpdateBuilder.Append("},"); userUpdateBuilder.Clear(); @@ -124,7 +142,7 @@ public static async Task SyncFromAzureActiveDir var o365 = new Office365Tenant(clientContext); var propDictionary = userProfilePropertyMappings.Cast().ToDictionary(kvp => (string)kvp.Key, kvp => (string)kvp.Key); var url = new Uri(clientContext.Url).GetLeftPart(UriPartial.Authority) + file.ServerRelativeUrl; - var id = o365.QueueImportProfileProperties(ImportProfilePropertiesUserIdType.PrincipalName, "IdName", propDictionary, url); + var id = o365.QueueImportProfileProperties(idType, "IdName", propDictionary, url); clientContext.ExecuteQueryRetry(); // Retrieve the import json details From 06cd660702e7e004c3b46de70da2936e51007541 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 6 Apr 2022 03:13:11 +0000 Subject: [PATCH 058/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 8c4969370..5650eb255 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -8c45ebefdcf43f8af6b7c1a2619ee466af36ce87 \ No newline at end of file +ecadf5ff9e791d25ee795f5c0fbe2128cb131d49 \ No newline at end of file diff --git a/version.txt b/version.txt index 9919148bd..c67978766 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.6 \ No newline at end of file +1.10.7 \ No newline at end of file From 2c079557112f8380bdcfcade607bc5aa1b0ccf50 Mon Sep 17 00:00:00 2001 From: Mikael Svenson Date: Wed, 6 Apr 2022 12:43:33 +0200 Subject: [PATCH 059/458] Add ExcludeVisualPromotedResults parameter --- src/Commands/Search/GetSearchConfiguration.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Commands/Search/GetSearchConfiguration.cs b/src/Commands/Search/GetSearchConfiguration.cs index da3526c46..afb9c25b9 100644 --- a/src/Commands/Search/GetSearchConfiguration.cs +++ b/src/Commands/Search/GetSearchConfiguration.cs @@ -50,6 +50,9 @@ public class GetSearchConfiguration : PnPWebCmdlet [Parameter(Mandatory = false, ParameterSetName = "CSV")] public BookmarkStatus BookmarkStatus = BookmarkStatus.Suggested; + [Parameter(Mandatory = false, ParameterSetName = "CSV")] + public bool ExcludeVisualPromotedResults = true; + protected override void ExecuteCmdlet() { string output = string.Empty; @@ -175,7 +178,7 @@ private List PromotedResultsToBookmarks(string json) bookmark.Title = promoResult.Title.Contains(" ") ? '"' + promoResult.Title + '"' : promoResult.Title; bookmark.Url = promoResult.Url; - if (promoResult.IsVisual) + if (promoResult.IsVisual && ExcludeVisualPromotedResults) { WriteWarning($"Skipping visual promoted result {bookmark.Title} ({bookmark.Url})"); continue; From acd12ccd57c7ecb11b6b7eb0f504207cc68481f4 Mon Sep 17 00:00:00 2001 From: Mikael Svenson Date: Wed, 6 Apr 2022 12:43:56 +0200 Subject: [PATCH 060/458] Update documentation --- documentation/Get-PnPSearchConfiguration.md | 37 ++++++++++++++------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/documentation/Get-PnPSearchConfiguration.md b/documentation/Get-PnPSearchConfiguration.md index 4ce451c6c..532b235ee 100644 --- a/documentation/Get-PnPSearchConfiguration.md +++ b/documentation/Get-PnPSearchConfiguration.md @@ -28,7 +28,7 @@ Get-PnPSearchConfiguration [-Scope ] [-OutputFormat ] [-PromotedResultsToBookmarkCSV] [-BookmarkStatus ] [-Path ] +Get-PnPSearchConfiguration [-Scope ] [-PromotedResultsToBookmarkCSV] [-ExcludeVisualPromotedResults ] [-BookmarkStatus ] [-Path ] [-Connection ] [] ``` @@ -42,56 +42,56 @@ Get-PnPSearchConfiguration [-Scope ] [-PromotedResults Get-PnPSearchConfiguration ``` -Returns the search configuration for the current web +Returns the search configuration for the current web. ### EXAMPLE 2 ```powershell Get-PnPSearchConfiguration -Scope Site ``` -Returns the search configuration for the current site collection +Returns the search configuration for the current site collection. ### EXAMPLE 3 ```powershell Get-PnPSearchConfiguration -Scope Subscription ``` -Returns the search configuration for the current tenant +Returns the search configuration for the current tenant. ### EXAMPLE 4 ```powershell Get-PnPSearchConfiguration -Path searchconfig.xml -Scope Subscription ``` -Returns the search configuration for the current tenant and saves it to the specified file +Returns the search configuration for the current tenant and saves it to the specified file. ### EXAMPLE 5 ```powershell Get-PnPSearchConfiguration -Scope Site -OutputFormat ManagedPropertyMappings ``` -Returns all custom managed properties and crawled property mapping at the current site collection +Returns all custom managed properties and crawled property mapping at the current site collection. ### EXAMPLE 6 ```powershell Get-PnPSearchConfiguration -Scope Site -PromotedResultsToBookmarkCSV -Path bookmarks.csv ``` -Export promoted results from query rules on the site collection as a CSV file with the bookmarks in suggested status +Export promoted results excluding visual ones from query rules on the site collection as a CSV file with the bookmarks in suggested status. ### EXAMPLE 7 ```powershell Get-PnPSearchConfiguration -Scope Site -PromotedResultsToBookmarkCSV -Path bookmarks.csv -BookmarkStatus Published ``` -Export promoted results from query rules on the site collection as a CSV file with the bookmarks in published status +Export promoted results excluding visual from query rules on the site collection as a CSV file with the bookmarks in published status. ### EXAMPLE 8 ```powershell -Get-PnPSearchConfiguration -Scope Subscription -PromotedResultsToBookmarkCSV +Get-PnPSearchConfiguration -Scope Subscription -PromotedResultsToBookmarkCSV -ExcludeVisualPromotedResults $false ``` -Export promoted results from query rules on the tenant in CSV format with the bookmarks in suggested status. +Export promoted results including visual ones from query rules on the tenant in CSV format with the bookmarks in suggested status. ## PARAMETERS @@ -158,9 +158,9 @@ Output promoted results to a compatible CSV file to be used as Bookmark import a Export details: -* Promoted results marked as "Render the URL as a banner instead of as a hyperlink" and query rules with no triggers will be skipped. +* Promoted results marked as "Render the URL as a banner instead of as a hyperlink" (visual promoted results) and query rules with no triggers will be skipped by default. * Triggers set to "Advanced Query Text Match" and "Query Contains Action Term" will have "Match Similar Keywords" set to true for the Bookmark. -* Multiple triggers on a query rule will be merged into one. +* Multiple triggers on a query rule will be merged into a single trigger. ```yaml Type: SwitchParameter @@ -173,6 +173,19 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -ExcludeVisualPromotedResults +Exclude promoted results marked as "Render the URL as a banner instead of as a hyperlink". Defaults to true. + +```yaml +Type: Boolean +Parameter Sets: CSV + +Required: False +Position: Named +Default value: True +Accept pipeline input: False +Accept wildcard characters: False + ### -BookmarkStatus Output bookmarks to be in suggested or published status upon CSV import. Defaults to suggested status. From 813e9972a9e0500a6bc4ea622859c1c130167b91 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 6 Apr 2022 16:30:41 +0200 Subject: [PATCH 061/458] Minor documentatipn updates --- documentation/New-PnPUPABulkImportJob.md | 2 +- ...ync-PnPSharePointUserProfilesFromAzureActiveDirectory.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/documentation/New-PnPUPABulkImportJob.md b/documentation/New-PnPUPABulkImportJob.md index 91c9a9818..09fd64f60 100644 --- a/documentation/New-PnPUPABulkImportJob.md +++ b/documentation/New-PnPUPABulkImportJob.md @@ -174,7 +174,7 @@ Accept wildcard characters: False ``` ### -Wait -Adding this parameter will cause the script to start the user profile sync operation and wait with proceeding with the rest of the script until the user profiles have been imported into the SharePoint Online user profile. It can take a long time for the user profile sync operation to complete. It will check every 30 seconds for the current status of the job, to avoid getting throttled. This retry value is non configurable. +Adding this parameter will cause the script to start the user profile sync operation and wait with proceeding with the rest of the script until the user profiles have been imported into the SharePoint Online user profile. It can take a long time for the user profile sync operation to complete. It will check every 30 seconds for the current status of the job, to avoid getting throttled. The check interval is non configurable. Add `-Verbose` as well to be notified about the progress while waiting. diff --git a/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md b/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md index 1cedfb646..37f2a5994 100644 --- a/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md +++ b/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md @@ -131,7 +131,7 @@ Accept wildcard characters: False ``` ### -IdType -The type of profile identifier (Email/CloudId/PrincipalName). Defaults to CloudId. Ensure that if you use this in combination with `-Users` that all of the user objects you're passing in are having their Mail property populated when choosing IdType Email, Id property for CloudId or UserPrincipalName for PrincipalName. +The type of profile identifier (Email/CloudId/PrincipalName). Defaults to CloudId. Ensure that if you use this in combination with `-Users` that all of the user objects you're passing in are having their Mail property populated when choosing IdType Email, Id property for IdType CloudId or UserPrincipalName for IdType PrincipalName. ```yaml Type: ImportProfilePropertiesUserIdType @@ -160,7 +160,7 @@ Accept wildcard characters: False ``` ### -Wait -Adding this parameter will cause the script to start the user profile sync operation and wait with proceeding with the rest of the script until the user profiles have been imported into the SharePoint Online user profile. It can take a long time for the user profile sync operation to complete. It will check every 30 seconds for the current status of the job, to avoid getting throttled. This retry value is non configurable. +Adding this parameter will cause the script to start the user profile sync operation and wait with proceeding with the rest of the script until the user profiles have been imported into the SharePoint Online user profile. It can take a long time for the user profile sync operation to complete. It will check every 30 seconds for the current status of the job, to avoid getting throttled. The check interval is non configurable. Add `-Verbose` as well to be notified about the progress while waiting. @@ -191,4 +191,4 @@ Accept wildcard characters: False ## RELATED LINKS -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file From 5d9cb2d86c81c843fbcc1e74ee5f9e80b91a48ef Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 6 Apr 2022 17:22:31 +0200 Subject: [PATCH 062/458] Update CHANGELOG.md Added placeholders for nightly release --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6149616d3..df30c94cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Added +### Changed + +### Fixed + +### Removed + +### Contributors + ## [1.10.0] ### Added From 939c8c529f575ea62c68655a5fa370d45e23fe70 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 5 Apr 2022 15:18:59 +0200 Subject: [PATCH 063/458] Work in progress --- documentation/New-PnPUPABulkImportJob.md | 41 ++++++++++++++++++- ...intUserProfilesFromAzureActiveDirectory.md | 16 +++++++- .../UserProfiles/NewUPABulkImportJob.cs | 31 ++++++++++++++ ...intUserProfilesFromAzureActiveDirectory.cs | 5 ++- .../Utilities/SharePointUserProfileSync.cs | 26 ++++++++++-- 5 files changed, 111 insertions(+), 8 deletions(-) diff --git a/documentation/New-PnPUPABulkImportJob.md b/documentation/New-PnPUPABulkImportJob.md index 8748a044e..91c9a9818 100644 --- a/documentation/New-PnPUPABulkImportJob.md +++ b/documentation/New-PnPUPABulkImportJob.md @@ -21,13 +21,13 @@ Submit up a new user profile bulk import job. ```powershell New-PnPUPABulkImportJob [-Folder] [-Path] [-UserProfilePropertyMapping] - [-IdProperty] [[-IdType] ] [-Connection ] + [-IdProperty] [[-IdType] ] [-Wait] [-Verbose] [-Connection ] [] ``` ```powershell New-PnPUPABulkImportJob -Url [-UserProfilePropertyMapping] - [-IdProperty] [[-IdType] ] [-Connection ] + [-IdProperty] [[-IdType] ] [-Wait] [-Verbose] [-Connection ] [] ``` @@ -65,6 +65,13 @@ New-PnPUPABulkImportJob -Url "https://{tenant}.sharepoint.com/Shared Documents/p This will submit a new user profile bulk import job to SharePoint Online using an already uploaded file. +### EXAMPLE 3 +```powershell +New-PnPUPABulkImportJob -Url "https://{tenant}.sharepoint.com/sites/userprofilesync/Shared Documents/profiles.json" -IdProperty "IdName" -UserProfilePropertyMapping @{"Department"="Department"} -Wait -Verbose +``` + +This will submit a new user profile bulk import job to SharePoint Online using an already uploaded file and will wait until the import has finished. + ## PARAMETERS ### -Folder @@ -166,6 +173,36 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Wait +Adding this parameter will cause the script to start the user profile sync operation and wait with proceeding with the rest of the script until the user profiles have been imported into the SharePoint Online user profile. It can take a long time for the user profile sync operation to complete. It will check every 30 seconds for the current status of the job, to avoid getting throttled. This retry value is non configurable. + +Add `-Verbose` as well to be notified about the progress while waiting. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Verbose +When provided, additional debug statements will be shown while going through the user profile sync steps. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) diff --git a/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md b/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md index cd0ead621..1cedfb646 100644 --- a/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md +++ b/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md @@ -16,7 +16,7 @@ Synchronizes user profiles from Azure Active Directory into the SharePoint Onlin ### Upload file ```powershell -Sync-PnPSharePointUserProfilesFromAzureActiveDirectory -UserProfilePropertyMapping [-Users ] [-Folder ] [-Wait] [-Verbose] [-Connection ] [] +Sync-PnPSharePointUserProfilesFromAzureActiveDirectory -UserProfilePropertyMapping [-IdType ] [-Users ] [-Folder ] [-Wait] [-Verbose] [-Connection ] [] ``` ## DESCRIPTION @@ -130,6 +130,20 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -IdType +The type of profile identifier (Email/CloudId/PrincipalName). Defaults to CloudId. Ensure that if you use this in combination with `-Users` that all of the user objects you're passing in are having their Mail property populated when choosing IdType Email, Id property for CloudId or UserPrincipalName for PrincipalName. + +```yaml +Type: ImportProfilePropertiesUserIdType +Parameter Sets: (All) +Accepted values: Email, CloudId, PrincipalName + +Required: False +Default value: CloudId +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -WhatIf Will retrieve the users from Azure Active Directory and create and upload the mappings JSON file, but will not invoke a request to SharePoint Online to queue the import process. This way you can examine the mappings JSON file on SharePoint Online first to ensure the mappings are being done correctly. diff --git a/src/Commands/UserProfiles/NewUPABulkImportJob.cs b/src/Commands/UserProfiles/NewUPABulkImportJob.cs index f2a900465..33d0a8393 100644 --- a/src/Commands/UserProfiles/NewUPABulkImportJob.cs +++ b/src/Commands/UserProfiles/NewUPABulkImportJob.cs @@ -7,6 +7,7 @@ using Microsoft.SharePoint.Client; using PnP.PowerShell.Commands.Base; using PnP.Framework.Utilities; +using System.Threading; namespace PnP.PowerShell.Commands.UserProfiles { @@ -37,6 +38,10 @@ public class NewUPABulkImportJob : PnPAdminCmdlet [Parameter(Mandatory = false, Position = 3, ParameterSetName = ParameterSet_URL)] public ImportProfilePropertiesUserIdType IdType = ImportProfilePropertiesUserIdType.Email; + [Parameter(Mandatory = false, Position = 4, ParameterSetName = ParameterSet_UPLOADFILE)] + [Parameter(Mandatory = false, Position = 3, ParameterSetName = ParameterSet_URL)] + public SwitchParameter Wait; + protected override void ExecuteCmdlet() { if (string.IsNullOrWhiteSpace(IdProperty)) @@ -85,6 +90,32 @@ protected override void ExecuteCmdlet() var job = o365.GetImportProfilePropertyJob(id.Value); ClientContext.Load(job); ClientContext.ExecuteQueryRetry(); + + WriteVerbose($"Job initiated with Id {job.JobId} and status {job.State} for file {job.SourceUri}"); + + // Check if we should wait with finalzing this cmdlet execution until the user profile import operation has completed + if(Wait.ToBool()) + { + // Go into a loop to wait for the import to be successful or erroneous + ImportProfilePropertiesJobInfo jobStatus; + do + { + // Wait for 30 seconds before requesting its current state again to avoid running into throttling + Thread.Sleep((int)System.TimeSpan.FromSeconds(30).TotalMilliseconds); + + // Request the current status of the import job + jobStatus = o365.GetImportProfilePropertyJob(job.JobId); + ClientContext.Load(jobStatus); + ClientContext.ExecuteQueryRetry(); + + WriteVerbose($"Current status of job {job.JobId}: {jobStatus.State}"); + } + while (jobStatus.State != ImportProfilePropertiesJobState.Succeeded && jobStatus.State != ImportProfilePropertiesJobState.Error); + + // Import job either completed or failed + job = jobStatus; + } + WriteObject(job); } } diff --git a/src/Commands/UserProfiles/SyncSharePointUserProfilesFromAzureActiveDirectory.cs b/src/Commands/UserProfiles/SyncSharePointUserProfilesFromAzureActiveDirectory.cs index 73102a3c4..1aba0f725 100644 --- a/src/Commands/UserProfiles/SyncSharePointUserProfilesFromAzureActiveDirectory.cs +++ b/src/Commands/UserProfiles/SyncSharePointUserProfilesFromAzureActiveDirectory.cs @@ -24,6 +24,9 @@ public class SyncSharePointUserProfilesFromAzureActiveDirectory : PnPSharePointC [Parameter(Mandatory = false)] public SwitchParameter WhatIf; + [Parameter(Mandatory = false)] + public ImportProfilePropertiesUserIdType IdType = ImportProfilePropertiesUserIdType.PrincipalName; + [Parameter(Mandatory = false)] public SwitchParameter Wait; @@ -78,7 +81,7 @@ protected override void ExecuteCmdlet() // Perform the mapping and execute the sync operation WriteVerbose($"Creating mapping file{(WhatIf.ToBool() ? " and" : ",")} uploading it to SharePoint Online to folder '{Folder}'{(WhatIf.ToBool() ? "" : " and executing sync job")}"); - var job = PnP.PowerShell.Commands.Utilities.SharePointUserProfileSync.SyncFromAzureActiveDirectory(nonAdminClientContext, aadUsers, UserProfilePropertyMapping, Folder, ParameterSpecified(nameof(WhatIf))).GetAwaiter().GetResult(); + var job = PnP.PowerShell.Commands.Utilities.SharePointUserProfileSync.SyncFromAzureActiveDirectory(nonAdminClientContext, aadUsers, IdType, UserProfilePropertyMapping, Folder, ParameterSpecified(nameof(WhatIf))).GetAwaiter().GetResult(); WriteVerbose($"Job initiated with Id {job.JobId} and status {job.State} for file {job.SourceUri}"); diff --git a/src/Commands/Utilities/SharePointUserProfileSync.cs b/src/Commands/Utilities/SharePointUserProfileSync.cs index 4db0a5493..d840a2ef6 100644 --- a/src/Commands/Utilities/SharePointUserProfileSync.cs +++ b/src/Commands/Utilities/SharePointUserProfileSync.cs @@ -23,10 +23,11 @@ public static class SharePointUserProfileSync /// A ClientContext which can be used to interact with SharePoint Online /// Azure AD User objects that need to be synced /// Hashtable with the mapping from the Azure Active Directory property (the value) to the SharePoint Online User Profile Property (the key) + /// Type of identifier to map the user on to synchronize its user profile of (CloudId, PrincipalName, Email) /// Location in the currently connected to site where to upload the JSON file to with instructions to update the user profiles /// Boolean indicating if only the mappings file should be created and uploaded to SharePoint Online (true) or if the import job on that file should also be invoked (false) /// Information on the status of the import job that has been created because of this action - public static async Task SyncFromAzureActiveDirectory(ClientContext clientContext, IEnumerable users, Hashtable userProfilePropertyMappings, string sharePointFolder, bool onlyCreateAndUploadMappingsFile = false) + public static async Task SyncFromAzureActiveDirectory(ClientContext clientContext, IEnumerable users, ImportProfilePropertiesUserIdType idType, Hashtable userProfilePropertyMappings, string sharePointFolder, bool onlyCreateAndUploadMappingsFile = false) { var webServerRelativeUrl = clientContext.Web.EnsureProperty(w => w.ServerRelativeUrl); if (!sharePointFolder.ToLower().StartsWith(webServerRelativeUrl)) @@ -72,12 +73,29 @@ public static async Task SyncFromAzureActiveDir } } + // If there are properties to update for this user, add the IdName property for this user and the fields to update to the mapping output if(userUpdateBuilder.Length > 0) { bulkUpdateBuilder.Append(@"{""IdName"":"""); - bulkUpdateBuilder.Append(user.UserPrincipalName); + + // Map the proper IdType property based on the IdType that we need to use to identify the user by + switch(idType) + { + case ImportProfilePropertiesUserIdType.CloudId: + bulkUpdateBuilder.Append(user.Id); + break; + + case ImportProfilePropertiesUserIdType.Email: + bulkUpdateBuilder.Append(user.Mail); + break; + + case ImportProfilePropertiesUserIdType.PrincipalName: + bulkUpdateBuilder.Append(user.UserPrincipalName); + break; + } + bulkUpdateBuilder.Append(@""","); - bulkUpdateBuilder.Append(userUpdateBuilder.ToString().TrimEnd(',').Replace(@"\", @"\\")); + bulkUpdateBuilder.Append(userUpdateBuilder.ToString().TrimEnd(',')).Replace(@"\", @"\\"); bulkUpdateBuilder.Append("},"); userUpdateBuilder.Clear(); @@ -124,7 +142,7 @@ public static async Task SyncFromAzureActiveDir var o365 = new Office365Tenant(clientContext); var propDictionary = userProfilePropertyMappings.Cast().ToDictionary(kvp => (string)kvp.Key, kvp => (string)kvp.Key); var url = new Uri(clientContext.Url).GetLeftPart(UriPartial.Authority) + file.ServerRelativeUrl; - var id = o365.QueueImportProfileProperties(ImportProfilePropertiesUserIdType.PrincipalName, "IdName", propDictionary, url); + var id = o365.QueueImportProfileProperties(idType, "IdName", propDictionary, url); clientContext.ExecuteQueryRetry(); // Retrieve the import json details From 87a078357b712f5681fb762dc42667ba9762513a Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 6 Apr 2022 16:30:41 +0200 Subject: [PATCH 064/458] Minor documentatipn updates --- documentation/New-PnPUPABulkImportJob.md | 2 +- ...ync-PnPSharePointUserProfilesFromAzureActiveDirectory.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/documentation/New-PnPUPABulkImportJob.md b/documentation/New-PnPUPABulkImportJob.md index 91c9a9818..09fd64f60 100644 --- a/documentation/New-PnPUPABulkImportJob.md +++ b/documentation/New-PnPUPABulkImportJob.md @@ -174,7 +174,7 @@ Accept wildcard characters: False ``` ### -Wait -Adding this parameter will cause the script to start the user profile sync operation and wait with proceeding with the rest of the script until the user profiles have been imported into the SharePoint Online user profile. It can take a long time for the user profile sync operation to complete. It will check every 30 seconds for the current status of the job, to avoid getting throttled. This retry value is non configurable. +Adding this parameter will cause the script to start the user profile sync operation and wait with proceeding with the rest of the script until the user profiles have been imported into the SharePoint Online user profile. It can take a long time for the user profile sync operation to complete. It will check every 30 seconds for the current status of the job, to avoid getting throttled. The check interval is non configurable. Add `-Verbose` as well to be notified about the progress while waiting. diff --git a/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md b/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md index 1cedfb646..37f2a5994 100644 --- a/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md +++ b/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md @@ -131,7 +131,7 @@ Accept wildcard characters: False ``` ### -IdType -The type of profile identifier (Email/CloudId/PrincipalName). Defaults to CloudId. Ensure that if you use this in combination with `-Users` that all of the user objects you're passing in are having their Mail property populated when choosing IdType Email, Id property for CloudId or UserPrincipalName for PrincipalName. +The type of profile identifier (Email/CloudId/PrincipalName). Defaults to CloudId. Ensure that if you use this in combination with `-Users` that all of the user objects you're passing in are having their Mail property populated when choosing IdType Email, Id property for IdType CloudId or UserPrincipalName for IdType PrincipalName. ```yaml Type: ImportProfilePropertiesUserIdType @@ -160,7 +160,7 @@ Accept wildcard characters: False ``` ### -Wait -Adding this parameter will cause the script to start the user profile sync operation and wait with proceeding with the rest of the script until the user profiles have been imported into the SharePoint Online user profile. It can take a long time for the user profile sync operation to complete. It will check every 30 seconds for the current status of the job, to avoid getting throttled. This retry value is non configurable. +Adding this parameter will cause the script to start the user profile sync operation and wait with proceeding with the rest of the script until the user profiles have been imported into the SharePoint Online user profile. It can take a long time for the user profile sync operation to complete. It will check every 30 seconds for the current status of the job, to avoid getting throttled. The check interval is non configurable. Add `-Verbose` as well to be notified about the progress while waiting. @@ -191,4 +191,4 @@ Accept wildcard characters: False ## RELATED LINKS -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file From 91e76d9591447478b8f63e8de5748e3b4b632236 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 6 Apr 2022 17:25:38 +0200 Subject: [PATCH 065/458] Adding changelog entry --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index df30c94cd..a73cce18d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ## [Current Nightly] ### Added +- Added -Wait and -Verbose optional paramarers to New-PnPUPABulkImportJob ### Changed +- Changed Sync-PnPSharePointUserProfilesFromAzureActiveDirectory to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new -IdType option to switch it back to PrincipalName if needed. ### Fixed @@ -17,6 +19,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Contributors +- Koen Zomers [koenzomers] + ## [1.10.0] ### Added From 5a26eb04aa0070fb8c82b3f527df141c775f3854 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 5 Apr 2022 15:18:59 +0200 Subject: [PATCH 066/458] Work in progress --- documentation/New-PnPUPABulkImportJob.md | 4 ++++ .../Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/documentation/New-PnPUPABulkImportJob.md b/documentation/New-PnPUPABulkImportJob.md index 09fd64f60..f9479c873 100644 --- a/documentation/New-PnPUPABulkImportJob.md +++ b/documentation/New-PnPUPABulkImportJob.md @@ -174,7 +174,11 @@ Accept wildcard characters: False ``` ### -Wait +<<<<<<< HEAD Adding this parameter will cause the script to start the user profile sync operation and wait with proceeding with the rest of the script until the user profiles have been imported into the SharePoint Online user profile. It can take a long time for the user profile sync operation to complete. It will check every 30 seconds for the current status of the job, to avoid getting throttled. The check interval is non configurable. +======= +Adding this parameter will cause the script to start the user profile sync operation and wait with proceeding with the rest of the script until the user profiles have been imported into the SharePoint Online user profile. It can take a long time for the user profile sync operation to complete. It will check every 30 seconds for the current status of the job, to avoid getting throttled. This retry value is non configurable. +>>>>>>> b75bab61... Work in progress Add `-Verbose` as well to be notified about the progress while waiting. diff --git a/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md b/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md index 37f2a5994..a348f30e0 100644 --- a/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md +++ b/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md @@ -131,7 +131,11 @@ Accept wildcard characters: False ``` ### -IdType +<<<<<<< HEAD The type of profile identifier (Email/CloudId/PrincipalName). Defaults to CloudId. Ensure that if you use this in combination with `-Users` that all of the user objects you're passing in are having their Mail property populated when choosing IdType Email, Id property for IdType CloudId or UserPrincipalName for IdType PrincipalName. +======= +The type of profile identifier (Email/CloudId/PrincipalName). Defaults to CloudId. Ensure that if you use this in combination with `-Users` that all of the user objects you're passing in are having their Mail property populated when choosing IdType Email, Id property for CloudId or UserPrincipalName for PrincipalName. +>>>>>>> b75bab61... Work in progress ```yaml Type: ImportProfilePropertiesUserIdType From 55078ef079a318ac9d5dc71c9663b83c60c49344 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 6 Apr 2022 17:27:57 +0200 Subject: [PATCH 067/458] Resolving conflicts --- documentation/New-PnPUPABulkImportJob.md | 4 ++++ .../Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/documentation/New-PnPUPABulkImportJob.md b/documentation/New-PnPUPABulkImportJob.md index f9479c873..279763b5f 100644 --- a/documentation/New-PnPUPABulkImportJob.md +++ b/documentation/New-PnPUPABulkImportJob.md @@ -175,10 +175,14 @@ Accept wildcard characters: False ### -Wait <<<<<<< HEAD +<<<<<<< HEAD Adding this parameter will cause the script to start the user profile sync operation and wait with proceeding with the rest of the script until the user profiles have been imported into the SharePoint Online user profile. It can take a long time for the user profile sync operation to complete. It will check every 30 seconds for the current status of the job, to avoid getting throttled. The check interval is non configurable. ======= Adding this parameter will cause the script to start the user profile sync operation and wait with proceeding with the rest of the script until the user profiles have been imported into the SharePoint Online user profile. It can take a long time for the user profile sync operation to complete. It will check every 30 seconds for the current status of the job, to avoid getting throttled. This retry value is non configurable. >>>>>>> b75bab61... Work in progress +======= +Adding this parameter will cause the script to start the user profile sync operation and wait with proceeding with the rest of the script until the user profiles have been imported into the SharePoint Online user profile. It can take a long time for the user profile sync operation to complete. It will check every 30 seconds for the current status of the job, to avoid getting throttled. The check interval is non configurable. +>>>>>>> 813e9972... Minor documentatipn updates Add `-Verbose` as well to be notified about the progress while waiting. diff --git a/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md b/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md index a348f30e0..add6b35a3 100644 --- a/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md +++ b/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md @@ -132,10 +132,14 @@ Accept wildcard characters: False ### -IdType <<<<<<< HEAD +<<<<<<< HEAD The type of profile identifier (Email/CloudId/PrincipalName). Defaults to CloudId. Ensure that if you use this in combination with `-Users` that all of the user objects you're passing in are having their Mail property populated when choosing IdType Email, Id property for IdType CloudId or UserPrincipalName for IdType PrincipalName. ======= The type of profile identifier (Email/CloudId/PrincipalName). Defaults to CloudId. Ensure that if you use this in combination with `-Users` that all of the user objects you're passing in are having their Mail property populated when choosing IdType Email, Id property for CloudId or UserPrincipalName for PrincipalName. >>>>>>> b75bab61... Work in progress +======= +The type of profile identifier (Email/CloudId/PrincipalName). Defaults to CloudId. Ensure that if you use this in combination with `-Users` that all of the user objects you're passing in are having their Mail property populated when choosing IdType Email, Id property for IdType CloudId or UserPrincipalName for IdType PrincipalName. +>>>>>>> 813e9972... Minor documentatipn updates ```yaml Type: ImportProfilePropertiesUserIdType From a0db851112519d698c97e3a1a17e18ccf9b94c07 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 6 Apr 2022 17:25:38 +0200 Subject: [PATCH 068/458] Adding changelog entry --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index df30c94cd..a73cce18d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ## [Current Nightly] ### Added +- Added -Wait and -Verbose optional paramarers to New-PnPUPABulkImportJob ### Changed +- Changed Sync-PnPSharePointUserProfilesFromAzureActiveDirectory to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new -IdType option to switch it back to PrincipalName if needed. ### Fixed @@ -17,6 +19,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Contributors +- Koen Zomers [koenzomers] + ## [1.10.0] ### Added From 1beb2395f27291fc79b2d1fe0f482ae0b37751ca Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 6 Apr 2022 17:29:29 +0200 Subject: [PATCH 069/458] Update CHANGELOG.md Added missing PR reference --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a73cce18d..7417489c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,10 +8,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ## [Current Nightly] ### Added -- Added -Wait and -Verbose optional paramarers to New-PnPUPABulkImportJob +- Added -Wait and -Verbose optional paramarers to New-PnPUPABulkImportJob [#1752](https://github.com/pnp/powershell/pull/1752) ### Changed -- Changed Sync-PnPSharePointUserProfilesFromAzureActiveDirectory to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new -IdType option to switch it back to PrincipalName if needed. +- Changed Sync-PnPSharePointUserProfilesFromAzureActiveDirectory to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new -IdType option to switch it back to PrincipalName if needed. [#1752](https://github.com/pnp/powershell/pull/1752) ### Fixed From c8263527ce721f3a6d67bdaf3002b808ccba8d30 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 6 Apr 2022 17:30:12 +0200 Subject: [PATCH 070/458] Update CHANGELOG.md Added missing backticks --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7417489c6..355e78221 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,10 +8,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ## [Current Nightly] ### Added -- Added -Wait and -Verbose optional paramarers to New-PnPUPABulkImportJob [#1752](https://github.com/pnp/powershell/pull/1752) +- Added `-Wait` and `-Verbose` optional paramarers to `New-PnPUPABulkImportJob` [#1752](https://github.com/pnp/powershell/pull/1752) ### Changed -- Changed Sync-PnPSharePointUserProfilesFromAzureActiveDirectory to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new -IdType option to switch it back to PrincipalName if needed. [#1752](https://github.com/pnp/powershell/pull/1752) +- Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) ### Fixed From 91519fee1c204a18dc80aeefe38942cc356b8295 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 6 Apr 2022 17:32:27 +0200 Subject: [PATCH 071/458] Update CHANGELOG.md Adding contributor recognition for PowershellScripts --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 355e78221..070edeea3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Contributors +- Arleta Wanat [PowershellScripts] - Koen Zomers [koenzomers] ## [1.10.0] From e816dc17e4f9da8b5752281b55a9e155db2ecec8 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 6 Apr 2022 17:36:01 +0200 Subject: [PATCH 072/458] Update New-PnPUPABulkImportJob.md Fixing build error --- documentation/New-PnPUPABulkImportJob.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/documentation/New-PnPUPABulkImportJob.md b/documentation/New-PnPUPABulkImportJob.md index 279763b5f..09fd64f60 100644 --- a/documentation/New-PnPUPABulkImportJob.md +++ b/documentation/New-PnPUPABulkImportJob.md @@ -174,15 +174,7 @@ Accept wildcard characters: False ``` ### -Wait -<<<<<<< HEAD -<<<<<<< HEAD Adding this parameter will cause the script to start the user profile sync operation and wait with proceeding with the rest of the script until the user profiles have been imported into the SharePoint Online user profile. It can take a long time for the user profile sync operation to complete. It will check every 30 seconds for the current status of the job, to avoid getting throttled. The check interval is non configurable. -======= -Adding this parameter will cause the script to start the user profile sync operation and wait with proceeding with the rest of the script until the user profiles have been imported into the SharePoint Online user profile. It can take a long time for the user profile sync operation to complete. It will check every 30 seconds for the current status of the job, to avoid getting throttled. This retry value is non configurable. ->>>>>>> b75bab61... Work in progress -======= -Adding this parameter will cause the script to start the user profile sync operation and wait with proceeding with the rest of the script until the user profiles have been imported into the SharePoint Online user profile. It can take a long time for the user profile sync operation to complete. It will check every 30 seconds for the current status of the job, to avoid getting throttled. The check interval is non configurable. ->>>>>>> 813e9972... Minor documentatipn updates Add `-Verbose` as well to be notified about the progress while waiting. From c09d5f44bc79cce6953ac7acb32f2a1d774b13b3 Mon Sep 17 00:00:00 2001 From: Mikael Svenson Date: Thu, 7 Apr 2022 13:14:58 +0200 Subject: [PATCH 073/458] Change value --- documentation/Get-PnPSearchConfiguration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Get-PnPSearchConfiguration.md b/documentation/Get-PnPSearchConfiguration.md index 532b235ee..105b995e9 100644 --- a/documentation/Get-PnPSearchConfiguration.md +++ b/documentation/Get-PnPSearchConfiguration.md @@ -182,7 +182,7 @@ Parameter Sets: CSV Required: False Position: Named -Default value: True +Default value: None Accept pipeline input: False Accept wildcard characters: False From 98410fc3432122e08d2b0fbedeeb6cedb9175fcd Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Thu, 7 Apr 2022 15:54:08 +0200 Subject: [PATCH 074/458] Cleaned up error in md file --- ...c-PnPSharePointUserProfilesFromAzureActiveDirectory.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md b/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md index add6b35a3..37f2a5994 100644 --- a/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md +++ b/documentation/Sync-PnPSharePointUserProfilesFromAzureActiveDirectory.md @@ -131,15 +131,7 @@ Accept wildcard characters: False ``` ### -IdType -<<<<<<< HEAD -<<<<<<< HEAD The type of profile identifier (Email/CloudId/PrincipalName). Defaults to CloudId. Ensure that if you use this in combination with `-Users` that all of the user objects you're passing in are having their Mail property populated when choosing IdType Email, Id property for IdType CloudId or UserPrincipalName for IdType PrincipalName. -======= -The type of profile identifier (Email/CloudId/PrincipalName). Defaults to CloudId. Ensure that if you use this in combination with `-Users` that all of the user objects you're passing in are having their Mail property populated when choosing IdType Email, Id property for CloudId or UserPrincipalName for PrincipalName. ->>>>>>> b75bab61... Work in progress -======= -The type of profile identifier (Email/CloudId/PrincipalName). Defaults to CloudId. Ensure that if you use this in combination with `-Users` that all of the user objects you're passing in are having their Mail property populated when choosing IdType Email, Id property for IdType CloudId or UserPrincipalName for IdType PrincipalName. ->>>>>>> 813e9972... Minor documentatipn updates ```yaml Type: ImportProfilePropertiesUserIdType From fa726807ba90640e93256c9d06e2974b30f235d9 Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Thu, 7 Apr 2022 16:01:18 +0200 Subject: [PATCH 075/458] Update Get-PnPSearchConfiguration.md --- documentation/Get-PnPSearchConfiguration.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/Get-PnPSearchConfiguration.md b/documentation/Get-PnPSearchConfiguration.md index 105b995e9..1e6797a77 100644 --- a/documentation/Get-PnPSearchConfiguration.md +++ b/documentation/Get-PnPSearchConfiguration.md @@ -158,9 +158,9 @@ Output promoted results to a compatible CSV file to be used as Bookmark import a Export details: -* Promoted results marked as "Render the URL as a banner instead of as a hyperlink" (visual promoted results) and query rules with no triggers will be skipped by default. -* Triggers set to "Advanced Query Text Match" and "Query Contains Action Term" will have "Match Similar Keywords" set to true for the Bookmark. -* Multiple triggers on a query rule will be merged into a single trigger. +- Promoted results marked as "Render the URL as a banner instead of as a hyperlink" (visual promoted results) and query rules with no triggers will be skipped by default. +- Triggers set to "Advanced Query Text Match" and "Query Contains Action Term" will have "Match Similar Keywords" set to true for the Bookmark. +- Multiple triggers on a query rule will be merged into a single trigger. ```yaml Type: SwitchParameter From 0f1550df47a9217a6dffb64f4bdc09393e75cce4 Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Thu, 7 Apr 2022 16:05:42 +0200 Subject: [PATCH 076/458] Update Get-PnPSearchConfiguration.md --- documentation/Get-PnPSearchConfiguration.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/documentation/Get-PnPSearchConfiguration.md b/documentation/Get-PnPSearchConfiguration.md index 1e6797a77..786ad76eb 100644 --- a/documentation/Get-PnPSearchConfiguration.md +++ b/documentation/Get-PnPSearchConfiguration.md @@ -158,14 +158,13 @@ Output promoted results to a compatible CSV file to be used as Bookmark import a Export details: -- Promoted results marked as "Render the URL as a banner instead of as a hyperlink" (visual promoted results) and query rules with no triggers will be skipped by default. -- Triggers set to "Advanced Query Text Match" and "Query Contains Action Term" will have "Match Similar Keywords" set to true for the Bookmark. -- Multiple triggers on a query rule will be merged into a single trigger. +* Promoted results marked as "Render the URL as a banner instead of as a hyperlink" (visual promoted results) and query rules with no triggers will be skipped by default. +* Triggers set to "Advanced Query Text Match" and "Query Contains Action Term" will have "Match Similar Keywords" set to true for the Bookmark. +* Multiple triggers on a query rule will be merged into a single trigger. ```yaml Type: SwitchParameter Parameter Sets: CSV - Required: False Position: Named Default value: None From e94c418b0c49814a7112415caad53f1ea0e973cf Mon Sep 17 00:00:00 2001 From: Mikael Svenson Date: Thu, 7 Apr 2022 16:09:25 +0200 Subject: [PATCH 077/458] Trying to find yaml build issue --- documentation/Get-PnPSearchConfiguration.md | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/documentation/Get-PnPSearchConfiguration.md b/documentation/Get-PnPSearchConfiguration.md index 1e6797a77..4764cd74d 100644 --- a/documentation/Get-PnPSearchConfiguration.md +++ b/documentation/Get-PnPSearchConfiguration.md @@ -173,19 +173,6 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -ExcludeVisualPromotedResults -Exclude promoted results marked as "Render the URL as a banner instead of as a hyperlink". Defaults to true. - -```yaml -Type: Boolean -Parameter Sets: CSV - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False - ### -BookmarkStatus Output bookmarks to be in suggested or published status upon CSV import. Defaults to suggested status. @@ -204,4 +191,3 @@ Accept wildcard characters: False ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - From 591b7dc12b5afab4c25223c33a33b9942eabcca4 Mon Sep 17 00:00:00 2001 From: Mikael Svenson Date: Thu, 7 Apr 2022 16:12:04 +0200 Subject: [PATCH 078/458] Closing yaml section --- documentation/Get-PnPSearchConfiguration.md | 46 +++++++++++++++++---- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/documentation/Get-PnPSearchConfiguration.md b/documentation/Get-PnPSearchConfiguration.md index 69a24de2b..dd5301431 100644 --- a/documentation/Get-PnPSearchConfiguration.md +++ b/documentation/Get-PnPSearchConfiguration.md @@ -6,38 +6,42 @@ applicable: SharePoint Online external help file: PnP.PowerShell.dll-Help.xml online version: https://pnp.github.io/powershell/cmdlets/Get-PnPSearchConfiguration.html --- - + # Get-PnPSearchConfiguration ## SYNOPSIS + Returns the search configuration ## SYNTAX ### Xml (Default) + ```powershell -Get-PnPSearchConfiguration [-Scope ] [-Path ] +Get-PnPSearchConfiguration [-Scope ] [-Path ] [-Connection ] [] ``` ### OutputFormat + ```powershell Get-PnPSearchConfiguration [-Scope ] [-OutputFormat ] [-Connection ] [] ``` ### BookmarksCSV + ```powershell Get-PnPSearchConfiguration [-Scope ] [-PromotedResultsToBookmarkCSV] [-ExcludeVisualPromotedResults ] [-BookmarkStatus ] [-Path ] [-Connection ] [] ``` - ## DESCRIPTION ## EXAMPLES ### EXAMPLE 1 + ```powershell Get-PnPSearchConfiguration ``` @@ -45,6 +49,7 @@ Get-PnPSearchConfiguration Returns the search configuration for the current web. ### EXAMPLE 2 + ```powershell Get-PnPSearchConfiguration -Scope Site ``` @@ -52,6 +57,7 @@ Get-PnPSearchConfiguration -Scope Site Returns the search configuration for the current site collection. ### EXAMPLE 3 + ```powershell Get-PnPSearchConfiguration -Scope Subscription ``` @@ -59,6 +65,7 @@ Get-PnPSearchConfiguration -Scope Subscription Returns the search configuration for the current tenant. ### EXAMPLE 4 + ```powershell Get-PnPSearchConfiguration -Path searchconfig.xml -Scope Subscription ``` @@ -66,6 +73,7 @@ Get-PnPSearchConfiguration -Path searchconfig.xml -Scope Subscription Returns the search configuration for the current tenant and saves it to the specified file. ### EXAMPLE 5 + ```powershell Get-PnPSearchConfiguration -Scope Site -OutputFormat ManagedPropertyMappings ``` @@ -73,6 +81,7 @@ Get-PnPSearchConfiguration -Scope Site -OutputFormat ManagedPropertyMappings Returns all custom managed properties and crawled property mapping at the current site collection. ### EXAMPLE 6 + ```powershell Get-PnPSearchConfiguration -Scope Site -PromotedResultsToBookmarkCSV -Path bookmarks.csv ``` @@ -80,6 +89,7 @@ Get-PnPSearchConfiguration -Scope Site -PromotedResultsToBookmarkCSV -Path bookm Export promoted results excluding visual ones from query rules on the site collection as a CSV file with the bookmarks in suggested status. ### EXAMPLE 7 + ```powershell Get-PnPSearchConfiguration -Scope Site -PromotedResultsToBookmarkCSV -Path bookmarks.csv -BookmarkStatus Published ``` @@ -87,6 +97,7 @@ Get-PnPSearchConfiguration -Scope Site -PromotedResultsToBookmarkCSV -Path bookm Export promoted results excluding visual from query rules on the site collection as a CSV file with the bookmarks in published status. ### EXAMPLE 8 + ```powershell Get-PnPSearchConfiguration -Scope Subscription -PromotedResultsToBookmarkCSV -ExcludeVisualPromotedResults $false ``` @@ -96,6 +107,7 @@ Export promoted results including visual ones from query rules on the tenant in ## PARAMETERS ### -Connection + Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. ```yaml @@ -110,6 +122,7 @@ Accept wildcard characters: False ``` ### -OutputFormat + Output format for of the configuration. Defaults to complete XML ```yaml @@ -125,6 +138,7 @@ Accept wildcard characters: False ``` ### -Path + Local path where the search configuration will be saved ```yaml @@ -139,6 +153,7 @@ Accept wildcard characters: False ``` ### -Scope + Scope to use. Either Web, Site, or Subscription. Defaults to Web ```yaml @@ -154,17 +169,19 @@ Accept wildcard characters: False ``` ### -PromotedResultsToBookmarkCSV + Output promoted results to a compatible CSV file to be used as Bookmark import at https://admin.microsoft.com/#/MicrosoftSearch/bookmarks. Export details: -* Promoted results marked as "Render the URL as a banner instead of as a hyperlink" (visual promoted results) and query rules with no triggers will be skipped by default. -* Triggers set to "Advanced Query Text Match" and "Query Contains Action Term" will have "Match Similar Keywords" set to true for the Bookmark. -* Multiple triggers on a query rule will be merged into a single trigger. +- Promoted results marked as "Render the URL as a banner instead of as a hyperlink" (visual promoted results) and query rules with no triggers will be skipped by default. +- Triggers set to "Advanced Query Text Match" and "Query Contains Action Term" will have "Match Similar Keywords" set to true for the Bookmark. +- Multiple triggers on a query rule will be merged into a single trigger. ```yaml Type: SwitchParameter Parameter Sets: CSV + Required: False Position: Named Default value: None @@ -172,6 +189,21 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -ExcludeVisualPromotedResults + +Exclude promoted results marked as "Render the URL as a banner instead of as a hyperlink". Defaults to true. + +````yaml +Type: Boolean +Parameter Sets: CSV + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +```` + ### -BookmarkStatus Output bookmarks to be in suggested or published status upon CSV import. Defaults to suggested status. @@ -185,7 +217,7 @@ Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False -``` +```` ## RELATED LINKS From 186b7ceded6436e3869317fbb6234e5197980d82 Mon Sep 17 00:00:00 2001 From: Mikael Svenson Date: Thu, 7 Apr 2022 16:15:34 +0200 Subject: [PATCH 079/458] Fixed ticks --- documentation/Get-PnPSearchConfiguration.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/Get-PnPSearchConfiguration.md b/documentation/Get-PnPSearchConfiguration.md index dd5301431..0ef4fd7de 100644 --- a/documentation/Get-PnPSearchConfiguration.md +++ b/documentation/Get-PnPSearchConfiguration.md @@ -193,7 +193,7 @@ Accept wildcard characters: False Exclude promoted results marked as "Render the URL as a banner instead of as a hyperlink". Defaults to true. -````yaml +```yaml Type: Boolean Parameter Sets: CSV @@ -202,7 +202,7 @@ Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False -```` +``` ### -BookmarkStatus Output bookmarks to be in suggested or published status upon CSV import. Defaults to suggested status. @@ -217,7 +217,7 @@ Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False -```` +``` ## RELATED LINKS From f58343d0206191f572ca098a04801c17d16a4e66 Mon Sep 17 00:00:00 2001 From: Mikael Svenson Date: Fri, 8 Apr 2022 03:23:31 +0000 Subject: [PATCH 080/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index 64fd6e48a..27539c80c 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -f4de8051073ca3b4a1a87cfaf84908f3ec892196 \ No newline at end of file +87d90fe8523d9ce8431f72c9562cb1369111b910 \ No newline at end of file diff --git a/version.txt b/version.txt index c67978766..819d3b1fc 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.7 \ No newline at end of file +1.10.8 \ No newline at end of file From f968a0f62b1788d9675d7ca0c3c3a9ff5043a5ee Mon Sep 17 00:00:00 2001 From: Mikael Svenson Date: Sat, 9 Apr 2022 03:11:35 +0000 Subject: [PATCH 081/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 5650eb255..1a9b15770 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -ecadf5ff9e791d25ee795f5c0fbe2128cb131d49 \ No newline at end of file +683f618a5e58592e0b98d5664b18038d71e65f7f \ No newline at end of file diff --git a/version.txt b/version.txt index 819d3b1fc..0203dc6b9 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.8 \ No newline at end of file +1.10.9 \ No newline at end of file From b74481fbcc50af8406b5d447abac2c696854c229 Mon Sep 17 00:00:00 2001 From: Mikael Svenson Date: Sun, 10 Apr 2022 03:13:39 +0000 Subject: [PATCH 082/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 1a9b15770..7176363b2 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -683f618a5e58592e0b98d5664b18038d71e65f7f \ No newline at end of file +6fedc38ae731147d2ff35f4e003627296c6bead4 \ No newline at end of file diff --git a/version.txt b/version.txt index 0203dc6b9..58084a6aa 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.9 \ No newline at end of file +1.10.10 \ No newline at end of file From 2085d3685602784723f2bcf2da9b7c74f2003ab4 Mon Sep 17 00:00:00 2001 From: Mikael Svenson Date: Mon, 11 Apr 2022 03:30:58 +0000 Subject: [PATCH 083/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 7176363b2..9d510bddc 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -6fedc38ae731147d2ff35f4e003627296c6bead4 \ No newline at end of file +25d1189264a2be9c6df0c1e190c33aa387aa8f25 \ No newline at end of file diff --git a/version.txt b/version.txt index 58084a6aa..4f788a92f 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.10 \ No newline at end of file +1.10.11 \ No newline at end of file From a1c8b1c655670aa739e487a3fd30121b5c8b7518 Mon Sep 17 00:00:00 2001 From: James May Date: Mon, 11 Apr 2022 23:15:08 +1000 Subject: [PATCH 084/458] PrincipalCmdLets: add OutputType attribute --- src/Commands/Principals/AddAlert.cs | 3 +- src/Commands/Principals/GetAlert.cs | 1 + src/Commands/Principals/GetGroup.cs | 1 + src/Commands/Principals/GetGroupMember.cs | 1 + .../Principals/GetGroupPermissions.cs | 2 ++ src/Commands/Principals/GetSiteGroup.cs | 1 + src/Commands/Principals/GetUser.cs | 32 +++++++++++++++---- src/Commands/Principals/NewGroup.cs | 1 + src/Commands/Principals/NewSiteGroup.cs | 2 +- src/Commands/Principals/NewUser.cs | 1 + src/Commands/Principals/SetSiteGroup.cs | 2 +- 11 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/Commands/Principals/AddAlert.cs b/src/Commands/Principals/AddAlert.cs index d5c6b041e..52219660a 100644 --- a/src/Commands/Principals/AddAlert.cs +++ b/src/Commands/Principals/AddAlert.cs @@ -9,6 +9,7 @@ namespace PnP.PowerShell.Commands.Principals { [Cmdlet(VerbsCommon.Add, "PnPAlert")] + [OutputType(typeof(AlertCreationInformation))] public class AddAlert : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] @@ -89,7 +90,7 @@ protected override void ExecuteCmdlet() { alert.AlertTime = Time; } - + user.Alerts.Add(alert); ClientContext.ExecuteQueryRetry(); WriteObject(alert); diff --git a/src/Commands/Principals/GetAlert.cs b/src/Commands/Principals/GetAlert.cs index 5070a9c29..7c13cecd5 100644 --- a/src/Commands/Principals/GetAlert.cs +++ b/src/Commands/Principals/GetAlert.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands.Principals { [Cmdlet(VerbsCommon.Get, "PnPAlert", DefaultParameterSetName = ParameterSet_SPECIFICUSER)] + [OutputType(typeof(Alert))] public class GetAlert : PnPWebCmdlet { private const string ParameterSet_SPECIFICUSER = "Alerts for a specific user"; diff --git a/src/Commands/Principals/GetGroup.cs b/src/Commands/Principals/GetGroup.cs index 69e107369..3c2c8a1df 100644 --- a/src/Commands/Principals/GetGroup.cs +++ b/src/Commands/Principals/GetGroup.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.Principals { [Cmdlet(VerbsCommon.Get, "PnPGroup", DefaultParameterSetName = "All")] + [OutputType(typeof(Group))] public class GetGroup : PnPWebRetrievalsCmdlet { [Parameter(Mandatory = false, Position = 0, ValueFromPipeline = true, ParameterSetName = "ByName")] diff --git a/src/Commands/Principals/GetGroupMember.cs b/src/Commands/Principals/GetGroupMember.cs index ca565fd14..205b1cee4 100644 --- a/src/Commands/Principals/GetGroupMember.cs +++ b/src/Commands/Principals/GetGroupMember.cs @@ -5,6 +5,7 @@ namespace PnP.PowerShell.Commands.Principals { [Cmdlet(VerbsCommon.Get, "PnPGroupMember")] + [OutputType(typeof(Microsoft.SharePoint.Client.User))] public class GetGroupMembers : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true)] diff --git a/src/Commands/Principals/GetGroupPermissions.cs b/src/Commands/Principals/GetGroupPermissions.cs index 88d8f503b..b52c18d8f 100644 --- a/src/Commands/Principals/GetGroupPermissions.cs +++ b/src/Commands/Principals/GetGroupPermissions.cs @@ -1,11 +1,13 @@ using System.Management.Automation; using Microsoft.SharePoint.Client; +using PnP.Core.Model.Security; using PnP.PowerShell.Commands.Base.PipeBinds; namespace PnP.PowerShell.Commands.Principals { [Cmdlet(VerbsCommon.Get, "PnPGroupPermissions")] + [OutputType(typeof(IRoleDefinition))] public class GetGroupPermissions : PnPWebCmdlet { [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true)] diff --git a/src/Commands/Principals/GetSiteGroup.cs b/src/Commands/Principals/GetSiteGroup.cs index 34ba33683..264f0ceb6 100644 --- a/src/Commands/Principals/GetSiteGroup.cs +++ b/src/Commands/Principals/GetSiteGroup.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.Principals { [Cmdlet(VerbsCommon.Get, "PnPSiteGroup", DefaultParameterSetName = "All")] + [OutputType(typeof(SiteGroup))] public class GetSiteGroup : PnPAdminCmdlet { [Parameter(Mandatory = false)] diff --git a/src/Commands/Principals/GetUser.cs b/src/Commands/Principals/GetUser.cs index 2fa5193d4..11e25eed0 100644 --- a/src/Commands/Principals/GetUser.cs +++ b/src/Commands/Principals/GetUser.cs @@ -12,6 +12,8 @@ namespace PnP.PowerShell.Commands.Principals { [Cmdlet(VerbsCommon.Get, "PnPUser", DefaultParameterSetName = PARAMETERSET_IDENTITY)] + [OutputType(typeof(User), ParameterSetName = new[] { PARAMETERSET_IDENTITY, PARAMETERSET_WITHRIGHTSASSIGNED })] + [OutputType(typeof(UserWithRightsAssignedDetailed), ParameterSetName = new[] { PARAMETERSET_WITHRIGHTSASSIGNEDDETAILED })] public class GetUser : PnPWebRetrievalsCmdlet { private const string PARAMETERSET_IDENTITY = "Identity based request"; @@ -122,7 +124,7 @@ protected override void ExecuteCmdlet() if (WithRightsAssignedDetailed) { - CurrentWeb.Context.Load(CurrentWeb.Lists, l => l.Include(li => li.ItemCount, li => li.IsSystemList, li=>li.IsCatalog, li => li.RootFolder.ServerRelativeUrl, li => li.RoleAssignments, li => li.Title, li => li.HasUniqueRoleAssignments)); + CurrentWeb.Context.Load(CurrentWeb.Lists, l => l.Include(li => li.ItemCount, li => li.IsSystemList, li => li.IsCatalog, li => li.RootFolder.ServerRelativeUrl, li => li.RoleAssignments, li => li.Title, li => li.HasUniqueRoleAssignments)); CurrentWeb.Context.ExecuteQueryRetry(); var progress = new ProgressRecord(0, $"Getting lists for {CurrentWeb.ServerRelativeUrl}", "Enumerating through lists"); @@ -237,8 +239,8 @@ protected override void ExecuteCmdlet() { // Getting all the assigned permissions per user var userPermissions = (from u in users - where u.User.LoginName == uniqueUser && u.Permissions != null - select u).ToList(); + where u.User.LoginName == uniqueUser && u.Permissions != null + select u).ToList(); // Making the permissions readable by getting the name of the permission and the URL of the artifact Dictionary Permissions = new Dictionary(); @@ -254,8 +256,8 @@ protected override void ExecuteCmdlet() // Getting all the groups where the user is added to var groupsMemberships = (from u in users - where u.User.LoginName == uniqueUser && u.Groups != null - select u.Groups).ToList(); + where u.User.LoginName == uniqueUser && u.Groups != null + select u.Groups).ToList(); // Getting the titles of the all the groups List Groups = new List(); @@ -266,13 +268,20 @@ protected override void ExecuteCmdlet() Groups.Add(group.Title); } } - + // Getting the User object of the user so we can get to the title, loginname, etc var userInformation = (from u in users where u.User.LoginName == uniqueUser select u.User).FirstOrDefault(); - WriteObject(new { userInformation.Title, userInformation.LoginName, userInformation.Email, Groups, Permissions }, true); + WriteObject(new UserWithRightsAssignedDetailed + { + Title = userInformation.Title, + LoginName = userInformation.LoginName, + Email = userInformation.Email, + Groups = Groups, + Permissions = Permissions + }); } } } @@ -322,4 +331,13 @@ private static List GetPermissions(RoleAssignmentCollection roleAs return users; } } + + public sealed class UserWithRightsAssignedDetailed + { + public string Title { get; set; } + public string LoginName { get; set; } + public string Email { get; set; } + public List Groups { get; set; } + public Dictionary Permissions { get; set; } + } } diff --git a/src/Commands/Principals/NewGroup.cs b/src/Commands/Principals/NewGroup.cs index 8e5525e80..19d54a84a 100644 --- a/src/Commands/Principals/NewGroup.cs +++ b/src/Commands/Principals/NewGroup.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.Principals { [Cmdlet(VerbsCommon.New, "PnPGroup")] + [OutputType(typeof(Group))] public class NewGroup : PnPWebCmdlet { [Parameter(Mandatory = true)] diff --git a/src/Commands/Principals/NewSiteGroup.cs b/src/Commands/Principals/NewSiteGroup.cs index b86ef7916..471ea19b3 100644 --- a/src/Commands/Principals/NewSiteGroup.cs +++ b/src/Commands/Principals/NewSiteGroup.cs @@ -9,7 +9,7 @@ namespace PnP.PowerShell.Commands.Principals { [Cmdlet(VerbsCommon.New, "PnPSiteGroup")] - + [OutputType(typeof(SiteGroup))] public class NewSiteGroup : PnPAdminCmdlet { [Parameter(Mandatory = false)] diff --git a/src/Commands/Principals/NewUser.cs b/src/Commands/Principals/NewUser.cs index d7ae6a11f..23d997f8f 100644 --- a/src/Commands/Principals/NewUser.cs +++ b/src/Commands/Principals/NewUser.cs @@ -5,6 +5,7 @@ namespace PnP.PowerShell.Commands.Principals { [Cmdlet(VerbsCommon.New, "PnPUser")] + [OutputType(typeof(User))] public class NewUser : PnPWebCmdlet { [Parameter(Mandatory = true)] diff --git a/src/Commands/Principals/SetSiteGroup.cs b/src/Commands/Principals/SetSiteGroup.cs index 37adae73d..234965526 100644 --- a/src/Commands/Principals/SetSiteGroup.cs +++ b/src/Commands/Principals/SetSiteGroup.cs @@ -9,7 +9,7 @@ namespace PnP.PowerShell.Commands.Principals { [Cmdlet(VerbsCommon.Set, "PnPSiteGroup")] - + [OutputType(typeof(SiteGroup))] public class SetSiteGroup : PnPAdminCmdlet { [Parameter(Mandatory = false)] From 80bdbdd00da05b08f3e6d578143a0f6b0eba986b Mon Sep 17 00:00:00 2001 From: Mikael Svenson Date: Tue, 12 Apr 2022 03:17:50 +0000 Subject: [PATCH 085/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 9d510bddc..b90f08990 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -25d1189264a2be9c6df0c1e190c33aa387aa8f25 \ No newline at end of file +44b31253d1ad9805ff1e35c334cc366bd929457f \ No newline at end of file diff --git a/version.txt b/version.txt index 4f788a92f..a2739ec84 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.11 \ No newline at end of file +1.10.12 \ No newline at end of file From 4e59e8790b9e37ae193c0f355b926fd7305bf2ce Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 12 Apr 2022 09:26:21 +0200 Subject: [PATCH 086/458] Update Grant-PnPAzureADAppSitePermission.md Updated documentation to clarify things --- .../Grant-PnPAzureADAppSitePermission.md | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/documentation/Grant-PnPAzureADAppSitePermission.md b/documentation/Grant-PnPAzureADAppSitePermission.md index 8cb5e60e2..128de56e1 100644 --- a/documentation/Grant-PnPAzureADAppSitePermission.md +++ b/documentation/Grant-PnPAzureADAppSitePermission.md @@ -15,7 +15,7 @@ title: Grant-PnPAzureADAppSitePermission * Microsoft Graph API: Sites.FullControl.All -Grants an Azure AD App registration, which has the "Sites.Selected" permission scope set, access to a site. +Adds permissions for a given Azure Active Directory application registration ## SYNTAX @@ -25,23 +25,23 @@ Grant-PnPAzureADAppSitePermission -AppId -DisplayName -Permissio ## DESCRIPTION -This cmdlet grants a specific Azure AD App registration access to a site. The app requires to have the "Sites.Selected" permission scope set. +This cmdlet adds permissions for a given Azure Active Directory application registration in a site collection. It is used in conjunction with the Azure Active Directory SharePoint application permission Sites.Selected. Notice that this cmdlet allows for fewer permissions compared for when updating rights through [Set-PnPAzureADAppSitePermission](Set-PnPAzureADAppSitePermission.html). If you wish to i.e. assign FullControl permissions, you need to add read or write permissions through this cmdlet first and then update it to FullControl. ## EXAMPLES ### EXAMPLE 1 ```powershell -Grant-PnPAzureADAppSitePermission -AppId "aa37b89e-75a7-47e3-bdb6-b763851c61b6" -DisplayName "TestApp" -Permissions Write +Grant-PnPAzureADAppSitePermission -AppId "aa37b89e-75a7-47e3-bdb6-b763851c61b6" -DisplayName "TestApp" -Permissions Read ``` -Grants the app with the specified ID write access to the current site that has been connected to with Connect-PnPOnline. +Adds permissions for the Azure Active Directory application registration with the specific application id and sets the rights to 'Read' access for the currently connected to site collection ### EXAMPLE 2 ```powershell Grant-PnPAzureADAppSitePermission -AppId "aa37b89e-75a7-47e3-bdb6-b763851c61b6" -DisplayName "TestApp" -Permissions Write -Site https://contoso.sharepoint.com/sites/projects ``` -Grants the app with the specified ID write access to specified site. +Adds permissions for the Azure Active Directory application registration with the specific application id and sets the rights to 'Write' access for the site collection at the provided URL ## PARAMETERS @@ -60,7 +60,7 @@ Accept wildcard characters: False ``` ### -DisplayName -The display name to set for the app in the site. +The display name to set for the application permission you're adding. Only for visual reference purposes, does not need to match the name of the application in Azure Active Directory. ```yaml Type: String @@ -74,14 +74,14 @@ Accept wildcard characters: False ``` ### -Permissions -Specifies the permissions to set for the app. +Specifies the permissions to set for the Azure Active Directory application registration which can either be Read or Write. Use [Set-PnPAzureADAppSitePermission](Set-PnPAzureADAppSitePermission.html) after initially adding these permissions to update it to Manage or FullControl permissions. ```yaml Type: String Parameter Sets: (All) Required: True -Accepted values: Write, Read +Accepted values: Read, Write Position: Named Default value: None Accept pipeline input: False @@ -89,7 +89,7 @@ Accept wildcard characters: False ``` ### -Site -Optional url to to a site to retrieve the permissions for. Defaults to the current site. +Optional url to to a site to set the permissions for. Defaults to the current site if not provided. ```yaml Type: SitePipeBind @@ -97,7 +97,7 @@ Parameter Sets: (All) Required: True Position: Named -Default value: None +Default value: Currently connected to site Accept pipeline input: False Accept wildcard characters: False ``` @@ -105,5 +105,3 @@ Accept wildcard characters: False ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - - From bb470774afcb6cfe0d5a7b609bf12cb166b8d9ef Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 12 Apr 2022 09:26:28 +0200 Subject: [PATCH 087/458] Update Set-PnPAzureADAppSitePermission.md Updated documentation to clarify things --- .../Set-PnPAzureADAppSitePermission.md | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/documentation/Set-PnPAzureADAppSitePermission.md b/documentation/Set-PnPAzureADAppSitePermission.md index b505b9882..51b46b6c0 100644 --- a/documentation/Set-PnPAzureADAppSitePermission.md +++ b/documentation/Set-PnPAzureADAppSitePermission.md @@ -15,17 +15,17 @@ title: Set-PnPAzureADAppSitePermission * Microsoft Graph API: Sites.FullControl.All -Updates permissions for a given app. +Updates permissions for a given Azure Active Directory application registration ## SYNTAX ```powershell -Set-PnPAzureADAppSitePermission -PermissionId -Permissions <"Read"|"Write"|"Manage"|"FullControl"> [-Site ] +Set-PnPAzureADAppSitePermission -PermissionId -Permissions [-Site ] ``` ## DESCRIPTION -This cmdlets updates permissions for a given app in a site. +This cmdlet updates permissions for a given Azure Active Directory application registration in a site collection. It is used in conjunction with the Azure Active Directory SharePoint application permission Sites.Selected. Notice that this cmdlet allows for more permissions compared for when initially setting rights through [Grant-PnPAzureADAppSitePermission](Grant-PnPAzureADAppSitePermission.html). ## EXAMPLES @@ -34,12 +34,19 @@ This cmdlets updates permissions for a given app in a site. Set-PnPAzureADAppSitePermission -PermissionId ABSDFefsdfef33fsdFSvsadf3e3fsdaffsa -Permissions Read ``` -Updates the app with the specific permission id and sets the rights to 'Read' access. +Updates the Azure Active Directory application registration with the specific permission id and sets the rights to 'Read' access for the currently connected to site collection + +### EXAMPLE 2 +```powershell +Set-PnPAzureADAppSitePermission -PermissionId ABSDFefsdfef33fsdFSvsadf3e3fsdaffsa -Permissions FullControl -Site https://contoso.microsoft.com/sites/projects +``` + +Updates the Azure Active Directory application registration with the specific permission id and sets the rights to 'FullControl' access for the site collection at the provided URL ## PARAMETERS ### -PermissionId -If specified the permission with that id specified will be retrieved +The permission with the id specified will be updated. Use [Get-PnPAzureADAppSitePermission](Get-PnPAzureADAppSitePermission.html) to discover currently set permissions which can be updated. ```yaml Type: String @@ -53,14 +60,14 @@ Accept wildcard characters: False ``` ### -Permissions -Specifies the permissions to set for the app. +Specifies the permissions to set for the Azure Active Directory application registration which can either be Read, Write, Manage or FullControl. ```yaml Type: String Parameter Sets: (All) Required: True -Accepted values: Write, Read, Manage, FullControl +Accepted values: Read, Write, Manage, FullControl Position: Named Default value: None Accept pipeline input: False @@ -68,7 +75,7 @@ Accept wildcard characters: False ``` ### -Site -Optional url to to a site to set the permissions for. Defaults to the current site. +Optional url to to a site to set the permissions for. Defaults to the current site if not provided. ```yaml Type: SitePipeBind @@ -76,7 +83,7 @@ Parameter Sets: (All) Required: False Position: Named -Default value: None +Default value: Currently connected to site Accept pipeline input: False Accept wildcard characters: False ``` From d61200ac548b8c00b4a93ac4121baa82ccbeeebf Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 13 Apr 2022 03:25:10 +0000 Subject: [PATCH 088/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index b90f08990..06be666c6 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -44b31253d1ad9805ff1e35c334cc366bd929457f \ No newline at end of file +b8413c2a199a7b3bdf33c3ca06fea253badadd2d \ No newline at end of file diff --git a/version.txt b/version.txt index a2739ec84..d9de3aaf4 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.12 \ No newline at end of file +1.10.13 \ No newline at end of file From 5174f5817141ce07e6ec4278f4eadff4f9e15747 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Thu, 14 Apr 2022 03:29:14 +0000 Subject: [PATCH 089/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 06be666c6..4a80ba2ac 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -b8413c2a199a7b3bdf33c3ca06fea253badadd2d \ No newline at end of file +6b396d0d2dbabd1c00848529bad6795c959009be \ No newline at end of file diff --git a/version.txt b/version.txt index d9de3aaf4..272f2e3fe 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.13 \ No newline at end of file +1.10.14 \ No newline at end of file From a2d2478fb4ece9b187d233b1738e2a58764fbeef Mon Sep 17 00:00:00 2001 From: James May Date: Mon, 11 Apr 2022 20:18:36 +1000 Subject: [PATCH 090/458] Build-Debug.ps1: improve error message when PowerShell is too old Without this the error is Unable to find type [System.Management.Automation.SemanticVersion]. --- build/Build-Debug.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/build/Build-Debug.ps1 b/build/Build-Debug.ps1 index 800859d09..b95fe52f6 100644 --- a/build/Build-Debug.ps1 +++ b/build/Build-Debug.ps1 @@ -1,3 +1,4 @@ +#Requires -PSEdition Core Param( [Parameter(Mandatory = $false, ValueFromPipeline = $false)] From 8633b5a546f1fb382f9b7680c1667c74f9a60491 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 14 Apr 2022 17:15:52 +0300 Subject: [PATCH 091/458] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 070edeea3..1eaf8d168 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Arleta Wanat [PowershellScripts] - Koen Zomers [koenzomers] +- James May [fowl2] ## [1.10.0] From 716dfb932e243b7c12aba7031be0892fa7272b82 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Thu, 14 Apr 2022 21:33:18 +0200 Subject: [PATCH 092/458] Added the ability to retrieve site collection information by its Id using `Get-PnPTenantSite -Identity ` --- CHANGELOG.md | 1 + documentation/Get-PnPTenantSite.md | 43 +++++++++++-------- src/Commands/Admin/GetTenantSite.cs | 16 +++++-- .../Base/PipeBinds/SPOSitePipeBind.cs | 20 ++++++--- 4 files changed, 53 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 070edeea3..b88c36eff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Added - Added `-Wait` and `-Verbose` optional paramarers to `New-PnPUPABulkImportJob` [#1752](https://github.com/pnp/powershell/pull/1752) +- Added the ability to retrieve site collection information by its Id using `Get-PnPTenantSite -Identity ` ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) diff --git a/documentation/Get-PnPTenantSite.md b/documentation/Get-PnPTenantSite.md index 9a9a7f66d..46b9f8334 100644 --- a/documentation/Get-PnPTenantSite.md +++ b/documentation/Get-PnPTenantSite.md @@ -15,7 +15,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Get-PnPTenantSite.html * SharePoint: Access to the SharePoint Tenant Administration site -Retrieve site information. +Retrieves site collection information ## SYNTAX @@ -32,7 +32,7 @@ Get-PnPTenantSite [-Template ] [-Detailed] [-IncludeOneDriveSites] [-Gro ``` ## DESCRIPTION -Use this cmdlet to retrieve site information from your tenant administration. +This cmdlet allows for retrieval of site collection information through the SharePoint Online tenant site. It requires having SharePoint Online administrator access. ## EXAMPLES @@ -41,56 +41,63 @@ Use this cmdlet to retrieve site information from your tenant administration. Get-PnPTenantSite ``` -Returns all site collections +Returns all site collections except the OneDrive for Business sites with basic information on them ### EXAMPLE 2 ```powershell -Get-PnPTenantSite -Identity "http://tenant.sharepoint.com/sites/projects" +Get-PnPTenantSite -Detailed ``` -Returns information about the project site +Returns all site collections except the OneDrive for Business sites with the full details on them ### EXAMPLE 3 ```powershell -Get-PnPTenantSite -Detailed +Get-PnPTenantSite -IncludeOneDriveSites ``` -Returns all sites with the full details of these sites +Returns all site collections including all OneDrive for Business sites ### EXAMPLE 4 ```powershell -Get-PnPTenantSite -IncludeOneDriveSites +Get-PnPTenantSite -IncludeOneDriveSites -Filter "Url -like '-my.sharepoint.com/personal/'" ``` -Returns all sites including all OneDrive for Business sites +Returns only OneDrive for Business site collections ### EXAMPLE 5 ```powershell -Get-PnPTenantSite -IncludeOneDriveSites -Filter "Url -like '-my.sharepoint.com/personal/'" +Get-PnPTenantSite -Identity "http://tenant.sharepoint.com/sites/projects" ``` -Returns all OneDrive for Business sites +Returns information of the site collection with the provided url ### EXAMPLE 6 ```powershell -Get-PnPTenantSite -Template SITEPAGEPUBLISHING#0 +Get-PnPTenantSite -Identity 7e8a6f56-92fe-4b22-9364-41799e579e8a ``` -Returns all Communication sites +Returns information of the site collection with the provided Id ### EXAMPLE 7 ```powershell -Get-PnPTenantSite -Filter "Url -like 'sales'" +Get-PnPTenantSite -Template SITEPAGEPUBLISHING#0 ``` -Returns all sites including 'sales' in the url +Returns all Communication site collections ### EXAMPLE 8 ```powershell +Get-PnPTenantSite -Filter "Url -like 'sales'" +``` + +Returns all site collections having 'sales' in their url + +### EXAMPLE 9 +```powershell Get-PnPTenantSite -GroupIdDefined $true ``` -Returns all sites which have an underlying Microsoft 365 Group +Returns all site collections which have an underlying Microsoft 365 Group ## PARAMETERS @@ -137,7 +144,7 @@ Accept wildcard characters: False ``` ### -Filter -Specifies the script block of the server-side filter to apply. See https://technet.microsoft.com/en-us/library/fp161380.aspx +Specifies the script block of the server-side filter to apply. See https://docs.microsoft.com/powershell/module/sharepoint-online/get-sposite?view=sharepoint-ps#:~:text=SharePoint%20Online-,%2DFilter,-Specifies%20the%20script. ```yaml Type: String @@ -193,7 +200,7 @@ Accept wildcard characters: False ``` ### -Identity -The URL of the site +The URL or Id of the site collection ```yaml Type: String diff --git a/src/Commands/Admin/GetTenantSite.cs b/src/Commands/Admin/GetTenantSite.cs index 7923503a1..8e6014eaf 100644 --- a/src/Commands/Admin/GetTenantSite.cs +++ b/src/Commands/Admin/GetTenantSite.cs @@ -3,7 +3,6 @@ using System.Management.Automation; using Microsoft.Online.SharePoint.TenantAdministration; using Microsoft.SharePoint.Client; - using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Enums; using System.Collections.Generic; @@ -45,9 +44,18 @@ protected override void ExecuteCmdlet() ClientContext.ExecuteQueryRetry(); if (ParameterSpecified(nameof(Identity))) { - var siteProperties = Tenant.GetSitePropertiesByUrl(Identity.Url, Detailed); - ClientContext.Load(siteProperties); - ClientContext.ExecuteQueryRetry(); + SiteProperties siteProperties; + if(Identity.Id.HasValue) + { + siteProperties = Tenant.GetSitePropertiesById(Identity.Id.Value, Detailed); + if(siteProperties == null) return; + } + else + { + siteProperties = Tenant.GetSitePropertiesByUrl(Identity.Url, Detailed); + ClientContext.Load(siteProperties); + ClientContext.ExecuteQueryRetry(); + } Model.SPOSite site = null; if (ParameterSpecified(nameof(DisableSharingForNonOwnersStatus))) { diff --git a/src/Commands/Base/PipeBinds/SPOSitePipeBind.cs b/src/Commands/Base/PipeBinds/SPOSitePipeBind.cs index 29dd788d2..e56f1f652 100644 --- a/src/Commands/Base/PipeBinds/SPOSitePipeBind.cs +++ b/src/Commands/Base/PipeBinds/SPOSitePipeBind.cs @@ -7,17 +7,27 @@ namespace PnP.PowerShell.Commands.Base.PipeBinds public class SPOSitePipeBind { private string _url; + private Guid? _id; public string Url => _url; - public SPOSitePipeBind(string url) + public Guid? Id => _id; + + public SPOSitePipeBind(string identity) { - _url = url?.TrimEnd(new char[] { '/' }); + if(Guid.TryParse(identity, out Guid id)) + { + _id = id; + } + else + { + _url = identity?.TrimEnd('/'); + } } public SPOSitePipeBind(Uri uri) { - _url = uri.AbsoluteUri?.TrimEnd(new char[] { '/' }); + _url = uri.AbsoluteUri?.TrimEnd('/'); } public SPOSitePipeBind(SPOSite site) @@ -26,7 +36,7 @@ public SPOSitePipeBind(SPOSite site) { throw new PSArgumentException("Site Url must be specified"); } - _url = site.Url?.TrimEnd(new char[] { '/' }); + _url = site.Url?.TrimEnd('/'); } public SPOSitePipeBind(Model.SPODeletedSite site) @@ -35,7 +45,7 @@ public SPOSitePipeBind(Model.SPODeletedSite site) { throw new PSArgumentException("Site Url must be specified"); } - _url = site.Url?.TrimEnd(new char[] { '/' }); + _url = site.Url?.TrimEnd('/'); } } } \ No newline at end of file From ced2e8fcf00a1aac89a9233cd56d8d7a6402803a Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Thu, 14 Apr 2022 22:23:23 +0200 Subject: [PATCH 093/458] Update Get-PnPTenantSite.md --- documentation/Get-PnPTenantSite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Get-PnPTenantSite.md b/documentation/Get-PnPTenantSite.md index 46b9f8334..c5752083b 100644 --- a/documentation/Get-PnPTenantSite.md +++ b/documentation/Get-PnPTenantSite.md @@ -200,7 +200,7 @@ Accept wildcard characters: False ``` ### -Identity -The URL or Id of the site collection +The URL or Id of the site collection. Requesting a site collection by its Id only works for modern SharePoint Online site collections. ```yaml Type: String From 3703a120746f03aeeb30a181f9961c02f0be27d3 Mon Sep 17 00:00:00 2001 From: Milan Holemans Date: Thu, 31 Mar 2022 00:05:09 +0200 Subject: [PATCH 094/458] Created Add-PnpTeamsChannelUser cmdlet --- documentation/Add-PnpTeamsChannelUser.md | 105 ++++++++++++++++++ src/Commands/Model/Teams/TeamChannelMember.cs | 2 - src/Commands/Teams/AddTeamsChannelUser.cs | 50 +++++++++ src/Commands/Utilities/TeamsUtility.cs | 16 +++ 4 files changed, 171 insertions(+), 2 deletions(-) create mode 100644 documentation/Add-PnpTeamsChannelUser.md create mode 100644 src/Commands/Teams/AddTeamsChannelUser.cs diff --git a/documentation/Add-PnpTeamsChannelUser.md b/documentation/Add-PnpTeamsChannelUser.md new file mode 100644 index 000000000..1a58e6c28 --- /dev/null +++ b/documentation/Add-PnpTeamsChannelUser.md @@ -0,0 +1,105 @@ +--- +Module Name: PnP.PowerShell +schema: 2.0.0 +applicable: SharePoint Online +online version: https://pnp.github.io/powershell/cmdlets/Add-PnPTeamsChannelUser.html +external help file: PnP.PowerShell.dll-Help.xml +title: Add-PnPTeamsChannelUser +--- + +# Add-PnPTeamsChannelUser + +## SYNOPSIS + +**Required Permissions** + + * Microsoft Graph API: ChannelMember.ReadWrite.All + +Adds a user to an existing Microsoft Teams private channel. + +## SYNTAX + +```powershell +Add-PnPTeamsChannelUser -Team -Channel -User -Role [] +``` + +## DESCRIPTION + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Add-PnPTeamsChannelUser -Team 4efdf392-8225-4763-9e7f-4edeb7f721aa -Channel "19:796d063b63e34497aeaf092c8fb9b44e@thread.skype" -User john@doe.com -Role Owner +``` + +Adds user as an owner to the private channel. + +### EXAMPLE 2 +```powershell +Add-PnPTeamsChannelUser -Team "My Team" -Channel "My Private Channel" -User john@doe.com -Role Member +``` + +Adds user as a member to the private channel. + +## PARAMETERS + +### -Team +Specify the group id, mailNickname or display name of the team to use. + +```yaml +Type: TeamsTeamPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Channel +The id or name of the channel to retrieve. + +```yaml +Type: TeamsChannelPipeBind +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -User +Specify the UPN (e.g. john@doe.com) + +```yaml +Type: String +Parameter Sets: (User) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Role +Specify the role of the user + +```yaml +Type: String +Parameter Sets: (All) +Accepted values: Owner, Member + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/src/Commands/Model/Teams/TeamChannelMember.cs b/src/Commands/Model/Teams/TeamChannelMember.cs index 096a4b085..907406277 100644 --- a/src/Commands/Model/Teams/TeamChannelMember.cs +++ b/src/Commands/Model/Teams/TeamChannelMember.cs @@ -26,6 +26,4 @@ public class TeamChannelMember [JsonPropertyName("email")] public string email { get; set; } } - - } diff --git a/src/Commands/Teams/AddTeamsChannelUser.cs b/src/Commands/Teams/AddTeamsChannelUser.cs new file mode 100644 index 000000000..72e494882 --- /dev/null +++ b/src/Commands/Teams/AddTeamsChannelUser.cs @@ -0,0 +1,50 @@ +using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Model.Graph; +using PnP.PowerShell.Commands.Utilities; +using System.Management.Automation; + +namespace PnP.PowerShell.Commands.Teams +{ + [Cmdlet(VerbsCommon.Add, "PnPTeamsChannelUser")] + [RequiredMinimalApiPermissions("ChannelMember.ReadWrite.All")] + public class AddTeamsChannelUser : PnPGraphCmdlet + { + [Parameter(Mandatory = true)] + public TeamsTeamPipeBind Team; + + [Parameter(Mandatory = true)] + public TeamsChannelPipeBind Channel; + + [Parameter(Mandatory = true)] + public string User; + + [Parameter(Mandatory = true)] + [ValidateSet("Owner", "Member")] + public string Role; + + protected override void ExecuteCmdlet() + { + var groupId = Team.GetGroupId(HttpClient, AccessToken); + if (groupId == null) + throw new PSArgumentException("Group not found"); + + var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + if (channelId == null) + throw new PSArgumentException("Channel not found"); + + try + { + TeamsUtility.AddChannelUserAsync(HttpClient, AccessToken, groupId, channelId, User, Role).GetAwaiter().GetResult(); + } + catch (GraphException ex) + { + if (ex.Error != null) + throw new PSInvalidOperationException(ex.Error.Message); + + throw; + } + } + } +} diff --git a/src/Commands/Utilities/TeamsUtility.cs b/src/Commands/Utilities/TeamsUtility.cs index 5e8bc12a6..8b15b8ad1 100644 --- a/src/Commands/Utilities/TeamsUtility.cs +++ b/src/Commands/Utilities/TeamsUtility.cs @@ -11,7 +11,9 @@ using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; +using Microsoft.Graph; using Group = PnP.PowerShell.Commands.Model.Graph.Group; +using Team = PnP.PowerShell.Commands.Model.Teams.Team; using TeamChannel = PnP.PowerShell.Commands.Model.Teams.TeamChannel; using User = PnP.PowerShell.Commands.Model.Teams.User; @@ -609,6 +611,20 @@ public static async Task AddChannelAsync(string accessToken, HttpCl } } + public static async Task AddChannelUserAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string upn, string role) + { + var channelMember = new TeamChannelMember + { + UserIdentifier = $"https://graph.microsoft.com/v1.0/users('{upn}')", + }; + + // The role for the user. Must be owner or empty. + if (role.Equals("Owner")) + channelMember.Roles.Add("owner"); + + return await GraphHelper.PostAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/members", channelMember, accessToken); + } + public static async Task PostMessageAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, TeamChannelMessage message) { await GraphHelper.PostAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/messages", message, accessToken); From 60186401deacd32df4a43f7d4b8c1d6c5e9ff0cc Mon Sep 17 00:00:00 2001 From: Milan Holemans Date: Thu, 31 Mar 2022 00:09:00 +0200 Subject: [PATCH 095/458] Removed unused using --- src/Commands/Utilities/TeamsUtility.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Commands/Utilities/TeamsUtility.cs b/src/Commands/Utilities/TeamsUtility.cs index 8b15b8ad1..8eb3ef7a1 100644 --- a/src/Commands/Utilities/TeamsUtility.cs +++ b/src/Commands/Utilities/TeamsUtility.cs @@ -11,7 +11,6 @@ using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; -using Microsoft.Graph; using Group = PnP.PowerShell.Commands.Model.Graph.Group; using Team = PnP.PowerShell.Commands.Model.Teams.Team; using TeamChannel = PnP.PowerShell.Commands.Model.Teams.TeamChannel; From 71d8d366b5e6e9885d92aee61099f65f2ed02e5b Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Thu, 14 Apr 2022 22:39:05 +0200 Subject: [PATCH 096/458] Coding style changes --- src/Commands/Teams/AddTeamsChannelUser.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Commands/Teams/AddTeamsChannelUser.cs b/src/Commands/Teams/AddTeamsChannelUser.cs index 72e494882..65d5c16be 100644 --- a/src/Commands/Teams/AddTeamsChannelUser.cs +++ b/src/Commands/Teams/AddTeamsChannelUser.cs @@ -28,11 +28,15 @@ protected override void ExecuteCmdlet() { var groupId = Team.GetGroupId(HttpClient, AccessToken); if (groupId == null) + { throw new PSArgumentException("Group not found"); + } var channelId = Channel.GetId(HttpClient, AccessToken, groupId); if (channelId == null) + { throw new PSArgumentException("Channel not found"); + } try { @@ -41,7 +45,9 @@ protected override void ExecuteCmdlet() catch (GraphException ex) { if (ex.Error != null) + { throw new PSInvalidOperationException(ex.Error.Message); + } throw; } From 8a5b3a29728cee5402a345dc92142179458d207f Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Thu, 14 Apr 2022 22:40:46 +0200 Subject: [PATCH 097/458] Added changelog entry --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 070edeea3..56a36ff1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Added - Added `-Wait` and `-Verbose` optional paramarers to `New-PnPUPABulkImportJob` [#1752](https://github.com/pnp/powershell/pull/1752) +- Added `Add-PnpTeamsChannelUser` which allows members and owners to be added to private channels in Teams ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) @@ -18,7 +19,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Removed ### Contributors - +- Milan Holemans [milanholemans] - Arleta Wanat [PowershellScripts] - Koen Zomers [koenzomers] From 3f72ab7f77b346c9efcaeafdd501f6ee693ded91 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Thu, 14 Apr 2022 23:13:48 +0200 Subject: [PATCH 098/458] Added changelog entry, moved output type definition class to its own file --- CHANGELOG.md | 1 + .../Model/UserWithRightsAssignedDetailed.cs | 13 +++++++++++++ src/Commands/Principals/GetUser.cs | 13 ++----------- 3 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 src/Commands/Model/UserWithRightsAssignedDetailed.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 070edeea3..537d6896e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Contributors +- James May [fowl2] - Arleta Wanat [PowershellScripts] - Koen Zomers [koenzomers] diff --git a/src/Commands/Model/UserWithRightsAssignedDetailed.cs b/src/Commands/Model/UserWithRightsAssignedDetailed.cs new file mode 100644 index 000000000..e1461ddcc --- /dev/null +++ b/src/Commands/Model/UserWithRightsAssignedDetailed.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace PnP.PowerShell.Commands.Model +{ + public class UserWithRightsAssignedDetailed + { + public string Title { get; set; } + public string LoginName { get; set; } + public string Email { get; set; } + public List Groups { get; set; } + public Dictionary Permissions { get; set; } + } +} \ No newline at end of file diff --git a/src/Commands/Principals/GetUser.cs b/src/Commands/Principals/GetUser.cs index 11e25eed0..6899fe9c8 100644 --- a/src/Commands/Principals/GetUser.cs +++ b/src/Commands/Principals/GetUser.cs @@ -13,7 +13,7 @@ namespace PnP.PowerShell.Commands.Principals { [Cmdlet(VerbsCommon.Get, "PnPUser", DefaultParameterSetName = PARAMETERSET_IDENTITY)] [OutputType(typeof(User), ParameterSetName = new[] { PARAMETERSET_IDENTITY, PARAMETERSET_WITHRIGHTSASSIGNED })] - [OutputType(typeof(UserWithRightsAssignedDetailed), ParameterSetName = new[] { PARAMETERSET_WITHRIGHTSASSIGNEDDETAILED })] + [OutputType(typeof(Model.UserWithRightsAssignedDetailed), ParameterSetName = new[] { PARAMETERSET_WITHRIGHTSASSIGNEDDETAILED })] public class GetUser : PnPWebRetrievalsCmdlet { private const string PARAMETERSET_IDENTITY = "Identity based request"; @@ -274,7 +274,7 @@ protected override void ExecuteCmdlet() where u.User.LoginName == uniqueUser select u.User).FirstOrDefault(); - WriteObject(new UserWithRightsAssignedDetailed + WriteObject(new Model.UserWithRightsAssignedDetailed { Title = userInformation.Title, LoginName = userInformation.LoginName, @@ -331,13 +331,4 @@ private static List GetPermissions(RoleAssignmentCollection roleAs return users; } } - - public sealed class UserWithRightsAssignedDetailed - { - public string Title { get; set; } - public string LoginName { get; set; } - public string Email { get; set; } - public List Groups { get; set; } - public Dictionary Permissions { get; set; } - } } From 6980b70e65804ba5a8b1e2b2cd521b145af24f88 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Thu, 14 Apr 2022 23:21:15 +0200 Subject: [PATCH 099/458] Update CHANGELOG.md Adding PR reference --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b88c36eff..a9f026c85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Added - Added `-Wait` and `-Verbose` optional paramarers to `New-PnPUPABulkImportJob` [#1752](https://github.com/pnp/powershell/pull/1752) -- Added the ability to retrieve site collection information by its Id using `Get-PnPTenantSite -Identity ` +- Added the ability to retrieve site collection information by its Id using `Get-PnPTenantSite -Identity ` [#1766](https://github.com/pnp/powershell/pull/1766) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) From 39ccdcc9e627c39762b4d70b9ce017ddc17bdab0 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Thu, 14 Apr 2022 23:24:05 +0200 Subject: [PATCH 100/458] Update CHANGELOG.md Adding PR reference for 1735 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6d3ef098..a5283c5d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Added - Added `-Wait` and `-Verbose` optional paramarers to `New-PnPUPABulkImportJob` [#1752](https://github.com/pnp/powershell/pull/1752) -- Added `Add-PnpTeamsChannelUser` which allows members and owners to be added to private channels in Teams +- Added `Add-PnpTeamsChannelUser` which allows members and owners to be added to private channels in Teams [#1735](https://github.com/pnp/powershell/pull/1735) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) From c31f3231e814516c0a7d1c44c6203d0a96760f26 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Thu, 14 Apr 2022 23:25:02 +0200 Subject: [PATCH 101/458] Update CHANGELOG.md Removing duplicate contributors entry for fowl2 --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5283c5d7..ae077fc98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Milan Holemans [milanholemans] - Arleta Wanat [PowershellScripts] - Koen Zomers [koenzomers] -- James May [fowl2] ## [1.10.0] From b36d9473dbb4ad87cb910e6790f8ef01531038c0 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Fri, 15 Apr 2022 03:54:08 +0000 Subject: [PATCH 102/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index 27539c80c..00c8fd35e 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -87d90fe8523d9ce8431f72c9562cb1369111b910 \ No newline at end of file +b3f7b8c57093591667298368f98e9fbc75c597da \ No newline at end of file diff --git a/version.txt b/version.txt index 272f2e3fe..ab26b261f 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.14 \ No newline at end of file +1.10.15 \ No newline at end of file From 6d781ce68e778bfe3d78e866d7b2a1c14609ca00 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Sat, 16 Apr 2022 03:10:35 +0000 Subject: [PATCH 103/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 4a80ba2ac..9f62c3c72 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -6b396d0d2dbabd1c00848529bad6795c959009be \ No newline at end of file +9dbdc3f2a8cacd844fa055cfab6fa7cfcd314336 \ No newline at end of file diff --git a/version.txt b/version.txt index ab26b261f..1206e5a97 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.15 \ No newline at end of file +1.10.16 \ No newline at end of file From b0dc70dc984540e6ed00fb18e328e3b491bfaf0a Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 16 Apr 2022 22:13:42 +0300 Subject: [PATCH 104/458] Feature: added additional props to PnPTenantSite cmdlet --- CHANGELOG.md | 6 +++++- documentation/Set-PnPSite.md | 14 ++++++++++++++ documentation/Set-PnPTenantSite.md | 17 ++++++++++++++++- src/Commands/Admin/SetTenantSite.cs | 13 +++++++++++-- src/Commands/Site/SetSite.cs | 13 ++++++++++++- 5 files changed, 58 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae077fc98..3793afe03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Added - Added `-Wait` and `-Verbose` optional paramarers to `New-PnPUPABulkImportJob` [#1752](https://github.com/pnp/powershell/pull/1752) -- Added `Add-PnpTeamsChannelUser` which allows members and owners to be added to private channels in Teams [#1735](https://github.com/pnp/powershell/pull/1735) +- Added `Add-PnPTeamsChannelUser` which allows members and owners to be added to private channels in Teams [#1735](https://github.com/pnp/powershell/pull/1735) +- Added `ExcludeVisualPromotedResults` parameter to `Get-PnPSearchConfiguration` which excludes promoted results [#1750](https://github.com/pnp/powershell/pull/1750) +- Added `MediaTranscription` parameter to `Set-PnPTenantSite` and `Set-PnPSite` cmdlets which when enabled allows videos to have transcripts generated on demand or generated automatically in certain scenarios ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) @@ -23,6 +25,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Milan Holemans [milanholemans] - Arleta Wanat [PowershellScripts] - Koen Zomers [koenzomers] +- Mikael Svenson [wobba] +- Gautam Sheth [gautamdsheth] ## [1.10.0] diff --git a/documentation/Set-PnPSite.md b/documentation/Set-PnPSite.md index 39789bd0b..3f70c37d2 100644 --- a/documentation/Set-PnPSite.md +++ b/documentation/Set-PnPSite.md @@ -24,6 +24,7 @@ Set-PnPSite [-Identity ] [-Classification ] [-DisableFlows] [-Lo [-DisableCompanyWideSharingLinks ] [-DisableSharingForNonOwners] [-LocaleId ] [-RestrictedToGeo ] [-SocialBarOnSitePagesDisabled] [-AnonymousLinkExpirationInDays ] [-OverrideTenantAnonymousLinkExpirationPolicy] + [-MediaTranscription ] [-Connection ] [] ``` @@ -422,6 +423,19 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -MediaTranscription +When the feature is enabled, videos can have transcripts generated on demand or generated automatically in certain scenarios. This is the default because the policy is default on. If a video owner decides they don’t want the transcript, they can always hide or delete it from that video. Possible values: `Enabled, Disabled` + +```yaml +Type: MediaTranscriptionPolicyType +Parameter Sets: (All) +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Wait Wait for the operation to complete diff --git a/documentation/Set-PnPTenantSite.md b/documentation/Set-PnPTenantSite.md index aa7fa36df..d8b5494d5 100644 --- a/documentation/Set-PnPTenantSite.md +++ b/documentation/Set-PnPTenantSite.md @@ -31,7 +31,9 @@ Set-PnPTenantSite [-Identity] [-Title ] [-LocaleId ] [- [-SharingDomainRestrictionMode ] [-CommentsOnSitePagesDisabled] [-DisableAppViews ] [-DisableCompanyWideSharingLinks ] [-DisableFlows ] [-AnonymousLinkExpirationInDays ] [-SensitivityLabel ] [-RemoveLabel] [-AddInformationSegment ] [-RemoveInformationSegment ] - [-OverrideTenantAnonymousLinkExpirationPolicy] [-InformationBarriersMode ] [-Wait] [-Connection ] [] + [-OverrideTenantAnonymousLinkExpirationPolicy] [-InformationBarriersMode ] + [-MediaTranscription ] [-Wait] + [-Connection ] [] ``` ### Set Lock State @@ -635,6 +637,19 @@ Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` +### -MediaTranscription +When the feature is enabled, videos can have transcripts generated on demand or generated automatically in certain scenarios. This is the default because the policy is default on. If a video owner decides they don’t want the transcript, they can always hide or delete it from that video. Possible values: `Enabled, Disabled` + +```yaml +Type: MediaTranscriptionPolicyType +Parameter Sets: (All) +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Wait Wait for the operation to complete diff --git a/src/Commands/Admin/SetTenantSite.cs b/src/Commands/Admin/SetTenantSite.cs index 76b5867ea..551ae4447 100644 --- a/src/Commands/Admin/SetTenantSite.cs +++ b/src/Commands/Admin/SetTenantSite.cs @@ -164,6 +164,9 @@ public class SetTenantSite : PnPAdminCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_PROPERTIES)] public InformationBarriersMode InformationBarriersMode; + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_PROPERTIES)] + public MediaTranscriptionPolicyType? MediaTranscription { get; set; } + [Parameter(Mandatory = false)] public SwitchParameter Wait; @@ -360,7 +363,7 @@ private void SetSiteProperties(Func timeoutFunctio { props.AnonymousLinkExpirationInDays = AnonymousLinkExpirationInDays.Value; updateRequired = true; - } + } if (ParameterSpecified(nameof(ConditionalAccessPolicy)) && ConditionalAccessPolicy == PnPConditionalAccessPolicyType.ProtectionLevel || ConditionalAccessPolicy == PnPConditionalAccessPolicyType.AuthenticationContext) { @@ -493,7 +496,13 @@ private void SetSiteProperties(Func timeoutFunctio { props.IBMode = InformationBarriersMode.ToString(); updateRequired = true; - } + } + + if (ParameterSpecified(nameof(MediaTranscription)) && MediaTranscription.HasValue) + { + props.MediaTranscription = MediaTranscription.Value; + updateRequired = true; + } if (updateRequired) { diff --git a/src/Commands/Site/SetSite.cs b/src/Commands/Site/SetSite.cs index 838e749fb..74fb53d54 100644 --- a/src/Commands/Site/SetSite.cs +++ b/src/Commands/Site/SetSite.cs @@ -89,6 +89,9 @@ public class SetSite : PnPSharePointCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_PROPERTIES)] public SwitchParameter OverrideTenantAnonymousLinkExpirationPolicy; + + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_PROPERTIES)] + public MediaTranscriptionPolicyType? MediaTranscription { get; set; } [Parameter(Mandatory = false, ParameterSetName = ParameterSet_LOCKSTATE)] public SwitchParameter Wait; @@ -284,6 +287,13 @@ protected override void ExecuteCmdlet() siteProperties.SocialBarOnSitePagesDisabled = SocialBarOnSitePagesDisabled.Value; executeQueryRequired = true; } + + if (MediaTranscription.HasValue) + { + siteProperties.MediaTranscription = MediaTranscription.Value; + executeQueryRequired = true; + } + if (executeQueryRequired) { siteProperties.Update(); @@ -332,6 +342,7 @@ private bool IsTenantProperty() => AnonymousLinkExpirationInDays.HasValue || ParameterSpecified(nameof(OverrideTenantAnonymousLinkExpirationPolicy)) || LocaleId.HasValue || - DisableCompanyWideSharingLinks.HasValue; + DisableCompanyWideSharingLinks.HasValue || + MediaTranscription.HasValue; } } From c103b3e4b9b3e37fe626bae4e6f5bb5ee3600bf4 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 16 Apr 2022 22:38:34 +0300 Subject: [PATCH 105/458] Feature: adding user to a teams private channel --- CHANGELOG.md | 1 + documentation/Add-PnPTeamsUser.md | 23 +++++++++++++++++++++-- src/Commands/Teams/AddTeamsUser.cs | 23 ++++++++++++++++++----- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae077fc98..9db32cc10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Added - Added `-Wait` and `-Verbose` optional paramarers to `New-PnPUPABulkImportJob` [#1752](https://github.com/pnp/powershell/pull/1752) - Added `Add-PnpTeamsChannelUser` which allows members and owners to be added to private channels in Teams [#1735](https://github.com/pnp/powershell/pull/1735) +- Added `Channel` parameter to `Add-PnPTeamsUser` cmdlet which if specified will allow owners and members to be added to private channels in a Teams Team. ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) diff --git a/documentation/Add-PnPTeamsUser.md b/documentation/Add-PnPTeamsUser.md index 4ab199d40..1df838cc8 100644 --- a/documentation/Add-PnPTeamsUser.md +++ b/documentation/Add-PnPTeamsUser.md @@ -20,7 +20,7 @@ Adds a user to an existing Microsoft Teams instance. ## SYNTAX ```powershell -Add-PnPTeamsUser -Team -User -Role [] +Add-PnPTeamsUser -Team -Channel -User -Role [] ``` ## DESCRIPTION @@ -44,7 +44,12 @@ Add-PnPTeamsUser -Team MyTeam -User john@doe.com -Role Member Add-PnPTeamsUser -Team MyTeam -Users "john@doe.com","jane@doe.com" -Role Member ``` -Adds users as a member to the team +### EXAMPLE 4 +```powershell +Add-PnPTeamsUser -Team MyTeam -User "jane@doe.com" -Role Member -Channel Private +``` + +Adds user as a member to a private channel named Private in MyTeam team. ## PARAMETERS @@ -77,6 +82,20 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Channel +Specify the channel id or name of the team to retrieve. + +```yaml +Type: TeamsChannelPipeBind +Parameter Sets: (User) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -User Specify the UPN (e.g. john@doe.com) diff --git a/src/Commands/Teams/AddTeamsUser.cs b/src/Commands/Teams/AddTeamsUser.cs index 14b933c6b..2b29d46d9 100644 --- a/src/Commands/Teams/AddTeamsUser.cs +++ b/src/Commands/Teams/AddTeamsUser.cs @@ -18,6 +18,9 @@ public class AddTeamsUser : PnPGraphCmdlet [Parameter(Mandatory = true, ParameterSetName = ParamSet_ByMultipleUsers)] public TeamsTeamPipeBind Team; + [Parameter(Mandatory = true, ParameterSetName = ParamSet_ByUser)] + public TeamsChannelPipeBind Channel; + [Parameter(Mandatory = true, ParameterSetName = ParamSet_ByUser)] public string User; @@ -35,15 +38,26 @@ protected override void ExecuteCmdlet() { try { - if (ParameterSetName == ParamSet_ByUser) + if (ParameterSpecified(nameof(Channel))) { - TeamsUtility.AddUserAsync(HttpClient, AccessToken, groupId, User, Role).GetAwaiter().GetResult(); + var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + if (channelId == null) + { + throw new PSArgumentException("Channel not found"); + } + TeamsUtility.AddChannelUserAsync(HttpClient, AccessToken, groupId, channelId, User, Role).GetAwaiter().GetResult(); } else { - TeamsUtility.AddUsersAsync(HttpClient, AccessToken, groupId, Users, Role).GetAwaiter().GetResult(); + if (ParameterSetName == ParamSet_ByUser) + { + TeamsUtility.AddUserAsync(HttpClient, AccessToken, groupId, User, Role).GetAwaiter().GetResult(); + } + else + { + TeamsUtility.AddUsersAsync(HttpClient, AccessToken, groupId, Users, Role).GetAwaiter().GetResult(); + } } - } catch (GraphException ex) { @@ -61,7 +75,6 @@ protected override void ExecuteCmdlet() { throw new PSArgumentException("Group not found"); } - } } } \ No newline at end of file From fb289adcbebecc393815a70311cf4743b698c871 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 16 Apr 2022 22:39:59 +0300 Subject: [PATCH 106/458] Corrected pipebind --- documentation/Add-PnPTeamsUser.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Add-PnPTeamsUser.md b/documentation/Add-PnPTeamsUser.md index 1df838cc8..680e86575 100644 --- a/documentation/Add-PnPTeamsUser.md +++ b/documentation/Add-PnPTeamsUser.md @@ -20,7 +20,7 @@ Adds a user to an existing Microsoft Teams instance. ## SYNTAX ```powershell -Add-PnPTeamsUser -Team -Channel -User -Role [] +Add-PnPTeamsUser -Team -Channel -User -Role [] ``` ## DESCRIPTION From 7e9452b0be3bf00a9025dc18a2999c97e0e549b0 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 16 Apr 2022 22:53:17 +0300 Subject: [PATCH 107/458] Fix #1717 - issue with Get-PnPTenantSite case sensitivity --- CHANGELOG.md | 2 ++ src/Commands/Admin/GetTenantSite.cs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae077fc98..fd183e7f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Fixed +- Fixed `Get-PnPTenantSite` cmdlet so that it will return data even if the template name is specified in a different case. + ### Removed ### Contributors diff --git a/src/Commands/Admin/GetTenantSite.cs b/src/Commands/Admin/GetTenantSite.cs index 7923503a1..d8401edfd 100644 --- a/src/Commands/Admin/GetTenantSite.cs +++ b/src/Commands/Admin/GetTenantSite.cs @@ -100,7 +100,7 @@ protected override void ExecuteCmdlet() if (Template != null) { - WriteObject(sites.Where(t => t.Template == Template).OrderBy(x => x.Url).Select(s => new Model.SPOSite(s, null)), true); + WriteObject(sites.Where(t => t.Template.ToLower() == Template.ToLower()).OrderBy(x => x.Url).Select(s => new Model.SPOSite(s, null)), true); } else { From 2cb86bb8db2cbbc127d0ad8937402476e4224a9e Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 16 Apr 2022 23:50:49 +0300 Subject: [PATCH 108/458] #1641- added ResourceBehaviorOptions to M365 Group creation --- CHANGELOG.md | 2 +- documentation/New-PnPMicrosoft365Group.md | 25 ++++++++++++++++++- .../NewMicrosoft365Group.cs | 16 ++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae077fc98..dab155eb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Added - Added `-Wait` and `-Verbose` optional paramarers to `New-PnPUPABulkImportJob` [#1752](https://github.com/pnp/powershell/pull/1752) - Added `Add-PnpTeamsChannelUser` which allows members and owners to be added to private channels in Teams [#1735](https://github.com/pnp/powershell/pull/1735) - +- Added `ResourceBehaviorOptions` option in `New-PnPMicrosoft365Group` cmdlet to set `ResourceBehaviorOptions` while provisioning a Microsoft 365 Group. ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) diff --git a/documentation/New-PnPMicrosoft365Group.md b/documentation/New-PnPMicrosoft365Group.md index 6e53b8242..615428396 100644 --- a/documentation/New-PnPMicrosoft365Group.md +++ b/documentation/New-PnPMicrosoft365Group.md @@ -21,7 +21,7 @@ Creates a new Microsoft 365 Group ```powershell New-PnPMicrosoft365Group -DisplayName -Description -MailNickname - [-Owners ] [-Members ] [-IsPrivate] [-LogoPath ] [-CreateTeam] [-HideFromAddressLists ] [-HideFromOutlookClients ] [-Force] + [-Owners ] [-Members ] [-IsPrivate] [-LogoPath ] [-CreateTeam] [-HideFromAddressLists ] [-HideFromOutlookClients ] [-ResourceBehaviorOptions ] [-Force] [] ``` @@ -57,6 +57,13 @@ New-PnPMicrosoft365Group -DisplayName $displayName -Description $description -Ma Creates a private Microsoft 365 Group with all the required properties, and with a custom list of Owners and a custom list of Members +### EXAMPLE 5 +```powershell +New-PnPMicrosoft365Group -DisplayName "myPnPDemo1" -Description $description -MailNickname $nickname -Owners $arrayOfOwners -Members $arrayOfMembers -IsPrivate -ResourceBehaviorOptions WelcomeEmailDisabled, HideGroupInOutlook +``` + +This will create a new Microsoft 365 Group called "myPnPDemo1" and sets the privacy to Private. Welcome Email will not be sent when the Group is created. The M365 Group will also not be visible in Outlook. + ## PARAMETERS ### -CreateTeam @@ -213,6 +220,22 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -ResourceBehaviorOptions + +Allows providing ResourceBehaviorOptions which accepts multiple values that specify group behaviors for a Microsoft 365 Group. + +```yaml +Type: TeamResourceBehaviorOptions +Parameter Sets: (All) +Accepted values: AllowOnlyMembersToPost, HideGroupInOutlook, SubscribeNewGroupMembers, WelcomeEmailDisabled + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) diff --git a/src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs b/src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs index fc89f3177..114bc530a 100644 --- a/src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs +++ b/src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs @@ -1,10 +1,12 @@ using PnP.Framework.Graph; using PnP.PowerShell.Commands.Attributes; using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Enums; using PnP.PowerShell.Commands.Model; using PnP.PowerShell.Commands.Properties; using PnP.PowerShell.Commands.Utilities; using System; +using System.Collections.Generic; using System.Linq; using System.Management.Automation; @@ -48,6 +50,9 @@ public class NewPnPMicrosoft365Group : PnPGraphCmdlet [Parameter(Mandatory = false)] public SwitchParameter Force; + [Parameter(Mandatory = false)] + public TeamResourceBehaviorOptions?[] ResourceBehaviorOptions; + protected override void ExecuteCmdlet() { if (MailNickname.Contains(" ")) @@ -89,6 +94,17 @@ protected override void ExecuteCmdlet() SecurityEnabled = false, GroupTypes = new string[] { "Unified" } }; + + if (ResourceBehaviorOptions != null && ResourceBehaviorOptions.Length > 0) + { + var teamResourceBehaviorOptionsValue = new List(); + for (int i = 0; i < ResourceBehaviorOptions.Length; i++) + { + teamResourceBehaviorOptionsValue.Add(ResourceBehaviorOptions[i].ToString()); + } + newGroup.ResourceBehaviorOptions = teamResourceBehaviorOptionsValue.ToArray(); + } + var group = Microsoft365GroupsUtility.CreateAsync(HttpClient, AccessToken, newGroup, CreateTeam, LogoPath, Owners, Members, HideFromAddressLists, HideFromOutlookClients).GetAwaiter().GetResult(); if (ParameterSpecified(nameof(HideFromAddressLists)) || ParameterSpecified(nameof(HideFromOutlookClients))) From d7c31fccb96e3a76f1aafa105c2be8e403db75a4 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Sun, 17 Apr 2022 03:12:56 +0000 Subject: [PATCH 109/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 9f62c3c72..749f26c3a 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -9dbdc3f2a8cacd844fa055cfab6fa7cfcd314336 \ No newline at end of file +cede607f1f92768067e548473db6698b0c0ac5a5 \ No newline at end of file diff --git a/version.txt b/version.txt index 1206e5a97..398bf47aa 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.16 \ No newline at end of file +1.10.17 \ No newline at end of file From 4daa59667fd6a501091298b8e9f7a43c9f1ff25a Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Mon, 18 Apr 2022 03:36:48 +0000 Subject: [PATCH 110/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 749f26c3a..0558979c8 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -cede607f1f92768067e548473db6698b0c0ac5a5 \ No newline at end of file +3c11e445eed61fa2ccd23aefa457ea047bc7e223 \ No newline at end of file diff --git a/version.txt b/version.txt index 398bf47aa..ae1c1e979 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.17 \ No newline at end of file +1.10.18 \ No newline at end of file From fcc833a550b61c09488cf0a63e6e1d42b415e3b6 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 19 Apr 2022 03:43:04 +0000 Subject: [PATCH 111/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 0558979c8..3320931c9 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -3c11e445eed61fa2ccd23aefa457ea047bc7e223 \ No newline at end of file +da9764150c02ef17e01e26f45956fd2022d3fab5 \ No newline at end of file diff --git a/version.txt b/version.txt index ae1c1e979..11ee04cad 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.18 \ No newline at end of file +1.10.19 \ No newline at end of file From 043d24a86a978424336c71054a61b2264a38419b Mon Sep 17 00:00:00 2001 From: Veronique Lengelle <25181757+veronicageek@users.noreply.github.com> Date: Tue, 19 Apr 2022 20:50:09 +0100 Subject: [PATCH 112/458] Updated ex. 6 of Add-PnPNavigationNode Updated ex. 6 of `Add-PnPNavigationNode`. It was missing the last forward slash. --- documentation/Add-PnPNavigationNode.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Add-PnPNavigationNode.md b/documentation/Add-PnPNavigationNode.md index 18175c312..1b043d542 100644 --- a/documentation/Add-PnPNavigationNode.md +++ b/documentation/Add-PnPNavigationNode.md @@ -61,7 +61,7 @@ Adds a navigation node to the quicklaunch. The navigation node will have the tit ### EXAMPLE 6 ```powershell -Add-PnPNavigationNode -Title "Label" -Location "TopNavigationBar" -Url "http://linkless.header" +Add-PnPNavigationNode -Title "Label" -Location "TopNavigationBar" -Url "http://linkless.header/" ``` Adds a navigation node to the top navigation bar. The navigation node will be created as a label. From 786ec36eb06a6cea7ee5354d176988deec93fbea Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 20 Apr 2022 11:24:27 +0200 Subject: [PATCH 113/458] Added additional documentation around setting permissions on lists --- documentation/Set-PnPList.md | 31 +++++++-------- documentation/Set-PnPListPermission.md | 54 +++++++++++++------------- 2 files changed, 41 insertions(+), 44 deletions(-) diff --git a/documentation/Set-PnPList.md b/documentation/Set-PnPList.md index dda2e0fd3..fe064de0b 100644 --- a/documentation/Set-PnPList.md +++ b/documentation/Set-PnPList.md @@ -73,7 +73,21 @@ Turns on attachments on a list ## PARAMETERS ### -BreakRoleInheritance -If used the security inheritance is broken for this list +If used the security inheritance is broken for this list from its parent, the web in which it resides. Permissions can be added using [Set-PnPListPermission](Set-PnPListPermission.html). + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ResetRoleInheritance +If used the security inheritance is reset for this list meaning it will not copy the permissions from its parent, but will start with an empty list of permissions. Permissions can be added using [Set-PnPListPermission](Set-PnPListPermission.html). ```yaml Type: SwitchParameter @@ -311,21 +325,6 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -ResetRoleInheritance -If used the security inheritance is reset for this list (inherited from parent) - -```yaml -Type: SwitchParameter -Parameter Sets: (All) - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - - ### -ReadSecurity Sets the read security for the list diff --git a/documentation/Set-PnPListPermission.md b/documentation/Set-PnPListPermission.md index 5c97fa2bd..603365c4f 100644 --- a/documentation/Set-PnPListPermission.md +++ b/documentation/Set-PnPListPermission.md @@ -10,7 +10,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Set-PnPListPermission.h # Set-PnPListPermission ## SYNOPSIS -Sets list permissions +Allows permissions on a SharePoint list to be changed ## SYNTAX @@ -27,6 +27,7 @@ Set-PnPListPermission -Identity -User [-AddRole ``` ## DESCRIPTION +Allows changing of permissions on a SharePoint list. In case you would like to break the permission inheritance on a list from its parent, you can use [Set-PnPList -BreakRoleInheritance](Set-PnPList.html#-breakroleinheritance). ## EXAMPLES @@ -46,25 +47,25 @@ Removes the 'Contribute' permission to the user 'user@contoso.com' for the list ## PARAMETERS -### -AddRole -The role that must be assigned to the group or user +### -Identity +The Id, title or an instance of the list ```yaml -Type: String +Type: ListPipeBind Parameter Sets: (All) -Required: False +Required: True Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False ``` -### -Connection -Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. +### -AddRole +The name of the role that must be assigned to the group or user. The name of the role is localized and depends on the language in which the site has been created, so i.e. for an English site you would use `Full Control`, but for a Dutch site you would use `Volledig beheer`. ```yaml -Type: PnPConnection +Type: String Parameter Sets: (All) Required: False @@ -74,25 +75,25 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -Group +### -RemoveRole +The name of the role that must be removed from the group or user. The name of the role is localized and depends on the language in which the site has been created, so i.e. for an English site you would use `Full Control`, but for a Dutch site you would use `Volledig beheer`. ```yaml -Type: GroupPipeBind -Parameter Sets: Group +Type: String +Parameter Sets: (All) -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False ``` -### -Identity -The ID or Title of the list. +### -Group ```yaml -Type: ListPipeBind -Parameter Sets: (All) +Type: GroupPipeBind +Parameter Sets: Group Required: True Position: Named @@ -101,36 +102,33 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -RemoveRole -The role that must be removed from the group or user +### -User ```yaml Type: String -Parameter Sets: (All) +Parameter Sets: User -Required: False +Required: True Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False ``` -### -User +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. ```yaml -Type: String -Parameter Sets: User +Type: PnPConnection +Parameter Sets: (All) -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False ``` - - ## RELATED LINKS -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file From 6667cf2538221acfd9fc6776531d2a473b763298 Mon Sep 17 00:00:00 2001 From: James May Date: Wed, 20 Apr 2022 20:36:05 +1000 Subject: [PATCH 114/458] List and ListItem: output the Id of the recycle bin item --- .../Lists/MoveListItemToRecycleBin.cs | 8 ++- src/Commands/Lists/RemoveList.cs | 17 ++++-- src/Commands/Lists/RemoveListItem.cs | 58 ++++++++++++++----- .../Model/SharePoint/RecycleResult.cs | 11 ++++ 4 files changed, 74 insertions(+), 20 deletions(-) create mode 100644 src/Commands/Model/SharePoint/RecycleResult.cs diff --git a/src/Commands/Lists/MoveListItemToRecycleBin.cs b/src/Commands/Lists/MoveListItemToRecycleBin.cs index f73570ac7..54e29300c 100644 --- a/src/Commands/Lists/MoveListItemToRecycleBin.cs +++ b/src/Commands/Lists/MoveListItemToRecycleBin.cs @@ -1,11 +1,14 @@ using System.Management.Automation; + using Microsoft.SharePoint.Client; using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Model.SharePoint; namespace PnP.PowerShell.Commands.Lists { [Cmdlet(VerbsCommon.Move, "PnPListItemToRecycleBin")] + [OutputType(typeof(RecycleResult))] public class MoveListItemToRecycleBin : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] @@ -25,10 +28,11 @@ protected override void ExecuteCmdlet() if (Identity != null) { var item = Identity.GetListItem(list); - if (Force || ShouldContinue(string.Format(Properties.Resources.MoveListItemWithId0ToRecycleBin,item.Id), Properties.Resources.Confirm)) + if (Force || ShouldContinue(string.Format(Properties.Resources.MoveListItemWithId0ToRecycleBin, item.Id), Properties.Resources.Confirm)) { - item.Recycle(); + var recycleBinResult = item.Recycle(); ClientContext.ExecuteQueryRetry(); + WriteObject(new RecycleResult { RecycleBinItemId = recycleBinResult.Value }); } } } diff --git a/src/Commands/Lists/RemoveList.cs b/src/Commands/Lists/RemoveList.cs index 85e0ace58..8cb9490d9 100644 --- a/src/Commands/Lists/RemoveList.cs +++ b/src/Commands/Lists/RemoveList.cs @@ -1,18 +1,25 @@ using System.Management.Automation; + using Microsoft.SharePoint.Client; using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Model.SharePoint; namespace PnP.PowerShell.Commands.Lists { - [Cmdlet(VerbsCommon.Remove, "PnPList")] + [Cmdlet(VerbsCommon.Remove, "PnPList", DefaultParameterSetName = ParameterSet_Delete)] + [OutputType(typeof(void), ParameterSetName = new[] { ParameterSet_Delete })] + [OutputType(typeof(RecycleResult), ParameterSetName = new[] { ParameterSet_Recycle })] public class RemoveList : PnPWebCmdlet { + public const string ParameterSet_Delete = "Delete"; + public const string ParameterSet_Recycle = "Recycle"; + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] [ValidateNotNull] public ListPipeBind Identity; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_Recycle)] public SwitchParameter Recycle; [Parameter(Mandatory = false)] @@ -26,13 +33,15 @@ protected override void ExecuteCmdlet() { if (Recycle) { - list.Recycle(); + var recycleResult = list.Recycle(); + ClientContext.ExecuteQueryRetry(); + WriteObject(new RecycleResult { RecycleBinItemId = recycleResult.Value }); } else { list.DeleteObject(); + ClientContext.ExecuteQueryRetry(); } - ClientContext.ExecuteQueryRetry(); } } } diff --git a/src/Commands/Lists/RemoveListItem.cs b/src/Commands/Lists/RemoveListItem.cs index 7a62f8682..ab0485f73 100644 --- a/src/Commands/Lists/RemoveListItem.cs +++ b/src/Commands/Lists/RemoveListItem.cs @@ -1,36 +1,64 @@ using System.Management.Automation; + using Microsoft.SharePoint.Client; + using PnP.PowerShell.Commands.Base.PipeBinds; using PnP.PowerShell.Commands.Model; +using PnP.PowerShell.Commands.Model.SharePoint; + using Resources = PnP.PowerShell.Commands.Properties.Resources; namespace PnP.PowerShell.Commands.Lists { - [Cmdlet(VerbsCommon.Remove, "PnPListItem", DefaultParameterSetName = ParameterSet_SINGLE)] + [Cmdlet(VerbsCommon.Remove, "PnPListItem", DefaultParameterSetName = ParameterSet_ALL_DELETE)] + [OutputType(typeof(void))] + [OutputType(typeof(RecycleResult), ParameterSetName = new[] { + ParameterSet_ALL_RECYCLE, + ParameterSet_SINGLE_List_RECYCLE, + ParameterSet_SINGLE_ListItem_RECYCLE } + )] public class RemoveListItem : PnPWebCmdlet { + const string ParameterSet_ALL_DELETE = "Delete single item in list"; + const string ParameterSet_ALL_RECYCLE = "Recycle all items in list"; + const string ParameterSet_BATCHED = "Batched"; - const string ParameterSet_SINGLE = "Single"; - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_SINGLE)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_BATCHED)] - [Parameter(ValueFromPipeline = true, Position = 0)] + const string ParameterSet_SINGLE_List_DELETE = "Delete single item in list"; + const string ParameterSet_SINGLE_ListItem_DELETE = "Delete single item"; + const string ParameterSet_SINGLE_List_RECYCLE = "Recycle single item in list"; + const string ParameterSet_SINGLE_ListItem_RECYCLE = "Recycle single item"; + + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, ParameterSetName = ParameterSet_BATCHED)] + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, ParameterSetName = ParameterSet_SINGLE_List_DELETE)] + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, ParameterSetName = ParameterSet_SINGLE_List_RECYCLE)] [ValidateNotNull] public ListPipeBind List; - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_SINGLE)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_BATCHED)] - [Parameter(ValueFromPipeline = true)] + [Parameter(Mandatory = true, ValueFromPipeline = true, ParameterSetName = ParameterSet_BATCHED)] + [Parameter(Mandatory = true, ValueFromPipeline = true, ParameterSetName = ParameterSet_SINGLE_List_DELETE)] + [Parameter(Mandatory = true, ValueFromPipeline = true, ParameterSetName = ParameterSet_SINGLE_ListItem_DELETE)] + [Parameter(Mandatory = true, ValueFromPipeline = true, ParameterSetName = ParameterSet_SINGLE_List_RECYCLE)] + [Parameter(Mandatory = true, ValueFromPipeline = true, ParameterSetName = ParameterSet_SINGLE_ListItem_RECYCLE)] + [ValidateNotNull] public ListItemPipeBind Identity; - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_SINGLE)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_BATCHED)] + [Parameter(ParameterSetName = ParameterSet_ALL_RECYCLE)] + [Parameter(ParameterSetName = ParameterSet_BATCHED)] + [Parameter(ParameterSetName = ParameterSet_SINGLE_List_RECYCLE)] + [Parameter(ParameterSetName = ParameterSet_SINGLE_ListItem_RECYCLE)] public SwitchParameter Recycle; - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_SINGLE)] + [Parameter(ParameterSetName = ParameterSet_ALL_DELETE)] + [Parameter(ParameterSetName = ParameterSet_ALL_RECYCLE)] + [Parameter(ParameterSetName = ParameterSet_SINGLE_List_DELETE)] + [Parameter(ParameterSetName = ParameterSet_SINGLE_ListItem_DELETE)] + [Parameter(ParameterSetName = ParameterSet_SINGLE_List_RECYCLE)] + [Parameter(ParameterSetName = ParameterSet_SINGLE_ListItem_RECYCLE)] public SwitchParameter Force; [Parameter(Mandatory = true, ParameterSetName = ParameterSet_BATCHED)] + [ValidateNotNull] public PnPBatch Batch; protected override void ExecuteCmdlet() @@ -92,7 +120,7 @@ protected override void ExecuteCmdlet() { stillItemsToProcess = false; } - } + } } return; } @@ -113,13 +141,15 @@ protected override void ExecuteCmdlet() { if (Recycle) { - item.Recycle(); + var recycleResult = item.Recycle(); + ClientContext.ExecuteQueryRetry(); + WriteObject(new RecycleResult { RecycleBinItemId = recycleResult.Value }); } else { item.DeleteObject(); + ClientContext.ExecuteQueryRetry(); } - ClientContext.ExecuteQueryRetry(); } } } diff --git a/src/Commands/Model/SharePoint/RecycleResult.cs b/src/Commands/Model/SharePoint/RecycleResult.cs new file mode 100644 index 000000000..a3aaca73d --- /dev/null +++ b/src/Commands/Model/SharePoint/RecycleResult.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace PnP.PowerShell.Commands.Model.SharePoint +{ + public sealed class RecycleResult + { + public Guid RecycleBinItemId { get; set; } + } +} From 71570046a6615d7d596e46a4edd93a610afb996d Mon Sep 17 00:00:00 2001 From: James May Date: Wed, 20 Apr 2022 20:50:14 +1000 Subject: [PATCH 115/458] RemoteFile, RemoveFolder, RemovePage: output the Id of the recycle bin item --- src/Commands/Files/RemoveFile.cs | 29 +++++++++++++++++++---------- src/Commands/Files/RemoveFolder.cs | 8 +++++--- src/Commands/Pages/RemovePage.cs | 16 ++++++++++++---- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/Commands/Files/RemoveFile.cs b/src/Commands/Files/RemoveFile.cs index 9ada57380..33089ecf4 100644 --- a/src/Commands/Files/RemoveFile.cs +++ b/src/Commands/Files/RemoveFile.cs @@ -2,22 +2,30 @@ using Microsoft.SharePoint.Client; using Resources = PnP.PowerShell.Commands.Properties.Resources; using PnP.Framework.Utilities; +using PnP.PowerShell.Commands.Model.SharePoint; namespace PnP.PowerShell.Commands.Files { [Cmdlet(VerbsCommon.Remove, "PnPFile")] public class RemoveFile : PnPWebCmdlet { - private const string ParameterSet_SERVER = "Server Relative"; - private const string ParameterSet_SITE = "Site Relative"; - - [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ParameterSetName = ParameterSet_SERVER)] + private const string ParameterSet_SERVER_Delete = "Delete by Server Relative"; + private const string ParameterSet_SITE_Delete = "Delete by Site Relative"; + private const string ParameterSet_SERVER_Recycle = "Recycle by Server Relative"; + private const string ParameterSet_SITE_Recycle = "Recycle by Site Relative"; + + [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ParameterSetName = ParameterSet_SERVER_Delete)] + [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ParameterSetName = ParameterSet_SERVER_Recycle)] + [ValidateNotNullOrEmpty] public string ServerRelativeUrl = string.Empty; - [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ParameterSetName = ParameterSet_SITE)] + [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ParameterSetName = ParameterSet_SITE_Delete)] + [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ParameterSetName = ParameterSet_SITE_Recycle)] + [ValidateNotNullOrEmpty] public string SiteRelativeUrl = string.Empty; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_SERVER_Recycle)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_SITE_Recycle)] public SwitchParameter Recycle; [Parameter(Mandatory = false)] @@ -25,7 +33,7 @@ public class RemoveFile : PnPWebCmdlet protected override void ExecuteCmdlet() { - if (ParameterSetName == ParameterSet_SITE) + if (!string.IsNullOrEmpty(ServerRelativeUrl)) { var webUrl = CurrentWeb.EnsureProperty(w => w.ServerRelativeUrl); ServerRelativeUrl = UrlUtility.Combine(webUrl, SiteRelativeUrl); @@ -38,14 +46,15 @@ protected override void ExecuteCmdlet() { if (Recycle) { - file.Recycle(); + var recycleResult = file.Recycle(); + ClientContext.ExecuteQueryRetry(); + WriteObject(new RecycleResult { RecycleBinItemId = recycleResult.Value }); } else { file.DeleteObject(); + ClientContext.ExecuteQueryRetry(); } - - ClientContext.ExecuteQueryRetry(); } } } diff --git a/src/Commands/Files/RemoveFolder.cs b/src/Commands/Files/RemoveFolder.cs index d12a368b9..87822262c 100644 --- a/src/Commands/Files/RemoveFolder.cs +++ b/src/Commands/Files/RemoveFolder.cs @@ -3,6 +3,7 @@ using Resources = PnP.PowerShell.Commands.Properties.Resources; using PnP.Framework.Utilities; using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Model.SharePoint; namespace PnP.PowerShell.Commands.Files { @@ -38,14 +39,15 @@ protected override void ExecuteCmdlet() { if (Recycle) { - folder.Recycle(); + var recycleResult = folder.Recycle(); + ClientContext.ExecuteQueryRetry(); + WriteObject(new RecycleResult { RecycleBinItemId = recycleResult.Value }); } else { folder.DeleteObject(); + ClientContext.ExecuteQueryRetry(); } - - ClientContext.ExecuteQueryRetry(); } } } diff --git a/src/Commands/Pages/RemovePage.cs b/src/Commands/Pages/RemovePage.cs index b1a0f27da..2b54f2b47 100644 --- a/src/Commands/Pages/RemovePage.cs +++ b/src/Commands/Pages/RemovePage.cs @@ -1,22 +1,29 @@  using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Model.SharePoint; using PnP.PowerShell.Commands.Properties; + using System; using System.Management.Automation; namespace PnP.PowerShell.Commands.Pages { - [Cmdlet(VerbsCommon.Remove, "PnPPage")] + [Cmdlet(VerbsCommon.Remove, "PnPPage", DefaultParameterSetName = ParameterSet_Delete)] [Alias("Remove-PnPClientSidePage")] + [OutputType(typeof(void), ParameterSetName = new[] { ParameterSet_Delete })] + [OutputType(typeof(RecycleResult), ParameterSetName = new[] { ParameterSet_Recycle })] public class RemovePage : PnPWebCmdlet { + public const string ParameterSet_Delete = "Delete"; + public const string ParameterSet_Recycle = "Recycle"; + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] public PagePipeBind Identity; [Parameter(Mandatory = false)] public SwitchParameter Force; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_Recycle)] public SwitchParameter Recycle; protected override void ExecuteCmdlet() @@ -29,12 +36,13 @@ protected override void ExecuteCmdlet() if (Recycle.IsPresent) { - clientSidePage.PageListItem.Recycle(); + var recycleResult = clientSidePage.PageListItem.Recycle(); + WriteObject(new RecycleResult { RecycleBinItemId = recycleResult }); } else { clientSidePage.Delete(); - } + } } } } From 67124538bf9e7d3f3b2c8e41d440acbbba264b99 Mon Sep 17 00:00:00 2001 From: James May Date: Wed, 20 Apr 2022 20:50:42 +1000 Subject: [PATCH 116/458] RecycleBinItemPipeBind: add conversion from RecycleResult --- src/Commands/Base/PipeBinds/RecycleBinItemPipeBind.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Commands/Base/PipeBinds/RecycleBinItemPipeBind.cs b/src/Commands/Base/PipeBinds/RecycleBinItemPipeBind.cs index 2eb090483..51bf5d955 100644 --- a/src/Commands/Base/PipeBinds/RecycleBinItemPipeBind.cs +++ b/src/Commands/Base/PipeBinds/RecycleBinItemPipeBind.cs @@ -1,6 +1,8 @@ using System; using Microsoft.SharePoint.Client; +using PnP.PowerShell.Commands.Model.SharePoint; + namespace PnP.PowerShell.Commands.Base.PipeBinds { public sealed class RecycleBinItemPipeBind @@ -19,6 +21,11 @@ public RecycleBinItemPipeBind(RecycleBinItem item) _item = item; } + public RecycleBinItemPipeBind(RecycleResult result) + { + _id = result.RecycleBinItemId; + } + public RecycleBinItemPipeBind(string id) { Guid guid; From 0a73e50dc21b1b3e113a55bf08941c7156d00b71 Mon Sep 17 00:00:00 2001 From: James May Date: Wed, 20 Apr 2022 20:29:28 +1000 Subject: [PATCH 117/458] AddListItemComment: output added comment --- src/Commands/Lists/AddListItemComment.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Commands/Lists/AddListItemComment.cs b/src/Commands/Lists/AddListItemComment.cs index 5bd4152bd..ec0cc996a 100644 --- a/src/Commands/Lists/AddListItemComment.cs +++ b/src/Commands/Lists/AddListItemComment.cs @@ -1,9 +1,11 @@ -using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.Core.Model.SharePoint; +using PnP.PowerShell.Commands.Base.PipeBinds; using System.Management.Automation; namespace PnP.PowerShell.Commands.Lists { [Cmdlet(VerbsCommon.Add, "PnPListItemComment")] + [OutputType(typeof(IComment))] public class AddListItemComment : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] @@ -38,7 +40,9 @@ protected override void ExecuteCmdlet() var comments = item.GetCommentsAsync().GetAwaiter().GetResult(); - comments.Add(Text); + var addedComment = comments.Add(Text); + + WriteObject(addedComment); } } } \ No newline at end of file From da9a03337671c630eed5d4e5328fc3c56a8d90e6 Mon Sep 17 00:00:00 2001 From: James May Date: Wed, 20 Apr 2022 20:59:31 +1000 Subject: [PATCH 118/458] DocumentSet cmdlets: add OutputType attributes for improved command completion --- src/Commands/DocumentSets/AddContentTypeToDocumentSet.cs | 1 + src/Commands/DocumentSets/AddDocumentSet.cs | 1 + src/Commands/DocumentSets/GetDocumentSetTemplate.cs | 1 + src/Commands/DocumentSets/RemoveContentTypeFromDocumentSet.cs | 1 + src/Commands/DocumentSets/SetDocumentSetField.cs | 1 + 5 files changed, 5 insertions(+) diff --git a/src/Commands/DocumentSets/AddContentTypeToDocumentSet.cs b/src/Commands/DocumentSets/AddContentTypeToDocumentSet.cs index d9d5ab1a4..4edb36475 100644 --- a/src/Commands/DocumentSets/AddContentTypeToDocumentSet.cs +++ b/src/Commands/DocumentSets/AddContentTypeToDocumentSet.cs @@ -6,6 +6,7 @@ namespace PnP.PowerShell.Commands.DocumentSets { [Cmdlet(VerbsCommon.Add,"PnPContentTypeToDocumentSet")] + [OutputType(typeof(void))] public class AddContentTypeToDocumentSet : PnPWebCmdlet { [Parameter(Mandatory = true)] diff --git a/src/Commands/DocumentSets/AddDocumentSet.cs b/src/Commands/DocumentSets/AddDocumentSet.cs index aae796e0d..5ec6bf79d 100644 --- a/src/Commands/DocumentSets/AddDocumentSet.cs +++ b/src/Commands/DocumentSets/AddDocumentSet.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands.DocumentSets { [Cmdlet(VerbsCommon.Add, "PnPDocumentSet")] + [OutputType(typeof(string))] public class AddDocumentSet : PnPWebCmdlet { [Parameter(Mandatory = true)] diff --git a/src/Commands/DocumentSets/GetDocumentSetTemplate.cs b/src/Commands/DocumentSets/GetDocumentSetTemplate.cs index ff107fe12..0d962f039 100644 --- a/src/Commands/DocumentSets/GetDocumentSetTemplate.cs +++ b/src/Commands/DocumentSets/GetDocumentSetTemplate.cs @@ -10,6 +10,7 @@ namespace PnP.PowerShell.Commands.DocumentSets { [Cmdlet(VerbsCommon.Get,"PnPDocumentSetTemplate")] + [OutputType(typeof(DocumentSetTemplate))] public class GetDocumentSetTemplate : PnPWebRetrievalsCmdlet { [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true)] diff --git a/src/Commands/DocumentSets/RemoveContentTypeFromDocumentSet.cs b/src/Commands/DocumentSets/RemoveContentTypeFromDocumentSet.cs index 7c3604413..1f9780358 100644 --- a/src/Commands/DocumentSets/RemoveContentTypeFromDocumentSet.cs +++ b/src/Commands/DocumentSets/RemoveContentTypeFromDocumentSet.cs @@ -6,6 +6,7 @@ namespace PnP.PowerShell.Commands.DocumentSets { [Cmdlet(VerbsCommon.Remove, "PnPContentTypeFromDocumentSet")] + [OutputType(typeof(void))] public class RemoveContentTypeFromDocumentSet : PnPWebCmdlet { [Parameter(Mandatory = true)] diff --git a/src/Commands/DocumentSets/SetDocumentSetField.cs b/src/Commands/DocumentSets/SetDocumentSetField.cs index daa337432..25e2267d0 100644 --- a/src/Commands/DocumentSets/SetDocumentSetField.cs +++ b/src/Commands/DocumentSets/SetDocumentSetField.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands.DocumentSets { [Cmdlet(VerbsCommon.Set, "PnPDocumentSetField")] + [OutputType(typeof(void))] public class SetFieldInDocumentSet : PnPWebCmdlet { [Parameter(Mandatory = true)] From cfeb7377f06b22ecdc07bd3a71f48bae5b9681bf Mon Sep 17 00:00:00 2001 From: James May Date: Wed, 20 Apr 2022 21:03:11 +1000 Subject: [PATCH 119/458] EventReceiver cmdlets: add OutputType attributes for improved command completion --- src/Commands/Events/AddEventReceiver.cs | 1 + src/Commands/Events/GetEventReceiver.cs | 3 ++- src/Commands/Events/RemoveEventReceiver.cs | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Commands/Events/AddEventReceiver.cs b/src/Commands/Events/AddEventReceiver.cs index b9d0787aa..56f669784 100644 --- a/src/Commands/Events/AddEventReceiver.cs +++ b/src/Commands/Events/AddEventReceiver.cs @@ -5,6 +5,7 @@ namespace PnP.PowerShell.Commands.Events { [Cmdlet(VerbsCommon.Add, "PnPEventReceiver", DefaultParameterSetName = ParameterSet_SCOPE)] + [OutputType(typeof(EventReceiverDefinition))] public class AddEventReceiver : PnPWebCmdlet { private const string ParameterSet_LIST = "On a list"; diff --git a/src/Commands/Events/GetEventReceiver.cs b/src/Commands/Events/GetEventReceiver.cs index 91053deab..043ea44c6 100644 --- a/src/Commands/Events/GetEventReceiver.cs +++ b/src/Commands/Events/GetEventReceiver.cs @@ -6,7 +6,8 @@ namespace PnP.PowerShell.Commands.Events { [Cmdlet(VerbsCommon.Get, "PnPEventReceiver", DefaultParameterSetName = ParameterSet_SCOPE)] - + [OutputType(typeof(EventReceiverDefinition))] + public class GetEventReceiver : PnPWebRetrievalsCmdlet { private const string ParameterSet_LIST = "On a list"; diff --git a/src/Commands/Events/RemoveEventReceiver.cs b/src/Commands/Events/RemoveEventReceiver.cs index 17ead903f..c6d52dc7f 100644 --- a/src/Commands/Events/RemoveEventReceiver.cs +++ b/src/Commands/Events/RemoveEventReceiver.cs @@ -6,6 +6,7 @@ namespace PnP.PowerShell.Commands.Events { [Cmdlet(VerbsCommon.Remove, "PnPEventReceiver", DefaultParameterSetName = ParameterSet_SCOPE)] + [OutputType(typeof(void))] public class RemoveEventReceiver : PnPWebCmdlet { private const string ParameterSet_LIST = "From a list"; From bb1a2f30908300b2298f7f3722fdd6556d5b8fc9 Mon Sep 17 00:00:00 2001 From: James May Date: Wed, 20 Apr 2022 21:04:56 +1000 Subject: [PATCH 120/458] New-ExtensibilityHandlerObject: Add missing OutputType attribute for better command completion --- src/Commands/Extensibility/NewExtensibilityHandlerObject.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Commands/Extensibility/NewExtensibilityHandlerObject.cs b/src/Commands/Extensibility/NewExtensibilityHandlerObject.cs index dee570314..21a163810 100644 --- a/src/Commands/Extensibility/NewExtensibilityHandlerObject.cs +++ b/src/Commands/Extensibility/NewExtensibilityHandlerObject.cs @@ -1,13 +1,15 @@ using System.Management.Automation; + using PnP.Framework.Provisioning.Model; namespace PnP.PowerShell.Commands.Extensibility { [Cmdlet(VerbsCommon.New, "PnPExtensibilityHandlerObject")] + [OutputType(typeof(ExtensibilityHandler))] public class NewExtensibilityHandlerObject : PSCmdlet { - [Parameter(Mandatory = true, Position=0, ValueFromPipeline=true)] + [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true)] public string Assembly; [Parameter(Mandatory = true)] From a9d70170b975ec7c81f61815b22337c202792a36 Mon Sep 17 00:00:00 2001 From: James May Date: Wed, 20 Apr 2022 21:06:15 +1000 Subject: [PATCH 121/458] Feature cmdlets: Add missing OutputType attributes for better command completion --- src/Commands/Features/DisableFeature.cs | 1 + src/Commands/Features/DisablePageScheduling.cs | 1 + src/Commands/Features/EnableFeature.cs | 1 + src/Commands/Features/EnablePageScheduling.cs | 1 + src/Commands/Features/GetFeature.cs | 1 + 5 files changed, 5 insertions(+) diff --git a/src/Commands/Features/DisableFeature.cs b/src/Commands/Features/DisableFeature.cs index 6a0a87dde..d4ba8c34d 100644 --- a/src/Commands/Features/DisableFeature.cs +++ b/src/Commands/Features/DisableFeature.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands.Features { [Cmdlet(VerbsLifecycle.Disable, "PnPFeature")] + [OutputType(typeof(void))] public class DisableFeature : PnPWebCmdlet { [Parameter(Mandatory = true, Position = 0, ParameterSetName = ParameterAttribute.AllParameterSets)] diff --git a/src/Commands/Features/DisablePageScheduling.cs b/src/Commands/Features/DisablePageScheduling.cs index cdd476f27..ca280d1fd 100644 --- a/src/Commands/Features/DisablePageScheduling.cs +++ b/src/Commands/Features/DisablePageScheduling.cs @@ -5,6 +5,7 @@ namespace PnP.PowerShell.Commands.Features { [Cmdlet(VerbsLifecycle.Disable, "PnPPageScheduling")] + [OutputType(typeof(void))] public class DisablePageScheduling : PnPWebCmdlet { protected override void ExecuteCmdlet() diff --git a/src/Commands/Features/EnableFeature.cs b/src/Commands/Features/EnableFeature.cs index 520984e11..98ee84773 100644 --- a/src/Commands/Features/EnableFeature.cs +++ b/src/Commands/Features/EnableFeature.cs @@ -6,6 +6,7 @@ namespace PnP.PowerShell.Commands.Features { [Cmdlet(VerbsLifecycle.Enable, "PnPFeature")] + [OutputType(typeof(void))] public class EnableFeature : PnPWebCmdlet { [Parameter(Mandatory = true, Position=0, ValueFromPipeline=true)] diff --git a/src/Commands/Features/EnablePageScheduling.cs b/src/Commands/Features/EnablePageScheduling.cs index 87d61a804..0f8108a02 100644 --- a/src/Commands/Features/EnablePageScheduling.cs +++ b/src/Commands/Features/EnablePageScheduling.cs @@ -5,6 +5,7 @@ namespace PnP.PowerShell.Commands.Features { [Cmdlet(VerbsLifecycle.Enable, "PnPPageScheduling")] + [OutputType(typeof(void))] public class EnablePageScheduling : PnPWebCmdlet { protected override void ExecuteCmdlet() diff --git a/src/Commands/Features/GetFeature.cs b/src/Commands/Features/GetFeature.cs index d64b75ee3..77da1357c 100644 --- a/src/Commands/Features/GetFeature.cs +++ b/src/Commands/Features/GetFeature.cs @@ -12,6 +12,7 @@ namespace PnP.PowerShell.Commands.Features { [Cmdlet(VerbsCommon.Get, "PnPFeature")] + [OutputType(typeof(Feature))] public class GetFeature : PnPWebRetrievalsCmdlet { [Parameter(Mandatory = false, Position = 0, ValueFromPipeline = true)] From 513ff3d0ca2b4b7592b557cc3c9fbb602c3dcf68 Mon Sep 17 00:00:00 2001 From: James May Date: Wed, 20 Apr 2022 21:10:08 +1000 Subject: [PATCH 122/458] Field cmdlets: Add missing OutputType attributes for better command completion --- src/Commands/Fields/AddField.cs | 1 + src/Commands/Fields/AddFieldFromXml.cs | 1 + src/Commands/Fields/AddTaxonomyField.cs | 1 + src/Commands/Fields/GetField.cs | 1 + src/Commands/Fields/RemoveField.cs | 1 + src/Commands/Fields/SetField.cs | 1 + 6 files changed, 6 insertions(+) diff --git a/src/Commands/Fields/AddField.cs b/src/Commands/Fields/AddField.cs index 6159ae493..1f94110c4 100644 --- a/src/Commands/Fields/AddField.cs +++ b/src/Commands/Fields/AddField.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands.Fields { [Cmdlet(VerbsCommon.Add, "PnPField", DefaultParameterSetName = "Add field to list")] + [OutputType(typeof(Field))] public class AddField : PnPWebCmdlet, IDynamicParameters { const string ParameterSet_ADDFIELDTOLIST = "Add field to list"; diff --git a/src/Commands/Fields/AddFieldFromXml.cs b/src/Commands/Fields/AddFieldFromXml.cs index 8e683397d..4dc17970e 100644 --- a/src/Commands/Fields/AddFieldFromXml.cs +++ b/src/Commands/Fields/AddFieldFromXml.cs @@ -6,6 +6,7 @@ namespace PnP.PowerShell.Commands.Fields { [Cmdlet(VerbsCommon.Add, "PnPFieldFromXml")] + [OutputType(typeof(Field))] public class AddFieldFromXml : PnPWebCmdlet { [Parameter(Mandatory = false, ValueFromPipeline = true)] diff --git a/src/Commands/Fields/AddTaxonomyField.cs b/src/Commands/Fields/AddTaxonomyField.cs index dd1e9a559..9911e16f5 100644 --- a/src/Commands/Fields/AddTaxonomyField.cs +++ b/src/Commands/Fields/AddTaxonomyField.cs @@ -9,6 +9,7 @@ namespace PnP.PowerShell.Commands.Fields { [Cmdlet(VerbsCommon.Add, "PnPTaxonomyField")] + [OutputType(typeof(TaxonomyField))] public class AddTaxonomyField : PnPWebCmdlet { [Parameter(Mandatory = false, ParameterSetName = ParameterAttribute.AllParameterSets)] diff --git a/src/Commands/Fields/GetField.cs b/src/Commands/Fields/GetField.cs index 271541b09..a8b5181d7 100644 --- a/src/Commands/Fields/GetField.cs +++ b/src/Commands/Fields/GetField.cs @@ -10,6 +10,7 @@ namespace PnP.PowerShell.Commands.Fields { [Cmdlet(VerbsCommon.Get, "PnPField")] + [OutputType(typeof(Field))] public class GetField : PnPWebRetrievalsCmdlet { [Parameter(Mandatory = false, ValueFromPipeline = true)] diff --git a/src/Commands/Fields/RemoveField.cs b/src/Commands/Fields/RemoveField.cs index 04725d528..e95ece772 100644 --- a/src/Commands/Fields/RemoveField.cs +++ b/src/Commands/Fields/RemoveField.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.Fields { [Cmdlet(VerbsCommon.Remove, "PnPField")] + [OutputType(typeof(void))] public class RemoveField : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] diff --git a/src/Commands/Fields/SetField.cs b/src/Commands/Fields/SetField.cs index 494910eeb..118ab827b 100644 --- a/src/Commands/Fields/SetField.cs +++ b/src/Commands/Fields/SetField.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands.Fields { [Cmdlet(VerbsCommon.Set, "PnPField")] + [OutputType(typeof(void))] public class SetField : PnPWebCmdlet { [Parameter(Mandatory = false, ValueFromPipeline = true)] From e8c59cb7f48f8675f7a5a4ef2cbd6edf30c14382 Mon Sep 17 00:00:00 2001 From: James May Date: Wed, 20 Apr 2022 20:24:30 +1000 Subject: [PATCH 123/458] Lists cmdlets: Add missing OutputType attributes for better autocompletion --- src/Commands/Lists/AddListItem.cs | 1 + src/Commands/Lists/AddView.cs | 1 + .../Lists/ClearDefaultColumnValues.cs | 1 + src/Commands/Lists/CopyList.cs | 1 + src/Commands/Lists/GetDefaultColumnValues.cs | 27 ++++++++++--------- src/Commands/Lists/GetList.cs | 1 + src/Commands/Lists/GetListItem.cs | 1 + src/Commands/Lists/GetListItemComment.cs | 3 ++- src/Commands/Lists/GetListPermissions.cs | 2 ++ src/Commands/Lists/GetView.cs | 2 ++ src/Commands/Lists/NewList.cs | 1 + src/Commands/Lists/RemoveListItemComment.cs | 1 + src/Commands/Lists/RemoveView.cs | 1 + src/Commands/Lists/RequestReIndexList.cs | 1 + src/Commands/Lists/SetDefaultColumnValues.cs | 1 + src/Commands/Lists/SetList.cs | 3 +++ src/Commands/Lists/SetListItem.cs | 1 + src/Commands/Lists/SetListItemPermission.cs | 1 + src/Commands/Lists/SetListPermission.cs | 1 + src/Commands/Lists/SetView.cs | 1 + .../SharePoint/ListDefaultColumnValue.cs | 13 +++++++++ 21 files changed, 51 insertions(+), 14 deletions(-) create mode 100644 src/Commands/Model/SharePoint/ListDefaultColumnValue.cs diff --git a/src/Commands/Lists/AddListItem.cs b/src/Commands/Lists/AddListItem.cs index fe933f4da..0f4da9c9d 100644 --- a/src/Commands/Lists/AddListItem.cs +++ b/src/Commands/Lists/AddListItem.cs @@ -13,6 +13,7 @@ namespace PnP.PowerShell.Commands.Lists { [Cmdlet(VerbsCommon.Add, "PnPListItem", DefaultParameterSetName = ParameterSet_SINGLE)] + [OutputType(typeof(ListItem))] public class AddListItem : PnPWebCmdlet { private const string ParameterSet_SINGLE = "Single"; diff --git a/src/Commands/Lists/AddView.cs b/src/Commands/Lists/AddView.cs index 5961395de..b6d8e5ea4 100644 --- a/src/Commands/Lists/AddView.cs +++ b/src/Commands/Lists/AddView.cs @@ -6,6 +6,7 @@ namespace PnP.PowerShell.Commands.Lists { [Cmdlet(VerbsCommon.Add, "PnPView")] + [OutputType(typeof(View))] public class AddView : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] diff --git a/src/Commands/Lists/ClearDefaultColumnValues.cs b/src/Commands/Lists/ClearDefaultColumnValues.cs index cc2ddbf15..df9464a82 100644 --- a/src/Commands/Lists/ClearDefaultColumnValues.cs +++ b/src/Commands/Lists/ClearDefaultColumnValues.cs @@ -11,6 +11,7 @@ namespace PnP.PowerShell.Commands.Lists //TODO: Create Test [Cmdlet(VerbsCommon.Clear, "PnPDefaultColumnValues")] + [OutputType(typeof(void))] public class ClearDefaultColumnValues : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] diff --git a/src/Commands/Lists/CopyList.cs b/src/Commands/Lists/CopyList.cs index 4f3dc135b..7b48b557b 100644 --- a/src/Commands/Lists/CopyList.cs +++ b/src/Commands/Lists/CopyList.cs @@ -10,6 +10,7 @@ namespace PnP.PowerShell.Commands.Lists { [Cmdlet(VerbsCommon.Copy, "PnPList", DefaultParameterSetName = ParameterSet_TOCURRENTSITEBYPIPE)] + [OutputType(typeof(List))] public class CopyList : PnPWebCmdlet { private const string ParameterSet_LISTBYPIPE = "By piping in a list"; diff --git a/src/Commands/Lists/GetDefaultColumnValues.cs b/src/Commands/Lists/GetDefaultColumnValues.cs index 3aabb33ec..f7123c441 100644 --- a/src/Commands/Lists/GetDefaultColumnValues.cs +++ b/src/Commands/Lists/GetDefaultColumnValues.cs @@ -1,13 +1,17 @@ using System.Collections; using System.Collections.Generic; using System.Management.Automation; +using System.Linq; + using Microsoft.SharePoint.Client; using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Model.SharePoint; namespace PnP.PowerShell.Commands.Lists { [Cmdlet(VerbsCommon.Get, "PnPDefaultColumnValues")] + [OutputType(typeof(ListDefaultColumnValue))] public class GetDefaultColumnValues : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] @@ -25,27 +29,24 @@ protected override void ExecuteCmdlet() if (list.BaseTemplate == (int)ListTemplateType.DocumentLibrary || list.BaseTemplate == (int)ListTemplateType.WebPageLibrary || list.BaseTemplate == (int)ListTemplateType.PictureLibrary) { var defaultValues = list.GetDefaultColumnValues(); - var dynamicList = new List(); if (defaultValues != null) { foreach (var dict in defaultValues) { - dynamicList.Add( - new - { - Path = dict["Path"], - Field = dict["Field"], - Value = dict["Value"] - }); + WriteObject(new ListDefaultColumnValue() + { + Path = dict["Path"], + Field = dict["Field"], + Value = dict["Value"] + }); } - WriteObject(dynamicList, true); } } - } - else - { - WriteWarning("List is not a document library"); + else + { + WriteWarning("List is not a document library"); + } } } } diff --git a/src/Commands/Lists/GetList.cs b/src/Commands/Lists/GetList.cs index 805822e0a..5e3686050 100644 --- a/src/Commands/Lists/GetList.cs +++ b/src/Commands/Lists/GetList.cs @@ -10,6 +10,7 @@ namespace PnP.PowerShell.Commands.Lists { [Cmdlet(VerbsCommon.Get, "PnPList")] + [OutputType(typeof(List))] public class GetList : PnPWebRetrievalsCmdlet { [Parameter(Mandatory = false, ValueFromPipeline = true, Position = 0)] diff --git a/src/Commands/Lists/GetListItem.cs b/src/Commands/Lists/GetListItem.cs index d8915d9af..0ac812b69 100644 --- a/src/Commands/Lists/GetListItem.cs +++ b/src/Commands/Lists/GetListItem.cs @@ -10,6 +10,7 @@ namespace PnP.PowerShell.Commands.Lists { [Cmdlet(VerbsCommon.Get, "PnPListItem", DefaultParameterSetName = ParameterSet_ALLITEMS)] + [OutputType(typeof(ListItem))] public class GetListItem : PnPWebCmdlet { private const string ParameterSet_BYID = "By Id"; diff --git a/src/Commands/Lists/GetListItemComment.cs b/src/Commands/Lists/GetListItemComment.cs index b4f65f3d0..3cd1873a3 100644 --- a/src/Commands/Lists/GetListItemComment.cs +++ b/src/Commands/Lists/GetListItemComment.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.Lists { [Cmdlet(VerbsCommon.Get, "PnPListItemComment")] + [OutputType(typeof(ListItemComments))] public class GetListItemComments : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] @@ -53,7 +54,7 @@ protected override void ExecuteCmdlet() ListId = comment.ListId, ParentId = comment.ParentId, ReplyCount = comment.ReplyCount, - Text = comment.Text + Text = comment.Text }; commentsList.Add(commentValue); diff --git a/src/Commands/Lists/GetListPermissions.cs b/src/Commands/Lists/GetListPermissions.cs index 26bd2a1d9..f00a32a30 100644 --- a/src/Commands/Lists/GetListPermissions.cs +++ b/src/Commands/Lists/GetListPermissions.cs @@ -1,11 +1,13 @@ using System.Management.Automation; using Microsoft.SharePoint.Client; +using PnP.Core.Model.Security; using PnP.PowerShell.Commands.Base.PipeBinds; namespace PnP.PowerShell.Commands.Principals { [Cmdlet(VerbsCommon.Get, "PnPListPermissions")] + [OutputType(typeof(IRoleDefinition))] public class GetListPermissions : PnPWebCmdlet { [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ParameterSetName = "ByName")] diff --git a/src/Commands/Lists/GetView.cs b/src/Commands/Lists/GetView.cs index 8cec9bf40..6b9eca72b 100644 --- a/src/Commands/Lists/GetView.cs +++ b/src/Commands/Lists/GetView.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Linq.Expressions; using System.Management.Automation; + using Microsoft.SharePoint.Client; using PnP.PowerShell.Commands.Base; @@ -11,6 +12,7 @@ namespace PnP.PowerShell.Commands.Lists { [Cmdlet(VerbsCommon.Get, "PnPView")] + [OutputType(typeof(View))] public class GetView : PnPWebRetrievalsCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] diff --git a/src/Commands/Lists/NewList.cs b/src/Commands/Lists/NewList.cs index 4e8353b2a..667ca7ac1 100644 --- a/src/Commands/Lists/NewList.cs +++ b/src/Commands/Lists/NewList.cs @@ -6,6 +6,7 @@ namespace PnP.PowerShell.Commands.Lists { [Cmdlet(VerbsCommon.New, "PnPList")] + [OutputType(typeof(List))] public class NewList : PnPWebCmdlet { [Parameter(Mandatory = true)] diff --git a/src/Commands/Lists/RemoveListItemComment.cs b/src/Commands/Lists/RemoveListItemComment.cs index 417143442..d32326c3d 100644 --- a/src/Commands/Lists/RemoveListItemComment.cs +++ b/src/Commands/Lists/RemoveListItemComment.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.Lists { [Cmdlet(VerbsCommon.Remove, "PnPListItemComment")] + [OutputType(typeof(void))] public class RemoveListItemComment : PnPWebCmdlet { private const string ParameterSet_SINGLE = "Single"; diff --git a/src/Commands/Lists/RemoveView.cs b/src/Commands/Lists/RemoveView.cs index 3a210c52a..04af12ec4 100644 --- a/src/Commands/Lists/RemoveView.cs +++ b/src/Commands/Lists/RemoveView.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.Lists { [Cmdlet(VerbsCommon.Remove, "PnPView")] + [OutputType(typeof(void))] public class RemoveView : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] diff --git a/src/Commands/Lists/RequestReIndexList.cs b/src/Commands/Lists/RequestReIndexList.cs index 776c75a27..787ed08fa 100644 --- a/src/Commands/Lists/RequestReIndexList.cs +++ b/src/Commands/Lists/RequestReIndexList.cs @@ -6,6 +6,7 @@ namespace PnP.PowerShell.Commands.Lists { [Cmdlet(VerbsLifecycle.Request, "PnPReIndexList")] + [OutputType(typeof(void))] public class RequestReIndexList : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] diff --git a/src/Commands/Lists/SetDefaultColumnValues.cs b/src/Commands/Lists/SetDefaultColumnValues.cs index 0ec9fb67a..1db0961a1 100644 --- a/src/Commands/Lists/SetDefaultColumnValues.cs +++ b/src/Commands/Lists/SetDefaultColumnValues.cs @@ -9,6 +9,7 @@ namespace PnP.PowerShell.Commands.Lists { [Cmdlet(VerbsCommon.Set, "PnPDefaultColumnValues")] + [OutputType(typeof(void))] public class SetDefaultColumnValues : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] diff --git a/src/Commands/Lists/SetList.cs b/src/Commands/Lists/SetList.cs index 75ae9d999..958f3af1e 100644 --- a/src/Commands/Lists/SetList.cs +++ b/src/Commands/Lists/SetList.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.Lists { [Cmdlet(VerbsCommon.Set, "PnPList")] + [OutputType(typeof(List))] public class SetList : PnPWebCmdlet { [Parameter(Mandatory = true)] @@ -228,6 +229,8 @@ protected override void ExecuteCmdlet() list.Update(); ClientContext.ExecuteQueryRetry(); } + + WriteObject(list); } } } diff --git a/src/Commands/Lists/SetListItem.cs b/src/Commands/Lists/SetListItem.cs index f18b62e35..7c969a9f3 100644 --- a/src/Commands/Lists/SetListItem.cs +++ b/src/Commands/Lists/SetListItem.cs @@ -14,6 +14,7 @@ namespace PnP.PowerShell.Commands.Lists { [Cmdlet(VerbsCommon.Set, "PnPListItem", DefaultParameterSetName = ParameterSet_SINGLE)] + [OutputType(typeof(ListItem))] public class SetListItem : PnPWebCmdlet { const string ParameterSet_SINGLE = "Single"; diff --git a/src/Commands/Lists/SetListItemPermission.cs b/src/Commands/Lists/SetListItemPermission.cs index 01bc98da8..dfb11b59c 100644 --- a/src/Commands/Lists/SetListItemPermission.cs +++ b/src/Commands/Lists/SetListItemPermission.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.Lists { [Cmdlet(VerbsCommon.Set, "PnPListItemPermission", DefaultParameterSetName = ParameterSet_USER)] + [OutputType(typeof(void))] public class SetListItemPermission : PnPWebCmdlet { private const string ParameterSet_GROUP = "Group"; diff --git a/src/Commands/Lists/SetListPermission.cs b/src/Commands/Lists/SetListPermission.cs index 8cdacc88a..e58613fe0 100644 --- a/src/Commands/Lists/SetListPermission.cs +++ b/src/Commands/Lists/SetListPermission.cs @@ -9,6 +9,7 @@ namespace PnP.PowerShell.Commands.Lists { //TODO: Create Test [Cmdlet(VerbsCommon.Set, "PnPListPermission")] + [OutputType(typeof(void))] public class SetListPermission : PnPWebCmdlet { [Parameter(Mandatory = true, ParameterSetName = ParameterAttribute.AllParameterSets)] diff --git a/src/Commands/Lists/SetView.cs b/src/Commands/Lists/SetView.cs index c347ba976..fe92e80f2 100644 --- a/src/Commands/Lists/SetView.cs +++ b/src/Commands/Lists/SetView.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands.Fields { [Cmdlet(VerbsCommon.Set, "PnPView")] + [OutputType(typeof(View))] public class SetView : PnPWebCmdlet { [Parameter(Mandatory = false, Position = 0)] diff --git a/src/Commands/Model/SharePoint/ListDefaultColumnValue.cs b/src/Commands/Model/SharePoint/ListDefaultColumnValue.cs new file mode 100644 index 000000000..c31246113 --- /dev/null +++ b/src/Commands/Model/SharePoint/ListDefaultColumnValue.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace PnP.PowerShell.Commands.Model.SharePoint +{ + public sealed class ListDefaultColumnValue + { + public string Path { get; set; } + public string Field { get; set; } + public string Value { get; set; } + } +} From b83727411aac2d5d8ed2aa6c525add209ae2049a Mon Sep 17 00:00:00 2001 From: James May Date: Thu, 21 Apr 2022 03:52:25 +0000 Subject: [PATCH 124/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index 00c8fd35e..7432d8ce8 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -b3f7b8c57093591667298368f98e9fbc75c597da \ No newline at end of file +0932f585e81bf1895827467c080e3a550bfca59e \ No newline at end of file diff --git a/version.txt b/version.txt index 11ee04cad..888c6bcf7 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.19 \ No newline at end of file +1.10.20 \ No newline at end of file From 33e2a64bdaa25f1e69584414886768710bebfd1d Mon Sep 17 00:00:00 2001 From: James May Date: Wed, 20 Apr 2022 21:21:08 +1000 Subject: [PATCH 125/458] Graph cmdlets: Add missing OutputType attributes for better command completion --- src/Commands/Graph/GetGraphAccessToken.cs | 12 +++++++++--- src/Commands/Graph/GetGraphSubscription.cs | 3 ++- src/Commands/Graph/NewGraphSubscription.cs | 3 ++- src/Commands/Graph/RemoveGraphSubscription.cs | 1 + src/Commands/Graph/RemoveSiteClassification.cs | 1 + src/Commands/Graph/SetGraphSubscription.cs | 1 + 6 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Commands/Graph/GetGraphAccessToken.cs b/src/Commands/Graph/GetGraphAccessToken.cs index a5260ae74..f008cddc2 100644 --- a/src/Commands/Graph/GetGraphAccessToken.cs +++ b/src/Commands/Graph/GetGraphAccessToken.cs @@ -1,19 +1,25 @@  +using System.IdentityModel.Tokens.Jwt; using System.Management.Automation; namespace PnP.PowerShell.Commands.Base { - [Cmdlet(VerbsCommon.Get, "PnPGraphAccessToken")] + [Cmdlet(VerbsCommon.Get, "PnPGraphAccessToken", DefaultParameterSetName = ParameterSet_Encoded)] + [OutputType(typeof(string), ParameterSetName = new[] { ParameterSet_Encoded })] + [OutputType(typeof(JwtSecurityToken), ParameterSetName = new[] { ParameterSet_Decoded })] public class GetGraphAccessToken : PnPGraphCmdlet { - [Parameter(Mandatory = false)] + public const string ParameterSet_Decoded = "Decoded (JwtSecurityToken)"; + public const string ParameterSet_Encoded = "Encoded (string)"; + + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_Decoded)] public SwitchParameter Decoded; protected override void ExecuteCmdlet() { if (Decoded.IsPresent) { - WriteObject(new System.IdentityModel.Tokens.Jwt.JwtSecurityToken(AccessToken)); + WriteObject(new JwtSecurityToken(AccessToken)); } else { diff --git a/src/Commands/Graph/GetGraphSubscription.cs b/src/Commands/Graph/GetGraphSubscription.cs index 8fe6cca08..41f262e5b 100644 --- a/src/Commands/Graph/GetGraphSubscription.cs +++ b/src/Commands/Graph/GetGraphSubscription.cs @@ -6,7 +6,8 @@ namespace PnP.PowerShell.Commands.Graph { [Cmdlet(VerbsCommon.Get, "PnPGraphSubscription", DefaultParameterSetName = ParameterSet_LIST)] - + [OutputType(typeof(Framework.Graph.Model.Subscription))] + // Deliberately omitting the CmdletMicrosoftGraphApiPermission attribute as permissions vary largely by the subscription type being used public class GetGraphSubscription : PnPGraphCmdlet { diff --git a/src/Commands/Graph/NewGraphSubscription.cs b/src/Commands/Graph/NewGraphSubscription.cs index 20604b205..0293a161d 100644 --- a/src/Commands/Graph/NewGraphSubscription.cs +++ b/src/Commands/Graph/NewGraphSubscription.cs @@ -7,7 +7,8 @@ namespace PnP.PowerShell.Commands.Graph { [Cmdlet(VerbsCommon.New, "PnPGraphSubscription")] - + [OutputType(typeof(Framework.Graph.Model.Subscription))] + // Deliberately omitting the CmdletMicrosoftGraphApiPermission attribute as permissions vary largely by the subscription type being used. This means it will not work with an app-only token. public class NewGraphSubscription : PnPGraphCmdlet { diff --git a/src/Commands/Graph/RemoveGraphSubscription.cs b/src/Commands/Graph/RemoveGraphSubscription.cs index 804692d3c..8fa974a12 100644 --- a/src/Commands/Graph/RemoveGraphSubscription.cs +++ b/src/Commands/Graph/RemoveGraphSubscription.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.Graph { [Cmdlet(VerbsCommon.Remove, "PnPGraphSubscription")] + [OutputType(typeof(void))] // Deliberately omitting the CmdletMicrosoftGraphApiPermission attribute as permissions vary largely by the subscription type being used public class RemoveGraphSubscription : PnPGraphCmdlet { diff --git a/src/Commands/Graph/RemoveSiteClassification.cs b/src/Commands/Graph/RemoveSiteClassification.cs index 47700378b..cb4a25965 100644 --- a/src/Commands/Graph/RemoveSiteClassification.cs +++ b/src/Commands/Graph/RemoveSiteClassification.cs @@ -10,6 +10,7 @@ namespace PnP.PowerShell.Commands.Graph { [Cmdlet(VerbsCommon.Remove, "PnPSiteClassification")] [RequiredMinimalApiPermissions("Directory.ReadWrite.All")] + [OutputType(typeof(void))] public class RemoveSiteClassification : PnPGraphCmdlet { diff --git a/src/Commands/Graph/SetGraphSubscription.cs b/src/Commands/Graph/SetGraphSubscription.cs index d4ddb51d5..db2fc2b4b 100644 --- a/src/Commands/Graph/SetGraphSubscription.cs +++ b/src/Commands/Graph/SetGraphSubscription.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands.Graph { [Cmdlet(VerbsCommon.Set, "PnPGraphSubscription")] + [OutputType(typeof(Framework.Graph.Model.Subscription))] public class SetGraphSubscription : PnPGraphCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true)] From 0ffee78ac95b0a2a57f5b6b7be2ee5bd81e30c31 Mon Sep 17 00:00:00 2001 From: James May Date: Wed, 20 Apr 2022 21:22:21 +1000 Subject: [PATCH 126/458] ListDesign cmdlets: Add missing OutputType attributes for better command completion --- src/Commands/ListDesign/AddListDesign.cs | 1 + src/Commands/ListDesign/GetListDesign.cs | 1 + src/Commands/ListDesign/InvokeListDesign.cs | 1 + src/Commands/ListDesign/RemoveListDesign.cs | 1 + 4 files changed, 4 insertions(+) diff --git a/src/Commands/ListDesign/AddListDesign.cs b/src/Commands/ListDesign/AddListDesign.cs index 3e0413745..8fb6339f8 100644 --- a/src/Commands/ListDesign/AddListDesign.cs +++ b/src/Commands/ListDesign/AddListDesign.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.Add, "PnPListDesign")] + [OutputType(typeof(TenantListDesign))] public class AddListDesign : PnPAdminCmdlet { [Parameter(Mandatory = true)] diff --git a/src/Commands/ListDesign/GetListDesign.cs b/src/Commands/ListDesign/GetListDesign.cs index b36ca785f..85a00e2c2 100644 --- a/src/Commands/ListDesign/GetListDesign.cs +++ b/src/Commands/ListDesign/GetListDesign.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.Get, "PnPListDesign")] + [OutputType(typeof(TenantListDesign))] public class GetListDesign : PnPAdminCmdlet { [Parameter(Mandatory = false, Position = 0, ValueFromPipeline = true)] diff --git a/src/Commands/ListDesign/InvokeListDesign.cs b/src/Commands/ListDesign/InvokeListDesign.cs index 87701db54..b004ebddf 100644 --- a/src/Commands/ListDesign/InvokeListDesign.cs +++ b/src/Commands/ListDesign/InvokeListDesign.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsLifecycle.Invoke, "PnPListDesign")] + [OutputType(typeof(TenantSiteScriptActionResult))] public class InvokeListDesign : PnPWebCmdlet { [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true)] diff --git a/src/Commands/ListDesign/RemoveListDesign.cs b/src/Commands/ListDesign/RemoveListDesign.cs index b89852c09..e23ffca02 100644 --- a/src/Commands/ListDesign/RemoveListDesign.cs +++ b/src/Commands/ListDesign/RemoveListDesign.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.Remove, "PnPListDesign")] + [OutputType(typeof(void))] public class RemoveListDesign : PnPAdminCmdlet { [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true)] From d16bd82c7ede74521f6ec825e698f5a2b536410a Mon Sep 17 00:00:00 2001 From: James May Date: Wed, 20 Apr 2022 21:25:39 +1000 Subject: [PATCH 127/458] Webhook cmdlets: Add missing OutputType attributes for better command completion --- src/Commands/Webhooks/AddWebhookSubscription.cs | 1 + src/Commands/Webhooks/GetWebhookSubscriptions.cs | 1 + src/Commands/Webhooks/RemoveWebhookSubscription.cs | 1 + src/Commands/Webhooks/SetWebhookSubscription.cs | 1 + 4 files changed, 4 insertions(+) diff --git a/src/Commands/Webhooks/AddWebhookSubscription.cs b/src/Commands/Webhooks/AddWebhookSubscription.cs index 0d50939ba..6e8093e76 100644 --- a/src/Commands/Webhooks/AddWebhookSubscription.cs +++ b/src/Commands/Webhooks/AddWebhookSubscription.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands.Webhooks { [Cmdlet(VerbsCommon.Add, "PnPWebhookSubscription")] + [OutputType(typeof(WebhookSubscription))] public class AddWebhookSubscription : PnPWebCmdlet { public const int DefaultValidityInDays = 180; // Note: the max is 180 days not 6 months - https://docs.microsoft.com/sharepoint/dev/apis/webhooks/overview-sharepoint-webhooks diff --git a/src/Commands/Webhooks/GetWebhookSubscriptions.cs b/src/Commands/Webhooks/GetWebhookSubscriptions.cs index dc56db508..0efaabaa3 100644 --- a/src/Commands/Webhooks/GetWebhookSubscriptions.cs +++ b/src/Commands/Webhooks/GetWebhookSubscriptions.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands.Webhooks { [Cmdlet(VerbsCommon.Get, "PnPWebhookSubscriptions")] + [OutputType(typeof(WebhookSubscription))] public class GetWebhookSubscriptions : PnPWebCmdlet { [Parameter(Mandatory = false, ValueFromPipeline = true)] diff --git a/src/Commands/Webhooks/RemoveWebhookSubscription.cs b/src/Commands/Webhooks/RemoveWebhookSubscription.cs index e31a0aa61..159427e7e 100644 --- a/src/Commands/Webhooks/RemoveWebhookSubscription.cs +++ b/src/Commands/Webhooks/RemoveWebhookSubscription.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands.Webhooks { [Cmdlet(VerbsCommon.Remove, "PnPWebhookSubscription")] + [OutputType(typeof(void))] public class RemoveWebhookSubscription : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] diff --git a/src/Commands/Webhooks/SetWebhookSubscription.cs b/src/Commands/Webhooks/SetWebhookSubscription.cs index 2ac44b3dc..8cde07718 100644 --- a/src/Commands/Webhooks/SetWebhookSubscription.cs +++ b/src/Commands/Webhooks/SetWebhookSubscription.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands.Webhooks { [Cmdlet(VerbsCommon.Set, "PnPWebhookSubscription")] + [OutputType(typeof(WebhookSubscription))] public class SetWebhookSubscription : PnPWebCmdlet { public const int DefaultValidityInMonths = 6; From bddb8e78ddd45b75002a54d55571405cd3c4ca31 Mon Sep 17 00:00:00 2001 From: James May Date: Wed, 20 Apr 2022 21:29:21 +1000 Subject: [PATCH 128/458] Syntex cmdlets: Add missing OutputType attributes for better command completion --- src/Commands/Syntex/GetSyntexModel.cs | 1 + src/Commands/Syntex/GetSyntexModelPublication.cs | 1 + src/Commands/Syntex/PublishSyntexModel.cs | 1 + .../Syntex/RequestSyntexClassifyAndExtract.cs | 11 +++++++---- src/Commands/Syntex/UnPublishSyntexModel.cs | 1 + 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Commands/Syntex/GetSyntexModel.cs b/src/Commands/Syntex/GetSyntexModel.cs index ea2f99d68..37cbdd17b 100644 --- a/src/Commands/Syntex/GetSyntexModel.cs +++ b/src/Commands/Syntex/GetSyntexModel.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.Syntex { [Cmdlet(VerbsCommon.Get, "PnPSyntexModel")] + [OutputType(typeof(SyntexModel))] public class GetSyntexModel : PnPWebCmdlet { diff --git a/src/Commands/Syntex/GetSyntexModelPublication.cs b/src/Commands/Syntex/GetSyntexModelPublication.cs index 508196fb8..a21ef813b 100644 --- a/src/Commands/Syntex/GetSyntexModelPublication.cs +++ b/src/Commands/Syntex/GetSyntexModelPublication.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.Syntex { [Cmdlet(VerbsCommon.Get, "PnPSyntexModelPublication")] + [OutputType(typeof(Model.Syntex.SyntexModelPublication))] public class GetSyntexModelPublication : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] diff --git a/src/Commands/Syntex/PublishSyntexModel.cs b/src/Commands/Syntex/PublishSyntexModel.cs index 8ab96a143..f06368dc0 100644 --- a/src/Commands/Syntex/PublishSyntexModel.cs +++ b/src/Commands/Syntex/PublishSyntexModel.cs @@ -9,6 +9,7 @@ namespace PnP.PowerShell.Commands.Syntex { [Cmdlet(VerbsData.Publish, "PnPSyntexModel")] + [OutputType(typeof(SyntexPublicationResult))] public class PublishSyntexModel : PnPWebCmdlet { const string ParameterSet_SINGLE = "Single"; diff --git a/src/Commands/Syntex/RequestSyntexClassifyAndExtract.cs b/src/Commands/Syntex/RequestSyntexClassifyAndExtract.cs index fd74ce0b7..3dc57339d 100644 --- a/src/Commands/Syntex/RequestSyntexClassifyAndExtract.cs +++ b/src/Commands/Syntex/RequestSyntexClassifyAndExtract.cs @@ -3,6 +3,8 @@ using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Base.PipeBinds; using PnP.PowerShell.Commands.Model; +using PnP.PowerShell.Commands.Model.Syntex; + using System.Collections.Generic; using System.Linq; using System.Management.Automation; @@ -10,6 +12,7 @@ namespace PnP.PowerShell.Commands.Syntex { [Cmdlet(VerbsLifecycle.Request, "PnPSyntexClassifyAndExtract")] + [OutputType(typeof(SyntexClassifyAndExtractResult))] public class RequestSyntexClassifyAndExtract : PnPWebCmdlet { const string ParameterSet_LIST = "List"; @@ -53,7 +56,7 @@ protected override void ExecuteCmdlet() if (OffPeak) { var classifyAndExtractResult = list.ClassifyAndExtractOffPeak(); - WriteObject(new Model.Syntex.SyntexClassifyAndExtractResult() + WriteObject(new SyntexClassifyAndExtractResult() { Created = classifyAndExtractResult.Created, DeliverDate = classifyAndExtractResult.DeliverDate, @@ -76,7 +79,7 @@ protected override void ExecuteCmdlet() { foreach (var classifyAndExtractResult in classifyAndExtractResults) { - classifyAndExtractResultsOutput.Add(new Model.Syntex.SyntexClassifyAndExtractResult() + classifyAndExtractResultsOutput.Add(new SyntexClassifyAndExtractResult() { Created = classifyAndExtractResult.Created, DeliverDate = classifyAndExtractResult.DeliverDate, @@ -99,7 +102,7 @@ protected override void ExecuteCmdlet() { IFolder folder = Folder.GetFolder(ctx); var classifyAndExtractResult = folder.ClassifyAndExtractOffPeak(); - WriteObject(new Model.Syntex.SyntexClassifyAndExtractResult() + WriteObject(new SyntexClassifyAndExtractResult() { Created = classifyAndExtractResult.Created, DeliverDate = classifyAndExtractResult.DeliverDate, @@ -138,7 +141,7 @@ protected override void ExecuteCmdlet() if (classifyAndExtractResult != null) { - WriteObject(new Model.Syntex.SyntexClassifyAndExtractResult() + WriteObject(new SyntexClassifyAndExtractResult() { Created = classifyAndExtractResult.Created, DeliverDate = classifyAndExtractResult.DeliverDate, diff --git a/src/Commands/Syntex/UnPublishSyntexModel.cs b/src/Commands/Syntex/UnPublishSyntexModel.cs index 6d14eb447..0f2aed5d1 100644 --- a/src/Commands/Syntex/UnPublishSyntexModel.cs +++ b/src/Commands/Syntex/UnPublishSyntexModel.cs @@ -9,6 +9,7 @@ namespace PnP.PowerShell.Commands.Syntex { [Cmdlet(VerbsData.Unpublish, "PnPSyntexModel")] + [OutputType(typeof(SyntexPublicationResult))] public class UnPublishSyntexModel : PnPWebCmdlet { const string ParameterSet_SINGLE = "Single"; From f6fd7fdc9f9a55cb5f4d6028f9ff88e3e8a26cc6 Mon Sep 17 00:00:00 2001 From: James May Date: Wed, 20 Apr 2022 21:31:30 +1000 Subject: [PATCH 129/458] NavigationNode cmdlets: Add missing OutputType attributes for better command completion --- src/Commands/Navigation/AddNavigationNode.cs | 1 + src/Commands/Navigation/GetStructuralNavigationCacheSiteState.cs | 1 + src/Commands/Navigation/GetStructuralNavigationCacheWebState.cs | 1 + src/Commands/Navigation/RemoveNavigationNode.cs | 1 + src/Commands/Navigation/SetStructuralNavigationCacheSiteState.cs | 1 + src/Commands/Navigation/SetStructuralNavigationCacheWebState.cs | 1 + 6 files changed, 6 insertions(+) diff --git a/src/Commands/Navigation/AddNavigationNode.cs b/src/Commands/Navigation/AddNavigationNode.cs index d0b15ea37..c0c718c10 100644 --- a/src/Commands/Navigation/AddNavigationNode.cs +++ b/src/Commands/Navigation/AddNavigationNode.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.Branding { [Cmdlet(VerbsCommon.Add, "PnPNavigationNode")] + [OutputType(typeof(NavigationNode))] public class AddNavigationNode : PnPWebCmdlet { [Parameter(Mandatory = true)] diff --git a/src/Commands/Navigation/GetStructuralNavigationCacheSiteState.cs b/src/Commands/Navigation/GetStructuralNavigationCacheSiteState.cs index bd708d176..946000c22 100644 --- a/src/Commands/Navigation/GetStructuralNavigationCacheSiteState.cs +++ b/src/Commands/Navigation/GetStructuralNavigationCacheSiteState.cs @@ -5,6 +5,7 @@ namespace PnP.PowerShell.Commands.Site { [Cmdlet(VerbsCommon.Get, "PnPStructuralNavigationCacheSiteState")] + [OutputType(typeof(bool))] public class GetStructuralNavigationCacheSiteState : PnPAdminCmdlet { [Parameter(Mandatory = false, ValueFromPipeline = true)] diff --git a/src/Commands/Navigation/GetStructuralNavigationCacheWebState.cs b/src/Commands/Navigation/GetStructuralNavigationCacheWebState.cs index 22b7b1645..8d630b582 100644 --- a/src/Commands/Navigation/GetStructuralNavigationCacheWebState.cs +++ b/src/Commands/Navigation/GetStructuralNavigationCacheWebState.cs @@ -5,6 +5,7 @@ namespace PnP.PowerShell.Commands.Site { [Cmdlet(VerbsCommon.Get, "PnPStructuralNavigationCacheWebState")] + [OutputType(typeof(bool))] public class GetStructuralNavigationCacheWebState : PnPAdminCmdlet { [Parameter(Mandatory = false, ValueFromPipeline = true)] diff --git a/src/Commands/Navigation/RemoveNavigationNode.cs b/src/Commands/Navigation/RemoveNavigationNode.cs index dc50131d6..ac2a13d96 100644 --- a/src/Commands/Navigation/RemoveNavigationNode.cs +++ b/src/Commands/Navigation/RemoveNavigationNode.cs @@ -9,6 +9,7 @@ namespace PnP.PowerShell.Commands.Branding { [Cmdlet(VerbsCommon.Remove, "PnPNavigationNode", DefaultParameterSetName = ParameterSet_BYID)] + [OutputType(typeof(void))] public class RemoveNavigationNode : PnPWebCmdlet { private const string ParameterSet_BYNAME = "Remove node by Title"; diff --git a/src/Commands/Navigation/SetStructuralNavigationCacheSiteState.cs b/src/Commands/Navigation/SetStructuralNavigationCacheSiteState.cs index 9a85230ce..24a42f9a4 100644 --- a/src/Commands/Navigation/SetStructuralNavigationCacheSiteState.cs +++ b/src/Commands/Navigation/SetStructuralNavigationCacheSiteState.cs @@ -5,6 +5,7 @@ namespace PnP.PowerShell.Commands.Site { [Cmdlet(VerbsCommon.Set, "PnPStructuralNavigationCacheSiteState")] + [OutputType(typeof(void))] public class SetStructuralNavigationCacheSiteState : PnPAdminCmdlet { [Parameter(Mandatory = false, ValueFromPipeline = true)] diff --git a/src/Commands/Navigation/SetStructuralNavigationCacheWebState.cs b/src/Commands/Navigation/SetStructuralNavigationCacheWebState.cs index 4a4398f7d..76c7da552 100644 --- a/src/Commands/Navigation/SetStructuralNavigationCacheWebState.cs +++ b/src/Commands/Navigation/SetStructuralNavigationCacheWebState.cs @@ -5,6 +5,7 @@ namespace PnP.PowerShell.Commands.Site { [Cmdlet(VerbsCommon.Set, "PnPStructuralNavigationCacheWebState")] + [OutputType(typeof(void))] public class SetStructuralNavigationCacheWebState : PnPAdminCmdlet { [Parameter(Mandatory = false, ValueFromPipeline = true)] From a3523e35bdd70ad62d647d4317745aedb148262b Mon Sep 17 00:00:00 2001 From: James May Date: Wed, 20 Apr 2022 21:31:44 +1000 Subject: [PATCH 130/458] GetUnifiedAuditLog: Add missing OutputType attribute for better command completion --- src/Commands/ManagementApi/GetUnifiedAuditLog.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Commands/ManagementApi/GetUnifiedAuditLog.cs b/src/Commands/ManagementApi/GetUnifiedAuditLog.cs index 6697eac2c..bafebdf1e 100644 --- a/src/Commands/ManagementApi/GetUnifiedAuditLog.cs +++ b/src/Commands/ManagementApi/GetUnifiedAuditLog.cs @@ -12,6 +12,7 @@ namespace PnP.PowerShell.Commands.ManagementApi { [Cmdlet(VerbsCommon.Get, "PnPUnifiedAuditLog")] [RequiredMinimalApiPermissions("https://manage.office.com/ActivityFeed.Read")] + [OutputType(typeof(ManagementApiUnifiedLogRecord))] public class GetUnifiedAuditLog : PnPOfficeManagementApiCmdlet { private const string ParameterSet_LogsByDate = "Logs by date"; From 36730ce683285abc9963624e761a9fc7de867479 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 21 Apr 2022 17:26:13 +0300 Subject: [PATCH 131/458] Feature: Added sensitivity label to New-PnPTeamsTeam and New-Microsoft365Group --- CHANGELOG.md | 3 +- documentation/New-PnPMicrosoft365Group.md | 20 +++++++++++ documentation/New-PnPTeamsTeam.md | 23 ++++++++++++- .../NewMicrosoft365Group.cs | 34 ++++++++++++++++--- src/Commands/Model/Graph/Group.cs | 1 + src/Commands/Model/Microsoft365Group.cs | 12 +++++-- src/Commands/Teams/NewTeamsTeam.cs | 22 +++++++++--- .../Utilities/Microsoft365GroupsUtility.cs | 27 ++++++++++++--- src/Commands/Utilities/TeamsUtility.cs | 24 +++++++++++-- 9 files changed, 145 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1becc58c7..033a2e5b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `ResourceBehaviorOptions` option in `New-PnPMicrosoft365Group` cmdlet to set `ResourceBehaviorOptions` while provisioning a Microsoft 365 Group. - Added `Add-PnPTeamsChannelUser` which allows members and owners to be added to private channels in Teams [#1735](https://github.com/pnp/powershell/pull/1735) - Added `ExcludeVisualPromotedResults` parameter to `Get-PnPSearchConfiguration` which excludes promoted results [#1750](https://github.com/pnp/powershell/pull/1750) -- Added `MediaTranscription` parameter to `Set-PnPTenantSite` and `Set-PnPSite` cmdlets which when enabled allows videos to have transcripts generated on demand or generated automatically in certain scenarios +- Added `MediaTranscription` parameter to `Set-PnPTenantSite` and `Set-PnPSite` cmdlets which when enabled allows videos to have transcripts generated on demand or generated automatically in certain scenarios. +- Added `-SensitivityLabels` parameter to `New-PnPTeamsTeam` and `New-PnPMicrosoft365Group` cmdlets to apply sensitivity label to the Microsoft 365 Group and Team. ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) diff --git a/documentation/New-PnPMicrosoft365Group.md b/documentation/New-PnPMicrosoft365Group.md index 615428396..82539d12a 100644 --- a/documentation/New-PnPMicrosoft365Group.md +++ b/documentation/New-PnPMicrosoft365Group.md @@ -64,6 +64,13 @@ New-PnPMicrosoft365Group -DisplayName "myPnPDemo1" -Description $description -Ma This will create a new Microsoft 365 Group called "myPnPDemo1" and sets the privacy to Private. Welcome Email will not be sent when the Group is created. The M365 Group will also not be visible in Outlook. +### EXAMPLE 6 +```powershell +New-PnPMicrosoft365Group -DisplayName $displayName -Description $description -MailNickname $nickname -IsPrivate -SensitivityLabels "bc98af29-59eb-4869-baaa-9a8dff631aa4" +``` + +Creates a private Microsoft 365 Group with all the required properties and applies the sensitivity label. + ## PARAMETERS ### -CreateTeam @@ -236,6 +243,19 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -SensitivityLabels +The Sensitivity label to be set to the Microsoft 365 Group. To retrieve the sensitivity label you need to use the Graph API mentioned [here](https://docs.microsoft.com/en-us/graph/api/informationprotectionlabel-get?view=graph-rest-beta&tabs=http). + +```yaml +Type: GUID[] +Parameter Sets: (All) +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) diff --git a/documentation/New-PnPTeamsTeam.md b/documentation/New-PnPTeamsTeam.md index 222075549..2911941fb 100644 --- a/documentation/New-PnPTeamsTeam.md +++ b/documentation/New-PnPTeamsTeam.md @@ -29,7 +29,7 @@ New-PnPTeamsTeam -GroupId [-AllowAddRemoveApps ] [-AllowOwnerDeleteMessages ] [-AllowStickersAndMemes ] [-AllowTeamMentions ] [-AllowUserDeleteMessages ] [-AllowUserEditMessages ] [-GiphyContentRating ] [-ShowInTeamsSearchAndSuggestions ] - [-Classification ] [-Owners ] [-Members ] [] + [-Classification ] [-Owners ] [-Members ] [] ``` ### For a new group @@ -45,6 +45,7 @@ New-PnPTeamsTeam -DisplayName [-MailNickName ] [-Description ] [-Classification ] [-Owners ] [-Members ] [-ResourceBehaviorOptions ] + [-SensitivityLabels ] [] ``` @@ -88,6 +89,13 @@ New-PnPTeamsTeam -DisplayName "myPnPDemo1" -Visibility Private -Owners "user1@co This will create a new Microsoft Teams team called "myPnPDemo1" and sets the privacy to Private. User1 and user2 will be added as owners. User3 will be added as a member. +### EXAMPLE 6 +```powershell +New-PnPTeamsTeam -DisplayName "myPnPDemo1" -Visibility Private -Owners "user1@contoso.onmicrosoft.com","user2@contoso.onmicrosoft.com" -Members "user3@contoso.onmicrosoft.com" -SensitivityLabels "bc98af29-59eb-4869-baaa-9a8dff631aa4" +``` + +This will create a new Microsoft Teams team called "myPnPDemo1" and sets the privacy to Private. User1 and user2 will be added as owners. User3 will be added as a member. The team will also get the sensitivity label value corresponding to the GUID specified. + ## PARAMETERS ### -AllowAddRemoveApps @@ -472,6 +480,19 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -SensitivityLabels +The Sensitivity label to be set to the Microsoft 365 Group and Team. To retrieve the sensitivity label you need to use the Graph API mentioned [here](https://docs.microsoft.com/en-us/graph/api/informationprotectionlabel-get?view=graph-rest-beta&tabs=http). + +```yaml +Type: GUID[] +Parameter Sets: For a new group +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) diff --git a/src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs b/src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs index 114bc530a..77611f4c8 100644 --- a/src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs +++ b/src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs @@ -1,4 +1,5 @@ -using PnP.Framework.Graph; +using Microsoft.SharePoint.Client; +using PnP.Framework.Graph; using PnP.PowerShell.Commands.Attributes; using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Enums; @@ -53,6 +54,9 @@ public class NewPnPMicrosoft365Group : PnPGraphCmdlet [Parameter(Mandatory = false)] public TeamResourceBehaviorOptions?[] ResourceBehaviorOptions; + [Parameter(Mandatory = false)] + public Guid[] SensitivityLabels; + protected override void ExecuteCmdlet() { if (MailNickname.Contains(" ")) @@ -94,7 +98,7 @@ protected override void ExecuteCmdlet() SecurityEnabled = false, GroupTypes = new string[] { "Unified" } }; - + if (ResourceBehaviorOptions != null && ResourceBehaviorOptions.Length > 0) { var teamResourceBehaviorOptionsValue = new List(); @@ -105,13 +109,33 @@ protected override void ExecuteCmdlet() newGroup.ResourceBehaviorOptions = teamResourceBehaviorOptionsValue.ToArray(); } - var group = Microsoft365GroupsUtility.CreateAsync(HttpClient, AccessToken, newGroup, CreateTeam, LogoPath, Owners, Members, HideFromAddressLists, HideFromOutlookClients).GetAwaiter().GetResult(); - + var Labels = new List(); + var contextSettings = PnPConnection.Current.Context.GetContextSettings(); + if (SensitivityLabels != null && SensitivityLabels.Length > 0) + { + if (contextSettings.Type != Framework.Utilities.Context.ClientContextType.AzureADCertificate) + { + foreach (var label in SensitivityLabels) + { + if (!Guid.Empty.Equals(label)) + { + Labels.Add(label.ToString()); + } + } + } + else + { + WriteWarning("Adding sensitivity labels in App-only context is not supported by Graph API, so it will be skipped in Group creation"); + } + } + + var group = Microsoft365GroupsUtility.CreateAsync(HttpClient, AccessToken, newGroup, CreateTeam, LogoPath, Owners, Members, HideFromAddressLists, HideFromOutlookClients, Labels).GetAwaiter().GetResult(); + if (ParameterSpecified(nameof(HideFromAddressLists)) || ParameterSpecified(nameof(HideFromOutlookClients))) { Microsoft365GroupsUtility.SetVisibilityAsync(HttpClient, AccessToken, group.Id.Value, HideFromAddressLists, HideFromOutlookClients).GetAwaiter().GetResult(); } - + var updatedGroup = Microsoft365GroupsUtility.GetGroupAsync(HttpClient, group.Id.Value, AccessToken, true, false).GetAwaiter().GetResult(); WriteObject(updatedGroup); diff --git a/src/Commands/Model/Graph/Group.cs b/src/Commands/Model/Graph/Group.cs index 85b752ad1..9c0808737 100644 --- a/src/Commands/Model/Graph/Group.cs +++ b/src/Commands/Model/Graph/Group.cs @@ -33,6 +33,7 @@ public class Group public string EducationObjectType { get; set; } public List ResourceBehaviorOptions { get; set; } + public List AssignedLabels { get; set; } } public enum GroupVisibility diff --git a/src/Commands/Model/Microsoft365Group.cs b/src/Commands/Model/Microsoft365Group.cs index 744a5ecba..189e69396 100644 --- a/src/Commands/Model/Microsoft365Group.cs +++ b/src/Commands/Model/Microsoft365Group.cs @@ -46,14 +46,15 @@ public string GroupId public DateTimeOffset? RenewedDateTime { get; set; } public string[] ResourceBehaviorOptions { get; set; } public string[] ResourceProvisioningOptions { get; set; } - public bool SecurityEnabled { get; set; } public string SecurityIdentified { get; set; } public string Theme { get; set; } public string Visibility { get; set; } public string SiteUrl { get; set; } public string[] GroupTypes { get; set; } - public IEnumerable Owners {get;set;} + public IEnumerable Owners { get; set; } + + public List AssignedLabels { get; set; } [JsonIgnore] public bool HasTeam @@ -68,4 +69,11 @@ public bool HasTeam } } } + + public class AssignedLabels + { + public string labelId { get; set; } + + public string displayName { get; set; } + } } \ No newline at end of file diff --git a/src/Commands/Teams/NewTeamsTeam.cs b/src/Commands/Teams/NewTeamsTeam.cs index aa8717f29..da7df490f 100644 --- a/src/Commands/Teams/NewTeamsTeam.cs +++ b/src/Commands/Teams/NewTeamsTeam.cs @@ -1,4 +1,5 @@ -using PnP.Framework.Provisioning.Model.Teams; +using Microsoft.SharePoint.Client; +using PnP.Framework.Provisioning.Model.Teams; using PnP.PowerShell.Commands.Attributes; using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Enums; @@ -108,6 +109,9 @@ public class NewTeamsTeam : PnPGraphCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_NEWGROUP)] public TeamResourceBehaviorOptions?[] ResourceBehaviorOptions; + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_NEWGROUP)] + public Guid[] SensitivityLabels; + protected override void ExecuteCmdlet() { var teamCI = new TeamCreationInformation() @@ -137,15 +141,25 @@ protected override void ExecuteCmdlet() AllowCreatePrivateChannels = AllowCreatePrivateChannels }; - #pragma warning disable 612, 618 // Disables the obsolete warning for the compiler output +#pragma warning disable 612, 618 // Disables the obsolete warning for the compiler output if (!string.IsNullOrWhiteSpace(Owner)) { // Adding Owner parameter to the Owners array for backwards compatibility Owners = Owners != null ? Owners.Concat(new[] { Owner }).ToArray() : new[] { Owner }; } - #pragma warning restore 612, 618 +#pragma warning restore 612, 618 + + var contextSettings = PnPConnection.Current.Context.GetContextSettings(); + if (contextSettings.Type != Framework.Utilities.Context.ClientContextType.AzureADCertificate) + { + if (SensitivityLabels != null && SensitivityLabels.Length > 0) + { + SensitivityLabels = null; + WriteWarning("Adding sensitivity labels in App-only context is not supported by Graph API, so it will be skipped in Team creation"); + } + } - WriteObject(TeamsUtility.NewTeamAsync(AccessToken, HttpClient, GroupId, DisplayName, Description, Classification, MailNickName, (GroupVisibility)Enum.Parse(typeof(GroupVisibility), Visibility.ToString()), teamCI, Owners, Members, Template, ResourceBehaviorOptions).GetAwaiter().GetResult()); + WriteObject(TeamsUtility.NewTeamAsync(AccessToken, HttpClient, GroupId, DisplayName, Description, Classification, MailNickName, (GroupVisibility)Enum.Parse(typeof(GroupVisibility), Visibility.ToString()), teamCI, Owners, Members, SensitivityLabels, Template, ResourceBehaviorOptions).GetAwaiter().GetResult()); } } } \ No newline at end of file diff --git a/src/Commands/Utilities/Microsoft365GroupsUtility.cs b/src/Commands/Utilities/Microsoft365GroupsUtility.cs index 9c02fb63e..562b59609 100644 --- a/src/Commands/Utilities/Microsoft365GroupsUtility.cs +++ b/src/Commands/Utilities/Microsoft365GroupsUtility.cs @@ -97,7 +97,7 @@ internal static async Task GetGroupAsync(HttpClient httpClien } internal static async Task GetGroupAsync(HttpClient httpClient, string displayName, string accessToken, bool includeSiteUrl, bool includeOwners) { - var results = await GraphHelper.GetAsync>(httpClient, $"v1.0/groups?$filter=displayName eq '{displayName}' or mailNickName eq '{displayName}'", accessToken); + var results = await GraphHelper.GetAsync>(httpClient, $"v1.0/groups?$filter=groupTypes/any(c:c+eq+'Unified') and displayName eq '{displayName}' or mailNickName eq '{displayName}'", accessToken); if (results != null && results.Items.Any()) { var group = results.Items.First(); @@ -162,7 +162,7 @@ internal static async Task AddMembersAsync(HttpClient httpClient, Guid groupId, internal static string GetUserGraphUrlForUPN(string upn) { - + var escapedUpn = upn.Replace("#", "%23"); if (escapedUpn.StartsWith("$")) return $"users('{escapedUpn}')"; @@ -366,7 +366,7 @@ internal static async Task GetUsersDataBindValueAsync(HttpClient httpC return null; } - internal static async Task CreateAsync(HttpClient httpClient, string accessToken, Microsoft365Group group, bool createTeam, string logoPath, string[] owners, string[] members, bool? hideFromAddressLists, bool? hideFromOutlookClients) + internal static async Task CreateAsync(HttpClient httpClient, string accessToken, Microsoft365Group group, bool createTeam, string logoPath, string[] owners, string[] members, bool? hideFromAddressLists, bool? hideFromOutlookClients, List sensitivityLabels) { if (owners != null && owners.Length > 0) { @@ -378,6 +378,23 @@ internal static async Task CreateAsync(HttpClient httpClient, group.MembersODataBind = await GetUsersDataBindValueAsync(httpClient, accessToken, members); } + if (sensitivityLabels.Count > 0) + { + var assignedLabels = new List(); + foreach (var label in sensitivityLabels) + { + if (!Guid.Empty.Equals(label)) + { + assignedLabels.Add(new AssignedLabels + { + labelId = label + }); + } + } + + group.AssignedLabels = assignedLabels; + } + var newGroup = await GraphHelper.PostAsync(httpClient, "v1.0/groups", group, accessToken); if (hideFromAddressLists.HasValue || hideFromOutlookClients.HasValue) @@ -569,12 +586,12 @@ internal static async Task UpdateGroupSetting(HttpClient httpClient, string acce } internal static async Task RemoveGroupSetting(HttpClient httpClient, string accessToken, string id) - { + { await GraphHelper.DeleteAsync(httpClient, $"v1.0/groupSettings/{id}", accessToken); } internal static async Task RemoveGroupSetting(HttpClient httpClient, string accessToken, string id, string groupId) - { + { await GraphHelper.DeleteAsync(httpClient, $"v1.0/groups/{groupId}/settings/{id}", accessToken); } diff --git a/src/Commands/Utilities/TeamsUtility.cs b/src/Commands/Utilities/TeamsUtility.cs index 8eb3ef7a1..1f4facbd6 100644 --- a/src/Commands/Utilities/TeamsUtility.cs +++ b/src/Commands/Utilities/TeamsUtility.cs @@ -1,6 +1,7 @@ using Microsoft.SharePoint.Client; using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Enums; +using PnP.PowerShell.Commands.Model; using PnP.PowerShell.Commands.Model.Graph; using PnP.PowerShell.Commands.Model.Teams; using PnP.PowerShell.Commands.Utilities.REST; @@ -120,7 +121,7 @@ private static async Task ParseTeamJsonAsync(string accessToken, HttpClien } } - public static async Task NewTeamAsync(string accessToken, HttpClient httpClient, string groupId, string displayName, string description, string classification, string mailNickname, GroupVisibility visibility, TeamCreationInformation teamCI, string[] owners, string[] members, TeamsTemplateType templateType = TeamsTemplateType.None, TeamResourceBehaviorOptions?[] resourceBehaviorOptions = null) + public static async Task NewTeamAsync(string accessToken, HttpClient httpClient, string groupId, string displayName, string description, string classification, string mailNickname, GroupVisibility visibility, TeamCreationInformation teamCI, string[] owners, string[] members, Guid[] sensitivityLabels, TeamsTemplateType templateType = TeamsTemplateType.None, TeamResourceBehaviorOptions?[] resourceBehaviorOptions = null) { Group group = null; Team returnTeam = null; @@ -128,7 +129,7 @@ public static async Task NewTeamAsync(string accessToken, HttpClient httpC // Create the Group if (string.IsNullOrEmpty(groupId)) { - group = await CreateGroupAsync(accessToken, httpClient, displayName, description, classification, mailNickname, visibility, owners, templateType, resourceBehaviorOptions); + group = await CreateGroupAsync(accessToken, httpClient, displayName, description, classification, mailNickname, visibility, owners, sensitivityLabels, templateType, resourceBehaviorOptions); bool wait = true; int iterations = 0; while (wait) @@ -236,7 +237,7 @@ internal static string GetUserGraphUrlForUPN(string upn) return $"users/{escapedUpn}"; } - private static async Task CreateGroupAsync(string accessToken, HttpClient httpClient, string displayName, string description, string classification, string mailNickname, GroupVisibility visibility, string[] owners, TeamsTemplateType templateType = TeamsTemplateType.None, TeamResourceBehaviorOptions?[] resourceBehaviorOptions = null) + private static async Task CreateGroupAsync(string accessToken, HttpClient httpClient, string displayName, string description, string classification, string mailNickname, GroupVisibility visibility, string[] owners, Guid[] sensitivityLabels, TeamsTemplateType templateType = TeamsTemplateType.None, TeamResourceBehaviorOptions?[] resourceBehaviorOptions = null) { // When creating a group, we always need an owner, thus we'll try to define it from the passed in owners array string ownerId = null; @@ -311,6 +312,23 @@ private static async Task CreateGroupAsync(string accessToken, HttpClient group.ResourceBehaviorOptions = teamResourceBehaviorOptionsValue; } + if (sensitivityLabels!= null && sensitivityLabels.Length > 0) + { + var assignedLabels = new List(); + foreach (var label in sensitivityLabels) + { + if (!Guid.Empty.Equals(label)) + { + assignedLabels.Add(new AssignedLabels + { + labelId = label.ToString() + }); + } + } + + group.AssignedLabels = assignedLabels; + } + switch (templateType) { case TeamsTemplateType.EDU_Class: From 747eadd4a5b666b9d274c6fa1b24b101962bffb8 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 21 Apr 2022 18:10:20 +0300 Subject: [PATCH 132/458] Fix build pipeline --- src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs b/src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs index 77611f4c8..cc7ddae5f 100644 --- a/src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs +++ b/src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs @@ -68,7 +68,7 @@ protected override void ExecuteCmdlet() if (!Force) { var candidate = Microsoft365GroupsUtility.GetGroupAsync(HttpClient, MailNickname, AccessToken, false, false).GetAwaiter().GetResult(); - forceCreation = candidate == null || ShouldContinue($"The Microsoft 365 Group '{MailNickname} already exists. Do you want to create a new one?", Resources.Confirm); + forceCreation = candidate == null || ShouldContinue($"The Microsoft 365 Group '{MailNickname} already exists. Do you want to create a new one?", Properties.Resources.Confirm); } else { From 52636fc6a6f541ea425aa816d73f2a66ae126b90 Mon Sep 17 00:00:00 2001 From: James May Date: Fri, 22 Apr 2022 03:59:44 +0000 Subject: [PATCH 133/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index 7432d8ce8..e036eaafd 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -0932f585e81bf1895827467c080e3a550bfca59e \ No newline at end of file +4d4afd92ae134bec2996b6f998d961f9003f2baf \ No newline at end of file diff --git a/version.txt b/version.txt index 888c6bcf7..67a15389c 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.20 \ No newline at end of file +1.10.21 \ No newline at end of file From 1dd7300f500dc03695cd6e06f4cb7b2343524676 Mon Sep 17 00:00:00 2001 From: Aleks Date: Fri, 22 Apr 2022 09:53:04 +0200 Subject: [PATCH 134/458] removing old files --- docker/build.ps1 | 1 - docker/powershell/installModules.ps1 | 20 -------------------- docker/runlocal.ps1 | 1 - 3 files changed, 22 deletions(-) delete mode 100644 docker/build.ps1 delete mode 100644 docker/powershell/installModules.ps1 delete mode 100644 docker/runlocal.ps1 diff --git a/docker/build.ps1 b/docker/build.ps1 deleted file mode 100644 index cbb23ef33..000000000 --- a/docker/build.ps1 +++ /dev/null @@ -1 +0,0 @@ -docker build -t pnppowershell -f ./pnppowershell.dockerFile . --no-cache \ No newline at end of file diff --git a/docker/powershell/installModules.ps1 b/docker/powershell/installModules.ps1 deleted file mode 100644 index fd7099c93..000000000 --- a/docker/powershell/installModules.ps1 +++ /dev/null @@ -1,20 +0,0 @@ -$modules = @( - "PnP.PowerShell", - "Microsoft.PowerShell.SecretManagement", - "Microsoft.PowerShell.SecretStore" -) - -Set-PSRepository -Name "PSGallery" -InstallationPolicy Trusted -$ProgressPreference = "SilentlyContinue" - -foreach($module in $modules) -{ - Write-Host "Installing $module" - Install-Module -Name $module -AllowPrerelease | Out-Null -} - -Register-SecretVault -Name "SecretStore" -ModuleName "Microsoft.PowerShell.SecretStore" -DefaultVault -Set-SecretStoreConfiguration -Authentation None - -$userProfile = "Import-Module -Name PnP.PowerShell" -Set-Content -Path $PROFILE.AllUsersAllHosts -Value $userProfile -Force \ No newline at end of file diff --git a/docker/runlocal.ps1 b/docker/runlocal.ps1 deleted file mode 100644 index f9f1dfb9b..000000000 --- a/docker/runlocal.ps1 +++ /dev/null @@ -1 +0,0 @@ -docker run -it pnppowershell \ No newline at end of file From 58f50ba4d6a0a0f4259251cd9d92a92d7f42dac7 Mon Sep 17 00:00:00 2001 From: Aleks Date: Fri, 22 Apr 2022 09:54:07 +0200 Subject: [PATCH 135/458] new line at end of file --- docker/Publish-UnpublishedImage.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 index 55f1183ca..fc259f547 100644 --- a/docker/Publish-UnpublishedImage.ps1 +++ b/docker/Publish-UnpublishedImage.ps1 @@ -35,4 +35,4 @@ $moduleVersions | % { docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$moduleVersion; docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; } -} \ No newline at end of file +} From fcae6966083376668a4da75ee7759bf04ec8fd76 Mon Sep 17 00:00:00 2001 From: Aleks Date: Fri, 22 Apr 2022 10:04:30 +0200 Subject: [PATCH 136/458] more documentation for manual publishing --- docker/README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docker/README.md b/docker/README.md index 736fa32ff..e039598e1 100644 --- a/docker/README.md +++ b/docker/README.md @@ -8,6 +8,17 @@ docker run --rm -it -v $(pwd):/home/powershell mcr.microsoft.com/dotnet/sdk:6.0 /home/powershell/docker/build-module-in-linux.ps1 ``` +# Publish manually + +1. Set "DOCKER_USERNAME" and "DOCKER_PASSWORD" variables + +2. Run + +```powershell +$securedPassword = ConvertTo-SecureString $DOCKER_PASSWORD -AsPlainText -Force +./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME powershell $securedPassword +``` + # Publish with prereleases manually 1. Set "DOCKER_USERNAME" and "DOCKER_PASSWORD" variables From 2fc111fa3f0f6af0f6b9445b707fb103d088e051 Mon Sep 17 00:00:00 2001 From: Aleks Date: Fri, 22 Apr 2022 10:06:31 +0200 Subject: [PATCH 137/458] removing unneccessary (so far) documentation --- docker/README.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/docker/README.md b/docker/README.md index e039598e1..cc61e4c4d 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,13 +1,3 @@ -# Build module in Docker in Linux - -```bash -docker run --rm -it -v $(pwd):/home/powershell mcr.microsoft.com/dotnet/sdk:6.0 pwsh -``` - -```powershell -/home/powershell/docker/build-module-in-linux.ps1 -``` - # Publish manually 1. Set "DOCKER_USERNAME" and "DOCKER_PASSWORD" variables From d42df0e189c9a5099bccb8b0bc573a3971cf6e51 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Fri, 22 Apr 2022 15:17:32 +0300 Subject: [PATCH 138/458] Update NewTeamsTeam.cs --- src/Commands/Teams/NewTeamsTeam.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Commands/Teams/NewTeamsTeam.cs b/src/Commands/Teams/NewTeamsTeam.cs index da7df490f..f2930c651 100644 --- a/src/Commands/Teams/NewTeamsTeam.cs +++ b/src/Commands/Teams/NewTeamsTeam.cs @@ -150,7 +150,7 @@ protected override void ExecuteCmdlet() #pragma warning restore 612, 618 var contextSettings = PnPConnection.Current.Context.GetContextSettings(); - if (contextSettings.Type != Framework.Utilities.Context.ClientContextType.AzureADCertificate) + if (contextSettings.Type == Framework.Utilities.Context.ClientContextType.AzureADCertificate) { if (SensitivityLabels != null && SensitivityLabels.Length > 0) { @@ -162,4 +162,4 @@ protected override void ExecuteCmdlet() WriteObject(TeamsUtility.NewTeamAsync(AccessToken, HttpClient, GroupId, DisplayName, Description, Classification, MailNickName, (GroupVisibility)Enum.Parse(typeof(GroupVisibility), Visibility.ToString()), teamCI, Owners, Members, SensitivityLabels, Template, ResourceBehaviorOptions).GetAwaiter().GetResult()); } } -} \ No newline at end of file +} From 5777d62c45e32a982c8a0bd3a3b976d6b349e2bf Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Fri, 22 Apr 2022 16:02:49 +0300 Subject: [PATCH 139/458] Added sensitivity label for update M365 groups --- CHANGELOG.md | 1 + documentation/New-PnPMicrosoft365Group.md | 2 +- documentation/Set-PnPMicrosoft365Group.md | 22 ++++++++++++- .../SetMicrosoft365Group.cs | 32 ++++++++++++++++++- .../Utilities/Microsoft365GroupsUtility.cs | 32 ++++++++++++++++++- 5 files changed, 85 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 033a2e5b2..d87689b25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `ExcludeVisualPromotedResults` parameter to `Get-PnPSearchConfiguration` which excludes promoted results [#1750](https://github.com/pnp/powershell/pull/1750) - Added `MediaTranscription` parameter to `Set-PnPTenantSite` and `Set-PnPSite` cmdlets which when enabled allows videos to have transcripts generated on demand or generated automatically in certain scenarios. - Added `-SensitivityLabels` parameter to `New-PnPTeamsTeam` and `New-PnPMicrosoft365Group` cmdlets to apply sensitivity label to the Microsoft 365 Group and Team. +- Added `-SensitivityLabels` parameter to `Set-PnPMicrosoft365Group` cmdlets to apply sensitivity label to the Microsoft 365 Group and Team. ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) diff --git a/documentation/New-PnPMicrosoft365Group.md b/documentation/New-PnPMicrosoft365Group.md index 82539d12a..59d7dfed2 100644 --- a/documentation/New-PnPMicrosoft365Group.md +++ b/documentation/New-PnPMicrosoft365Group.md @@ -21,7 +21,7 @@ Creates a new Microsoft 365 Group ```powershell New-PnPMicrosoft365Group -DisplayName -Description -MailNickname - [-Owners ] [-Members ] [-IsPrivate] [-LogoPath ] [-CreateTeam] [-HideFromAddressLists ] [-HideFromOutlookClients ] [-ResourceBehaviorOptions ] [-Force] + [-Owners ] [-Members ] [-IsPrivate] [-LogoPath ] [-CreateTeam] [-HideFromAddressLists ] [-HideFromOutlookClients ] [-ResourceBehaviorOptions ] [-Force] [-SensitivityLabels ] [] ``` diff --git a/documentation/Set-PnPMicrosoft365Group.md b/documentation/Set-PnPMicrosoft365Group.md index e4cb7e7ca..40a6042a0 100644 --- a/documentation/Set-PnPMicrosoft365Group.md +++ b/documentation/Set-PnPMicrosoft365Group.md @@ -22,7 +22,7 @@ Sets Microsoft 365 Group properties ```powershell Set-PnPMicrosoft365Group -Identity [-DisplayName ] [-Description ] [-Owners ] [-Members ] [-IsPrivate] [-LogoPath ] [-CreateTeam] - [-HideFromAddressLists ] [-HideFromOutlookClients ] + [-HideFromAddressLists ] [-HideFromOutlookClients ] [-SensitivityLabels ] [] ``` @@ -65,6 +65,13 @@ Set-PnPMicrosoft365Group -Identity $group -Owners demo@contoso.com Sets demo@contoso.com as owner of the group +### EXAMPLE 6 +```powershell +Set-PnPMicrosoft365Group -Identity $group -SensitivityLabels "bc98af29-59eb-4869-baaa-9a8dff631aa4" +``` + +Sets the sensitivity label of the group + ## PARAMETERS ### -CreateTeam @@ -207,6 +214,19 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -SensitivityLabels +The Sensitivity label to be set to the Microsoft 365 Group. To retrieve the sensitivity label you need to use the Graph API mentioned [here](https://docs.microsoft.com/en-us/graph/api/informationprotectionlabel-get?view=graph-rest-beta&tabs=http). + +```yaml +Type: GUID[] +Parameter Sets: (All) +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) diff --git a/src/Commands/Microsoft365Groups/SetMicrosoft365Group.cs b/src/Commands/Microsoft365Groups/SetMicrosoft365Group.cs index 8df7c645d..0881c03eb 100644 --- a/src/Commands/Microsoft365Groups/SetMicrosoft365Group.cs +++ b/src/Commands/Microsoft365Groups/SetMicrosoft365Group.cs @@ -1,9 +1,12 @@ -using PnP.Framework.Graph; +using Microsoft.SharePoint.Client; +using PnP.Framework.Graph; using PnP.PowerShell.Commands.Attributes; using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Model; using PnP.PowerShell.Commands.Utilities; using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Management.Automation; @@ -46,6 +49,9 @@ public class SetMicrosoft365Group : PnPGraphCmdlet [Parameter(Mandatory = false)] public bool? HideFromOutlookClients; + [Parameter(Mandatory = false)] + public Guid[] SensitivityLabels; + protected override void ExecuteCmdlet() { var group = Identity.GetGroup(HttpClient, AccessToken, false, false); @@ -110,6 +116,30 @@ protected override void ExecuteCmdlet() // For this scenario a separate call needs to be made Microsoft365GroupsUtility.SetVisibilityAsync(HttpClient, AccessToken, group.Id.Value, HideFromAddressLists, HideFromOutlookClients).GetAwaiter().GetResult(); } + + var assignedLabels = new List(); + if (SensitivityLabels != null && SensitivityLabels.Length > 0) + { + var contextSettings = PnPConnection.Current.Context.GetContextSettings(); + if (contextSettings.Type != Framework.Utilities.Context.ClientContextType.AzureADCertificate) + { + foreach (var label in SensitivityLabels) + { + if (!Guid.Empty.Equals(label)) + { + assignedLabels.Add(new AssignedLabels + { + labelId = label.ToString() + }); + } + } + Microsoft365GroupsUtility.SetSensitivityLabelsAsync(HttpClient, AccessToken, group.Id.Value, assignedLabels).GetAwaiter().GetResult(); + } + else + { + WriteWarning("Adding sensitivity labels in App-only context is not supported by Graph API, so it will be skipped in Group creation"); + } + } } } } diff --git a/src/Commands/Utilities/Microsoft365GroupsUtility.cs b/src/Commands/Utilities/Microsoft365GroupsUtility.cs index 562b59609..87c25e01e 100644 --- a/src/Commands/Utilities/Microsoft365GroupsUtility.cs +++ b/src/Commands/Utilities/Microsoft365GroupsUtility.cs @@ -389,7 +389,7 @@ internal static async Task CreateAsync(HttpClient httpClient, { labelId = label }); - } + } } group.AssignedLabels = assignedLabels; @@ -606,5 +606,35 @@ internal static async Task GetGroupTemplateSet var result = await GraphHelper.GetAsync(httpClient, $"v1.0/groupSettingTemplates/{id}", accessToken, propertyNameCaseInsensitive: true); return result; } + + internal static async Task SetSensitivityLabelsAsync(HttpClient httpClient, string accessToken, Guid groupId, List assignedLabels) + { + var patchData = new + { + assignedLabels, + }; + + var retry = true; + var iteration = 0; + while (retry) + { + try + { + await GraphHelper.PatchAsync(httpClient, accessToken, $"v1.0/groups/{groupId}", patchData); + retry = false; + } + + catch (Exception) + { + await Task.Delay(5000); + iteration++; + } + + if (iteration > 10) // don't try more than 10 times + { + retry = false; + } + } + } } } \ No newline at end of file From f4c32f9f4eefe4ee9c56117f60bd457dde4e1520 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Fri, 22 Apr 2022 17:14:53 +0300 Subject: [PATCH 140/458] Feature #444 - added Get-PnPTeamsChannelFilesFolder --- CHANGELOG.md | 1 + .../Get-PnPTeamsChannelFilesFolder.md | 78 +++++++++++++++++++ .../Model/Teams/TeamsChannelFilesFolder.cs | 37 +++++++++ .../Teams/GetTeamsChannelFilesFolder.cs | 41 ++++++++++ src/Commands/Utilities/TeamsUtility.cs | 6 ++ 5 files changed, 163 insertions(+) create mode 100644 documentation/Get-PnPTeamsChannelFilesFolder.md create mode 100644 src/Commands/Model/Teams/TeamsChannelFilesFolder.cs create mode 100644 src/Commands/Teams/GetTeamsChannelFilesFolder.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 1becc58c7..5239cd01d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Add-PnPTeamsChannelUser` which allows members and owners to be added to private channels in Teams [#1735](https://github.com/pnp/powershell/pull/1735) - Added `ExcludeVisualPromotedResults` parameter to `Get-PnPSearchConfiguration` which excludes promoted results [#1750](https://github.com/pnp/powershell/pull/1750) - Added `MediaTranscription` parameter to `Set-PnPTenantSite` and `Set-PnPSite` cmdlets which when enabled allows videos to have transcripts generated on demand or generated automatically in certain scenarios +- Added `Get-PnPTeamsChannelFilesFolder` cmdlet to retrieve metadata for the location where files of a Teams channel are stored. ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) diff --git a/documentation/Get-PnPTeamsChannelFilesFolder.md b/documentation/Get-PnPTeamsChannelFilesFolder.md new file mode 100644 index 000000000..1a71d9344 --- /dev/null +++ b/documentation/Get-PnPTeamsChannelFilesFolder.md @@ -0,0 +1,78 @@ +--- +Module Name: PnP.PowerShell +title: Get-PnPTeamsChannelFilesFolder +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Get-PnPTeamsChannelFilesFolder.html +--- + +# Get-PnPTeamsChannelFilesFolder + +## SYNOPSIS + +**Required Permissions** + + * Microsoft Graph API : One of Group.Read.All + +Gets the metadata for the location where the files of a channel are stored. + +## SYNTAX + +```powershell +Get-PnPTeamsChannel [-Team ] [-Channel ] + [] +``` + +## DESCRIPTION + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Get-PnPTeamsChannelFilesFolder -Team a6c1e0d7-f579-4993-81ab-4b666f8edea8 -Identity "Test Channel" +``` + +Retrieves the folder metadata for the channel called 'Test Channel' + +### EXAMPLE 2 +```powershell +Get-PnPTeamsChannelFilesFolder -Team a6c1e0d7-f579-4993-81ab-4b666f8edea8 -Identity "19:796d063b63e34497aeaf092c8fb9b44e@thread.skype" +``` + +Retrieves the folder metadata for the channel specified by its channel id + +## PARAMETERS + +### -Channel +The id or name of the channel to retrieve. + +```yaml +Type: TeamsChannelPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Team +Specify the group id, mailNickname or display name of the team to use. + +```yaml +Type: TeamsTeamPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) + diff --git a/src/Commands/Model/Teams/TeamsChannelFilesFolder.cs b/src/Commands/Model/Teams/TeamsChannelFilesFolder.cs new file mode 100644 index 000000000..458088762 --- /dev/null +++ b/src/Commands/Model/Teams/TeamsChannelFilesFolder.cs @@ -0,0 +1,37 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace PnP.PowerShell.Commands.Model.Teams +{ + public partial class TeamsChannelFilesFolder + { + public string id { get; set; } + public DateTime createdDateTime { get; set; } + public DateTime lastModifiedDateTime { get; set; } + public string name { get; set; } + public string webUrl { get; set; } + public int size { get; set; } + public TeamChannelParentReference parentReference { get; set; } + public TeamChannelFileSystemInfo fileSystemInfo { get; set; } + public TeamChannelFolder folder { get; set; } + } + + public partial class TeamChannelParentReference + { + public string driveId { get; set; } + public string driveType { get; set; } + } + + public partial class TeamChannelFileSystemInfo + { + public DateTime createdDateTime { get; set; } + public DateTime lastModifiedDateTime { get; set; } + } + + public partial class TeamChannelFolder + { + public int childCount { get; set; } + } +} diff --git a/src/Commands/Teams/GetTeamsChannelFilesFolder.cs b/src/Commands/Teams/GetTeamsChannelFilesFolder.cs new file mode 100644 index 000000000..f18a94b55 --- /dev/null +++ b/src/Commands/Teams/GetTeamsChannelFilesFolder.cs @@ -0,0 +1,41 @@ +using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Base.PipeBinds; +using System; +using System.Collections.Generic; +using System.Management.Automation; + +namespace PnP.PowerShell.Commands.Teams +{ + [Cmdlet(VerbsCommon.Get, "PnPTeamsChannelFilesFolder")] + [RequiredMinimalApiPermissions("Group.Read.All")] + public class GetTeamsChannelFilesFolder : PnPGraphCmdlet + { + [Parameter(Mandatory = true, ValueFromPipeline = true)] + public TeamsTeamPipeBind Team; + + [Parameter(Mandatory = true)] + public TeamsChannelPipeBind Channel; + + protected override void ExecuteCmdlet() + { + var groupId = Team.GetGroupId(HttpClient, AccessToken); + if (groupId != null) + { + + var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + if (channelId == null) + { + throw new PSArgumentException("Channel not found"); + } + + WriteObject(Utilities.TeamsUtility.GetChannelsFilesFolderAsync(HttpClient, AccessToken, groupId, channelId).GetAwaiter().GetResult()); + + } + else + { + throw new PSArgumentException("Team not found", nameof(Team)); + } + } + } +} diff --git a/src/Commands/Utilities/TeamsUtility.cs b/src/Commands/Utilities/TeamsUtility.cs index 8eb3ef7a1..dd172fca2 100644 --- a/src/Commands/Utilities/TeamsUtility.cs +++ b/src/Commands/Utilities/TeamsUtility.cs @@ -649,6 +649,12 @@ public static async Task UpdateChannelAsync(HttpClient httpClient, { return await GraphHelper.PatchAsync(httpClient, accessToken, $"beta/teams/{groupId}/channels/{channelId}", channel); } + + public static async Task GetChannelsFilesFolderAsync(HttpClient httpClient, string accessToken, string groupId, string channelId) + { + var collection = await GraphHelper.GetAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/filesFolder", accessToken); + return collection; + } #endregion #region Tabs From 12c34e83851424d223e348e26702288ded581e32 Mon Sep 17 00:00:00 2001 From: James May Date: Wed, 27 Apr 2022 03:56:47 +0000 Subject: [PATCH 141/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 3320931c9..9c925045e 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -da9764150c02ef17e01e26f45956fd2022d3fab5 \ No newline at end of file +d038ad31e1bba2b60dae869f38ad700ebe36efbe \ No newline at end of file diff --git a/version.txt b/version.txt index 67a15389c..5c6f2a5b9 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.21 \ No newline at end of file +1.10.22 \ No newline at end of file From 9e956b9b499d22ad69cde382fff21549b2cc06ff Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Wed, 27 Apr 2022 22:03:07 +0300 Subject: [PATCH 142/458] Update CHANGELOG.md Removed duplicate entry from changelog --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1becc58c7..bc7856450 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Added - Added `-Wait` and `-Verbose` optional paramarers to `New-PnPUPABulkImportJob` [#1752](https://github.com/pnp/powershell/pull/1752) -- Added `Add-PnpTeamsChannelUser` which allows members and owners to be added to private channels in Teams [#1735](https://github.com/pnp/powershell/pull/1735) - Added `ResourceBehaviorOptions` option in `New-PnPMicrosoft365Group` cmdlet to set `ResourceBehaviorOptions` while provisioning a Microsoft 365 Group. - Added `Add-PnPTeamsChannelUser` which allows members and owners to be added to private channels in Teams [#1735](https://github.com/pnp/powershell/pull/1735) - Added `ExcludeVisualPromotedResults` parameter to `Get-PnPSearchConfiguration` which excludes promoted results [#1750](https://github.com/pnp/powershell/pull/1750) From fb2f57dfe91efe9c1d02b751c3375093745c9094 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Fri, 22 Apr 2022 15:00:28 +0300 Subject: [PATCH 143/458] #1710 - issue with Add-PnPDocumentSet --- CHANGELOG.md | 1 + src/Commands/DocumentSets/AddDocumentSet.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc7856450..43b9bbf52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Fixed - Fixed `Get-PnPTenantSite` cmdlet so that it will return data even if the template name is specified in a different case. +- Fixed `Add-PnPDocumentSet` cmdlet so that it will support Document Set Content Type Id specified at the web level. ### Removed diff --git a/src/Commands/DocumentSets/AddDocumentSet.cs b/src/Commands/DocumentSets/AddDocumentSet.cs index 5ec6bf79d..ed173a2cc 100644 --- a/src/Commands/DocumentSets/AddDocumentSet.cs +++ b/src/Commands/DocumentSets/AddDocumentSet.cs @@ -29,7 +29,7 @@ protected override void ExecuteCmdlet() var listContentType = ContentType.GetContentType(list); - if (listContentType is null) + if (listContentType.ServerObjectIsNull == null || listContentType.ServerObjectIsNull == true) { var siteContentType = ContentType.GetContentTypeOrThrow(nameof(ContentType), CurrentWeb); listContentType = new ContentTypePipeBind(siteContentType.Name).GetContentTypeOrThrow(nameof(ContentType), list); From 0a9d9809726839f50d1efa74d73658e4e6e8aed3 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Fri, 22 Apr 2022 13:18:54 +0300 Subject: [PATCH 144/458] #1723 - added Accept header as parameter --- CHANGELOG.md | 1 + documentation/Invoke-PnPSPRestMethod.md | 12 ++++++++++++ src/Commands/Base/InvokeSPRestMethod.cs | 10 +++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43b9bbf52..d6fbf146a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Add-PnPTeamsChannelUser` which allows members and owners to be added to private channels in Teams [#1735](https://github.com/pnp/powershell/pull/1735) - Added `ExcludeVisualPromotedResults` parameter to `Get-PnPSearchConfiguration` which excludes promoted results [#1750](https://github.com/pnp/powershell/pull/1750) - Added `MediaTranscription` parameter to `Set-PnPTenantSite` and `Set-PnPSite` cmdlets which when enabled allows videos to have transcripts generated on demand or generated automatically in certain scenarios +- Added `Accept` parameter to `Invoke-PnPSPRestMethod` cmdlet which if specified will pass the Accept HTTP request header. ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) diff --git a/documentation/Invoke-PnPSPRestMethod.md b/documentation/Invoke-PnPSPRestMethod.md index 593029f67..177437ecd 100644 --- a/documentation/Invoke-PnPSPRestMethod.md +++ b/documentation/Invoke-PnPSPRestMethod.md @@ -128,6 +128,18 @@ Position: 0 Accept pipeline input: False ``` +### -Accept +The Accept HTTP request header. Defaults to 'application/json;odata=nometadata'. + +```yaml +Type: String +Parameter Sets: (All) + +Required: False +Position: Named +Accept pipeline input: False +``` + ### -Connection Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. diff --git a/src/Commands/Base/InvokeSPRestMethod.cs b/src/Commands/Base/InvokeSPRestMethod.cs index 87a3f7917..85885ad09 100644 --- a/src/Commands/Base/InvokeSPRestMethod.cs +++ b/src/Commands/Base/InvokeSPRestMethod.cs @@ -33,6 +33,9 @@ public class InvokeSPRestMethod : PnPSharePointCmdlet [Parameter(Mandatory = false)] public string ContentType = "application/json"; + [Parameter(Mandatory = false)] + public string Accept = "application/json;odata=nometadata"; + [Parameter(Mandatory = false)] public SwitchParameter Raw; @@ -52,7 +55,12 @@ protected override void ExecuteCmdlet() using (HttpRequestMessage request = new HttpRequestMessage(method, requestUrl)) { - request.Headers.Add("accept", "application/json;odata=nometadata"); + if(string.IsNullOrEmpty(Accept)) + { + Accept = "application/json;odata=nometadata"; + } + + request.Headers.Add("accept", Accept); if (Method == HttpRequestMethod.Merge) { From 1527d495c81cc83e284198b495c963138ae5ee56 Mon Sep 17 00:00:00 2001 From: Martin Lingstuyl Date: Tue, 26 Apr 2022 22:31:46 +0200 Subject: [PATCH 145/458] Fix case sensitivity issue in Get-PnPGroup for consisitency and add consistent null checks with exceptions. --- documentation/Get-PnPGroup.md | 4 ++-- src/Commands/Principals/GetGroup.cs | 21 +++++++++++++++++-- .../Principals/GetGroupPermissions.cs | 12 +++++++---- .../Principals/SetGroupPermissions.cs | 4 ++++ 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/documentation/Get-PnPGroup.md b/documentation/Get-PnPGroup.md index 5df8fce53..7045613eb 100644 --- a/documentation/Get-PnPGroup.md +++ b/documentation/Get-PnPGroup.md @@ -59,7 +59,7 @@ Returns all SharePoint groups in the current site Get-PnPGroup -Identity 'My Site Users' ``` -This will return the group called 'My Site Users' if available in the current site +This will return the group called 'My Site Users' if available in the current site. The name is case sensitive, so a group called 'My site users' would not be found. ### EXAMPLE 3 ```powershell @@ -127,7 +127,7 @@ Accept wildcard characters: False ``` ### -Identity -Get a specific group by its name or id +Get a specific group by its name or id. The name case sensitive. ```yaml Type: GroupPipeBind diff --git a/src/Commands/Principals/GetGroup.cs b/src/Commands/Principals/GetGroup.cs index 3c2c8a1df..be57412bf 100644 --- a/src/Commands/Principals/GetGroup.cs +++ b/src/Commands/Principals/GetGroup.cs @@ -26,8 +26,25 @@ protected override void ExecuteCmdlet() { if (ParameterSetName == "ByName") { - Group group = Identity.GetGroup(CurrentWeb); - WriteObject(group); + // Get group by name using Core SDK because of + // case sensitivity difference between Core SDK and CSOM + // Loads group using CSOM to bypass a breaking change + var pnpGroup = Identity.GetGroup(PnPContext); + + if (pnpGroup != null) + { + var csomGroup = CurrentWeb.SiteGroups.GetById(pnpGroup.Id); + ClientContext.Load(csomGroup); + ClientContext.Load(csomGroup.Users); + ClientContext.ExecuteQueryRetry(); + + WriteObject(csomGroup); + } + else + { + throw new PSArgumentException("Site group not found", nameof(Identity)); + } + } else if (ParameterSetName == "Members") { diff --git a/src/Commands/Principals/GetGroupPermissions.cs b/src/Commands/Principals/GetGroupPermissions.cs index b52c18d8f..daac12337 100644 --- a/src/Commands/Principals/GetGroupPermissions.cs +++ b/src/Commands/Principals/GetGroupPermissions.cs @@ -15,11 +15,15 @@ public class GetGroupPermissions : PnPWebCmdlet protected override void ExecuteCmdlet() { - var g = Identity.GetGroup(PnPContext); - var r = g.GetRoleDefinitions(); - if (r != null) + var group = Identity.GetGroup(PnPContext); + + if (group == null) + throw new PSArgumentException("Site group not found", nameof(Identity)); + + var roleDefinitions = group.GetRoleDefinitions(); + if (roleDefinitions != null) { - WriteObject(r.RequestedItems, true); + WriteObject(roleDefinitions.RequestedItems, true); } } } diff --git a/src/Commands/Principals/SetGroupPermissions.cs b/src/Commands/Principals/SetGroupPermissions.cs index c532b9668..02fdd45e8 100644 --- a/src/Commands/Principals/SetGroupPermissions.cs +++ b/src/Commands/Principals/SetGroupPermissions.cs @@ -24,6 +24,10 @@ public class SetGroupPermissions : PnPWebCmdlet protected override void ExecuteCmdlet() { var group = Identity.GetGroup(PnPContext); + + if (group == null) + throw new PSArgumentException("Site group not found", nameof(Identity)); + PnP.Core.Model.SharePoint.IList list = null; if (ParameterSpecified(nameof(List))) { From aafe4a523cfb7da002b5d12894618dcf19e8744f Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 28 Apr 2022 17:39:38 +0300 Subject: [PATCH 146/458] Update CHANGELOG.md Updated changelog --- CHANGELOG.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6fbf146a..160594d38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,23 +9,25 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Added - Added `-Wait` and `-Verbose` optional paramarers to `New-PnPUPABulkImportJob` [#1752](https://github.com/pnp/powershell/pull/1752) -- Added `ResourceBehaviorOptions` option in `New-PnPMicrosoft365Group` cmdlet to set `ResourceBehaviorOptions` while provisioning a Microsoft 365 Group. +- Added `ResourceBehaviorOptions` option in `New-PnPMicrosoft365Group` cmdlet to set `ResourceBehaviorOptions` while provisioning a Microsoft 365 Group. [#1774](https://github.com/pnp/powershell/pull/1774) - Added `Add-PnPTeamsChannelUser` which allows members and owners to be added to private channels in Teams [#1735](https://github.com/pnp/powershell/pull/1735) - Added `ExcludeVisualPromotedResults` parameter to `Get-PnPSearchConfiguration` which excludes promoted results [#1750](https://github.com/pnp/powershell/pull/1750) -- Added `MediaTranscription` parameter to `Set-PnPTenantSite` and `Set-PnPSite` cmdlets which when enabled allows videos to have transcripts generated on demand or generated automatically in certain scenarios -- Added `Accept` parameter to `Invoke-PnPSPRestMethod` cmdlet which if specified will pass the Accept HTTP request header. +- Added `MediaTranscription` parameter to `Set-PnPTenantSite` and `Set-PnPSite` cmdlets which when enabled allows videos to have transcripts generated on demand or generated automatically in certain scenarios. [#1771](https://github.com/pnp/powershell/pull/1771) +- Added `Accept` parameter to `Invoke-PnPSPRestMethod` cmdlet which if specified will pass the Accept HTTP request header. [#1795](https://github.com/pnp/powershell/pull/1795) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) ### Fixed -- Fixed `Get-PnPTenantSite` cmdlet so that it will return data even if the template name is specified in a different case. -- Fixed `Add-PnPDocumentSet` cmdlet so that it will support Document Set Content Type Id specified at the web level. +- Fixed `Get-PnPTenantSite` cmdlet so that it will return data even if the template name is specified in a different case. [#1773](https://github.com/pnp/powershell/pull/1773) +- Fixed `Add-PnPDocumentSet` cmdlet so that it will support Document Set Content Type Id specified at the web level. [#1796](https://github.com/pnp/powershell/pull/1796) +- Fixed `Get-PnPGroup` , `Get-PnPGroupPermissions` and `Set-PnPGroupPermissions ` cmdlets by making them more consistent. They will also throw error if a group is not found. [#1808](https://github.com/pnp/powershell/pull/1808) ### Removed ### Contributors +- Martin Lingstuyl [martinlingstuyl] - James May [fowl2] - Milan Holemans [milanholemans] - Arleta Wanat [PowershellScripts] From f1111df4002c7c7719801f0a83d46bf954044006 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 28 Apr 2022 18:17:33 +0300 Subject: [PATCH 147/458] Fix #1809 - issue with usage of Site parameter --- CHANGELOG.md | 1 + .../ContentTypes/AddContentTypesFromContentTypeHub.cs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 160594d38..49da75c40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Get-PnPTenantSite` cmdlet so that it will return data even if the template name is specified in a different case. [#1773](https://github.com/pnp/powershell/pull/1773) - Fixed `Add-PnPDocumentSet` cmdlet so that it will support Document Set Content Type Id specified at the web level. [#1796](https://github.com/pnp/powershell/pull/1796) - Fixed `Get-PnPGroup` , `Get-PnPGroupPermissions` and `Set-PnPGroupPermissions ` cmdlets by making them more consistent. They will also throw error if a group is not found. [#1808](https://github.com/pnp/powershell/pull/1808) +- Fixed `Add-PnPContentTypesFromContentTypeHub`, if `Site` parameter is specified, it will be used now to sync content types from content type hub site. ### Removed diff --git a/src/Commands/ContentTypes/AddContentTypesFromContentTypeHub.cs b/src/Commands/ContentTypes/AddContentTypesFromContentTypeHub.cs index ec328e346..c15833796 100644 --- a/src/Commands/ContentTypes/AddContentTypesFromContentTypeHub.cs +++ b/src/Commands/ContentTypes/AddContentTypesFromContentTypeHub.cs @@ -23,10 +23,10 @@ protected override void ExecuteCmdlet() ClientContext.Load(sub); ClientContext.ExecuteQueryRetry(); - var res = sub.SyncContentTypesFromHubSite2(ClientContext.Url, ContentTypes); + var res = sub.SyncContentTypesFromHubSite2(site, ContentTypes); ClientContext.ExecuteQueryRetry(); - var result = new PnP.PowerShell.Commands.Model.SharePoint.AddContentTypesFromContentTypeHubResponse + var result = new Model.SharePoint.AddContentTypesFromContentTypeHubResponse { FailedContentTypeErrors = res.Value.FailedContentTypeErrors, FailedReason = res.Value.FailedReason, From d9cac05389be4877eeaf0e7a7a9c3b436ab4c4a1 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 28 Apr 2022 21:58:03 +0300 Subject: [PATCH 148/458] Fix #1618 - issue with Get-PnPFile in PS 5 hanging (#1816) * Fix #1618 - issue with Get-PnPFile in PS 5 hanging * Added using block to stream --- CHANGELOG.md | 1 + src/Commands/Files/GetFile.cs | 52 +++++++++++++++++------------------ 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49da75c40..c78332667 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Get-PnPTenantSite` cmdlet so that it will return data even if the template name is specified in a different case. [#1773](https://github.com/pnp/powershell/pull/1773) - Fixed `Add-PnPDocumentSet` cmdlet so that it will support Document Set Content Type Id specified at the web level. [#1796](https://github.com/pnp/powershell/pull/1796) - Fixed `Get-PnPGroup` , `Get-PnPGroupPermissions` and `Set-PnPGroupPermissions ` cmdlets by making them more consistent. They will also throw error if a group is not found. [#1808](https://github.com/pnp/powershell/pull/1808) +- Fixed `Get-PnPFile` issue with every 3rd file download in PS 5. - Fixed `Add-PnPContentTypesFromContentTypeHub`, if `Site` parameter is specified, it will be used now to sync content types from content type hub site. ### Removed diff --git a/src/Commands/Files/GetFile.cs b/src/Commands/Files/GetFile.cs index 99bf3edb2..cce1d642b 100644 --- a/src/Commands/Files/GetFile.cs +++ b/src/Commands/Files/GetFile.cs @@ -51,7 +51,7 @@ public class GetFile : PnPWebCmdlet public SwitchParameter AsFileObject; [Parameter(Mandatory = false, ParameterSetName = URLASMEMORYSTREAM)] - public SwitchParameter AsMemoryStream; + public SwitchParameter AsMemoryStream; protected override void ExecuteCmdlet() { @@ -86,14 +86,20 @@ protected override void ExecuteCmdlet() { case URLTOPATH: - SaveFileToLocal(CurrentWeb, serverRelativeUrl, Path, Filename, (fileToSave) => + // Get a reference to the file to download + IFile fileToDownload = PnPContext.Web.GetFileByServerRelativeUrlAsync(serverRelativeUrl).GetAwaiter().GetResult(); + string fileToDownloadName = !string.IsNullOrEmpty(Filename) ? Filename : fileToDownload.Name; + string fileOut = System.IO.Path.Combine(Path, fileToDownloadName); + + if (System.IO.File.Exists(fileOut) && !Force) { - if (!Force) - { - WriteWarning($"File '{fileToSave}' exists already. use the -Force parameter to overwrite the file."); - } - return Force; - }).Wait(); + WriteWarning($"File '{fileToDownloadName}' exists already. Use the -Force parameter to overwrite the file."); + } + else + { + SaveFileToLocal(fileToDownload, fileOut).GetAwaiter().GetResult(); + } + break; case URLASFILEOBJECT: var fileObject = CurrentWeb.GetFileByServerRelativePath(ResourcePath.FromDecodedUrl(serverRelativeUrl)); @@ -145,40 +151,32 @@ protected override void ExecuteCmdlet() // Fallback in case the creator or person having last modified the file no longer exists in the environment such that the file can still be downloaded fileMemoryStream = PnPContext.Web.GetFileByServerRelativeUrl(ResourcePath.FromDecodedUrl(serverRelativeUrl).DecodedUrl, f => f.Length, f => f.Name, f => f.TimeCreated, f => f.TimeLastModified, f => f.Title); } - - var stream = new System.IO.MemoryStream(fileMemoryStream.GetContentBytes()); + + var stream = new System.IO.MemoryStream(fileMemoryStream.GetContentBytes()); WriteObject(stream); break; } } - private async Task SaveFileToLocal(Web web, string serverRelativeUrl, string localPath, string localFileName = null, Func fileExistsCallBack = null) + private static async Task SaveFileToLocal(IFile fileToDownload, string filePath) { - // Get a reference to the file to download - IFile fileToDownload = await PnPContext.Web.GetFileByServerRelativeUrlAsync(serverRelativeUrl); - // Start the download - Stream downloadedContentStream = await fileToDownload.GetContentAsync(true); - - // Download the file bytes in 2MB chunks and immediately write them to a file on disk - // This approach avoids the file being fully loaded in the process memory - var bufferSize = 2 * 1024 * 1024; // 2 MB buffer - - var fileOut = System.IO.Path.Combine(localPath, !string.IsNullOrEmpty(localFileName) ? localFileName : fileToDownload.Name); - - if (!System.IO.File.Exists(fileOut) || (fileExistsCallBack != null && fileExistsCallBack(fileOut))) + using (Stream downloadedContentStream = await fileToDownload.GetContentAsync(true)) { - using (var content = System.IO.File.Create(fileOut)) + // Download the file bytes in 2MB chunks and immediately write them to a file on disk + // This approach avoids the file being fully loaded in the process memory + var bufferSize = 2 * 1024 * 1024; // 2 MB buffer + + using (FileStream content = System.IO.File.Create(filePath)) { - var buffer = new byte[bufferSize]; + byte[] buffer = new byte[bufferSize]; int read; while ((read = await downloadedContentStream.ReadAsync(buffer, 0, buffer.Length)) != 0) { content.Write(buffer, 0, read); } } - } - + } } } } From aed3c9128b82a29ad6c2e1806a8603eabdcf2398 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Fri, 29 Apr 2022 03:43:39 +0000 Subject: [PATCH 149/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index e036eaafd..938290dc3 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -4d4afd92ae134bec2996b6f998d961f9003f2baf \ No newline at end of file +97c6fb60eec4a61bef4d243e88de6d68f7275784 \ No newline at end of file diff --git a/version.txt b/version.txt index 5c6f2a5b9..0ee4c08bf 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.22 \ No newline at end of file +1.10.23 \ No newline at end of file From b0bcff86a7686ff81e7750d1a425ae7883d9fb17 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Fri, 29 Apr 2022 10:20:10 +0300 Subject: [PATCH 150/458] #1770 - update docs #1770 - update docs --- documentation/Add-PnPContentTypesFromContentTypeHub.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/documentation/Add-PnPContentTypesFromContentTypeHub.md b/documentation/Add-PnPContentTypesFromContentTypeHub.md index 3201fc700..96e10b5b3 100644 --- a/documentation/Add-PnPContentTypesFromContentTypeHub.md +++ b/documentation/Add-PnPContentTypesFromContentTypeHub.md @@ -34,6 +34,9 @@ Add-PnPContentTypesFromContentTypeHub -ContentTypes List [-Site Date: Tue, 3 May 2022 08:40:43 +0200 Subject: [PATCH 151/458] [Enhancement] Extra properties when retrieving team (#1825) * Added extra team properties * Revert change --- src/Commands/Model/Teams/Team.cs | 22 ++++++++++++++++++--- src/Commands/Model/Teams/TeamFunSettings.cs | 7 +------ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/Commands/Model/Teams/Team.cs b/src/Commands/Model/Teams/Team.cs index fdd44c616..22808a23c 100644 --- a/src/Commands/Model/Teams/Team.cs +++ b/src/Commands/Model/Teams/Team.cs @@ -1,6 +1,8 @@ -using System.Collections.Generic; +using PnP.PowerShell.Commands.Model.Graph; +using PnP.PowerShell.Commands.Utilities.JSON; +using System; +using System.Collections.Generic; using System.Text.Json.Serialization; -using PnP.PowerShell.Commands.Model.Graph; namespace PnP.PowerShell.Commands.Model.Teams { @@ -84,8 +86,22 @@ public class Team [JsonConverter(typeof(JsonStringEnumConverter))] public GroupVisibility Visibility { get; set; } - #endregion + /// + /// Web URL of the team + /// + public string WebUrl { get; set; } + + /// + /// Internal unique ID of the team + /// + public string InternalId { get; set; } + /// + /// Date when the team was created + /// + public DateTimeOffset? CreatedDateTime { get; set; } + + #endregion } /// diff --git a/src/Commands/Model/Teams/TeamFunSettings.cs b/src/Commands/Model/Teams/TeamFunSettings.cs index 434ee22b2..d2a9f3f37 100644 --- a/src/Commands/Model/Teams/TeamFunSettings.cs +++ b/src/Commands/Model/Teams/TeamFunSettings.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Json.Serialization; -using System.Threading.Tasks; +using System.Text.Json.Serialization; namespace PnP.PowerShell.Commands.Model.Teams { From 07b9acbde0118e5c07fbf2a5a56672c8d5dff12c Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Tue, 3 May 2022 08:45:17 +0200 Subject: [PATCH 152/458] Implemented Get-PnpFlowRun cmdlet (#1819) --- documentation/Get-PnPFlow.md | 4 +- documentation/Get-PnPFlowRun.md | 109 ++++++++++++++++++ .../PipeBinds/PowerAutomateFlowRunPipeBind.cs | 24 ++++ .../PowerPlatform/PowerAutomate/FlowRun.cs | 28 +++++ .../PowerAutomate/FlowRunProperties.cs | 32 +++++ .../PowerAutomate/FlowRunTrigger.cs | 21 ++++ .../PowerPlatform/PowerAutomate/GetFlowRun.cs | 50 ++++++++ 7 files changed, 266 insertions(+), 2 deletions(-) create mode 100644 documentation/Get-PnPFlowRun.md create mode 100644 src/Commands/Base/PipeBinds/PowerAutomateFlowRunPipeBind.cs create mode 100644 src/Commands/Model/PowerPlatform/PowerAutomate/FlowRun.cs create mode 100644 src/Commands/Model/PowerPlatform/PowerAutomate/FlowRunProperties.cs create mode 100644 src/Commands/Model/PowerPlatform/PowerAutomate/FlowRunTrigger.cs create mode 100644 src/Commands/PowerPlatform/PowerAutomate/GetFlowRun.cs diff --git a/documentation/Get-PnPFlow.md b/documentation/Get-PnPFlow.md index 6d84ba2ce..2143e9bd5 100644 --- a/documentation/Get-PnPFlow.md +++ b/documentation/Get-PnPFlow.md @@ -23,7 +23,7 @@ Get-PnPFlow -Environment [-AsAdmin] [-Identit ``` ## DESCRIPTION -This cmdlets returns the flows for a given enviroment. +This cmdlet returns the flows for a given enviroment. ## EXAMPLES @@ -39,7 +39,7 @@ This returns all the flows for a given power platform environment $environment = Get-PnPPowerPlatformEnvironment Get-PnPFlow -Environment $environment -Identity fba63225-baf9-4d76-86a1-1b42c917a182 ``` -This returns specific flow +This returns a specific flow ## PARAMETERS diff --git a/documentation/Get-PnPFlowRun.md b/documentation/Get-PnPFlowRun.md new file mode 100644 index 000000000..a1ee67051 --- /dev/null +++ b/documentation/Get-PnPFlowRun.md @@ -0,0 +1,109 @@ +--- +online version: https://pnp.github.io/powershell/cmdlets/Get-PnPFlowRun.html +Module Name: PnP.PowerShell +external help file: PnP.PowerShell.dll-Help.xml +schema: 2.0.0 +--- + +# Get-PnPFlowRun + +## SYNOPSIS + +**Required Permissions** + +* Azure: management.azure.com + +Returns the flows runs for a given flow. + +## SYNTAX + +```powershell +Get-PnPFlowRun -Environment -Flow [-Identity ] +[-Connection ] [] +``` + +## DESCRIPTION +This cmdlet returns the flow runs for a given flow. + +## EXAMPLES + +### Example 1 +```powershell +$environment = Get-PnPPowerPlatformEnvironment +Get-PnPFlowRun -Environment $environment -Flow fba63225-baf9-4d76-86a1-1b42c917a182 +``` +This returns all the flow runs for a given flow + +### Example 2 +```powershell +$environment = Get-PnPPowerPlatformEnvironment +Get-PnPFlowRun -Environment $environment -Flow fba63225-baf9-4d76-86a1-1b42c917a182 -Identity 08585531682024670884771461819CU230 +``` +This returns a specific flow run + +## PARAMETERS + +### -Environment +The name of the Power Platform environment or an Environment object to retrieve the available flows for. + +```yaml +Type: PowerAutomateEnvironmentPipeBind +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Flow +The Name/Id of the flow to retrieve. + +```yaml +Type: PowerAutomateFlowPipeBind +Parameter Sets: (All) +Aliases: + +Required: true +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Identity +The Name/Id of the flow run to retrieve. + +```yaml +Type: PowerAutomateFlowRunPipeBind +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Connection +Optional connection to be used by the cmdlet. +Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) diff --git a/src/Commands/Base/PipeBinds/PowerAutomateFlowRunPipeBind.cs b/src/Commands/Base/PipeBinds/PowerAutomateFlowRunPipeBind.cs new file mode 100644 index 000000000..d0100ec4a --- /dev/null +++ b/src/Commands/Base/PipeBinds/PowerAutomateFlowRunPipeBind.cs @@ -0,0 +1,24 @@ +using PnP.PowerShell.Commands.Model.PowerPlatform.PowerAutomate; + +namespace PnP.PowerShell.Commands.Base.PipeBinds +{ + public sealed class PowerAutomateFlowRunPipeBind + { + private readonly string _name; + private readonly FlowRun _flowRun; + public PowerAutomateFlowRunPipeBind(string input) + { + _name = input; + } + + public PowerAutomateFlowRunPipeBind(FlowRun flowRun) + { + _flowRun = flowRun; + } + + public string GetName() + { + return _flowRun != null ? _flowRun.Name : _name; + } + } +} diff --git a/src/Commands/Model/PowerPlatform/PowerAutomate/FlowRun.cs b/src/Commands/Model/PowerPlatform/PowerAutomate/FlowRun.cs new file mode 100644 index 000000000..7e8941a19 --- /dev/null +++ b/src/Commands/Model/PowerPlatform/PowerAutomate/FlowRun.cs @@ -0,0 +1,28 @@ +namespace PnP.PowerShell.Commands.Model.PowerPlatform.PowerAutomate +{ + /// + /// Contains information of one Microsoft Power Automate Flow run + /// + public class FlowRun + { + /// + /// Name of the Flow as its Flow GUID + /// + public string Name { get; set; } + + /// + /// Unique identifier of this Flow run. + /// + public string Id { get; set; } + + /// + /// Type of object, typically Microsoft.ProcessSimple/environments/flows/runs + /// + public string Type { get; set; } + + /// + /// Additional information of the Flow run. + /// + public FlowRunProperties Properties { get; set; } + } +} diff --git a/src/Commands/Model/PowerPlatform/PowerAutomate/FlowRunProperties.cs b/src/Commands/Model/PowerPlatform/PowerAutomate/FlowRunProperties.cs new file mode 100644 index 000000000..40dfa1514 --- /dev/null +++ b/src/Commands/Model/PowerPlatform/PowerAutomate/FlowRunProperties.cs @@ -0,0 +1,32 @@ +using System; + +namespace PnP.PowerShell.Commands.Model.PowerPlatform.PowerAutomate +{ + /// + /// Contains additional information of one Microsoft Power Automate Flow run. + /// + public class FlowRunProperties + { + /// + /// Start time of the Flow run. + /// + public DateTime StartTime { get; set; } + + /// + /// End time of the Flow run. + /// + public DateTime EndTime { get; set; } + + /// + /// Flow run status. Succeeded, failed, cancelled. + /// + public string Status { get; set; } + + public string Code { get; set; } + + /// + /// Contains additional information about the trigger. + /// + public FlowRunTrigger Trigger { get; set; } + } +} diff --git a/src/Commands/Model/PowerPlatform/PowerAutomate/FlowRunTrigger.cs b/src/Commands/Model/PowerPlatform/PowerAutomate/FlowRunTrigger.cs new file mode 100644 index 000000000..393887c34 --- /dev/null +++ b/src/Commands/Model/PowerPlatform/PowerAutomate/FlowRunTrigger.cs @@ -0,0 +1,21 @@ +using System; + +namespace PnP.PowerShell.Commands.Model.PowerPlatform.PowerAutomate +{ + public class FlowRunTrigger + { + public string Name { get; set; } + + public DateTime StartTime { get; set; } + + public DateTime EndTime { get; set; } + + public DateTime ScheduledTime { get; set; } + + public string OriginHistoryName { get; set; } + + public string Code { get; set; } + + public string Status { get; set; } + } +} diff --git a/src/Commands/PowerPlatform/PowerAutomate/GetFlowRun.cs b/src/Commands/PowerPlatform/PowerAutomate/GetFlowRun.cs new file mode 100644 index 000000000..af8d4a20f --- /dev/null +++ b/src/Commands/PowerPlatform/PowerAutomate/GetFlowRun.cs @@ -0,0 +1,50 @@ +using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Model.PowerPlatform.PowerAutomate; +using PnP.PowerShell.Commands.Utilities.REST; +using System.Management.Automation; + +namespace PnP.PowerShell.Commands.PowerPlatform.PowerAutomate +{ + [Cmdlet(VerbsCommon.Get, "PnPFlowRun")] + [RequiredMinimalApiPermissions("https://management.azure.com/.default")] + public class GetFlowRun : PnPGraphCmdlet + { + [Parameter(Mandatory = true)] + public PowerPlatformEnvironmentPipeBind Environment; + + [Parameter(Mandatory = true)] + public PowerAutomateFlowPipeBind Flow; + + [Parameter(Mandatory = false)] + public PowerAutomateFlowRunPipeBind Identity; + + protected override void ExecuteCmdlet() + { + var environmentName = Environment.GetName(); + if (string.IsNullOrEmpty(environmentName)) + { + throw new PSArgumentException("Environment not found."); + } + + var flowName = Flow.GetName(); + if (string.IsNullOrEmpty(flowName)) + { + throw new PSArgumentException("Flow not found."); + } + + if (ParameterSpecified(nameof(Identity))) + { + var flowRunName = Identity.GetName(); + var flowRun = GraphHelper.GetAsync(HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple/environments/{environmentName}/flows/{flowName}/runs/{flowRunName}?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); + WriteObject(flowRun, false); + } + else + { + var flowRuns = GraphHelper.GetResultCollectionAsync(HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple/environments/{environmentName}/flows/{flowName}/runs?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); + WriteObject(flowRuns, true); + } + } + } +} From 2649ccf0e0217b722443bd58797142029bfc7b03 Mon Sep 17 00:00:00 2001 From: 4ndri Date: Tue, 3 May 2022 09:14:48 +0200 Subject: [PATCH 153/458] Feature/Invoke-PnPGraphMethod (#1820) * support additional headers * JsonToPSObject Converter * #873 Invoke-PnPGraphMethod * use json helper to convert to psobject * Invoke-PnPGraphMethod docs correct format * resolved bug in Url param, support beta and v1.0 --- documentation/Invoke-PnPGraphMethod.md | 206 +++++++++++++++ src/Commands/Base/InvokeSPRestMethod.cs | 70 +---- src/Commands/Graph/InvokeGraphMethod.cs | 282 +++++++++++++++++++++ src/Commands/Utilities/Json/Convert.cs | 96 +++++++ src/Commands/Utilities/REST/GraphHelper.cs | 32 +-- 5 files changed, 604 insertions(+), 82 deletions(-) create mode 100644 documentation/Invoke-PnPGraphMethod.md create mode 100644 src/Commands/Graph/InvokeGraphMethod.cs create mode 100644 src/Commands/Utilities/Json/Convert.cs diff --git a/documentation/Invoke-PnPGraphMethod.md b/documentation/Invoke-PnPGraphMethod.md new file mode 100644 index 000000000..efef12cf4 --- /dev/null +++ b/documentation/Invoke-PnPGraphMethod.md @@ -0,0 +1,206 @@ +--- +Module Name: PnP.PowerShell +title: Invoke-PnPGraphMethod +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Invoke-PnPGraphMethod.html +--- + +# Invoke-PnPGraphMethod + +## SYNOPSIS +Invokes a REST request towards the Microsoft Graph API + +## SYNTAX + +```powershell +Invoke-PnPGraphMethod -Url + [-AdditionalHeaders ] + [[-Method] ] + [-Content ] + [-ContentType ] + [-ConsistencyLevelEventual] + [-Raw] + [-All] + [-Connection ] + [] +``` + +## DESCRIPTION +Invokes a REST request towards the Microsoft Graph API + +## EXAMPLES + +### Example 1 +```powershell +Invoke-PnPGraphMethod -Url "groups?`$filter=startsWith(displayName,'ZZ')&`$select=displayName" +Invoke-PnPGraphMethod -Url 'groups/{id}?`$select=hideFromOutlookClients' +``` + +Execute a GET request to get groups by filter and select. + +### Example 2 +```powershell +Invoke-PnPGraphMethod -Url "groups/{id}" -Method Delete +``` + +delete the group with id. + +### Example 3 +```powershell +Invoke-PnPGraphMethod -Url "groups/{id}" -Method Patch -Content @{ displayName = "NewName" } +``` + +set the displayName of the group with a Patch request. + +### Example 4 +```powershell +Invoke-PnPGraphMethod -Url "v1.0/users?$filter=accountEnabled ne true&$count=true" -Method Get -ConsistencyLevelEventual +``` + +get users with advanced query capabilities. use of -ConsistencyLevelEventual. + +## PARAMETERS + +### -AdditionalHeaders +Additional request headers + +```yaml +Type: System.Collections.Generic.IDictionary`2[System.String,System.String] +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -All +retrieve all pages of results. this will loop through all @odata.nextLink. this flag will only be respected if the request is a GET request. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Connection +Optional connection to be used by the cmdlet. +Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ConsistencyLevelEventual +set the ConsistencyLevel header to eventual for advanced query capabilities on Azure AD directory objects + + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Content +A string or object to send + +```yaml +Type: Object +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ContentType +The content type of the object to send. Defaults to 'application/json'. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Method +The Http method to execute. Defaults to GET. + +```yaml +Type: HttpRequestMethod +Parameter Sets: (All) +Aliases: +Accepted values: Default, Get, Head, Post, Put, Delete, Trace, Options, Merge, Patch + +Required: False +Position: 0 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Raw +If specified the returned data will not be converted to an object but returned as a JSON string. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Url +the graph endpoint to invoke + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/src/Commands/Base/InvokeSPRestMethod.cs b/src/Commands/Base/InvokeSPRestMethod.cs index 85885ad09..ce066cb1a 100644 --- a/src/Commands/Base/InvokeSPRestMethod.cs +++ b/src/Commands/Base/InvokeSPRestMethod.cs @@ -3,6 +3,8 @@ using PnP.Framework.Utilities; using PnP.PowerShell.Commands.Enums; +using PnP.PowerShell.Commands.Utilities.JSON; + using System; using System.Collections; using System.Collections.Generic; @@ -103,7 +105,7 @@ protected override void ExecuteCmdlet() } if (jsonElement.TryGetProperty("value", out JsonElement valueProperty)) { - var formattedObject = ConvertToPSObject(valueProperty, "value"); + var formattedObject = Utilities.JSON.Convert.ConvertToPSObject(valueProperty, "value"); if (!string.IsNullOrEmpty(nextLink)) { formattedObject.Properties.Add(new PSNoteProperty("odata.nextLink", nextLink)); @@ -113,7 +115,7 @@ protected override void ExecuteCmdlet() } else { - WriteObject(ConvertToPSObject(jsonElement, null), true); + WriteObject(Utilities.JSON.Convert.ConvertToPSObject(jsonElement, null), true); } } else @@ -160,70 +162,6 @@ private void SetAuthenticationCookies(HttpClientHandler handler, ClientContext c handler.CookieContainer = authCookiesContainer; //} } - - private PSObject ConvertToPSObject(JsonElement element, string jsonPropertyName) - { - var list = new List(); - var pso = new PSObject(); - - if (element.ValueKind == JsonValueKind.Array) - { - var array = ConvertToPSObjectArray(element); - pso.Properties.Add(new PSNoteProperty(jsonPropertyName, array)); - } - else - { - foreach (var prop in element.EnumerateObject()) - { - object value = null; - switch (prop.Value.ValueKind) - { - - case JsonValueKind.Array: - { - value = ConvertToPSObjectArray(prop.Value); - break; - } - case JsonValueKind.True: - case JsonValueKind.False: - { - value = prop.Value.GetBoolean(); - break; - } - case JsonValueKind.String: - { - value = prop.Value.GetString(); - break; - } - case JsonValueKind.Object: - { - value = ConvertToPSObject(prop.Value, prop.Name); - break; - } - case JsonValueKind.Number: - { - value = prop.Value.GetInt64(); - break; - } - } - pso.Properties.Add(new PSNoteProperty(prop.Name, value)); - } - } - - return pso; - } - - private List ConvertToPSObjectArray(JsonElement element) - { - var list = new List(); - - foreach (var subelement in element.EnumerateArray()) - { - var value = ConvertToPSObject(subelement, null); - list.Add(value); - } - return list; - } } //Taken from "Remote Authentication in SharePoint Online Using the Client Object Model" diff --git a/src/Commands/Graph/InvokeGraphMethod.cs b/src/Commands/Graph/InvokeGraphMethod.cs new file mode 100644 index 000000000..5429a421c --- /dev/null +++ b/src/Commands/Graph/InvokeGraphMethod.cs @@ -0,0 +1,282 @@ + +using Newtonsoft.Json.Linq; +using PnP.Framework.Utilities; +using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Enums; +using PnP.PowerShell.Commands.Utilities; +using PnP.PowerShell.Commands.Utilities.REST; +using PnP.PowerShell.Commands.Utilities.JSON; +using System; +using System.Linq; +using System.Management.Automation; +using System.Text.Json; +using System.Collections; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using PnP.PowerShell.Commands.Model.Graph; + +namespace PnP.PowerShell.Commands.Base +{ + [Cmdlet(VerbsLifecycle.Invoke, "PnPGraphMethod")] + public class InvokeGraphMethod : PnPGraphCmdlet + { + [Parameter(Mandatory = false, Position = 0)] + public HttpRequestMethod Method = HttpRequestMethod.Get; + + private string _url; + + [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true)] + public string Url + { + get { return _url; } + set + { + var url = value; + if (!url.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) + { + if (url.StartsWith("/", StringComparison.OrdinalIgnoreCase)) + { + url = url.Remove(0, 1); + } + if (!url.StartsWith("beta/", StringComparison.OrdinalIgnoreCase) && !url.StartsWith("v1.0/", StringComparison.OrdinalIgnoreCase)) + { + url = UrlUtility.Combine("v1.0", url); + } + } + _url = url; + } + } + + [Parameter(Mandatory = false)] + public object Content; + + [Parameter(Mandatory = false)] + public string ContentType = "application/json"; + + IDictionary additionalHeaders = null; + + [Parameter(Mandatory = false)] + public IDictionary AdditionalHeaders + { + get + { + if (ConsistencyLevelEventual.IsPresent) + { + if (additionalHeaders == null) + { + additionalHeaders = new Dictionary(); + } + additionalHeaders.Remove("ConsistencyLevel"); + additionalHeaders.Add("ConsistencyLevel", "eventual"); + } + return additionalHeaders; + } + set + { + additionalHeaders = value; + } + } + + [Parameter(Mandatory = false)] + public SwitchParameter ConsistencyLevelEventual; + + [Parameter(Mandatory = false)] + public SwitchParameter Raw; + + [Parameter(Mandatory = false)] + public SwitchParameter All; + + protected override void ExecuteCmdlet() + { + try + { + SendRequest(); + } + catch (Exception ex) + { + WriteError(ex, ErrorCategory.WriteError); + } + } + + private HttpContent GetHttpContent() + { + if (Content != null) + { + if (Content is HttpContent) + { + return (HttpContent)Content; + } + if (string.IsNullOrEmpty(ContentType)) + { + ContentType = "application/json"; + } + var contentString = Content is string ? Content.ToString() : + JsonSerializer.Serialize(Content); + + HttpContent httpContent = new StringContent(contentString, System.Text.Encoding.UTF8); + httpContent.Headers.ContentType = MediaTypeHeaderValue.Parse(ContentType); + return httpContent; + } + return null; + } + + private void SendRequest() + { + try + { + switch (Method) + { + case HttpRequestMethod.Get: + GetRequest(); + return; + case HttpRequestMethod.Post: + PostRequest(); + return; + case HttpRequestMethod.Patch: + PatchRequest(); + return; + case HttpRequestMethod.Put: + PutRequest(); + return; + case HttpRequestMethod.Delete: + DeleteRequest(); + return; + } + throw new NotSupportedException($"method [{Method}] not supported"); + } + catch (PnP.PowerShell.Commands.Model.Graph.GraphException gex) + { + if (gex.Error.Code == "Authorization_RequestDenied") + { + if (!string.IsNullOrEmpty(gex.AccessToken)) + { + TokenHandler.ValidateTokenForPermissions(GetType(), gex.AccessToken); + } + } + throw new PSInvalidOperationException(gex.Error.Message); + } + } + + private void ThrowIfNoSuccess(HttpResponseMessage response) + { + if (!response.IsSuccessStatusCode) + { + var errorContent = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + var exception = JsonSerializer.Deserialize(errorContent, new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); + exception.AccessToken = AccessToken; + throw exception; + } + } + + private object Deserialize(string result) + { + var element = JsonSerializer.Deserialize(result); + return Deserialize(element); + } + + private object Deserialize(JsonElement element) + { + var obj = Utilities.JSON.Convert.ConvertToObject(element); + return obj; + } + + private void WriteGraphResult(string result) + { + if (!string.IsNullOrEmpty(result)) + { + if (Raw.IsPresent) + { + WriteObject(result); + } + else + { + WriteObject(Deserialize(result)); + } + } + } + + private void GetRequest() + { + var result = GraphHelper.GetAsync(HttpClient, Url, AccessToken, AdditionalHeaders).GetAwaiter().GetResult(); + if (Raw.IsPresent) + { + WriteObject(result); + } + else + { + var element = JsonSerializer.Deserialize(result); + var rootObj = Deserialize(element); + + dynamic obj = rootObj; + + if (All.IsPresent && obj != null && obj.value != null && (obj.value is List)) + { + List values = obj.value; + while (true) + { + if (element.TryGetProperty("@odata.nextLink", out JsonElement nextLinkProperty)) + { + if (nextLinkProperty.ValueKind != JsonValueKind.String) + { + break; + } + var nextLink = nextLinkProperty.ToString(); + result = GraphHelper.GetAsync(HttpClient, nextLink, AccessToken, AdditionalHeaders).GetAwaiter().GetResult(); + element = JsonSerializer.Deserialize(result); + dynamic nextObj = Deserialize(element); + if (nextObj != null && nextObj.value != null && (nextObj.value is List)) + { + List nextValues = nextObj.value; + values.AddRange(nextValues); + } + else + { + break; + } + } + else + { + break; + } + } + } + WriteObject(rootObj); + } + } + + private void PostRequest() + { + var response = GraphHelper.PostAsync(HttpClient, Url, AccessToken, GetHttpContent(), AdditionalHeaders).GetAwaiter().GetResult(); + ThrowIfNoSuccess(response); + var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + WriteGraphResult(result); + } + + private void PutRequest() + { + var response = GraphHelper.PutAsync(HttpClient, Url, AccessToken, GetHttpContent(), AdditionalHeaders).GetAwaiter().GetResult(); + ThrowIfNoSuccess(response); + var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + WriteGraphResult(result); + } + + private void PatchRequest() + { + var response = GraphHelper.PatchAsync(HttpClient, AccessToken, GetHttpContent(), Url, AdditionalHeaders).GetAwaiter().GetResult(); + ThrowIfNoSuccess(response); + var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + WriteGraphResult(result); + } + + private void DeleteRequest() + { + var response = GraphHelper.DeleteAsync(HttpClient, Url, AccessToken, AdditionalHeaders).GetAwaiter().GetResult(); + ThrowIfNoSuccess(response); + var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + WriteGraphResult(result); + } + } +} \ No newline at end of file diff --git a/src/Commands/Utilities/Json/Convert.cs b/src/Commands/Utilities/Json/Convert.cs new file mode 100644 index 000000000..872534eb1 --- /dev/null +++ b/src/Commands/Utilities/Json/Convert.cs @@ -0,0 +1,96 @@ +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Management.Automation; +using System.Text; +using System.Text.Json; + +namespace PnP.PowerShell.Commands.Utilities.JSON +{ + public static class Convert + { + public static PSObject ConvertToPSObject(string jsonString) + { + var jsonElement = JsonSerializer.Deserialize(jsonString); + return ConvertToPSObject(jsonElement); + } + + public static PSObject ConvertToPSObject(JsonElement element) => ConvertToPSObject(element, null); + + public static PSObject ConvertToPSObject(JsonElement element, string jsonPropertyName) + { + var value = ConvertToObject(element); + if (!string.IsNullOrEmpty(jsonPropertyName)) + { + var pso = new PSObject(); + pso.Properties.Add(new PSNoteProperty(jsonPropertyName, value)); + return pso; + } + if (value is PSObject) + { + return (PSObject)value; + } + throw new FormatException($"primitive type[{element.ValueKind}] can not be converted to PSObject"); + } + + public static object ConvertToObject(string jsonString) + { + var jsonElement = JsonSerializer.Deserialize(jsonString); + return ConvertToObject(jsonElement); + } + + public static object ConvertToObject(JsonElement element) + { + object value = null; + switch (element.ValueKind) + { + case JsonValueKind.Array: + { + value = ConvertToPSObjectArray(element); + break; + } + case JsonValueKind.True: + case JsonValueKind.False: + { + value = element.GetBoolean(); + break; + } + case JsonValueKind.String: + { + value = element.GetString(); + break; + } + case JsonValueKind.Object: + { + var nestedPso = new PSObject(); + foreach (var prop in element.EnumerateObject()) + { + var propValue = ConvertToObject(prop.Value); + nestedPso.Properties.Add(new PSNoteProperty(prop.Name, propValue)); + } + value = nestedPso; + break; + } + case JsonValueKind.Number: + { + value = element.GetInt64(); + break; + } + } + + return value; + } + + private static List ConvertToPSObjectArray(JsonElement element) + { + var list = new List(); + + foreach (var subelement in element.EnumerateArray()) + { + var value = ConvertToObject(subelement); + list.Add(value); + } + return list; + } + } +} \ No newline at end of file diff --git a/src/Commands/Utilities/REST/GraphHelper.cs b/src/Commands/Utilities/REST/GraphHelper.cs index 24212773b..500246f48 100644 --- a/src/Commands/Utilities/REST/GraphHelper.cs +++ b/src/Commands/Utilities/REST/GraphHelper.cs @@ -40,7 +40,7 @@ public static bool TryGetGraphException(HttpResponseMessage responseMessage, out } } - private static HttpRequestMessage GetMessage(string url, HttpMethod method, string accessToken, HttpContent content = null, Dictionary additionalHeaders = null) + private static HttpRequestMessage GetMessage(string url, HttpMethod method, string accessToken, HttpContent content = null, IDictionary additionalHeaders = null) { if (url.StartsWith("/")) { @@ -67,9 +67,9 @@ private static HttpRequestMessage GetMessage(string url, HttpMethod method, stri return message; } - public static async Task GetAsync(HttpClient httpClient, string url, string accessToken) + public static async Task GetAsync(HttpClient httpClient, string url, string accessToken, IDictionary additionalHeaders = null) { - var message = GetMessage(url, HttpMethod.Get, accessToken); + var message = GetMessage(url, HttpMethod.Get, accessToken, null, additionalHeaders); return await SendMessageAsync(httpClient, message, accessToken); } @@ -148,26 +148,26 @@ public static async Task GetAsync(HttpClient httpClient, string url, strin return default(T); } - public static async Task PostAsync(HttpClient httpClient, string url, string accessToken, HttpContent content) + public static async Task PostAsync(HttpClient httpClient, string url, string accessToken, HttpContent content, IDictionary additionalHeaders = null) { - var message = GetMessage(url, HttpMethod.Post, accessToken, content); + var message = GetMessage(url, HttpMethod.Post, accessToken, content, additionalHeaders); return await GetResponseMessageAsync(httpClient, message); } - public static async Task PutAsync(HttpClient httpClient, string url, string accessToken, HttpContent content) + public static async Task PutAsync(HttpClient httpClient, string url, string accessToken, HttpContent content, IDictionary additionalHeaders = null) { - var message = GetMessage(url, HttpMethod.Put, accessToken, content); + var message = GetMessage(url, HttpMethod.Put, accessToken, content, additionalHeaders); return await GetResponseMessageAsync(httpClient, message); } #region DELETE - public static async Task DeleteAsync(HttpClient httpClient, string url, string accessToken, Dictionary additionalHeaders = null) + public static async Task DeleteAsync(HttpClient httpClient, string url, string accessToken, IDictionary additionalHeaders = null) { var message = GetMessage(url, HttpMethod.Delete, accessToken, null, additionalHeaders); return await GetResponseMessageAsync(httpClient, message); } - public static async Task DeleteAsync(HttpClient httpClient, string url, string accessToken, bool camlCasePolicy = true, Dictionary additionalHeaders = null) + public static async Task DeleteAsync(HttpClient httpClient, string url, string accessToken, bool camlCasePolicy = true, IDictionary additionalHeaders = null) { var response = await DeleteAsync(httpClient, url, accessToken, additionalHeaders); if (response.IsSuccessStatusCode) @@ -196,7 +196,7 @@ public static async Task DeleteAsync(HttpClient httpClient, string url, st #endregion #region PATCH - public static async Task PatchAsync(HttpClient httpClient, string accessToken, string url, T content, Dictionary additionalHeaders = null, bool camlCasePolicy = true) + public static async Task PatchAsync(HttpClient httpClient, string accessToken, string url, T content, IDictionary additionalHeaders = null, bool camlCasePolicy = true) { var serializerSettings = new JsonSerializerOptions() { IgnoreNullValues = true }; if (camlCasePolicy) @@ -221,7 +221,7 @@ public static async Task PatchAsync(HttpClient httpClient, string accessTo } } - public static async Task PatchAsync(HttpClient httpClient, string accessToken, string url, HttpContent content, Dictionary additionalHeaders = null) + public static async Task PatchAsync(HttpClient httpClient, string accessToken, string url, HttpContent content, IDictionary additionalHeaders = null) { #if NETFRAMEWORK var message = GetMessage(url, new HttpMethod("PATCH"), accessToken, content, additionalHeaders); @@ -239,7 +239,7 @@ public static async Task PatchAsync(HttpClient httpClient, string accessTo } } - public static async Task PatchAsync(HttpClient httpClient, string accessToken, HttpContent content, string url, Dictionary additionalHeaders = null) + public static async Task PatchAsync(HttpClient httpClient, string accessToken, HttpContent content, string url, IDictionary additionalHeaders = null) { #if NETFRAMEWORK var message = GetMessage(url, new HttpMethod("PATCH"), accessToken, content, additionalHeaders); @@ -251,7 +251,7 @@ public static async Task PatchAsync(HttpClient httpClient, - // public static async Task PatchAsync(HttpClient httpClient, string accessToken, string url, T content,Dictionary additionalHeaders = null) + // public static async Task PatchAsync(HttpClient httpClient, string accessToken, string url, T content,IDictionary additionalHeaders = null) // { // var requestContent = new StringContent(JsonSerializer.Serialize(content, new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase })); // requestContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); @@ -269,12 +269,12 @@ public static async Task PatchAsync(HttpClient httpClient, #endregion - public static async Task PostAsync(HttpClient httpClient, string url, HttpContent content, string accessToken, Dictionary additionalHeaders = null, bool propertyNameCaseInsensitive = false) + public static async Task PostAsync(HttpClient httpClient, string url, HttpContent content, string accessToken, IDictionary additionalHeaders = null, bool propertyNameCaseInsensitive = false) { return await PostInternalAsync(httpClient, url, accessToken, content, additionalHeaders, propertyNameCaseInsensitive); } - public static async Task PutAsync(HttpClient httpClient, string url, string accessToken, HttpContent content, Dictionary additionalHeaders = null) + public static async Task PutAsync(HttpClient httpClient, string url, string accessToken, HttpContent content, IDictionary additionalHeaders = null) { var message = GetMessage(url, HttpMethod.Put, accessToken, content, additionalHeaders); var stringContent = await SendMessageAsync(httpClient, message, accessToken); @@ -305,7 +305,7 @@ public static async Task PostAsync(HttpClient httpClient, string url, stri return await PostInternalAsync(httpClient, url, accessToken, null); } - private static async Task PostInternalAsync(HttpClient httpClient, string url, string accessToken, HttpContent content, Dictionary additionalHeaders = null, bool propertyNameCaseInsensitive = false) + private static async Task PostInternalAsync(HttpClient httpClient, string url, string accessToken, HttpContent content, IDictionary additionalHeaders = null, bool propertyNameCaseInsensitive = false) { var message = GetMessage(url, HttpMethod.Post, accessToken, content, additionalHeaders); var stringContent = await SendMessageAsync(httpClient, message, accessToken); From 6273df09a04160b49fd5e92b4fe09ab637a999c6 Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Tue, 3 May 2022 09:18:31 +0200 Subject: [PATCH 154/458] Get-PnPTeamsChannelUser cmdlet (#1802) --- documentation/Get-PnPTeamsChannelUser.md | 123 ++++++++++++++++++ .../PipeBinds/TeamsChannelMemberPipeBind.cs | 85 ++++++++++++ src/Commands/Model/Teams/TeamChannelMember.cs | 2 +- src/Commands/Teams/GetTeamsChannelUser.cs | 50 +++++++ src/Commands/Utilities/TeamsUtility.cs | 24 +++- 5 files changed, 282 insertions(+), 2 deletions(-) create mode 100644 documentation/Get-PnPTeamsChannelUser.md create mode 100644 src/Commands/Base/PipeBinds/TeamsChannelMemberPipeBind.cs create mode 100644 src/Commands/Teams/GetTeamsChannelUser.cs diff --git a/documentation/Get-PnPTeamsChannelUser.md b/documentation/Get-PnPTeamsChannelUser.md new file mode 100644 index 000000000..565d24828 --- /dev/null +++ b/documentation/Get-PnPTeamsChannelUser.md @@ -0,0 +1,123 @@ +--- +Module Name: PnP.PowerShell +title: Get-PnPTeamsChannelUser +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Get-PnPTeamsChannelUser.html +--- + +# Get-PnPTeamsChannelUser + +## SYNOPSIS + +**Required Permissions** + + * Microsoft Graph API: ChannelMember.Read.All + +Returns members from the specified Microsoft Teams private Channel. + +## SYNTAX + +```powershell +Get-PnPTeamsChannelUser -Team -Channel [-Identity ] [-Role ] + [] +``` + +## DESCRIPTION + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Get-PnPTeamsChannelUser -Team "My Team" -Channel "My Channel" +``` + +Returns all owners, members and guests from the specified channel. + +### EXAMPLE 2 + +```powershell +Get-PnPTeamsChannelUser -Team "My Team" -Channel "My Channel" -Role Member +``` + +Returns all members from the specified channel. + +### EXAMPLE 3 + +```powershell +Get-PnPTeamsChannelUser -Team "My Team" -Channel "My Channel" -Identity john.doe@contoso.com +``` + +Returns membership of the user "john.doe@contoso.com" for the specified channel. + +### EXAMPLE 4 + +```powershell +Get-PnPTeamsChannelUser -Team "My Team" -Channel "My Channel" -Identity 00000000-0000-0000-0000-000000000000 +``` + +Returns membership of the user with ID "00000000-0000-0000-0000-000000000000" for the specified channel. + +## PARAMETERS + +### -Identity +Specify membership id, UPN or user ID of the channel member. + +```yaml +Type: TeamsChannelMemberPipeBind +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Channel +Specify id or name of the channel to use. + +```yaml +Type: TeamsChannelPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Team +Specify the group id, mailNickname or display name of the team to use. + +```yaml +Type: TeamsTeamPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Role +Specify to filter on the role of the user. + +```yaml +Type: String +Parameter Sets: (All) +Accepted values: Owner, Member, Guest + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) diff --git a/src/Commands/Base/PipeBinds/TeamsChannelMemberPipeBind.cs b/src/Commands/Base/PipeBinds/TeamsChannelMemberPipeBind.cs new file mode 100644 index 000000000..6796f5c13 --- /dev/null +++ b/src/Commands/Base/PipeBinds/TeamsChannelMemberPipeBind.cs @@ -0,0 +1,85 @@ +using PnP.PowerShell.Commands.Model.Teams; +using PnP.PowerShell.Commands.Utilities; +using System; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; + +namespace PnP.PowerShell.Commands.Base.PipeBinds +{ + public sealed class TeamsChannelMemberPipeBind + { + private readonly string _id; + private readonly string _userId; + private readonly string _userUpn; + private readonly TeamChannelMember _membership; + + public TeamsChannelMemberPipeBind() + { + + } + + public TeamsChannelMemberPipeBind(string input) + { + if (Guid.TryParse(input, out var userId)) + { + _userId = userId.ToString(); + } + else if (input.Contains('@') && input.Contains('.')) + { + _userUpn = input; + } + else + { + _id = input; + } + } + + public TeamsChannelMemberPipeBind(TeamChannelMember membership) + { + _membership = membership; + } + + public async Task GetIdAsync(HttpClient httpClient, string accessToken, string groupId, string channelId) + { + if (!string.IsNullOrEmpty(_id)) + { + return _id; + } + + if (_membership != null) + { + return _membership.Id; + } + + var memberships = await TeamsUtility.GetChannelMembersAsync(httpClient, accessToken, groupId, channelId); + if (!string.IsNullOrEmpty(_userUpn)) + { + return memberships.FirstOrDefault(m => _userUpn.Equals(m.Email, StringComparison.OrdinalIgnoreCase))?.Id; + } + + return memberships.FirstOrDefault(m => !string.IsNullOrEmpty(m.UserId) && _userId.Equals(m.UserId, StringComparison.OrdinalIgnoreCase))?.Id; + } + + public async Task GetMembershipAsync(HttpClient httpClient, string accessToken, string groupId, string channelId) + { + if (_membership != null) + { + return _membership; + } + + if (!string.IsNullOrEmpty(_id)) + { + return await TeamsUtility.GetChannelMemberAsync(httpClient, accessToken, groupId, channelId, _id); + } + + var memberships = await TeamsUtility.GetChannelMembersAsync(httpClient, accessToken, groupId, channelId); + if (!string.IsNullOrEmpty(_userUpn)) + { + return memberships.FirstOrDefault(m => _userUpn.Equals(m.Email, StringComparison.OrdinalIgnoreCase)); + } + + return memberships.FirstOrDefault(m => !string.IsNullOrEmpty(m.UserId) && _userId.Equals(m.UserId, StringComparison.OrdinalIgnoreCase)); + } + } +} diff --git a/src/Commands/Model/Teams/TeamChannelMember.cs b/src/Commands/Model/Teams/TeamChannelMember.cs index 907406277..41a5d646e 100644 --- a/src/Commands/Model/Teams/TeamChannelMember.cs +++ b/src/Commands/Model/Teams/TeamChannelMember.cs @@ -24,6 +24,6 @@ public class TeamChannelMember public string UserId { get; set; } [JsonPropertyName("email")] - public string email { get; set; } + public string Email { get; set; } } } diff --git a/src/Commands/Teams/GetTeamsChannelUser.cs b/src/Commands/Teams/GetTeamsChannelUser.cs new file mode 100644 index 000000000..c3711d8bc --- /dev/null +++ b/src/Commands/Teams/GetTeamsChannelUser.cs @@ -0,0 +1,50 @@ +using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Utilities; +using System.Management.Automation; + +namespace PnP.PowerShell.Commands.Teams +{ + [Cmdlet(VerbsCommon.Get, "PnPTeamsChannelUser")] + [RequiredMinimalApiPermissions("ChannelMember.Read.All")] + public class GetTeamsChannelUser : PnPGraphCmdlet + { + [Parameter(Mandatory = true)] + public TeamsTeamPipeBind Team; + + [Parameter(Mandatory = true)] + public TeamsChannelPipeBind Channel; + + [Parameter(Mandatory = false)] + public TeamsChannelMemberPipeBind Identity; + + [Parameter(Mandatory = false)] + [ValidateSet("Owner", "Member", "Guest")] + public string Role; + + protected override void ExecuteCmdlet() + { + var groupId = Team.GetGroupId(HttpClient, AccessToken); + if (groupId == null) + { + throw new PSArgumentException("Group not found"); + } + + var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + if (channelId == null) + { + throw new PSArgumentException("Channel not found"); + } + + if (ParameterSpecified(nameof(Identity))) + { + WriteObject(Identity.GetMembershipAsync(HttpClient, AccessToken, groupId, channelId).GetAwaiter().GetResult()); + } + else + { + WriteObject(TeamsUtility.GetChannelMembersAsync(HttpClient, AccessToken, groupId, channelId, Role).GetAwaiter().GetResult(), true); + } + } + } +} diff --git a/src/Commands/Utilities/TeamsUtility.cs b/src/Commands/Utilities/TeamsUtility.cs index 8eb3ef7a1..dbf722d96 100644 --- a/src/Commands/Utilities/TeamsUtility.cs +++ b/src/Commands/Utilities/TeamsUtility.cs @@ -500,7 +500,7 @@ public static async Task> GetUsersAsync(HttpClient httpClient, var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/members", accessToken); if (collection != null && collection.Any()) { - users.AddRange(collection.Select(m => new User() { DisplayName = m.DisplayName, Id = m.UserId, UserPrincipalName = m.email, UserType = m.Roles.Count > 0 ? m.Roles[0].ToLower() : "" })); + users.AddRange(collection.Select(m => new User() { DisplayName = m.DisplayName, Id = m.UserId, UserPrincipalName = m.Email, UserType = m.Roles.Count > 0 ? m.Roles[0].ToLower() : "" })); } if (selectedRole != null) @@ -610,6 +610,28 @@ public static async Task AddChannelAsync(string accessToken, HttpCl } } + public static async Task GetChannelMemberAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string membershipId) + { + // Currently the Graph request to get a membership by id fails (v1.0/teams/{groupId}/channels/{channelId}/members/{membershipId}). + // This is why the method below is used. + + var memberships = await GetChannelMembersAsync(httpClient, accessToken, groupId, channelId); + return memberships.FirstOrDefault(m => membershipId.Equals(m.Id)); + } + + public static async Task> GetChannelMembersAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string role = null) + { + var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/members", accessToken); + + if (!string.IsNullOrEmpty(role)) + { + // Members have no role value + collection = role.Equals("member", StringComparison.OrdinalIgnoreCase) ? collection.Where(i => !i.Roles.Any()) : collection.Where(i => i.Roles.Any(r => role.Equals(r, StringComparison.OrdinalIgnoreCase))); + } + + return collection; + } + public static async Task AddChannelUserAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string upn, string role) { var channelMember = new TeamChannelMember From fe53b62525cdd61739b001a2e7bba35d7b00f9bc Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Tue, 3 May 2022 10:24:21 +0300 Subject: [PATCH 155/458] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c78332667..b0f5a960d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `ExcludeVisualPromotedResults` parameter to `Get-PnPSearchConfiguration` which excludes promoted results [#1750](https://github.com/pnp/powershell/pull/1750) - Added `MediaTranscription` parameter to `Set-PnPTenantSite` and `Set-PnPSite` cmdlets which when enabled allows videos to have transcripts generated on demand or generated automatically in certain scenarios. [#1771](https://github.com/pnp/powershell/pull/1771) - Added `Accept` parameter to `Invoke-PnPSPRestMethod` cmdlet which if specified will pass the Accept HTTP request header. [#1795](https://github.com/pnp/powershell/pull/1795) +- Added `Get-PnPFlowRun` cmdlet to retrieve a specific run, or all runs from a specific Power Automate flow. [#1819](https://github.com/pnp/powershell/pull/1819) +- Added `Invoke-PnPGraphMethod` cmdlet to invoke generic Microsoft Graph API Methods. [#1820](https://github.com/pnp/powershell/pull/1820) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) @@ -25,10 +27,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Get-PnPGroup` , `Get-PnPGroupPermissions` and `Set-PnPGroupPermissions ` cmdlets by making them more consistent. They will also throw error if a group is not found. [#1808](https://github.com/pnp/powershell/pull/1808) - Fixed `Get-PnPFile` issue with every 3rd file download in PS 5. - Fixed `Add-PnPContentTypesFromContentTypeHub`, if `Site` parameter is specified, it will be used now to sync content types from content type hub site. +- Fixed `Get-PnPTeamsTeam`, the cmdlet now also returns additional properties like `WebUrl, CreatedDateTime, InternalId` [#1825](https://github.com/pnp/powershell/pull/1825) ### Removed ### Contributors +- [4ndri] - Martin Lingstuyl [martinlingstuyl] - James May [fowl2] - Milan Holemans [milanholemans] From 344a533ebb9b6d062770e5bfa829ca0dec8ad7ea Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Tue, 3 May 2022 17:21:09 +0300 Subject: [PATCH 156/458] Feature: add support for Viva connections webparts (#1805) * Feature: add support for Viva connections webparts * Removed ValueFromPipeline * Update CHANGELOG.md --- CHANGELOG.md | 6 +- .../Add-PnPVivaConnectionsDashboardACE.md | 171 ++++++++++++++++++ .../Get-PnPVivaConnectionsDashboardACE.md | 73 ++++++++ .../Remove-PnPVivaConnectionsDashboardACE.md | 66 +++++++ .../Update-PnPVivaConnectionsDashboardACE.md | 171 ++++++++++++++++++ .../Viva/AddVivaConnectionsDashboardACE.cs | 76 ++++++++ .../Viva/GetVivaConnectionsDashboard.cs | 45 +++++ src/Commands/Viva/RemoveVivaConnectionsACE.cs | 38 ++++ src/Commands/Viva/UpdateVivaConnectionsACE.cs | 103 +++++++++++ 9 files changed, 748 insertions(+), 1 deletion(-) create mode 100644 documentation/Add-PnPVivaConnectionsDashboardACE.md create mode 100644 documentation/Get-PnPVivaConnectionsDashboardACE.md create mode 100644 documentation/Remove-PnPVivaConnectionsDashboardACE.md create mode 100644 documentation/Update-PnPVivaConnectionsDashboardACE.md create mode 100644 src/Commands/Viva/AddVivaConnectionsDashboardACE.cs create mode 100644 src/Commands/Viva/GetVivaConnectionsDashboard.cs create mode 100644 src/Commands/Viva/RemoveVivaConnectionsACE.cs create mode 100644 src/Commands/Viva/UpdateVivaConnectionsACE.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index b0f5a960d..356626e6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `ResourceBehaviorOptions` option in `New-PnPMicrosoft365Group` cmdlet to set `ResourceBehaviorOptions` while provisioning a Microsoft 365 Group. [#1774](https://github.com/pnp/powershell/pull/1774) - Added `Add-PnPTeamsChannelUser` which allows members and owners to be added to private channels in Teams [#1735](https://github.com/pnp/powershell/pull/1735) - Added `ExcludeVisualPromotedResults` parameter to `Get-PnPSearchConfiguration` which excludes promoted results [#1750](https://github.com/pnp/powershell/pull/1750) -- Added `MediaTranscription` parameter to `Set-PnPTenantSite` and `Set-PnPSite` cmdlets which when enabled allows videos to have transcripts generated on demand or generated automatically in certain scenarios. [#1771](https://github.com/pnp/powershell/pull/1771) +- Added `MediaTranscription` parameter to `Set-PnPTenantSite` and `Set-PnPSite` cmdlets which when enabled allows videos to have transcripts generated on demand or generated automatically in certain scenarios +- Added `Get-PnPVivaConnectionsDashboardACE` to retrieve the Adaptive Card extensions from the Viva connections dashboard page. [#1805](https://github.com/pnp/powershell/pull/1805) +- Added `Add-PnPVivaConnectionsDashboardACE` to add an Adaptive Card extension to the Viva connections dashboard page. [#1805](https://github.com/pnp/powershell/pull/1805) +- Added `Update-PnPVivaConnectionsDashboardACE` to update an Adaptive Card extension in the Viva connections dashboard page. [#1805](https://github.com/pnp/powershell/pull/1805) +- Added `Remove-PnPVivaConnectionsDashboardACE` to remove an Adaptive Card extension in the Viva connections dashboard page. [#1805](https://github.com/pnp/powershell/pull/1805) - Added `Accept` parameter to `Invoke-PnPSPRestMethod` cmdlet which if specified will pass the Accept HTTP request header. [#1795](https://github.com/pnp/powershell/pull/1795) - Added `Get-PnPFlowRun` cmdlet to retrieve a specific run, or all runs from a specific Power Automate flow. [#1819](https://github.com/pnp/powershell/pull/1819) - Added `Invoke-PnPGraphMethod` cmdlet to invoke generic Microsoft Graph API Methods. [#1820](https://github.com/pnp/powershell/pull/1820) diff --git a/documentation/Add-PnPVivaConnectionsDashboardACE.md b/documentation/Add-PnPVivaConnectionsDashboardACE.md new file mode 100644 index 000000000..4f97bac21 --- /dev/null +++ b/documentation/Add-PnPVivaConnectionsDashboardACE.md @@ -0,0 +1,171 @@ +--- +Module Name: PnP.PowerShell +title: Add-PnPVivaConnectionsDashboardACE +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Add-PnPVivaConnectionsDashboardACE.html +--- + +# Add-PnPVivaConnectionsDashboardACE + +## SYNOPSIS +Add an Adaptive card extension in the Viva connections dashboard page. This requires that you connect to a SharePoint Home site and have configured the Viva connections page. + +## SYNTAX + +```powershell +Add-PnPVivaConnectionsDashboardACE [-Identity ] [-Title ] [-PropertiesJSON ] [-Description ] [-IconProperty ] [-Order ][-CardSize ] [-Connection ] [] +``` + +## DESCRIPTION + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Add-PnPVivaConnectionsDashboardACE -Identity CardDesigner -Order 3 -Title "Hello there" -PropertiesJSON $myProperties -CardSize Large -Description "ACE description" -Iconproperty "https://cdn.hubblecontent.osi.office.net/m365content/publish/002f8bf9-b8ee-4689-ae97-e411b756099d/691108002.jpg" +``` + +Add an Adaptive card extension of type Card Designer in the Viva connections dashboard page with Title, Description, IconProperty, Order , CardSize and PropertiesJSON of the ACE. + +### EXAMPLE 2 +```powershell +Add-PnPVivaConnectionsDashboardACE -Identity ThirdPartyApp -Order 1 -Title "Hello there" -PropertiesJSON $myProperties -CardSize Medium -Description "ACE with description" -Iconproperty "https://cdn.hubblecontent.osi.office.net/m365content/publish/002f8bf9-b8ee-4689-ae97-e411b756099d/691108002.jpg" +``` + +Add an Adaptive card extension of type Third party(custom adaptive card) in the Viva connections dashboard page with Title, Description, IconProperty, Order , CardSize and PropertiesJSON of the ACE. + +### EXAMPLE 3 +```powershell +Add-PnPVivaConnectionsDashboardACE -Identity AssignedTasks -Order 2 -Title "Tasks" -PropertiesJSON $myProperties -CardSize Medium -Description "My Assigned tasks" -Iconproperty "https://cdn.hubblecontent.osi.office.net/m365content/publish/002f8bf9-b8ee-4689-ae97-e411b756099d/691108002.jpg" +``` + +Add an Adaptive card extension of type AssignedTasks in the Viva connections dashboard page with Title, Description, IconProperty, Order , CardSize and PropertiesJSON of the ACE. + +## PARAMETERS + +### -Identity +The Id of the Adaptive Card on the Viva connections dashboard page. Supported values are: + +- Approvals +- AssignedTasks +- CardDesigner +- Shifts +- TeamsApp +- ThirdParty +- WebLink + +```yaml +Type: DefaultACE +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Title +The Tite of the Adaptive Card extension. + +```yaml +Type: string +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Description +The Description of the Adaptive Card extension. + +```yaml +Type: string +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -IconProperty +The Icon used by Adaptive Card extension. + +```yaml +Type: string +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -PropertiesJSON +The properties of the Adaptive Card extension. You can get the properties by executing `Get-PnPVivaConnectionsDashboardACE` and then use the `JSONProperties`. + +```yaml +Type: string +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Order +The Order of appearance of the Adaptive Card extension on the Viva connections dashboard page. The default value is 0. + +```yaml +Type: Int +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -CardSize +The size of the Adaptive Card extension. The available values are `Large` or `Medium`. Default card size is `Medium` + +```yaml +Type: CardSize +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) + diff --git a/documentation/Get-PnPVivaConnectionsDashboardACE.md b/documentation/Get-PnPVivaConnectionsDashboardACE.md new file mode 100644 index 000000000..b0f236404 --- /dev/null +++ b/documentation/Get-PnPVivaConnectionsDashboardACE.md @@ -0,0 +1,73 @@ +--- +Module Name: PnP.PowerShell +title: Get-PnPVivaConnectionsDashboardACE +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Get-PnPVivaConnectionsDashboardACE.html +--- + +# Get-PnPVivaConnectionsDashboardACE + +## SYNOPSIS +Returns the Adaptive card extensions from the Viva connections dashboard page. This requires that you connect to a SharePoint Home site and have configured the Viva connections page. + +## SYNTAX + +```powershell +Get-PnPVivaConnectionsDashboardACE [-Identity ] [-Connection ] [] +``` + +## DESCRIPTION + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Get-PnPVivaConnectionsDashboardACE +``` + +Returns all the adaptive card extensions present in the Viva Connections dashboard page. + +### EXAMPLE 2 +```powershell +Get-PnPVivaConnectionsDashboardACE -Identity "58108715-185e-4214-8786-01218e7ab9ef" +``` + +Returns the adaptive card extensions with specified Instance Id from the Viva Connections dashboard page. + + +## PARAMETERS + +### -Identity +The instance Id of the Adaptive Card extension present on the Viva connections dashboard page. You can retrieve the value for this parameter by executing `Get-PnPVivaConnectionsDashboardACE` cmdlet + +```yaml +Type: GUID +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) + diff --git a/documentation/Remove-PnPVivaConnectionsDashboardACE.md b/documentation/Remove-PnPVivaConnectionsDashboardACE.md new file mode 100644 index 000000000..31577fb0f --- /dev/null +++ b/documentation/Remove-PnPVivaConnectionsDashboardACE.md @@ -0,0 +1,66 @@ +--- +Module Name: PnP.PowerShell +title: Remove-PnPVivaConnectionsDashboardACE +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Remove-PnPVivaConnectionsDashboardACE.html +--- + +# Remove-PnPVivaConnectionsDashboardACE + +## SYNOPSIS +Removes the Adaptive card extensions from the Viva connections dashboard page. This requires that you connect to a SharePoint Home site and have configured the Viva connections page. + +## SYNTAX + +```powershell +Remove-PnPVivaConnectionsDashboardACE [-Identity ] [-Connection ] [] +``` + +## DESCRIPTION + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Remove-PnPVivaConnectionsDashboardACE -Identity "58108715-185e-4214-8786-01218e7ab9ef" +``` + +Removes the adaptive card extensions with Instance Id `58108715-185e-4214-8786-01218e7ab9ef` from the Viva connections dashboard page + + +## PARAMETERS + +### -Identity +The instance Id of the Adaptive Card extension present on the Viva connections dashboard page. You can retrieve the value for this parameter by executing `Get-PnPVivaConnectionsDashboardACE` cmdlet + +```yaml +Type: GUID +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) + diff --git a/documentation/Update-PnPVivaConnectionsDashboardACE.md b/documentation/Update-PnPVivaConnectionsDashboardACE.md new file mode 100644 index 000000000..6e8ce0ecf --- /dev/null +++ b/documentation/Update-PnPVivaConnectionsDashboardACE.md @@ -0,0 +1,171 @@ +--- +Module Name: PnP.PowerShell +title: Update-PnPVivaConnectionsDashboardACE +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Update-PnPVivaConnectionsDashboardACE.html +--- + +# Update-PnPVivaConnectionsDashboardACE + +## SYNOPSIS +Update the Adaptive card extension in the Viva connections dashboard page. This requires that you connect to a SharePoint Home site and have configured the Viva connections page. + +## SYNTAX + +```powershell +Update-PnPVivaConnectionsDashboardACE [-Identity ] [-Title ] [-PropertiesJSON ] [-Description ] [-IconProperty ] [-Order ][-CardSize ] [-Connection ] [] +``` + +## DESCRIPTION + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Update-PnPVivaConnectionsDashboardACE -Identity "58108715-185e-4214-8786-01218e7ab9ef" -Title "Update title" -Description "Update Description" -IconProperty "https://cdn.hubblecontent.osi.office.net/m365content/publish/002f8bf9-b8ee-4689-ae97-e411b756099d/691108002.jpg" -Order 4 -CardSize Large -PropertiesJSON $myProperties +``` + +Update the adaptive card extensions with Instance Id `58108715-185e-4214-8786-01218e7ab9ef` in the Viva connections dashboard page. It will update the Title, Description, IconProperty, Order , CardSize and PropertiesJSON of the ACE. + +### EXAMPLE 2 +```powershell +Update-PnPVivaConnectionsDashboardACE -Identity "58108715-185e-4214-8786-01218e7ab9ef" -Title "Update title" -Description "Update Description" +``` + +Update the adaptive card extensions with Instance Id `58108715-185e-4214-8786-01218e7ab9ef` in the Viva connections dashboard page. It will update the Title and Description of the ACE. + +### EXAMPLE 3 +```powershell +Update-PnPVivaConnectionsDashboardACE -Identity "58108715-185e-4214-8786-01218e7ab9ef" -IconProperty "https://cdn.hubblecontent.osi.office.net/m365content/publish/002f8bf9-b8ee-4689-ae97-e411b756099d/691108002.jpg" -Order 4 +``` + +Update the adaptive card extensions with Instance Id `58108715-185e-4214-8786-01218e7ab9ef` in the Viva connections dashboard page. It will update the IconProperty and Order of the ACE. + +### EXAMPLE 4 +```powershell +Update-PnPVivaConnectionsDashboardACE -Identity "58108715-185e-4214-8786-01218e7ab9ef" -CardSize Large +``` + +Update the adaptive card extensions with Instance Id `58108715-185e-4214-8786-01218e7ab9ef` in the Viva connections dashboard page. It will update the CardSize to large. + + +## PARAMETERS + +### -Identity +The instance Id of the Adaptive Card extension present on the Viva connections dashboard page. You can retrieve the value for this parameter by executing `Get-PnPVivaConnectionsDashboardACE` cmdlet + +```yaml +Type: GUID +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Title +The Tite of the Adaptive Card extension present on the Viva connections dashboard page. + +```yaml +Type: string +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Description +The Description of the Adaptive Card extension present on the Viva connections dashboard page. + +```yaml +Type: string +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -IconProperty +The Icon used by Adaptive Card extension present on the Viva connections dashboard page. + +```yaml +Type: string +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -PropertiesJSON +The properties of the Adaptive Card extension present on the Viva connections dashboard page. + +```yaml +Type: string +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Order +The Order of appearance of the Adaptive Card extension present on the Viva connections dashboard page. + +```yaml +Type: Int +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -CardSize +The size of the Adaptive Card extension present on the Viva connections dashboard page. The available values are `Large` or `Medium`. + +```yaml +Type: CardSize +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) + diff --git a/src/Commands/Viva/AddVivaConnectionsDashboardACE.cs b/src/Commands/Viva/AddVivaConnectionsDashboardACE.cs new file mode 100644 index 000000000..0131a2718 --- /dev/null +++ b/src/Commands/Viva/AddVivaConnectionsDashboardACE.cs @@ -0,0 +1,76 @@ +using PnP.Core.Model.SharePoint; +using System; +using System.Collections.Generic; +using System.Management.Automation; +using System.Text; +using PnP.Core.Services; +using System.Text.Json; + +namespace PnP.PowerShell.Commands.Viva +{ + [Cmdlet(VerbsCommon.Add, "PnPVivaConnectionsDashboardACE")] + public class AddVivaConnectionsDashboardACE : PnPWebCmdlet + { + [Parameter(Mandatory = true)] + public DefaultACE Identity; + + [Parameter(Mandatory = true)] + public int Order = 0; + + [Parameter(Mandatory = false)] + public string Title = ""; + + [Parameter(Mandatory = true)] + public string PropertiesJSON; + + [Parameter(Mandatory = false)] + public string Description; + + [Parameter(Mandatory = false)] + public string IconProperty; + + [Parameter(Mandatory = false)] + public CardSize CardSize = CardSize.Medium; + + protected override void ExecuteCmdlet() + { + if (PnPContext.Site.IsHomeSite()) + { + IVivaDashboard dashboard = PnPContext.Web.GetVivaDashboardAsync().GetAwaiter().GetResult(); + + var cardDesignerACE = dashboard.NewACE(Identity, CardSize); + cardDesignerACE.Title = Title; + cardDesignerACE.Properties = JsonSerializer.Deserialize(PropertiesJSON); + + if (ParameterSpecified(nameof(Description))) + { + cardDesignerACE.Description = Description; + } + + if (ParameterSpecified(nameof(IconProperty))) + { + cardDesignerACE.IconProperty = IconProperty; + } + + if (ParameterSpecified(nameof(Order)) && Order > -1) + { + dashboard.AddACE(cardDesignerACE, Order); + } + else + { + dashboard.AddACE(cardDesignerACE); + } + + dashboard.Save(); + + // load the dashboard again + dashboard = PnPContext.Web.GetVivaDashboardAsync().GetAwaiter().GetResult(); + WriteObject(dashboard, true); + } + else + { + WriteWarning("Connected site is not a home site"); + } + } + } +} diff --git a/src/Commands/Viva/GetVivaConnectionsDashboard.cs b/src/Commands/Viva/GetVivaConnectionsDashboard.cs new file mode 100644 index 000000000..b6e7e3ca8 --- /dev/null +++ b/src/Commands/Viva/GetVivaConnectionsDashboard.cs @@ -0,0 +1,45 @@ +using PnP.Core.Model.SharePoint; +using PnP.PowerShell.Commands.Base; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation; +using System.Text; + +namespace PnP.PowerShell.Commands.Viva +{ + [Cmdlet(VerbsCommon.Get, "PnPVivaConnectionsDashboardACE")] + public class GetVivaConnectionsDashboard : PnPWebCmdlet + { + [Parameter(Mandatory = false)] + public Guid Identity; + protected override void ExecuteCmdlet() + { + if (PnPContext.Site.IsHomeSite()) + { + IVivaDashboard dashboard = PnPContext.Web.GetVivaDashboardAsync().GetAwaiter().GetResult(); + + if (ParameterSpecified(nameof(Identity))) + { + var aceToRetrieve = dashboard.ACEs.FirstOrDefault(p => p.InstanceId == Identity); + if (aceToRetrieve != null) + { + WriteObject(aceToRetrieve); + } + else + { + WriteWarning("ACE with specified Instance Id not found"); + } + } + else + { + WriteObject(dashboard.ACEs, true); + } + } + else + { + WriteWarning("Connected site is not a home site"); + } + } + } +} diff --git a/src/Commands/Viva/RemoveVivaConnectionsACE.cs b/src/Commands/Viva/RemoveVivaConnectionsACE.cs new file mode 100644 index 000000000..6cd6a6e27 --- /dev/null +++ b/src/Commands/Viva/RemoveVivaConnectionsACE.cs @@ -0,0 +1,38 @@ +using PnP.Core.Model.SharePoint; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation; +using System.Text; + +namespace PnP.PowerShell.Commands.Viva +{ + [Cmdlet(VerbsCommon.Remove, "PnPVivaConnectionsDashboardACE")] + public class RemoveVivaConnectionsACE : PnPWebCmdlet + { + [Parameter(Mandatory = true)] + public Guid Identity; + protected override void ExecuteCmdlet() + { + if (PnPContext.Site.IsHomeSite()) + { + IVivaDashboard dashboard = PnPContext.Web.GetVivaDashboardAsync().GetAwaiter().GetResult(); + var aceToRemove = dashboard.ACEs.FirstOrDefault(p => p.InstanceId == Identity); + + if (aceToRemove != null) + { + dashboard.RemoveACE(aceToRemove.InstanceId); + dashboard.Save(); + } + else + { + WriteWarning("ACE with specified instance Id not found"); + } + } + else + { + WriteWarning("Connected site is not a home site"); + } + } + } +} diff --git a/src/Commands/Viva/UpdateVivaConnectionsACE.cs b/src/Commands/Viva/UpdateVivaConnectionsACE.cs new file mode 100644 index 000000000..304f604fb --- /dev/null +++ b/src/Commands/Viva/UpdateVivaConnectionsACE.cs @@ -0,0 +1,103 @@ +using PnP.Core.Model.SharePoint; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation; +using System.Text; +using System.Text.Json; + +namespace PnP.PowerShell.Commands.Viva +{ + [Cmdlet(VerbsData.Update, "PnPVivaConnectionsDashboardACE")] + public class UpdateVivaConnectionsACE : PnPWebCmdlet + { + [Parameter(Mandatory = true)] + public Guid Identity; + + [Parameter(Mandatory = false)] + public string Title; + + [Parameter(Mandatory = false)] + public string PropertiesJSON; + + [Parameter(Mandatory = false)] + public string Description; + + [Parameter(Mandatory = false)] + public string IconProperty; + + [Parameter(Mandatory = false)] + public int Order; + + [Parameter(Mandatory = false)] + public CardSize CardSize = CardSize.Medium; + + protected override void ExecuteCmdlet() + { + if (PnPContext.Site.IsHomeSite()) + { + IVivaDashboard dashboard = PnPContext.Web.GetVivaDashboardAsync().GetAwaiter().GetResult(); + var aceToUpdate = dashboard.ACEs.FirstOrDefault(p => p.InstanceId == Identity); + + if (aceToUpdate != null) + { + bool updateRequired = false; + if (ParameterSpecified(nameof(Title))) + { + aceToUpdate.Title = Title; + updateRequired = true; + } + + if (ParameterSpecified(nameof(PropertiesJSON))) + { + aceToUpdate.Properties = JsonSerializer.Deserialize(PropertiesJSON); + updateRequired = true; + } + + if (ParameterSpecified(nameof(Description))) + { + aceToUpdate.Description = Description; + updateRequired = true; + } + + if (ParameterSpecified(nameof(IconProperty))) + { + aceToUpdate.IconProperty = IconProperty; + updateRequired = true; + } + + if (ParameterSpecified(nameof(CardSize))) + { + aceToUpdate.CardSize = CardSize; + updateRequired = true; + } + + if (updateRequired) + { + if (ParameterSpecified(nameof(Order)) && Order > -1) + { + dashboard.UpdateACE(aceToUpdate, Order); + } + else + { + dashboard.UpdateACE(aceToUpdate); + } + + dashboard.Save(); + + dashboard = PnPContext.Web.GetVivaDashboardAsync().GetAwaiter().GetResult(); + WriteObject(dashboard, true); + } + } + else + { + WriteWarning("ACE with specified instance ID not found"); + } + } + else + { + WriteWarning("Connected site is not a home site"); + } + } + } +} From 137c8fdb05709ac7c5d8332ebea7cdd06d2fcb42 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Wed, 4 May 2022 03:42:32 +0000 Subject: [PATCH 157/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index 938290dc3..d3b68894a 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -97c6fb60eec4a61bef4d243e88de6d68f7275784 \ No newline at end of file +1b399164959e04d3c5a8ef967c22475118db9163 \ No newline at end of file diff --git a/version.txt b/version.txt index 0ee4c08bf..8208f7843 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.23 \ No newline at end of file +1.10.24 \ No newline at end of file From cddc1032ee988a04c18e84c04ae94ddb5681366f Mon Sep 17 00:00:00 2001 From: Carl Joakim Damsleth Date: Wed, 4 May 2022 08:59:24 +0200 Subject: [PATCH 158/458] Clarify that `Remove-PnPTeamsTeam` also removes the associated M365 group (#1831) * Update Remove-PnPTeamsTeam.md Clarified that Remove-PnPTeamsTeam also removes the corresponding M365 group * Update Remove-PnPTeamsTeam.md --- documentation/Remove-PnPTeamsTeam.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/documentation/Remove-PnPTeamsTeam.md b/documentation/Remove-PnPTeamsTeam.md index bfef05cf0..e5a61f3b5 100644 --- a/documentation/Remove-PnPTeamsTeam.md +++ b/documentation/Remove-PnPTeamsTeam.md @@ -15,7 +15,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Remove-PnPTeamsTeam.htm * Microsoft Graph API: Group.ReadWrite.All -Removes a Microsoft Teams Team instance +Removes a Microsoft Teams Team instance and its corresponding Microsoft 365 Group ## SYNTAX @@ -25,6 +25,8 @@ Remove-PnPTeamsTeam -Identity [-Force] [] ## DESCRIPTION +Removes a Microsoft Teams Team. This also removes the associated Microsoft 365 Group, and is functionally identical to `Remove-PnPMicrosoft365Group` + ## EXAMPLES ### EXAMPLE 1 From 426ab0134f6ee78bba00800487d4b5f84cf0abca Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Thu, 5 May 2022 07:21:17 +0200 Subject: [PATCH 159/458] Removing obsolete `Get-PnPAvailableClientSideComponents` (#1833) * Removing obsolete Get-PnPAvailableClientSideComponents * Added PR Reference --- CHANGELOG.md | 1 + .../Get-PnPAvailableClientSideComponents.md | 122 ------------------ .../Pages/GetAvailablePageComponents.cs | 2 - 3 files changed, 1 insertion(+), 124 deletions(-) delete mode 100644 documentation/Get-PnPAvailableClientSideComponents.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 356626e6b..718d9f487 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Get-PnPTeamsTeam`, the cmdlet now also returns additional properties like `WebUrl, CreatedDateTime, InternalId` [#1825](https://github.com/pnp/powershell/pull/1825) ### Removed +- Removed `Get-PnPAvailableClientSideComponents`. Use `Get-PnPPageComponent -Page -ListAvailable` instead. [#1833](https://github.com/pnp/powershell/pull/1833) ### Contributors - [4ndri] diff --git a/documentation/Get-PnPAvailableClientSideComponents.md b/documentation/Get-PnPAvailableClientSideComponents.md deleted file mode 100644 index 9d7d5f100..000000000 --- a/documentation/Get-PnPAvailableClientSideComponents.md +++ /dev/null @@ -1,122 +0,0 @@ ---- -Module Name: PnP.PowerShell -schema: 2.0.0 -applicable: SharePoint Online -online version: https://pnp.github.io/powershell/cmdlets/Get-PnPAvailableClientSideComponents.html -external help file: PnP.PowerShell.dll-Help.xml -title: Get-PnPAvailableClientSideComponents ---- - -# Get-PnPAvailableClientSideComponents - -## SYNOPSIS -Gets the available client side components on a particular page - -## SYNTAX - -``` -Get-PnPAvailableClientSideComponents [-Page] - [-Component ] [-Web ] [-Connection ] - [] -``` - -## DESCRIPTION - -## EXAMPLES - -### EXAMPLE 1 -```powershell -Get-PnPAvailableClientSideComponents -Page "MyPage.aspx" -``` - -Gets the list of available client side components on the page 'MyPage.aspx' - -### EXAMPLE 2 -```powershell -Get-PnPAvailableClientSideComponents $page -``` - -Gets the list of available client side components on the page contained in the $page variable - -### EXAMPLE 3 -```powershell -Get-PnPAvailableClientSideComponents -Page "MyPage.aspx" -ComponentName "HelloWorld" -``` - -Gets the client side component 'HelloWorld' if available on the page 'MyPage.aspx' - -## PARAMETERS - -### -Component -Specifies the component instance or Id to look for. - -Only applicable to: SharePoint Online, SharePoint Server 2019 - -```yaml -Type: ClientSideComponentPipeBind -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Connection -Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. - -Only applicable to: SharePoint Online, SharePoint Server 2019 - -```yaml -Type: PnPConnection -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Page -The name of the page. - -Only applicable to: SharePoint Online, SharePoint Server 2019 - -```yaml -Type: ClientSidePagePipeBind -Parameter Sets: (All) -Aliases: - -Required: True -Position: 0 -Default value: None -Accept pipeline input: True (ByValue) -Accept wildcard characters: False -``` - -### -Web -This parameter allows you to optionally apply the cmdlet action to a subweb within the current web. In most situations this parameter is not required and you can connect to the subweb using Connect-PnPOnline instead. Specify the GUID, server relative url (i.e. /sites/team1) or web instance of the web to apply the command to. Omit this parameter to use the current web. - -Only applicable to: SharePoint Online, SharePoint Server 2019 - -```yaml -Type: WebPipeBind -Parameter Sets: (All) -Aliases: - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -## RELATED LINKS - -[SharePoint Developer Patterns and Practices](https://aka.ms/sppnp) - - diff --git a/src/Commands/Pages/GetAvailablePageComponents.cs b/src/Commands/Pages/GetAvailablePageComponents.cs index a9feb36a5..4ed52deeb 100644 --- a/src/Commands/Pages/GetAvailablePageComponents.cs +++ b/src/Commands/Pages/GetAvailablePageComponents.cs @@ -7,8 +7,6 @@ namespace PnP.PowerShell.Commands.Pages { [Cmdlet(VerbsCommon.Get, "PnPAvailablePageComponents")] - [Alias("Get-PnPAvailableClientSideComponents")] - [Obsolete("Use Get-PnPPageComponent -Page -ListAvailable")] public class GetAvailablePageComponents : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] From fbd4f3543f85a4ae61fc9624e2d4f1a6a292a30d Mon Sep 17 00:00:00 2001 From: James May Date: Thu, 5 May 2022 15:59:06 +1000 Subject: [PATCH 160/458] Add OutputType Attribute - #4 (Web and WebPart) (#1793) * WebParts cmdlets: Add missing OutputType attributes for better command completion * Web cmdlets: Add missing OutputType attributes for better command completion --- src/Commands/Model/PropertyBagValue.cs | 8 ++++ src/Commands/Web/AddIndexedProperty.cs | 1 + src/Commands/Web/GetAvailableLanguage.cs | 1 + src/Commands/Web/GetIndexedPropertyKeys.cs | 1 + src/Commands/Web/GetPropertyBag.cs | 23 +++++----- src/Commands/Web/GetRequestAccessEmails.cs | 1 + src/Commands/Web/GetSubweb.cs | 1 + src/Commands/Web/GetWeb.cs | 1 + src/Commands/Web/GetWebHeader.cs | 1 + src/Commands/Web/InvokeWebAction.cs | 1 + src/Commands/Web/NewWeb.cs | 1 + src/Commands/Web/RemovePropertyBagValue.cs | 1 + src/Commands/Web/RemoveWeb.cs | 1 + src/Commands/Web/RemovedIndexedProperty.cs | 1 + src/Commands/Web/RequestReIndexWeb.cs | 1 + src/Commands/Web/SetIndexedProperties.cs | 1 + src/Commands/Web/SetPropertyBagValue.cs | 1 + src/Commands/Web/SetRequestAccessEmails.cs | 1 + src/Commands/Web/SetWeb.cs | 1 + src/Commands/Web/SetWebHeader.cs | 1 + src/Commands/Web/SetWebPermission.cs | 1 + .../WebParts/AddWebPartToWebPartPage.cs | 42 ++++++++++--------- src/Commands/WebParts/AddWebPartToWikiPage.cs | 41 +++++++++--------- src/Commands/WebParts/GetWebPart.cs | 1 + src/Commands/WebParts/GetWebPartProperty.cs | 24 ++++++----- src/Commands/WebParts/GetWebPartXml.cs | 8 ++-- src/Commands/WebParts/RemoveWebPart.cs | 1 + src/Commands/WebParts/SetWebPartProperty.cs | 1 + 28 files changed, 104 insertions(+), 64 deletions(-) create mode 100644 src/Commands/Model/PropertyBagValue.cs diff --git a/src/Commands/Model/PropertyBagValue.cs b/src/Commands/Model/PropertyBagValue.cs new file mode 100644 index 000000000..a9908f9cf --- /dev/null +++ b/src/Commands/Model/PropertyBagValue.cs @@ -0,0 +1,8 @@ +namespace PnP.PowerShell.Commands.Model +{ + public class PropertyBagValue + { + public string Key { get; set; } + public object Value { get; set; } + } +} diff --git a/src/Commands/Web/AddIndexedProperty.cs b/src/Commands/Web/AddIndexedProperty.cs index 5bb07f988..c7d0420c3 100644 --- a/src/Commands/Web/AddIndexedProperty.cs +++ b/src/Commands/Web/AddIndexedProperty.cs @@ -6,6 +6,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.Add, "PnPIndexedProperty")] + [OutputType(typeof(void))] public class AddIndexedProperty : PnPWebCmdlet { [Parameter(Mandatory = true, Position = 0)] diff --git a/src/Commands/Web/GetAvailableLanguage.cs b/src/Commands/Web/GetAvailableLanguage.cs index a64f8479b..81fd12653 100644 --- a/src/Commands/Web/GetAvailableLanguage.cs +++ b/src/Commands/Web/GetAvailableLanguage.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.Get, "PnPAvailableLanguage")] + [OutputType(typeof(Language))] public class GetAvailableLanguage : PnPSharePointCmdlet { protected override void ExecuteCmdlet() diff --git a/src/Commands/Web/GetIndexedPropertyKeys.cs b/src/Commands/Web/GetIndexedPropertyKeys.cs index 528fb5116..870a0cce9 100644 --- a/src/Commands/Web/GetIndexedPropertyKeys.cs +++ b/src/Commands/Web/GetIndexedPropertyKeys.cs @@ -6,6 +6,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.Get, "PnPIndexedPropertyKeys")] + [OutputType(typeof(string))] public class GetIndexedProperties : PnPWebCmdlet { [Parameter(Mandatory = false, ValueFromPipeline = true)] diff --git a/src/Commands/Web/GetPropertyBag.cs b/src/Commands/Web/GetPropertyBag.cs index 310e4ee8a..3d7b17b13 100644 --- a/src/Commands/Web/GetPropertyBag.cs +++ b/src/Commands/Web/GetPropertyBag.cs @@ -1,15 +1,22 @@ using System.Linq; using System.Management.Automation; + using Microsoft.SharePoint.Client; using PnP.Framework.Utilities; +using PnP.PowerShell.Commands.Model; namespace PnP.PowerShell.Commands { - [Cmdlet(VerbsCommon.Get, "PnPPropertyBag")] + [Cmdlet(VerbsCommon.Get, "PnPPropertyBag", DefaultParameterSetName = ParameterSet_All)] + [OutputType(typeof(PropertyBagValue), ParameterSetName = new[] { ParameterSet_All })] + [OutputType(typeof(object), ParameterSetName = new[] { ParameterSet_Key })] public class GetPropertyBag : PnPWebCmdlet { - [Parameter(Mandatory = false, Position = 0, ValueFromPipeline = true)] + public const string ParameterSet_All = "All"; + public const string ParameterSet_Key = "Key"; + + [Parameter(Mandatory = false, Position = 0, ValueFromPipeline = true, ParameterSetName = ParameterSet_Key)] public string Key = string.Empty; [Parameter(Mandatory = false)] @@ -26,7 +33,7 @@ protected override void ExecuteCmdlet() else { CurrentWeb.EnsureProperty(w => w.AllProperties); - + var values = CurrentWeb.AllProperties.FieldValues.Select(x => new PropertyBagValue() { Key = x.Key, Value = x.Value }); WriteObject(values, true); } @@ -36,11 +43,11 @@ protected override void ExecuteCmdlet() // Folder Property Bag CurrentWeb.EnsureProperty(w => w.ServerRelativeUrl); - + var folderUrl = UrlUtility.Combine(CurrentWeb.ServerRelativeUrl, Folder); var folder = CurrentWeb.GetFolderByServerRelativePath(ResourcePath.FromDecodedUrl(folderUrl)); folder.EnsureProperty(f => f.Properties); - + if (!string.IsNullOrEmpty(Key)) { var value = folder.Properties.FieldValues.FirstOrDefault(x => x.Key == Key); @@ -55,10 +62,4 @@ protected override void ExecuteCmdlet() } } } - - public class PropertyBagValue - { - public string Key { get; set; } - public object Value { get; set; } - } } diff --git a/src/Commands/Web/GetRequestAccessEmails.cs b/src/Commands/Web/GetRequestAccessEmails.cs index 581d2d43e..5338f9f5c 100644 --- a/src/Commands/Web/GetRequestAccessEmails.cs +++ b/src/Commands/Web/GetRequestAccessEmails.cs @@ -6,6 +6,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.Get, "PnPRequestAccessEmails")] + [OutputType(typeof(string))] public class GetRequestAccessEmails : PnPWebCmdlet { protected override void ExecuteCmdlet() diff --git a/src/Commands/Web/GetSubweb.cs b/src/Commands/Web/GetSubweb.cs index b72710830..176d97770 100644 --- a/src/Commands/Web/GetSubweb.cs +++ b/src/Commands/Web/GetSubweb.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.Get, "PnPSubWeb")] + [OutputType(typeof(Web))] public class GetSubWebs : PnPWebRetrievalsCmdlet { [Parameter(Mandatory = false, ValueFromPipeline = true, Position = 0)] diff --git a/src/Commands/Web/GetWeb.cs b/src/Commands/Web/GetWeb.cs index f18bc6db8..7fb74f0ad 100644 --- a/src/Commands/Web/GetWeb.cs +++ b/src/Commands/Web/GetWeb.cs @@ -9,6 +9,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.Get, "PnPWeb")] + [OutputType(typeof(Web))] public class GetWeb : PnPRetrievalsCmdlet { [Parameter(Mandatory = false, ValueFromPipeline = true, Position = 0)] diff --git a/src/Commands/Web/GetWebHeader.cs b/src/Commands/Web/GetWebHeader.cs index b067da3db..d8b68878d 100644 --- a/src/Commands/Web/GetWebHeader.cs +++ b/src/Commands/Web/GetWebHeader.cs @@ -6,6 +6,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.Get, "PnPWebHeader")] + [OutputType(typeof(SharePointWebHeader))] public class GetWebHeader : PnPWebCmdlet { protected override void ExecuteCmdlet() diff --git a/src/Commands/Web/InvokeWebAction.cs b/src/Commands/Web/InvokeWebAction.cs index b6304b30b..84bbce9ff 100644 --- a/src/Commands/Web/InvokeWebAction.cs +++ b/src/Commands/Web/InvokeWebAction.cs @@ -9,6 +9,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsLifecycle.Invoke, "PnPWebAction", SupportsShouldProcess = true)] + [OutputType(typeof(InvokeWebActionResult), typeof(System.Data.DataTable))] public class InvokeWebAction : PnPWebCmdlet { [Parameter(Mandatory = false)] diff --git a/src/Commands/Web/NewWeb.cs b/src/Commands/Web/NewWeb.cs index f72608fb8..b3132e662 100644 --- a/src/Commands/Web/NewWeb.cs +++ b/src/Commands/Web/NewWeb.cs @@ -5,6 +5,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.New, "PnPWeb")] + [OutputType(typeof(Web))] public class NewWeb : PnPWebCmdlet { [Parameter(Mandatory = true)] diff --git a/src/Commands/Web/RemovePropertyBagValue.cs b/src/Commands/Web/RemovePropertyBagValue.cs index b8feb62de..2dcebb168 100644 --- a/src/Commands/Web/RemovePropertyBagValue.cs +++ b/src/Commands/Web/RemovePropertyBagValue.cs @@ -6,6 +6,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.Remove, "PnPPropertyBagValue")] + [OutputType(typeof(void))] public class RemovePropertyBagValue : PnPWebCmdlet { [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true)] diff --git a/src/Commands/Web/RemoveWeb.cs b/src/Commands/Web/RemoveWeb.cs index b6d9e812d..51847f161 100644 --- a/src/Commands/Web/RemoveWeb.cs +++ b/src/Commands/Web/RemoveWeb.cs @@ -9,6 +9,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.Remove, "PnPWeb")] + [OutputType(typeof(void))] public class RemoveWeb : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true)] diff --git a/src/Commands/Web/RemovedIndexedProperty.cs b/src/Commands/Web/RemovedIndexedProperty.cs index 4972510a3..6c9007c92 100644 --- a/src/Commands/Web/RemovedIndexedProperty.cs +++ b/src/Commands/Web/RemovedIndexedProperty.cs @@ -6,6 +6,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.Remove, "PnPIndexedProperty")] + [OutputType(typeof(void))] public class RemovedIndexedProperty : PnPWebCmdlet { [Parameter(Mandatory = true, Position = 0)] diff --git a/src/Commands/Web/RequestReIndexWeb.cs b/src/Commands/Web/RequestReIndexWeb.cs index 8df47e0d9..2e3c6dceb 100644 --- a/src/Commands/Web/RequestReIndexWeb.cs +++ b/src/Commands/Web/RequestReIndexWeb.cs @@ -5,6 +5,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsLifecycle.Request, "PnPReIndexWeb")] + [OutputType(typeof(void))] public class RequestReIndexWeb : PnPWebCmdlet { diff --git a/src/Commands/Web/SetIndexedProperties.cs b/src/Commands/Web/SetIndexedProperties.cs index b3c726977..8ed686275 100644 --- a/src/Commands/Web/SetIndexedProperties.cs +++ b/src/Commands/Web/SetIndexedProperties.cs @@ -6,6 +6,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.Set, "PnPIndexedProperties")] + [OutputType(typeof(void))] public class SetIndexedProperties : PnPWebCmdlet { [Parameter(Mandatory = true)] diff --git a/src/Commands/Web/SetPropertyBagValue.cs b/src/Commands/Web/SetPropertyBagValue.cs index 331c7e8dc..0bc431bf3 100644 --- a/src/Commands/Web/SetPropertyBagValue.cs +++ b/src/Commands/Web/SetPropertyBagValue.cs @@ -9,6 +9,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.Set, "PnPPropertyBagValue")] [Alias("Add-PnPPropertyBagValue")] + [OutputType(typeof(void))] public class SetPropertyBagValue : PnPWebCmdlet { [Parameter(Mandatory = true, ParameterSetName = "Web")] diff --git a/src/Commands/Web/SetRequestAccessEmails.cs b/src/Commands/Web/SetRequestAccessEmails.cs index 3b10833cb..cbdf7f428 100644 --- a/src/Commands/Web/SetRequestAccessEmails.cs +++ b/src/Commands/Web/SetRequestAccessEmails.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.Set, "PnPRequestAccessEmails")] + [OutputType(typeof(void))] public class SetRequestAccessEmails : PnPWebCmdlet { // Parameter must remain a string array for backwards compatibility, even though only one e-mail address can be provided diff --git a/src/Commands/Web/SetWeb.cs b/src/Commands/Web/SetWeb.cs index 0efdcec4a..a8af56398 100644 --- a/src/Commands/Web/SetWeb.cs +++ b/src/Commands/Web/SetWeb.cs @@ -5,6 +5,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.Set, "PnPWeb")] + [OutputType(typeof(void))] public class SetWeb : PnPWebCmdlet { [Parameter(Mandatory = false)] diff --git a/src/Commands/Web/SetWebHeader.cs b/src/Commands/Web/SetWebHeader.cs index 670fe8fa0..ce2c56915 100644 --- a/src/Commands/Web/SetWebHeader.cs +++ b/src/Commands/Web/SetWebHeader.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.Set, "PnPWebHeader")] + [OutputType(typeof(void))] public class SetWebHeader : PnPWebCmdlet { [Parameter(Mandatory = false)] diff --git a/src/Commands/Web/SetWebPermission.cs b/src/Commands/Web/SetWebPermission.cs index 0492706a0..f6e6b8e50 100644 --- a/src/Commands/Web/SetWebPermission.cs +++ b/src/Commands/Web/SetWebPermission.cs @@ -9,6 +9,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.Set, "PnPWebPermission", DefaultParameterSetName = "User")] + [OutputType(typeof(void))] public class SetWebPermission : PnPWebCmdlet { private const string ParameterSet_GROUP = "Set group permissions"; diff --git a/src/Commands/WebParts/AddWebPartToWebPartPage.cs b/src/Commands/WebParts/AddWebPartToWebPartPage.cs index 4441d14f7..8c40b9fab 100644 --- a/src/Commands/WebParts/AddWebPartToWebPartPage.cs +++ b/src/Commands/WebParts/AddWebPartToWebPartPage.cs @@ -1,6 +1,9 @@ using System.IO; using System.Management.Automation; + using Microsoft.SharePoint.Client; +using Microsoft.SharePoint.Client.WebParts; + using PnP.Framework.Entities; using PnP.Framework.Utilities; @@ -9,16 +12,20 @@ namespace PnP.PowerShell.Commands.WebParts { [Cmdlet(VerbsCommon.Add, "PnPWebPartToWebPartPage")] + [OutputType(typeof(WebPartDefinition))] public class AddWebPartToWebPartPage : PnPWebCmdlet { + private const string ParameterSet_File = "File"; + private const string ParameterSet_Xml = "Xml"; + [Parameter(Mandatory = true)] [Alias("PageUrl")] public string ServerRelativePageUrl = string.Empty; - [Parameter(Mandatory = true, ParameterSetName = "XML")] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_Xml)] public string Xml = string.Empty; - [Parameter(Mandatory = true, ParameterSetName = "FILE")] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_File)] public string Path = string.Empty; [Parameter(Mandatory = true)] @@ -36,34 +43,29 @@ protected override void ExecuteCmdlet() ServerRelativePageUrl = UrlUtility.Combine(serverRelativeWebUrl, ServerRelativePageUrl); } - - WebPartEntity wp = null; - switch (ParameterSetName) { - case "FILE": + case ParameterSet_File: if (!System.IO.Path.IsPathRooted(Path)) { Path = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Path); } - if (File.Exists(Path)) - { - var fileStream = new StreamReader(Path); - var webPartString = fileStream.ReadToEnd(); - fileStream.Close(); - - wp = new WebPartEntity {WebPartZone = ZoneId, WebPartIndex = ZoneIndex, WebPartXml = webPartString}; - } + var webPartString = File.ReadAllText(Path); + AddWebPartToPage(webPartString); break; - case "XML": - wp = new WebPartEntity {WebPartZone = ZoneId, WebPartIndex = ZoneIndex, WebPartXml = Xml}; + + case ParameterSet_Xml: + AddWebPartToPage(Xml); break; } - if (wp != null) - { - CurrentWeb.AddWebPartToWebPartPage(ServerRelativePageUrl, wp); - } + } + + private void AddWebPartToPage(string webPartXml) + { + var wp = new WebPartEntity { WebPartZone = ZoneId, WebPartIndex = ZoneIndex, WebPartXml = webPartXml }; + var webPartDef = CurrentWeb.AddWebPartToWebPartPage(ServerRelativePageUrl, wp); + WriteObject(webPartDef); } } } diff --git a/src/Commands/WebParts/AddWebPartToWikiPage.cs b/src/Commands/WebParts/AddWebPartToWikiPage.cs index 40ba1944d..4e5418e4d 100644 --- a/src/Commands/WebParts/AddWebPartToWikiPage.cs +++ b/src/Commands/WebParts/AddWebPartToWikiPage.cs @@ -1,6 +1,9 @@ using System.IO; using System.Management.Automation; + using Microsoft.SharePoint.Client; +using Microsoft.SharePoint.Client.WebParts; + using PnP.Framework.Entities; using PnP.Framework.Utilities; @@ -9,16 +12,20 @@ namespace PnP.PowerShell.Commands.WebParts { [Cmdlet(VerbsCommon.Add, "PnPWebPartToWikiPage")] + [OutputType(typeof(WebPartDefinition))] public class AddWebPartToWikiPage : PnPWebCmdlet { + private const string ParameterSet_File = "File"; + private const string ParameterSet_Xml = "Xml"; + [Parameter(Mandatory = true)] [Alias("PageUrl")] public string ServerRelativePageUrl = string.Empty; - [Parameter(Mandatory = true, ParameterSetName = "XML")] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_Xml)] public string Xml = string.Empty; - [Parameter(Mandatory = true, ParameterSetName = "FILE")] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_File)] public string Path = string.Empty; [Parameter(Mandatory = true)] @@ -39,33 +46,29 @@ protected override void ExecuteCmdlet() ServerRelativePageUrl = UrlUtility.Combine(serverRelativeWebUrl, ServerRelativePageUrl); } - - WebPartEntity wp = null; - switch (ParameterSetName) { - case "FILE": + case ParameterSet_File: if (!System.IO.Path.IsPathRooted(Path)) { Path = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Path); } - if (File.Exists(Path)) - { - var fileStream = new StreamReader(Path); - var webPartString = fileStream.ReadToEnd(); - fileStream.Close(); - wp = new WebPartEntity { WebPartXml = webPartString }; - } + var webPartXml = File.ReadAllText(Path); + AddWebPartToWiki(webPartXml); break; - case "XML": - wp = new WebPartEntity { WebPartXml = Xml }; + + case ParameterSet_Xml: + AddWebPartToWiki(Xml); break; } - if (wp != null) - { - CurrentWeb.AddWebPartToWikiPage(ServerRelativePageUrl, wp, Row, Column, AddSpace); - } + } + + private void AddWebPartToWiki(string webPartXml) + { + var wp = new WebPartEntity { WebPartXml = webPartXml }; + var webPartDefinition = CurrentWeb.AddWebPartToWikiPage(ServerRelativePageUrl, wp, Row, Column, AddSpace); + WriteObject(webPartDefinition); } } } diff --git a/src/Commands/WebParts/GetWebPart.cs b/src/Commands/WebParts/GetWebPart.cs index b9f68a102..9b6d858cc 100644 --- a/src/Commands/WebParts/GetWebPart.cs +++ b/src/Commands/WebParts/GetWebPart.cs @@ -11,6 +11,7 @@ namespace PnP.PowerShell.Commands.WebParts { [Cmdlet(VerbsCommon.Get, "PnPWebPart")] + [OutputType(typeof(WebPartDefinition))] public class GetWebPart : PnPWebCmdlet { [Parameter(Mandatory = true)] diff --git a/src/Commands/WebParts/GetWebPartProperty.cs b/src/Commands/WebParts/GetWebPartProperty.cs index 862a9822d..dc39b447c 100644 --- a/src/Commands/WebParts/GetWebPartProperty.cs +++ b/src/Commands/WebParts/GetWebPartProperty.cs @@ -1,14 +1,22 @@ using System; using System.Linq; using System.Management.Automation; + using Microsoft.SharePoint.Client; + using PnP.Framework.Utilities; +using PnP.PowerShell.Commands.Model; namespace PnP.PowerShell.Commands.WebParts { [Cmdlet(VerbsCommon.Get, "PnPWebPartProperty")] + [OutputType(typeof(PropertyBagValue), ParameterSetName = new[] { ParameterSet_All })] + [OutputType(typeof(object), ParameterSetName = new[] { ParameterSet_Key })] public class GetWebPartProperty : PnPWebCmdlet { + private const string ParameterSet_All = "All"; + private const string ParameterSet_Key = "Key"; + [Parameter(Mandatory = true)] [Alias("PageUrl")] public string ServerRelativePageUrl = string.Empty; @@ -16,7 +24,7 @@ public class GetWebPartProperty : PnPWebCmdlet [Parameter(Mandatory = true)] public Guid Identity; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_Key)] public string Key; protected override void ExecuteCmdlet() @@ -28,24 +36,20 @@ protected override void ExecuteCmdlet() ServerRelativePageUrl = UrlUtility.Combine(serverRelativeWebUrl, ServerRelativePageUrl); } - var properties = CurrentWeb.GetWebPartProperties(Identity, ServerRelativePageUrl); - var values = properties.FieldValues.Select(x => new PropertyBagValue() { Key = x.Key, Value = x.Value }); + if (!string.IsNullOrEmpty(Key)) { - var value = values.FirstOrDefault(v => v.Key == Key); - if (value != null) + if (properties.FieldValues.TryGetValue(Key, out var value)) { - WriteObject(value.Value); + WriteObject(value); } } else { - WriteObject(values, true); + var values = properties.FieldValues.Select(x => new PropertyBagValue() { Key = x.Key, Value = x.Value }); + WriteObject(values, enumerateCollection: true); } } - - - } } diff --git a/src/Commands/WebParts/GetWebPartXml.cs b/src/Commands/WebParts/GetWebPartXml.cs index 52c9ff1f9..c6c54b8a7 100644 --- a/src/Commands/WebParts/GetWebPartXml.cs +++ b/src/Commands/WebParts/GetWebPartXml.cs @@ -4,7 +4,9 @@ using System.Management.Automation; using System.Net; using System.Text; + using Microsoft.SharePoint.Client; + using PnP.Framework.Utilities; using PnP.PowerShell.Commands.Base; @@ -13,6 +15,7 @@ namespace PnP.PowerShell.Commands.WebParts { [Cmdlet(VerbsCommon.Get, "PnPWebPartXml")] + [OutputType(typeof(string))] public class GetWebPartXml : PnPWebCmdlet { [Parameter(Mandatory = true)] @@ -50,10 +53,7 @@ protected override void ExecuteCmdlet() } - WriteObject(CurrentWeb.GetWebPartXml(id,ServerRelativePageUrl)); - - + WriteObject(CurrentWeb.GetWebPartXml(id, ServerRelativePageUrl)); } - } } diff --git a/src/Commands/WebParts/RemoveWebPart.cs b/src/Commands/WebParts/RemoveWebPart.cs index 657a650a2..d4fe082da 100644 --- a/src/Commands/WebParts/RemoveWebPart.cs +++ b/src/Commands/WebParts/RemoveWebPart.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands.WebParts { [Cmdlet(VerbsCommon.Remove, "PnPWebPart")] + [OutputType(typeof(void))] public class RemoveWebPart : PnPWebCmdlet { [Parameter(Mandatory = true, ParameterSetName = "ID")] diff --git a/src/Commands/WebParts/SetWebPartProperty.cs b/src/Commands/WebParts/SetWebPartProperty.cs index 23ca0d4c2..b878126f3 100644 --- a/src/Commands/WebParts/SetWebPartProperty.cs +++ b/src/Commands/WebParts/SetWebPartProperty.cs @@ -6,6 +6,7 @@ namespace PnP.PowerShell.Commands.WebParts { [Cmdlet(VerbsCommon.Set, "PnPWebPartProperty")] + [OutputType(typeof(void))] public class SetWebPartProperty : PnPWebCmdlet { [Parameter(Mandatory = true)] From e5f1bd817ebb698cb0f01e8e183e1634f87898dd Mon Sep 17 00:00:00 2001 From: James May Date: Fri, 6 May 2022 03:29:37 +0000 Subject: [PATCH 161/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index d3b68894a..cff0236ba 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -1b399164959e04d3c5a8ef967c22475118db9163 \ No newline at end of file +f20b02860af2dea43d156048f43fef5421322e02 \ No newline at end of file diff --git a/version.txt b/version.txt index 8208f7843..bd5f5d009 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.24 \ No newline at end of file +1.10.25 \ No newline at end of file From cc0d4e8c1c984a7521efbfe6ed6e8e2fc404e026 Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Mon, 9 May 2022 09:18:10 +0200 Subject: [PATCH 162/458] Doc fix (#1839) --- documentation/Get-PnPFlowRun.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/Get-PnPFlowRun.md b/documentation/Get-PnPFlowRun.md index a1ee67051..2901fbe23 100644 --- a/documentation/Get-PnPFlowRun.md +++ b/documentation/Get-PnPFlowRun.md @@ -59,14 +59,14 @@ Accept wildcard characters: False ``` ### -Flow -The Name/Id of the flow to retrieve. +The Name/Id of the flow to retrieve the available runs for. ```yaml Type: PowerAutomateFlowPipeBind Parameter Sets: (All) Aliases: -Required: true +Required: True Position: Named Default value: None Accept pipeline input: False From c4951201ae902ce9c4f89ed33fd933f83a12f50a Mon Sep 17 00:00:00 2001 From: James May Date: Tue, 10 May 2022 20:39:44 +1000 Subject: [PATCH 163/458] Add OutputType Attribute - #5 (Base) (#1793) Add missing OutputType attributes for better command completion --- src/Commands/Base/AddStoredCredential.cs | 1 + src/Commands/Base/DisconnectOnline.cs | 1 + src/Commands/Base/GetAccessToken.cs | 15 +++++-- src/Commands/Base/GetAppAuthAccessToken.cs | 1 + src/Commands/Base/GetAuthenticationRealm.cs | 1 + src/Commands/Base/GetChangeLog.cs | 1 + src/Commands/Base/GetConnection.cs | 1 + src/Commands/Base/GetContext.cs | 1 + src/Commands/Base/GetDiagnostics.cs | 45 +++++++++---------- src/Commands/Base/GetException.cs | 1 + .../Base/GetPowerShellTelemetryEnabled.cs | 1 + src/Commands/Base/GetProperty.cs | 1 + src/Commands/Base/GetStoredCredential.cs | 1 + src/Commands/Base/InvokeBatch.cs | 19 +++++--- src/Commands/Base/InvokeQuery.cs | 1 + src/Commands/Base/InvokeSPRestMethod.cs | 27 +++++++---- src/Commands/Base/NewBatch.cs | 1 + src/Commands/Base/PnPSharePointCmdlet.cs | 2 +- src/Commands/Base/RemoveStoredCredential.cs | 1 + src/Commands/Model/Diagnostics.cs | 22 +++++++++ 20 files changed, 103 insertions(+), 41 deletions(-) create mode 100644 src/Commands/Model/Diagnostics.cs diff --git a/src/Commands/Base/AddStoredCredential.cs b/src/Commands/Base/AddStoredCredential.cs index d8cd246bc..ce2eb24d7 100644 --- a/src/Commands/Base/AddStoredCredential.cs +++ b/src/Commands/Base/AddStoredCredential.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.Base { [Cmdlet(VerbsCommon.Add, "PnPStoredCredential")] + [OutputType(typeof(void))] public class AddStoredCredential : PSCmdlet { [Parameter(Mandatory = true)] diff --git a/src/Commands/Base/DisconnectOnline.cs b/src/Commands/Base/DisconnectOnline.cs index 59b77c265..5d8c837a5 100644 --- a/src/Commands/Base/DisconnectOnline.cs +++ b/src/Commands/Base/DisconnectOnline.cs @@ -9,6 +9,7 @@ namespace PnP.PowerShell.Commands.Base { [Cmdlet(VerbsCommunications.Disconnect, "PnPOnline")] + [OutputType(typeof(void))] public class DisconnectOnline : PSCmdlet { [Parameter(Mandatory = false)] diff --git a/src/Commands/Base/GetAccessToken.cs b/src/Commands/Base/GetAccessToken.cs index e98b27c2f..006280de8 100644 --- a/src/Commands/Base/GetAccessToken.cs +++ b/src/Commands/Base/GetAccessToken.cs @@ -1,4 +1,5 @@ using System.Management.Automation; + using PnP.PowerShell.Commands.Attributes; using PnP.PowerShell.Commands.Enums; @@ -6,21 +7,29 @@ namespace PnP.PowerShell.Commands.Base { [Cmdlet(VerbsCommon.Get, "PnPAccessToken", DefaultParameterSetName = DefaultParam)] [RequiredMinimalApiPermissions("https://graph.microsoft.com/.default")] + [OutputType(typeof(System.IdentityModel.Tokens.Jwt.JwtSecurityToken), ParameterSetName = new[] { DefaultParam_Decoded, ResourceTypeParam_Decoded, ResourceUrlParam_Decoded })] + [OutputType(typeof(string), ParameterSetName = new[] { DefaultParam, ResourceTypeParam, ResourceUrlParam })] public class GetPnPAccessToken : PnPGraphCmdlet { private const string DefaultParam = "Default"; private const string ResourceTypeParam = "Resource Type Name"; private const string ResourceUrlParam = "Resource Url"; + private const string DefaultParam_Decoded = "Default (decoded)"; + private const string ResourceTypeParam_Decoded = "Resource Type Name (decoded)"; + private const string ResourceUrlParam_Decoded = "Resource Url (decoded)"; + [Parameter(Mandatory = false, ParameterSetName = ResourceTypeParam)] + [Parameter(Mandatory = false, ParameterSetName = ResourceTypeParam_Decoded)] public ResourceTypeName ResourceTypeName = ResourceTypeName.Graph; [Parameter(Mandatory = false, ParameterSetName = ResourceUrlParam)] + [Parameter(Mandatory = false, ParameterSetName = ResourceUrlParam_Decoded)] public string ResourceUrl; - [Parameter(ParameterSetName = DefaultParam)] - [Parameter(ParameterSetName = ResourceTypeParam)] - [Parameter(ParameterSetName = ResourceUrlParam)] + [Parameter(ParameterSetName = DefaultParam_Decoded)] + [Parameter(ParameterSetName = ResourceTypeParam_Decoded)] + [Parameter(ParameterSetName = ResourceUrlParam_Decoded)] [Parameter(Mandatory = false)] public SwitchParameter Decoded; protected override void ExecuteCmdlet() diff --git a/src/Commands/Base/GetAppAuthAccessToken.cs b/src/Commands/Base/GetAppAuthAccessToken.cs index c142f4883..949eb641e 100644 --- a/src/Commands/Base/GetAppAuthAccessToken.cs +++ b/src/Commands/Base/GetAppAuthAccessToken.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.Base { [Cmdlet(VerbsCommon.Get, "PnPAppAuthAccessToken")] + [OutputType(typeof(string))] public class GetPnPAppAuthAccessToken : PnPSharePointCmdlet { protected override void ExecuteCmdlet() diff --git a/src/Commands/Base/GetAuthenticationRealm.cs b/src/Commands/Base/GetAuthenticationRealm.cs index ed22ab901..73b93549e 100644 --- a/src/Commands/Base/GetAuthenticationRealm.cs +++ b/src/Commands/Base/GetAuthenticationRealm.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.Get, "PnPAuthenticationRealm")] + [OutputType(typeof(string))] public class GetAuthenticationRealm : PnPSharePointCmdlet { diff --git a/src/Commands/Base/GetChangeLog.cs b/src/Commands/Base/GetChangeLog.cs index c1dee5dec..fb67a6893 100644 --- a/src/Commands/Base/GetChangeLog.cs +++ b/src/Commands/Base/GetChangeLog.cs @@ -5,6 +5,7 @@ namespace PnP.PowerShell.Commands { [Cmdlet(VerbsCommon.Get, "PnPChangeLog")] + [OutputType(typeof(string))] public class GetChangeLog : PSCmdlet { [Parameter(Mandatory = false)] diff --git a/src/Commands/Base/GetConnection.cs b/src/Commands/Base/GetConnection.cs index 19b6b873a..97beff965 100644 --- a/src/Commands/Base/GetConnection.cs +++ b/src/Commands/Base/GetConnection.cs @@ -6,6 +6,7 @@ namespace PnP.PowerShell.Commands.Base { [Cmdlet(VerbsCommon.Get, "PnPConnection")] + [OutputType(typeof(PnPConnection))] public class GetPnPConnection : PSCmdlet { diff --git a/src/Commands/Base/GetContext.cs b/src/Commands/Base/GetContext.cs index 39710e556..0c5b08f84 100644 --- a/src/Commands/Base/GetContext.cs +++ b/src/Commands/Base/GetContext.cs @@ -6,6 +6,7 @@ namespace PnP.PowerShell.Commands.Base { [Cmdlet(VerbsCommon.Get, "PnPContext")] + [OutputType(typeof(Microsoft.SharePoint.Client.ClientContext))] public class GetSPOContext : PSCmdlet { diff --git a/src/Commands/Base/GetDiagnostics.cs b/src/Commands/Base/GetDiagnostics.cs index 8771bd272..9a806fbe9 100644 --- a/src/Commands/Base/GetDiagnostics.cs +++ b/src/Commands/Base/GetDiagnostics.cs @@ -1,6 +1,7 @@ using PnP.PowerShell.Commands.Enums; using PnP.PowerShell.Commands.Model; using PnP.PowerShell.Commands.Utilities; + using System; using System.Collections; using System.Collections.Generic; @@ -13,11 +14,12 @@ namespace PnP.PowerShell.Commands.Base { [Cmdlet(VerbsCommon.Get, "PnPDiagnostics")] + [OutputType(typeof(Diagnostics))] public class GetDiagnostics : BasePSCmdlet { protected override void ExecuteCmdlet() { - var result = new PSObject(); + var result = new Diagnostics(); FillVersion(result); FillModuleInfo(result); @@ -30,14 +32,14 @@ protected override void ExecuteCmdlet() WriteObject(result, true); } - void FillVersion(PSObject result) + void FillVersion(Diagnostics result) { var assembly = Assembly.GetExecutingAssembly(); var version = ((AssemblyFileVersionAttribute)assembly.GetCustomAttribute(typeof(AssemblyFileVersionAttribute))).Version; - AddProperty(result, "Version", version); + result.Version = version; } - void FillModuleInfo(PSObject result) + void FillModuleInfo(Diagnostics result) { var location = Assembly.GetExecutingAssembly().Location; var escapedLocation = Uri.UnescapeDataString(location); @@ -45,34 +47,34 @@ void FillModuleInfo(PSObject result) var modulePath = System.IO.Path.GetDirectoryName(escapedLocation); DirectoryInfo dirInfo = new DirectoryInfo(modulePath); - AddProperty(result, "ModulePath", modulePath); + result.ModulePath = modulePath; } - void FillOperatingSystem(PSObject result) + void FillOperatingSystem(Diagnostics result) { - AddProperty(result, "OperatingSystem", Environment.OSVersion.VersionString); + result.OperatingSystem = Environment.OSVersion.VersionString; } - void FillConnectionMethod(PSObject result) + void FillConnectionMethod(Diagnostics result) { - AddProperty(result, "ConnectionMethod", PnPConnection.Current?.ConnectionMethod); + result.ConnectionMethod = PnPConnection.Current?.ConnectionMethod; } - void FillCurrentSite(PSObject result) + void FillCurrentSite(Diagnostics result) { - AddProperty(result, "CurrentSite", PnPConnection.Current?.Url); + result.CurrentSite = PnPConnection.Current?.Url; } - void FillNewerVersionAvailable(PSObject result) + void FillNewerVersionAvailable(Diagnostics result) { var versionAvailable = VersionChecker.GetAvailableVersion(); if (versionAvailable != null && VersionChecker.IsNewer(versionAvailable)) { - AddProperty(result, "NewerVersionAvailable", versionAvailable.ToString()); + result.NewerVersionAvailable = versionAvailable.ToString(); } } - void FillLastException(PSObject result) + void FillLastException(Diagnostics result) { // Most of this code has been copied from GetException cmdlet PnPException pnpException = null; @@ -94,16 +96,11 @@ void FillLastException(PSObject result) } - AddProperty(result, "LastCorrelationId", pnpException?.CorrelationId); - AddProperty(result, "LastExceptionTimeStampUtc", pnpException?.TimeStampUtc); - AddProperty(result, "LastExceptionMessage", pnpException?.Message); - AddProperty(result, "LastExceptionStacktrace", pnpException?.Stacktrace); - AddProperty(result, "LastExceptionScriptLineNumber", pnpException?.ScriptLineNumber); - } - - void AddProperty(PSObject pso, string name, object value) - { - pso.Properties.Add(new PSVariableProperty(new PSVariable(name, value))); + result.LastCorrelationId = pnpException?.CorrelationId; + result.LastExceptionTimeStampUtc = pnpException?.TimeStampUtc; + result.LastExceptionMessage = pnpException?.Message; + result.LastExceptionStacktrace = pnpException?.Stacktrace; + result.LastExceptionScriptLineNumber = pnpException?.ScriptLineNumber; } } } \ No newline at end of file diff --git a/src/Commands/Base/GetException.cs b/src/Commands/Base/GetException.cs index 81856834d..fe3cf7fc8 100644 --- a/src/Commands/Base/GetException.cs +++ b/src/Commands/Base/GetException.cs @@ -9,6 +9,7 @@ namespace PnP.PowerShell.Commands.Base { [Cmdlet(VerbsCommon.Get, "PnPException")] + [OutputType(typeof(PnPException))] public class GetException : PSCmdlet { [Parameter(Mandatory = false)] diff --git a/src/Commands/Base/GetPowerShellTelemetryEnabled.cs b/src/Commands/Base/GetPowerShellTelemetryEnabled.cs index d2dc0c4d5..9e2845dde 100644 --- a/src/Commands/Base/GetPowerShellTelemetryEnabled.cs +++ b/src/Commands/Base/GetPowerShellTelemetryEnabled.cs @@ -3,6 +3,7 @@ namespace PnP.PowerShell.Commands.Base { [Cmdlet(VerbsCommon.Get, "PnPPowerShellTelemetryEnabled")] + [OutputType(typeof(bool))] public class GetPowerShellTelemetryEnabled : PnPSharePointCmdlet { protected override void ProcessRecord() diff --git a/src/Commands/Base/GetProperty.cs b/src/Commands/Base/GetProperty.cs index 4dfe155b2..c5af32a20 100644 --- a/src/Commands/Base/GetProperty.cs +++ b/src/Commands/Base/GetProperty.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.Base { [Cmdlet(VerbsCommon.Get, "PnPProperty")] + [OutputType(typeof(object))] public class EnsureProperty : PnPSharePointCmdlet { [Parameter(Mandatory = true, Position = 0)] diff --git a/src/Commands/Base/GetStoredCredential.cs b/src/Commands/Base/GetStoredCredential.cs index 1dd607e44..62d8db88d 100644 --- a/src/Commands/Base/GetStoredCredential.cs +++ b/src/Commands/Base/GetStoredCredential.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands.Base { [Cmdlet(VerbsCommon.Get, "PnPStoredCredential")] + [OutputType(typeof(PSCredential))] public class GetStoredCredential : PSCmdlet { [Parameter(Mandatory = true)] diff --git a/src/Commands/Base/InvokeBatch.cs b/src/Commands/Base/InvokeBatch.cs index c0ff41ddf..22864bd5e 100644 --- a/src/Commands/Base/InvokeBatch.cs +++ b/src/Commands/Base/InvokeBatch.cs @@ -1,24 +1,33 @@ using System.Collections.Generic; using System.Linq; using System.Management.Automation; + using PnP.Core.Services; using PnP.PowerShell.Commands.Model; namespace PnP.PowerShell.Commands.Base { - [Cmdlet(VerbsLifecycle.Invoke, "PnPBatch")] + [Cmdlet(VerbsLifecycle.Invoke, "PnPBatch", DefaultParameterSetName = PARAMETERSET_Default)] + [OutputType(typeof(BatchResult), ParameterSetName = new[] { PARAMETERSET_Default })] + [OutputType(typeof(BatchResult), typeof(Model.BatchRequest), ParameterSetName = new[] { PARAMETERSET_Detailed })] public class InvokeBatch : PnPWebCmdlet { - [Parameter(Mandatory = true, Position = 0)] + public const string PARAMETERSET_Default = "Default"; + public const string PARAMETERSET_Detailed = "Detailed"; + + [Parameter(Mandatory = true, Position = 0, ParameterSetName = PARAMETERSET_Default)] + [Parameter(Mandatory = true, Position = 0, ParameterSetName = PARAMETERSET_Detailed)] public PnPBatch Batch; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = false, ParameterSetName = PARAMETERSET_Default)] + [Parameter(Mandatory = false, ParameterSetName = PARAMETERSET_Detailed)] public SwitchParameter Force; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = false, ParameterSetName = PARAMETERSET_Detailed)] public SwitchParameter Details; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = false, ParameterSetName = PARAMETERSET_Default)] + [Parameter(Mandatory = false, ParameterSetName = PARAMETERSET_Detailed)] public SwitchParameter StopOnException; protected override void ExecuteCmdlet() diff --git a/src/Commands/Base/InvokeQuery.cs b/src/Commands/Base/InvokeQuery.cs index 992441c9d..7d5751595 100644 --- a/src/Commands/Base/InvokeQuery.cs +++ b/src/Commands/Base/InvokeQuery.cs @@ -5,6 +5,7 @@ namespace PnP.PowerShell.Commands.Base { [Cmdlet(VerbsLifecycle.Invoke, "PnPQuery")] + [OutputType(typeof(void))] public class InvokeQuery : PnPSharePointCmdlet { [Parameter(Mandatory = false)] diff --git a/src/Commands/Base/InvokeSPRestMethod.cs b/src/Commands/Base/InvokeSPRestMethod.cs index ce066cb1a..bfae4b980 100644 --- a/src/Commands/Base/InvokeSPRestMethod.cs +++ b/src/Commands/Base/InvokeSPRestMethod.cs @@ -1,4 +1,5 @@ using Microsoft.SharePoint.Client; + using PnP.Framework.Http; using PnP.Framework.Utilities; @@ -20,25 +21,35 @@ namespace PnP.PowerShell.Commands.Admin { - [Cmdlet(VerbsLifecycle.Invoke, "PnPSPRestMethod")] + [Cmdlet(VerbsLifecycle.Invoke, "PnPSPRestMethod", DefaultParameterSetName = PARAMETERSET_Parsed)] + [OutputType(typeof(PSObject), ParameterSetName = new[] { PARAMETERSET_Parsed })] + [OutputType(typeof(string), ParameterSetName = new[] { PARAMETERSET_Raw })] public class InvokeSPRestMethod : PnPSharePointCmdlet { - [Parameter(Mandatory = false, Position = 0)] + public const string PARAMETERSET_Parsed = "Parsed"; + public const string PARAMETERSET_Raw = "Raw"; + + [Parameter(Mandatory = false, Position = 0, ParameterSetName = PARAMETERSET_Parsed)] + [Parameter(Mandatory = false, Position = 0, ParameterSetName = PARAMETERSET_Raw)] public HttpRequestMethod Method = HttpRequestMethod.Get; - [Parameter(Mandatory = true, Position = 0)] + [Parameter(Mandatory = true, Position = 0, ParameterSetName = PARAMETERSET_Parsed)] + [Parameter(Mandatory = true, Position = 0, ParameterSetName = PARAMETERSET_Raw)] public string Url; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = false, ParameterSetName = PARAMETERSET_Parsed)] + [Parameter(Mandatory = false, ParameterSetName = PARAMETERSET_Raw)] public object Content; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = false, ParameterSetName = PARAMETERSET_Parsed)] + [Parameter(Mandatory = false, ParameterSetName = PARAMETERSET_Raw)] public string ContentType = "application/json"; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = false, ParameterSetName = PARAMETERSET_Parsed)] + [Parameter(Mandatory = false, ParameterSetName = PARAMETERSET_Raw)] public string Accept = "application/json;odata=nometadata"; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = false, ParameterSetName = PARAMETERSET_Raw)] public SwitchParameter Raw; protected override void ExecuteCmdlet() @@ -57,7 +68,7 @@ protected override void ExecuteCmdlet() using (HttpRequestMessage request = new HttpRequestMessage(method, requestUrl)) { - if(string.IsNullOrEmpty(Accept)) + if (string.IsNullOrEmpty(Accept)) { Accept = "application/json;odata=nometadata"; } diff --git a/src/Commands/Base/NewBatch.cs b/src/Commands/Base/NewBatch.cs index e97b28aa2..6893b2ee7 100644 --- a/src/Commands/Base/NewBatch.cs +++ b/src/Commands/Base/NewBatch.cs @@ -4,6 +4,7 @@ namespace PnP.PowerShell.Commands.Base { [Cmdlet(VerbsCommon.New, "PnPBatch")] + [OutputType(typeof(PnPBatch))] public class NewBatch : PnPWebCmdlet { [Parameter(Mandatory = false)] diff --git a/src/Commands/Base/PnPSharePointCmdlet.cs b/src/Commands/Base/PnPSharePointCmdlet.cs index 5ba088d67..49e02fc53 100644 --- a/src/Commands/Base/PnPSharePointCmdlet.cs +++ b/src/Commands/Base/PnPSharePointCmdlet.cs @@ -15,7 +15,7 @@ namespace PnP.PowerShell.Commands /// /// Base class for all the PnP SharePoint related cmdlets /// - public class PnPSharePointCmdlet : PnPConnectedCmdlet + public abstract class PnPSharePointCmdlet : PnPConnectedCmdlet { /// /// Reference the the SharePoint context on the current connection. If NULL it means there is no SharePoint context available on the current connection. diff --git a/src/Commands/Base/RemoveStoredCredential.cs b/src/Commands/Base/RemoveStoredCredential.cs index bd886b674..efb67e507 100644 --- a/src/Commands/Base/RemoveStoredCredential.cs +++ b/src/Commands/Base/RemoveStoredCredential.cs @@ -4,6 +4,7 @@ namespace PnP.PowerShell.Commands.Base { [Cmdlet(VerbsCommon.Remove, "PnPStoredCredential")] + [OutputType(typeof(void))] public class RemoveStoredCredential : PSCmdlet { [Parameter(Mandatory = true)] diff --git a/src/Commands/Model/Diagnostics.cs b/src/Commands/Model/Diagnostics.cs new file mode 100644 index 000000000..90fba8e48 --- /dev/null +++ b/src/Commands/Model/Diagnostics.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace PnP.PowerShell.Commands.Model +{ + public sealed class Diagnostics + { + public string Version { get; set; } + public string ModulePath { get; set; } + public string OperatingSystem { get; set; } + public ConnectionMethod? ConnectionMethod { get; set; } + public string CurrentSite { get; set; } + public string NewerVersionAvailable { get; set; } + public string/*?*/ LastCorrelationId { get; set; } + public DateTime? LastExceptionTimeStampUtc { get; set; } + public string/*?*/ LastExceptionMessage { get; set; } + public string/*?*/ LastExceptionStacktrace { get; set; } + public int? LastExceptionScriptLineNumber { get; set; } + + } +} From 8bf606d212fd239ee4be33f6f672a9a10a59b466 Mon Sep 17 00:00:00 2001 From: James May Date: Tue, 10 May 2022 20:40:34 +1000 Subject: [PATCH 164/458] AzureCertificate: unify output production --- src/Commands/Base/GetAzureCertificate.cs | 69 +++++++++++++++++------- src/Commands/Base/NewAzureCertificate.cs | 36 ++----------- src/Commands/Model/AzureCertificate.cs | 34 ++++++++++++ 3 files changed, 87 insertions(+), 52 deletions(-) create mode 100644 src/Commands/Model/AzureCertificate.cs diff --git a/src/Commands/Base/GetAzureCertificate.cs b/src/Commands/Base/GetAzureCertificate.cs index 73167dc79..8d98ddee1 100644 --- a/src/Commands/Base/GetAzureCertificate.cs +++ b/src/Commands/Base/GetAzureCertificate.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.Base { [Cmdlet(VerbsCommon.Get, "PnPAzureCertificate", DefaultParameterSetName = "SELF")] + [OutputType(typeof(Model.AzureCertificate))] public class GetPnPAdalCertificate : PSCmdlet { [Parameter(Mandatory = true)] @@ -26,13 +27,23 @@ protected override void ProcessRecord() if (System.IO.File.Exists(Path)) { var certificate = new X509Certificate2(Path, Password, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet); - var rawCert = certificate.GetRawCertData(); - var base64Cert = Convert.ToBase64String(rawCert); - var rawCertHash = certificate.GetCertHash(); - var base64CertHash = Convert.ToBase64String(rawCertHash); - var keyId = Guid.NewGuid(); + WriteAzureCertificateOutput(this, certificate, Password); + } + else + { + throw new PSArgumentException("Certificate file does not exist"); + } + } + + static string GetManifestEntry(X509Certificate2 certificate) + { + var rawCert = certificate.GetRawCertData(); + var base64Cert = Convert.ToBase64String(rawCert); + var rawCertHash = certificate.GetCertHash(); + var base64CertHash = Convert.ToBase64String(rawCertHash); + var keyId = Guid.NewGuid(); - var template = @" + var template = @" {{ ""customKeyIdentifier"": ""{0}"", ""keyId"": ""{1}"", @@ -41,24 +52,42 @@ protected override void ProcessRecord() ""value"": ""{2}"" }} "; - var manifestEntry = string.Format(template, base64CertHash, keyId, base64Cert); - - var record = new PSObject(); - record.Properties.Add(new PSVariableProperty(new PSVariable("Subject", certificate.Subject))); - record.Properties.Add(new PSVariableProperty(new PSVariable("ValidFrom", certificate.NotBefore))); - record.Properties.Add(new PSVariableProperty(new PSVariable("ValidTo", certificate.NotAfter))); - record.Properties.Add(new PSVariableProperty(new PSVariable("Thumbprint", certificate.Thumbprint))); - - record.Properties.Add(new PSVariableProperty(new PSVariable("KeyCredentials", manifestEntry))); - record.Properties.Add(new PSVariableProperty(new PSVariable("Certificate", CertificateHelper.CertificateToBase64(certificate)))); - record.Properties.Add(new PSVariableProperty(new PSVariable("PrivateKey", CertificateHelper.PrivateKeyToBase64(certificate)))); + var manifestEntry = string.Format(template, base64CertHash, keyId, base64Cert); + return manifestEntry; + } - WriteObject(record); + static string/*?*/ GetPfxBase64OrWarn(Cmdlet cmdlet, X509Certificate2 certificate, SecureString password) + { + try + { + var pfxBytes = certificate.Export(X509ContentType.Pfx, password); + var base64string = Convert.ToBase64String(pfxBytes); + return base64string; } - else + catch (Exception ex) { - throw new PSArgumentException("Certificate file does not exist"); + cmdlet.WriteWarning(ex.Message); + return null; } } + + internal static void WriteAzureCertificateOutput(PSCmdlet cmdlet, X509Certificate2 certificate, SecureString password) + { + string manifestEntry = GetManifestEntry(certificate); + var pfxBase64 = GetPfxBase64OrWarn(cmdlet, certificate, password); + + var record = new Model.AzureCertificate( + subject: certificate.Subject, + notBefore: certificate.NotBefore, + notAfter: certificate.NotAfter, + thumbprint: certificate.Thumbprint, + pfxBase64: pfxBase64, + keyCredentials: manifestEntry, + certificate: CertificateHelper.CertificateToBase64(certificate), + privateKey: CertificateHelper.PrivateKeyToBase64(certificate) + ); + + cmdlet.WriteObject(record); + } } } diff --git a/src/Commands/Base/NewAzureCertificate.cs b/src/Commands/Base/NewAzureCertificate.cs index 64afb8b46..9a5d66497 100644 --- a/src/Commands/Base/NewAzureCertificate.cs +++ b/src/Commands/Base/NewAzureCertificate.cs @@ -1,15 +1,16 @@ -using System.Management.Automation; +using PnP.PowerShell.Commands.Utilities; using System; using System.Collections.Generic; using System.IO; +using System.Management.Automation; using System.Security; using System.Security.Cryptography.X509Certificates; -using PnP.PowerShell.Commands.Utilities; namespace PnP.PowerShell.Commands.Base { [Cmdlet(VerbsCommon.New, "PnPAzureCertificate")] + [OutputType(typeof(Model.AzureCertificate))] public class NewPnPAdalCertificate : PSCmdlet { [Parameter(Mandatory = false, Position = 0)] @@ -108,36 +109,7 @@ protected override void ProcessRecord() Host.UI.WriteLine(ConsoleColor.Yellow, Host.UI.RawUI.BackgroundColor, "Certificate added to store"); } - var rawCert = certificate.GetRawCertData(); - var base64Cert = Convert.ToBase64String(rawCert); - var rawCertHash = certificate.GetCertHash(); - var base64CertHash = Convert.ToBase64String(rawCertHash); - var keyId = Guid.NewGuid(); - - var template = @" -{{ - ""customKeyIdentifier"": ""{0}"", - ""keyId"": ""{1}"", - ""type"": ""AsymmetricX509Cert"", - ""usage"": ""Verify"", - ""value"": ""{2}"" -}} -"; - var manifestEntry = string.Format(template, base64CertHash, keyId, base64Cert); - - var record = new PSObject(); - record.Properties.Add(new PSVariableProperty(new PSVariable("Subject", certificate.Subject))); - record.Properties.Add(new PSVariableProperty(new PSVariable("ValidFrom", certificate.NotBefore))); - record.Properties.Add(new PSVariableProperty(new PSVariable("ValidTo", certificate.NotAfter))); - record.Properties.Add(new PSVariableProperty(new PSVariable("Thumbprint", certificate.Thumbprint))); - var pfxBytes = certificate.Export(X509ContentType.Pfx, CertificatePassword); - var base64string = Convert.ToBase64String(pfxBytes); - record.Properties.Add(new PSVariableProperty(new PSVariable("PfxBase64", base64string))); - record.Properties.Add(new PSVariableProperty(new PSVariable("KeyCredentials", manifestEntry))); - record.Properties.Add(new PSVariableProperty(new PSVariable("Certificate", CertificateHelper.CertificateToBase64(certificate)))); - record.Properties.Add(new PSVariableProperty(new PSVariable("PrivateKey", CertificateHelper.PrivateKeyToBase64(certificate)))); - - WriteObject(record); + GetPnPAdalCertificate.WriteAzureCertificateOutput(this, certificate, CertificatePassword); } } } diff --git a/src/Commands/Model/AzureCertificate.cs b/src/Commands/Model/AzureCertificate.cs new file mode 100644 index 000000000..5e8ec2483 --- /dev/null +++ b/src/Commands/Model/AzureCertificate.cs @@ -0,0 +1,34 @@ +using PnP.PowerShell.Commands.Utilities; + +using System; +using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; +using System.Text; + +namespace PnP.PowerShell.Commands.Model +{ + public sealed class AzureCertificate + { + internal AzureCertificate(string subject, DateTime notBefore, DateTime notAfter, string thumbprint, string/*?*/ pfxBase64, string keyCredentials, string certificate, string privateKey) + { + Subject = subject ?? throw new ArgumentNullException(nameof(subject)); + NotBefore = notBefore; + NotAfter = notAfter; + Thumbprint = thumbprint ?? throw new ArgumentNullException(nameof(thumbprint)); + PfxBase64 = pfxBase64; + KeyCredentials = keyCredentials ?? throw new ArgumentNullException(nameof(keyCredentials)); + Certificate = certificate ?? throw new ArgumentNullException(nameof(certificate)); + PrivateKey = privateKey ?? throw new ArgumentNullException(nameof(privateKey)); + } + + public string Subject { get; } + public DateTime NotBefore { get; } + public DateTime NotAfter { get; } + public string Thumbprint { get; } + public string/*?*/ PfxBase64 { get; } + public string KeyCredentials { get; } + public string Certificate { get; } + public string PrivateKey { get; } + + } +} From f4575ae1ae2649ea98cb5abd241135e374562284 Mon Sep 17 00:00:00 2001 From: James May Date: Tue, 10 May 2022 21:15:12 +1000 Subject: [PATCH 165/458] Viva cmdlets: Add missing OutputType attributes for better command completion --- src/Commands/Viva/AddVivaConnectionsDashboardACE.cs | 1 + src/Commands/Viva/GetVivaConnectionsDashboard.cs | 1 + src/Commands/Viva/RemoveVivaConnectionsACE.cs | 1 + src/Commands/Viva/UpdateVivaConnectionsACE.cs | 1 + 4 files changed, 4 insertions(+) diff --git a/src/Commands/Viva/AddVivaConnectionsDashboardACE.cs b/src/Commands/Viva/AddVivaConnectionsDashboardACE.cs index 0131a2718..638f799ec 100644 --- a/src/Commands/Viva/AddVivaConnectionsDashboardACE.cs +++ b/src/Commands/Viva/AddVivaConnectionsDashboardACE.cs @@ -9,6 +9,7 @@ namespace PnP.PowerShell.Commands.Viva { [Cmdlet(VerbsCommon.Add, "PnPVivaConnectionsDashboardACE")] + [OutputType(typeof(IVivaDashboard))] public class AddVivaConnectionsDashboardACE : PnPWebCmdlet { [Parameter(Mandatory = true)] diff --git a/src/Commands/Viva/GetVivaConnectionsDashboard.cs b/src/Commands/Viva/GetVivaConnectionsDashboard.cs index b6e7e3ca8..807989330 100644 --- a/src/Commands/Viva/GetVivaConnectionsDashboard.cs +++ b/src/Commands/Viva/GetVivaConnectionsDashboard.cs @@ -9,6 +9,7 @@ namespace PnP.PowerShell.Commands.Viva { [Cmdlet(VerbsCommon.Get, "PnPVivaConnectionsDashboardACE")] + [OutputType(typeof(AdaptiveCardExtension))] public class GetVivaConnectionsDashboard : PnPWebCmdlet { [Parameter(Mandatory = false)] diff --git a/src/Commands/Viva/RemoveVivaConnectionsACE.cs b/src/Commands/Viva/RemoveVivaConnectionsACE.cs index 6cd6a6e27..01e1cf6ad 100644 --- a/src/Commands/Viva/RemoveVivaConnectionsACE.cs +++ b/src/Commands/Viva/RemoveVivaConnectionsACE.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands.Viva { [Cmdlet(VerbsCommon.Remove, "PnPVivaConnectionsDashboardACE")] + [OutputType(typeof(void))] public class RemoveVivaConnectionsACE : PnPWebCmdlet { [Parameter(Mandatory = true)] diff --git a/src/Commands/Viva/UpdateVivaConnectionsACE.cs b/src/Commands/Viva/UpdateVivaConnectionsACE.cs index 304f604fb..78813a60c 100644 --- a/src/Commands/Viva/UpdateVivaConnectionsACE.cs +++ b/src/Commands/Viva/UpdateVivaConnectionsACE.cs @@ -9,6 +9,7 @@ namespace PnP.PowerShell.Commands.Viva { [Cmdlet(VerbsData.Update, "PnPVivaConnectionsDashboardACE")] + [OutputType(typeof(IVivaDashboard))] public class UpdateVivaConnectionsACE : PnPWebCmdlet { [Parameter(Mandatory = true)] From c8766afbbc7f5041989e65f789e72a264eea4291 Mon Sep 17 00:00:00 2001 From: James May Date: Tue, 10 May 2022 21:15:29 +1000 Subject: [PATCH 166/458] UserProfile cmdlets: Add missing OutputType attributes for better command completion --- .../SubscribeSharePointNewsDigestStatus.cs | 14 ++++++++++ .../UserProfiles/ExportUserProfile.cs | 4 ++- .../GetSubscribeSharePointNewsDigest.cs | 7 ++--- .../UserProfiles/GetUPABulkImportStatus.cs | 1 + .../UserProfiles/GetUserOneDriveQuota.cs | 1 + .../UserProfiles/GetUserProfileProperty.cs | 1 + src/Commands/UserProfiles/NewPersonalSite.cs | 1 + .../UserProfiles/NewUPABulkImportJob.cs | 1 + .../UserProfiles/RemoveUserProfile.cs | 4 ++- .../ResetUserOneDriveQuotaToDefault.cs | 1 + .../SetSubscribeSharePointNewsDigest.cs | 7 +++-- .../UserProfiles/SetUserOneDriveQuota.cs | 1 + .../UserProfiles/SetUserProfileProperty.cs | 1 + ...intUserProfilesFromAzureActiveDirectory.cs | 27 ++++++++++--------- 14 files changed, 50 insertions(+), 21 deletions(-) create mode 100644 src/Commands/Model/SubscribeSharePointNewsDigestStatus.cs diff --git a/src/Commands/Model/SubscribeSharePointNewsDigestStatus.cs b/src/Commands/Model/SubscribeSharePointNewsDigestStatus.cs new file mode 100644 index 000000000..fae69c647 --- /dev/null +++ b/src/Commands/Model/SubscribeSharePointNewsDigestStatus.cs @@ -0,0 +1,14 @@ +namespace PnP.PowerShell.Commands.Model +{ + public sealed class SubscribeSharePointNewsDigestStatus + { + internal SubscribeSharePointNewsDigestStatus(string account, bool enabled) + { + Account = account ?? throw new System.ArgumentNullException(nameof(account)); + Enabled = enabled; + } + + public string Account { get; } + public bool Enabled { get; } + } +} diff --git a/src/Commands/UserProfiles/ExportUserProfile.cs b/src/Commands/UserProfiles/ExportUserProfile.cs index 477d63b89..85d95d060 100644 --- a/src/Commands/UserProfiles/ExportUserProfile.cs +++ b/src/Commands/UserProfiles/ExportUserProfile.cs @@ -1,4 +1,5 @@ using System.Management.Automation; + using Microsoft.SharePoint.Client; using PnP.PowerShell.Commands.Base; @@ -9,6 +10,7 @@ namespace PnP.PowerShell.Commands.UserProfiles { [Cmdlet(VerbsData.Export, "PnPUserProfile")] + [OutputType(typeof(object))] public class ExportUserProfile : PnPAdminCmdlet { [Parameter(Mandatory = true, Position = 0)] @@ -16,7 +18,7 @@ public class ExportUserProfile : PnPAdminCmdlet protected override void ExecuteCmdlet() { - var hostUrl = ClientContext.Url; + var hostUrl = ClientContext.Url; if (hostUrl.EndsWith("/")) { hostUrl = hostUrl.Substring(0, hostUrl.Length - 1); diff --git a/src/Commands/UserProfiles/GetSubscribeSharePointNewsDigest.cs b/src/Commands/UserProfiles/GetSubscribeSharePointNewsDigest.cs index 9557c6c7d..4e11a5923 100644 --- a/src/Commands/UserProfiles/GetSubscribeSharePointNewsDigest.cs +++ b/src/Commands/UserProfiles/GetSubscribeSharePointNewsDigest.cs @@ -4,10 +4,12 @@ using System.Linq; using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Model; namespace PnP.PowerShell.Commands.UserProfiles { [Cmdlet(VerbsCommon.Get, "PnPSubscribeSharePointNewsDigest")] + [OutputType(typeof(SubscribeSharePointNewsDigestStatus))] public class GetSubscribeSharePointNewsDigest : PnPAdminCmdlet { [Parameter(Mandatory = true, Position = 0)] @@ -58,9 +60,8 @@ protected override void ExecuteCmdlet() WriteVerbose($"{listItems.Count} item{(listItems.Count != 1 ? "s" : "")} returned"); - var record = new PSObject(); - record.Properties.Add(new PSVariableProperty(new PSVariable("Account", Account))); - record.Properties.Add(new PSVariableProperty(new PSVariable("Enabled", listItems.Count == 0))); + var record = new SubscribeSharePointNewsDigestStatus(Account, enabled: listItems.Count == 0); + WriteObject(record); } diff --git a/src/Commands/UserProfiles/GetUPABulkImportStatus.cs b/src/Commands/UserProfiles/GetUPABulkImportStatus.cs index 77f77ae3d..d281a646c 100644 --- a/src/Commands/UserProfiles/GetUPABulkImportStatus.cs +++ b/src/Commands/UserProfiles/GetUPABulkImportStatus.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.UserProfiles { [Cmdlet(VerbsCommon.Get, "PnPUPABulkImportStatus")] + [OutputType(typeof(ImportProfilePropertiesJobInfo))] public class GetUPABulkImportStatus : PnPAdminCmdlet { [Parameter(Mandatory = false, ValueFromPipeline = true)] diff --git a/src/Commands/UserProfiles/GetUserOneDriveQuota.cs b/src/Commands/UserProfiles/GetUserOneDriveQuota.cs index c9c01cea7..409e5d28d 100644 --- a/src/Commands/UserProfiles/GetUserOneDriveQuota.cs +++ b/src/Commands/UserProfiles/GetUserOneDriveQuota.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.UserProfiles { [Cmdlet(VerbsCommon.Get, "PnPUserOneDriveQuota")] + [OutputType(typeof(long))] public class GetUserOneDriveQuota : PnPAdminCmdlet { [Parameter(Mandatory = true, Position = 0)] diff --git a/src/Commands/UserProfiles/GetUserProfileProperty.cs b/src/Commands/UserProfiles/GetUserProfileProperty.cs index 085a3f234..4f4ebf03e 100644 --- a/src/Commands/UserProfiles/GetUserProfileProperty.cs +++ b/src/Commands/UserProfiles/GetUserProfileProperty.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.UserProfiles { [Cmdlet(VerbsCommon.Get, "PnPUserProfileProperty")] + [OutputType(typeof(PersonProperties))] public class GetUserProfileProperty : PnPAdminCmdlet { [Parameter(Mandatory = true, Position = 0)] diff --git a/src/Commands/UserProfiles/NewPersonalSite.cs b/src/Commands/UserProfiles/NewPersonalSite.cs index 98d6f5ca9..b7178e749 100644 --- a/src/Commands/UserProfiles/NewPersonalSite.cs +++ b/src/Commands/UserProfiles/NewPersonalSite.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.UserProfiles { [Cmdlet(VerbsCommon.New, "PnPPersonalSite")] + [OutputType(typeof(void))] public class NewPersonalSite : PnPAdminCmdlet { [Parameter(Mandatory = true, Position = 0)] diff --git a/src/Commands/UserProfiles/NewUPABulkImportJob.cs b/src/Commands/UserProfiles/NewUPABulkImportJob.cs index 33d0a8393..71a6d3135 100644 --- a/src/Commands/UserProfiles/NewUPABulkImportJob.cs +++ b/src/Commands/UserProfiles/NewUPABulkImportJob.cs @@ -12,6 +12,7 @@ namespace PnP.PowerShell.Commands.UserProfiles { [Cmdlet(VerbsCommon.New, "PnPUPABulkImportJob", DefaultParameterSetName = ParameterSet_UPLOADFILE)] + [OutputType(typeof(ImportProfilePropertiesJobInfo))] public class NewUPABulkImportJob : PnPAdminCmdlet { private const string ParameterSet_UPLOADFILE = "Submit up a new user profile bulk import job from local file"; diff --git a/src/Commands/UserProfiles/RemoveUserProfile.cs b/src/Commands/UserProfiles/RemoveUserProfile.cs index 9510ba36f..c56a0d59e 100644 --- a/src/Commands/UserProfiles/RemoveUserProfile.cs +++ b/src/Commands/UserProfiles/RemoveUserProfile.cs @@ -9,6 +9,7 @@ namespace PnP.PowerShell.Commands.UserProfiles { [Cmdlet(VerbsCommon.Remove, "PnPUserProfile")] + [OutputType(typeof(void))] public class RemoveUserProfile : PnPAdminCmdlet { [Parameter(Mandatory = true, Position = 0)] @@ -34,7 +35,8 @@ protected override void ExecuteCmdlet() { RestHelper.PostAsync(this.HttpClient, $"{hostUrl}/_api/sp.userprofiles.peoplemanager/HardDeleteUserProfile(accountName=@a,userId='{UserId}')?@a='{normalizedUserName}'", ClientContext).GetAwaiter().GetResult(); } - WriteObject($"Completed deletion of user profile {LoginName}"); + + WriteVerbose($"Completed deletion of user profile {LoginName}"); } } } diff --git a/src/Commands/UserProfiles/ResetUserOneDriveQuotaToDefault.cs b/src/Commands/UserProfiles/ResetUserOneDriveQuotaToDefault.cs index 5093e3c2f..cd3061581 100644 --- a/src/Commands/UserProfiles/ResetUserOneDriveQuotaToDefault.cs +++ b/src/Commands/UserProfiles/ResetUserOneDriveQuotaToDefault.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.UserProfiles { [Cmdlet(VerbsCommon.Reset, "PnPUserOneDriveQuotaToDefault")] + [OutputType(typeof(ClientResult))] public class ResetUserOneDriveQuotaMax : PnPAdminCmdlet { [Parameter(Mandatory = true, Position = 0)] diff --git a/src/Commands/UserProfiles/SetSubscribeSharePointNewsDigest.cs b/src/Commands/UserProfiles/SetSubscribeSharePointNewsDigest.cs index 50a9fc45d..a63fca066 100644 --- a/src/Commands/UserProfiles/SetSubscribeSharePointNewsDigest.cs +++ b/src/Commands/UserProfiles/SetSubscribeSharePointNewsDigest.cs @@ -4,10 +4,12 @@ using System.Linq; using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Model; namespace PnP.PowerShell.Commands.UserProfiles { [Cmdlet(VerbsCommon.Set, "PnPSubscribeSharePointNewsDigest")] + [OutputType(typeof(string))] public class SetSubscribeSharePointNewsDigest : PnPAdminCmdlet { [Parameter(Mandatory = true, Position = 0)] @@ -92,10 +94,7 @@ protected override void ExecuteCmdlet() subscriptionEnabled = false; } - var record = new PSObject(); - record.Properties.Add(new PSVariableProperty(new PSVariable("Account", Account))); - record.Properties.Add(new PSVariableProperty(new PSVariable("Enabled", subscriptionEnabled))); - + var record = new SubscribeSharePointNewsDigestStatus(Account, subscriptionEnabled); WriteObject(record); } } diff --git a/src/Commands/UserProfiles/SetUserOneDriveQuota.cs b/src/Commands/UserProfiles/SetUserOneDriveQuota.cs index f9100242d..1aadf2930 100644 --- a/src/Commands/UserProfiles/SetUserOneDriveQuota.cs +++ b/src/Commands/UserProfiles/SetUserOneDriveQuota.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.UserProfiles { [Cmdlet(VerbsCommon.Set, "PnPUserOneDriveQuota")] + [OutputType(typeof(ClientResult))] public class SetUserOneDriveQuota : PnPAdminCmdlet { [Parameter(Mandatory = true, Position = 0)] diff --git a/src/Commands/UserProfiles/SetUserProfileProperty.cs b/src/Commands/UserProfiles/SetUserProfileProperty.cs index 47c175522..60c1d5ec3 100644 --- a/src/Commands/UserProfiles/SetUserProfileProperty.cs +++ b/src/Commands/UserProfiles/SetUserProfileProperty.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands.UserProfiles { [Cmdlet(VerbsCommon.Set, "PnPUserProfileProperty")] + [OutputType(typeof(void))] public class SetUserProfileProperty : PnPAdminCmdlet { [Parameter(Mandatory = true, ParameterSetName = ParameterAttribute.AllParameterSets)] diff --git a/src/Commands/UserProfiles/SyncSharePointUserProfilesFromAzureActiveDirectory.cs b/src/Commands/UserProfiles/SyncSharePointUserProfilesFromAzureActiveDirectory.cs index 1aba0f725..f33488efc 100644 --- a/src/Commands/UserProfiles/SyncSharePointUserProfilesFromAzureActiveDirectory.cs +++ b/src/Commands/UserProfiles/SyncSharePointUserProfilesFromAzureActiveDirectory.cs @@ -1,19 +1,22 @@ -using System.Collections; -using System.Management.Automation; +using Microsoft.Online.SharePoint.TenantManagement; using Microsoft.SharePoint.Client; + using PnP.PowerShell.Commands.Base; -using System.Collections.Generic; -using Microsoft.Online.SharePoint.TenantManagement; using PnP.PowerShell.Commands.Model.SharePoint.SharePointUserProfileSync; + +using System.Collections; +using System.Collections.Generic; +using System.Management.Automation; using System.Threading; namespace PnP.PowerShell.Commands.UserProfiles { [Cmdlet(VerbsData.Sync, "PnPSharePointUserProfilesFromAzureActiveDirectory")] + [OutputType(typeof(SharePointUserProfileSyncStatus))] public class SyncSharePointUserProfilesFromAzureActiveDirectory : PnPSharePointCmdlet { [Parameter(Mandatory = false)] - public List Users; + public List Users; [Parameter(Mandatory = false)] public string Folder = "Shared Documents"; @@ -41,12 +44,12 @@ protected override void ExecuteCmdlet() if (ParameterSpecified(nameof(Users))) { // Users to sync have been provided - if(Users == null) + if (Users == null) { throw new PSArgumentNullException(nameof(Users), "Provided Users collection cannot be null"); } - WriteVerbose($"Using provided user collection containing {Users.Count} user{(Users.Count != 1 ? "s": "")}"); + WriteVerbose($"Using provided user collection containing {Users.Count} user{(Users.Count != 1 ? "s" : "")}"); aadUsers = Users; } @@ -55,7 +58,7 @@ protected override void ExecuteCmdlet() // No users to sync have been provided, retrieve all users // Construct an array with all the Azure Active Directory properties that need to be fetched from the users to be able to make the mapping var allAadPropertiesList = new List(); - foreach(DictionaryEntry userProfilePropertyMappingEntry in UserProfilePropertyMapping) + foreach (DictionaryEntry userProfilePropertyMappingEntry in UserProfilePropertyMapping) { if (userProfilePropertyMappingEntry.Value != null && !allAadPropertiesList.Contains(userProfilePropertyMappingEntry.Value.ToString())) { @@ -70,7 +73,7 @@ protected override void ExecuteCmdlet() WriteVerbose($"{aadUsers.Count} user{(aadUsers.Count != 1 ? "s have" : " has")} been retrieved from Azure Active Directory"); - if(aadUsers.Count == 0) + if (aadUsers.Count == 0) { throw new PSInvalidOperationException($"No Azure Active Directory users found to process"); } @@ -81,12 +84,12 @@ protected override void ExecuteCmdlet() // Perform the mapping and execute the sync operation WriteVerbose($"Creating mapping file{(WhatIf.ToBool() ? " and" : ",")} uploading it to SharePoint Online to folder '{Folder}'{(WhatIf.ToBool() ? "" : " and executing sync job")}"); - var job = PnP.PowerShell.Commands.Utilities.SharePointUserProfileSync.SyncFromAzureActiveDirectory(nonAdminClientContext, aadUsers, IdType, UserProfilePropertyMapping, Folder, ParameterSpecified(nameof(WhatIf))).GetAwaiter().GetResult(); + var job = Utilities.SharePointUserProfileSync.SyncFromAzureActiveDirectory(nonAdminClientContext, aadUsers, IdType, UserProfilePropertyMapping, Folder, ParameterSpecified(nameof(WhatIf))).GetAwaiter().GetResult(); WriteVerbose($"Job initiated with Id {job.JobId} and status {job.State} for file {job.SourceUri}"); // Check if we should wait with finalzing this cmdlet execution until the user profile import operation has completed - if(Wait.ToBool() && !WhatIf.ToBool()) + if (Wait.ToBool() && !WhatIf.ToBool()) { // Go into a loop to wait for the import to be successful or erroneous var o365 = new Office365Tenant(ClientContext); @@ -95,7 +98,7 @@ protected override void ExecuteCmdlet() do { // Wait for 30 seconds before requesting its current state again to avoid running into throttling - Thread.Sleep((int)System.TimeSpan.FromSeconds(30).TotalMilliseconds); + Thread.Sleep((int)System.TimeSpan.FromSeconds(30).TotalMilliseconds); // Request the current status of the import job jobStatus = o365.GetImportProfilePropertyJob(job.JobId.Value); From 4d1ffbadc73384a9348f2cb137b647d369cfae9d Mon Sep 17 00:00:00 2001 From: James May Date: Tue, 10 May 2022 21:15:42 +1000 Subject: [PATCH 167/458] RecycleBin cmdlets: Add missing OutputType attributes for better command completion --- src/Commands/RecycleBin/ClearRecycleBinItem.cs | 1 + src/Commands/RecycleBin/ClearTenantRecycleBinItem.cs | 1 + src/Commands/RecycleBin/GetRecycleBinItem.cs | 1 + src/Commands/RecycleBin/GetTenantRecycleBinItem.cs | 1 + src/Commands/RecycleBin/MoveRecycleBinItem.cs | 1 + src/Commands/RecycleBin/RestoreRecycleBinItem.cs | 1 + src/Commands/RecycleBin/RestoreTenantRecycleBinItem.cs | 1 + 7 files changed, 7 insertions(+) diff --git a/src/Commands/RecycleBin/ClearRecycleBinItem.cs b/src/Commands/RecycleBin/ClearRecycleBinItem.cs index 85e8b8d8e..d328ded10 100644 --- a/src/Commands/RecycleBin/ClearRecycleBinItem.cs +++ b/src/Commands/RecycleBin/ClearRecycleBinItem.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.RecycleBin { [Cmdlet(VerbsCommon.Clear, "PnPRecycleBinItem", DefaultParameterSetName = PARAMETERSET_ALL)] + [OutputType(typeof(void))] public class ClearRecycleBinItem : PnPSharePointCmdlet { const string PARAMETERSET_ALL = "All"; diff --git a/src/Commands/RecycleBin/ClearTenantRecycleBinItem.cs b/src/Commands/RecycleBin/ClearTenantRecycleBinItem.cs index 9f3296608..dd3fd900c 100644 --- a/src/Commands/RecycleBin/ClearTenantRecycleBinItem.cs +++ b/src/Commands/RecycleBin/ClearTenantRecycleBinItem.cs @@ -9,6 +9,7 @@ namespace PnP.PowerShell.Commands.RecycleBin { [Cmdlet(VerbsCommon.Clear, "PnPTenantRecycleBinItem")] + [OutputType(typeof(void))] public class ClearTenantRecycleBinItem : PnPAdminCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = false)] diff --git a/src/Commands/RecycleBin/GetRecycleBinItem.cs b/src/Commands/RecycleBin/GetRecycleBinItem.cs index f67162f3a..8b0b4361a 100644 --- a/src/Commands/RecycleBin/GetRecycleBinItem.cs +++ b/src/Commands/RecycleBin/GetRecycleBinItem.cs @@ -10,6 +10,7 @@ namespace PnP.PowerShell.Commands.RecycleBin { [Cmdlet(VerbsCommon.Get, "PnPRecycleBinItem", DefaultParameterSetName = ParameterSet_ALL)] + [OutputType(typeof(RecycleBinItem))] public class GetRecycleBinItems : PnPRetrievalsCmdlet { private const string ParameterSet_ALL = "All"; diff --git a/src/Commands/RecycleBin/GetTenantRecycleBinItem.cs b/src/Commands/RecycleBin/GetTenantRecycleBinItem.cs index 757bc88aa..1c0478352 100644 --- a/src/Commands/RecycleBin/GetTenantRecycleBinItem.cs +++ b/src/Commands/RecycleBin/GetTenantRecycleBinItem.cs @@ -7,6 +7,7 @@ namespace PnP.PowerShell.Commands.RecycleBin { [Cmdlet(VerbsCommon.Get, "PnPTenantRecycleBinItem", DefaultParameterSetName = "All")] + [OutputType(typeof(DeletedSiteProperties))] public class GetTenantRecycleBinItems : PnPAdminCmdlet { protected override void ExecuteCmdlet() diff --git a/src/Commands/RecycleBin/MoveRecycleBinItem.cs b/src/Commands/RecycleBin/MoveRecycleBinItem.cs index ed7a5b285..dcec2692c 100644 --- a/src/Commands/RecycleBin/MoveRecycleBinItem.cs +++ b/src/Commands/RecycleBin/MoveRecycleBinItem.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands.RecycleBin { [Cmdlet(VerbsCommon.Move, "PnPRecycleBinItem")] + [OutputType(typeof(void))] public class MoveRecycleBinItems : PnPSharePointCmdlet { [Parameter(Mandatory = false, ValueFromPipeline = true)] diff --git a/src/Commands/RecycleBin/RestoreRecycleBinItem.cs b/src/Commands/RecycleBin/RestoreRecycleBinItem.cs index 5266591b2..d10fbb4c1 100644 --- a/src/Commands/RecycleBin/RestoreRecycleBinItem.cs +++ b/src/Commands/RecycleBin/RestoreRecycleBinItem.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands.RecycleBin { [Cmdlet(VerbsData.Restore, "PnPRecycleBinItem")] + [OutputType(typeof(void))] public class RestoreRecycleBinItem : PnPSharePointCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true)] diff --git a/src/Commands/RecycleBin/RestoreTenantRecycleBinItem.cs b/src/Commands/RecycleBin/RestoreTenantRecycleBinItem.cs index f0ef18bb9..0bbb44005 100644 --- a/src/Commands/RecycleBin/RestoreTenantRecycleBinItem.cs +++ b/src/Commands/RecycleBin/RestoreTenantRecycleBinItem.cs @@ -8,6 +8,7 @@ namespace PnP.PowerShell.Commands.RecycleBin { [Cmdlet(VerbsData.Restore, "PnPTenantRecycleBinItem")] + [OutputType(typeof(void))] public class RestoreTenantRecycleBinItem : PnPAdminCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = false)] From 6e3c70373a8db773335276624190e0392b0ec0bd Mon Sep 17 00:00:00 2001 From: James May Date: Tue, 10 May 2022 21:15:54 +1000 Subject: [PATCH 168/458] Transformation cmdlet: Add missing OutputType attributes for better command completion --- src/Commands/Transformation/InvokeTransformation.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Commands/Transformation/InvokeTransformation.cs b/src/Commands/Transformation/InvokeTransformation.cs index 03de15e0b..d49521b3a 100644 --- a/src/Commands/Transformation/InvokeTransformation.cs +++ b/src/Commands/Transformation/InvokeTransformation.cs @@ -24,6 +24,7 @@ namespace PnP.PowerShell.Commands.Transformation { [Cmdlet(VerbsLifecycle.Invoke, "PnPTransformation")] + [OutputType(typeof(string))] public class InvokeTransformation : PnPWebCmdlet { private static string rootFolder = ""; From 8c785ed1d6117d132541db69d138e060a8439c1c Mon Sep 17 00:00:00 2001 From: Aleks Date: Wed, 11 May 2022 08:48:06 +0200 Subject: [PATCH 169/458] publishing image for 5 platform --- .github/workflows/release.yml | 13 +++++++++-- docker/Publish-UnpublishedImage.ps1 | 36 ++++++++++++++++++++++------- docker/README.md | 15 ++++++++++-- docker/pnppowershell.dockerFile | 11 +++++++-- 4 files changed, 61 insertions(+), 14 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8d2129030..071c93f89 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,7 +5,16 @@ on: schedule: - cron: '30 3 * * *' jobs: - publish-docker: + publish-docker-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Build and Publish All + shell: pwsh + run: | + $securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword "pnppowershell-windows.dockerFile" "nanoserver-1809,nanoserver-20h2,nanoserver-ltsc2022" + publish-docker-linux: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -13,4 +22,4 @@ jobs: shell: pwsh run: | $securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force - ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword "pnppowershell.dockerFile" "alpine-3.14,arm32v7-ubuntu-bionic" diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 index fc259f547..7ddb8fceb 100644 --- a/docker/Publish-UnpublishedImage.ps1 +++ b/docker/Publish-UnpublishedImage.ps1 @@ -18,7 +18,22 @@ Param( Mandatory = $true, ValueFromPipeline = $false)] [Security.SecureString] - $DOCKER_PASSWORD + $DOCKER_PASSWORD, + [Parameter(Position = 4, + Mandatory = $false, + ValueFromPipeline = $false)] + [String] + $DOCKER_FILE_NAME = "pnppowershell.dockerFile", + [Parameter(Position = 5, + Mandatory = $false, + ValueFromPipeline = $false)] + [bool] + $SKIP_PUBLISHER_CHECK = $false, + [Parameter(Position = 6, + Mandatory = $false, + ValueFromPipeline = $false)] + [String] + $DOCKER_IMAGE_SUFFIX_ARRAY = "nanoserver-1809" ) $publishedImageVersions = (Invoke-RestMethod https://registry.hub.docker.com/v2/repositories/$DOCKER_USERNAME/$DOCKER_IMAGE_NAME/tags?page_size=10240).results | % { $_.name @@ -27,12 +42,17 @@ $moduleVersions = Find-Module $PS_MODULE_NAME -AllVersions; [array]::Reverse($moduleVersions); $moduleVersions | % { $moduleVersion = $_.Version; - if ( !( $publishedImageVersions -contains $moduleVersion ) ) { - docker build --build-arg "PNP_MODULE_VERSION=$moduleVersion" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$moduleVersion; - docker image tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$moduleVersion $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; - $plainStringPassword = [System.Net.NetworkCredential]::new("", $DOCKER_PASSWORD).Password; - docker login -u $DOCKER_USERNAME -p "$plainStringPassword"; - docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$moduleVersion; - docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; + $DOCKER_IMAGE_SUFFIX_ARRAY.Split( "," ) | % { + $baseImageSuffix = $_; + $imageVersion = "$moduleVersion-$baseImageSuffix"; + Write-Host "Checking $imageVersion" + if ( !( $publishedImageVersions -contains $imageVersion ) ) { + docker build --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "SKIP_PUBLISHER_CHECK=FALSE" ./docker -f ./docker/$DOCKER_FILE_NAME --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; + docker image tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion$DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; + $plainStringPassword = [System.Net.NetworkCredential]::new("", $DOCKER_PASSWORD).Password; + docker login -u $DOCKER_USERNAME -p "$plainStringPassword"; + docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; + docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; + } } } diff --git a/docker/README.md b/docker/README.md index cc61e4c4d..ddd5f55d8 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,4 +1,4 @@ -# Publish manually +# Publish manually in Windows 1. Set "DOCKER_USERNAME" and "DOCKER_PASSWORD" variables @@ -6,7 +6,18 @@ ```powershell $securedPassword = ConvertTo-SecureString $DOCKER_PASSWORD -AsPlainText -Force -./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME powershell $securedPassword +./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME powershell $securedPassword "pnppowershell-windows.dockerFile" $true "nanoserver-1809,nanoserver-20h2,nanoserver-ltsc2022" +``` + +# Publish manually in Linux + +1. Set "DOCKER_USERNAME" and "DOCKER_PASSWORD" variables + +2. Run + +```powershell +$securedPassword = ConvertTo-SecureString $DOCKER_PASSWORD -AsPlainText -Force +./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME powershell $securedPassword "pnppowershell.dockerFile" $false "alpine-3.14,arm32v7-ubuntu-bionic" ``` # Publish with prereleases manually diff --git a/docker/pnppowershell.dockerFile b/docker/pnppowershell.dockerFile index b46b7e6c9..210218447 100644 --- a/docker/pnppowershell.dockerFile +++ b/docker/pnppowershell.dockerFile @@ -1,5 +1,12 @@ -FROM mcr.microsoft.com/powershell:7.2.2-alpine-3.14-20220318 +ARG BASE_IMAGE_SUFFIX=alpine-3.14 +ARG BASE_IMAGE=mcr.microsoft.com/powershell:lts-7.2-$BASE_IMAGE_SUFFIX +FROM $BASE_IMAGE SHELL ["pwsh", "-command"] +ARG SKIP_PUBLISHER_CHECK = "TRUE" ARG PNP_MODULE_VERSION -RUN Install-Module -Name PnP.PowerShell -RequiredVersion $env:PNP_MODULE_VERSION -Force -AllowPrerelease +RUN if ( $SKIP_PUBLISHER_CHECK ) { \ + Install-Module -Name PnP.PowerShell -RequiredVersion $env:PNP_MODULE_VERSION -Force -AllowPrerelease -SkipPublisherCheck \ + } else { \ + Install-Module -Name PnP.PowerShell -RequiredVersion $env:PNP_MODULE_VERSION -Force -AllowPrerelease \ + } From 648bef5f87d7e8e9f60b0606187b7ec1f2042b97 Mon Sep 17 00:00:00 2001 From: Aleks Date: Wed, 11 May 2022 08:51:43 +0200 Subject: [PATCH 170/458] fixing parameters in github actions --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 071c93f89..f543fe225 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: shell: pwsh run: | $securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force - ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword "pnppowershell-windows.dockerFile" "nanoserver-1809,nanoserver-20h2,nanoserver-ltsc2022" + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword "pnppowershell-windows.dockerFile" $true "nanoserver-1809,nanoserver-20h2,nanoserver-ltsc2022" publish-docker-linux: runs-on: ubuntu-latest steps: @@ -22,4 +22,4 @@ jobs: shell: pwsh run: | $securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force - ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword "pnppowershell.dockerFile" "alpine-3.14,arm32v7-ubuntu-bionic" + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword "pnppowershell.dockerFile" $false "alpine-3.14,arm32v7-ubuntu-bionic" From f30429151dc2440afe6bf553d60225734f1c9a30 Mon Sep 17 00:00:00 2001 From: Aleks Date: Wed, 11 May 2022 08:55:51 +0200 Subject: [PATCH 171/458] typo in space --- docker/Publish-UnpublishedImage.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 index 7ddb8fceb..c4c529cae 100644 --- a/docker/Publish-UnpublishedImage.ps1 +++ b/docker/Publish-UnpublishedImage.ps1 @@ -48,7 +48,7 @@ $moduleVersions | % { Write-Host "Checking $imageVersion" if ( !( $publishedImageVersions -contains $imageVersion ) ) { docker build --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "SKIP_PUBLISHER_CHECK=FALSE" ./docker -f ./docker/$DOCKER_FILE_NAME --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; - docker image tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion$DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; + docker image tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; $plainStringPassword = [System.Net.NetworkCredential]::new("", $DOCKER_PASSWORD).Password; docker login -u $DOCKER_USERNAME -p "$plainStringPassword"; docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; From 22bbf90dd5d55ab129e275a720fbca7d77fe86d0 Mon Sep 17 00:00:00 2001 From: Aleks Date: Wed, 11 May 2022 09:13:06 +0200 Subject: [PATCH 172/458] sticking to 1 dockerfile --- .github/workflows/release.yml | 4 ++-- docker/Publish-UnpublishedImage.ps1 | 7 +------ docker/README.md | 4 ++-- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f543fe225..38138fde0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: shell: pwsh run: | $securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force - ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword "pnppowershell-windows.dockerFile" $true "nanoserver-1809,nanoserver-20h2,nanoserver-ltsc2022" + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword $true "nanoserver-1809,nanoserver-20h2,nanoserver-ltsc2022" publish-docker-linux: runs-on: ubuntu-latest steps: @@ -22,4 +22,4 @@ jobs: shell: pwsh run: | $securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force - ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword "pnppowershell.dockerFile" $false "alpine-3.14,arm32v7-ubuntu-bionic" + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword $false "alpine-3.14,arm32v7-ubuntu-bionic" diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 index c4c529cae..8689f163c 100644 --- a/docker/Publish-UnpublishedImage.ps1 +++ b/docker/Publish-UnpublishedImage.ps1 @@ -22,14 +22,9 @@ Param( [Parameter(Position = 4, Mandatory = $false, ValueFromPipeline = $false)] - [String] - $DOCKER_FILE_NAME = "pnppowershell.dockerFile", - [Parameter(Position = 5, - Mandatory = $false, - ValueFromPipeline = $false)] [bool] $SKIP_PUBLISHER_CHECK = $false, - [Parameter(Position = 6, + [Parameter(Position = 5, Mandatory = $false, ValueFromPipeline = $false)] [String] diff --git a/docker/README.md b/docker/README.md index ddd5f55d8..c4abd5c26 100644 --- a/docker/README.md +++ b/docker/README.md @@ -6,7 +6,7 @@ ```powershell $securedPassword = ConvertTo-SecureString $DOCKER_PASSWORD -AsPlainText -Force -./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME powershell $securedPassword "pnppowershell-windows.dockerFile" $true "nanoserver-1809,nanoserver-20h2,nanoserver-ltsc2022" +./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME powershell $securedPassword $true "nanoserver-1809,nanoserver-20h2,nanoserver-ltsc2022" ``` # Publish manually in Linux @@ -17,7 +17,7 @@ $securedPassword = ConvertTo-SecureString $DOCKER_PASSWORD -AsPlainText -Force ```powershell $securedPassword = ConvertTo-SecureString $DOCKER_PASSWORD -AsPlainText -Force -./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME powershell $securedPassword "pnppowershell.dockerFile" $false "alpine-3.14,arm32v7-ubuntu-bionic" +./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME powershell $securedPassword $false "alpine-3.14,arm32v7-ubuntu-bionic" ``` # Publish with prereleases manually From b7c01a8b6ab8abde1a68fb0c0bfc8aa8720d6e6b Mon Sep 17 00:00:00 2001 From: Aleks Date: Wed, 11 May 2022 10:56:39 +0200 Subject: [PATCH 173/458] altering installation user --- .github/workflows/release.yml | 4 ++-- docker/Publish-UnpublishedImage.ps1 | 9 +++++++-- docker/README.md | 4 ++-- docker/pnppowershell.dockerFile | 11 +++++++---- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 38138fde0..4803e56c5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: shell: pwsh run: | $securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force - ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword $true "nanoserver-1809,nanoserver-20h2,nanoserver-ltsc2022" + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword "ContainerAdministrator" $true "nanoserver-1809,nanoserver-20h2,nanoserver-ltsc2022" publish-docker-linux: runs-on: ubuntu-latest steps: @@ -22,4 +22,4 @@ jobs: shell: pwsh run: | $securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force - ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword $false "alpine-3.14,arm32v7-ubuntu-bionic" + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword "root" $false "alpine-3.14,arm32v7-ubuntu-bionic" diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 index 8689f163c..539a23033 100644 --- a/docker/Publish-UnpublishedImage.ps1 +++ b/docker/Publish-UnpublishedImage.ps1 @@ -22,9 +22,14 @@ Param( [Parameter(Position = 4, Mandatory = $false, ValueFromPipeline = $false)] + [String] + $DOCKER_INSTALL_USER = "ContainerAdministrator", + [Parameter(Position = 5, + Mandatory = $false, + ValueFromPipeline = $false)] [bool] $SKIP_PUBLISHER_CHECK = $false, - [Parameter(Position = 5, + [Parameter(Position = 6, Mandatory = $false, ValueFromPipeline = $false)] [String] @@ -42,7 +47,7 @@ $moduleVersions | % { $imageVersion = "$moduleVersion-$baseImageSuffix"; Write-Host "Checking $imageVersion" if ( !( $publishedImageVersions -contains $imageVersion ) ) { - docker build --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "SKIP_PUBLISHER_CHECK=FALSE" ./docker -f ./docker/$DOCKER_FILE_NAME --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; + docker build --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "INSTALL_USER=$DOCKER_INSTALL_USER" --build-arg "SKIP_PUBLISHER_CHECK=$SKIP_PUBLISHER_CHECK" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; docker image tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; $plainStringPassword = [System.Net.NetworkCredential]::new("", $DOCKER_PASSWORD).Password; docker login -u $DOCKER_USERNAME -p "$plainStringPassword"; diff --git a/docker/README.md b/docker/README.md index c4abd5c26..df53f4033 100644 --- a/docker/README.md +++ b/docker/README.md @@ -6,7 +6,7 @@ ```powershell $securedPassword = ConvertTo-SecureString $DOCKER_PASSWORD -AsPlainText -Force -./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME powershell $securedPassword $true "nanoserver-1809,nanoserver-20h2,nanoserver-ltsc2022" +./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME powershell $securedPassword "ContainerAdministrator" $true "nanoserver-1809,nanoserver-20h2,nanoserver-ltsc2022" ``` # Publish manually in Linux @@ -17,7 +17,7 @@ $securedPassword = ConvertTo-SecureString $DOCKER_PASSWORD -AsPlainText -Force ```powershell $securedPassword = ConvertTo-SecureString $DOCKER_PASSWORD -AsPlainText -Force -./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME powershell $securedPassword $false "alpine-3.14,arm32v7-ubuntu-bionic" +./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME powershell $securedPassword $false "root" "alpine-3.14,arm32v7-ubuntu-bionic" ``` # Publish with prereleases manually diff --git a/docker/pnppowershell.dockerFile b/docker/pnppowershell.dockerFile index 210218447..350ca3084 100644 --- a/docker/pnppowershell.dockerFile +++ b/docker/pnppowershell.dockerFile @@ -3,10 +3,13 @@ ARG BASE_IMAGE=mcr.microsoft.com/powershell:lts-7.2-$BASE_IMAGE_SUFFIX FROM $BASE_IMAGE SHELL ["pwsh", "-command"] -ARG SKIP_PUBLISHER_CHECK = "TRUE" +ARG INSTALL_USER="ContainerAdministrator" +USER $INSTALL_USER ARG PNP_MODULE_VERSION -RUN if ( $SKIP_PUBLISHER_CHECK ) { \ - Install-Module -Name PnP.PowerShell -RequiredVersion $env:PNP_MODULE_VERSION -Force -AllowPrerelease -SkipPublisherCheck \ +ARG SKIP_PUBLISHER_CHECK="TRUE" +RUN if ( $env:SKIP_PUBLISHER_CHECK ) { \ + Write-Host "SKIP_PUBLISHER_CHECK"; \ + Install-Module -Name PnP.PowerShell -RequiredVersion $env:PNP_MODULE_VERSION -Force -AllowPrerelease -SkipPublisherCheck; \ } else { \ - Install-Module -Name PnP.PowerShell -RequiredVersion $env:PNP_MODULE_VERSION -Force -AllowPrerelease \ + Install-Module -Name PnP.PowerShell -RequiredVersion $env:PNP_MODULE_VERSION -Force -AllowPrerelease; \ } From 20888fd22a34c19c91f230d494866d211e363232 Mon Sep 17 00:00:00 2001 From: Aleks Date: Wed, 11 May 2022 12:11:35 +0200 Subject: [PATCH 174/458] 3 platforms for Windows is default --- docker/Publish-UnpublishedImage.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 index 539a23033..5995002a8 100644 --- a/docker/Publish-UnpublishedImage.ps1 +++ b/docker/Publish-UnpublishedImage.ps1 @@ -33,7 +33,7 @@ Param( Mandatory = $false, ValueFromPipeline = $false)] [String] - $DOCKER_IMAGE_SUFFIX_ARRAY = "nanoserver-1809" + $DOCKER_IMAGE_SUFFIX_ARRAY = "nanoserver-1809,nanoserver-20h2,nanoserver-ltsc2022" ) $publishedImageVersions = (Invoke-RestMethod https://registry.hub.docker.com/v2/repositories/$DOCKER_USERNAME/$DOCKER_IMAGE_NAME/tags?page_size=10240).results | % { $_.name From dc1dcfe4398127ee95801deb4bca8f1a544e0db1 Mon Sep 17 00:00:00 2001 From: Aleks Date: Wed, 11 May 2022 12:16:16 +0200 Subject: [PATCH 175/458] fixed string comparison --- docker/pnppowershell.dockerFile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/pnppowershell.dockerFile b/docker/pnppowershell.dockerFile index 350ca3084..585cefd75 100644 --- a/docker/pnppowershell.dockerFile +++ b/docker/pnppowershell.dockerFile @@ -7,7 +7,7 @@ ARG INSTALL_USER="ContainerAdministrator" USER $INSTALL_USER ARG PNP_MODULE_VERSION ARG SKIP_PUBLISHER_CHECK="TRUE" -RUN if ( $env:SKIP_PUBLISHER_CHECK ) { \ +RUN if ( $env:SKIP_PUBLISHER_CHECK -eq "True" ) { \ Write-Host "SKIP_PUBLISHER_CHECK"; \ Install-Module -Name PnP.PowerShell -RequiredVersion $env:PNP_MODULE_VERSION -Force -AllowPrerelease -SkipPublisherCheck; \ } else { \ From 2a3c3e7e2e35acea44aa37f967b5b91069cf3be7 Mon Sep 17 00:00:00 2001 From: Aleks Date: Wed, 11 May 2022 12:18:21 +0200 Subject: [PATCH 176/458] removing unnecessary quotes --- docker/pnppowershell.dockerFile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/pnppowershell.dockerFile b/docker/pnppowershell.dockerFile index 585cefd75..9389e9298 100644 --- a/docker/pnppowershell.dockerFile +++ b/docker/pnppowershell.dockerFile @@ -3,10 +3,10 @@ ARG BASE_IMAGE=mcr.microsoft.com/powershell:lts-7.2-$BASE_IMAGE_SUFFIX FROM $BASE_IMAGE SHELL ["pwsh", "-command"] -ARG INSTALL_USER="ContainerAdministrator" +ARG INSTALL_USER=ContainerAdministrator USER $INSTALL_USER ARG PNP_MODULE_VERSION -ARG SKIP_PUBLISHER_CHECK="TRUE" +ARG SKIP_PUBLISHER_CHECK=TRUE RUN if ( $env:SKIP_PUBLISHER_CHECK -eq "True" ) { \ Write-Host "SKIP_PUBLISHER_CHECK"; \ Install-Module -Name PnP.PowerShell -RequiredVersion $env:PNP_MODULE_VERSION -Force -AllowPrerelease -SkipPublisherCheck; \ From b91081aef4e2326a104f5efd9d74f065239bdcce Mon Sep 17 00:00:00 2001 From: Aleks Date: Wed, 11 May 2022 12:31:53 +0200 Subject: [PATCH 177/458] fixed logical comparison --- docker/pnppowershell.dockerFile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/pnppowershell.dockerFile b/docker/pnppowershell.dockerFile index 9389e9298..28fe6302b 100644 --- a/docker/pnppowershell.dockerFile +++ b/docker/pnppowershell.dockerFile @@ -7,7 +7,7 @@ ARG INSTALL_USER=ContainerAdministrator USER $INSTALL_USER ARG PNP_MODULE_VERSION ARG SKIP_PUBLISHER_CHECK=TRUE -RUN if ( $env:SKIP_PUBLISHER_CHECK -eq "True" ) { \ +RUN if ( $env:SKIP_PUBLISHER_CHECK -eq $true ) { \ Write-Host "SKIP_PUBLISHER_CHECK"; \ Install-Module -Name PnP.PowerShell -RequiredVersion $env:PNP_MODULE_VERSION -Force -AllowPrerelease -SkipPublisherCheck; \ } else { \ From 05b0cfc591698ee57b617df4762227e10f2c308f Mon Sep 17 00:00:00 2001 From: Aleks Date: Wed, 11 May 2022 13:57:33 +0200 Subject: [PATCH 178/458] specifying platform for building --- docker/Publish-UnpublishedImage.ps1 | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 index 5995002a8..e75d052ee 100644 --- a/docker/Publish-UnpublishedImage.ps1 +++ b/docker/Publish-UnpublishedImage.ps1 @@ -47,7 +47,18 @@ $moduleVersions | % { $imageVersion = "$moduleVersion-$baseImageSuffix"; Write-Host "Checking $imageVersion" if ( !( $publishedImageVersions -contains $imageVersion ) ) { - docker build --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "INSTALL_USER=$DOCKER_INSTALL_USER" --build-arg "SKIP_PUBLISHER_CHECK=$SKIP_PUBLISHER_CHECK" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; + $platform = switch ( $baseImageSuffix ) { + "alpine-3.14" { + "linux/amd64" + } + "arm32v7-ubuntu-bionic" { + "linux/arm/v7" + } + default { "Windows" } + } + $platform + if ( $baseImageSuffix -eq "alpine-3.14" ) {} + docker build --platform=$platform --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "INSTALL_USER=$DOCKER_INSTALL_USER" --build-arg "SKIP_PUBLISHER_CHECK=$SKIP_PUBLISHER_CHECK" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; docker image tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; $plainStringPassword = [System.Net.NetworkCredential]::new("", $DOCKER_PASSWORD).Password; docker login -u $DOCKER_USERNAME -p "$plainStringPassword"; From e6bcb170b947cd6b5734e208f52fd0aa8cfd3630 Mon Sep 17 00:00:00 2001 From: Aleks Date: Wed, 11 May 2022 18:00:10 +0200 Subject: [PATCH 179/458] using buildx for arm32v7-ubuntu-bionic --- .github/workflows/release.yml | 4 ++++ docker/Publish-UnpublishedImage.ps1 | 15 ++++----------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4803e56c5..5e72fcb1a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,6 +18,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 - name: Build and Publish All shell: pwsh run: | diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 index e75d052ee..e88f0a928 100644 --- a/docker/Publish-UnpublishedImage.ps1 +++ b/docker/Publish-UnpublishedImage.ps1 @@ -47,18 +47,11 @@ $moduleVersions | % { $imageVersion = "$moduleVersion-$baseImageSuffix"; Write-Host "Checking $imageVersion" if ( !( $publishedImageVersions -contains $imageVersion ) ) { - $platform = switch ( $baseImageSuffix ) { - "alpine-3.14" { - "linux/amd64" - } - "arm32v7-ubuntu-bionic" { - "linux/arm/v7" - } - default { "Windows" } + if ( $baseImageSuffix -eq "arm32v7-ubuntu-bionic" ) { + docker buildx build --platform linux/arm/v7 --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "INSTALL_USER=$DOCKER_INSTALL_USER" --build-arg "SKIP_PUBLISHER_CHECK=$SKIP_PUBLISHER_CHECK" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; + } else { + docker build --platform=$platform --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "INSTALL_USER=$DOCKER_INSTALL_USER" --build-arg "SKIP_PUBLISHER_CHECK=$SKIP_PUBLISHER_CHECK" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; } - $platform - if ( $baseImageSuffix -eq "alpine-3.14" ) {} - docker build --platform=$platform --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "INSTALL_USER=$DOCKER_INSTALL_USER" --build-arg "SKIP_PUBLISHER_CHECK=$SKIP_PUBLISHER_CHECK" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; docker image tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; $plainStringPassword = [System.Net.NetworkCredential]::new("", $DOCKER_PASSWORD).Password; docker login -u $DOCKER_USERNAME -p "$plainStringPassword"; From b07ee24a0cb5dd286ff25290987c2046b7708471 Mon Sep 17 00:00:00 2001 From: Aleks Date: Wed, 11 May 2022 18:11:35 +0200 Subject: [PATCH 180/458] buildx debug output --- docker/Publish-UnpublishedImage.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 index e88f0a928..097204124 100644 --- a/docker/Publish-UnpublishedImage.ps1 +++ b/docker/Publish-UnpublishedImage.ps1 @@ -48,9 +48,10 @@ $moduleVersions | % { Write-Host "Checking $imageVersion" if ( !( $publishedImageVersions -contains $imageVersion ) ) { if ( $baseImageSuffix -eq "arm32v7-ubuntu-bionic" ) { + Write-Host "Using buildx --platform linux/arm/v7"; docker buildx build --platform linux/arm/v7 --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "INSTALL_USER=$DOCKER_INSTALL_USER" --build-arg "SKIP_PUBLISHER_CHECK=$SKIP_PUBLISHER_CHECK" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; } else { - docker build --platform=$platform --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "INSTALL_USER=$DOCKER_INSTALL_USER" --build-arg "SKIP_PUBLISHER_CHECK=$SKIP_PUBLISHER_CHECK" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; + docker build --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "INSTALL_USER=$DOCKER_INSTALL_USER" --build-arg "SKIP_PUBLISHER_CHECK=$SKIP_PUBLISHER_CHECK" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; } docker image tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; $plainStringPassword = [System.Net.NetworkCredential]::new("", $DOCKER_PASSWORD).Password; From b67fc3116a89662733830a67b5db411f3b12c307 Mon Sep 17 00:00:00 2001 From: Aleks Date: Wed, 11 May 2022 18:41:02 +0200 Subject: [PATCH 181/458] buildx build --load --- docker/Publish-UnpublishedImage.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 index 097204124..862c19ba9 100644 --- a/docker/Publish-UnpublishedImage.ps1 +++ b/docker/Publish-UnpublishedImage.ps1 @@ -49,7 +49,7 @@ $moduleVersions | % { if ( !( $publishedImageVersions -contains $imageVersion ) ) { if ( $baseImageSuffix -eq "arm32v7-ubuntu-bionic" ) { Write-Host "Using buildx --platform linux/arm/v7"; - docker buildx build --platform linux/arm/v7 --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "INSTALL_USER=$DOCKER_INSTALL_USER" --build-arg "SKIP_PUBLISHER_CHECK=$SKIP_PUBLISHER_CHECK" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; + docker buildx build --load --platform linux/arm/v7 --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "INSTALL_USER=$DOCKER_INSTALL_USER" --build-arg "SKIP_PUBLISHER_CHECK=$SKIP_PUBLISHER_CHECK" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; } else { docker build --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "INSTALL_USER=$DOCKER_INSTALL_USER" --build-arg "SKIP_PUBLISHER_CHECK=$SKIP_PUBLISHER_CHECK" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; } From c57d18dc3e1fc9e09fd450e37cdac7b01716f77d Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Wed, 11 May 2022 20:49:30 +0200 Subject: [PATCH 182/458] Implemented Stop-PnPFlowRun (#1838) --- documentation/Stop-PnPFlowRun.md | 109 ++++++++++++++++++ .../PowerAutomate/StopFlowRun.cs | 52 +++++++++ 2 files changed, 161 insertions(+) create mode 100644 documentation/Stop-PnPFlowRun.md create mode 100644 src/Commands/PowerPlatform/PowerAutomate/StopFlowRun.cs diff --git a/documentation/Stop-PnPFlowRun.md b/documentation/Stop-PnPFlowRun.md new file mode 100644 index 000000000..b9f16e7e2 --- /dev/null +++ b/documentation/Stop-PnPFlowRun.md @@ -0,0 +1,109 @@ +--- +Module Name: PnP.PowerShell +title: Stop-PnPFlowRun +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Stop-PnPFlowRun.html +--- + +# Stop-PnPFlowRun + +## SYNOPSIS +**Required Permissions** + +* Azure: management.azure.com + +Stops/cancels a specific run of a Microsoft flow. + +## SYNTAX + +```powershell +Stop-PnPFlowRun -Environment -Flow -Identity [-Force] [] +``` + +## DESCRIPTION +This cmdlet cancels a running Power Automate flow run. + +## EXAMPLES + +### Example 1 +```powershell +$environment = Get-PnPPowerPlatformEnvironment +Stop-PnPFlowRun -Environment $environment -Flow fba63225-baf9-4d76-86a1-1b42c917a182 -Identity 08585531682024670884771461819CU230 +``` +This cancels the specified flow run of the specified flow + + +### Example 2 +```powershell +$environment = Get-PnPPowerPlatformEnvironment +Stop-PnPFlowRun -Environment $environment -Flow fba63225-baf9-4d76-86a1-1b42c917a182 -Identity 08585531682024670884771461819CU230 -Force +``` +This cancels the specified flow run of the specified flow without confirmation + +## PARAMETERS + +### -Environment +The name of the Power Platform environment or an Environment object to retrieve the available flows for. + +```yaml +Type: PowerAutomateEnvironmentPipeBind +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Flow +The Name/Id of the flow to retrieve the available flow runs for. + +```yaml +Type: PowerAutomateFlowPipeBind +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Identity +The Name/Id of the flow run to cancel. + +```yaml +Type: PowerAutomateFlowRunPipeBind +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Force +Specifying the Force parameter will skip the confirmation question. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) + diff --git a/src/Commands/PowerPlatform/PowerAutomate/StopFlowRun.cs b/src/Commands/PowerPlatform/PowerAutomate/StopFlowRun.cs new file mode 100644 index 000000000..29c7411fa --- /dev/null +++ b/src/Commands/PowerPlatform/PowerAutomate/StopFlowRun.cs @@ -0,0 +1,52 @@ +using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Base.PipeBinds; +using System.Management.Automation; +using PnP.PowerShell.Commands.Utilities.REST; +using Resources = PnP.PowerShell.Commands.Properties.Resources; + +namespace PnP.PowerShell.Commands.PowerPlatform.PowerAutomate +{ + [Cmdlet(VerbsLifecycle.Stop, "PnPFlowRun")] + [RequiredMinimalApiPermissions("https://management.azure.com/.default")] + public class StopFlowRun : PnPGraphCmdlet + { + [Parameter(Mandatory = true)] + public PowerPlatformEnvironmentPipeBind Environment; + + [Parameter(Mandatory = true)] + public PowerAutomateFlowPipeBind Flow; + + [Parameter(Mandatory = true)] + public PowerAutomateFlowRunPipeBind Identity; + + [Parameter(Mandatory = false)] + public SwitchParameter Force; + + protected override void ExecuteCmdlet() + { + var environmentName = Environment.GetName(); + if (string.IsNullOrEmpty(environmentName)) + { + throw new PSArgumentException("Environment not found."); + } + + var flowName = Flow.GetName(); + if (string.IsNullOrEmpty(flowName)) + { + throw new PSArgumentException("Flow not found."); + } + + var flowRunName = Identity.GetName(); + if (string.IsNullOrEmpty(flowRunName)) + { + throw new PSArgumentException("Flow run not found."); + } + + if (Force || ShouldContinue($"Stop flow run with name '{flowRunName}'?", Resources.Confirm)) + { + RestHelper.PostAsync(HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple/environments/{environmentName}/flows/{flowName}/runs/{flowRunName}/cancel?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); + } + } + } +} From 3c39ecb21c51f05f325a95726eea6d002b3e3fe2 Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Wed, 11 May 2022 20:54:58 +0200 Subject: [PATCH 183/458] New cmdlet: Remove-PnPTeamsChannelUser (#1840) * Implemented Stop-PnPFlowRun * Imeplemented Remove-PnPTeamsChannelUser * Remove wrong files --- documentation/Remove-PnPTeamsChannelUser.md | 113 +++++++++++++++++++ src/Commands/Teams/AddTeamsChannelUser.cs | 2 +- src/Commands/Teams/RemoveTeamsChannelUser.cs | 63 +++++++++++ src/Commands/Utilities/TeamsUtility.cs | 74 ++++++++---- 4 files changed, 227 insertions(+), 25 deletions(-) create mode 100644 documentation/Remove-PnPTeamsChannelUser.md create mode 100644 src/Commands/Teams/RemoveTeamsChannelUser.cs diff --git a/documentation/Remove-PnPTeamsChannelUser.md b/documentation/Remove-PnPTeamsChannelUser.md new file mode 100644 index 000000000..d2bfcab7c --- /dev/null +++ b/documentation/Remove-PnPTeamsChannelUser.md @@ -0,0 +1,113 @@ +--- +Module Name: PnP.PowerShell +title: Remove-PnPTeamsChannelUser +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Remove-PnPTeamsChannelUser.html +--- + +# Remove-PnPTeamsChannelUser + +## SYNOPSIS + +**Required Permissions** + + * Microsoft Graph API: ChannelMember.ReadWrite.All + +Removes a specified user of a specified Microsoft Teams private Channel. + +## SYNTAX + +```powershell +Remove-PnPTeamsChannelUser -Team -Channel -Identity [-Force] + [] +``` + +## DESCRIPTION + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Remove-PnPTeamsChannelUser -Team "My Team" -Channel "My Channel" -Identity MCMjMiMjMDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAwIyMxOTowMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMEB0aHJlYWQuc2t5cGUjIzAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDAwMA== +``` + +Removes the user with specific membership ID from the specified Teams channel. + +### EXAMPLE 2 + +```powershell +Remove-PnPTeamsChannelUser -Team "My Team" -Channel "My Channel" -Identity 00000000-0000-0000-0000-000000000000 +``` + +Removes the user with ID "00000000-0000-0000-0000-000000000000" from the specified Teams channel. + +### EXAMPLE 3 + +```powershell +Remove-PnPTeamsChannelUser -Team "My Team" -Channel "My Channel" -Identity john.doe@contoso.com -Force +``` + +Removes the user "john.doe@contoso.com" from the specified Teams channel without confirmation prompt. + +## PARAMETERS + +### -Identity +Specify membership id, UPN or user ID of the channel member. + +```yaml +Type: TeamsChannelMemberPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Channel +Specify id or name of the channel to use. + +```yaml +Type: TeamsChannelPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Team +Specify the group id, mailNickname or display name of the team to use. + +```yaml +Type: TeamsTeamPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Force +Specifying the Force parameter will skip the confirmation question. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) diff --git a/src/Commands/Teams/AddTeamsChannelUser.cs b/src/Commands/Teams/AddTeamsChannelUser.cs index 65d5c16be..3cb2f2457 100644 --- a/src/Commands/Teams/AddTeamsChannelUser.cs +++ b/src/Commands/Teams/AddTeamsChannelUser.cs @@ -40,7 +40,7 @@ protected override void ExecuteCmdlet() try { - TeamsUtility.AddChannelUserAsync(HttpClient, AccessToken, groupId, channelId, User, Role).GetAwaiter().GetResult(); + TeamsUtility.AddChannelMemberAsync(HttpClient, AccessToken, groupId, channelId, User, Role).GetAwaiter().GetResult(); } catch (GraphException ex) { diff --git a/src/Commands/Teams/RemoveTeamsChannelUser.cs b/src/Commands/Teams/RemoveTeamsChannelUser.cs new file mode 100644 index 000000000..31a35c3e4 --- /dev/null +++ b/src/Commands/Teams/RemoveTeamsChannelUser.cs @@ -0,0 +1,63 @@ +using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Properties; +using PnP.PowerShell.Commands.Utilities; +using PnP.PowerShell.Commands.Utilities.REST; +using System.Management.Automation; + +namespace PnP.PowerShell.Commands.Teams +{ + [Cmdlet(VerbsCommon.Remove, "PnPTeamsChannelUser")] + [RequiredMinimalApiPermissions("ChannelMember.ReadWrite.All")] + public class RemoveTeamsChannelUser : PnPGraphCmdlet + { + [Parameter(Mandatory = true)] + public TeamsTeamPipeBind Team; + + [Parameter(Mandatory = true)] + public TeamsChannelPipeBind Channel; + + [Parameter(Mandatory = true)] + public TeamsChannelMemberPipeBind Identity; + + [Parameter(Mandatory = false)] + public SwitchParameter Force; + + protected override void ExecuteCmdlet() + { + var groupId = Team.GetGroupId(HttpClient, AccessToken); + if (string.IsNullOrEmpty(groupId)) + { + throw new PSArgumentException("Group not found"); + } + + var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + if (string.IsNullOrEmpty(channelId)) + { + throw new PSArgumentException("Channel not found in the specified team"); + } + + var memberId = Identity.GetIdAsync(HttpClient, AccessToken, groupId, channelId).GetAwaiter().GetResult(); + if (string.IsNullOrEmpty(memberId)) + { + throw new PSArgumentException("User was not found in the specified Teams channel"); + } + + if (Force || ShouldContinue("Remove specified member from the Microsoft Teams channel?", Resources.Confirm)) + { + var response = TeamsUtility.DeleteChannelMemberAsync(HttpClient, AccessToken, groupId, channelId, memberId).GetAwaiter().GetResult(); + + if (!response.IsSuccessStatusCode) + { + if (GraphHelper.TryGetGraphException(response, out var ex) && !string.IsNullOrEmpty(ex.Error.Message)) + { + throw new PSInvalidOperationException(ex.Error.Message); + } + + throw new PSInvalidOperationException("Failed to remove user from channel."); + } + } + } + } +} diff --git a/src/Commands/Utilities/TeamsUtility.cs b/src/Commands/Utilities/TeamsUtility.cs index dbf722d96..1b095d556 100644 --- a/src/Commands/Utilities/TeamsUtility.cs +++ b/src/Commands/Utilities/TeamsUtility.cs @@ -610,6 +610,39 @@ public static async Task AddChannelAsync(string accessToken, HttpCl } } + public static async Task PostMessageAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, TeamChannelMessage message) + { + await GraphHelper.PostAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/messages", message, accessToken); + } + + public static async Task> GetMessagesAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, bool includeDeleted = false) + { + List messages = new List(); + var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"beta/teams/{groupId}/channels/{channelId}/messages", accessToken); + messages.AddRange(collection); + + if (includeDeleted) + { + return messages; + } + else + { + return messages.Where(m => !m.DeletedDateTime.HasValue).ToList(); + } + } + + public static async Task UpdateChannelAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, TeamChannel channel) + { + return await GraphHelper.PatchAsync(httpClient, accessToken, $"beta/teams/{groupId}/channels/{channelId}", channel); + } + #endregion + + #region Channel member + + /// + /// Get specific memberbership of user who has access to a certain Microsoft Teams channel. + /// + /// User channel membership. public static async Task GetChannelMemberAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string membershipId) { // Currently the Graph request to get a membership by id fails (v1.0/teams/{groupId}/channels/{channelId}/members/{membershipId}). @@ -619,6 +652,10 @@ public static async Task GetChannelMemberAsync(HttpClient htt return memberships.FirstOrDefault(m => membershipId.Equals(m.Id)); } + /// + /// Get list of all memberships of a certain Microsoft Teams channel. + /// + /// List of memberships. public static async Task> GetChannelMembersAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string role = null) { var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/members", accessToken); @@ -632,7 +669,12 @@ public static async Task> GetChannelMembersAsync( return collection; } - public static async Task AddChannelUserAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string upn, string role) + /// + /// Add specified member to a specified Microsoft Teams channel with a certain role. + /// + /// User role, valid values: Owner, Member + /// Added membership. + public static async Task AddChannelMemberAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string upn, string role) { var channelMember = new TeamChannelMember { @@ -640,37 +682,21 @@ public static async Task AddChannelUserAsync(HttpClient httpC }; // The role for the user. Must be owner or empty. - if (role.Equals("Owner")) + if (role.Equals("owner", StringComparison.OrdinalIgnoreCase)) channelMember.Roles.Add("owner"); return await GraphHelper.PostAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/members", channelMember, accessToken); } - public static async Task PostMessageAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, TeamChannelMessage message) + /// + /// Remove specified member of a specified Microsoft Teams channel. + /// + /// True when removal succeeded, else false. + public static async Task DeleteChannelMemberAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string membershipId) { - await GraphHelper.PostAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/messages", message, accessToken); + return await GraphHelper.DeleteAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/members/{membershipId}", accessToken); } - public static async Task> GetMessagesAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, bool includeDeleted = false) - { - List messages = new List(); - var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"beta/teams/{groupId}/channels/{channelId}/messages", accessToken); - messages.AddRange(collection); - - if (includeDeleted) - { - return messages; - } - else - { - return messages.Where(m => !m.DeletedDateTime.HasValue).ToList(); - } - } - - public static async Task UpdateChannelAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, TeamChannel channel) - { - return await GraphHelper.PatchAsync(httpClient, accessToken, $"beta/teams/{groupId}/channels/{channelId}", channel); - } #endregion #region Tabs From b95ece0ea596b5309be405b00604e432fad7ac75 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Wed, 11 May 2022 22:02:38 +0300 Subject: [PATCH 184/458] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 718d9f487..6265a2c7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Accept` parameter to `Invoke-PnPSPRestMethod` cmdlet which if specified will pass the Accept HTTP request header. [#1795](https://github.com/pnp/powershell/pull/1795) - Added `Get-PnPFlowRun` cmdlet to retrieve a specific run, or all runs from a specific Power Automate flow. [#1819](https://github.com/pnp/powershell/pull/1819) - Added `Invoke-PnPGraphMethod` cmdlet to invoke generic Microsoft Graph API Methods. [#1820](https://github.com/pnp/powershell/pull/1820) +- Added `Stop-PnPFlowRun` cmdlet to stop/cancel a specific Power Automate flow run. [#1838](https://github.com/pnp/powershell/pull/1838) +- Added `Remove-PnPTeamsChannelUser` cmdlet to remove a user from a private channel. [#1840](https://github.com/pnp/powershell/pull/1840) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) From f2c41de2fbc6defce6e6cc304b184c3c2a776b8c Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 12 May 2022 03:35:18 +0000 Subject: [PATCH 185/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index cff0236ba..538e6a0ee 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -f20b02860af2dea43d156048f43fef5421322e02 \ No newline at end of file +45a0db5dddfc6f014928f942cbe2612729187d02 \ No newline at end of file diff --git a/version.txt b/version.txt index bd5f5d009..23aa90704 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.25 \ No newline at end of file +1.10.26 \ No newline at end of file From 91ccc10caccf23b4e93613e560aea11bf5909fca Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 12 May 2022 12:44:28 +0200 Subject: [PATCH 186/458] removing arm image --- .github/workflows/release.yml | 6 +----- docker/Publish-UnpublishedImage.ps1 | 7 +------ 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5e72fcb1a..8e3ccc3b6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,12 +18,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Set up QEMU - uses: docker/setup-qemu-action@v1 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - name: Build and Publish All shell: pwsh run: | $securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force - ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword "root" $false "alpine-3.14,arm32v7-ubuntu-bionic" + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword "root" $false "alpine-3.14" diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 index 862c19ba9..5995002a8 100644 --- a/docker/Publish-UnpublishedImage.ps1 +++ b/docker/Publish-UnpublishedImage.ps1 @@ -47,12 +47,7 @@ $moduleVersions | % { $imageVersion = "$moduleVersion-$baseImageSuffix"; Write-Host "Checking $imageVersion" if ( !( $publishedImageVersions -contains $imageVersion ) ) { - if ( $baseImageSuffix -eq "arm32v7-ubuntu-bionic" ) { - Write-Host "Using buildx --platform linux/arm/v7"; - docker buildx build --load --platform linux/arm/v7 --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "INSTALL_USER=$DOCKER_INSTALL_USER" --build-arg "SKIP_PUBLISHER_CHECK=$SKIP_PUBLISHER_CHECK" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; - } else { - docker build --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "INSTALL_USER=$DOCKER_INSTALL_USER" --build-arg "SKIP_PUBLISHER_CHECK=$SKIP_PUBLISHER_CHECK" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; - } + docker build --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "INSTALL_USER=$DOCKER_INSTALL_USER" --build-arg "SKIP_PUBLISHER_CHECK=$SKIP_PUBLISHER_CHECK" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; docker image tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; $plainStringPassword = [System.Net.NetworkCredential]::new("", $DOCKER_PASSWORD).Password; docker login -u $DOCKER_USERNAME -p "$plainStringPassword"; From f80ce939209554c60246778a7120c2a486a44d99 Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 12 May 2022 14:27:10 +0200 Subject: [PATCH 187/458] keeping 3 platforms --- .github/workflows/release.yml | 15 ++++++++++++--- docker/Publish-UnpublishedImage.ps1 | 2 +- docker/README.md | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8e3ccc3b6..7f22680de 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,15 +5,24 @@ on: schedule: - cron: '30 3 * * *' jobs: - publish-docker-windows: - runs-on: windows-latest + publish-docker-windows-2022: + runs-on: windows-2022 steps: - uses: actions/checkout@v2 - name: Build and Publish All shell: pwsh run: | $securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force - ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword "ContainerAdministrator" $true "nanoserver-1809,nanoserver-20h2,nanoserver-ltsc2022" + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword "ContainerAdministrator" $true "nanoserver-ltsc2022" + publish-docker-windows-2019: + runs-on: windows-2019 + steps: + - uses: actions/checkout@v2 + - name: Build and Publish All + shell: pwsh + run: | + $securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword "ContainerAdministrator" $true "nanoserver-1809" publish-docker-linux: runs-on: ubuntu-latest steps: diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 index 5995002a8..b9a2520bd 100644 --- a/docker/Publish-UnpublishedImage.ps1 +++ b/docker/Publish-UnpublishedImage.ps1 @@ -33,7 +33,7 @@ Param( Mandatory = $false, ValueFromPipeline = $false)] [String] - $DOCKER_IMAGE_SUFFIX_ARRAY = "nanoserver-1809,nanoserver-20h2,nanoserver-ltsc2022" + $DOCKER_IMAGE_SUFFIX_ARRAY = "nanoserver-ltsc2022" ) $publishedImageVersions = (Invoke-RestMethod https://registry.hub.docker.com/v2/repositories/$DOCKER_USERNAME/$DOCKER_IMAGE_NAME/tags?page_size=10240).results | % { $_.name diff --git a/docker/README.md b/docker/README.md index df53f4033..a675a6db8 100644 --- a/docker/README.md +++ b/docker/README.md @@ -6,7 +6,7 @@ ```powershell $securedPassword = ConvertTo-SecureString $DOCKER_PASSWORD -AsPlainText -Force -./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME powershell $securedPassword "ContainerAdministrator" $true "nanoserver-1809,nanoserver-20h2,nanoserver-ltsc2022" +./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME powershell $securedPassword "ContainerAdministrator" $true "nanoserver-1809" ``` # Publish manually in Linux From cf2abc5d34894b9730edbe23d309e9124524f990 Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 12 May 2022 14:58:26 +0200 Subject: [PATCH 188/458] publishing latest for only linux --- docker/Publish-UnpublishedImage.ps1 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 index b9a2520bd..c75b3fc02 100644 --- a/docker/Publish-UnpublishedImage.ps1 +++ b/docker/Publish-UnpublishedImage.ps1 @@ -48,11 +48,13 @@ $moduleVersions | % { Write-Host "Checking $imageVersion" if ( !( $publishedImageVersions -contains $imageVersion ) ) { docker build --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "INSTALL_USER=$DOCKER_INSTALL_USER" --build-arg "SKIP_PUBLISHER_CHECK=$SKIP_PUBLISHER_CHECK" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; - docker image tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; $plainStringPassword = [System.Net.NetworkCredential]::new("", $DOCKER_PASSWORD).Password; docker login -u $DOCKER_USERNAME -p "$plainStringPassword"; docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; - docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; + if ( $baseImageSuffix -eq "alpine-3.14") { + docker image tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; + docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; + } } } } From 6478344a0e0407631c4cc279bbf65106ba432470 Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 12 May 2022 16:17:51 +0200 Subject: [PATCH 189/458] nightly release for 3 platforms --- .github/workflows/nightlyrelease.yml | 51 ++++++++++++++++------------ 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index f6c9493ee..2e99c4718 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -20,26 +20,35 @@ jobs: with: ref: dev token: ${{ secrets.PAT }} - - name: Build and Publish Module - env: - POWERSHELLGALLERY_API_KEY: ${{ secrets.POWERSHELLGALLERY_API_KEY }} - shell: pwsh + publish-docker-windows-2022: + runs-on: windows-2022 + needs: [ build ] + steps: + - uses: actions/checkout@v2 + - name: Build an image run: | - ./build/Build-Nightly.ps1 - - name: Set variables - shell: pwsh + VERSION=$(cat ./version.txt)-nightly + docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-ltsc2022" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-ltsc2022; + - name: Push the image run: | - $version = Get-Content version.txt -raw - "BUILDVERSION=$version" | Out-File $env:GITHUB_ENV -Encoding utf8 -Append - - name: Add & Commit - uses: EndBug/add-and-commit@v6 - with: - message: 'Nightly publish to PowerShell Gallery' - tag: '${{env.BUILDVERSION}}-nightly --force' - push: true - branch: dev - token: ${{ secrets.PAT }} - publish-docker: + VERSION=$(cat ./version.txt)-nightly + docker login -u ${{ secrets.DOCKER_USERNAME }} -p '${{ secrets.DOCKER_PASSWORD }}' + docker push ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-ltsc2022 + publish-docker-windows-2019: + runs-on: windows-2019 + needs: [ build ] + steps: + - uses: actions/checkout@v2 + - name: Build an image + run: | + VERSION=$(cat ./version.txt)-nightly + docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-1809" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-1809; + - name: Push the image + run: | + VERSION=$(cat ./version.txt)-nightly + docker login -u ${{ secrets.DOCKER_USERNAME }} -p '${{ secrets.DOCKER_PASSWORD }}' + docker push ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-1809 + publish-docker-linux: runs-on: ubuntu-latest needs: [ build ] steps: @@ -47,14 +56,14 @@ jobs: - name: Build an image run: | VERSION=$(cat ./version.txt)-nightly - docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell.dockerFile --tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION + docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=alpine-3.14" --build-arg "INSTALL_USER=root" --build-arg "SKIP_PUBLISHER_CHECK=False" ./docker -f ./docker/pnppowershell.dockerFile --tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-alpine-3.14; - name: Tag the image run: | VERSION=$(cat ./version.txt)-nightly - docker image tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION ${{ secrets.DOCKER_USERNAME }}/powershell:nightly + docker image tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-alpine-3.14 ${{ secrets.DOCKER_USERNAME }}/powershell:nightly - name: Push the image run: | VERSION=$(cat ./version.txt)-nightly docker login -u ${{ secrets.DOCKER_USERNAME }} -p '${{ secrets.DOCKER_PASSWORD }}' - docker push ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION + docker push ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-alpine-3.14 docker push ${{ secrets.DOCKER_USERNAME }}/powershell:nightly From f531593d9b3e75bd7708837cbd80cc497788c458 Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 12 May 2022 16:23:50 +0200 Subject: [PATCH 190/458] replacing with a fake job --- .github/workflows/nightlyrelease.yml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index 2e99c4718..2c0320e0c 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -10,16 +10,8 @@ jobs: runs-on: windows-latest steps: - - name: Setup .NET Core - uses: actions/setup-dotnet@v1 - with: - dotnet-version: | - 3.1.301 - 5.x - - uses: actions/checkout@v2 - with: - ref: dev - token: ${{ secrets.PAT }} + - shell: pwsh + run: Write-Host "hi" publish-docker-windows-2022: runs-on: windows-2022 needs: [ build ] From 051a63c1511d3389a8724361ffbd42a137aed239 Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 12 May 2022 16:29:26 +0200 Subject: [PATCH 191/458] using PS syntax for Windows builder --- .github/workflows/nightlyrelease.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index 2c0320e0c..3828ac238 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -19,11 +19,11 @@ jobs: - uses: actions/checkout@v2 - name: Build an image run: | - VERSION=$(cat ./version.txt)-nightly + $VERSION=$(cat ./version.txt)-nightly docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-ltsc2022" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-ltsc2022; - name: Push the image run: | - VERSION=$(cat ./version.txt)-nightly + $VERSION=$(cat ./version.txt)-nightly docker login -u ${{ secrets.DOCKER_USERNAME }} -p '${{ secrets.DOCKER_PASSWORD }}' docker push ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-ltsc2022 publish-docker-windows-2019: @@ -33,11 +33,11 @@ jobs: - uses: actions/checkout@v2 - name: Build an image run: | - VERSION=$(cat ./version.txt)-nightly + $VERSION=$(cat ./version.txt)-nightly docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-1809" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-1809; - name: Push the image run: | - VERSION=$(cat ./version.txt)-nightly + $VERSION=$(cat ./version.txt)-nightly docker login -u ${{ secrets.DOCKER_USERNAME }} -p '${{ secrets.DOCKER_PASSWORD }}' docker push ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-1809 publish-docker-linux: From a78076b9088ecc29afeaa28cc4a12d958bb43cef Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 12 May 2022 16:34:15 +0200 Subject: [PATCH 192/458] fixing more PS syntax in pipeline --- .github/workflows/nightlyrelease.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index 3828ac238..94140abee 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -19,13 +19,13 @@ jobs: - uses: actions/checkout@v2 - name: Build an image run: | - $VERSION=$(cat ./version.txt)-nightly - docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-ltsc2022" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-ltsc2022; + $VERSION="$(cat ./version.txt)-nightly" + docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-ltsc2022" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag "${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-ltsc2022"; - name: Push the image run: | - $VERSION=$(cat ./version.txt)-nightly + $VERSION="$(cat ./version.txt)-nightly" docker login -u ${{ secrets.DOCKER_USERNAME }} -p '${{ secrets.DOCKER_PASSWORD }}' - docker push ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-ltsc2022 + docker push "${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-ltsc2022" publish-docker-windows-2019: runs-on: windows-2019 needs: [ build ] @@ -33,13 +33,13 @@ jobs: - uses: actions/checkout@v2 - name: Build an image run: | - $VERSION=$(cat ./version.txt)-nightly - docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-1809" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-1809; + $VERSION="$(cat ./version.txt)-nightly" + docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-1809" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag "${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-1809"; - name: Push the image run: | - $VERSION=$(cat ./version.txt)-nightly + $VERSION="$(cat ./version.txt)-nightly" docker login -u ${{ secrets.DOCKER_USERNAME }} -p '${{ secrets.DOCKER_PASSWORD }}' - docker push ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-1809 + docker push "${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-1809" publish-docker-linux: runs-on: ubuntu-latest needs: [ build ] From cb7c5b18afd6346d992b1ef431e401e08bbf531e Mon Sep 17 00:00:00 2001 From: Aleks Date: Thu, 12 May 2022 16:41:08 +0200 Subject: [PATCH 193/458] returning back the module publishing steps --- .github/workflows/nightlyrelease.yml | 31 ++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index 94140abee..f647794d7 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -10,8 +10,35 @@ jobs: runs-on: windows-latest steps: - - shell: pwsh - run: Write-Host "hi" + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: | + 3.1.301 + 5.x + - uses: actions/checkout@v2 + with: + ref: dev + token: ${{ secrets.PAT }} + - name: Build and Publish Module + env: + POWERSHELLGALLERY_API_KEY: ${{ secrets.POWERSHELLGALLERY_API_KEY }} + shell: pwsh + run: | + ./build/Build-Nightly.ps1 + - name: Set variables + shell: pwsh + run: | + $version = Get-Content version.txt -raw + "BUILDVERSION=$version" | Out-File $env:GITHUB_ENV -Encoding utf8 -Append + - name: Add & Commit + uses: EndBug/add-and-commit@v6 + with: + message: 'Nightly publish to PowerShell Gallery' + tag: '${{env.BUILDVERSION}}-nightly --force' + push: true + branch: dev + token: ${{ secrets.PAT }} publish-docker-windows-2022: runs-on: windows-2022 needs: [ build ] From bd4a0c7c2b27e7d35b15fd2787346054cf9a9e81 Mon Sep 17 00:00:00 2001 From: Aleksandr SaPozhkov Date: Thu, 12 May 2022 21:31:29 +0200 Subject: [PATCH 194/458] installing module for all the users * outputting module list after installing * installing module for all the users * installing for all users in Linux --- docker/pnppowershell.dockerFile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docker/pnppowershell.dockerFile b/docker/pnppowershell.dockerFile index 28fe6302b..e98fcbb0f 100644 --- a/docker/pnppowershell.dockerFile +++ b/docker/pnppowershell.dockerFile @@ -9,7 +9,8 @@ ARG PNP_MODULE_VERSION ARG SKIP_PUBLISHER_CHECK=TRUE RUN if ( $env:SKIP_PUBLISHER_CHECK -eq $true ) { \ Write-Host "SKIP_PUBLISHER_CHECK"; \ - Install-Module -Name PnP.PowerShell -RequiredVersion $env:PNP_MODULE_VERSION -Force -AllowPrerelease -SkipPublisherCheck; \ + Install-Module -Name PnP.PowerShell -RequiredVersion $env:PNP_MODULE_VERSION -Force -AllowPrerelease -Scope AllUsers -SkipPublisherCheck; \ } else { \ - Install-Module -Name PnP.PowerShell -RequiredVersion $env:PNP_MODULE_VERSION -Force -AllowPrerelease; \ + Install-Module -Name PnP.PowerShell -RequiredVersion $env:PNP_MODULE_VERSION -Force -AllowPrerelease -Scope AllUsers; \ } + \ No newline at end of file From d1978bbc8d752d7ec650033a448357065b78c336 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 12 May 2022 23:17:58 +0300 Subject: [PATCH 195/458] #1803 - added timezone parameter to New-PnPSite --- CHANGELOG.md | 1 + documentation/New-PnPSite.md | 28 ++++++++++++++++++--- src/Commands/Admin/NewSite.cs | 46 ++++++++++++++++++++++++++++++++--- 3 files changed, 69 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 718d9f487..3d2d6fb84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Accept` parameter to `Invoke-PnPSPRestMethod` cmdlet which if specified will pass the Accept HTTP request header. [#1795](https://github.com/pnp/powershell/pull/1795) - Added `Get-PnPFlowRun` cmdlet to retrieve a specific run, or all runs from a specific Power Automate flow. [#1819](https://github.com/pnp/powershell/pull/1819) - Added `Invoke-PnPGraphMethod` cmdlet to invoke generic Microsoft Graph API Methods. [#1820](https://github.com/pnp/powershell/pull/1820) +- Added `TimeZone` parameter to `New-PnPSite` cmdlet which allows setting of the site collection in the specified timezone. ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) diff --git a/documentation/New-PnPSite.md b/documentation/New-PnPSite.md index 7622b3c98..01fce3993 100644 --- a/documentation/New-PnPSite.md +++ b/documentation/New-PnPSite.md @@ -16,18 +16,18 @@ Creates a communication site, Microsoft 365 group-connected team site or Modern ### TeamSite ```powershell -New-PnPSite -Type TeamSite -Title -Alias [-Description ] [-Classification ] [-IsPublic] [-Lcid ] [-Owners ] [-PreferredDataLocation ] [-SensitivityLabel ] [-HubSiteId ] [-SiteAlias ] [-Wait] [-Connection ] +New-PnPSite -Type TeamSite -Title -Alias [-Description ] [-Classification ] [-IsPublic] [-Lcid ] [-Owners ] [-PreferredDataLocation ] [-SensitivityLabel ] [-HubSiteId ] [-SiteAlias ] [-TimeZone ] [-Wait] [-Connection ] [] ``` ### CommunicationSite ```powershell -New-PnPSite -Type CommunicationSite -Title -Url [-HubSiteId ] [-Classification ] [-SiteDesign ] [-SiteDesignId ] [-Lcid ] [-Owner ] [-PreferredDataLocation ] [-SensitivityLabel ] +New-PnPSite -Type CommunicationSite -Title -Url [-HubSiteId ] [-Classification ] [-SiteDesign ] [-SiteDesignId ] [-Lcid ] [-Owner ] [-PreferredDataLocation ] [-SensitivityLabel ] [-TimeZone ] ``` ### TeamSiteWithoutMicrosoft365Group ```powershell -New-PnPSite -Type TeamSiteWithoutMicrosoft365Group -Title -Url [-HubSiteId ] [-Classification ] [-SiteDesignId ] [-Lcid ] [-Owner ] [-PreferredDataLocation ] [-SensitivityLabel ] +New-PnPSite -Type TeamSiteWithoutMicrosoft365Group -Title -Url [-HubSiteId ] [-Classification ] [-SiteDesignId ] [-Lcid ] [-Owner ] [-PreferredDataLocation ] [-SensitivityLabel ] [-TimeZone ] ``` ## DESCRIPTION @@ -140,6 +140,13 @@ New-PnPSite -Type TeamSiteWithoutMicrosoft365Group -Title Contoso -Url https://t This will create a new Modern team site collection not connected to M365 group with the title 'Contoso' and the url 'https://tenant.sharepoint.com/sites/contoso' and sets the default language to Italian (LCID 1040). +### EXAMPLE 16 +```powershell +New-PnPSite -Type TeamSite -TimeZone UTCPLUS0200_HELSINKI_KYIV_RIGA_SOFIA_TALLINN_VILNIUS -Title "Contoso" -Alias "Contoso" +``` + +This will create a new Modern team site collection connected to M365 group with the title 'Contoso' and the url 'https://tenant.sharepoint.com/sites/contoso' and sets the timezone to UTC + 2 which is the Eastern European time zone. + ## PARAMETERS ### -Alias @@ -383,6 +390,21 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -TimeZone +Specifies the timezone of the site to create. +To get the full list of timezone that you can select, you can visit [https://docs.microsoft.com/en-us/dotnet/api/officedevpnp.core.enums.timezone](https://docs.microsoft.com/en-us/dotnet/api/officedevpnp.core.enums.timezone) + +```yaml +Type: Framework.Enums.TimeZone +Parameter Sets: CommunicationSite, TeamSiteWithoutMicrosoft365Group, TeamSite + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Type Specifies with type of site to create. diff --git a/src/Commands/Admin/NewSite.cs b/src/Commands/Admin/NewSite.cs index a43764a18..ae1132275 100644 --- a/src/Commands/Admin/NewSite.cs +++ b/src/Commands/Admin/NewSite.cs @@ -6,6 +6,7 @@ using System; using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Attributes; +using System.Linq; namespace PnP.PowerShell.Commands { @@ -30,6 +31,9 @@ public class NewSite : PnPSharePointCmdlet, IDynamicParameters [Parameter(Mandatory = false)] public SwitchParameter Wait; + + [Parameter(Mandatory = false)] + public Framework.Enums.TimeZone TimeZone; public object GetDynamicParameters() { @@ -89,7 +93,19 @@ protected override void ExecuteCmdlet() creationInformation.SensitivityLabel = _communicationSiteParameters.SensitivityLabel; var returnedContext = Framework.Sites.SiteCollection.Create(ClientContext, creationInformation, noWait: !Wait); - WriteObject(returnedContext.Url); + if (ParameterSpecified(nameof(TimeZone))) + { + returnedContext.Web.EnsureProperties(w => w.RegionalSettings, w => w.RegionalSettings.TimeZones); + returnedContext.Web.RegionalSettings.TimeZone = returnedContext.Web.RegionalSettings.TimeZones.Where(t => t.Id == ((int)TimeZone)).First(); + returnedContext.Web.RegionalSettings.Update(); + returnedContext.ExecuteQueryRetry(); + returnedContext.Site.EnsureProperty(s => s.Url); + WriteObject(returnedContext.Site.Url); + } + else + { + WriteObject(returnedContext.Url); + } } else if (Type == SiteType.TeamSite) { @@ -121,7 +137,19 @@ protected override void ExecuteCmdlet() if (ClientContext.GetContextSettings()?.Type != Framework.Utilities.Context.ClientContextType.SharePointACSAppOnly) { var returnedContext = Framework.Sites.SiteCollection.Create(ClientContext, creationInformation, noWait: !Wait, graphAccessToken: GraphAccessToken); - WriteObject(returnedContext.Url); + if (ParameterSpecified(nameof(TimeZone))) + { + returnedContext.Web.EnsureProperties(w => w.RegionalSettings, w => w.RegionalSettings.TimeZones); + returnedContext.Web.RegionalSettings.TimeZone = returnedContext.Web.RegionalSettings.TimeZones.Where(t => t.Id == ((int)TimeZone)).First(); + returnedContext.Web.RegionalSettings.Update(); + returnedContext.ExecuteQueryRetry(); + returnedContext.Site.EnsureProperty(s => s.Url); + WriteObject(returnedContext.Site.Url); + } + else + { + WriteObject(returnedContext.Url); + } } else { @@ -154,7 +182,19 @@ protected override void ExecuteCmdlet() creationInformation.SensitivityLabel = _teamSiteWithoutMicrosoft365GroupParameters.SensitivityLabel; var returnedContext = Framework.Sites.SiteCollection.Create(ClientContext, creationInformation, noWait: !Wait); - WriteObject(returnedContext.Url); + if (ParameterSpecified(nameof(TimeZone))) + { + returnedContext.Web.EnsureProperties(w => w.RegionalSettings, w => w.RegionalSettings.TimeZones); + returnedContext.Web.RegionalSettings.TimeZone = returnedContext.Web.RegionalSettings.TimeZones.Where(t => t.Id == ((int)TimeZone)).First(); + returnedContext.Web.RegionalSettings.Update(); + returnedContext.ExecuteQueryRetry(); + returnedContext.Site.EnsureProperty(s => s.Url); + WriteObject(returnedContext.Site.Url); + } + else + { + WriteObject(returnedContext.Url); + } } } From 73967df21c298e1f141a8db5eed800d4ad0d6863 Mon Sep 17 00:00:00 2001 From: Aleks Date: Fri, 13 May 2022 00:22:45 +0200 Subject: [PATCH 196/458] removing arm image from docu --- docker/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/README.md b/docker/README.md index a675a6db8..5affcb5f4 100644 --- a/docker/README.md +++ b/docker/README.md @@ -17,7 +17,7 @@ $securedPassword = ConvertTo-SecureString $DOCKER_PASSWORD -AsPlainText -Force ```powershell $securedPassword = ConvertTo-SecureString $DOCKER_PASSWORD -AsPlainText -Force -./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME powershell $securedPassword $false "root" "alpine-3.14,arm32v7-ubuntu-bionic" +./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME powershell $securedPassword $false "root" "alpine-3.14" ``` # Publish with prereleases manually From f3aa083f1d08ac287975a0b35a826e2a0a18157b Mon Sep 17 00:00:00 2001 From: Aleks Date: Fri, 13 May 2022 00:23:54 +0200 Subject: [PATCH 197/458] more user documentation --- docker/README.md | 34 +++++++++++++++++--- docker/hub.docker.md | 75 ++++++++++++++++++++++++++++++++++++++++++++ pages/index.md | 12 +++++++ 3 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 docker/hub.docker.md diff --git a/docker/README.md b/docker/README.md index 5affcb5f4..32dd74e1f 100644 --- a/docker/README.md +++ b/docker/README.md @@ -20,7 +20,31 @@ $securedPassword = ConvertTo-SecureString $DOCKER_PASSWORD -AsPlainText -Force ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME powershell $securedPassword $false "root" "alpine-3.14" ``` -# Publish with prereleases manually +# Publish with prereleases manually in Windows + +1. Set "DOCKER_USERNAME" and "DOCKER_PASSWORD" variables + +2. Run + +```PowerShell +$VERSION="$(cat ./version.txt)-nightly" +docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-ltsc2022" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag "$DOCKER_USERNAME/powershell:$VERSION-nanoserver-ltsc2022"; +$VERSION="$(cat ./version.txt)-nightly" +docker login -u $DOCKER_USERNAME -p "$DOCKER_PASSWORD" +docker push "$DOCKER_USERNAME/powershell:$VERSION-nanoserver-ltsc2022" +``` + +or + +```PowerShell +$VERSION="$(cat ./version.txt)-nightly" +docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-1809" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag "$DOCKER_USERNAME/powershell:$VERSION-nanoserver-1809"; +$VERSION="$(cat ./version.txt)-nightly" +docker login -u $DOCKER_USERNAME -p "$DOCKER_PASSWORD" +docker push "$DOCKER_USERNAME/powershell:$VERSION-nanoserver-1809" +``` + +# Publish with prereleases manually in Linux 1. Set "DOCKER_USERNAME" and "DOCKER_PASSWORD" variables @@ -28,10 +52,10 @@ $securedPassword = ConvertTo-SecureString $DOCKER_PASSWORD -AsPlainText -Force ```bash VERSION=$(cat ./version.txt)-nightly -docker build --build-arg "PNP_MODULE_VERSION=$VERSION" ./docker -f ./docker/pnppowershell-prerelease.dockerFile --tag $DOCKER_USERNAME/powershell:$VERSION -docker image tag $DOCKER_USERNAME/powershell:$VERSION $DOCKER_USERNAME/powershell:nightly -docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD -docker push $DOCKER_USERNAME/powershell:$VERSION +docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=alpine-3.14" --build-arg "INSTALL_USER=root" --build-arg "SKIP_PUBLISHER_CHECK=False" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/powershell:$VERSION-alpine-3.14; +docker image tag $DOCKER_USERNAME/powershell:$VERSION-alpine-3.14 $DOCKER_USERNAME/powershell:nightly +docker login -u $DOCKER_USERNAME -p "$DOCKER_PASSWORD" +docker push $DOCKER_USERNAME/powershell:$VERSION-alpine-3.14 docker push $DOCKER_USERNAME/powershell:nightly ``` diff --git a/docker/hub.docker.md b/docker/hub.docker.md new file mode 100644 index 000000000..9344d00f9 --- /dev/null +++ b/docker/hub.docker.md @@ -0,0 +1,75 @@ +# PnP.Powershell + +## Featured tags + +### Latest + +* latest: The latest stable image + + * alpine-3.14 + * `docker pull pnp/powershell` or `docker pull pnp/powershell:latest` + +### Nightly + +* nightly: The latest night image + + * alpine-3.14 + * `docker pull pnp/powershell:nightly` + +## About this image + +**PnP PowerShell** is a .NET Core 3.1 / .NET Framework 4.6.1 based PowerShell Module providing over 600 cmdlets that work with Microsoft 365 environments such as SharePoint Online, Microsoft Teams, Microsoft Project, Security & Compliance, Azure Active Directory, and more. + +## Usage examples + +### Windows-container + +Starting an isolated container with PnP.PowerShell module installed: + +``` +docker run --rm -it asapozhkov/powershell:1.10.0-nanoserver-1809 +``` + +Starting a PnP.PowerShell container with the current directory mounted: + +```PowerShell +docker run --rm -it -v ${PWD}:c:/app -w c:/app asapozhkov/powershell:1.10.0-nanoserver-1809 +``` + +### Linux-container + +Starting an isolated container with PnP.PowerShell module installed: + +``` +docker run --rm -it asapozhkov/powershell +``` + +Starting a PnP.PowerShell container with the current directory mounted: + +```bash +docker run --rm -it -v ${PWD}:/home -w /home asapozhkov/powershell +``` + +## Tag explanation + +Tags names mean the following: + +`(-nightly)-` + +Currently supported platforms: + +* nanoserver-ltsc2022 +* nanoserver-1809 +* alpine-3.14 + +Tag name examples: + +* 1.8.0-nanoserver-ltsc2022 +* 1.9.0-nanoserver-ltsc2022 +* 1.10.0-nanoserver-1809 +* 1.10.0-alpine-3.14 +* 1.10.26-nightly-nanoserver-ltsc2022 + +## Feedback + +* To give feedback for PnP.PowerShell or for how the images are built, file an issue at [PnP/PowerShell](https://github.com/pnp/powershell/issues/new/choose) diff --git a/pages/index.md b/pages/index.md index f104e492b..8a28887d1 100644 --- a/pages/index.md +++ b/pages/index.md @@ -19,6 +19,18 @@ To install a nightly build of PnP PowerShell: Install-Module -Name PnP.PowerShell -AllowPrerelease -SkipPublisherCheck -AllowClobber ``` +To use PnP.PowerShell in a Windows container: + +``` +docker run -it pnp/powershell:1.10.0-nanoserver-1809 +``` + +To use PnP.PowerShell in a Linux container: + +``` +docker run -it pnp/powershell +``` + See the [articles](/powershell/articles) section for more information on authentication and configuration. All [cmdlets](/powershell/cmdlets/Add-PnPAlert.html) have been documented too. # I've found a bug, where do I need to log an issue or create a PR From a2145529dcef45015fe499c8d73c698ec1667b9c Mon Sep 17 00:00:00 2001 From: Aleks Date: Fri, 13 May 2022 00:24:11 +0200 Subject: [PATCH 198/458] removed unused space in the dockerfile --- docker/pnppowershell.dockerFile | 1 - 1 file changed, 1 deletion(-) diff --git a/docker/pnppowershell.dockerFile b/docker/pnppowershell.dockerFile index e98fcbb0f..1c3144156 100644 --- a/docker/pnppowershell.dockerFile +++ b/docker/pnppowershell.dockerFile @@ -13,4 +13,3 @@ RUN if ( $env:SKIP_PUBLISHER_CHECK -eq $true ) { \ } else { \ Install-Module -Name PnP.PowerShell -RequiredVersion $env:PNP_MODULE_VERSION -Force -AllowPrerelease -Scope AllUsers; \ } - \ No newline at end of file From cb4240db5d6bc350e24d08f31b257f2e1564f266 Mon Sep 17 00:00:00 2001 From: Aleks Date: Fri, 13 May 2022 00:28:14 +0200 Subject: [PATCH 199/458] reference to the PS gallery for versions --- docker/hub.docker.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker/hub.docker.md b/docker/hub.docker.md index 9344d00f9..26f9e2ab7 100644 --- a/docker/hub.docker.md +++ b/docker/hub.docker.md @@ -70,6 +70,8 @@ Tag name examples: * 1.10.0-alpine-3.14 * 1.10.26-nightly-nanoserver-ltsc2022 +To find the version numbers please visit https://www.powershellgallery.com/packages/PnP.PowerShell + ## Feedback * To give feedback for PnP.PowerShell or for how the images are built, file an issue at [PnP/PowerShell](https://github.com/pnp/powershell/issues/new/choose) From 36959699734fd683e4c4d84b28c3d6e7ec846d80 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Mon, 16 May 2022 20:26:33 +0200 Subject: [PATCH 200/458] Adding missing documentation on using -Content with Add-PnPFile (#1867) Co-authored-by: = <=> --- documentation/Add-PnPFile.md | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/documentation/Add-PnPFile.md b/documentation/Add-PnPFile.md index f44d043bd..19d9b20ee 100644 --- a/documentation/Add-PnPFile.md +++ b/documentation/Add-PnPFile.md @@ -28,8 +28,15 @@ Add-PnPFile -Folder -FileName -Stream [-Check [-ContentType ] [-Connection ] [] ``` +### Create or update file from text +```powershell +Add-PnPFile -Folder -FileName -Content [-Checkout] [-CheckInComment ] + [-Approve] [-ApproveComment ] [-Publish] [-PublishComment ] [-UseWebDav] [-Values ] + [-ContentType ] [-Connection ] [] +``` + ## DESCRIPTION -This cmdlet uploads a local file or a file from a stream to the specified folder. +This cmdlet uploads a local file, file from a stream or plain text to the specified folder. ## EXAMPLES @@ -82,6 +89,13 @@ Add-PnPFile -Path sample.docx -Folder "Documents" -NewFileName "differentname.do This will upload a local file sample.docx to the Documents folder giving it the filename differentname.docx on SharePoint +### EXAMPLE 8 +```powershell +Add-PnPFile -FileName sample.txt -Folder "Shared Documents" -Content '{ "Test": "Value" }' +``` + +This will create a file sample.docx in the Documents library inserting the provided plain text into it. If a similarly file already exists at this location, its contents will be overwritten. + ## PARAMETERS ### -Approve @@ -279,6 +293,20 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Content +Content to add to the file to create or overwrite on SharePoint. It will blindly overwrite the contents of the file if it already exists. + +```yaml +Type: String +Parameter Sets: ASTEXT + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Values Use the internal names of the fields when specifying field names. @@ -331,10 +359,6 @@ Accept pipeline input: False Accept wildcard characters: False ``` - - ## RELATED LINKS -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - - +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file From 74847d386a9e7575076d9a451fe5f6853939be2d Mon Sep 17 00:00:00 2001 From: Jago Pauwels <40368126+jagopauwels@users.noreply.github.com> Date: Mon, 16 May 2022 20:26:58 +0200 Subject: [PATCH 201/458] Update New-PnPSite.md (#1859) When the parameter -IsPublic is not specified the default value is false, so the group will be Private. The current documentation incorrectly says it will be Public. --- documentation/New-PnPSite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/New-PnPSite.md b/documentation/New-PnPSite.md index 7622b3c98..9e3e946f2 100644 --- a/documentation/New-PnPSite.md +++ b/documentation/New-PnPSite.md @@ -226,7 +226,7 @@ Accept wildcard characters: False ``` ### -IsPublic -Identifies whether the corresponding Microsoft365 group type is Private or Public. If not specified, group is considered Public. +Identifies whether the corresponding Microsoft365 group type is Private or Public. If not specified, group is considered Private. Content in a Public group can be seen by anybody in the organization, and anybody in the organization is able to join the group. Content in a Private group can only be seen by the members of the group and people who want to join a private group have to be approved by a group owner. From 75a30894baae42585389365e478ab4a6cace4a00 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Mon, 16 May 2022 21:28:54 +0300 Subject: [PATCH 202/458] Update CHANGELOG.md Added contributor --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6265a2c7c..08b2e1a69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Removed `Get-PnPAvailableClientSideComponents`. Use `Get-PnPPageComponent -Page -ListAvailable` instead. [#1833](https://github.com/pnp/powershell/pull/1833) ### Contributors +- Jago Pauwels [jagopauwels] - [4ndri] - Martin Lingstuyl [martinlingstuyl] - James May [fowl2] From d5e91cd8365d0ffb12ffd105f9c998ad039a9563 Mon Sep 17 00:00:00 2001 From: Aleks Date: Fri, 20 May 2022 08:53:08 +0200 Subject: [PATCH 203/458] correcting the pnp organization name in Docker --- docker/hub.docker.md | 4 ++-- pages/index.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/hub.docker.md b/docker/hub.docker.md index 26f9e2ab7..54a7117dc 100644 --- a/docker/hub.docker.md +++ b/docker/hub.docker.md @@ -7,14 +7,14 @@ * latest: The latest stable image * alpine-3.14 - * `docker pull pnp/powershell` or `docker pull pnp/powershell:latest` + * `docker pull m365pnp/powershell` or `docker pull m365pnp/powershell:latest` ### Nightly * nightly: The latest night image * alpine-3.14 - * `docker pull pnp/powershell:nightly` + * `docker pull m365pnp/powershell:nightly` ## About this image diff --git a/pages/index.md b/pages/index.md index 8a28887d1..6c21d105e 100644 --- a/pages/index.md +++ b/pages/index.md @@ -22,13 +22,13 @@ Install-Module -Name PnP.PowerShell -AllowPrerelease -SkipPublisherCheck -AllowC To use PnP.PowerShell in a Windows container: ``` -docker run -it pnp/powershell:1.10.0-nanoserver-1809 +docker run -it m365pnp/powershell:1.10.0-nanoserver-1809 ``` To use PnP.PowerShell in a Linux container: ``` -docker run -it pnp/powershell +docker run -it m365pnp/powershell ``` See the [articles](/powershell/articles) section for more information on authentication and configuration. All [cmdlets](/powershell/cmdlets/Add-PnPAlert.html) have been documented too. From a39ee77f384d5ceb264839caf954ba3ac898ef52 Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Sat, 21 May 2022 18:08:20 +0200 Subject: [PATCH 204/458] Add cmdlet Get-PnPTeamsChannelMessageReply --- .../Get-PnPTeamsChannelMessageReply.md | 106 ++++++++++++++++++ .../PipeBinds/TeamsChannelMessagePipeBind.cs | 25 +++++ .../TeamsChannelMessageReplyPipeBind.cs | 25 +++++ .../Model/Teams/TeamChannelMessage.cs | 3 - .../Model/Teams/TeamChannelMessageReply.cs | 23 ++++ .../Teams/GetTeamsChannelMessageReply.cs | 66 +++++++++++ src/Commands/Utilities/TeamsUtility.cs | 16 +++ 7 files changed, 261 insertions(+), 3 deletions(-) create mode 100644 documentation/Get-PnPTeamsChannelMessageReply.md create mode 100644 src/Commands/Base/PipeBinds/TeamsChannelMessagePipeBind.cs create mode 100644 src/Commands/Base/PipeBinds/TeamsChannelMessageReplyPipeBind.cs create mode 100644 src/Commands/Model/Teams/TeamChannelMessageReply.cs create mode 100644 src/Commands/Teams/GetTeamsChannelMessageReply.cs diff --git a/documentation/Get-PnPTeamsChannelMessageReply.md b/documentation/Get-PnPTeamsChannelMessageReply.md new file mode 100644 index 000000000..183894be0 --- /dev/null +++ b/documentation/Get-PnPTeamsChannelMessageReply.md @@ -0,0 +1,106 @@ +--- +Module Name: PnP.PowerShell +title: Get-PnPTeamsChannelMessageReply +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Get-PnPTeamsChannelMessageReply.html +--- + +# Get-PnPTeamsChannelMessageReply + +## SYNOPSIS + +**Required Permissions** + + * Microsoft Graph API: ChannelMessage.Read.All + +Returns replies from the specified Microsoft Teams channel message. + +## SYNTAX + +```powershell +Get-PnPTeamsChannelMessageReply -Team -Channel -Message [-Identity ] [] +``` + +## DESCRIPTION + +## EXAMPLES + +### EXAMPLE 1 + +```powershell +Get-PnPTeamsChannelMessageReply -Team MyTestTeam -Channel "My Channel" -Message 1653089769293 +``` + +Gets all replies of the specified channel message. + +### EXAMPLE 2 +```powershell +Get-PnPTeamsChannelMessageReply -Team MyTestTeam -Channel "My Channel" -Message 1653089769293 -Identity 1653086004630 +``` + +Gets a specific reply of the specified channel message. + + +## PARAMETERS + +### -Team +Specify the group id, mailNickname or display name of the team to use. + +```yaml +Type: TeamsTeamPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Channel +Specify id or name of the channel to use. + +```yaml +Type: TeamsChannelPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Message +Specify the id of the message to use. + +```yaml +Type: TeamsChannelMessagePipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Identity +Specify the id of the message reply to use. + +```yaml +Type: TeamsChannelMessageReplyPipeBind +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) diff --git a/src/Commands/Base/PipeBinds/TeamsChannelMessagePipeBind.cs b/src/Commands/Base/PipeBinds/TeamsChannelMessagePipeBind.cs new file mode 100644 index 000000000..a364c3b44 --- /dev/null +++ b/src/Commands/Base/PipeBinds/TeamsChannelMessagePipeBind.cs @@ -0,0 +1,25 @@ +using PnP.PowerShell.Commands.Model.Teams; + +namespace PnP.PowerShell.Commands.Base.PipeBinds +{ + public class TeamsChannelMessagePipeBind + { + private readonly string _id; + private readonly TeamChannelMessage _message; + + public TeamsChannelMessagePipeBind(string input) + { + _id = input; + } + + public TeamsChannelMessagePipeBind(TeamChannelMessage input) + { + _message = input; + } + + public string GetId() + { + return _message?.Id ?? _id; + } + } +} diff --git a/src/Commands/Base/PipeBinds/TeamsChannelMessageReplyPipeBind.cs b/src/Commands/Base/PipeBinds/TeamsChannelMessageReplyPipeBind.cs new file mode 100644 index 000000000..734fb4b8a --- /dev/null +++ b/src/Commands/Base/PipeBinds/TeamsChannelMessageReplyPipeBind.cs @@ -0,0 +1,25 @@ +using PnP.PowerShell.Commands.Model.Teams; + +namespace PnP.PowerShell.Commands.Base.PipeBinds +{ + public class TeamsChannelMessageReplyPipeBind + { + private readonly string _id; + private readonly TeamChannelMessageReply _reply; + + public TeamsChannelMessageReplyPipeBind(string input) + { + _id = input; + } + + public TeamsChannelMessageReplyPipeBind(TeamChannelMessageReply input) + { + _reply = input; + } + + public string GetId() + { + return _reply?.Id ?? _id; + } + } +} diff --git a/src/Commands/Model/Teams/TeamChannelMessage.cs b/src/Commands/Model/Teams/TeamChannelMessage.cs index 6e3e67c67..5436f81eb 100644 --- a/src/Commands/Model/Teams/TeamChannelMessage.cs +++ b/src/Commands/Model/Teams/TeamChannelMessage.cs @@ -1,7 +1,4 @@ using System; -using System.Globalization; -using System.Reflection; -using System.Text.Json.Serialization; namespace PnP.PowerShell.Commands.Model.Teams { diff --git a/src/Commands/Model/Teams/TeamChannelMessageReply.cs b/src/Commands/Model/Teams/TeamChannelMessageReply.cs new file mode 100644 index 000000000..45b798d45 --- /dev/null +++ b/src/Commands/Model/Teams/TeamChannelMessageReply.cs @@ -0,0 +1,23 @@ +using System; + +namespace PnP.PowerShell.Commands.Model.Teams +{ + public class TeamChannelMessageReply + { + public string Id { get; set; } + + public string ReplyToId { get; set; } + + public DateTime? CreatedDateTime { get; set; } + + public DateTime? DeletedDateTime { get; set; } + + public DateTime? LastModifiedDateTime { get; set; } + + public string Importance { get; set; } = "normal"; + + public TeamChannelMessageBody Body { get; set; } = new TeamChannelMessageBody(); + + public TeamChannelMessageFrom From { get; set; } = new TeamChannelMessageFrom(); + } +} diff --git a/src/Commands/Teams/GetTeamsChannelMessageReply.cs b/src/Commands/Teams/GetTeamsChannelMessageReply.cs new file mode 100644 index 000000000..7e32e3fe6 --- /dev/null +++ b/src/Commands/Teams/GetTeamsChannelMessageReply.cs @@ -0,0 +1,66 @@ +using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Utilities; +using System.Management.Automation; + +namespace PnP.PowerShell.Commands.Teams +{ + [Cmdlet(VerbsCommon.Get, "PnPTeamsChannelMessageReply")] + [RequiredMinimalApiPermissions("ChannelMessage.Read.All")] + public class GetTeamsChannelMessageReply : PnPGraphCmdlet + { + [Parameter(Mandatory = true)] + public TeamsTeamPipeBind Team; + + [Parameter(Mandatory = true)] + public TeamsChannelPipeBind Channel; + + [Parameter(Mandatory = true)] + public TeamsChannelMessagePipeBind Message; + + [Parameter(Mandatory = false)] + public TeamsChannelMessageReplyPipeBind Identity; + + protected override void ExecuteCmdlet() + { + var groupId = Team.GetGroupId(HttpClient, AccessToken); + if (groupId == null) + { + throw new PSArgumentException("Group not found"); + } + + var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + if (channelId == null) + { + throw new PSArgumentException("Channel not found"); + } + + var messageId = Message.GetId(); + if (messageId == null) + { + throw new PSArgumentException("Message not found"); + } + + try + { + if (ParameterSpecified(nameof(Identity))) + { + var reply = TeamsUtility.GetMessageReplyAsync(HttpClient, AccessToken, groupId, channelId, messageId, Identity.GetId()).GetAwaiter().GetResult(); + WriteObject(reply); + } + else + { + var replies = TeamsUtility.GetMessageRepliesAsync(HttpClient, AccessToken, groupId, channelId, messageId).GetAwaiter().GetResult(); + WriteObject(replies, true); + } + } + catch + { + // Exception thrown by Graph is quite unclear. + var message = ParameterSpecified(nameof(Identity)) ? "Failed to retrieve reply." : "Failed to retrieve replies."; + throw new PSArgumentException(message); + } + } + } +} diff --git a/src/Commands/Utilities/TeamsUtility.cs b/src/Commands/Utilities/TeamsUtility.cs index 1b095d556..f7b351973 100644 --- a/src/Commands/Utilities/TeamsUtility.cs +++ b/src/Commands/Utilities/TeamsUtility.cs @@ -631,6 +631,22 @@ public static async Task> GetMessagesAsync(HttpClient h } } + /// + /// List all the replies to a message in a channel of a team. + /// + public static async Task> GetMessageRepliesAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string messageId) + { + return await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/messages/{messageId}/replies", accessToken); + } + + /// + /// Get a specific reply of a message in a channel of a team. + /// + public static async Task GetMessageReplyAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string messageId, string replyId) + { + return await GraphHelper.GetAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/messages/{messageId}/replies/{replyId}", accessToken); + } + public static async Task UpdateChannelAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, TeamChannel channel) { return await GraphHelper.PatchAsync(httpClient, accessToken, $"beta/teams/{groupId}/channels/{channelId}", channel); From 8843ffb4482352d26aaf75836e52190bae042e6f Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Sat, 21 May 2022 19:14:44 +0200 Subject: [PATCH 205/458] Include deleted parameter --- .../Get-PnPTeamsChannelMessageReply.md | 22 ++++++++++++++++--- .../Teams/GetTeamsChannelMessageReply.cs | 5 ++++- src/Commands/Utilities/TeamsUtility.cs | 6 +++-- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/documentation/Get-PnPTeamsChannelMessageReply.md b/documentation/Get-PnPTeamsChannelMessageReply.md index 183894be0..c9fe38999 100644 --- a/documentation/Get-PnPTeamsChannelMessageReply.md +++ b/documentation/Get-PnPTeamsChannelMessageReply.md @@ -20,7 +20,9 @@ Returns replies from the specified Microsoft Teams channel message. ## SYNTAX ```powershell -Get-PnPTeamsChannelMessageReply -Team -Channel -Message [-Identity ] [] +Get-PnPTeamsChannelMessageReply -Team -Channel -Message +[-Identity ] [-IncludeDeleted] +[] ``` ## DESCRIPTION @@ -30,10 +32,10 @@ Get-PnPTeamsChannelMessageReply -Team -Channel > GetMessagesAsync(HttpClient h /// /// List all the replies to a message in a channel of a team. /// - public static async Task> GetMessageRepliesAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string messageId) + public static async Task> GetMessageRepliesAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string messageId, bool includeDeleted = false) { - return await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/messages/{messageId}/replies", accessToken); + var replies = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/messages/{messageId}/replies", accessToken); + + return includeDeleted ? replies.ToList() : replies.Where(r => r.DeletedDateTime.HasValue).ToList(); } /// From 144bf97950ce4fc1515a19db2208ad2ba8aa3868 Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Sat, 21 May 2022 19:40:58 +0200 Subject: [PATCH 206/458] Add Identity parameter for Get-PnPTeamsChannelMessage cmdlet --- documentation/Get-PnPTeamsChannelMessage.md | 40 ++++++++++++++----- .../PipeBinds/TeamsChannelMessagePipeBind.cs | 25 ++++++++++++ src/Commands/Teams/GetTeamsChannelMessage.cs | 37 ++++++++++++----- src/Commands/Utilities/TeamsUtility.cs | 7 +++- 4 files changed, 87 insertions(+), 22 deletions(-) create mode 100644 src/Commands/Base/PipeBinds/TeamsChannelMessagePipeBind.cs diff --git a/documentation/Get-PnPTeamsChannelMessage.md b/documentation/Get-PnPTeamsChannelMessage.md index b7c0159dc..e48bf4c3f 100644 --- a/documentation/Get-PnPTeamsChannelMessage.md +++ b/documentation/Get-PnPTeamsChannelMessage.md @@ -20,7 +20,7 @@ Returns messages from the specified Microsoft Teams Channel. ## SYNTAX ```powershell -Get-PnPTeamsChannelMessage -Team -Channel [-IncludeDeleted] +Get-PnPTeamsChannelMessage -Team -Channel [-Identity ] [-IncludeDeleted] [] ``` @@ -35,8 +35,30 @@ Get-PnPTeamsChannelMessage -Team MyTestTeam -Channel "My Channel" Gets all messages of the specified channel +### EXAMPLE 2 + +```powershell +Get-PnPTeamsChannelMessage -Team MyTestTeam -Channel "My Channel" -Identity 1653089769293 +``` + +Gets a specific message of the specified channel + ## PARAMETERS +### -Team +Specify the group id, mailNickname or display name of the team to use. + +```yaml +Type: TeamsTeamPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Channel Specify id or name of the channel to use. @@ -51,13 +73,12 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -IncludeDeleted -Specify to include deleted messages + ### -Identity +Specify the id of the message to use. ```yaml -Type: SwitchParameter +Type: TeamsChannelMessagePipeBind Parameter Sets: (All) - Required: False Position: Named Default value: None @@ -65,14 +86,14 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -Team -Specify the group id, mailNickname or display name of the team to use. +### -IncludeDeleted +Specify to include deleted messages. ```yaml -Type: TeamsTeamPipeBind +Type: SwitchParameter Parameter Sets: (All) -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False @@ -82,4 +103,3 @@ Accept wildcard characters: False ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - diff --git a/src/Commands/Base/PipeBinds/TeamsChannelMessagePipeBind.cs b/src/Commands/Base/PipeBinds/TeamsChannelMessagePipeBind.cs new file mode 100644 index 000000000..a364c3b44 --- /dev/null +++ b/src/Commands/Base/PipeBinds/TeamsChannelMessagePipeBind.cs @@ -0,0 +1,25 @@ +using PnP.PowerShell.Commands.Model.Teams; + +namespace PnP.PowerShell.Commands.Base.PipeBinds +{ + public class TeamsChannelMessagePipeBind + { + private readonly string _id; + private readonly TeamChannelMessage _message; + + public TeamsChannelMessagePipeBind(string input) + { + _id = input; + } + + public TeamsChannelMessagePipeBind(TeamChannelMessage input) + { + _message = input; + } + + public string GetId() + { + return _message?.Id ?? _id; + } + } +} diff --git a/src/Commands/Teams/GetTeamsChannelMessage.cs b/src/Commands/Teams/GetTeamsChannelMessage.cs index ea3092dcd..a172af916 100644 --- a/src/Commands/Teams/GetTeamsChannelMessage.cs +++ b/src/Commands/Teams/GetTeamsChannelMessage.cs @@ -4,7 +4,7 @@ using PnP.PowerShell.Commands.Utilities; using System.Management.Automation; -namespace PnP.PowerShell.Commands.Graph +namespace PnP.PowerShell.Commands.Teams { [Cmdlet(VerbsCommon.Get, "PnPTeamsChannelMessage")] [RequiredMinimalApiPermissions("Group.Read.All")] @@ -16,26 +16,41 @@ public class GetTeamsChannelMessage : PnPGraphCmdlet [Parameter(Mandatory = true)] public TeamsChannelPipeBind Channel; + [Parameter(Mandatory = false)] + public TeamsChannelMessagePipeBind Identity; + [Parameter(Mandatory = false)] public SwitchParameter IncludeDeleted; + protected override void ExecuteCmdlet() { var groupId = Team.GetGroupId(HttpClient, AccessToken); - if (groupId != null) + if (groupId == null) { - var channel = Channel.GetChannel(HttpClient, AccessToken, groupId); - if (channel != null) - { - WriteObject(TeamsUtility.GetMessagesAsync(HttpClient, AccessToken, groupId, channel.Id, IncludeDeleted).GetAwaiter().GetResult(), true); - } else + throw new PSArgumentException("Team not found"); + } + + var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + if (channelId == null) + { + throw new PSArgumentException("Channel not found"); + } + + if (ParameterSpecified(nameof(Identity))) + { + if (ParameterSpecified(nameof(IncludeDeleted))) { - throw new PSArgumentException("Channel not found"); + throw new PSArgumentException($"Don't specify {nameof(IncludeDeleted)} when using the {nameof(Identity)} parameter."); } - } else + + var message = TeamsUtility.GetMessageAsync(HttpClient, AccessToken, groupId, channelId, Identity.GetId()).GetAwaiter().GetResult(); + WriteObject(message); + } + else { - throw new PSArgumentException("Team not found"); + var messages = TeamsUtility.GetMessagesAsync(HttpClient, AccessToken, groupId, channelId, IncludeDeleted).GetAwaiter().GetResult(); + WriteObject(messages, true); } - } } } \ No newline at end of file diff --git a/src/Commands/Utilities/TeamsUtility.cs b/src/Commands/Utilities/TeamsUtility.cs index 1b095d556..59fb21099 100644 --- a/src/Commands/Utilities/TeamsUtility.cs +++ b/src/Commands/Utilities/TeamsUtility.cs @@ -615,10 +615,15 @@ public static async Task PostMessageAsync(HttpClient httpClient, string accessTo await GraphHelper.PostAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/messages", message, accessToken); } + public static async Task GetMessageAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string messageId) + { + return await GraphHelper.GetAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/messages/{messageId}", accessToken); + } + public static async Task> GetMessagesAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, bool includeDeleted = false) { List messages = new List(); - var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"beta/teams/{groupId}/channels/{channelId}/messages", accessToken); + var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/messages", accessToken); messages.AddRange(collection); if (includeDeleted) From 1adf8bf286d23f1b6a6c9dfaf7ccf434fbc0cbe6 Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Sat, 21 May 2022 19:43:45 +0200 Subject: [PATCH 207/458] Added IncludeDeleted parameter validation --- src/Commands/Teams/GetTeamsChannelMessageReply.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Commands/Teams/GetTeamsChannelMessageReply.cs b/src/Commands/Teams/GetTeamsChannelMessageReply.cs index 824a49ef2..04b461c20 100644 --- a/src/Commands/Teams/GetTeamsChannelMessageReply.cs +++ b/src/Commands/Teams/GetTeamsChannelMessageReply.cs @@ -49,6 +49,11 @@ protected override void ExecuteCmdlet() { if (ParameterSpecified(nameof(Identity))) { + if (ParameterSpecified(nameof(IncludeDeleted))) + { + throw new PSArgumentException($"Don't specify {nameof(IncludeDeleted)} when using the {nameof(Identity)} parameter."); + } + var reply = TeamsUtility.GetMessageReplyAsync(HttpClient, AccessToken, groupId, channelId, messageId, Identity.GetId()).GetAwaiter().GetResult(); WriteObject(reply); } From b39bf45b9b2b7f15ec2f59084fed9c2ba23b46d6 Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Sat, 21 May 2022 19:47:50 +0200 Subject: [PATCH 208/458] Fixed documentation error --- documentation/Get-PnPTeamsChannelMessage.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/Get-PnPTeamsChannelMessage.md b/documentation/Get-PnPTeamsChannelMessage.md index e48bf4c3f..7ff97e70e 100644 --- a/documentation/Get-PnPTeamsChannelMessage.md +++ b/documentation/Get-PnPTeamsChannelMessage.md @@ -73,13 +73,14 @@ Accept pipeline input: False Accept wildcard characters: False ``` - ### -Identity +### -Identity Specify the id of the message to use. ```yaml Type: TeamsChannelMessagePipeBind Parameter Sets: (All) Required: False + Position: Named Default value: None Accept pipeline input: False From 0af8f7b75152e84ae30b94ef311eb4305c10b348 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 21 May 2022 22:18:03 +0300 Subject: [PATCH 209/458] Fix: Get-PnPField now returns specific field type --- CHANGELOG.md | 1 + src/Commands/Fields/GetField.cs | 83 +++++++++++++++++++++++++++++++-- 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08b2e1a69..744a99fae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) +- Changed `Get-PnPField` now returns specific type instead of the generic type. ### Fixed diff --git a/src/Commands/Fields/GetField.cs b/src/Commands/Fields/GetField.cs index a8b5181d7..c4535f52a 100644 --- a/src/Commands/Fields/GetField.cs +++ b/src/Commands/Fields/GetField.cs @@ -3,8 +3,6 @@ using System.Management.Automation; using Microsoft.SharePoint.Client; using Microsoft.SharePoint.Client.Taxonomy; - -using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Base.PipeBinds; namespace PnP.PowerShell.Commands.Fields @@ -54,7 +52,86 @@ protected override void ExecuteCmdlet() { ClientContext.Load(field, RetrievalExpressions); ClientContext.ExecuteQueryRetry(); - WriteObject(field); + + switch (field.FieldTypeKind) + { + case FieldType.DateTime: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.Choice: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.Calculated: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.Computed: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.Geolocation: + { + WriteObject(ClientContext.CastTo(field)); + break; + + } + case FieldType.User: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.Currency: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.Guid: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.URL: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.Lookup: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.MultiChoice: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.Number: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.Invalid: + { + if (field.TypeAsString.StartsWith("TaxonomyFieldType")) + { + WriteObject(ClientContext.CastTo(field)); + break; + } + goto default; + } + default: + { + WriteObject(field); + break; + } + } + } else if (fieldCollection != null) { From 793f1912b4ad3b9d35b328244ff63dd7129522a8 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 21 May 2022 22:20:27 +0300 Subject: [PATCH 210/458] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 744a99fae..4e7de0db4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) -- Changed `Get-PnPField` now returns specific type instead of the generic type. +- Changed `Get-PnPField` now returns specific type instead of the generic type. [#1888] (https://github.com/pnp/powershell/pull/1888) ### Fixed From 22e63c42a2e7045dc9c9814ad55e691530ca05b8 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 21 May 2022 22:38:17 +0300 Subject: [PATCH 211/458] Fix add-pnpfield taxonomy type --- src/Commands/Fields/AddField.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Commands/Fields/AddField.cs b/src/Commands/Fields/AddField.cs index 1f94110c4..0af47bcd1 100644 --- a/src/Commands/Fields/AddField.cs +++ b/src/Commands/Fields/AddField.cs @@ -4,6 +4,7 @@ using PnP.Framework.Entities; using PnP.PowerShell.Commands.Base.PipeBinds; using System.Collections.Generic; +using Microsoft.SharePoint.Client.Taxonomy; namespace PnP.PowerShell.Commands.Fields { @@ -319,6 +320,15 @@ protected override void ExecuteCmdlet() WriteObject(ClientContext.CastTo(f)); break; } + case FieldType.Invalid: + { + if (f.TypeAsString.StartsWith("TaxonomyFieldType")) + { + WriteObject(ClientContext.CastTo(f)); + break; + } + goto default; + } default: { WriteObject(f); From c6a9656cdb387f47e51ed16bc6d942b590ecfa0e Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 21 May 2022 22:40:04 +0300 Subject: [PATCH 212/458] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e7de0db4..00ecc2f37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) - Changed `Get-PnPField` now returns specific type instead of the generic type. [#1888] (https://github.com/pnp/powershell/pull/1888) +- Changed `Add-PnPField` now returns specific type taxonomy field type instead of the generic type. [#1888] (https://github.com/pnp/powershell/pull/1888) ### Fixed From a531d00078e663d4b81192e588824896798c57b3 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 21 May 2022 23:17:35 +0300 Subject: [PATCH 213/458] Fixed changelog --- CHANGELOG.md | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index deb1d1de5..e18d3cf45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Invoke-PnPGraphMethod` cmdlet to invoke generic Microsoft Graph API Methods. [#1820](https://github.com/pnp/powershell/pull/1820) - Added `Stop-PnPFlowRun` cmdlet to stop/cancel a specific Power Automate flow run. [#1838](https://github.com/pnp/powershell/pull/1838) - Added `Remove-PnPTeamsChannelUser` cmdlet to remove a user from a private channel. [#1840](https://github.com/pnp/powershell/pull/1840) -- Added `Get-PnPListItemPermissions` cmdlet to retrieve permissions of a list item. ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) @@ -58,12 +57,36 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Update-PnPTeamsUser` cmdlet to change the role of a user in an existing Teams team [#1499](https://github.com/pnp/powershell/pull/1499) - Added `Get\New\Remove\Set-PnPMicrosoft365GroupSettings` cmdlets to interact with Microsoft 365 Group settings. - Added `Get-PnPMicrosoft365GroupSettingTemplates` cmdlet to retrieve system wide Microsoft 365 Group setting templates. -- Added `Add\Remove\Invoke-PnPListDesign` cmdlets to add a list design, remove a list design and apply the list design. -- Added `Get-PnPListItemPermissions` cmdlet to retrieve permissions of a list item. +- Added `Add\Remove\Invoke\Get-PnPListDesign` cmdlets to add a list design, remove a list design and apply the list design. +- Added `DisablePersonalListCreation` parameter to `Set-PnPTenant` cmdlet to provide ability to disable personal lists creation [#1545](https://github.com/pnp/powershell/pull/1545) +- Added `DisabledModernListTemplateIds` and `EnableModernListTemplateIds` parameters to `Set-PnPTenant` cmdlet to provide ability to disable or enable modern lists with specific Ids [#1545](https://github.com/pnp/powershell/pull/1545) +- Added `DisablePersonalListCreation` and `DisabledModernListTemplateIds` values to be displayed when using `Get-PnPTenant` cmdlet [#1545](https://github.com/pnp/powershell/pull/1545) - Added `Add\Remove\Set-PnPAdaptiveScopeProperty` cmdlets to add/update/remove a property bag value while dealing with the noscript toggling in one cmdlet [#1556](https://github.com/pnp/powershell/pull/1556) - Added support to add multiple owners and members in `New-PnPTeamsTeam` cmdlet [#1241](https://github.com/pnp/powershell/pull/1241) - Added the ability to set the title of a new modern page in SharePoint Online using `Add-PnPPage` to be different from its filename by using `-Title` - +- Added optional `-UseBeta` parameter to `Get-PnPAzureADUser` to force it to use the Microsoft Graph beta endpoint. This can be necessary when i.e. using `-Select "PreferredDataLocation"` to query for users with a specific multi geo location as this property is only available through the beta endpoint. [#1559](https://github.com/pnp/powershell/pull/1559) +- Added `-Content` option to `Add-PnPFile` which allows creating a new file on SharePoint Online and directly providing its textual content, i.e. to upload a log file of the execution [#1559](https://github.com/pnp/powershell/pull/1559) +- Added `Get-PnPTeamsPrimaryChannel` to get the primary Teams channel, general, of a Team [#1572](https://github.com/pnp/powershell/pull/1572) +- Added `IgnoreDefaultProperties` parameter to `Get-PnPAzureADUser` to allow for the default properties not to be retrieved but instead just the ones you specify using `Select` [#1575](https://github.com/pnp/powershell/pull/1575) +- Added `Publish\Unpublish-PnPContentType` to allow for content types to be published or unpublished on hub sites [#1597](https://github.com/pnp/powershell/pull/1597) +- Added `Get-PnPContentTypePublishingStatus` to get te current publication state of a content type in the content type hub site [#1597](https://github.com/pnp/powershell/pull/1597) +- Added ability to pipe the output of `Get-PnPTenantDeletedSite` to either `Restore-PnPTenantDeletedSite` or `Remove-PnPTenantDeletedSite` [#1596](https://github.com/pnp/powershell/pull/1596) +- Added `Rename-PnPTenantSite` to rename a SharePoint Online site URL [#1606](https://github.com/pnp/powershell/pull/1606) +- Added optional `-Wait` option to `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to allow for the script to wait until the user profile sync has completed +- Added optional `-Verbose` option to `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to allow for seeing the progress of the synchronization process +- Added `Copy-PnPTeamsTeam` which allows an existing Teams team to be copied into a new Team [#1624](https://github.com/pnp/powershell/pull/1624) +- Added `Set-PnPMessageCenterAnnouncementAsRead` which allows setting one or more message center announcements as read for the current user [#1151](https://github.com/pnp/powershell/pull/1151) +- Added `Set-PnPMessageCenterAnnouncementAsUnread` which allows setting one or more message center announcements as unread for the current user [#1151](https://github.com/pnp/powershell/pull/1151) +- Added `Set-PnPMessageCenterAnnouncementAsArchived` which allows setting one or more message center announcements as archived for the current user [#1151](https://github.com/pnp/powershell/pull/1151) +- Added `Set-PnPMessageCenterAnnouncementAsNotArchived` which allows setting one or more message center announcements as not archived for the current user [#1151](https://github.com/pnp/powershell/pull/1151) +- Added `Set-PnPMessageCenterAnnouncementAsFavorite` which allows setting one or more message center announcements as favorite for the current user [#1151](https://github.com/pnp/powershell/pull/1151) +- Added `Set-PnPMessageCenterAnnouncementAsNotFavorite` which allows setting one or more message center announcements as not favorite for the current user [#1151](https://github.com/pnp/powershell/pull/1151) +- Added `-AsMemoryStream` option to `Get-PnPFile` to allow for downloading of a file from SharePoint Online in memory for further processing [#1638](https://github.com/pnp/powershell/pull/1638) +- Added `-Stream` option to `Read-PnPSiteTemplate` to allow for processing on a PnP Provisioning Template coming from memory [#1638](https://github.com/pnp/powershell/pull/1638) +- Added `New-PnPAzureADUserTemporaryAccessPass` which allows creation of a Temporary Access Pass for a specific user in Azure Active Directory +- Added `-Force` option to `Set-PnPTenant` to allow skipping the confirmation question for certain other parameters like `SignInAccelerationDomain,EnableGuestSignInAcceleration,BccExternalSharingInvitations,OrphanedPersonalSitesRetentionPeriod,OneDriveForGuestsEnabled,AllowDownloadingNonWebViewableFiles`. +- Added `Get-PnPCompatibleHubContentTypes` which allows the list of content types present in the content type hub site that can be added to the root web or a list on a target site to be returned [#1678](https://github.com/pnp/powershell/pull/1678) + ### Changed - Improved `Add-PnPTeamsUser` cmdlet. The cmdlet executes faster and we can now add users in batches of 200. [#1548](https://github.com/pnp/powershell/pull/1548) @@ -771,4 +794,4 @@ First released version of PnP PowerShell - Koen Zomers [koenzomers] - Carlos Marins Jr [kadu-jr] - Aimery Thomas [a1mery] -- Veronique Lengelle [veronicageek] +- Veronique Lengelle [veronicageek] \ No newline at end of file From c3e200d23730ca451383c66f9fb4295a930b95be Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 21 May 2022 23:32:28 +0300 Subject: [PATCH 214/458] Fix to make it return usable values --- src/Commands/Lists/GetListItemPermission.cs | 25 +++++++++++++-------- src/Commands/Model/ListItemPermissions.cs | 4 ++-- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/Commands/Lists/GetListItemPermission.cs b/src/Commands/Lists/GetListItemPermission.cs index 5d37091b6..e65eee8f2 100644 --- a/src/Commands/Lists/GetListItemPermission.cs +++ b/src/Commands/Lists/GetListItemPermission.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Management.Automation; +using Microsoft.SharePoint.Client; using PnP.Core.QueryModel; using PnP.PowerShell.Commands.Base.PipeBinds; using PnP.PowerShell.Commands.Model; @@ -17,12 +18,9 @@ public class GetListItemPermission : PnPWebCmdlet protected override void ExecuteCmdlet() { - var list = List.GetList(PnPContext); - + var list = List.GetList(CurrentWeb); if (list == null) - { - throw new PSArgumentException($"Cannot find list provided through - {nameof(List)}", nameof(List)); - } + throw new PSArgumentException($"No list found with id, title or url '{List}'", "List"); var item = Identity.GetListItem(list); @@ -31,18 +29,27 @@ protected override void ExecuteCmdlet() throw new PSArgumentException($"Cannot find list item provided through -{nameof(Identity)}", nameof(Identity)); } - item.LoadAsync(w => w.RoleAssignments.QueryProperties(p => p.RoleDefinitions, p => p.PrincipalId)).GetAwaiter().GetResult(); + ClientContext.Load(item, a => a.RoleAssignments.Include(roleAsg => roleAsg.Member.LoginName, + roleAsg => roleAsg.RoleDefinitionBindings.Include(roleDef => roleDef.Name, + roleDef => roleDef.Description))); + ClientContext.ExecuteQueryRetry(); var listItemPermissions = new List(); - foreach (var roleAssignment in item.RoleAssignments.AsRequested()) + foreach (var roleAssignment in item.RoleAssignments) { var listItemPermission = new ListItemPermissions { - RoleDefinitions = roleAssignment.RoleDefinitions.AsRequested(), - PrincipalId = roleAssignment.PrincipalId + PrincipalName = roleAssignment.Member.LoginName }; + List roles = new List(); + foreach (var role in roleAssignment.RoleDefinitionBindings) + { + roles.Add(role.Description); + } + + listItemPermission.Permissions = roles; listItemPermissions.Add(listItemPermission); } diff --git a/src/Commands/Model/ListItemPermissions.cs b/src/Commands/Model/ListItemPermissions.cs index 108e4c782..6ea554b07 100644 --- a/src/Commands/Model/ListItemPermissions.cs +++ b/src/Commands/Model/ListItemPermissions.cs @@ -5,8 +5,8 @@ namespace PnP.PowerShell.Commands.Model { public class ListItemPermissions { - public IEnumerable RoleDefinitions { get; set; } + public List Permissions { get; set; } - public int PrincipalId { get; set; } + public string PrincipalName { get; set; } } } From 5faa0527d43ba789ca736e3c6350de3cdaf7f838 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 21 May 2022 23:33:46 +0300 Subject: [PATCH 215/458] Added changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e18d3cf45..fbd2b3854 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Invoke-PnPGraphMethod` cmdlet to invoke generic Microsoft Graph API Methods. [#1820](https://github.com/pnp/powershell/pull/1820) - Added `Stop-PnPFlowRun` cmdlet to stop/cancel a specific Power Automate flow run. [#1838](https://github.com/pnp/powershell/pull/1838) - Added `Remove-PnPTeamsChannelUser` cmdlet to remove a user from a private channel. [#1840](https://github.com/pnp/powershell/pull/1840) +- Added `Get-PnPListItemPermissions` cmdlet to retrieve item level permissions. [#1534](https://github.com/pnp/powershell/pull/1534) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) From e8acec9de171c40cedad90fa324a88f012cd76b9 Mon Sep 17 00:00:00 2001 From: Aleksandr SaPozhkov Date: Sun, 22 May 2022 13:10:20 -0700 Subject: [PATCH 216/458] Feature #1580, Docker Image (#1794) * building docker image in workflows * ls * cd dev * checkout * removing debug output * nightly version * docker login * docker secret * pushing to docker hub * docker readme * using DOCKER_USERNAME and DOCKER_PASSWORD * using DOCKER_USERNAME and DOCKER_PASSWORD * adding nightly tag * verified adding nightly tag * corrected user name ref * run via schedule again * more specific job name * divided job into simpler steps * testing pipeline for publishing * preparing for linux containers * triggering on yaml change * password in quoutes * testing publishing script with parameters * no ` needed for pwsh jobs * Getting docker tags via Invoke-RestMethod * show every unpublished image * fixed version comparisons * publishin all not prereleases * safer manipulating with : * replacing quotes * universal docker file * using universal dockerfile instead of special * listing published images with 10k limit * release only running via schedule * updated base image version * cleaning nightly release before merging to origin * typo in yaml cleaning * removing unused file * removing unused file * removing old files * new line at end of file * more documentation for manual publishing * removing unneccessary (so far) documentation * publishing image for 5 platform * fixing parameters in github actions * typo in space * sticking to 1 dockerfile * altering installation user * 3 platforms for Windows is default * fixed string comparison * removing unnecessary quotes * fixed logical comparison * specifying platform for building * using buildx for arm32v7-ubuntu-bionic * buildx debug output * buildx build --load * removing arm image * keeping 3 platforms * publishing latest for only linux * nightly release for 3 platforms * replacing with a fake job * using PS syntax for Windows builder * fixing more PS syntax in pipeline * returning back the module publishing steps * installing module for all the users * outputting module list after installing * installing module for all the users * installing for all users in Linux * removing arm image from docu * more user documentation * removed unused space in the dockerfile * reference to the PS gallery for versions * correcting the pnp organization name in Docker --- .github/workflows/nightlyrelease.yml | 49 +++++++++++++++++- .github/workflows/release.yml | 34 ++++++++++++ docker/Publish-UnpublishedImage.ps1 | 60 ++++++++++++++++++++++ docker/README.md | 64 +++++++++++++++++++++++ docker/build.ps1 | 1 - docker/hub.docker.md | 77 ++++++++++++++++++++++++++++ docker/pnppowershell.dockerFile | 18 +++++-- docker/powershell/installModules.ps1 | 20 -------- docker/runlocal.ps1 | 1 - pages/index.md | 12 +++++ 10 files changed, 310 insertions(+), 26 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 docker/Publish-UnpublishedImage.ps1 create mode 100644 docker/README.md delete mode 100644 docker/build.ps1 create mode 100644 docker/hub.docker.md delete mode 100644 docker/powershell/installModules.ps1 delete mode 100644 docker/runlocal.ps1 diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index 0e01889e5..f647794d7 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -1,4 +1,4 @@ -name: Nightly Release to PowerShell Gallery +name: Nightly Release to PowerShell Gallery and Docker Hub on: workflow_dispatch: @@ -39,3 +39,50 @@ jobs: push: true branch: dev token: ${{ secrets.PAT }} + publish-docker-windows-2022: + runs-on: windows-2022 + needs: [ build ] + steps: + - uses: actions/checkout@v2 + - name: Build an image + run: | + $VERSION="$(cat ./version.txt)-nightly" + docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-ltsc2022" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag "${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-ltsc2022"; + - name: Push the image + run: | + $VERSION="$(cat ./version.txt)-nightly" + docker login -u ${{ secrets.DOCKER_USERNAME }} -p '${{ secrets.DOCKER_PASSWORD }}' + docker push "${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-ltsc2022" + publish-docker-windows-2019: + runs-on: windows-2019 + needs: [ build ] + steps: + - uses: actions/checkout@v2 + - name: Build an image + run: | + $VERSION="$(cat ./version.txt)-nightly" + docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-1809" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag "${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-1809"; + - name: Push the image + run: | + $VERSION="$(cat ./version.txt)-nightly" + docker login -u ${{ secrets.DOCKER_USERNAME }} -p '${{ secrets.DOCKER_PASSWORD }}' + docker push "${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-1809" + publish-docker-linux: + runs-on: ubuntu-latest + needs: [ build ] + steps: + - uses: actions/checkout@v2 + - name: Build an image + run: | + VERSION=$(cat ./version.txt)-nightly + docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=alpine-3.14" --build-arg "INSTALL_USER=root" --build-arg "SKIP_PUBLISHER_CHECK=False" ./docker -f ./docker/pnppowershell.dockerFile --tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-alpine-3.14; + - name: Tag the image + run: | + VERSION=$(cat ./version.txt)-nightly + docker image tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-alpine-3.14 ${{ secrets.DOCKER_USERNAME }}/powershell:nightly + - name: Push the image + run: | + VERSION=$(cat ./version.txt)-nightly + docker login -u ${{ secrets.DOCKER_USERNAME }} -p '${{ secrets.DOCKER_PASSWORD }}' + docker push ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-alpine-3.14 + docker push ${{ secrets.DOCKER_USERNAME }}/powershell:nightly diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..7f22680de --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,34 @@ +name: Release to Docker Hub + +on: + workflow_dispatch: + schedule: + - cron: '30 3 * * *' +jobs: + publish-docker-windows-2022: + runs-on: windows-2022 + steps: + - uses: actions/checkout@v2 + - name: Build and Publish All + shell: pwsh + run: | + $securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword "ContainerAdministrator" $true "nanoserver-ltsc2022" + publish-docker-windows-2019: + runs-on: windows-2019 + steps: + - uses: actions/checkout@v2 + - name: Build and Publish All + shell: pwsh + run: | + $securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword "ContainerAdministrator" $true "nanoserver-1809" + publish-docker-linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Build and Publish All + shell: pwsh + run: | + $securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword "root" $false "alpine-3.14" diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 new file mode 100644 index 000000000..c75b3fc02 --- /dev/null +++ b/docker/Publish-UnpublishedImage.ps1 @@ -0,0 +1,60 @@ +Param( + [Parameter(Position = 0, + Mandatory = $true, + ValueFromPipeline = $false)] + [String] + $PS_MODULE_NAME, + [Parameter(Position = 1, + Mandatory = $true, + ValueFromPipeline = $false)] + [String] + $DOCKER_USERNAME, + [Parameter(Position = 2, + Mandatory = $true, + ValueFromPipeline = $false)] + [String] + $DOCKER_IMAGE_NAME, + [Parameter(Position = 3, + Mandatory = $true, + ValueFromPipeline = $false)] + [Security.SecureString] + $DOCKER_PASSWORD, + [Parameter(Position = 4, + Mandatory = $false, + ValueFromPipeline = $false)] + [String] + $DOCKER_INSTALL_USER = "ContainerAdministrator", + [Parameter(Position = 5, + Mandatory = $false, + ValueFromPipeline = $false)] + [bool] + $SKIP_PUBLISHER_CHECK = $false, + [Parameter(Position = 6, + Mandatory = $false, + ValueFromPipeline = $false)] + [String] + $DOCKER_IMAGE_SUFFIX_ARRAY = "nanoserver-ltsc2022" +) +$publishedImageVersions = (Invoke-RestMethod https://registry.hub.docker.com/v2/repositories/$DOCKER_USERNAME/$DOCKER_IMAGE_NAME/tags?page_size=10240).results | % { + $_.name +} +$moduleVersions = Find-Module $PS_MODULE_NAME -AllVersions; +[array]::Reverse($moduleVersions); +$moduleVersions | % { + $moduleVersion = $_.Version; + $DOCKER_IMAGE_SUFFIX_ARRAY.Split( "," ) | % { + $baseImageSuffix = $_; + $imageVersion = "$moduleVersion-$baseImageSuffix"; + Write-Host "Checking $imageVersion" + if ( !( $publishedImageVersions -contains $imageVersion ) ) { + docker build --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "INSTALL_USER=$DOCKER_INSTALL_USER" --build-arg "SKIP_PUBLISHER_CHECK=$SKIP_PUBLISHER_CHECK" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; + $plainStringPassword = [System.Net.NetworkCredential]::new("", $DOCKER_PASSWORD).Password; + docker login -u $DOCKER_USERNAME -p "$plainStringPassword"; + docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; + if ( $baseImageSuffix -eq "alpine-3.14") { + docker image tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; + docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; + } + } + } +} diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 000000000..32dd74e1f --- /dev/null +++ b/docker/README.md @@ -0,0 +1,64 @@ +# Publish manually in Windows + +1. Set "DOCKER_USERNAME" and "DOCKER_PASSWORD" variables + +2. Run + +```powershell +$securedPassword = ConvertTo-SecureString $DOCKER_PASSWORD -AsPlainText -Force +./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME powershell $securedPassword "ContainerAdministrator" $true "nanoserver-1809" +``` + +# Publish manually in Linux + +1. Set "DOCKER_USERNAME" and "DOCKER_PASSWORD" variables + +2. Run + +```powershell +$securedPassword = ConvertTo-SecureString $DOCKER_PASSWORD -AsPlainText -Force +./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME powershell $securedPassword $false "root" "alpine-3.14" +``` + +# Publish with prereleases manually in Windows + +1. Set "DOCKER_USERNAME" and "DOCKER_PASSWORD" variables + +2. Run + +```PowerShell +$VERSION="$(cat ./version.txt)-nightly" +docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-ltsc2022" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag "$DOCKER_USERNAME/powershell:$VERSION-nanoserver-ltsc2022"; +$VERSION="$(cat ./version.txt)-nightly" +docker login -u $DOCKER_USERNAME -p "$DOCKER_PASSWORD" +docker push "$DOCKER_USERNAME/powershell:$VERSION-nanoserver-ltsc2022" +``` + +or + +```PowerShell +$VERSION="$(cat ./version.txt)-nightly" +docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-1809" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag "$DOCKER_USERNAME/powershell:$VERSION-nanoserver-1809"; +$VERSION="$(cat ./version.txt)-nightly" +docker login -u $DOCKER_USERNAME -p "$DOCKER_PASSWORD" +docker push "$DOCKER_USERNAME/powershell:$VERSION-nanoserver-1809" +``` + +# Publish with prereleases manually in Linux + +1. Set "DOCKER_USERNAME" and "DOCKER_PASSWORD" variables + +2. Run + +```bash +VERSION=$(cat ./version.txt)-nightly +docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=alpine-3.14" --build-arg "INSTALL_USER=root" --build-arg "SKIP_PUBLISHER_CHECK=False" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/powershell:$VERSION-alpine-3.14; +docker image tag $DOCKER_USERNAME/powershell:$VERSION-alpine-3.14 $DOCKER_USERNAME/powershell:nightly +docker login -u $DOCKER_USERNAME -p "$DOCKER_PASSWORD" +docker push $DOCKER_USERNAME/powershell:$VERSION-alpine-3.14 +docker push $DOCKER_USERNAME/powershell:nightly +``` + +# Publish automatically with Github Actions + +Set "DOCKER_USERNAME" and "DOCKER_PASSWORD" variables in Github Actions Secrets diff --git a/docker/build.ps1 b/docker/build.ps1 deleted file mode 100644 index cbb23ef33..000000000 --- a/docker/build.ps1 +++ /dev/null @@ -1 +0,0 @@ -docker build -t pnppowershell -f ./pnppowershell.dockerFile . --no-cache \ No newline at end of file diff --git a/docker/hub.docker.md b/docker/hub.docker.md new file mode 100644 index 000000000..54a7117dc --- /dev/null +++ b/docker/hub.docker.md @@ -0,0 +1,77 @@ +# PnP.Powershell + +## Featured tags + +### Latest + +* latest: The latest stable image + + * alpine-3.14 + * `docker pull m365pnp/powershell` or `docker pull m365pnp/powershell:latest` + +### Nightly + +* nightly: The latest night image + + * alpine-3.14 + * `docker pull m365pnp/powershell:nightly` + +## About this image + +**PnP PowerShell** is a .NET Core 3.1 / .NET Framework 4.6.1 based PowerShell Module providing over 600 cmdlets that work with Microsoft 365 environments such as SharePoint Online, Microsoft Teams, Microsoft Project, Security & Compliance, Azure Active Directory, and more. + +## Usage examples + +### Windows-container + +Starting an isolated container with PnP.PowerShell module installed: + +``` +docker run --rm -it asapozhkov/powershell:1.10.0-nanoserver-1809 +``` + +Starting a PnP.PowerShell container with the current directory mounted: + +```PowerShell +docker run --rm -it -v ${PWD}:c:/app -w c:/app asapozhkov/powershell:1.10.0-nanoserver-1809 +``` + +### Linux-container + +Starting an isolated container with PnP.PowerShell module installed: + +``` +docker run --rm -it asapozhkov/powershell +``` + +Starting a PnP.PowerShell container with the current directory mounted: + +```bash +docker run --rm -it -v ${PWD}:/home -w /home asapozhkov/powershell +``` + +## Tag explanation + +Tags names mean the following: + +`(-nightly)-` + +Currently supported platforms: + +* nanoserver-ltsc2022 +* nanoserver-1809 +* alpine-3.14 + +Tag name examples: + +* 1.8.0-nanoserver-ltsc2022 +* 1.9.0-nanoserver-ltsc2022 +* 1.10.0-nanoserver-1809 +* 1.10.0-alpine-3.14 +* 1.10.26-nightly-nanoserver-ltsc2022 + +To find the version numbers please visit https://www.powershellgallery.com/packages/PnP.PowerShell + +## Feedback + +* To give feedback for PnP.PowerShell or for how the images are built, file an issue at [PnP/PowerShell](https://github.com/pnp/powershell/issues/new/choose) diff --git a/docker/pnppowershell.dockerFile b/docker/pnppowershell.dockerFile index 6771f01bc..1c3144156 100644 --- a/docker/pnppowershell.dockerFile +++ b/docker/pnppowershell.dockerFile @@ -1,3 +1,15 @@ -FROM mcr.microsoft.com/powershell:lts-debian-10 -COPY ./powershell/ powershell -RUN /usr/bin/pwsh -File ./powershell/installModules.ps1 && rm -rf ./powershell \ No newline at end of file +ARG BASE_IMAGE_SUFFIX=alpine-3.14 +ARG BASE_IMAGE=mcr.microsoft.com/powershell:lts-7.2-$BASE_IMAGE_SUFFIX +FROM $BASE_IMAGE + +SHELL ["pwsh", "-command"] +ARG INSTALL_USER=ContainerAdministrator +USER $INSTALL_USER +ARG PNP_MODULE_VERSION +ARG SKIP_PUBLISHER_CHECK=TRUE +RUN if ( $env:SKIP_PUBLISHER_CHECK -eq $true ) { \ + Write-Host "SKIP_PUBLISHER_CHECK"; \ + Install-Module -Name PnP.PowerShell -RequiredVersion $env:PNP_MODULE_VERSION -Force -AllowPrerelease -Scope AllUsers -SkipPublisherCheck; \ + } else { \ + Install-Module -Name PnP.PowerShell -RequiredVersion $env:PNP_MODULE_VERSION -Force -AllowPrerelease -Scope AllUsers; \ + } diff --git a/docker/powershell/installModules.ps1 b/docker/powershell/installModules.ps1 deleted file mode 100644 index fd7099c93..000000000 --- a/docker/powershell/installModules.ps1 +++ /dev/null @@ -1,20 +0,0 @@ -$modules = @( - "PnP.PowerShell", - "Microsoft.PowerShell.SecretManagement", - "Microsoft.PowerShell.SecretStore" -) - -Set-PSRepository -Name "PSGallery" -InstallationPolicy Trusted -$ProgressPreference = "SilentlyContinue" - -foreach($module in $modules) -{ - Write-Host "Installing $module" - Install-Module -Name $module -AllowPrerelease | Out-Null -} - -Register-SecretVault -Name "SecretStore" -ModuleName "Microsoft.PowerShell.SecretStore" -DefaultVault -Set-SecretStoreConfiguration -Authentation None - -$userProfile = "Import-Module -Name PnP.PowerShell" -Set-Content -Path $PROFILE.AllUsersAllHosts -Value $userProfile -Force \ No newline at end of file diff --git a/docker/runlocal.ps1 b/docker/runlocal.ps1 deleted file mode 100644 index f9f1dfb9b..000000000 --- a/docker/runlocal.ps1 +++ /dev/null @@ -1 +0,0 @@ -docker run -it pnppowershell \ No newline at end of file diff --git a/pages/index.md b/pages/index.md index f104e492b..6c21d105e 100644 --- a/pages/index.md +++ b/pages/index.md @@ -19,6 +19,18 @@ To install a nightly build of PnP PowerShell: Install-Module -Name PnP.PowerShell -AllowPrerelease -SkipPublisherCheck -AllowClobber ``` +To use PnP.PowerShell in a Windows container: + +``` +docker run -it m365pnp/powershell:1.10.0-nanoserver-1809 +``` + +To use PnP.PowerShell in a Linux container: + +``` +docker run -it m365pnp/powershell +``` + See the [articles](/powershell/articles) section for more information on authentication and configuration. All [cmdlets](/powershell/cmdlets/Add-PnPAlert.html) have been documented too. # I've found a bug, where do I need to log an issue or create a PR From 79778ac7077c52fc25a7c4706c1d5c4248582194 Mon Sep 17 00:00:00 2001 From: spg-iwilson Date: Mon, 23 May 2022 06:17:40 +1000 Subject: [PATCH 217/458] Update Add-PnPPage.md (#1892) Change "cite" to site" in the Description --- documentation/Add-PnPPage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Add-PnPPage.md b/documentation/Add-PnPPage.md index 219cfac7f..935dcd235 100644 --- a/documentation/Add-PnPPage.md +++ b/documentation/Add-PnPPage.md @@ -23,7 +23,7 @@ Add-PnPPage [-Name] [-LayoutType ] ``` ## DESCRIPTION -Creates a new page. The page will be located inside the Site Pages library of the cite currently connected to. +Creates a new page. The page will be located inside the Site Pages library of the site currently connected to. ## EXAMPLES From 63e528327aa8a411ce3cc3004c3778e926df1a5f Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sun, 22 May 2022 23:20:59 +0300 Subject: [PATCH 218/458] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08b2e1a69..444544149 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Removed `Get-PnPAvailableClientSideComponents`. Use `Get-PnPPageComponent -Page -ListAvailable` instead. [#1833](https://github.com/pnp/powershell/pull/1833) ### Contributors + +- Aleksandr Sapozhkov [shurick81] +- [spg-iwilson] - Jago Pauwels [jagopauwels] - [4ndri] - Martin Lingstuyl [martinlingstuyl] From 46658994c061b4697cc1028389388e8975f324f1 Mon Sep 17 00:00:00 2001 From: 4ndri Date: Sun, 22 May 2022 22:21:54 +0200 Subject: [PATCH 219/458] Bugfix/convert json number (#1879) * convert json number to double or int * ConvertToPSObject Json List * remove debug launchSettings --- src/Commands/Utilities/Json/Convert.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Commands/Utilities/Json/Convert.cs b/src/Commands/Utilities/Json/Convert.cs index 872534eb1..f08812d5b 100644 --- a/src/Commands/Utilities/Json/Convert.cs +++ b/src/Commands/Utilities/Json/Convert.cs @@ -30,6 +30,12 @@ public static PSObject ConvertToPSObject(JsonElement element, string jsonPropert { return (PSObject)value; } + if(value is List) + { + var pso = new PSObject(); + pso.Properties.Add(new PSNoteProperty("Values", value)); + return pso; + } throw new FormatException($"primitive type[{element.ValueKind}] can not be converted to PSObject"); } @@ -73,7 +79,14 @@ public static object ConvertToObject(JsonElement element) } case JsonValueKind.Number: { - value = element.GetInt64(); + if (element.TryGetInt64(out long valLong)) + { + value = valLong; + } + else if (element.TryGetDouble(out double valDouble)) + { + value = valDouble; + } break; } } From f6354490e5421cf3c18c2f06661652ca9a29fe5d Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sun, 22 May 2022 23:25:01 +0300 Subject: [PATCH 220/458] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 444544149..7ba07a1bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Invoke-PnPGraphMethod` cmdlet to invoke generic Microsoft Graph API Methods. [#1820](https://github.com/pnp/powershell/pull/1820) - Added `Stop-PnPFlowRun` cmdlet to stop/cancel a specific Power Automate flow run. [#1838](https://github.com/pnp/powershell/pull/1838) - Added `Remove-PnPTeamsChannelUser` cmdlet to remove a user from a private channel. [#1840](https://github.com/pnp/powershell/pull/1840) +- Added new `PnP.PowerShell` image which also gets published to Docker Hub. [#1580](https://github.com/pnp/powershell/pull/1794) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) @@ -33,7 +34,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Get-PnPGroup` , `Get-PnPGroupPermissions` and `Set-PnPGroupPermissions ` cmdlets by making them more consistent. They will also throw error if a group is not found. [#1808](https://github.com/pnp/powershell/pull/1808) - Fixed `Get-PnPFile` issue with every 3rd file download in PS 5. - Fixed `Add-PnPContentTypesFromContentTypeHub`, if `Site` parameter is specified, it will be used now to sync content types from content type hub site. -- Fixed `Get-PnPTeamsTeam`, the cmdlet now also returns additional properties like `WebUrl, CreatedDateTime, InternalId` [#1825](https://github.com/pnp/powershell/pull/1825) +- Fixed `Get-PnPTeamsTeam`, the cmdlet now also returns additional properties like `WebUrl, CreatedDateTime, InternalId`. [#1825](https://github.com/pnp/powershell/pull/1825) +- Fixed `Invoke-PnPSPRestMethod` invalid parsing for SharePoint number columns. [#1877](https://github.com/pnp/powershell/pull/1879) ### Removed - Removed `Get-PnPAvailableClientSideComponents`. Use `Get-PnPPageComponent -Page -ListAvailable` instead. [#1833](https://github.com/pnp/powershell/pull/1833) From b61eacb3a5a36062e050b9cb2bcb8b0d0b6915c2 Mon Sep 17 00:00:00 2001 From: 4ndri Date: Sun, 22 May 2022 22:29:00 +0200 Subject: [PATCH 221/458] launchsettings for debugging in visual studio (#1880) --- src/Commands/Properties/launchSettings.json | 10 +++++++++ src/Commands/_debug/debug.ps1 | 24 +++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 src/Commands/Properties/launchSettings.json create mode 100644 src/Commands/_debug/debug.ps1 diff --git a/src/Commands/Properties/launchSettings.json b/src/Commands/Properties/launchSettings.json new file mode 100644 index 000000000..6f38dd905 --- /dev/null +++ b/src/Commands/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "PnP.PowerShell-Module": { + "commandName": "Executable", + "executablePath": "pwsh", + "commandLineArgs": "-NoExit -NoProfile -Command Import-Module $(ProjectDir)/_debug/debug.ps1", + "workingDirectory": "$(ProjectDir)\\..\\..\\" + } + } +} \ No newline at end of file diff --git a/src/Commands/_debug/debug.ps1 b/src/Commands/_debug/debug.ps1 new file mode 100644 index 000000000..e2284020f --- /dev/null +++ b/src/Commands/_debug/debug.ps1 @@ -0,0 +1,24 @@ +$ProjectPath = $PSScriptRoot | Split-Path -Parent +$BinPath = "$ProjectPath\bin\Debug" + +$dlls = @("PnP.PowerShell.ALC.dll", "PnP.PowerShell.dll") + +$netversion = "netcoreapp3.1" + +if ($PSEdition -eq 'Core') { + $netversion = "netcoreapp3.1" +} +else { + $netversion = "net461" +} + +$BinPath = "$BinPath\$netversion" + +foreach ($dll in $dlls) { + try { + Import-Module "$BinPath\$dll" -Force -Global + } + catch { + Write-Warning -Message "load.ps1: Import Modules -- $($_.Exception.Message)" + } +} \ No newline at end of file From 3800323f8931cb4f7861590d8e4325787d472a31 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sun, 22 May 2022 23:30:43 +0300 Subject: [PATCH 222/458] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ba07a1bd..a60d362fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Stop-PnPFlowRun` cmdlet to stop/cancel a specific Power Automate flow run. [#1838](https://github.com/pnp/powershell/pull/1838) - Added `Remove-PnPTeamsChannelUser` cmdlet to remove a user from a private channel. [#1840](https://github.com/pnp/powershell/pull/1840) - Added new `PnP.PowerShell` image which also gets published to Docker Hub. [#1580](https://github.com/pnp/powershell/pull/1794) +- Added capability to Debug the module in Visual Studio. [#1880](https://github.com/pnp/powershell/pull/1880) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) From cb243180fbe3281ef8bed12678bd955c76939b8d Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Sun, 22 May 2022 22:33:14 +0200 Subject: [PATCH 223/458] Adds cmdlet Set-PnPTeamsChannelUser (#1865) * Add cmdlet Set-PnPTeamsChannelUser * Docs syntax typo --- documentation/Add-PnpTeamsChannelUser.md | 2 +- documentation/Set-PnpTeamsChannelUser.md | 105 ++++++++++++++++++++++ src/Commands/Teams/SetTeamsChannelUser.cs | 63 +++++++++++++ src/Commands/Utilities/TeamsUtility.cs | 15 ++++ 4 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 documentation/Set-PnpTeamsChannelUser.md create mode 100644 src/Commands/Teams/SetTeamsChannelUser.cs diff --git a/documentation/Add-PnpTeamsChannelUser.md b/documentation/Add-PnpTeamsChannelUser.md index 1a58e6c28..1f4180e54 100644 --- a/documentation/Add-PnpTeamsChannelUser.md +++ b/documentation/Add-PnpTeamsChannelUser.md @@ -64,7 +64,7 @@ The id or name of the channel to retrieve. Type: TeamsChannelPipeBind Parameter Sets: (All) -Required: False +Required: True Position: Named Default value: None Accept pipeline input: False diff --git a/documentation/Set-PnpTeamsChannelUser.md b/documentation/Set-PnpTeamsChannelUser.md new file mode 100644 index 000000000..b28248214 --- /dev/null +++ b/documentation/Set-PnpTeamsChannelUser.md @@ -0,0 +1,105 @@ +--- +Module Name: PnP.PowerShell +schema: 2.0.0 +applicable: SharePoint Online +online version: https://pnp.github.io/powershell/cmdlets/Set-PnPTeamsChannelUser.html +external help file: PnP.PowerShell.dll-Help.xml +title: Set-PnPTeamsChannelUser +--- + +# Set-PnPTeamsChannelUser + +## SYNOPSIS + +**Required Permissions** + + * Microsoft Graph API: ChannelMember.ReadWrite.All + +Updates the role of a user in an existing Microsoft Teams private channel. + +## SYNTAX + +```powershell +Set-PnPTeamsChannelUser -Team -Channel -Identity -Role [] +``` + +## DESCRIPTION + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Set-PnPTeamsChannelUser -Team 4efdf392-8225-4763-9e7f-4edeb7f721aa -Channel "19:796d063b63e34497aeaf092c8fb9b44e@thread.skype" -Identity MCMjMiMjMDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMDAwMDAwMDAwMDAwIyMxOTowMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMEB0aHJlYWQuc2t5cGUjIzAwMDAwMDAwLTAwMDAtMDAwMC0wMDAwLTAwMDAwMDAwMDAwMA== -Role Owner +``` + +Updates the user with specific membership ID as owner of the specified Teams private channel. + +### EXAMPLE 2 +```powershell +Set-PnPTeamsChannelUser -Team "My Team" -Channel "My Private Channel" -Identity john@doe.com -Role Member +``` + +Updates the user john@doe.com as member of the specified Teams private channel. + +## PARAMETERS + +### -Team +Specify the group id, mailNickname or display name of the team to use. + +```yaml +Type: TeamsTeamPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Channel +The id or name of the channel to retrieve. + +```yaml +Type: TeamsChannelPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Identity +Specify membership id, UPN or user ID of the channel member. + +```yaml +Type: TeamsChannelMemberPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Role +Specify the role of the user + +```yaml +Type: String +Parameter Sets: (All) +Accepted values: Owner, Member + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/src/Commands/Teams/SetTeamsChannelUser.cs b/src/Commands/Teams/SetTeamsChannelUser.cs new file mode 100644 index 000000000..d985bc2e9 --- /dev/null +++ b/src/Commands/Teams/SetTeamsChannelUser.cs @@ -0,0 +1,63 @@ +using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Model.Graph; +using PnP.PowerShell.Commands.Utilities; +using System.Management.Automation; + +namespace PnP.PowerShell.Commands.Teams +{ + [Cmdlet(VerbsCommon.Set, "PnPTeamsChannelUser")] + [RequiredMinimalApiPermissions("ChannelMember.ReadWrite.All")] + public class SetTeamsChannelUser : PnPGraphCmdlet + { + [Parameter(Mandatory = true)] + public TeamsTeamPipeBind Team; + + [Parameter(Mandatory = true)] + public TeamsChannelPipeBind Channel; + + [Parameter(Mandatory = true)] + public TeamsChannelMemberPipeBind Identity; + + [Parameter(Mandatory = true)] + [ValidateSet("Owner", "Member")] + public string Role; + + protected override void ExecuteCmdlet() + { + var groupId = Team.GetGroupId(HttpClient, AccessToken); + if (groupId == null) + { + throw new PSArgumentException("Group not found"); + } + + var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + if (channelId == null) + { + throw new PSArgumentException("Channel not found"); + } + + var membershipId = Identity.GetIdAsync(HttpClient, AccessToken, groupId, channelId).GetAwaiter().GetResult(); + if (string.IsNullOrEmpty(membershipId)) + { + throw new PSArgumentException("User was not found in the specified Teams channel"); + } + + try + { + var updatedMember = TeamsUtility.UpdateChannelMemberAsync(HttpClient, AccessToken, groupId, channelId, membershipId, Role).GetAwaiter().GetResult(); + WriteObject(updatedMember); + } + catch (GraphException ex) + { + if (ex.Error != null) + { + throw new PSInvalidOperationException(ex.Error.Message); + } + + throw; + } + } + } +} diff --git a/src/Commands/Utilities/TeamsUtility.cs b/src/Commands/Utilities/TeamsUtility.cs index 1b095d556..af12592d9 100644 --- a/src/Commands/Utilities/TeamsUtility.cs +++ b/src/Commands/Utilities/TeamsUtility.cs @@ -697,6 +697,21 @@ public static async Task DeleteChannelMemberAsync(HttpClien return await GraphHelper.DeleteAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/members/{membershipId}", accessToken); } + /// + /// Update the role of a specific member of a Microsoft Teams channel. + /// + /// Updated membership object. + public static async Task UpdateChannelMemberAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string membershipId, string role) + { + var channelMember = new TeamChannelMember(); + + // User role. Empty for member, 'owner' for owner. + if (role.Equals("owner", StringComparison.OrdinalIgnoreCase)) + channelMember.Roles.Add("owner"); + + return await GraphHelper.PatchAsync(httpClient, accessToken, $"v1.0/teams/{groupId}/channels/{channelId}/members/{membershipId}", channelMember); + } + #endregion #region Tabs From fb538c0e6d373fca38b8b5514d0c07031dd692b1 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sun, 22 May 2022 23:35:06 +0300 Subject: [PATCH 224/458] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a60d362fb..6157c1692 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Remove-PnPTeamsChannelUser` cmdlet to remove a user from a private channel. [#1840](https://github.com/pnp/powershell/pull/1840) - Added new `PnP.PowerShell` image which also gets published to Docker Hub. [#1580](https://github.com/pnp/powershell/pull/1794) - Added capability to Debug the module in Visual Studio. [#1880](https://github.com/pnp/powershell/pull/1880) +- Added `Set-PnPTeamsChannelUser` cmdlet to update the role of user in a private channel. [#1865](https://github.com/pnp/powershell/pull/1865) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) From 45aa92d3a4d2297b6ffa4d08ac83e3d10d6b189f Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sun, 22 May 2022 23:36:38 +0300 Subject: [PATCH 225/458] Feature: bumping .NET Framework version to 4.6.2 (#1856) * Feature: bumping .NET Framework version to 4.6.2 * Added changelog entry --- CHANGELOG.md | 1 + src/Commands/PnP.PowerShell.csproj | 6 +++--- src/Commands/WindowsSdk.targets | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6157c1692..5e4f1a3b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) +- Bumped .NET Framework version to 4.6.2 as the 4.6.1 is not supported anymore. [#1856](https://github.com/pnp/powershell/pull/1856) ### Fixed diff --git a/src/Commands/PnP.PowerShell.csproj b/src/Commands/PnP.PowerShell.csproj index b0a340278..621fcd9e4 100644 --- a/src/Commands/PnP.PowerShell.csproj +++ b/src/Commands/PnP.PowerShell.csproj @@ -31,7 +31,7 @@ Linux - + TRACE;$(DefineConstants);NETFRAMEWORK @@ -133,11 +133,11 @@ - + - + diff --git a/src/Commands/WindowsSdk.targets b/src/Commands/WindowsSdk.targets index 905d55690..925421b76 100644 --- a/src/Commands/WindowsSdk.targets +++ b/src/Commands/WindowsSdk.targets @@ -1,7 +1,7 @@ - netcoreapp3.1;net461 + netcoreapp3.1;net462 true From 93a176b5f55668502ede55fe3acd251af2caee1a Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sun, 22 May 2022 23:42:25 +0300 Subject: [PATCH 226/458] Fix #1882 - issue with Set-PnPListPermission (#1891) --- CHANGELOG.md | 2 + src/Commands/Lists/SetListPermission.cs | 88 +++++++++++++------------ 2 files changed, 47 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e4f1a3b6..7956a3bc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,8 +38,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Get-PnPFile` issue with every 3rd file download in PS 5. - Fixed `Add-PnPContentTypesFromContentTypeHub`, if `Site` parameter is specified, it will be used now to sync content types from content type hub site. - Fixed `Get-PnPTeamsTeam`, the cmdlet now also returns additional properties like `WebUrl, CreatedDateTime, InternalId`. [#1825](https://github.com/pnp/powershell/pull/1825) +- Fixed `Set-PnPListPermission`, it will now throw error if the list does not exist. [#1891](https://github.com/pnp/powershell/pull/1891) - Fixed `Invoke-PnPSPRestMethod` invalid parsing for SharePoint number columns. [#1877](https://github.com/pnp/powershell/pull/1879) + ### Removed - Removed `Get-PnPAvailableClientSideComponents`. Use `Get-PnPPageComponent -Page -ListAvailable` instead. [#1833](https://github.com/pnp/powershell/pull/1833) diff --git a/src/Commands/Lists/SetListPermission.cs b/src/Commands/Lists/SetListPermission.cs index e58613fe0..174ad8394 100644 --- a/src/Commands/Lists/SetListPermission.cs +++ b/src/Commands/Lists/SetListPermission.cs @@ -31,60 +31,62 @@ protected override void ExecuteCmdlet() { var list = Identity.GetList(CurrentWeb); - if (list != null) + if (list == null) { - Principal principal = null; - if (ParameterSetName == "Group") + throw new PSArgumentException($"No list found with id, title or url '{Identity}'", "Identity"); + } + + Principal principal = null; + if (ParameterSetName == "Group") + { + if (Group.Id != -1) { - if (Group.Id != -1) - { - principal = CurrentWeb.SiteGroups.GetById(Group.Id); - } - else if (!string.IsNullOrEmpty(Group.Name)) - { - principal = CurrentWeb.SiteGroups.GetByName(Group.Name); - } - else if (Group.Group != null) - { - principal = Group.Group; - } + principal = CurrentWeb.SiteGroups.GetById(Group.Id); + } + else if (!string.IsNullOrEmpty(Group.Name)) + { + principal = CurrentWeb.SiteGroups.GetByName(Group.Name); } - else + else if (Group.Group != null) { - principal = CurrentWeb.EnsureUser(User); + principal = Group.Group; + } + } + else + { + principal = CurrentWeb.EnsureUser(User); + ClientContext.ExecuteQueryRetry(); + } + if (principal != null) + { + if (!string.IsNullOrEmpty(AddRole)) + { + var roleDefinition = CurrentWeb.RoleDefinitions.GetByName(AddRole); + var roleDefinitionBindings = new RoleDefinitionBindingCollection(ClientContext); + roleDefinitionBindings.Add(roleDefinition); + var roleAssignments = list.RoleAssignments; + roleAssignments.Add(principal, roleDefinitionBindings); + ClientContext.Load(roleAssignments); ClientContext.ExecuteQueryRetry(); } - if (principal != null) + if (!string.IsNullOrEmpty(RemoveRole)) { - if (!string.IsNullOrEmpty(AddRole)) - { - var roleDefinition = CurrentWeb.RoleDefinitions.GetByName(AddRole); - var roleDefinitionBindings = new RoleDefinitionBindingCollection(ClientContext); - roleDefinitionBindings.Add(roleDefinition); - var roleAssignments = list.RoleAssignments; - roleAssignments.Add(principal, roleDefinitionBindings); - ClientContext.Load(roleAssignments); - ClientContext.ExecuteQueryRetry(); - } - if (!string.IsNullOrEmpty(RemoveRole)) + var roleAssignment = list.RoleAssignments.GetByPrincipal(principal); + var roleDefinitionBindings = roleAssignment.RoleDefinitionBindings; + ClientContext.Load(roleDefinitionBindings); + ClientContext.ExecuteQueryRetry(); + foreach (var roleDefinition in roleDefinitionBindings.Where(roleDefinition => roleDefinition.Name == RemoveRole)) { - var roleAssignment = list.RoleAssignments.GetByPrincipal(principal); - var roleDefinitionBindings = roleAssignment.RoleDefinitionBindings; - ClientContext.Load(roleDefinitionBindings); + roleDefinitionBindings.Remove(roleDefinition); + roleAssignment.Update(); ClientContext.ExecuteQueryRetry(); - foreach (var roleDefinition in roleDefinitionBindings.Where(roleDefinition => roleDefinition.Name == RemoveRole)) - { - roleDefinitionBindings.Remove(roleDefinition); - roleAssignment.Update(); - ClientContext.ExecuteQueryRetry(); - break; - } + break; } } - else - { - WriteError(new ErrorRecord(new Exception("Principal not found"), "1", ErrorCategory.ObjectNotFound, null)); - } + } + else + { + WriteError(new ErrorRecord(new Exception("Principal not found"), "1", ErrorCategory.ObjectNotFound, null)); } } } From 29b5687fe6514e4f12efa09a663b59b98069ffe4 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sun, 22 May 2022 23:46:59 +0300 Subject: [PATCH 227/458] Fix: Get-PnPOrgAssetsLibrary to return correct data (#1889) * Fix: Get-PnPOrgAssetsLibrary to return correct data * Update CHANGELOG.md --- CHANGELOG.md | 1 + src/Commands/Admin/GetOrgAssetsLibrary.cs | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7956a3bc9..61d9d5749 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) +- Changed `Get-PnPOrgAssetsLibrary` to return a proper value of the organisation assets libraries. [#1889](https://github.com/pnp/powershell/pull/1889) - Bumped .NET Framework version to 4.6.2 as the 4.6.1 is not supported anymore. [#1856](https://github.com/pnp/powershell/pull/1856) ### Fixed diff --git a/src/Commands/Admin/GetOrgAssetsLibrary.cs b/src/Commands/Admin/GetOrgAssetsLibrary.cs index 2b0eedee7..744b7afa6 100644 --- a/src/Commands/Admin/GetOrgAssetsLibrary.cs +++ b/src/Commands/Admin/GetOrgAssetsLibrary.cs @@ -1,6 +1,9 @@ -using Microsoft.SharePoint.Client; +using Microsoft.SharePoint.Administration; +using Microsoft.SharePoint.Client; using PnP.PowerShell.Commands.Base; +using System.Collections.Generic; +using System.Linq; using System.Management.Automation; namespace PnP.PowerShell.Commands.Admin @@ -12,7 +15,9 @@ protected override void ExecuteCmdlet() { var results = Tenant.GetOrgAssets(); ClientContext.ExecuteQueryRetry(); - WriteObject(results.Value, true); + + List orgassetlibs = results.Value?.OrgAssetsLibraries?.ToList(); + WriteObject(orgassetlibs, true); } } } \ No newline at end of file From d46ea8eff486cb4cc48d97db5686c457908ab45e Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sun, 22 May 2022 23:50:04 +0300 Subject: [PATCH 228/458] Fix #1595 - issue with tax item update (#1870) --- src/Commands/Utilities/ListItemHelper.cs | 31 +++++++++++++++++++----- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/Commands/Utilities/ListItemHelper.cs b/src/Commands/Utilities/ListItemHelper.cs index d8c62a54b..043824892 100644 --- a/src/Commands/Utilities/ListItemHelper.cs +++ b/src/Commands/Utilities/ListItemHelper.cs @@ -129,7 +129,14 @@ public static void SetFieldValues(this ListItem item, Hashtable valuesToSet, Cmd clonedContext.Load(taxonomyItem); clonedContext.ExecuteQueryRetry(); } - terms.Add(new KeyValuePair(taxonomyItem.Id, taxonomyItem.Name)); + if(taxonomyItem != null) + { + terms.Add(new KeyValuePair(taxonomyItem.Id, taxonomyItem.Name)); + } + else + { + cmdlet.WriteWarning($@"Unable to find the specified term.Skipping values for field ""{field.InternalName}"""); + } } TaxonomyField taxField = context.CastTo(field); @@ -142,14 +149,17 @@ public static void SetFieldValues(this ListItem item, Hashtable valuesToSet, Cmd termValuesString += "-1;#" + term.Value + "|" + term.Key.ToString("D") + ";#"; } - termValuesString = termValuesString.Substring(0, termValuesString.Length - 2); + if (!string.IsNullOrEmpty(termValuesString)) + { + termValuesString = termValuesString.Substring(0, termValuesString.Length - 2); - var newTaxFieldValue = new TaxonomyFieldValueCollection(context, termValuesString, taxField); - itemValues.Add(new FieldUpdateValue(key as string, newTaxFieldValue, field.TypeAsString)); + var newTaxFieldValue = new TaxonomyFieldValueCollection(context, termValuesString, taxField); + itemValues.Add(new FieldUpdateValue(key as string, newTaxFieldValue, field.TypeAsString)); + } } else { - cmdlet.WriteWarning(@"You are trying to set multiple values in a single value field. Skipping values for field ""{field.InternalName}"""); + cmdlet.WriteWarning($@"You are trying to set multiple values in a single value field. Skipping values for field ""{field.InternalName}"""); } } else @@ -158,10 +168,16 @@ public static void SetFieldValues(this ListItem item, Hashtable valuesToSet, Cmd var taxSession = clonedContext.Site.GetTaxonomySession(); TaxonomyItem taxonomyItem = null; + bool updateTaxItemValue = true; if (value != null && !Guid.TryParse(value as string, out termGuid)) { // Assume it's a TermPath taxonomyItem = clonedContext.Site.GetTaxonomyItemByPath(value as string); + if (taxonomyItem == null) + { + updateTaxItemValue = false; + cmdlet.WriteWarning($@"Unable to find the specified term.Skipping values for field ""{field.InternalName}"""); + } } else { @@ -183,7 +199,10 @@ public static void SetFieldValues(this ListItem item, Hashtable valuesToSet, Cmd } else { - taxField.ValidateSetValue(item, null); + if(updateTaxItemValue) + { + taxField.ValidateSetValue(item, null); + } } } break; From f2ddb1d4cfaaf9d9b024c2305b3185233a9c2a09 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sun, 22 May 2022 23:52:16 +0300 Subject: [PATCH 229/458] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61d9d5749..5e4a83e46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,7 +41,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Get-PnPTeamsTeam`, the cmdlet now also returns additional properties like `WebUrl, CreatedDateTime, InternalId`. [#1825](https://github.com/pnp/powershell/pull/1825) - Fixed `Set-PnPListPermission`, it will now throw error if the list does not exist. [#1891](https://github.com/pnp/powershell/pull/1891) - Fixed `Invoke-PnPSPRestMethod` invalid parsing for SharePoint number columns. [#1877](https://github.com/pnp/powershell/pull/1879) - +- Fix issue with `Add/Set-PnPListItem` not throwing correct exception for invalid taxonomy values. [#1870](https://github.com/pnp/powershell/pull/1870) ### Removed - Removed `Get-PnPAvailableClientSideComponents`. Use `Get-PnPPageComponent -Page -ListAvailable` instead. [#1833](https://github.com/pnp/powershell/pull/1833) From fd16734f28cf59d82f2e6aaaa1de6eb15d1f5180 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sun, 22 May 2022 23:57:28 +0300 Subject: [PATCH 230/458] Fix: batch issue with Add/Set-PnPListItem (#1890) * Fix batch issues with nonstring values * Added changelog --- CHANGELOG.md | 2 ++ src/Commands/Utilities/ListItemHelper.cs | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e4a83e46..58295c011 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Get-PnPGroup` , `Get-PnPGroupPermissions` and `Set-PnPGroupPermissions ` cmdlets by making them more consistent. They will also throw error if a group is not found. [#1808](https://github.com/pnp/powershell/pull/1808) - Fixed `Get-PnPFile` issue with every 3rd file download in PS 5. - Fixed `Add-PnPContentTypesFromContentTypeHub`, if `Site` parameter is specified, it will be used now to sync content types from content type hub site. +- Fixed `Get-PnPTeamsTeam`, the cmdlet now also returns additional properties like `WebUrl, CreatedDateTime, InternalId` [#1825](https://github.com/pnp/powershell/pull/1825) +- Fixed `Add/Set-PnPListItem` , the cmdlet now works correctly with `-Batch` parameter for field types other than string. [#1890](https://github.com/pnp/powershell/pull/1890) - Fixed `Get-PnPTeamsTeam`, the cmdlet now also returns additional properties like `WebUrl, CreatedDateTime, InternalId`. [#1825](https://github.com/pnp/powershell/pull/1825) - Fixed `Set-PnPListPermission`, it will now throw error if the list does not exist. [#1891](https://github.com/pnp/powershell/pull/1891) - Fixed `Invoke-PnPSPRestMethod` invalid parsing for SharePoint number columns. [#1877](https://github.com/pnp/powershell/pull/1879) diff --git a/src/Commands/Utilities/ListItemHelper.cs b/src/Commands/Utilities/ListItemHelper.cs index 043824892..6cdfbb02d 100644 --- a/src/Commands/Utilities/ListItemHelper.cs +++ b/src/Commands/Utilities/ListItemHelper.cs @@ -391,7 +391,7 @@ public static Dictionary GetFieldValues(PnP.Core.Model.SharePoin } if (value != null && value.GetType().IsArray) { - var fieldValueCollection = field.NewFieldValueCollection(); + var fieldValueCollection = field.NewFieldValueCollection(); foreach (var arrayItem in value as object[]) { Term taxonomyItem; @@ -548,7 +548,8 @@ public static Dictionary GetFieldValues(PnP.Core.Model.SharePoin } default: { - item[key as string] = values[key]; + object itemValue = values[key] is PSObject ? ((PSObject)values[key]).BaseObject : values[key]; + item[key as string] = itemValue; break; } } From 1943cc2a2a6124b7299af10c29905ec8321a21e1 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Mon, 23 May 2022 10:03:21 +0300 Subject: [PATCH 231/458] Update Build-Nightly.ps1 Fix nightly --- build/Build-Nightly.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/Build-Nightly.ps1 b/build/Build-Nightly.ps1 index 717239144..53d9e1efc 100644 --- a/build/Build-Nightly.ps1 +++ b/build/Build-Nightly.ps1 @@ -115,7 +115,7 @@ if ($runPublish -eq $true) { Get-ChildItem -Path "$PSScriptRoot/../src/ALC/bin/Release/netstandard2.0" | Where-Object { $_.Extension -in '.dll', '.pdb' } | Foreach-Object { if (!$assemblyExceptions.Contains($_.Name)) { [void]$commonFiles.Add($_.Name) }; Copy-Item -LiteralPath $_.FullName -Destination $commonPath } Get-ChildItem -Path "$PSScriptRoot/../src/Commands/bin/Release/netcoreapp3.1" | Where-Object { $_.Extension -in '.dll', '.pdb' -and -not $commonFiles.Contains($_.Name) } | Foreach-Object { Copy-Item -LiteralPath $_.FullName -Destination $corePath } if (!$IsLinux -and !$IsMacOs) { - Get-ChildItem -Path "$PSScriptRoot/../src/Commands/bin/Release/net461" | Where-Object { $_.Extension -in '.dll', '.pdb' -and -not $commonFiles.Contains($_.Name) } | Foreach-Object { Copy-Item -LiteralPath $_.FullName -Destination $frameworkPath } + Get-ChildItem -Path "$PSScriptRoot/../src/Commands/bin/Release/net462" | Where-Object { $_.Extension -in '.dll', '.pdb' -and -not $commonFiles.Contains($_.Name) } | Foreach-Object { Copy-Item -LiteralPath $_.FullName -Destination $frameworkPath } } } Catch { @@ -171,7 +171,7 @@ if ($runPublish -eq $true) { CompanyName = 'Microsoft 365 Patterns and Practices' CompatiblePSEditions = @(`"Core`",`"Desktop`") PowerShellVersion = '5.1' - DotNetFrameworkVersion = '4.6.1' + DotNetFrameworkVersion = '4.6.2' ProcessorArchitecture = 'None' FunctionsToExport = '*' CmdletsToExport = @($cmdletsString) @@ -208,4 +208,4 @@ if ($runPublish -eq $true) { # Write version back to version Set-Content ./version.txt -Value $version -Force -NoNewline -} \ No newline at end of file +} From 1076bf502138ddffca149378ed1f90add2452cbf Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Mon, 23 May 2022 07:08:02 +0000 Subject: [PATCH 232/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index 538e6a0ee..8184c545d 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -45a0db5dddfc6f014928f942cbe2612729187d02 \ No newline at end of file +0aef907136918af434033fe2a56048d569cc01dc \ No newline at end of file diff --git a/version.txt b/version.txt index 23aa90704..7fa69eb07 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.26 \ No newline at end of file +1.10.27 \ No newline at end of file From 6f9bc1973366025bec2085c1c9761728fb6b3607 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Mon, 23 May 2022 10:13:41 +0300 Subject: [PATCH 233/458] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 27230e745..1d3665730 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # PnP PowerShell -**PnP PowerShell** is a .NET Core 3.1 / .NET Framework 4.6.1 based PowerShell Module providing over 600 cmdlets that work with Microsoft 365 environments such as SharePoint Online, Microsoft Teams, Microsoft Project, Security & Compliance, Azure Active Directory, and more. +**PnP PowerShell** is a .NET Core 3.1 / .NET Framework 4.6.2 based PowerShell Module providing over 600 cmdlets that work with Microsoft 365 environments such as SharePoint Online, Microsoft Teams, Microsoft Project, Security & Compliance, Azure Active Directory, and more. Last version | Last nightly version -------------|--------------------- From 4db70c19a3991b9f1d9b277180d36797ebb7fce4 Mon Sep 17 00:00:00 2001 From: Bart-Jan Dekker Date: Mon, 23 May 2022 20:16:17 +0200 Subject: [PATCH 234/458] Updated example of the query parameter (#1893) * Updated example of the query parameter * Updated example of the query parameter Co-authored-by: Bart-Jan --- documentation/Add-PnPDataRowsToSiteTemplate.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/Add-PnPDataRowsToSiteTemplate.md b/documentation/Add-PnPDataRowsToSiteTemplate.md index 45a3bc444..a6f1551fb 100644 --- a/documentation/Add-PnPDataRowsToSiteTemplate.md +++ b/documentation/Add-PnPDataRowsToSiteTemplate.md @@ -26,17 +26,17 @@ Add-PnPDataRowsToSiteTemplate [-Path] -List [-Query ' -Fields 'Title','Choice' +Add-PnPDataRowsToSiteTemplate -Path template.pnp -List 'PnPTestList' -Fields 'Title','Choice' ``` Adds datarows from the provided list to the PnP Provisioning Template at the provided location ### EXAMPLE 2 ```powershell -Add-PnPDataRowsToSiteTemplate -Path template.pnp -List 'PnPTestList' -Query '' -Fields 'Title','Choice' -IncludeSecurity +Add-PnPDataRowsToSiteTemplate -Path template.pnp -List 'PnPTestList' -Query '' -Fields 'Title','Choice' -IncludeSecurity ``` -Adds datarows from the provided list to the PnP Provisioning Template at the provided location +Adds datarows from the provided list to the PnP Provisioning Template at the provided location, only the items that have changed since a week ago ## PARAMETERS From 5b1c09f9aa2905115badfb116ae3c45f553d60f3 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Mon, 23 May 2022 21:17:35 +0300 Subject: [PATCH 235/458] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58295c011..8e552f746 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Contributors +- Bart-Jan Dekker [bjdekker] - Aleksandr Sapozhkov [shurick81] - [spg-iwilson] - Jago Pauwels [jagopauwels] From 16119fad3e8d4028fab1d2d9b40d5e72142efefd Mon Sep 17 00:00:00 2001 From: Aleks Date: Mon, 23 May 2022 23:39:47 +0200 Subject: [PATCH 236/458] parametrizing DOCKER_ORG --- .github/workflows/nightlyrelease.yml | 16 +++++++-------- .github/workflows/release.yml | 6 +++--- docker/Publish-UnpublishedImage.ps1 | 23 ++++++++++++--------- docker/README.md | 30 ++++++++++++++-------------- 4 files changed, 40 insertions(+), 35 deletions(-) diff --git a/.github/workflows/nightlyrelease.yml b/.github/workflows/nightlyrelease.yml index f647794d7..1cc121657 100644 --- a/.github/workflows/nightlyrelease.yml +++ b/.github/workflows/nightlyrelease.yml @@ -47,12 +47,12 @@ jobs: - name: Build an image run: | $VERSION="$(cat ./version.txt)-nightly" - docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-ltsc2022" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag "${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-ltsc2022"; + docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-ltsc2022" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag "${{ secrets.DOCKER_ORG }}/powershell:$VERSION-nanoserver-ltsc2022"; - name: Push the image run: | $VERSION="$(cat ./version.txt)-nightly" docker login -u ${{ secrets.DOCKER_USERNAME }} -p '${{ secrets.DOCKER_PASSWORD }}' - docker push "${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-ltsc2022" + docker push "${{ secrets.DOCKER_ORG }}/powershell:$VERSION-nanoserver-ltsc2022" publish-docker-windows-2019: runs-on: windows-2019 needs: [ build ] @@ -61,12 +61,12 @@ jobs: - name: Build an image run: | $VERSION="$(cat ./version.txt)-nightly" - docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-1809" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag "${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-1809"; + docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-1809" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag "${{ secrets.DOCKER_ORG }}/powershell:$VERSION-nanoserver-1809"; - name: Push the image run: | $VERSION="$(cat ./version.txt)-nightly" docker login -u ${{ secrets.DOCKER_USERNAME }} -p '${{ secrets.DOCKER_PASSWORD }}' - docker push "${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-nanoserver-1809" + docker push "${{ secrets.DOCKER_ORG }}/powershell:$VERSION-nanoserver-1809" publish-docker-linux: runs-on: ubuntu-latest needs: [ build ] @@ -75,14 +75,14 @@ jobs: - name: Build an image run: | VERSION=$(cat ./version.txt)-nightly - docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=alpine-3.14" --build-arg "INSTALL_USER=root" --build-arg "SKIP_PUBLISHER_CHECK=False" ./docker -f ./docker/pnppowershell.dockerFile --tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-alpine-3.14; + docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=alpine-3.14" --build-arg "INSTALL_USER=root" --build-arg "SKIP_PUBLISHER_CHECK=False" ./docker -f ./docker/pnppowershell.dockerFile --tag ${{ secrets.DOCKER_ORG }}/powershell:$VERSION-alpine-3.14; - name: Tag the image run: | VERSION=$(cat ./version.txt)-nightly - docker image tag ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-alpine-3.14 ${{ secrets.DOCKER_USERNAME }}/powershell:nightly + docker image tag ${{ secrets.DOCKER_ORG }}/powershell:$VERSION-alpine-3.14 ${{ secrets.DOCKER_ORG }}/powershell:nightly - name: Push the image run: | VERSION=$(cat ./version.txt)-nightly docker login -u ${{ secrets.DOCKER_USERNAME }} -p '${{ secrets.DOCKER_PASSWORD }}' - docker push ${{ secrets.DOCKER_USERNAME }}/powershell:$VERSION-alpine-3.14 - docker push ${{ secrets.DOCKER_USERNAME }}/powershell:nightly + docker push ${{ secrets.DOCKER_ORG }}/powershell:$VERSION-alpine-3.14 + docker push ${{ secrets.DOCKER_ORG }}/powershell:nightly diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7f22680de..820b842db 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: shell: pwsh run: | $securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force - ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword "ContainerAdministrator" $true "nanoserver-ltsc2022" + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} ${{ secrets.DOCKER_IMAGE_NAME }} powershell $securedPassword "ContainerAdministrator" $true "nanoserver-ltsc2022" publish-docker-windows-2019: runs-on: windows-2019 steps: @@ -22,7 +22,7 @@ jobs: shell: pwsh run: | $securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force - ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword "ContainerAdministrator" $true "nanoserver-1809" + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} ${{ secrets.DOCKER_IMAGE_NAME }} powershell $securedPassword "ContainerAdministrator" $true "nanoserver-1809" publish-docker-linux: runs-on: ubuntu-latest steps: @@ -31,4 +31,4 @@ jobs: shell: pwsh run: | $securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force - ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} powershell $securedPassword "root" $false "alpine-3.14" + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} ${{ secrets.DOCKER_IMAGE_NAME }} powershell $securedPassword "root" $false "alpine-3.14" diff --git a/docker/Publish-UnpublishedImage.ps1 b/docker/Publish-UnpublishedImage.ps1 index c75b3fc02..d15912860 100644 --- a/docker/Publish-UnpublishedImage.ps1 +++ b/docker/Publish-UnpublishedImage.ps1 @@ -13,29 +13,34 @@ Param( Mandatory = $true, ValueFromPipeline = $false)] [String] - $DOCKER_IMAGE_NAME, + $DOCKER_ORG, [Parameter(Position = 3, Mandatory = $true, ValueFromPipeline = $false)] + [String] + $DOCKER_IMAGE_NAME, + [Parameter(Position = 4, + Mandatory = $true, + ValueFromPipeline = $false)] [Security.SecureString] $DOCKER_PASSWORD, - [Parameter(Position = 4, + [Parameter(Position = 5, Mandatory = $false, ValueFromPipeline = $false)] [String] $DOCKER_INSTALL_USER = "ContainerAdministrator", - [Parameter(Position = 5, + [Parameter(Position = 6, Mandatory = $false, ValueFromPipeline = $false)] [bool] $SKIP_PUBLISHER_CHECK = $false, - [Parameter(Position = 6, + [Parameter(Position = 7, Mandatory = $false, ValueFromPipeline = $false)] [String] $DOCKER_IMAGE_SUFFIX_ARRAY = "nanoserver-ltsc2022" ) -$publishedImageVersions = (Invoke-RestMethod https://registry.hub.docker.com/v2/repositories/$DOCKER_USERNAME/$DOCKER_IMAGE_NAME/tags?page_size=10240).results | % { +$publishedImageVersions = (Invoke-RestMethod https://registry.hub.docker.com/v2/repositories/$DOCKER_ORG/$DOCKER_IMAGE_NAME/tags?page_size=10240).results | % { $_.name } $moduleVersions = Find-Module $PS_MODULE_NAME -AllVersions; @@ -47,13 +52,13 @@ $moduleVersions | % { $imageVersion = "$moduleVersion-$baseImageSuffix"; Write-Host "Checking $imageVersion" if ( !( $publishedImageVersions -contains $imageVersion ) ) { - docker build --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "INSTALL_USER=$DOCKER_INSTALL_USER" --build-arg "SKIP_PUBLISHER_CHECK=$SKIP_PUBLISHER_CHECK" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; + docker build --build-arg "PNP_MODULE_VERSION=$moduleVersion" --build-arg "BASE_IMAGE_SUFFIX=$baseImageSuffix" --build-arg "INSTALL_USER=$DOCKER_INSTALL_USER" --build-arg "SKIP_PUBLISHER_CHECK=$SKIP_PUBLISHER_CHECK" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_ORG/$DOCKER_IMAGE_NAME`:$imageVersion; $plainStringPassword = [System.Net.NetworkCredential]::new("", $DOCKER_PASSWORD).Password; docker login -u $DOCKER_USERNAME -p "$plainStringPassword"; - docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion; + docker push $DOCKER_ORG/$DOCKER_IMAGE_NAME`:$imageVersion; if ( $baseImageSuffix -eq "alpine-3.14") { - docker image tag $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:$imageVersion $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; - docker push $DOCKER_USERNAME/$DOCKER_IMAGE_NAME`:latest; + docker image tag $DOCKER_ORG/$DOCKER_IMAGE_NAME`:$imageVersion $DOCKER_ORG/$DOCKER_IMAGE_NAME`:latest; + docker push $DOCKER_ORG/$DOCKER_IMAGE_NAME`:latest; } } } diff --git a/docker/README.md b/docker/README.md index 32dd74e1f..7083a7c98 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,64 +1,64 @@ # Publish manually in Windows -1. Set "DOCKER_USERNAME" and "DOCKER_PASSWORD" variables +1. Set `DOCKER_USERNAME`, `DOCKER_ORG` and `DOCKER_PASSWORD` variables 2. Run ```powershell $securedPassword = ConvertTo-SecureString $DOCKER_PASSWORD -AsPlainText -Force -./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME powershell $securedPassword "ContainerAdministrator" $true "nanoserver-1809" +./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME $DOCKER_ORG powershell $securedPassword "ContainerAdministrator" $true "nanoserver-1809" ``` # Publish manually in Linux -1. Set "DOCKER_USERNAME" and "DOCKER_PASSWORD" variables +1. Set `DOCKER_USERNAME`, `DOCKER_ORG` and `DOCKER_PASSWORD` variables 2. Run ```powershell $securedPassword = ConvertTo-SecureString $DOCKER_PASSWORD -AsPlainText -Force -./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME powershell $securedPassword $false "root" "alpine-3.14" +./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell $DOCKER_USERNAME $DOCKER_ORG powershell $securedPassword $false "root" "alpine-3.14" ``` # Publish with prereleases manually in Windows -1. Set "DOCKER_USERNAME" and "DOCKER_PASSWORD" variables +1. Set `DOCKER_USERNAME`, `DOCKER_ORG` and `DOCKER_PASSWORD` variables 2. Run ```PowerShell $VERSION="$(cat ./version.txt)-nightly" -docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-ltsc2022" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag "$DOCKER_USERNAME/powershell:$VERSION-nanoserver-ltsc2022"; +docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-ltsc2022" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag "$DOCKER_ORG/powershell:$VERSION-nanoserver-ltsc2022"; $VERSION="$(cat ./version.txt)-nightly" docker login -u $DOCKER_USERNAME -p "$DOCKER_PASSWORD" -docker push "$DOCKER_USERNAME/powershell:$VERSION-nanoserver-ltsc2022" +docker push "$DOCKER_ORG/powershell:$VERSION-nanoserver-ltsc2022" ``` or ```PowerShell $VERSION="$(cat ./version.txt)-nightly" -docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-1809" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag "$DOCKER_USERNAME/powershell:$VERSION-nanoserver-1809"; +docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=nanoserver-1809" --build-arg "INSTALL_USER=ContainerAdministrator" --build-arg "SKIP_PUBLISHER_CHECK=True" ./docker -f ./docker/pnppowershell.dockerFile --tag "$DOCKER_ORG/powershell:$VERSION-nanoserver-1809"; $VERSION="$(cat ./version.txt)-nightly" docker login -u $DOCKER_USERNAME -p "$DOCKER_PASSWORD" -docker push "$DOCKER_USERNAME/powershell:$VERSION-nanoserver-1809" +docker push "$DOCKER_ORG/powershell:$VERSION-nanoserver-1809" ``` # Publish with prereleases manually in Linux -1. Set "DOCKER_USERNAME" and "DOCKER_PASSWORD" variables +1. Set `DOCKER_USERNAME`, `DOCKER_ORG` and `DOCKER_PASSWORD` variables 2. Run ```bash VERSION=$(cat ./version.txt)-nightly -docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=alpine-3.14" --build-arg "INSTALL_USER=root" --build-arg "SKIP_PUBLISHER_CHECK=False" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_USERNAME/powershell:$VERSION-alpine-3.14; -docker image tag $DOCKER_USERNAME/powershell:$VERSION-alpine-3.14 $DOCKER_USERNAME/powershell:nightly +docker build --build-arg "PNP_MODULE_VERSION=$VERSION" --build-arg "BASE_IMAGE_SUFFIX=alpine-3.14" --build-arg "INSTALL_USER=root" --build-arg "SKIP_PUBLISHER_CHECK=False" ./docker -f ./docker/pnppowershell.dockerFile --tag $DOCKER_ORG/powershell:$VERSION-alpine-3.14; +docker image tag $DOCKER_ORG/powershell:$VERSION-alpine-3.14 $DOCKER_ORG/powershell:nightly docker login -u $DOCKER_USERNAME -p "$DOCKER_PASSWORD" -docker push $DOCKER_USERNAME/powershell:$VERSION-alpine-3.14 -docker push $DOCKER_USERNAME/powershell:nightly +docker push $DOCKER_ORG/powershell:$VERSION-alpine-3.14 +docker push $DOCKER_ORG/powershell:nightly ``` # Publish automatically with Github Actions -Set "DOCKER_USERNAME" and "DOCKER_PASSWORD" variables in Github Actions Secrets +Set `DOCKER_USERNAME`, `DOCKER_ORG` and `DOCKER_PASSWORD` variables in Github Actions Secrets From 6e558caef9084197eb38d175a66937bc917305fc Mon Sep 17 00:00:00 2001 From: Aleks Date: Mon, 23 May 2022 23:48:30 +0200 Subject: [PATCH 237/458] correcting typo with DOCKER_ORG --- .github/workflows/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 820b842db..e0f854125 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: shell: pwsh run: | $securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force - ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} ${{ secrets.DOCKER_IMAGE_NAME }} powershell $securedPassword "ContainerAdministrator" $true "nanoserver-ltsc2022" + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} ${{ secrets.DOCKER_ORG }} powershell $securedPassword "ContainerAdministrator" $true "nanoserver-ltsc2022" publish-docker-windows-2019: runs-on: windows-2019 steps: @@ -22,7 +22,7 @@ jobs: shell: pwsh run: | $securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force - ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} ${{ secrets.DOCKER_IMAGE_NAME }} powershell $securedPassword "ContainerAdministrator" $true "nanoserver-1809" + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} ${{ secrets.DOCKER_ORG }} powershell $securedPassword "ContainerAdministrator" $true "nanoserver-1809" publish-docker-linux: runs-on: ubuntu-latest steps: @@ -31,4 +31,4 @@ jobs: shell: pwsh run: | $securedPassword = ConvertTo-SecureString "${{ secrets.DOCKER_PASSWORD }}" -AsPlainText -Force - ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} ${{ secrets.DOCKER_IMAGE_NAME }} powershell $securedPassword "root" $false "alpine-3.14" + ./docker/Publish-UnpublishedImage.ps1 PnP.PowerShell ${{ secrets.DOCKER_USERNAME }} ${{ secrets.DOCKER_ORG }} powershell $securedPassword "root" $false "alpine-3.14" From 721848445d8327491b4efcc7dafd6aa8d7373a18 Mon Sep 17 00:00:00 2001 From: = <=> Date: Tue, 24 May 2022 01:10:42 +0200 Subject: [PATCH 238/458] Bumped .NET version from 4.6.1 to 4.6.2 --- build/Build-Debug.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/Build-Debug.ps1 b/build/Build-Debug.ps1 index b95fe52f6..47b6da03f 100644 --- a/build/Build-Debug.ps1 +++ b/build/Build-Debug.ps1 @@ -120,7 +120,7 @@ if ($LASTEXITCODE -eq 0) { Get-ChildItem -Path "$PSScriptRoot/../src/ALC/bin/Debug/netstandard2.0" | Where-Object { $_.Extension -in '.dll', '.pdb' } | Foreach-Object { if (!$assemblyExceptions.Contains($_.Name)) { [void]$commonFiles.Add($_.Name) }; Copy-Item -LiteralPath $_.FullName -Destination $commonPath } Get-ChildItem -Path "$PSScriptRoot/../src/Commands/bin/Debug/$configuration" | Where-Object { $_.Extension -in '.dll', '.pdb' -and -not $commonFiles.Contains($_.Name) } | Foreach-Object { Copy-Item -LiteralPath $_.FullName -Destination $corePath } if (!$IsLinux -and !$IsMacOs) { - Get-ChildItem -Path "$PSScriptRoot/../src/Commands/bin/Debug/net461" | Where-Object { $_.Extension -in '.dll', '.pdb' -and -not $commonFiles.Contains($_.Name) } | Foreach-Object { Copy-Item -LiteralPath $_.FullName -Destination $frameworkPath } + Get-ChildItem -Path "$PSScriptRoot/../src/Commands/bin/Debug/net462" | Where-Object { $_.Extension -in '.dll', '.pdb' -and -not $commonFiles.Contains($_.Name) } | Foreach-Object { Copy-Item -LiteralPath $_.FullName -Destination $frameworkPath } } } Catch { @@ -169,7 +169,7 @@ if ($LASTEXITCODE -eq 0) { CompanyName = 'Microsoft 365 Patterns and Practices' CompatiblePSEditions = @(`"Core`",`"Desktop`") PowerShellVersion = '5.1' - DotNetFrameworkVersion = '4.6.1' + DotNetFrameworkVersion = '4.6.2' ProcessorArchitecture = 'None' FunctionsToExport = '*' CmdletsToExport = @($cmdletsString) From f5b0a8ac290aa2e6022bf658e9aa3b5a699948b8 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 24 May 2022 01:31:38 +0200 Subject: [PATCH 239/458] Update Get-PnPTeamsChannelFilesFolder.md Slight change in the example to indicate also the name can be used of the Team and minor fix in the needed permissions section --- documentation/Get-PnPTeamsChannelFilesFolder.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/Get-PnPTeamsChannelFilesFolder.md b/documentation/Get-PnPTeamsChannelFilesFolder.md index 1a71d9344..011c9ce3e 100644 --- a/documentation/Get-PnPTeamsChannelFilesFolder.md +++ b/documentation/Get-PnPTeamsChannelFilesFolder.md @@ -13,7 +13,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Get-PnPTeamsChannelFile **Required Permissions** - * Microsoft Graph API : One of Group.Read.All + * Microsoft Graph API : Group.Read.All Gets the metadata for the location where the files of a channel are stored. @@ -30,10 +30,10 @@ Get-PnPTeamsChannel [-Team ] [-Channel ### EXAMPLE 1 ```powershell -Get-PnPTeamsChannelFilesFolder -Team a6c1e0d7-f579-4993-81ab-4b666f8edea8 -Identity "Test Channel" +Get-PnPTeamsChannelFilesFolder -Team "Sales Team" -Identity "Test Channel" ``` -Retrieves the folder metadata for the channel called 'Test Channel' +Retrieves the folder metadata for the channel called 'Test Channel' located in the Team named 'Sales Team' ### EXAMPLE 2 ```powershell From 1da89ebafb6881e2145dc8f1b96f56423dd1b50f Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 24 May 2022 01:39:34 +0200 Subject: [PATCH 240/458] Update New-PnPSite.md Minor changes --- documentation/New-PnPSite.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/documentation/New-PnPSite.md b/documentation/New-PnPSite.md index 01fce3993..e078c83d3 100644 --- a/documentation/New-PnPSite.md +++ b/documentation/New-PnPSite.md @@ -145,7 +145,7 @@ This will create a new Modern team site collection not connected to M365 group w New-PnPSite -Type TeamSite -TimeZone UTCPLUS0200_HELSINKI_KYIV_RIGA_SOFIA_TALLINN_VILNIUS -Title "Contoso" -Alias "Contoso" ``` -This will create a new Modern team site collection connected to M365 group with the title 'Contoso' and the url 'https://tenant.sharepoint.com/sites/contoso' and sets the timezone to UTC + 2 which is the Eastern European time zone. +This will create a new Modern team site collection connected to a Microsoft 365 Group with the title 'Contoso' and the url 'https://tenant.sharepoint.com/sites/contoso' and sets the timezone to UTC + 2 which is the Eastern European time zone. ## PARAMETERS @@ -392,7 +392,7 @@ Accept wildcard characters: False ### -TimeZone Specifies the timezone of the site to create. -To get the full list of timezone that you can select, you can visit [https://docs.microsoft.com/en-us/dotnet/api/officedevpnp.core.enums.timezone](https://docs.microsoft.com/en-us/dotnet/api/officedevpnp.core.enums.timezone) +To get the full list of timezone that you can select, you can visit [https://docs.microsoft.com/dotnet/api/officedevpnp.core.enums.timezone](https://docs.microsoft.com/dotnet/api/officedevpnp.core.enums.timezone) ```yaml Type: Framework.Enums.TimeZone @@ -437,4 +437,3 @@ Accept wildcard characters: False ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - From 8902588113f561741e278aead34d3a625ebcd642 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 24 May 2022 01:41:48 +0200 Subject: [PATCH 241/458] Update Get-PnPTeamsChannelMessageReply.md Minor changes --- documentation/Get-PnPTeamsChannelMessageReply.md | 1 - 1 file changed, 1 deletion(-) diff --git a/documentation/Get-PnPTeamsChannelMessageReply.md b/documentation/Get-PnPTeamsChannelMessageReply.md index c9fe38999..67b38bd86 100644 --- a/documentation/Get-PnPTeamsChannelMessageReply.md +++ b/documentation/Get-PnPTeamsChannelMessageReply.md @@ -44,7 +44,6 @@ Get-PnPTeamsChannelMessageReply -Team MyTestTeam -Channel "My Channel" -Message Gets a specific reply of the specified channel message. - ## PARAMETERS ### -Team From 6a376f50a25b2373118d67b5f05ccc32f256f67e Mon Sep 17 00:00:00 2001 From: = <=> Date: Tue, 24 May 2022 01:46:17 +0200 Subject: [PATCH 242/458] Adding changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08b2e1a69..7e03ae27a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Invoke-PnPGraphMethod` cmdlet to invoke generic Microsoft Graph API Methods. [#1820](https://github.com/pnp/powershell/pull/1820) - Added `Stop-PnPFlowRun` cmdlet to stop/cancel a specific Power Automate flow run. [#1838](https://github.com/pnp/powershell/pull/1838) - Added `Remove-PnPTeamsChannelUser` cmdlet to remove a user from a private channel. [#1840](https://github.com/pnp/powershell/pull/1840) +- Added `-Identity` parameter to `Get-PnPTeamsChannelMessage` cmdlet to retrieve a specific message [#1887](https://github.com/pnp/powershell/pull/1887) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) From 7f50e39abb536283f4e6701a673b1f22043e3b5f Mon Sep 17 00:00:00 2001 From: = <=> Date: Tue, 24 May 2022 01:48:52 +0200 Subject: [PATCH 243/458] Added changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6265a2c7c..a5b85884a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Invoke-PnPGraphMethod` cmdlet to invoke generic Microsoft Graph API Methods. [#1820](https://github.com/pnp/powershell/pull/1820) - Added `Stop-PnPFlowRun` cmdlet to stop/cancel a specific Power Automate flow run. [#1838](https://github.com/pnp/powershell/pull/1838) - Added `Remove-PnPTeamsChannelUser` cmdlet to remove a user from a private channel. [#1840](https://github.com/pnp/powershell/pull/1840) +- Added `Get-PnPTeamsChannelMessageReply` to retrieve all replies or a specific reply of a message in a Teams channel [#1885](https://github.com/pnp/powershell/pull/1885) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) From 2967eaeab6ea2fc6d121ea4912c81fbbb3a383d0 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 24 May 2022 03:46:11 +0000 Subject: [PATCH 244/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index 8184c545d..db93d360a 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -0aef907136918af434033fe2a56048d569cc01dc \ No newline at end of file +8cf7198ca468e3a2775f88ad8b1c92f3771fbcbe \ No newline at end of file diff --git a/version.txt b/version.txt index 7fa69eb07..24dfaf258 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.27 \ No newline at end of file +1.10.28 \ No newline at end of file From eb8875010795a8766505bbc6253dc394f6c4a582 Mon Sep 17 00:00:00 2001 From: Aleksandr SaPozhkov Date: Mon, 23 May 2022 23:54:18 -0700 Subject: [PATCH 245/458] corrected organization name in docker hub md (#1898) --- docker/hub.docker.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/hub.docker.md b/docker/hub.docker.md index 54a7117dc..0ff7ba485 100644 --- a/docker/hub.docker.md +++ b/docker/hub.docker.md @@ -27,13 +27,13 @@ Starting an isolated container with PnP.PowerShell module installed: ``` -docker run --rm -it asapozhkov/powershell:1.10.0-nanoserver-1809 +docker run --rm -it m365pnp/powershell:1.10.0-nanoserver-1809 ``` Starting a PnP.PowerShell container with the current directory mounted: ```PowerShell -docker run --rm -it -v ${PWD}:c:/app -w c:/app asapozhkov/powershell:1.10.0-nanoserver-1809 +docker run --rm -it -v ${PWD}:c:/app -w c:/app m365pnp/powershell:1.10.0-nanoserver-1809 ``` ### Linux-container @@ -41,13 +41,13 @@ docker run --rm -it -v ${PWD}:c:/app -w c:/app asapozhkov/powershell:1.10.0-nano Starting an isolated container with PnP.PowerShell module installed: ``` -docker run --rm -it asapozhkov/powershell +docker run --rm -it m365pnp/powershell ``` Starting a PnP.PowerShell container with the current directory mounted: ```bash -docker run --rm -it -v ${PWD}:/home -w /home asapozhkov/powershell +docker run --rm -it -v ${PWD}:/home -w /home m365pnp/powershell ``` ## Tag explanation From 18118d5a1bb8f145be4ed7c3d2370c11b337b42c Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 24 May 2022 08:54:55 +0200 Subject: [PATCH 246/458] Bugfix in `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` (#1896) * Added extra check if the provided user collection contains users and a job can be created * Added extra error handling * Added changelog entry Co-authored-by: = <=> --- CHANGELOG.md | 1 + ...intUserProfilesFromAzureActiveDirectory.cs | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89841980c..266c15765 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Set-PnPListPermission`, it will now throw error if the list does not exist. [#1891](https://github.com/pnp/powershell/pull/1891) - Fixed `Invoke-PnPSPRestMethod` invalid parsing for SharePoint number columns. [#1877](https://github.com/pnp/powershell/pull/1879) - Fix issue with `Add/Set-PnPListItem` not throwing correct exception for invalid taxonomy values. [#1870](https://github.com/pnp/powershell/pull/1870) +- Fixed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` throwing an "Object reference not set to an instance of an object" exception when providing an empty users collection or incorrect user mapping [#1896](https://github.com/pnp/powershell/pull/1896) ### Removed - Removed `Get-PnPAvailableClientSideComponents`. Use `Get-PnPPageComponent -Page -ListAvailable` instead. [#1833](https://github.com/pnp/powershell/pull/1833) diff --git a/src/Commands/UserProfiles/SyncSharePointUserProfilesFromAzureActiveDirectory.cs b/src/Commands/UserProfiles/SyncSharePointUserProfilesFromAzureActiveDirectory.cs index f33488efc..2b6daf6b1 100644 --- a/src/Commands/UserProfiles/SyncSharePointUserProfilesFromAzureActiveDirectory.cs +++ b/src/Commands/UserProfiles/SyncSharePointUserProfilesFromAzureActiveDirectory.cs @@ -43,13 +43,20 @@ protected override void ExecuteCmdlet() var aadUsers = new List(); if (ParameterSpecified(nameof(Users))) { - // Users to sync have been provided - if (Users == null) + // Ensure users have been provided + if(Users == null) { throw new PSArgumentNullException(nameof(Users), "Provided Users collection cannot be null"); } + if(Users.Count == 0) + { + WriteVerbose("No users have been provided"); + return; + } + - WriteVerbose($"Using provided user collection containing {Users.Count} user{(Users.Count != 1 ? "s" : "")}"); + // Users to sync have been provided + WriteVerbose($"Using provided user collection containing {Users.Count} user{(Users.Count != 1 ? "s": "")}"); aadUsers = Users; } @@ -86,6 +93,12 @@ protected override void ExecuteCmdlet() WriteVerbose($"Creating mapping file{(WhatIf.ToBool() ? " and" : ",")} uploading it to SharePoint Online to folder '{Folder}'{(WhatIf.ToBool() ? "" : " and executing sync job")}"); var job = Utilities.SharePointUserProfileSync.SyncFromAzureActiveDirectory(nonAdminClientContext, aadUsers, IdType, UserProfilePropertyMapping, Folder, ParameterSpecified(nameof(WhatIf))).GetAwaiter().GetResult(); + // Ensure a sync job has been created + if(job == null) + { + throw new PSInvalidOperationException($"Failed to create sync job. Ensure you're providing users to sync and that the mapping is correct."); + } + WriteVerbose($"Job initiated with Id {job.JobId} and status {job.State} for file {job.SourceUri}"); // Check if we should wait with finalzing this cmdlet execution until the user profile import operation has completed From c74aea8467b0a0a81cc53233215c05f5b8f5a331 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Tue, 24 May 2022 21:45:10 +0300 Subject: [PATCH 247/458] Fix to return proper values --- src/Commands/Lists/GetListItemPermission.cs | 9 ++++++--- src/Commands/Model/ListItemPermissions.cs | 12 +++++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/Commands/Lists/GetListItemPermission.cs b/src/Commands/Lists/GetListItemPermission.cs index e65eee8f2..90cf57652 100644 --- a/src/Commands/Lists/GetListItemPermission.cs +++ b/src/Commands/Lists/GetListItemPermission.cs @@ -38,15 +38,18 @@ protected override void ExecuteCmdlet() foreach (var roleAssignment in item.RoleAssignments) { + roleAssignment.EnsureProperties(r => r.Member, r => r.Member.PrincipalType, r => r.Member.Id, r => r.Member.Id); var listItemPermission = new ListItemPermissions { - PrincipalName = roleAssignment.Member.LoginName + Principal = roleAssignment.Member.LoginName, + PrincipalType = roleAssignment.Member.PrincipalType, + PrincipalId = roleAssignment.Member.Id }; - List roles = new List(); + List roles = new List(); foreach (var role in roleAssignment.RoleDefinitionBindings) { - roles.Add(role.Description); + roles.Add(role); } listItemPermission.Permissions = roles; diff --git a/src/Commands/Model/ListItemPermissions.cs b/src/Commands/Model/ListItemPermissions.cs index 6ea554b07..a463b585e 100644 --- a/src/Commands/Model/ListItemPermissions.cs +++ b/src/Commands/Model/ListItemPermissions.cs @@ -1,12 +1,18 @@ -using System; +using Microsoft.SharePoint.Client; +using Microsoft.SharePoint.Client.Utilities; +using System; using System.Collections.Generic; namespace PnP.PowerShell.Commands.Model { public class ListItemPermissions { - public List Permissions { get; set; } + public List Permissions { get; set; } - public string PrincipalName { get; set; } + public string Principal { get; set; } + + public PrincipalType PrincipalType { get; set; } + + public int PrincipalId { get; set; } } } From 3b570b289e821e2873635ea326016d00c0937381 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Tue, 24 May 2022 21:49:07 +0300 Subject: [PATCH 248/458] Fixed better output --- src/Commands/Lists/GetListItemPermission.cs | 2 +- src/Commands/Model/ListItemPermissions.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Commands/Lists/GetListItemPermission.cs b/src/Commands/Lists/GetListItemPermission.cs index 90cf57652..47de1d7eb 100644 --- a/src/Commands/Lists/GetListItemPermission.cs +++ b/src/Commands/Lists/GetListItemPermission.cs @@ -41,7 +41,7 @@ protected override void ExecuteCmdlet() roleAssignment.EnsureProperties(r => r.Member, r => r.Member.PrincipalType, r => r.Member.Id, r => r.Member.Id); var listItemPermission = new ListItemPermissions { - Principal = roleAssignment.Member.LoginName, + PrincipalName = roleAssignment.Member.LoginName, PrincipalType = roleAssignment.Member.PrincipalType, PrincipalId = roleAssignment.Member.Id }; diff --git a/src/Commands/Model/ListItemPermissions.cs b/src/Commands/Model/ListItemPermissions.cs index a463b585e..b51da5eda 100644 --- a/src/Commands/Model/ListItemPermissions.cs +++ b/src/Commands/Model/ListItemPermissions.cs @@ -9,7 +9,7 @@ public class ListItemPermissions { public List Permissions { get; set; } - public string Principal { get; set; } + public string PrincipalName { get; set; } public PrincipalType PrincipalType { get; set; } From 250d60c5ba56fb7aaacd596226b3f4396fb7c842 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Tue, 24 May 2022 21:52:00 +0300 Subject: [PATCH 249/458] Fix build error --- src/Commands/Teams/AddTeamsUser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Commands/Teams/AddTeamsUser.cs b/src/Commands/Teams/AddTeamsUser.cs index 2b29d46d9..da595a475 100644 --- a/src/Commands/Teams/AddTeamsUser.cs +++ b/src/Commands/Teams/AddTeamsUser.cs @@ -45,7 +45,7 @@ protected override void ExecuteCmdlet() { throw new PSArgumentException("Channel not found"); } - TeamsUtility.AddChannelUserAsync(HttpClient, AccessToken, groupId, channelId, User, Role).GetAwaiter().GetResult(); + TeamsUtility.AddChannelMemberAsync(HttpClient, AccessToken, groupId, channelId, User, Role).GetAwaiter().GetResult(); } else { From 639f095e9ecb52f64abcc0ede7390f8374782018 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 25 May 2022 03:40:46 +0000 Subject: [PATCH 250/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index db93d360a..450e1ec5c 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -8cf7198ca468e3a2775f88ad8b1c92f3771fbcbe \ No newline at end of file +b32ab166967ab85eb488c9326ea16071c090d929 \ No newline at end of file diff --git a/version.txt b/version.txt index 24dfaf258..d10991a5a 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.28 \ No newline at end of file +1.10.29 \ No newline at end of file From 99ffab2e505eed10301850fd12f43de03b358e31 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Wed, 25 May 2022 23:41:16 +0300 Subject: [PATCH 251/458] Fix #1554 - issue with onedrive site storage quota --- .../UserProfiles/GetUserOneDriveQuota.cs | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/src/Commands/UserProfiles/GetUserOneDriveQuota.cs b/src/Commands/UserProfiles/GetUserOneDriveQuota.cs index 409e5d28d..1a7b9d56b 100644 --- a/src/Commands/UserProfiles/GetUserOneDriveQuota.cs +++ b/src/Commands/UserProfiles/GetUserOneDriveQuota.cs @@ -1,4 +1,7 @@ -using System.Management.Automation; +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation; +using Microsoft.Online.SharePoint.TenantAdministration; using Microsoft.SharePoint.Client; using Microsoft.SharePoint.Client.UserProfiles; @@ -16,9 +19,43 @@ public class GetUserOneDriveQuota : PnPAdminCmdlet protected override void ExecuteCmdlet() { var peopleManager = new PeopleManager(ClientContext); - var oneDriveQuota = peopleManager.GetUserOneDriveQuotaMax(Account); + + var result = Tenant.EncodeClaim(Account); + ClientContext.ExecuteQueryRetry(); + Account = result.Value; + + var properties = peopleManager.GetPropertiesFor(Account); + ClientContext.Load(properties); ClientContext.ExecuteQueryRetry(); - WriteObject(oneDriveQuota); + + var personalSiteUrl = properties.PersonalUrl; + + SPOSitePropertiesEnumerableFilter filter = new SPOSitePropertiesEnumerableFilter() + { + IncludePersonalSite = PersonalSiteFilter.Include, + IncludeDetail = true, + Template = "SPSPERS" + }; + + var sitesList = Tenant.GetSitePropertiesFromSharePointByFilters(filter); + var sites = new List(); + do + { + Tenant.Context.Load(sitesList); + Tenant.Context.ExecuteQueryRetry(); + sites.AddRange(sitesList.ToList()); + } while (!string.IsNullOrWhiteSpace(sitesList.NextStartIndexFromSharePoint)); + + var userSite = sitesList.Where(s => s.Url.ToLower() == personalSiteUrl.TrimEnd('/').ToLower()).FirstOrDefault(); + + if (userSite != null) + { + WriteObject(userSite.StorageMaximumLevel * 1024 * 1024); + } + else + { + WriteWarning("Couldn't find quota for the specified personal site"); + } } } } \ No newline at end of file From 0f3cc5dcbcd327f3c95e59cff7eb63a262020132 Mon Sep 17 00:00:00 2001 From: Bart-Jan Dekker Date: Thu, 26 May 2022 20:34:39 +0200 Subject: [PATCH 252/458] Add-PnPDataRowsToSiteTemplate export dates as UTC string (#1900) * Updated example of the query parameter * Updated example of the query parameter * Exports date as UTC string so they are correctly imported again Co-authored-by: Bart-Jan --- .../Provisioning/Site/AddDataRowsToSiteTemplate.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Commands/Provisioning/Site/AddDataRowsToSiteTemplate.cs b/src/Commands/Provisioning/Site/AddDataRowsToSiteTemplate.cs index 21a24140a..afb70321a 100644 --- a/src/Commands/Provisioning/Site/AddDataRowsToSiteTemplate.cs +++ b/src/Commands/Provisioning/Site/AddDataRowsToSiteTemplate.cs @@ -272,6 +272,13 @@ private string GetFieldValueAsText(Web web, ListItem listItem, Microsoft.SharePo return string.Join(";#", multipleChoiceValue); } return Convert.ToString(rawValue); + case FieldType.DateTime: + var dateValue = rawValue as DateTime?; + if(dateValue != null) + { + return string.Format("{0:O}", dateValue.Value.ToUniversalTime()); + } + throw new Exception("Invalid data in field"); default: return Convert.ToString(rawValue); } From 124bc5af7e718ed3127f5b669b6502f28ac8007d Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 26 May 2022 21:36:52 +0300 Subject: [PATCH 253/458] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7039ba6fa..3132e6246 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) - Changed `Get-PnPOrgAssetsLibrary` to return a proper value of the organisation assets libraries. [#1889](https://github.com/pnp/powershell/pull/1889) - Bumped .NET Framework version to 4.6.2 as the 4.6.1 is not supported anymore. [#1856](https://github.com/pnp/powershell/pull/1856) +- Changed `Add-PnPDataRowsToSiteTemplate`, it will now export a datetime field value as UTC string. [#1900](https://github.com/pnp/powershell/pull/1900) ### Fixed From 69816c9e777c8d9d5774ed1b14c6abccf711cf2d Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Fri, 27 May 2022 03:46:26 +0000 Subject: [PATCH 254/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index 450e1ec5c..da353fd12 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -b32ab166967ab85eb488c9326ea16071c090d929 \ No newline at end of file +a9a0a166cbaa8b0c28ca469d787d888ff3b23c0f \ No newline at end of file diff --git a/version.txt b/version.txt index d10991a5a..917efc10c 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.29 \ No newline at end of file +1.10.30 \ No newline at end of file From 47db1f22a84b4807ac71789e93b9ee671a492917 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 28 May 2022 03:34:29 +0000 Subject: [PATCH 255/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 9c925045e..615b088fe 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -d038ad31e1bba2b60dae869f38ad700ebe36efbe \ No newline at end of file +3fc2b33e2c00d29b643d00e4bdea1c8246be338e \ No newline at end of file diff --git a/version.txt b/version.txt index 917efc10c..4c21942f5 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.30 \ No newline at end of file +1.10.31 \ No newline at end of file From 7163828861af71ea4158587cbfb883017188ef9d Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sun, 29 May 2022 03:44:14 +0000 Subject: [PATCH 256/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 615b088fe..6b7790535 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -3fc2b33e2c00d29b643d00e4bdea1c8246be338e \ No newline at end of file +2002290a28b5408b9d9da435f1679d89f7a8b2b3 \ No newline at end of file diff --git a/version.txt b/version.txt index 4c21942f5..7809be057 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.31 \ No newline at end of file +1.10.32 \ No newline at end of file From d675a825b43a4c64aa455f535b8f9a22dbdc5ba9 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Mon, 30 May 2022 03:51:24 +0000 Subject: [PATCH 257/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 6b7790535..6a785e33a 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -2002290a28b5408b9d9da435f1679d89f7a8b2b3 \ No newline at end of file +6362a8cf147d3234ec246f5643364ecb107db811 \ No newline at end of file diff --git a/version.txt b/version.txt index 7809be057..74f0293e7 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.32 \ No newline at end of file +1.10.33 \ No newline at end of file From f71f991a63779b70fb1b30a517c40340cd3c01fa Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Mon, 30 May 2022 14:54:55 +0200 Subject: [PATCH 258/458] Fixed typo (#1908) --- documentation/Get-PnPPlannerConfiguration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/Get-PnPPlannerConfiguration.md b/documentation/Get-PnPPlannerConfiguration.md index 84a0a6e7b..db77f1a3b 100644 --- a/documentation/Get-PnPPlannerConfiguration.md +++ b/documentation/Get-PnPPlannerConfiguration.md @@ -22,7 +22,7 @@ Get-PnPPlannerConfiguration [-Connection ] [] ``` ## DESCRIPTION -This cmdlet returns the Microsoft Planner admin configuration of the tenant. Note that after changing the configuration using `Set-PnPPlannerTenantConfiguration`, this cmdlet may return varying results which could deviate from your desired configuration while the new configuration is being propagated accross the tenant. +This cmdlet returns the Microsoft Planner admin configuration of the tenant. Note that after changing the configuration using `Set-PnPPlannerTenantConfiguration`, this cmdlet may return varying results which could deviate from your desired configuration while the new configuration is being propagated across the tenant. ## EXAMPLES @@ -52,4 +52,4 @@ Accept wildcard characters: False ## RELATED LINKS -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) From 200e39a49b38f50bab4dbb83971aa829ffc32496 Mon Sep 17 00:00:00 2001 From: dc366 Date: Mon, 30 May 2022 08:56:25 -0400 Subject: [PATCH 259/458] Fix links in batching.md (#1906) --- pages/articles/batching.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pages/articles/batching.md b/pages/articles/batching.md index ddcf4d521..6f3fc3823 100644 --- a/pages/articles/batching.md +++ b/pages/articles/batching.md @@ -46,9 +46,9 @@ If during one of these chunks an exception occurs (for instance you are trying t As of release 1.7.63-nightly the following cmdlets support batching: -* [`Add-PnPListItem`](/cmdlets/Add-PnPListItem) -* [`Set-PnPListItem`](/cmdlets/Set-PnPListItem) -* [`Remove-PnPListItem`](/cmdlets/Remove-PnPListItem) -* [`Publish-PnPSyntexModel`](/cmdlets/Publish-PnPSyntexModel) -* [`Unpublish-PnPSyntexModel`](/cmdlets/Unpublish-PnPSyntexModel) -* [`Request-PnPSyntexClassifyAndExtract`](/cmdlets/Request-PnPSyntexClassifyAndExtract) +* [`Add-PnPListItem`](/documentation/Add-PnPListItem.md) +* [`Set-PnPListItem`](/documentation/Set-PnPListItem.md) +* [`Remove-PnPListItem`](/documentation/Remove-PnPListItem.md) +* [`Publish-PnPSyntexModel`](/documentation/Publish-PnPSyntexModel.md) +* [`Unpublish-PnPSyntexModel`](/documentation/Unpublish-PnPSyntexModel.md) +* [`Request-PnPSyntexClassifyAndExtract`](/documentation/Request-PnPSyntexClassifyAndExtract.md) From b5e428a4d02b8406e949732a635ae4da701b8f2f Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Mon, 30 May 2022 15:57:47 +0300 Subject: [PATCH 260/458] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3132e6246..e3d938e17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Contributors +- dc366 [dc366] - Bart-Jan Dekker [bjdekker] - Aleksandr Sapozhkov [shurick81] - [spg-iwilson] From d21b03ca4c3362b43d7619d9b02f84beedae0855 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Mon, 30 May 2022 22:55:06 +0300 Subject: [PATCH 261/458] Fix: Article update for Azure functions (#1914) --- pages/articles/azurefunctions.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pages/articles/azurefunctions.md b/pages/articles/azurefunctions.md index 28e623f84..4c294f2fa 100644 --- a/pages/articles/azurefunctions.md +++ b/pages/articles/azurefunctions.md @@ -195,39 +195,39 @@ Next step is to assign permissions to this managed identity so it is authorized 1. Ensure you're having the Azure PowerShell management module installed on your environment. You can install it using: ```powershell - Install-Module AzureAD + Install-Module Az ``` 1. Connect to the Azure instance where your Azure Function runs and of which you want to use the Microsoft Graph through PnP PowerShell ```powershell - Connect-AzureAD -TenantId .onmicrosoft.com + Connect-AzAccount -Tenant .onmicrosoft.com ``` 1. Retrieve the Azure AD Service Principal instance for the Microsoft Graph. It should always be AppId 00000003-0000-0000-c000-000000000000. ```powershell - $graphServicePrincipal = Get-AzureADServicePrincipal -SearchString "Microsoft Graph" | Select-Object -First 1 + $graphServicePrincipal = Get-AzADServicePrincipal -SearchString "Microsoft Graph" | Select-Object -First 1 ``` 1. Using the following PowerShell cmdlet you can list all the possible Microsoft Graph permissions you can give your Azure Function through the Managed Identity. This list will be long. Notice that we are specifically querying for application permissions. Delegate permissions cannot be utilized using a Managed Identity. ```powershell - $graphServicePrincipal.AppRoles | Where-Object { $_.AllowedMemberTypes -eq "Application" } + $graphServicePrincipal.AppRole | Where-Object { $_.AllowedMemberType -eq "Application" } ``` 1. Pick a permission which you would like to grant your Azure Function to have towards the Microsoft Graph, i.e. `Group.Read.All`. ```powershell - $appRole = $graphServicePrincipal.AppRoles | Where-Object { $_.AllowedMemberTypes -eq "Application" -and $_.Value -eq "Group.Read.All" } + $appRole = $graphServicePrincipal.AppRole | Where-Object { $_.AllowedMemberType -eq "Application" -and $_.Value -eq "Group.Read.All" } ``` 1. Now assign this permission to the Azure Active Directory app registration that has been created automatically by enabling the managed identity on the Azure Function in the steps above: ```powershell $managedIdentityId = "" - New-AzureAdServiceAppRoleAssignment -ObjectId $managedIdentityId -PrincipalId $managedIdentityId -ResourceId $graphServicePrincipal.ObjectId -Id $appRole.Id - + Add-AzADAppPermission -ObjectId $managedIdentityId -ApiId $graphServicePrincipal.AppId -PermissionId $appRole.Id -Type 'Role' + ``` #### Create the Azure Function From 2451311ce0cb37ab8fe56a641a307ed6e376e47d Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Mon, 30 May 2022 22:11:03 +0200 Subject: [PATCH 262/458] Restart-PnPFlowRun --- documentation/Restart-PnPFlowRun.md | 108 ++++++++++++++++++ .../PowerAutomate/RestartFlowRun.cs | 55 +++++++++ 2 files changed, 163 insertions(+) create mode 100644 documentation/Restart-PnPFlowRun.md create mode 100644 src/Commands/PowerPlatform/PowerAutomate/RestartFlowRun.cs diff --git a/documentation/Restart-PnPFlowRun.md b/documentation/Restart-PnPFlowRun.md new file mode 100644 index 000000000..466d124fe --- /dev/null +++ b/documentation/Restart-PnPFlowRun.md @@ -0,0 +1,108 @@ +--- +Module Name: PnP.PowerShell +title: Restart-PnPFlowRun +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Restart-PnPFlowRun.html +--- + +# Restart-PnPFlowRun + +## SYNOPSIS +**Required Permissions** + +* Azure: management.azure.com + +Restarts/resubmits a specific flow run for the specified Microsoft Power Automate flow. + +## SYNTAX + +```powershell +Restart-PnPFlowRun -Environment -Flow -Identity [-Force] [] +``` + +## DESCRIPTION +This cmdlet restarts/resubmits a specific Power Automate flow run. + +## EXAMPLES + +### Example 1 +```powershell +$environment = Get-PnPPowerPlatformEnvironment +Restart-PnPFlowRun -Environment $environment -Flow fba63225-baf9-4d76-86a1-1b42c917a182 -Identity 08585531682024670884771461819CU230 +``` +This restarts the specified flow run of the specified flow + + +### Example 2 +```powershell +$environment = Get-PnPPowerPlatformEnvironment +Restart-PnPFlowRun -Environment $environment -Flow fba63225-baf9-4d76-86a1-1b42c917a182 -Identity 08585531682024670884771461819CU230 -Force +``` +This restarts the specified flow run of the specified flow without confirmation + +## PARAMETERS + +### -Environment +The name of the Power Platform environment or an Environment object to retrieve the available flows for. + +```yaml +Type: PowerAutomateEnvironmentPipeBind +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Flow +The Name/Id of the flow to retrieve the available flow runs for. + +```yaml +Type: PowerAutomateFlowPipeBind +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Identity +The Name/Id of the flow run to restart. + +```yaml +Type: PowerAutomateFlowRunPipeBind +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Force +Specifying the Force parameter will skip the confirmation question. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) diff --git a/src/Commands/PowerPlatform/PowerAutomate/RestartFlowRun.cs b/src/Commands/PowerPlatform/PowerAutomate/RestartFlowRun.cs new file mode 100644 index 000000000..13f61be61 --- /dev/null +++ b/src/Commands/PowerPlatform/PowerAutomate/RestartFlowRun.cs @@ -0,0 +1,55 @@ +using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Model.PowerPlatform.PowerAutomate; +using PnP.PowerShell.Commands.Properties; +using PnP.PowerShell.Commands.Utilities.REST; +using System.Linq; +using System.Management.Automation; + +namespace PnP.PowerShell.Commands.PowerPlatform.PowerAutomate +{ + [Cmdlet(VerbsLifecycle.Restart, "PnPFlowRun")] + [RequiredMinimalApiPermissions("https://management.azure.com/.default")] + public class RestartFlowRun : PnPGraphCmdlet + { + [Parameter(Mandatory = true)] + public PowerPlatformEnvironmentPipeBind Environment; + + [Parameter(Mandatory = true)] + public PowerAutomateFlowPipeBind Flow; + + [Parameter(Mandatory = true)] + public PowerAutomateFlowRunPipeBind Identity; + + [Parameter(Mandatory = false)] + public SwitchParameter Force; + + protected override void ExecuteCmdlet() + { + var environmentName = Environment.GetName(); + if (string.IsNullOrEmpty(environmentName)) + { + throw new PSArgumentException("Environment not found."); + } + + var flowName = Flow.GetName(); + if (string.IsNullOrEmpty(flowName)) + { + throw new PSArgumentException("Flow not found."); + } + + var flowRunName = Identity.GetName(); + if (string.IsNullOrEmpty(flowRunName)) + { + throw new PSArgumentException("Flow run not found."); + } + + if (!Force && !ShouldContinue($"Restart flow run with name '{flowRunName}'?", Resources.Confirm)) + return; + + var triggers = GraphHelper.GetResultCollectionAsync(HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple/environments/{environmentName}/flows/{flowName}/triggers?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); + RestHelper.PostAsync(HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple/environments/{environmentName}/flows/{flowName}/triggers/{triggers.First().Name}/histories/{flowRunName}/resubmit?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); + } + } +} From 85e31d7a1917b300046ecc497e0d1af80c6f991f Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Mon, 30 May 2022 22:13:30 +0200 Subject: [PATCH 263/458] Restart-PnPFlowRun --- documentation/Restart-PnPFlowRun.md | 108 ++++++++++++++++++ .../PowerAutomate/RestartFlowRun.cs | 55 +++++++++ 2 files changed, 163 insertions(+) create mode 100644 documentation/Restart-PnPFlowRun.md create mode 100644 src/Commands/PowerPlatform/PowerAutomate/RestartFlowRun.cs diff --git a/documentation/Restart-PnPFlowRun.md b/documentation/Restart-PnPFlowRun.md new file mode 100644 index 000000000..466d124fe --- /dev/null +++ b/documentation/Restart-PnPFlowRun.md @@ -0,0 +1,108 @@ +--- +Module Name: PnP.PowerShell +title: Restart-PnPFlowRun +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Restart-PnPFlowRun.html +--- + +# Restart-PnPFlowRun + +## SYNOPSIS +**Required Permissions** + +* Azure: management.azure.com + +Restarts/resubmits a specific flow run for the specified Microsoft Power Automate flow. + +## SYNTAX + +```powershell +Restart-PnPFlowRun -Environment -Flow -Identity [-Force] [] +``` + +## DESCRIPTION +This cmdlet restarts/resubmits a specific Power Automate flow run. + +## EXAMPLES + +### Example 1 +```powershell +$environment = Get-PnPPowerPlatformEnvironment +Restart-PnPFlowRun -Environment $environment -Flow fba63225-baf9-4d76-86a1-1b42c917a182 -Identity 08585531682024670884771461819CU230 +``` +This restarts the specified flow run of the specified flow + + +### Example 2 +```powershell +$environment = Get-PnPPowerPlatformEnvironment +Restart-PnPFlowRun -Environment $environment -Flow fba63225-baf9-4d76-86a1-1b42c917a182 -Identity 08585531682024670884771461819CU230 -Force +``` +This restarts the specified flow run of the specified flow without confirmation + +## PARAMETERS + +### -Environment +The name of the Power Platform environment or an Environment object to retrieve the available flows for. + +```yaml +Type: PowerAutomateEnvironmentPipeBind +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Flow +The Name/Id of the flow to retrieve the available flow runs for. + +```yaml +Type: PowerAutomateFlowPipeBind +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Identity +The Name/Id of the flow run to restart. + +```yaml +Type: PowerAutomateFlowRunPipeBind +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Force +Specifying the Force parameter will skip the confirmation question. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) diff --git a/src/Commands/PowerPlatform/PowerAutomate/RestartFlowRun.cs b/src/Commands/PowerPlatform/PowerAutomate/RestartFlowRun.cs new file mode 100644 index 000000000..13f61be61 --- /dev/null +++ b/src/Commands/PowerPlatform/PowerAutomate/RestartFlowRun.cs @@ -0,0 +1,55 @@ +using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Model.PowerPlatform.PowerAutomate; +using PnP.PowerShell.Commands.Properties; +using PnP.PowerShell.Commands.Utilities.REST; +using System.Linq; +using System.Management.Automation; + +namespace PnP.PowerShell.Commands.PowerPlatform.PowerAutomate +{ + [Cmdlet(VerbsLifecycle.Restart, "PnPFlowRun")] + [RequiredMinimalApiPermissions("https://management.azure.com/.default")] + public class RestartFlowRun : PnPGraphCmdlet + { + [Parameter(Mandatory = true)] + public PowerPlatformEnvironmentPipeBind Environment; + + [Parameter(Mandatory = true)] + public PowerAutomateFlowPipeBind Flow; + + [Parameter(Mandatory = true)] + public PowerAutomateFlowRunPipeBind Identity; + + [Parameter(Mandatory = false)] + public SwitchParameter Force; + + protected override void ExecuteCmdlet() + { + var environmentName = Environment.GetName(); + if (string.IsNullOrEmpty(environmentName)) + { + throw new PSArgumentException("Environment not found."); + } + + var flowName = Flow.GetName(); + if (string.IsNullOrEmpty(flowName)) + { + throw new PSArgumentException("Flow not found."); + } + + var flowRunName = Identity.GetName(); + if (string.IsNullOrEmpty(flowRunName)) + { + throw new PSArgumentException("Flow run not found."); + } + + if (!Force && !ShouldContinue($"Restart flow run with name '{flowRunName}'?", Resources.Confirm)) + return; + + var triggers = GraphHelper.GetResultCollectionAsync(HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple/environments/{environmentName}/flows/{flowName}/triggers?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); + RestHelper.PostAsync(HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple/environments/{environmentName}/flows/{flowName}/triggers/{triggers.First().Name}/histories/{flowRunName}/resubmit?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); + } + } +} From 07fe3acc2fa19dd1a125a759a2615341758228d2 Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Mon, 30 May 2022 23:36:00 +0200 Subject: [PATCH 264/458] ResolveIdentities parameter fix --- documentation/Get-PnPPlannerPlan.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/Get-PnPPlannerPlan.md b/documentation/Get-PnPPlannerPlan.md index cc6abf460..6854e607a 100644 --- a/documentation/Get-PnPPlannerPlan.md +++ b/documentation/Get-PnPPlannerPlan.md @@ -21,7 +21,7 @@ Returns all or a specific Planner plan for a Microsoft 365 Group. ## SYNTAX ```powershell -Get-PnPPlannerPlan -Group [-Identity ] [-ResolveUserDisplayNames] +Get-PnPPlannerPlan -Group [-Identity ] [-ResolveIdentities] [] ``` @@ -76,8 +76,8 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -ResolveUserDisplayNames -{{ Fill ResolveUserDisplayNames Description }} +### -ResolveIdentities +Show user display names instead of user IDs. ```yaml Type: SwitchParameter From 3b494830a573dc0e314658d60fe23192512bbadf Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Mon, 30 May 2022 23:38:20 +0200 Subject: [PATCH 265/458] Remove wrong code --- documentation/Restart-PnPFlowRun.md | 108 ------------------ .../PowerAutomate/RestartFlowRun.cs | 55 --------- 2 files changed, 163 deletions(-) delete mode 100644 documentation/Restart-PnPFlowRun.md delete mode 100644 src/Commands/PowerPlatform/PowerAutomate/RestartFlowRun.cs diff --git a/documentation/Restart-PnPFlowRun.md b/documentation/Restart-PnPFlowRun.md deleted file mode 100644 index 466d124fe..000000000 --- a/documentation/Restart-PnPFlowRun.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -Module Name: PnP.PowerShell -title: Restart-PnPFlowRun -schema: 2.0.0 -applicable: SharePoint Online -external help file: PnP.PowerShell.dll-Help.xml -online version: https://pnp.github.io/powershell/cmdlets/Restart-PnPFlowRun.html ---- - -# Restart-PnPFlowRun - -## SYNOPSIS -**Required Permissions** - -* Azure: management.azure.com - -Restarts/resubmits a specific flow run for the specified Microsoft Power Automate flow. - -## SYNTAX - -```powershell -Restart-PnPFlowRun -Environment -Flow -Identity [-Force] [] -``` - -## DESCRIPTION -This cmdlet restarts/resubmits a specific Power Automate flow run. - -## EXAMPLES - -### Example 1 -```powershell -$environment = Get-PnPPowerPlatformEnvironment -Restart-PnPFlowRun -Environment $environment -Flow fba63225-baf9-4d76-86a1-1b42c917a182 -Identity 08585531682024670884771461819CU230 -``` -This restarts the specified flow run of the specified flow - - -### Example 2 -```powershell -$environment = Get-PnPPowerPlatformEnvironment -Restart-PnPFlowRun -Environment $environment -Flow fba63225-baf9-4d76-86a1-1b42c917a182 -Identity 08585531682024670884771461819CU230 -Force -``` -This restarts the specified flow run of the specified flow without confirmation - -## PARAMETERS - -### -Environment -The name of the Power Platform environment or an Environment object to retrieve the available flows for. - -```yaml -Type: PowerAutomateEnvironmentPipeBind -Parameter Sets: (All) -Aliases: - -Required: True -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Flow -The Name/Id of the flow to retrieve the available flow runs for. - -```yaml -Type: PowerAutomateFlowPipeBind -Parameter Sets: (All) -Aliases: - -Required: True -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Identity -The Name/Id of the flow run to restart. - -```yaml -Type: PowerAutomateFlowRunPipeBind -Parameter Sets: (All) -Aliases: - -Required: True -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Force -Specifying the Force parameter will skip the confirmation question. - -```yaml -Type: SwitchParameter -Parameter Sets: (All) - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -## RELATED LINKS - -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) diff --git a/src/Commands/PowerPlatform/PowerAutomate/RestartFlowRun.cs b/src/Commands/PowerPlatform/PowerAutomate/RestartFlowRun.cs deleted file mode 100644 index 13f61be61..000000000 --- a/src/Commands/PowerPlatform/PowerAutomate/RestartFlowRun.cs +++ /dev/null @@ -1,55 +0,0 @@ -using PnP.PowerShell.Commands.Attributes; -using PnP.PowerShell.Commands.Base; -using PnP.PowerShell.Commands.Base.PipeBinds; -using PnP.PowerShell.Commands.Model.PowerPlatform.PowerAutomate; -using PnP.PowerShell.Commands.Properties; -using PnP.PowerShell.Commands.Utilities.REST; -using System.Linq; -using System.Management.Automation; - -namespace PnP.PowerShell.Commands.PowerPlatform.PowerAutomate -{ - [Cmdlet(VerbsLifecycle.Restart, "PnPFlowRun")] - [RequiredMinimalApiPermissions("https://management.azure.com/.default")] - public class RestartFlowRun : PnPGraphCmdlet - { - [Parameter(Mandatory = true)] - public PowerPlatformEnvironmentPipeBind Environment; - - [Parameter(Mandatory = true)] - public PowerAutomateFlowPipeBind Flow; - - [Parameter(Mandatory = true)] - public PowerAutomateFlowRunPipeBind Identity; - - [Parameter(Mandatory = false)] - public SwitchParameter Force; - - protected override void ExecuteCmdlet() - { - var environmentName = Environment.GetName(); - if (string.IsNullOrEmpty(environmentName)) - { - throw new PSArgumentException("Environment not found."); - } - - var flowName = Flow.GetName(); - if (string.IsNullOrEmpty(flowName)) - { - throw new PSArgumentException("Flow not found."); - } - - var flowRunName = Identity.GetName(); - if (string.IsNullOrEmpty(flowRunName)) - { - throw new PSArgumentException("Flow run not found."); - } - - if (!Force && !ShouldContinue($"Restart flow run with name '{flowRunName}'?", Resources.Confirm)) - return; - - var triggers = GraphHelper.GetResultCollectionAsync(HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple/environments/{environmentName}/flows/{flowName}/triggers?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); - RestHelper.PostAsync(HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple/environments/{environmentName}/flows/{flowName}/triggers/{triggers.First().Name}/histories/{flowRunName}/resubmit?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); - } - } -} From 0071cf8b95e9b41aa6eeee9a43c6eb9358de7280 Mon Sep 17 00:00:00 2001 From: James May Date: Wed, 20 Apr 2022 20:36:05 +1000 Subject: [PATCH 266/458] List and ListItem: output the Id of the recycle bin item --- .../Lists/MoveListItemToRecycleBin.cs | 8 ++- src/Commands/Lists/RemoveList.cs | 17 ++++-- src/Commands/Lists/RemoveListItem.cs | 58 ++++++++++++++----- .../Model/SharePoint/RecycleResult.cs | 11 ++++ 4 files changed, 74 insertions(+), 20 deletions(-) create mode 100644 src/Commands/Model/SharePoint/RecycleResult.cs diff --git a/src/Commands/Lists/MoveListItemToRecycleBin.cs b/src/Commands/Lists/MoveListItemToRecycleBin.cs index f73570ac7..54e29300c 100644 --- a/src/Commands/Lists/MoveListItemToRecycleBin.cs +++ b/src/Commands/Lists/MoveListItemToRecycleBin.cs @@ -1,11 +1,14 @@ using System.Management.Automation; + using Microsoft.SharePoint.Client; using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Model.SharePoint; namespace PnP.PowerShell.Commands.Lists { [Cmdlet(VerbsCommon.Move, "PnPListItemToRecycleBin")] + [OutputType(typeof(RecycleResult))] public class MoveListItemToRecycleBin : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] @@ -25,10 +28,11 @@ protected override void ExecuteCmdlet() if (Identity != null) { var item = Identity.GetListItem(list); - if (Force || ShouldContinue(string.Format(Properties.Resources.MoveListItemWithId0ToRecycleBin,item.Id), Properties.Resources.Confirm)) + if (Force || ShouldContinue(string.Format(Properties.Resources.MoveListItemWithId0ToRecycleBin, item.Id), Properties.Resources.Confirm)) { - item.Recycle(); + var recycleBinResult = item.Recycle(); ClientContext.ExecuteQueryRetry(); + WriteObject(new RecycleResult { RecycleBinItemId = recycleBinResult.Value }); } } } diff --git a/src/Commands/Lists/RemoveList.cs b/src/Commands/Lists/RemoveList.cs index 85e0ace58..8cb9490d9 100644 --- a/src/Commands/Lists/RemoveList.cs +++ b/src/Commands/Lists/RemoveList.cs @@ -1,18 +1,25 @@ using System.Management.Automation; + using Microsoft.SharePoint.Client; using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Model.SharePoint; namespace PnP.PowerShell.Commands.Lists { - [Cmdlet(VerbsCommon.Remove, "PnPList")] + [Cmdlet(VerbsCommon.Remove, "PnPList", DefaultParameterSetName = ParameterSet_Delete)] + [OutputType(typeof(void), ParameterSetName = new[] { ParameterSet_Delete })] + [OutputType(typeof(RecycleResult), ParameterSetName = new[] { ParameterSet_Recycle })] public class RemoveList : PnPWebCmdlet { + public const string ParameterSet_Delete = "Delete"; + public const string ParameterSet_Recycle = "Recycle"; + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] [ValidateNotNull] public ListPipeBind Identity; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_Recycle)] public SwitchParameter Recycle; [Parameter(Mandatory = false)] @@ -26,13 +33,15 @@ protected override void ExecuteCmdlet() { if (Recycle) { - list.Recycle(); + var recycleResult = list.Recycle(); + ClientContext.ExecuteQueryRetry(); + WriteObject(new RecycleResult { RecycleBinItemId = recycleResult.Value }); } else { list.DeleteObject(); + ClientContext.ExecuteQueryRetry(); } - ClientContext.ExecuteQueryRetry(); } } } diff --git a/src/Commands/Lists/RemoveListItem.cs b/src/Commands/Lists/RemoveListItem.cs index 7a62f8682..ab0485f73 100644 --- a/src/Commands/Lists/RemoveListItem.cs +++ b/src/Commands/Lists/RemoveListItem.cs @@ -1,36 +1,64 @@ using System.Management.Automation; + using Microsoft.SharePoint.Client; + using PnP.PowerShell.Commands.Base.PipeBinds; using PnP.PowerShell.Commands.Model; +using PnP.PowerShell.Commands.Model.SharePoint; + using Resources = PnP.PowerShell.Commands.Properties.Resources; namespace PnP.PowerShell.Commands.Lists { - [Cmdlet(VerbsCommon.Remove, "PnPListItem", DefaultParameterSetName = ParameterSet_SINGLE)] + [Cmdlet(VerbsCommon.Remove, "PnPListItem", DefaultParameterSetName = ParameterSet_ALL_DELETE)] + [OutputType(typeof(void))] + [OutputType(typeof(RecycleResult), ParameterSetName = new[] { + ParameterSet_ALL_RECYCLE, + ParameterSet_SINGLE_List_RECYCLE, + ParameterSet_SINGLE_ListItem_RECYCLE } + )] public class RemoveListItem : PnPWebCmdlet { + const string ParameterSet_ALL_DELETE = "Delete single item in list"; + const string ParameterSet_ALL_RECYCLE = "Recycle all items in list"; + const string ParameterSet_BATCHED = "Batched"; - const string ParameterSet_SINGLE = "Single"; - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_SINGLE)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_BATCHED)] - [Parameter(ValueFromPipeline = true, Position = 0)] + const string ParameterSet_SINGLE_List_DELETE = "Delete single item in list"; + const string ParameterSet_SINGLE_ListItem_DELETE = "Delete single item"; + const string ParameterSet_SINGLE_List_RECYCLE = "Recycle single item in list"; + const string ParameterSet_SINGLE_ListItem_RECYCLE = "Recycle single item"; + + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, ParameterSetName = ParameterSet_BATCHED)] + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, ParameterSetName = ParameterSet_SINGLE_List_DELETE)] + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, ParameterSetName = ParameterSet_SINGLE_List_RECYCLE)] [ValidateNotNull] public ListPipeBind List; - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_SINGLE)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_BATCHED)] - [Parameter(ValueFromPipeline = true)] + [Parameter(Mandatory = true, ValueFromPipeline = true, ParameterSetName = ParameterSet_BATCHED)] + [Parameter(Mandatory = true, ValueFromPipeline = true, ParameterSetName = ParameterSet_SINGLE_List_DELETE)] + [Parameter(Mandatory = true, ValueFromPipeline = true, ParameterSetName = ParameterSet_SINGLE_ListItem_DELETE)] + [Parameter(Mandatory = true, ValueFromPipeline = true, ParameterSetName = ParameterSet_SINGLE_List_RECYCLE)] + [Parameter(Mandatory = true, ValueFromPipeline = true, ParameterSetName = ParameterSet_SINGLE_ListItem_RECYCLE)] + [ValidateNotNull] public ListItemPipeBind Identity; - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_SINGLE)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_BATCHED)] + [Parameter(ParameterSetName = ParameterSet_ALL_RECYCLE)] + [Parameter(ParameterSetName = ParameterSet_BATCHED)] + [Parameter(ParameterSetName = ParameterSet_SINGLE_List_RECYCLE)] + [Parameter(ParameterSetName = ParameterSet_SINGLE_ListItem_RECYCLE)] public SwitchParameter Recycle; - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_SINGLE)] + [Parameter(ParameterSetName = ParameterSet_ALL_DELETE)] + [Parameter(ParameterSetName = ParameterSet_ALL_RECYCLE)] + [Parameter(ParameterSetName = ParameterSet_SINGLE_List_DELETE)] + [Parameter(ParameterSetName = ParameterSet_SINGLE_ListItem_DELETE)] + [Parameter(ParameterSetName = ParameterSet_SINGLE_List_RECYCLE)] + [Parameter(ParameterSetName = ParameterSet_SINGLE_ListItem_RECYCLE)] public SwitchParameter Force; [Parameter(Mandatory = true, ParameterSetName = ParameterSet_BATCHED)] + [ValidateNotNull] public PnPBatch Batch; protected override void ExecuteCmdlet() @@ -92,7 +120,7 @@ protected override void ExecuteCmdlet() { stillItemsToProcess = false; } - } + } } return; } @@ -113,13 +141,15 @@ protected override void ExecuteCmdlet() { if (Recycle) { - item.Recycle(); + var recycleResult = item.Recycle(); + ClientContext.ExecuteQueryRetry(); + WriteObject(new RecycleResult { RecycleBinItemId = recycleResult.Value }); } else { item.DeleteObject(); + ClientContext.ExecuteQueryRetry(); } - ClientContext.ExecuteQueryRetry(); } } } diff --git a/src/Commands/Model/SharePoint/RecycleResult.cs b/src/Commands/Model/SharePoint/RecycleResult.cs new file mode 100644 index 000000000..a3aaca73d --- /dev/null +++ b/src/Commands/Model/SharePoint/RecycleResult.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace PnP.PowerShell.Commands.Model.SharePoint +{ + public sealed class RecycleResult + { + public Guid RecycleBinItemId { get; set; } + } +} From 2de927d0c3b55c3444b6f65bd65b7cdb57b24542 Mon Sep 17 00:00:00 2001 From: James May Date: Wed, 20 Apr 2022 20:50:14 +1000 Subject: [PATCH 267/458] RemoteFile, RemoveFolder, RemovePage: output the Id of the recycle bin item --- src/Commands/Files/RemoveFile.cs | 29 +++++++++++++++++++---------- src/Commands/Files/RemoveFolder.cs | 8 +++++--- src/Commands/Pages/RemovePage.cs | 16 ++++++++++++---- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/Commands/Files/RemoveFile.cs b/src/Commands/Files/RemoveFile.cs index 9ada57380..33089ecf4 100644 --- a/src/Commands/Files/RemoveFile.cs +++ b/src/Commands/Files/RemoveFile.cs @@ -2,22 +2,30 @@ using Microsoft.SharePoint.Client; using Resources = PnP.PowerShell.Commands.Properties.Resources; using PnP.Framework.Utilities; +using PnP.PowerShell.Commands.Model.SharePoint; namespace PnP.PowerShell.Commands.Files { [Cmdlet(VerbsCommon.Remove, "PnPFile")] public class RemoveFile : PnPWebCmdlet { - private const string ParameterSet_SERVER = "Server Relative"; - private const string ParameterSet_SITE = "Site Relative"; - - [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ParameterSetName = ParameterSet_SERVER)] + private const string ParameterSet_SERVER_Delete = "Delete by Server Relative"; + private const string ParameterSet_SITE_Delete = "Delete by Site Relative"; + private const string ParameterSet_SERVER_Recycle = "Recycle by Server Relative"; + private const string ParameterSet_SITE_Recycle = "Recycle by Site Relative"; + + [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ParameterSetName = ParameterSet_SERVER_Delete)] + [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ParameterSetName = ParameterSet_SERVER_Recycle)] + [ValidateNotNullOrEmpty] public string ServerRelativeUrl = string.Empty; - [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ParameterSetName = ParameterSet_SITE)] + [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ParameterSetName = ParameterSet_SITE_Delete)] + [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ParameterSetName = ParameterSet_SITE_Recycle)] + [ValidateNotNullOrEmpty] public string SiteRelativeUrl = string.Empty; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_SERVER_Recycle)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_SITE_Recycle)] public SwitchParameter Recycle; [Parameter(Mandatory = false)] @@ -25,7 +33,7 @@ public class RemoveFile : PnPWebCmdlet protected override void ExecuteCmdlet() { - if (ParameterSetName == ParameterSet_SITE) + if (!string.IsNullOrEmpty(ServerRelativeUrl)) { var webUrl = CurrentWeb.EnsureProperty(w => w.ServerRelativeUrl); ServerRelativeUrl = UrlUtility.Combine(webUrl, SiteRelativeUrl); @@ -38,14 +46,15 @@ protected override void ExecuteCmdlet() { if (Recycle) { - file.Recycle(); + var recycleResult = file.Recycle(); + ClientContext.ExecuteQueryRetry(); + WriteObject(new RecycleResult { RecycleBinItemId = recycleResult.Value }); } else { file.DeleteObject(); + ClientContext.ExecuteQueryRetry(); } - - ClientContext.ExecuteQueryRetry(); } } } diff --git a/src/Commands/Files/RemoveFolder.cs b/src/Commands/Files/RemoveFolder.cs index d12a368b9..87822262c 100644 --- a/src/Commands/Files/RemoveFolder.cs +++ b/src/Commands/Files/RemoveFolder.cs @@ -3,6 +3,7 @@ using Resources = PnP.PowerShell.Commands.Properties.Resources; using PnP.Framework.Utilities; using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Model.SharePoint; namespace PnP.PowerShell.Commands.Files { @@ -38,14 +39,15 @@ protected override void ExecuteCmdlet() { if (Recycle) { - folder.Recycle(); + var recycleResult = folder.Recycle(); + ClientContext.ExecuteQueryRetry(); + WriteObject(new RecycleResult { RecycleBinItemId = recycleResult.Value }); } else { folder.DeleteObject(); + ClientContext.ExecuteQueryRetry(); } - - ClientContext.ExecuteQueryRetry(); } } } diff --git a/src/Commands/Pages/RemovePage.cs b/src/Commands/Pages/RemovePage.cs index b1a0f27da..2b54f2b47 100644 --- a/src/Commands/Pages/RemovePage.cs +++ b/src/Commands/Pages/RemovePage.cs @@ -1,22 +1,29 @@  using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Model.SharePoint; using PnP.PowerShell.Commands.Properties; + using System; using System.Management.Automation; namespace PnP.PowerShell.Commands.Pages { - [Cmdlet(VerbsCommon.Remove, "PnPPage")] + [Cmdlet(VerbsCommon.Remove, "PnPPage", DefaultParameterSetName = ParameterSet_Delete)] [Alias("Remove-PnPClientSidePage")] + [OutputType(typeof(void), ParameterSetName = new[] { ParameterSet_Delete })] + [OutputType(typeof(RecycleResult), ParameterSetName = new[] { ParameterSet_Recycle })] public class RemovePage : PnPWebCmdlet { + public const string ParameterSet_Delete = "Delete"; + public const string ParameterSet_Recycle = "Recycle"; + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] public PagePipeBind Identity; [Parameter(Mandatory = false)] public SwitchParameter Force; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_Recycle)] public SwitchParameter Recycle; protected override void ExecuteCmdlet() @@ -29,12 +36,13 @@ protected override void ExecuteCmdlet() if (Recycle.IsPresent) { - clientSidePage.PageListItem.Recycle(); + var recycleResult = clientSidePage.PageListItem.Recycle(); + WriteObject(new RecycleResult { RecycleBinItemId = recycleResult }); } else { clientSidePage.Delete(); - } + } } } } From b17d28eb483e701f8a23c21f109083d024fb44c8 Mon Sep 17 00:00:00 2001 From: James May Date: Wed, 20 Apr 2022 20:50:42 +1000 Subject: [PATCH 268/458] RecycleBinItemPipeBind: add conversion from RecycleResult --- src/Commands/Base/PipeBinds/RecycleBinItemPipeBind.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Commands/Base/PipeBinds/RecycleBinItemPipeBind.cs b/src/Commands/Base/PipeBinds/RecycleBinItemPipeBind.cs index 2eb090483..51bf5d955 100644 --- a/src/Commands/Base/PipeBinds/RecycleBinItemPipeBind.cs +++ b/src/Commands/Base/PipeBinds/RecycleBinItemPipeBind.cs @@ -1,6 +1,8 @@ using System; using Microsoft.SharePoint.Client; +using PnP.PowerShell.Commands.Model.SharePoint; + namespace PnP.PowerShell.Commands.Base.PipeBinds { public sealed class RecycleBinItemPipeBind @@ -19,6 +21,11 @@ public RecycleBinItemPipeBind(RecycleBinItem item) _item = item; } + public RecycleBinItemPipeBind(RecycleResult result) + { + _id = result.RecycleBinItemId; + } + public RecycleBinItemPipeBind(string id) { Guid guid; From fecc9369371677b7b714f107caa071bd72fd4860 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Mon, 30 May 2022 23:52:52 +0200 Subject: [PATCH 269/458] Added changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3d938e17..56cdf30cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Changed `Get-PnPOrgAssetsLibrary` to return a proper value of the organisation assets libraries. [#1889](https://github.com/pnp/powershell/pull/1889) - Bumped .NET Framework version to 4.6.2 as the 4.6.1 is not supported anymore. [#1856](https://github.com/pnp/powershell/pull/1856) - Changed `Add-PnPDataRowsToSiteTemplate`, it will now export a datetime field value as UTC string. [#1900](https://github.com/pnp/powershell/pull/1900) +- The cmdlets `Remove-PnPFile`, `Remove-PnPFolder`, `Move-PnPListItemToRecycleBin`, `Remove-PnPList`, `Remove-PnPListItem` and `Remove-PnPPage` will now return the corresponding recycle bin item if they get deleted to the recycle bin. Before they would not return anything. [#1783](https://github.com/pnp/powershell/pull/1783) ### Fixed From 2c26c483cd6afc6b79d8479b1164106ddd07b0af Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 31 May 2022 00:08:13 +0200 Subject: [PATCH 270/458] Added changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3d938e17..f8bb8c93f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added new `PnP.PowerShell` image which also gets published to Docker Hub. [#1580](https://github.com/pnp/powershell/pull/1794) - Added capability to Debug the module in Visual Studio. [#1880](https://github.com/pnp/powershell/pull/1880) - Added `Set-PnPTeamsChannelUser` cmdlet to update the role of user in a private channel. [#1865](https://github.com/pnp/powershell/pull/1865) +- Added `Restart-PnPFlowRun` which allows for a failed Power Automate flow run to be retried [#1915](https://github.com/pnp/powershell/pull/1915) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) From 89df72c52898aadbd8a0075f088947da5506648f Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 31 May 2022 00:35:51 +0200 Subject: [PATCH 271/458] Removed 200 mb limit --- documentation/Copy-PnPFile.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Copy-PnPFile.md b/documentation/Copy-PnPFile.md index 2cf5b0fe5..9335df11f 100644 --- a/documentation/Copy-PnPFile.md +++ b/documentation/Copy-PnPFile.md @@ -21,7 +21,7 @@ Copy-PnPFile [-SourceUrl] [-TargetUrl] [-Overwrite] [-Force] [ ## DESCRIPTION -Copies a file or folder to a different location. This location can be within the same document library, same site, same site collection or even to another site collection on the same tenant. Currently there is a 200MB file size limit for the file or folder to be copied. Notice that if copying between sites or to a subsite you cannot specify a target filename, only a folder name. +Copies a file or folder to a different location. This location can be within the same document library, same site, same site collection or even to another site collection on the same tenant. Notice that if copying between sites or to a subsite you cannot specify a target filename, only a folder name. It is currently not possible to copy files between a OneDrive for Business site to SharePoint or vice versa. Copying files and folders is bound to some restrictions. You can find more on it here: https://docs.microsoft.com/office365/servicedescriptions/sharepoint-online-service-description/sharepoint-online-limits#moving-and-copying-across-sites From b16468e561ea7093d849b8fedc72aac00835d686 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 31 May 2022 02:42:30 +0200 Subject: [PATCH 272/458] Bugfixes and code cleanup --- CHANGELOG.md | 2 + documentation/Get-PnPConnection.md | 22 +++++++-- documentation/Get-PnPContext.md | 22 +++++++-- documentation/Get-PnPList.md | 30 ++++++------ src/Commands/Base/BasePSCmdlet.cs | 5 -- src/Commands/Base/ConnectOnline.cs | 13 ++++-- src/Commands/Base/DisconnectOnline.cs | 51 +++++---------------- src/Commands/Base/GetConnection.cs | 24 ++-------- src/Commands/Base/GetContext.cs | 24 ++-------- src/Commands/Base/PnPConnectedCmdlet.cs | 13 ++++-- src/Commands/Base/PnPConnection.cs | 2 + src/Commands/Base/PnPSharePointCmdlet.cs | 41 ++++++++++------- src/Commands/Base/PnPWebRetrievalsCmdlet.cs | 18 ++++---- 13 files changed, 118 insertions(+), 149 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bab2d0cb7..bfa3757b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Invoke-PnPSPRestMethod` invalid parsing for SharePoint number columns. [#1877](https://github.com/pnp/powershell/pull/1879) - Fix issue with `Add/Set-PnPListItem` not throwing correct exception for invalid taxonomy values. [#1870](https://github.com/pnp/powershell/pull/1870) - Fixed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` throwing an "Object reference not set to an instance of an object" exception when providing an empty users collection or incorrect user mapping [#1896](https://github.com/pnp/powershell/pull/1896) +- Fixed `Connect-PnPOnline -ReturnConnection` also setting the current connection instead of just the returned connection +- Fixed `Disconnect-PnPOnline -Connection` also disconnecting other connections next to the provided connection ### Removed - Removed `Get-PnPAvailableClientSideComponents`. Use `Get-PnPPageComponent -Page -ListAvailable` instead. [#1833](https://github.com/pnp/powershell/pull/1833) diff --git a/documentation/Get-PnPConnection.md b/documentation/Get-PnPConnection.md index f0a033f12..c67553c36 100644 --- a/documentation/Get-PnPConnection.md +++ b/documentation/Get-PnPConnection.md @@ -10,12 +10,12 @@ title: Get-PnPConnection # Get-PnPConnection ## SYNOPSIS -Returns the current context +Returns the current connection ## SYNTAX ```powershell -Get-PnPConnection [] +Get-PnPConnection [-Connection ] [] ``` ## DESCRIPTION @@ -32,8 +32,22 @@ This will put the current connection for use with the -Connection parameter on o ## PARAMETERS -## RELATED LINKS +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by specifying -ReturnConnection on Connect-PnPOnline. If not provided, the connection will be retrieved from the current context. + +```yaml -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) +```yaml +Type: PnPConnection +Parameter Sets: (All) +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/documentation/Get-PnPContext.md b/documentation/Get-PnPContext.md index 81e213c75..100f615e3 100644 --- a/documentation/Get-PnPContext.md +++ b/documentation/Get-PnPContext.md @@ -10,16 +10,16 @@ title: Get-PnPContext # Get-PnPContext ## SYNOPSIS -Returns the current context +Returns the current SharePoint Online CSOM context ## SYNTAX ```powershell -Get-PnPContext [] +Get-PnPContext [-Connection ] [] ``` ## DESCRIPTION -Returns a Client Side Object Model context +Returns a SharePoint Online Client Side Object Model (CSOM) context ## EXAMPLES @@ -43,8 +43,20 @@ Get-PnPList # returns the lists from site A ## PARAMETERS -## RELATED LINKS +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. If not provided, the context of the connection will be retrieved from the current connection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` +## RELATED LINKS +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/documentation/Get-PnPList.md b/documentation/Get-PnPList.md index 77797c022..8a538c751 100644 --- a/documentation/Get-PnPList.md +++ b/documentation/Get-PnPList.md @@ -53,20 +53,6 @@ This examples shows how to do wildcard searches on the list URL. It returns all ## PARAMETERS -### -Connection -Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. - -```yaml -Type: PnPConnection -Parameter Sets: (All) - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - ### -Identity The ID, name or Url (Lists/MyList) of the list @@ -95,10 +81,20 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. +```yaml +Type: PnPConnection +Parameter Sets: (All) -## RELATED LINKS - -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` +## RELATED LINKS +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/src/Commands/Base/BasePSCmdlet.cs b/src/Commands/Base/BasePSCmdlet.cs index ef31875c7..f16d0927c 100644 --- a/src/Commands/Base/BasePSCmdlet.cs +++ b/src/Commands/Base/BasePSCmdlet.cs @@ -1,10 +1,5 @@ using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Linq.Expressions; using System.Management.Automation; -using System.Reflection; using PnP.PowerShell.Commands.Attributes; namespace PnP.PowerShell.Commands.Base diff --git a/src/Commands/Base/ConnectOnline.cs b/src/Commands/Base/ConnectOnline.cs index ad43c8597..2c600674b 100644 --- a/src/Commands/Base/ConnectOnline.cs +++ b/src/Commands/Base/ConnectOnline.cs @@ -283,8 +283,8 @@ protected void Connect(ref CancellationToken cancellationToken) #else WriteVerbose($"PnP PowerShell Cmdlets ({Assembly.GetExecutingAssembly().GetName().Version})"); #endif - PnPConnection.Current = connection; - if (CreateDrive && PnPConnection.Current.Context != null) + + if (CreateDrive && connection.Context != null) { var provider = SessionState.Provider.GetAll().FirstOrDefault(p => p.Name.Equals(SPOProvider.PSProviderName, StringComparison.InvariantCultureIgnoreCase)); if (provider != null) @@ -299,9 +299,9 @@ protected void Connect(ref CancellationToken cancellationToken) } } - if (PnPConnection.Current.Url != null) + if (connection.Url != null) { - var hostUri = new Uri(PnPConnection.Current.Url); + var hostUri = new Uri(connection.Url); Environment.SetEnvironmentVariable("PNPPSHOST", hostUri.Host); Environment.SetEnvironmentVariable("PNPPSSITE", hostUri.LocalPath); } @@ -315,7 +315,10 @@ protected void Connect(ref CancellationToken cancellationToken) { WriteObject(connection); } - + else + { + PnPConnection.Current = connection; + } } #region Connect Types diff --git a/src/Commands/Base/DisconnectOnline.cs b/src/Commands/Base/DisconnectOnline.cs index 5d8c837a5..c611a82f2 100644 --- a/src/Commands/Base/DisconnectOnline.cs +++ b/src/Commands/Base/DisconnectOnline.cs @@ -4,7 +4,6 @@ using System.Management.Automation; using System.Reflection; using PnP.PowerShell.Commands.Provider; -using PnP.PowerShell.Commands.Model; namespace PnP.PowerShell.Commands.Base { @@ -18,35 +17,24 @@ public class DisconnectOnline : PSCmdlet protected override void ProcessRecord() { // If no specific connection has been passed in, take the connection from the current context - if (Connection == null) - { - Connection = PnPConnection.Current; - } - if (Connection?.Certificate != null) + var connection = Connection ?? PnPConnection.Current; + + if (connection?.Certificate != null) { - if (Connection != null && Connection.DeleteCertificateFromCacheOnDisconnect) + if (connection != null && connection.DeleteCertificateFromCacheOnDisconnect) { - PnPConnection.CleanupCryptoMachineKey(Connection.Certificate); + PnPConnection.CleanupCryptoMachineKey(connection.Certificate); } - Connection.Certificate = null; - } - var success = false; - if (Connection != null) - { - success = DisconnectProvidedService(Connection); - } - else - { - success = DisconnectCurrentService(); + connection.Certificate = null; } + + var success = DisconnectProvidedService(ref connection); + if (!success) { throw new InvalidOperationException(Properties.Resources.NoConnectionToDisconnect); } - // clear credentials - PnPConnection.Current = null; - var provider = SessionState.Provider.GetAll().FirstOrDefault(p => p.Name.Equals(SPOProvider.PSProviderName, StringComparison.InvariantCultureIgnoreCase)); if (provider != null) { @@ -59,7 +47,7 @@ protected override void ProcessRecord() } } - internal static bool DisconnectProvidedService(PnPConnection connection) + internal static bool DisconnectProvidedService(ref PnPConnection connection) { Environment.SetEnvironmentVariable("PNPPSHOST", string.Empty); Environment.SetEnvironmentVariable("PNPPSSITE", string.Empty); @@ -71,22 +59,5 @@ internal static bool DisconnectProvidedService(PnPConnection connection) connection = null; return true; } - - internal static bool DisconnectCurrentService() - { - Environment.SetEnvironmentVariable("PNPPSHOST", string.Empty); - Environment.SetEnvironmentVariable("PNPPSSITE", string.Empty); - - if (PnPConnection.Current == null) - { - return false; - } - else - { - PnPConnection.Current.Context = null; - PnPConnection.Current = null; - return true; - } - } } -} +} \ No newline at end of file diff --git a/src/Commands/Base/GetConnection.cs b/src/Commands/Base/GetConnection.cs index 97beff965..cd05c3f94 100644 --- a/src/Commands/Base/GetConnection.cs +++ b/src/Commands/Base/GetConnection.cs @@ -1,32 +1,14 @@ using System.Management.Automation; -using System; -using PnP.PowerShell.Commands.Properties; - namespace PnP.PowerShell.Commands.Base { [Cmdlet(VerbsCommon.Get, "PnPConnection")] [OutputType(typeof(PnPConnection))] - public class GetPnPConnection : PSCmdlet + public class GetPnPConnection : PnPSharePointCmdlet { - - protected override void BeginProcessing() - { - base.BeginProcessing(); - - if (PnPConnection.Current == null) - { - throw new InvalidOperationException(Resources.NoSharePointConnection); - } - if (PnPConnection.Current.Context == null) - { - throw new InvalidOperationException(Resources.NoSharePointConnection); - } - } - protected override void ProcessRecord() { - WriteObject(PnPConnection.Current); + WriteObject(Connection); } } -} +} \ No newline at end of file diff --git a/src/Commands/Base/GetContext.cs b/src/Commands/Base/GetContext.cs index 0c5b08f84..6e45652fd 100644 --- a/src/Commands/Base/GetContext.cs +++ b/src/Commands/Base/GetContext.cs @@ -1,32 +1,14 @@ using System.Management.Automation; -using System; -using PnP.PowerShell.Commands.Properties; - namespace PnP.PowerShell.Commands.Base { [Cmdlet(VerbsCommon.Get, "PnPContext")] [OutputType(typeof(Microsoft.SharePoint.Client.ClientContext))] - public class GetSPOContext : PSCmdlet + public class GetSPOContext : PnPSharePointCmdlet { - - protected override void BeginProcessing() - { - base.BeginProcessing(); - - if (PnPConnection.Current == null) - { - throw new InvalidOperationException(Resources.NoSharePointConnection); - } - if (PnPConnection.Current.Context == null) - { - throw new InvalidOperationException(Resources.NoSharePointConnection); - } - } - protected override void ProcessRecord() { - WriteObject(PnPConnection.Current.Context); + WriteObject(Connection.Context); } } -} +} \ No newline at end of file diff --git a/src/Commands/Base/PnPConnectedCmdlet.cs b/src/Commands/Base/PnPConnectedCmdlet.cs index 9feabbd78..c6b5eb630 100644 --- a/src/Commands/Base/PnPConnectedCmdlet.cs +++ b/src/Commands/Base/PnPConnectedCmdlet.cs @@ -1,5 +1,4 @@ using System; -using System.Management.Automation; using System.Net.Http; namespace PnP.PowerShell.Commands.Base @@ -10,9 +9,17 @@ namespace PnP.PowerShell.Commands.Base public abstract class PnPConnectedCmdlet : BasePSCmdlet { protected override void BeginProcessing() + { + BeginProcessing(false); + } + + protected void BeginProcessing(bool skipConnectedValidation) { base.BeginProcessing(); + // Check if we should ensure that we are connected + if(skipConnectedValidation) return; + // Ensure there is an active connection if (PnPConnection.Current == null) { @@ -21,9 +28,5 @@ protected override void BeginProcessing() } public HttpClient HttpClient => PnP.Framework.Http.PnPHttpClient.Instance.GetHttpClient(); - - } - - } diff --git a/src/Commands/Base/PnPConnection.cs b/src/Commands/Base/PnPConnection.cs index 2bf5115d3..985ffd856 100644 --- a/src/Commands/Base/PnPConnection.cs +++ b/src/Commands/Base/PnPConnection.cs @@ -611,6 +611,8 @@ internal void RestoreCachedContext(string url) internal void CacheContext() { + if(Context == null) return; + var c = ContextCache.FirstOrDefault(cc => new Uri(cc.Url).AbsoluteUri == new Uri(Context.Url).AbsoluteUri); if (c == null) { diff --git a/src/Commands/Base/PnPSharePointCmdlet.cs b/src/Commands/Base/PnPSharePointCmdlet.cs index 49e02fc53..42a6cce56 100644 --- a/src/Commands/Base/PnPSharePointCmdlet.cs +++ b/src/Commands/Base/PnPSharePointCmdlet.cs @@ -20,9 +20,9 @@ public abstract class PnPSharePointCmdlet : PnPConnectedCmdlet /// /// Reference the the SharePoint context on the current connection. If NULL it means there is no SharePoint context available on the current connection. /// - public ClientContext ClientContext => Connection?.Context ?? PnPConnection.Current.Context; + public ClientContext ClientContext => Connection?.Context; - public PnPContext PnPContext => Connection?.PnPContext ?? PnPConnection.Current.PnPContext; + public PnPContext PnPContext => Connection?.PnPContext ?? Connection.PnPContext; public new HttpClient HttpClient => PnP.Framework.Http.PnPHttpClient.Instance.GetHttpClient(ClientContext); @@ -33,14 +33,23 @@ public abstract class PnPSharePointCmdlet : PnPConnectedCmdlet protected override void BeginProcessing() { - base.BeginProcessing(); + // Call the base but instruct it not to check if there's an active connection as we will do that in this method already + base.BeginProcessing(true); - if (PnPConnection.Current != null && PnPConnection.Current.ApplicationInsights != null) + // If a specific connection has been provided, use that, otherwise use the current connection + if(Connection == null) { - PnPConnection.Current.ApplicationInsights.TrackEvent(MyInvocation.MyCommand.Name); + Connection = PnPConnection.Current; } - if (Connection == null && ClientContext == null) + // Track the execution of the cmdlet + if (Connection != null && Connection.ApplicationInsights != null) + { + Connection.ApplicationInsights.TrackEvent(MyInvocation.MyCommand.Name); + } + + // Ensure there is an active connection to work with + if (Connection == null || ClientContext == null) { throw new InvalidOperationException(Resources.NoSharePointConnection); } @@ -50,7 +59,7 @@ protected override void ProcessRecord() { try { - var tag = PnPConnection.Current.PnPVersionTag + ":" + MyInvocation.MyCommand.Name; + var tag = Connection.PnPVersionTag + ":" + MyInvocation.MyCommand.Name; if (tag.Length > 32) { tag = tag.Substring(0, 32); @@ -75,8 +84,8 @@ protected override void ProcessRecord() } catch (Exception ex) { - PnPConnection.Current.RestoreCachedContext(PnPConnection.Current.Url); - ex.Data["CorrelationId"] = PnPConnection.Current.Context.TraceCorrelationId; + Connection.RestoreCachedContext(Connection.Url); + ex.Data["CorrelationId"] = Connection.Context.TraceCorrelationId; ex.Data["TimeStampUtc"] = DateTime.UtcNow; var errorDetails = new ErrorDetails(ex.Message); @@ -97,17 +106,17 @@ protected string AccessToken { get { - if (PnPConnection.Current != null) + if (Connection != null) { - if (PnPConnection.Current.Context != null) + if (Connection.Context != null) { - var settings = Microsoft.SharePoint.Client.InternalClientContextExtensions.GetContextSettings(PnPConnection.Current.Context); + var settings = Microsoft.SharePoint.Client.InternalClientContextExtensions.GetContextSettings(Connection.Context); if (settings != null) { var authManager = settings.AuthenticationManager; if (authManager != null) { - return authManager.GetAccessTokenAsync(PnPConnection.Current.Context.Url).GetAwaiter().GetResult(); + return authManager.GetAccessTokenAsync(Connection.Context.Url).GetAwaiter().GetResult(); } } } @@ -120,15 +129,15 @@ public string GraphAccessToken { get { - if (PnPConnection.Current?.ConnectionMethod == ConnectionMethod.ManagedIdentity) + if (Connection?.ConnectionMethod == ConnectionMethod.ManagedIdentity) { return TokenHandler.GetManagedIdentityTokenAsync(this, HttpClient, $"https://graph.microsoft.com/").GetAwaiter().GetResult(); } else { - if (PnPConnection.Current?.Context != null) + if (Connection?.Context != null) { - return TokenHandler.GetAccessToken(GetType(), $"https://{PnPConnection.Current.GraphEndPoint}/.default"); + return TokenHandler.GetAccessToken(GetType(), $"https://{Connection.GraphEndPoint}/.default"); } } diff --git a/src/Commands/Base/PnPWebRetrievalsCmdlet.cs b/src/Commands/Base/PnPWebRetrievalsCmdlet.cs index f447b6861..7b7e8e50d 100644 --- a/src/Commands/Base/PnPWebRetrievalsCmdlet.cs +++ b/src/Commands/Base/PnPWebRetrievalsCmdlet.cs @@ -4,8 +4,6 @@ using System.Management.Automation; using Microsoft.SharePoint.Client; -using PnP.PowerShell.Commands.Extensions; - namespace PnP.PowerShell.Commands { /// @@ -41,20 +39,20 @@ private Web GetWeb() { var subWeb = Web.GetWeb(ClientContext); subWeb.EnsureProperty(w => w.Url); - PnPConnection.Current.CloneContext(subWeb.Url); - web = PnPConnection.Current.Context.Web; + Connection.CloneContext(subWeb.Url); + web = Connection.Context.Web; } #pragma warning restore CS0618 else { - if (PnPConnection.Current.Context.Url != PnPConnection.Current.Url) + if (Connection.Context.Url != Connection.Url) { - PnPConnection.Current.RestoreCachedContext(PnPConnection.Current.Url); + Connection.RestoreCachedContext(Connection.Url); } web = ClientContext.Web; } - PnPConnection.Current.Context.ExecuteQueryRetry(); + Connection.Context.ExecuteQueryRetry(); return web; } @@ -62,16 +60,16 @@ private Web GetWeb() protected override void EndProcessing() { base.EndProcessing(); - if (PnPConnection.Current.Context.Url != PnPConnection.Current.Url) + if (Connection.Context.Url != Connection.Url) { - PnPConnection.Current.RestoreCachedContext(PnPConnection.Current.Url); + Connection.RestoreCachedContext(Connection.Url); } } protected override void BeginProcessing() { base.BeginProcessing(); - PnPConnection.Current.CacheContext(); + Connection.CacheContext(); } } } \ No newline at end of file From 4d70a9f438efe1c6b1644bf5b1a628cd03b0c8ab Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 31 May 2022 03:42:02 +0000 Subject: [PATCH 273/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index da353fd12..6d1f5686a 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -a9a0a166cbaa8b0c28ca469d787d888ff3b23c0f \ No newline at end of file +1a2db2bfc90f91d5c429d1f3a7277e6fa2b50090 \ No newline at end of file diff --git a/version.txt b/version.txt index 74f0293e7..132b45dbf 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.33 \ No newline at end of file +1.10.34 \ No newline at end of file From fbdbad96365267c96a0ab87794daa75aa4565ee3 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Tue, 31 May 2022 10:39:45 +0300 Subject: [PATCH 274/458] #1277 - retrieve deprecated terms (#1903) * #1277 - retrieve deprecated terms * Added changelog --- CHANGELOG.md | 1 + documentation/Get-PnPTerm.md | 23 +++++++++++- .../Base/PipeBinds/TaxonomyTermPipeBind.cs | 37 +++++++++++++------ src/Commands/Taxonomy/GetTerm.cs | 7 +++- 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bab2d0cb7..b5932c10a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added capability to Debug the module in Visual Studio. [#1880](https://github.com/pnp/powershell/pull/1880) - Added `Set-PnPTeamsChannelUser` cmdlet to update the role of user in a private channel. [#1865](https://github.com/pnp/powershell/pull/1865) - Added `Restart-PnPFlowRun` which allows for a failed Power Automate flow run to be retried [#1915](https://github.com/pnp/powershell/pull/1915) +- Added `-IncludeDeprecated` parameter to `Get-PnPTerm` cmdlet to fetch deprecated terms if specified [#1903](https://github.com/pnp/powershell/pull/1903) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) diff --git a/documentation/Get-PnPTerm.md b/documentation/Get-PnPTerm.md index 789cb50ae..0c7175935 100644 --- a/documentation/Get-PnPTerm.md +++ b/documentation/Get-PnPTerm.md @@ -30,7 +30,7 @@ Get-PnPTerm -TermGroup [-TermStore ] [-Recursive] - [-IncludeChildTerms] [-Connection ] [-Includes ] [] + [-IncludeChildTerms][-IncludeDeprecated] [-Connection ] [-Includes ] [] ``` ## DESCRIPTION @@ -74,6 +74,13 @@ $term.Labels Returns all the localized labels for the term named "Small Finance", from the "Departments" termset in a term group called "Corporate" +### EXAMPLE 6 +```powershell +Get-PnPTerm -Identity "Small Finance" -TermSet "Departments" -TermGroup "Corporate" -Recursive -IncludeDeprecated +``` + +Returns the deprecated term named "Small Finance", from the "Departments" termset in a term group called "Corporate" from the site collection termstore even if it is a subterm below "Finance" + ## PARAMETERS ### -Connection @@ -174,6 +181,20 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -IncludeDeprecated +Includes the deprecated terms if available. + +```yaml +Type: SwitchParameter +Parameter Sets: By Term name + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) diff --git a/src/Commands/Base/PipeBinds/TaxonomyTermPipeBind.cs b/src/Commands/Base/PipeBinds/TaxonomyTermPipeBind.cs index e8df26fa9..be2956e1f 100644 --- a/src/Commands/Base/PipeBinds/TaxonomyTermPipeBind.cs +++ b/src/Commands/Base/PipeBinds/TaxonomyTermPipeBind.cs @@ -39,7 +39,7 @@ public TaxonomyTermPipeBind(Term item) public Term Item => _item; - public Term GetTerm(ClientContext clientContext, TermStore termStore, TermSet termSet, bool recursive, Expression>[] expressions = null) + public Term GetTerm(ClientContext clientContext, TermStore termStore, TermSet termSet, bool recursive, Expression>[] expressions = null, bool includeDeprecated = false) { Term term = null; if (_id != Guid.Empty) @@ -55,20 +55,35 @@ public Term GetTerm(ClientContext clientContext, TermStore termStore, TermSet te } else { - var lmi = new LabelMatchInformation(clientContext) + if (includeDeprecated) { - TrimUnavailable = true, - TermLabel = termName - }; + var allTerms = termSet.GetAllTermsIncludeDeprecated(); + clientContext.Load(allTerms); + clientContext.ExecuteQueryRetry(); - var termMatches = termSet.GetTerms(lmi); - clientContext.Load(termMatches); - clientContext.ExecuteQueryRetry(); - - if (termMatches.AreItemsAvailable) + if (allTerms.AreItemsAvailable) + { + term = allTerms.Where(t => t.Name == termName).FirstOrDefault(); + } + } + else { - term = termMatches.FirstOrDefault(); + var lmi = new LabelMatchInformation(clientContext) + { + TrimUnavailable = true, + TermLabel = termName + }; + + var termMatches = termSet.GetTerms(lmi); + clientContext.Load(termMatches); + clientContext.ExecuteQueryRetry(); + + if (termMatches.AreItemsAvailable) + { + term = termMatches.FirstOrDefault(); + } } + } } else diff --git a/src/Commands/Taxonomy/GetTerm.cs b/src/Commands/Taxonomy/GetTerm.cs index 51fdfdd04..8d5c0aa36 100644 --- a/src/Commands/Taxonomy/GetTerm.cs +++ b/src/Commands/Taxonomy/GetTerm.cs @@ -37,6 +37,9 @@ public class GetTerm : PnPRetrievalsCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_TERMNAME)] public TaxonomyTermPipeBind ParentTerm; + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_TERMNAME)] + public SwitchParameter IncludeDeprecated; + protected override void ExecuteCmdlet() { DefaultRetrievalExpressions = new Expression>[] { g => g.Name, g => g.TermsCount, g => g.Id }; @@ -77,7 +80,7 @@ protected override void ExecuteCmdlet() if (Identity != null && ParentTerm == null) { - var term = Identity.GetTerm(ClientContext, termStore, termSet, Recursive, RetrievalExpressions); + var term = Identity.GetTerm(ClientContext, termStore, termSet, Recursive, RetrievalExpressions, IncludeDeprecated); if (IncludeChildTerms.IsPresent && term.TermsCount > 0) { @@ -87,7 +90,7 @@ protected override void ExecuteCmdlet() } else if (Identity != null && ParentTerm != null) { - var term = ParentTerm.GetTerm(ClientContext, termStore, termSet, Recursive, RetrievalExpressions); + var term = ParentTerm.GetTerm(ClientContext, termStore, termSet, Recursive, RetrievalExpressions, IncludeDeprecated); if (IncludeChildTerms.IsPresent && term.TermsCount > 0) { From 5c096d15cd54a5bdea8366f9a58c8a02cb8e7cb3 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Tue, 31 May 2022 21:39:49 +0300 Subject: [PATCH 275/458] Updates to flag if it has unique permissions or not --- src/Commands/Lists/GetListItemPermission.cs | 18 ++++++++++++------ src/Commands/Model/ListItemPermissions.cs | 11 +++++++++-- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/Commands/Lists/GetListItemPermission.cs b/src/Commands/Lists/GetListItemPermission.cs index 47de1d7eb..8efd0c35e 100644 --- a/src/Commands/Lists/GetListItemPermission.cs +++ b/src/Commands/Lists/GetListItemPermission.cs @@ -30,16 +30,20 @@ protected override void ExecuteCmdlet() } ClientContext.Load(item, a => a.RoleAssignments.Include(roleAsg => roleAsg.Member.LoginName, + roleAsg => roleAsg.Member.PrincipalType, roleAsg => roleAsg.Member.Id, roleAsg => roleAsg.RoleDefinitionBindings.Include(roleDef => roleDef.Name, - roleDef => roleDef.Description))); + roleDef => roleDef.Description, roleDef => roleDef.Id, roleDef => roleDef.RoleTypeKind)), a => a.HasUniqueRoleAssignments); ClientContext.ExecuteQueryRetry(); - var listItemPermissions = new List(); + var listItemPermissions = new List(); + var listItemPermissionCollection = new ListItemPermissionCollection + { + HasUniqueRoleAssignments = item.HasUniqueRoleAssignments + }; foreach (var roleAssignment in item.RoleAssignments) { - roleAssignment.EnsureProperties(r => r.Member, r => r.Member.PrincipalType, r => r.Member.Id, r => r.Member.Id); - var listItemPermission = new ListItemPermissions + var listItemPermission = new Permissions { PrincipalName = roleAssignment.Member.LoginName, PrincipalType = roleAssignment.Member.PrincipalType, @@ -52,11 +56,13 @@ protected override void ExecuteCmdlet() roles.Add(role); } - listItemPermission.Permissions = roles; + listItemPermission.RoleDefinitions = roles; listItemPermissions.Add(listItemPermission); + + listItemPermissionCollection.Permissions = listItemPermissions; } - WriteObject(listItemPermissions, true); + WriteObject(listItemPermissionCollection, true); } } } \ No newline at end of file diff --git a/src/Commands/Model/ListItemPermissions.cs b/src/Commands/Model/ListItemPermissions.cs index b51da5eda..759958cd0 100644 --- a/src/Commands/Model/ListItemPermissions.cs +++ b/src/Commands/Model/ListItemPermissions.cs @@ -5,9 +5,16 @@ namespace PnP.PowerShell.Commands.Model { - public class ListItemPermissions + public class ListItemPermissionCollection { - public List Permissions { get; set; } + public bool HasUniqueRoleAssignments { get; set; } + + public List Permissions { get; set; } + } + + public class Permissions + { + public List RoleDefinitions { get; set; } public string PrincipalName { get; set; } From 888cba3d874fdc53d199da46fe9ff26c6e516daa Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Tue, 31 May 2022 21:45:23 +0300 Subject: [PATCH 276/458] Fixed return values --- src/Commands/Lists/GetListItemPermission.cs | 2 +- src/Commands/Model/ListItemPermissions.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Commands/Lists/GetListItemPermission.cs b/src/Commands/Lists/GetListItemPermission.cs index 8efd0c35e..58dba1ff4 100644 --- a/src/Commands/Lists/GetListItemPermission.cs +++ b/src/Commands/Lists/GetListItemPermission.cs @@ -56,7 +56,7 @@ protected override void ExecuteCmdlet() roles.Add(role); } - listItemPermission.RoleDefinitions = roles; + listItemPermission.PermissionLevels = roles; listItemPermissions.Add(listItemPermission); listItemPermissionCollection.Permissions = listItemPermissions; diff --git a/src/Commands/Model/ListItemPermissions.cs b/src/Commands/Model/ListItemPermissions.cs index 759958cd0..e26eb25f4 100644 --- a/src/Commands/Model/ListItemPermissions.cs +++ b/src/Commands/Model/ListItemPermissions.cs @@ -14,7 +14,7 @@ public class ListItemPermissionCollection public class Permissions { - public List RoleDefinitions { get; set; } + public List PermissionLevels { get; set; } public string PrincipalName { get; set; } From 55c3d58ebb6909e730a55824cd6b6a928c5f8e08 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Tue, 31 May 2022 22:27:49 +0300 Subject: [PATCH 277/458] #1360 - fetch Content type info for list items --- CHANGELOG.md | 1 + documentation/Get-PnPListItem.md | 28 ++++++++++++++++++---- src/Commands/Lists/GetListItem.cs | 40 ++++++++++++++++++++++--------- 3 files changed, 53 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bab2d0cb7..61c014e50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added capability to Debug the module in Visual Studio. [#1880](https://github.com/pnp/powershell/pull/1880) - Added `Set-PnPTeamsChannelUser` cmdlet to update the role of user in a private channel. [#1865](https://github.com/pnp/powershell/pull/1865) - Added `Restart-PnPFlowRun` which allows for a failed Power Automate flow run to be retried [#1915](https://github.com/pnp/powershell/pull/1915) +- Added `-IncludeContentType` parameter, which if specified will retrieve content type information of the list items. ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) diff --git a/documentation/Get-PnPListItem.md b/documentation/Get-PnPListItem.md index baa7d94bb..98acddec0 100644 --- a/documentation/Get-PnPListItem.md +++ b/documentation/Get-PnPListItem.md @@ -17,26 +17,25 @@ Retrieves list items ### All Items (Default) ```powershell Get-PnPListItem [-List] [-FolderServerRelativeUrl ] [-Fields ] - [-PageSize ] [-ScriptBlock ] [-Connection ] + [-PageSize ] [-ScriptBlock ][-IncludeContentType ][-Connection ] [] ``` ### By Id ```powershell -Get-PnPListItem [-List] [-Id ] [-Fields ] +Get-PnPListItem [-List] [-Id ] [-Fields ] [-IncludeContentType ] [-Connection ] [] ``` ### By Unique Id ```powershell -Get-PnPListItem [-List] [-UniqueId ] [-Fields ] - [-Connection ] [] +Get-PnPListItem [-List] [-UniqueId ] [-Fields ] [-IncludeContentType ] [-Connection ] [] ``` ### By Query ```powershell Get-PnPListItem [-List] [-Query ] [-FolderServerRelativeUrl ] - [-PageSize ] [-ScriptBlock ] [-Connection ] + [-PageSize ] [-IncludeContentType ] [-ScriptBlock ] [-Connection ] [] ``` @@ -119,6 +118,13 @@ Id Filename Retrieves all list items from the Shared Documents and shows each item's ID and Filename +### EXAMPLE 11 +```powershell +Get-PnPListItem -List Tasks -Id 1 -IncludeContentType +``` + +Retrieves the list item with ID 1 from the Tasks list along with its content type information. + ## PARAMETERS ### -Connection @@ -247,7 +253,19 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -IncludeContentType +If specified, it will retrieve the content type information of the list item(s). + +```yaml +Type: Switch Parameter +Parameter Sets: All +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` ## RELATED LINKS diff --git a/src/Commands/Lists/GetListItem.cs b/src/Commands/Lists/GetListItem.cs index 0ac812b69..055ab1767 100644 --- a/src/Commands/Lists/GetListItem.cs +++ b/src/Commands/Lists/GetListItem.cs @@ -39,14 +39,20 @@ public class GetListItem : PnPWebCmdlet public string[] Fields; [Parameter(Mandatory = false, ParameterSetName = ParameterSet_ALLITEMS)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_BYQUERY)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_BYQUERY)] public int PageSize = -1; - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_ALLITEMS)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_BYQUERY)] - public ScriptBlock ScriptBlock; + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_ALLITEMS)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_BYQUERY)] + public ScriptBlock ScriptBlock; - protected override void ExecuteCmdlet() + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_ALLITEMS)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_BYID)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_BYUNIQUEID)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_BYQUERY)] + public SwitchParameter IncludeContentType; + + protected override void ExecuteCmdlet() { var list = List.GetList(CurrentWeb); if (list == null) @@ -66,6 +72,10 @@ protected override void ExecuteCmdlet() { ClientContext.Load(listItem); } + if (IncludeContentType) + { + ClientContext.Load(listItem, l => l.ContentType, l => l.ContentType.Name, l => l.ContentType.Id, l => l.ContentType.StringId, l => l.ContentType.Description); + } ClientContext.ExecuteQueryRetry(); WriteObject(listItem); } @@ -83,18 +93,22 @@ protected override void ExecuteCmdlet() viewFieldsStringBuilder.Append(""); } query.ViewXml = $"{UniqueId}{UniqueId}{viewFieldsStringBuilder}"; - + var listItem = list.GetItems(query); ClientContext.Load(listItem); + if (IncludeContentType) + { + ClientContext.Load(listItem, l => l.Include(a => a.ContentType, a => a.ContentType.Id, a => a.ContentType.Name, a => a.ContentType.Description, a => a.ContentType.StringId)); + } ClientContext.ExecuteQueryRetry(); WriteObject(listItem); } else { - CamlQuery query = HasCamlQuery() ? new CamlQuery { ViewXml = Query } : CamlQuery.CreateAllItemsQuery(); + CamlQuery query = HasCamlQuery() ? new CamlQuery { ViewXml = Query } : CamlQuery.CreateAllItemsQuery(); query.FolderServerRelativeUrl = FolderServerRelativeUrl; - if (Fields != null) + if (Fields != null) { var queryElement = XElement.Parse(query.ViewXml); @@ -143,16 +157,20 @@ protected override void ExecuteCmdlet() { var listItems = list.GetItems(query); ClientContext.Load(listItems); + if (IncludeContentType) + { + ClientContext.Load(listItems, l => l.Include(a => a.ContentType, a => a.ContentType.Id, a => a.ContentType.Name, a => a.ContentType.Description, a => a.ContentType.StringId)); + } ClientContext.ExecuteQueryRetry(); WriteObject(listItems, true); if (ScriptBlock != null) { - ScriptBlock.Invoke(listItems); - } + ScriptBlock.Invoke(listItems); + } - query.ListItemCollectionPosition = listItems.ListItemCollectionPosition; + query.ListItemCollectionPosition = listItems.ListItemCollectionPosition; } while (query.ListItemCollectionPosition != null); } } From 9babaa8a0cbd5d6f64300cda48260397d93b4c20 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 31 May 2022 22:56:04 +0200 Subject: [PATCH 278/458] Added PR reference --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfa3757b8..0b157b566 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added capability to Debug the module in Visual Studio. [#1880](https://github.com/pnp/powershell/pull/1880) - Added `Set-PnPTeamsChannelUser` cmdlet to update the role of user in a private channel. [#1865](https://github.com/pnp/powershell/pull/1865) - Added `Restart-PnPFlowRun` which allows for a failed Power Automate flow run to be retried [#1915](https://github.com/pnp/powershell/pull/1915) +- Added optional `-Connection` parameter to `Get-PnPConnection` and `Get-PnPContext` which allows for retrieving both of these for a specific connection [#1919](https://github.com/pnp/powershell/pull/1919) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) @@ -58,8 +59,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Invoke-PnPSPRestMethod` invalid parsing for SharePoint number columns. [#1877](https://github.com/pnp/powershell/pull/1879) - Fix issue with `Add/Set-PnPListItem` not throwing correct exception for invalid taxonomy values. [#1870](https://github.com/pnp/powershell/pull/1870) - Fixed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` throwing an "Object reference not set to an instance of an object" exception when providing an empty users collection or incorrect user mapping [#1896](https://github.com/pnp/powershell/pull/1896) -- Fixed `Connect-PnPOnline -ReturnConnection` also setting the current connection instead of just the returned connection -- Fixed `Disconnect-PnPOnline -Connection` also disconnecting other connections next to the provided connection +- Fixed `Connect-PnPOnline -ReturnConnection` also setting the current connection instead of just the returned connection [#1919](https://github.com/pnp/powershell/pull/1919 +- Fixed `Disconnect-PnPOnline -Connection` also disconnecting other connections next to the provided connection [#1919](https://github.com/pnp/powershell/pull/1919 ### Removed - Removed `Get-PnPAvailableClientSideComponents`. Use `Get-PnPPageComponent -Page -ListAvailable` instead. [#1833](https://github.com/pnp/powershell/pull/1833) From f1b4e6f64e5c9f892ba827a82311871901825c39 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 31 May 2022 22:56:19 +0200 Subject: [PATCH 279/458] Fixed build validation issue --- documentation/Get-PnPConnection.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/documentation/Get-PnPConnection.md b/documentation/Get-PnPConnection.md index c67553c36..1a54d466d 100644 --- a/documentation/Get-PnPConnection.md +++ b/documentation/Get-PnPConnection.md @@ -35,8 +35,6 @@ This will put the current connection for use with the -Connection parameter on o ### -Connection Optional connection to be used by the cmdlet. Retrieve the value for this parameter by specifying -ReturnConnection on Connect-PnPOnline. If not provided, the connection will be retrieved from the current context. -```yaml - ```yaml Type: PnPConnection Parameter Sets: (All) From 99e18703c455643e7ea9921db227615d06b9b775 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 31 May 2022 23:28:53 +0200 Subject: [PATCH 280/458] Fix for issue #1729 --- documentation/Set-PnPContext.md | 21 +++++++++++++++++---- src/Commands/Base/PnPWebRetrievalsCmdlet.cs | 5 +++-- src/Commands/Base/SetContext.cs | 7 +++---- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/documentation/Set-PnPContext.md b/documentation/Set-PnPContext.md index ea5077b4b..0eebc9454 100644 --- a/documentation/Set-PnPContext.md +++ b/documentation/Set-PnPContext.md @@ -15,11 +15,11 @@ Set the ClientContext ## SYNTAX ```powershell -Set-PnPContext [-Context] [] +Set-PnPContext -Context [-Connection ] [] ``` ## DESCRIPTION -Sets the Client Context to use by the cmdlets, which allows easy context switching. See examples for details. +Sets the Client Context to be used by the cmdlets, which allows easy context switching. See examples for details. ## EXAMPLES @@ -50,7 +50,20 @@ Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` -## RELATED LINKS +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by specifying -ReturnConnection on Connect-PnPOnline. If not provided, the connection will be retrieved from the current context. -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/src/Commands/Base/PnPWebRetrievalsCmdlet.cs b/src/Commands/Base/PnPWebRetrievalsCmdlet.cs index 7b7e8e50d..1bd16968a 100644 --- a/src/Commands/Base/PnPWebRetrievalsCmdlet.cs +++ b/src/Commands/Base/PnPWebRetrievalsCmdlet.cs @@ -1,5 +1,4 @@ using System; -using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Base.PipeBinds; using System.Management.Automation; using Microsoft.SharePoint.Client; @@ -45,9 +44,11 @@ private Web GetWeb() #pragma warning restore CS0618 else { + // Validate that our ClientContext and PnPConnection are both for the same site if (Connection.Context.Url != Connection.Url) { - Connection.RestoreCachedContext(Connection.Url); + // ClientContext is for a different site than our PnPConnection, try to make the connection match the ClientContext URL + Connection.RestoreCachedContext(Connection.Context.Url); } web = ClientContext.Web; } diff --git a/src/Commands/Base/SetContext.cs b/src/Commands/Base/SetContext.cs index 7f9c03758..4ba675848 100644 --- a/src/Commands/Base/SetContext.cs +++ b/src/Commands/Base/SetContext.cs @@ -1,18 +1,17 @@ using System.Management.Automation; - using Microsoft.SharePoint.Client; namespace PnP.PowerShell.Commands.Base { [Cmdlet(VerbsCommon.Set, "PnPContext")] - public class SetContext : PSCmdlet + public class SetContext : PnPSharePointCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 1)] public ClientContext Context; protected override void ProcessRecord() { - PnPConnection.Current.Context = Context; + Connection.Context = Context; } } -} +} \ No newline at end of file From b74aad7b7a3c9bb58ab2e5243c6a3000e3c9fcb0 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 31 May 2022 23:31:24 +0200 Subject: [PATCH 281/458] Added changelog entry for the fix for issue #1729 --- CHANGELOG.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcf4a87f7..237548855 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,7 +36,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added capability to Debug the module in Visual Studio. [#1880](https://github.com/pnp/powershell/pull/1880) - Added `Set-PnPTeamsChannelUser` cmdlet to update the role of user in a private channel. [#1865](https://github.com/pnp/powershell/pull/1865) - Added `Restart-PnPFlowRun` which allows for a failed Power Automate flow run to be retried [#1915](https://github.com/pnp/powershell/pull/1915) -- Added optional `-Connection` parameter to `Get-PnPConnection` and `Get-PnPContext` which allows for retrieving both of these for a specific connection [#1919](https://github.com/pnp/powershell/pull/1919) +- Added optional `-Connection` parameter to `Get-PnPConnection`, `Get-PnPContext` and `Set-PnPContext` which allows for retrieving both of these for a specific connection [#1919](https://github.com/pnp/powershell/pull/1919) - Added `-IncludeDeprecated` parameter to `Get-PnPTerm` cmdlet to fetch deprecated terms if specified [#1903](https://github.com/pnp/powershell/pull/1903) ### Changed @@ -60,8 +60,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Invoke-PnPSPRestMethod` invalid parsing for SharePoint number columns. [#1877](https://github.com/pnp/powershell/pull/1879) - Fix issue with `Add/Set-PnPListItem` not throwing correct exception for invalid taxonomy values. [#1870](https://github.com/pnp/powershell/pull/1870) - Fixed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` throwing an "Object reference not set to an instance of an object" exception when providing an empty users collection or incorrect user mapping [#1896](https://github.com/pnp/powershell/pull/1896) -- Fixed `Connect-PnPOnline -ReturnConnection` also setting the current connection instead of just the returned connection [#1919](https://github.com/pnp/powershell/pull/1919 -- Fixed `Disconnect-PnPOnline -Connection` also disconnecting other connections next to the provided connection [#1919](https://github.com/pnp/powershell/pull/1919 +- Fixed `Connect-PnPOnline -ReturnConnection` also setting the current connection instead of just the returned connection [#1919](https://github.com/pnp/powershell/pull/1919) +- Fixed `Disconnect-PnPOnline -Connection` also disconnecting other connections next to the provided connection [#1919](https://github.com/pnp/powershell/pull/1919) +- Fixed `Set-PnPContext` not properly applying the provided context [#1919](https://github.com/pnp/powershell/pull/1919) ### Removed - Removed `Get-PnPAvailableClientSideComponents`. Use `Get-PnPPageComponent -Page -ListAvailable` instead. [#1833](https://github.com/pnp/powershell/pull/1833) From db3e75584287ba8faab989fa4342d03a447d73d5 Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Wed, 1 Jun 2022 00:06:29 +0200 Subject: [PATCH 282/458] Maintain orderhint --- src/Commands/Utilities/PlannerUtility.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Commands/Utilities/PlannerUtility.cs b/src/Commands/Utilities/PlannerUtility.cs index 3af34d835..e0d8d4f6e 100644 --- a/src/Commands/Utilities/PlannerUtility.cs +++ b/src/Commands/Utilities/PlannerUtility.cs @@ -401,8 +401,7 @@ private static async Task ResolveGroupName(HttpClient httpClient, string public static async Task> GetBucketsAsync(HttpClient httpClient, string accessToken, string planId) { - var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/planner/plans/{planId}/buckets", accessToken); - return collection.OrderBy(p => p.OrderHint); + return await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/planner/plans/{planId}/buckets", accessToken); } public static async Task CreateBucketAsync(HttpClient httpClient, string accessToken, string name, string planId) From 046de310b7f984acb82e665644a9e384163bcb06 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 1 Jun 2022 01:18:03 +0200 Subject: [PATCH 283/458] Added changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5932c10a..9b6ab1528 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Bumped .NET Framework version to 4.6.2 as the 4.6.1 is not supported anymore. [#1856](https://github.com/pnp/powershell/pull/1856) - Changed `Add-PnPDataRowsToSiteTemplate`, it will now export a datetime field value as UTC string. [#1900](https://github.com/pnp/powershell/pull/1900) - The cmdlets `Remove-PnPFile`, `Remove-PnPFolder`, `Move-PnPListItemToRecycleBin`, `Remove-PnPList`, `Remove-PnPListItem` and `Remove-PnPPage` will now return the corresponding recycle bin item if they get deleted to the recycle bin. Before they would not return anything. [#1783](https://github.com/pnp/powershell/pull/1783) +- Changed `Get-PnPPlannerBucket` to return the buckets in the correct (reversed) order as you see them through the web interface [#1922](https://github.com/pnp/powershell/pull/1922) ### Fixed From 0152ab1ac8a4ea8351f960f631a5f4fcb1009460 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 1 Jun 2022 04:18:55 +0000 Subject: [PATCH 284/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index 6d1f5686a..93c4a0f41 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -1a2db2bfc90f91d5c429d1f3a7277e6fa2b50090 \ No newline at end of file +d41d1524c064f510c59a7c18fd7c6c7b111d77a6 \ No newline at end of file diff --git a/version.txt b/version.txt index 132b45dbf..019826c27 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.34 \ No newline at end of file +1.10.35 \ No newline at end of file From cafeb0a4165ce06ebeab56ca8f610fa8014921fd Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 1 Jun 2022 09:01:21 +0200 Subject: [PATCH 285/458] Update CHANGELOG.md Wording fix --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 237548855..61a4680db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,7 +36,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added capability to Debug the module in Visual Studio. [#1880](https://github.com/pnp/powershell/pull/1880) - Added `Set-PnPTeamsChannelUser` cmdlet to update the role of user in a private channel. [#1865](https://github.com/pnp/powershell/pull/1865) - Added `Restart-PnPFlowRun` which allows for a failed Power Automate flow run to be retried [#1915](https://github.com/pnp/powershell/pull/1915) -- Added optional `-Connection` parameter to `Get-PnPConnection`, `Get-PnPContext` and `Set-PnPContext` which allows for retrieving both of these for a specific connection [#1919](https://github.com/pnp/powershell/pull/1919) +- Added optional `-Connection` parameter to `Get-PnPConnection`, `Get-PnPContext` and `Set-PnPContext` which allows for using any of these for a specific connection [#1919](https://github.com/pnp/powershell/pull/1919) - Added `-IncludeDeprecated` parameter to `Get-PnPTerm` cmdlet to fetch deprecated terms if specified [#1903](https://github.com/pnp/powershell/pull/1903) ### Changed From d9b85853a675c020f9452a66985c6d27c9a7262d Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 1 Jun 2022 09:19:56 +0200 Subject: [PATCH 286/458] Some cleanup and adding comments --- ...ssions.md => Get-PnPListItemPermission.md} | 38 +++++++++---------- src/Commands/Lists/GetListItemPermission.cs | 7 ++-- .../Model/ListITemPermissionCollection.cs | 20 ++++++++++ src/Commands/Model/ListItemPermission.cs | 32 ++++++++++++++++ src/Commands/Model/ListItemPermissions.cs | 25 ------------ 5 files changed, 74 insertions(+), 48 deletions(-) rename documentation/{Get-PnPListItemPermissions.md => Get-PnPListItemPermission.md} (83%) create mode 100644 src/Commands/Model/ListITemPermissionCollection.cs create mode 100644 src/Commands/Model/ListItemPermission.cs delete mode 100644 src/Commands/Model/ListItemPermissions.cs diff --git a/documentation/Get-PnPListItemPermissions.md b/documentation/Get-PnPListItemPermission.md similarity index 83% rename from documentation/Get-PnPListItemPermissions.md rename to documentation/Get-PnPListItemPermission.md index 0650f3c0e..b697b97ea 100644 --- a/documentation/Get-PnPListItemPermissions.md +++ b/documentation/Get-PnPListItemPermission.md @@ -1,10 +1,10 @@ --- Module Name: PnP.PowerShell -title: Get-PnPListItemPermissions +title: Get-PnPListItemPermission schema: 2.0.0 applicable: SharePoint Online external help file: PnP.PowerShell.dll-Help.xml -online version: https://pnp.github.io/powershell/cmdlets/Get-PnPListItemPermissions.html +online version: https://pnp.github.io/powershell/cmdlets/Get-PnPListItemPermission.html --- # Get-PnPListItemPermission @@ -16,7 +16,7 @@ Gets list item permissions. ```powershell -Get-PnPListItemPermissions [-List] -Identity +Get-PnPListItemPermission [-List] -Identity [-Connection ] [] ``` @@ -27,27 +27,13 @@ Get-PnPListItemPermissions [-List] -Identity ### EXAMPLE 1 ```powershell -Get-PnPListItemPermissions -List 'Documents' -Identity 1 +Get-PnPListItemPermission -List 'Documents' -Identity 1 ``` Get the permissions for listitem with id 1 in the list 'Documents' ## PARAMETERS -### -Connection -Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. - -```yaml -Type: PnPConnection -Parameter Sets: (All) - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - ### -Identity The ID of the listitem, or actual ListItem object @@ -76,6 +62,20 @@ Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ## RELATED LINKS -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/src/Commands/Lists/GetListItemPermission.cs b/src/Commands/Lists/GetListItemPermission.cs index 58dba1ff4..d6883e8bb 100644 --- a/src/Commands/Lists/GetListItemPermission.cs +++ b/src/Commands/Lists/GetListItemPermission.cs @@ -1,13 +1,12 @@ using System.Collections.Generic; using System.Management.Automation; using Microsoft.SharePoint.Client; -using PnP.Core.QueryModel; using PnP.PowerShell.Commands.Base.PipeBinds; using PnP.PowerShell.Commands.Model; namespace PnP.PowerShell.Commands.Lists { - [Cmdlet(VerbsCommon.Get, "PnPListItemPermissions")] + [Cmdlet(VerbsCommon.Get, "PnPListItemPermission")] public class GetListItemPermission : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, ParameterSetName = ParameterAttribute.AllParameterSets)] @@ -35,7 +34,7 @@ protected override void ExecuteCmdlet() roleDef => roleDef.Description, roleDef => roleDef.Id, roleDef => roleDef.RoleTypeKind)), a => a.HasUniqueRoleAssignments); ClientContext.ExecuteQueryRetry(); - var listItemPermissions = new List(); + var listItemPermissions = new List(); var listItemPermissionCollection = new ListItemPermissionCollection { HasUniqueRoleAssignments = item.HasUniqueRoleAssignments @@ -43,7 +42,7 @@ protected override void ExecuteCmdlet() foreach (var roleAssignment in item.RoleAssignments) { - var listItemPermission = new Permissions + var listItemPermission = new ListItemPermission { PrincipalName = roleAssignment.Member.LoginName, PrincipalType = roleAssignment.Member.PrincipalType, diff --git a/src/Commands/Model/ListITemPermissionCollection.cs b/src/Commands/Model/ListITemPermissionCollection.cs new file mode 100644 index 000000000..16c9fe2fc --- /dev/null +++ b/src/Commands/Model/ListITemPermissionCollection.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +namespace PnP.PowerShell.Commands.Model +{ + /// + /// Model to contain a collection of list item permissions + /// + public class ListItemPermissionCollection + { + /// + /// Boolean indicating if the list item has unique item level permissions (true) or if it inherits from its parent (false) + /// + public bool? HasUniqueRoleAssignments { get; set; } + + /// + /// Permissions that are set for the list item + /// + public List Permissions { get; set; } + } +} \ No newline at end of file diff --git a/src/Commands/Model/ListItemPermission.cs b/src/Commands/Model/ListItemPermission.cs new file mode 100644 index 000000000..84c859ae1 --- /dev/null +++ b/src/Commands/Model/ListItemPermission.cs @@ -0,0 +1,32 @@ +using Microsoft.SharePoint.Client; +using Microsoft.SharePoint.Client.Utilities; +using System.Collections.Generic; + +namespace PnP.PowerShell.Commands.Model +{ + /// + /// Model to contain one list item level permission + /// + public class ListItemPermission + { + /// + /// Permission levels set for this list item + /// + public List PermissionLevels { get; set; } + + /// + /// Name of the principical that has been granted permissions + /// + public string PrincipalName { get; set; } + + /// + /// Type of principal that has been granted permissions + /// + public PrincipalType? PrincipalType { get; set; } + + /// + /// Id of the principal on the list item + /// + public int? PrincipalId { get; set; } + } +} diff --git a/src/Commands/Model/ListItemPermissions.cs b/src/Commands/Model/ListItemPermissions.cs deleted file mode 100644 index e26eb25f4..000000000 --- a/src/Commands/Model/ListItemPermissions.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.SharePoint.Client; -using Microsoft.SharePoint.Client.Utilities; -using System; -using System.Collections.Generic; - -namespace PnP.PowerShell.Commands.Model -{ - public class ListItemPermissionCollection - { - public bool HasUniqueRoleAssignments { get; set; } - - public List Permissions { get; set; } - } - - public class Permissions - { - public List PermissionLevels { get; set; } - - public string PrincipalName { get; set; } - - public PrincipalType PrincipalType { get; set; } - - public int PrincipalId { get; set; } - } -} From 788f453aa2594bf1e07da44cca034d782f675df8 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 1 Jun 2022 09:20:51 +0200 Subject: [PATCH 287/458] Changed cmdlet name to singular --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d24a973a..fc99dd748 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `TimeZone` parameter to `New-PnPSite` cmdlet which allows setting of the site collection in the specified timezone. - Added `Stop-PnPFlowRun` cmdlet to stop/cancel a specific Power Automate flow run. [#1838](https://github.com/pnp/powershell/pull/1838) - Added `Remove-PnPTeamsChannelUser` cmdlet to remove a user from a private channel. [#1840](https://github.com/pnp/powershell/pull/1840) -- Added `Get-PnPListItemPermissions` cmdlet to retrieve item level permissions. [#1534](https://github.com/pnp/powershell/pull/1534) +- Added `Get-PnPListItemPermission` cmdlet to retrieve item level permissions. [#1534](https://github.com/pnp/powershell/pull/1534) - Added `Get-PnPTeamsChannelMessageReply` to retrieve all replies or a specific reply of a message in a Teams channel [#1885](https://github.com/pnp/powershell/pull/1885) - Added `-Identity` parameter to `Get-PnPTeamsChannelMessage` cmdlet to retrieve a specific message [#1887](https://github.com/pnp/powershell/pull/1887) - Added new `PnP.PowerShell` image which also gets published to Docker Hub. [#1580](https://github.com/pnp/powershell/pull/1794) From cf9d6a1683e1841c5cf6e0bb901a8be04a1beb64 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 1 Jun 2022 09:43:50 +0200 Subject: [PATCH 288/458] Added `-ValidateConnection` option to `Connect-PnPOnline` (#1924) * Added ValidateConnection option to Connect-PnPOnline * Added verbose logging * Added PR reference --- CHANGELOG.md | 1 + documentation/Connect-PnPOnline.md | 44 ++++++++++++++++++++++++------ src/Commands/Base/ConnectOnline.cs | 34 +++++++++++++++++++---- 3 files changed, 66 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b6ab1528..8f5242ec8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Set-PnPTeamsChannelUser` cmdlet to update the role of user in a private channel. [#1865](https://github.com/pnp/powershell/pull/1865) - Added `Restart-PnPFlowRun` which allows for a failed Power Automate flow run to be retried [#1915](https://github.com/pnp/powershell/pull/1915) - Added `-IncludeDeprecated` parameter to `Get-PnPTerm` cmdlet to fetch deprecated terms if specified [#1903](https://github.com/pnp/powershell/pull/1903) +- Added optional `-ValidateConnection` to `Connect-PnPOnline` which will check if the site you are connecting to exists and if not, will throw an exception [#1924](https://github.com/pnp/powershell/pull/1924) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) diff --git a/documentation/Connect-PnPOnline.md b/documentation/Connect-PnPOnline.md index 1e159ef68..361dab93b 100644 --- a/documentation/Connect-PnPOnline.md +++ b/documentation/Connect-PnPOnline.md @@ -17,14 +17,14 @@ Connect to a SharePoint site Connect-PnPOnline [-ReturnConnection] [-Url] [-Credentials ] [-CurrentCredentials] [-CreateDrive] [-DriveName ] [-ClientId ] [-RedirectUri ] [-AzureEnvironment ] [-TenantAdminUrl ] - [-TransformationOnPrem] [] + [-TransformationOnPrem] [-ValidateConnection] [] ``` ### SharePoint ACS (Legacy) App Only ``` Connect-PnPOnline [-ReturnConnection] [-Url] [-Realm ] -ClientSecret [-CreateDrive] [-DriveName ] -ClientId [-AzureEnvironment ] [-TenantAdminUrl ] - [] + [-ValidateConnection] [] ``` ### App-Only with Azure Active Directory @@ -32,34 +32,35 @@ Connect-PnPOnline [-ReturnConnection] [-Url] [-Realm ] -ClientS Connect-PnPOnline [-ReturnConnection] [-Url] [-CreateDrive] [-DriveName ] -ClientId -Tenant [-CertificatePath ] [-CertificateBase64Encoded ] [-CertificatePassword ] [-AzureEnvironment ] [-TenantAdminUrl ] - [] + [-ValidateConnection] [] ``` ### App-Only with Azure Active Directory using a certificate from the Windows Certificate Management Store by thumbprint ``` Connect-PnPOnline [-ReturnConnection] [-Url] [-CreateDrive] [-DriveName ] -ClientId -Tenant -Thumbprint [-AzureEnvironment ] [-TenantAdminUrl ] - [] + [-ValidateConnection] [] ``` ### PnP Management Shell / DeviceLogin ``` Connect-PnPOnline [-ReturnConnection] [-Url] [-CreateDrive] [-DriveName ] [-DeviceLogin] [-LaunchBrowser] [-ClientId ] [-AzureEnvironment ] - [] + [-ValidateConnection] [] ``` ### Web Login for Multi Factor Authentication ``` Connect-PnPOnline [-ReturnConnection] [-Url] [-CreateDrive] [-DriveName ] [-TenantAdminUrl ] [-UseWebLogin] [-ForceAuthentication] - [] + [-ValidateConnection] [] ``` ### Interactive for Multi Factor Authentication ``` Connect-PnPOnline -Interactive [-ReturnConnection] -Url [-CreateDrive] [-DriveName ] [-LaunchBrowser] - [-ClientId ] [-AzureEnvironment ] [-TenantAdminUrl ] [-ForceAuthentication] [] + [-ClientId ] [-AzureEnvironment ] [-TenantAdminUrl ] [-ForceAuthentication] [-ValidateConnection] + [] ``` ### On-premises login for page transformation from on-premises SharePoint to SharePoint Online @@ -517,6 +518,20 @@ Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` +### -ValidateConnection +When provided, the cmdlet will check to ensure the SharePoint Online site specified through `-Url` exists and if not, will throw an exception. If you omit this flag or set it to $false, it will blindly set up a connection without validating that the site actually exists. Making use of this option does make one extra call on the connection attempt, so it is recommended to only use it in scenarios where you know the site you're trying to connect o may not exist and would like to have feedback on this during the connect. + +```yaml +Type: SwitchParameter +Parameter Sets: Credentials, SharePoint ACS (Legacy) App Only, App-Only with Azure Active Directory, App-Only with Azure Active Directory using a certificate from the Windows Certificate Management Store by thumbprint, SPO Management Shell Credentials, PnP Management Shell / DeviceLogin, Web Login for Multi Factor Authentication, Interactive for Multi Factor Authentication, Access Token +Aliases: + +Required: False +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -TransformationOnPrem If you want to the use page transformation cmdlets, setting this switch will allow you to connect to an on-prem server. Notice that this -only- applies to Transformation cmdlets. @@ -617,7 +632,20 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Verbose +When provided, additional debug statements will be shown while going through setting up a connection. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` ## RELATED LINKS -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/src/Commands/Base/ConnectOnline.cs b/src/Commands/Base/ConnectOnline.cs index ad43c8597..749023004 100644 --- a/src/Commands/Base/ConnectOnline.cs +++ b/src/Commands/Base/ConnectOnline.cs @@ -37,7 +37,6 @@ public class ConnectOnline : BasePSCmdlet private const string SPOManagementClientId = "9bc3ab49-b65d-410a-85ad-de819febfddc"; private const string SPOManagementRedirectUri = "https://oauth.spops.microsoft.com/"; - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_CREDENTIALS, ValueFromPipeline = true)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_ACSAPPONLY, ValueFromPipeline = true)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_APPONLYAADCERTIFICATE, ValueFromPipeline = true)] @@ -49,6 +48,17 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_ACCESSTOKEN, ValueFromPipeline = true)] public SwitchParameter ReturnConnection; + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_CREDENTIALS, ValueFromPipeline = true)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_ACSAPPONLY, ValueFromPipeline = true)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_APPONLYAADCERTIFICATE, ValueFromPipeline = true)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_APPONLYAADTHUMBPRINT, ValueFromPipeline = true)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_SPOMANAGEMENT, ValueFromPipeline = true)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_DEVICELOGIN, ValueFromPipeline = true)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_WEBLOGIN, ValueFromPipeline = true)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_INTERACTIVE, ValueFromPipeline = true)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_ACCESSTOKEN, ValueFromPipeline = true)] + public SwitchParameter ValidateConnection; + [Parameter(Mandatory = true, Position = 0, ParameterSetName = ParameterSet_CREDENTIALS, ValueFromPipeline = true)] [Parameter(Mandatory = true, Position = 0, ParameterSetName = ParameterSet_ACSAPPONLY, ValueFromPipeline = true)] [Parameter(Mandatory = true, Position = 0, ParameterSetName = ParameterSet_APPONLYAADCERTIFICATE, ValueFromPipeline = true)] @@ -202,8 +212,6 @@ protected override void ProcessRecord() } #pragma warning restore CS6018 - - VersionChecker.CheckVersion(this); try { @@ -221,7 +229,6 @@ protected override void ProcessRecord() /// protected void Connect(ref CancellationToken cancellationToken) { - if (!string.IsNullOrEmpty(Url) && Url.EndsWith("/")) { Url = Url.TrimEnd('/'); @@ -311,11 +318,28 @@ protected void Connect(ref CancellationToken cancellationToken) Environment.SetEnvironmentVariable("PNPPSSITE", "GRAPH"); } + if (ValidateConnection) + { + // Try requesting the site Id to validate that the site to which is being connected exists + WriteVerbose($"Validating if the site at {Url} exists"); + connection.Context.Load(connection.Context.Site, p => p.Id); + + try + { + connection.Context.ExecuteQueryRetry(); + WriteVerbose($"Site at {Url} exists"); + } + catch(System.Net.WebException e) when (e.Message.Contains("404")) + { + WriteVerbose($"Site at {Url} does not exist"); + throw new PSInvalidOperationException($"The specified site {Url} does not exist", e); + } + } + if (ReturnConnection) { WriteObject(connection); } - } #region Connect Types From 6e22c842eadf6e12f4dfe5d9f95951c584a3b4ae Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 1 Jun 2022 09:59:48 +0200 Subject: [PATCH 289/458] Added error output handling for Graph calls (#1923) * Added error output handling for Graph calls * Added PR reference Co-authored-by: Gautam Sheth --- CHANGELOG.md | 1 + src/Commands/Graph/InvokeGraphMethod.cs | 27 +-------------- src/Commands/Teams/CopyTeamsTeam.cs | 16 +-------- src/Commands/Utilities/REST/GraphHelper.cs | 40 ++++++++++------------ src/Commands/Utilities/TeamsUtility.cs | 15 ++------ 5 files changed, 23 insertions(+), 76 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f5242ec8..7939814d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Bumped .NET Framework version to 4.6.2 as the 4.6.1 is not supported anymore. [#1856](https://github.com/pnp/powershell/pull/1856) - Changed `Add-PnPDataRowsToSiteTemplate`, it will now export a datetime field value as UTC string. [#1900](https://github.com/pnp/powershell/pull/1900) - The cmdlets `Remove-PnPFile`, `Remove-PnPFolder`, `Move-PnPListItemToRecycleBin`, `Remove-PnPList`, `Remove-PnPListItem` and `Remove-PnPPage` will now return the corresponding recycle bin item if they get deleted to the recycle bin. Before they would not return anything. [#1783](https://github.com/pnp/powershell/pull/1783) +- Cmdlets backed by a Microsoft Graph call will now return detailed information when the Graph call fails [#1923](https://github.com/pnp/powershell/pull/1923) - Changed `Get-PnPPlannerBucket` to return the buckets in the correct (reversed) order as you see them through the web interface [#1922](https://github.com/pnp/powershell/pull/1922) ### Fixed diff --git a/src/Commands/Graph/InvokeGraphMethod.cs b/src/Commands/Graph/InvokeGraphMethod.cs index 5429a421c..d82781e12 100644 --- a/src/Commands/Graph/InvokeGraphMethod.cs +++ b/src/Commands/Graph/InvokeGraphMethod.cs @@ -1,22 +1,12 @@ - -using Newtonsoft.Json.Linq; -using PnP.Framework.Utilities; -using PnP.PowerShell.Commands.Attributes; -using PnP.PowerShell.Commands.Base; -using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.Framework.Utilities; using PnP.PowerShell.Commands.Enums; -using PnP.PowerShell.Commands.Utilities; using PnP.PowerShell.Commands.Utilities.REST; -using PnP.PowerShell.Commands.Utilities.JSON; using System; -using System.Linq; using System.Management.Automation; using System.Text.Json; -using System.Collections; using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Headers; -using PnP.PowerShell.Commands.Model.Graph; namespace PnP.PowerShell.Commands.Base { @@ -160,17 +150,6 @@ private void SendRequest() } } - private void ThrowIfNoSuccess(HttpResponseMessage response) - { - if (!response.IsSuccessStatusCode) - { - var errorContent = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); - var exception = JsonSerializer.Deserialize(errorContent, new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); - exception.AccessToken = AccessToken; - throw exception; - } - } - private object Deserialize(string result) { var element = JsonSerializer.Deserialize(result); @@ -250,7 +229,6 @@ private void GetRequest() private void PostRequest() { var response = GraphHelper.PostAsync(HttpClient, Url, AccessToken, GetHttpContent(), AdditionalHeaders).GetAwaiter().GetResult(); - ThrowIfNoSuccess(response); var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); WriteGraphResult(result); } @@ -258,7 +236,6 @@ private void PostRequest() private void PutRequest() { var response = GraphHelper.PutAsync(HttpClient, Url, AccessToken, GetHttpContent(), AdditionalHeaders).GetAwaiter().GetResult(); - ThrowIfNoSuccess(response); var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); WriteGraphResult(result); } @@ -266,7 +243,6 @@ private void PutRequest() private void PatchRequest() { var response = GraphHelper.PatchAsync(HttpClient, AccessToken, GetHttpContent(), Url, AdditionalHeaders).GetAwaiter().GetResult(); - ThrowIfNoSuccess(response); var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); WriteGraphResult(result); } @@ -274,7 +250,6 @@ private void PatchRequest() private void DeleteRequest() { var response = GraphHelper.DeleteAsync(HttpClient, Url, AccessToken, AdditionalHeaders).GetAwaiter().GetResult(); - ThrowIfNoSuccess(response); var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); WriteGraphResult(result); } diff --git a/src/Commands/Teams/CopyTeamsTeam.cs b/src/Commands/Teams/CopyTeamsTeam.cs index ce2830066..48be4d2bf 100644 --- a/src/Commands/Teams/CopyTeamsTeam.cs +++ b/src/Commands/Teams/CopyTeamsTeam.cs @@ -70,21 +70,7 @@ protected override void ExecuteCmdlet() * but currently ignored and can't be set by user */ teamClone.MailNickName = DisplayName; teamClone.Visibility = (GroupVisibility)Enum.Parse(typeof(GroupVisibility), Visibility.ToString()); - var response = TeamsUtility.CloneTeamAsync(AccessToken, HttpClient, groupId, teamClone).GetAwaiter().GetResult(); - if (!response.IsSuccessStatusCode) - { - if (GraphHelper.TryGetGraphException(response, out GraphException ex)) - { - if (ex.Error != null) - { - throw new PSInvalidOperationException(ex.Error.Message); - } - } - else - { - WriteError(new ErrorRecord(new Exception($"Team clone failed"), "CLONEFAILED", ErrorCategory.InvalidResult, this)); - } - } + TeamsUtility.CloneTeamAsync(AccessToken, HttpClient, groupId, teamClone).GetAwaiter().GetResult(); } } } diff --git a/src/Commands/Utilities/REST/GraphHelper.cs b/src/Commands/Utilities/REST/GraphHelper.cs index 500246f48..4a37d1a93 100644 --- a/src/Commands/Utilities/REST/GraphHelper.cs +++ b/src/Commands/Utilities/REST/GraphHelper.cs @@ -1,14 +1,13 @@ using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Model.Graph; -using PnP.PowerShell.Commands.Model.Teams; using System; using System.Collections.Generic; using System.Linq; +using System.Management.Automation; using System.Net; using System.Net.Http; using System.Text.Json; using System.Text.Json.Serialization; -using System.Threading; using System.Threading.Tasks; namespace PnP.PowerShell.Commands.Utilities.REST @@ -249,26 +248,8 @@ public static async Task PatchAsync(HttpClient httpClient, return await GetResponseMessageAsync(httpClient, message); } - - - // public static async Task PatchAsync(HttpClient httpClient, string accessToken, string url, T content,IDictionary additionalHeaders = null) - // { - // var requestContent = new StringContent(JsonSerializer.Serialize(content, new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase })); - // requestContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - // var message = GetMessage(url, new HttpMethod("PATCH"), accessToken, requestContent, additionalHeaders); - // var returnValue = await SendMessageAsync(httpClient, message); - // if (!string.IsNullOrEmpty(returnValue)) - // { - // return JsonSerializer.Deserialize(returnValue, new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); - // } - // else - // { - // return default; - // } - // } #endregion - public static async Task PostAsync(HttpClient httpClient, string url, HttpContent content, string accessToken, IDictionary additionalHeaders = null, bool propertyNameCaseInsensitive = false) { return await PostInternalAsync(httpClient, url, accessToken, content, additionalHeaders, propertyNameCaseInsensitive); @@ -369,8 +350,6 @@ private static async Task SendMessageAsync(HttpClient httpClient, HttpRe } } - - public static async Task GetResponseMessageAsync(HttpClient httpClient, HttpRequestMessage message) { var response = await httpClient.SendAsync(message); @@ -381,6 +360,23 @@ public static async Task GetResponseMessageAsync(HttpClient await Task.Delay(retryAfter.Delta.Value.Seconds * 1000); response = await httpClient.SendAsync(CloneMessage(message)); } + + // Validate if the response was successful, if not throw an exception + if (!response.IsSuccessStatusCode) + { + if (GraphHelper.TryGetGraphException(response, out GraphException ex)) + { + if (ex.Error != null) + { + throw new PSInvalidOperationException(ex.Error.Message); + } + } + else + { + throw new PSInvalidOperationException($"Call to Microsoft Graph URL {message.RequestUri} failed with status code {response.StatusCode}"); + } + } + return response; } diff --git a/src/Commands/Utilities/TeamsUtility.cs b/src/Commands/Utilities/TeamsUtility.cs index 04467a9e3..ced2b994d 100644 --- a/src/Commands/Utilities/TeamsUtility.cs +++ b/src/Commands/Utilities/TeamsUtility.cs @@ -913,19 +913,8 @@ public static async Task AddAppAsync(HttpClient httpClient, string acce var byteArrayContent = new ByteArrayContent(bytes); byteArrayContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/zip"); var response = await GraphHelper.PostAsync(httpClient, "v1.0/appCatalogs/teamsApps", accessToken, byteArrayContent); - if (!response.IsSuccessStatusCode) - { - if (GraphHelper.TryGetGraphException(response, out GraphException exception)) - { - throw exception; - } - } - else - { - var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); - return JsonSerializer.Deserialize(content, new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); - } - return null; + var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + return JsonSerializer.Deserialize(content, new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); } public static async Task UpdateAppAsync(HttpClient httpClient, string accessToken, byte[] bytes, string appId) From 995cc6516f133aaf95b0aae80ddd24e40b0a81ee Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Thu, 2 Jun 2022 04:01:22 +0000 Subject: [PATCH 290/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index 93c4a0f41..05c7cb7a6 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -d41d1524c064f510c59a7c18fd7c6c7b111d77a6 \ No newline at end of file +70ff8d3bb385bd392f7a5a958e0f82a95bed96fb \ No newline at end of file diff --git a/version.txt b/version.txt index 019826c27..c5f06fe2b 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.35 \ No newline at end of file +1.10.36 \ No newline at end of file From fa15d3934edd300ed7a31cca72cf92ff2cc4d9ba Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Thu, 2 Jun 2022 20:06:16 +0200 Subject: [PATCH 291/458] Removed the NextLink from the model as it wasn't used anyway and caused confusion (#1930) Co-authored-by: = <=> --- src/Commands/Model/AzureAD/UserDelta.cs | 5 ----- src/Commands/Utilities/AzureAdUtility.cs | 1 - 2 files changed, 6 deletions(-) diff --git a/src/Commands/Model/AzureAD/UserDelta.cs b/src/Commands/Model/AzureAD/UserDelta.cs index 1557ba32d..f2bfea720 100644 --- a/src/Commands/Model/AzureAD/UserDelta.cs +++ b/src/Commands/Model/AzureAD/UserDelta.cs @@ -16,10 +16,5 @@ public class UserDelta /// The DeltaToken which can be used when querying for changes to request changes made to User objects since this DeltaToken has been given out /// public string DeltaToken { get; set; } - - /// - /// The NextLink which indicates there are more results - /// - public string NextLink { get; set; } } } diff --git a/src/Commands/Utilities/AzureAdUtility.cs b/src/Commands/Utilities/AzureAdUtility.cs index 4bef387dd..20c31b769 100644 --- a/src/Commands/Utilities/AzureAdUtility.cs +++ b/src/Commands/Utilities/AzureAdUtility.cs @@ -31,7 +31,6 @@ public static UserDelta ListUserDelta(string accessToken, string deltaToken, str var result = new UserDelta { DeltaToken = userDelta.DeltaToken, - NextLink = userDelta.NextLink, Users = userDelta.Users.Select(User.CreateFrom).ToList() }; return result; From e8959546a004e5b2fd33b83430cc789cb3dcc15e Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 2 Jun 2022 21:08:27 +0300 Subject: [PATCH 292/458] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ca0364e7..3303bb880 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Removed - Removed `Get-PnPAvailableClientSideComponents`. Use `Get-PnPPageComponent -Page -ListAvailable` instead. [#1833](https://github.com/pnp/powershell/pull/1833) +- Removed `NextLink` property from `Get-PnPAzureADUser` cmdlet, as it was causing confusion. [#1930](https://github.com/pnp/powershell/pull/1930) ### Contributors @@ -828,4 +829,4 @@ First released version of PnP PowerShell - Koen Zomers [koenzomers] - Carlos Marins Jr [kadu-jr] - Aimery Thomas [a1mery] -- Veronique Lengelle [veronicageek] \ No newline at end of file +- Veronique Lengelle [veronicageek] From 7d2805f64054e879c5d1547344f37f9c09c37898 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 2 Jun 2022 23:14:43 +0300 Subject: [PATCH 293/458] Feature: Add/Remove list item attachments --- CHANGELOG.md | 2 + documentation/Add-PnPListItemAttachment.md | 175 ++++++++++++++++++ documentation/Remove-PnPListItemAttachment.md | 168 +++++++++++++++++ src/Commands/Lists/AddListItemAttachment.cs | 112 +++++++++++ .../Lists/RemoveListItemAttachment.cs | 91 +++++++++ 5 files changed, 548 insertions(+) create mode 100644 documentation/Add-PnPListItemAttachment.md create mode 100644 documentation/Remove-PnPListItemAttachment.md create mode 100644 src/Commands/Lists/AddListItemAttachment.cs create mode 100644 src/Commands/Lists/RemoveListItemAttachment.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 3303bb880..da923efd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Restart-PnPFlowRun` which allows for a failed Power Automate flow run to be retried [#1915](https://github.com/pnp/powershell/pull/1915) - Added `-IncludeDeprecated` parameter to `Get-PnPTerm` cmdlet to fetch deprecated terms if specified [#1903](https://github.com/pnp/powershell/pull/1903) - Added optional `-ValidateConnection` to `Connect-PnPOnline` which will check if the site you are connecting to exists and if not, will throw an exception [#1924](https://github.com/pnp/powershell/pull/1924) +- Added `Add-PnPListItemAttachment` cmdlet to provide ability to upload a file as an attachment to a SharePoint list item. +- Added `Remove-PnPListItemAttachment` cmdlet to provide ability to delete a list item attachment. ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) diff --git a/documentation/Add-PnPListItemAttachment.md b/documentation/Add-PnPListItemAttachment.md new file mode 100644 index 000000000..2c9f37034 --- /dev/null +++ b/documentation/Add-PnPListItemAttachment.md @@ -0,0 +1,175 @@ +--- +Module Name: PnP.PowerShell +schema: 2.0.0 +applicable: SharePoint Online +online version: https://pnp.github.io/powershell/cmdlets/Add-PnPListItemAttachment.html +external help file: PnP.PowerShell.dll-Help.xml +title: Add-PnPListItemAttachment +--- + +# Add-PnPListItemAttachment + +## SYNOPSIS +Adds an attachment to the specified list item in the SharePoint list + +## SYNTAX + +### Upload attachment file from path +```powershell +Add-PnPListItemAttachment [-List] [-Identity] [-Path ] [-NewFileName ] [-Connection ] [] +``` + +### Upload attachment file from stream +```powershell +Add-PnPListItemAttachment [-List] [-Identity] [-FileName ] [-Stream ] [-Connection ] [] +``` + +### Upload attachment file from text +```powershell +Add-PnPListItemAttachment [-List] [-Identity] [-FileName ] [-Content ] [-Connection ] [] +``` + +## DESCRIPTION + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Add-PnPListItemAttachement -List "Demo List" -Identity 1 -Path c:\temp\test.mp4 +``` + +Adds a new attachment to the list item with Id "1" in the "Demo List" SharePoint list with file name as test.mp4 from the specified path. + + +### EXAMPLE 2 +```powershell +Add-PnPListItemAttachement -List "Demo List" -Identity 1 -FileName "test.txt" -Content '{ "Test": "Value" }' +``` + +Adds a new attachment to the list item with Id "1" in the "Demo List" SharePoint list with file name as test.txt and content as specified. + +### EXAMPLE 3 +```powershell +Add-PnPListItemAttachement -List "Demo List" -Identity 1 -FileName "test.mp4" -Stream $fileStream +``` + +Adds a new attachment to the list item with Id "1" in the "Demo List" SharePoint list with file name as test.mp4 and content coming from a stream. + +## PARAMETERS + +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Content +Specify text of the attachment for the list item. + +```yaml +Type: String +Parameter Sets: (Upload file from text) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Path +The local file path + +```yaml +Type: String +Parameter Sets: (Upload file) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -NewFileName +Filename to give to the attachment file on SharePoint + +```yaml +Type: String +Parameter Sets: (Upload file) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -FileName +Name for attachment file + +```yaml +Type: String +Parameter Sets: (Upload file from stream, Upload file from text) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Stream +Stream with the file contents + +```yaml +Type: Stream +Parameter Sets: (Upload file from stream) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + + +### -List +The ID, Title or Url of the list. + +```yaml +Type: ListPipeBind +Parameter Sets: (All) + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Identity +The ID of the listitem, or actual ListItem object + +```yaml +Type: ListItemPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/documentation/Remove-PnPListItemAttachment.md b/documentation/Remove-PnPListItemAttachment.md new file mode 100644 index 000000000..235e9f51f --- /dev/null +++ b/documentation/Remove-PnPListItemAttachment.md @@ -0,0 +1,168 @@ +--- +Module Name: PnP.PowerShell +schema: 2.0.0 +applicable: SharePoint Online +online version: https://pnp.github.io/powershell/cmdlets/Remove-PnPListItemAttachment.html +external help file: PnP.PowerShell.dll-Help.xml +title: Remove-PnPListItemAttachment +--- + +# Add-PnPListItemAttachment + +## SYNOPSIS +Removes attachment from the specified list item in the SharePoint list + +## SYNTAX + +### Remove attachment from list item +```powershell +Remove-PnPListItemAttachment [-List] [-Identity] [-FileName ] [-Recycle ] [-Force ] [-Connection ] [] +``` + +### Removes all attachments file from list item +```powershell +Remove-PnPListItemAttachment [-List] [-Identity] [-All ] [-Recycle ] [-Force ] [-Connection ] [] +``` + +## DESCRIPTION + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Remove-PnPListItemAttachement -List "Demo List" -Identity 1 -FileName test.txt +``` + +Permanently delete an attachment from the list item with Id "1" in the "Demo List" SharePoint list with file name as test.txt. + +### EXAMPLE 2 +```powershell +Remove-PnPListItemAttachement -List "Demo List" -Identity 1 -FileName test.txt -Recycle +``` + +Removes an attachment from the list item with Id "1" in the "Demo List" SharePoint list with file name as test.txt and sends it to recycle bin. + +### EXAMPLE 3 +```powershell +Remove-PnPListItemAttachement -List "Demo List" -Identity 1 -FileName test.txt -Recycle -Force +``` + +Removes an attachment from the list item with Id "1" in the "Demo List" SharePoint list with file name as test.txt and sends it to recycle bin. It will not ask for confirmation from user. + +### EXAMPLE 4 +```powershell +Remove-PnPListItemAttachement -List "Demo List" -Identity 1 -All -Recycle -Force +``` + +Removes all attachments from the list item with Id "1" in the "Demo List" SharePoint list and sends them to recycle bin. It will not ask for confirmation from user. + +### EXAMPLE 5 +```powershell +Remove-PnPListItemAttachement -List "Demo List" -Identity 1 -All +``` + +Permanently deletes all attachments from the list item with Id "1" in the "Demo List" SharePoint list and sends them to recycle bin. + +## PARAMETERS + +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -FileName +Specify name of the attachment to delete from list item + +```yaml +Type: String +Parameter Sets: (Single) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -All +Specify if you want to delete all list item attachments. + +```yaml +Type: SwitchParameter +Parameter Sets: (Multiple) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Recycle +Specify if you want to send the attachment(s) to the recycle bin. + +```yaml +Type: String +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Force +Specifying the Force parameter will skip the confirmation question. + +```yaml +Type: String +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -List +The ID, Title or Url of the list. + +```yaml +Type: ListPipeBind +Parameter Sets: (All) + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Identity +The ID of the listitem, or actual ListItem object + +```yaml +Type: ListItemPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/src/Commands/Lists/AddListItemAttachment.cs b/src/Commands/Lists/AddListItemAttachment.cs new file mode 100644 index 000000000..5dc6e194e --- /dev/null +++ b/src/Commands/Lists/AddListItemAttachment.cs @@ -0,0 +1,112 @@ +using PnP.Core.Model.SharePoint; +using PnP.PowerShell.Commands.Base.PipeBinds; +using System; +using System.Collections.Generic; +using System.IO; +using System.Management.Automation; +using System.Text; + +namespace PnP.PowerShell.Commands.Lists +{ + [Cmdlet(VerbsCommon.Add, "PnPListItemAttachment")] + [OutputType(typeof(IAttachment))] + public class AddListItemAttachment : PnPWebCmdlet + { + private const string ParameterSet_ASFILE = "Upload file"; + private const string ParameterSet_ASSTREAM = "Upload file from stream"; + private const string ParameterSet_ASTEXT = "Upload file from text"; + + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, ParameterSetName = ParameterSet_ASSTREAM)] + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, ParameterSetName = ParameterSet_ASFILE)] + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, ParameterSetName = ParameterSet_ASTEXT)] + public ListPipeBind List; + + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 1, ParameterSetName = ParameterSet_ASSTREAM)] + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 1, ParameterSetName = ParameterSet_ASFILE)] + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 1, ParameterSetName = ParameterSet_ASTEXT)] + public ListItemPipeBind Identity; + + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_ASFILE)] + [ValidateNotNullOrEmpty] + public string Path = string.Empty; + + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_ASSTREAM)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_ASTEXT)] + [ValidateNotNullOrEmpty] + public string FileName = string.Empty; + + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_ASFILE)] + [ValidateNotNullOrEmpty] + public string NewFileName = string.Empty; + + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_ASSTREAM)] + [ValidateNotNullOrEmpty] + public Stream Stream; + + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_ASTEXT)] + public string Content; + + protected override void ExecuteCmdlet() + { + IList list = List.GetList(PnPContext); + + if (list == null) + { + throw new PSArgumentException($"Cannot find list provided through -{nameof(List)}", nameof(List)); + } + + IListItem item = Identity.GetListItem(list); + + if (item == null) + { + throw new PSArgumentException($"Cannot find list item provided through -{nameof(Identity)}", nameof(Identity)); + } + + if (ParameterSetName == ParameterSet_ASFILE) + { + if (!System.IO.Path.IsPathRooted(Path)) + { + Path = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Path); + } + if (string.IsNullOrEmpty(NewFileName)) + { + FileName = System.IO.Path.GetFileName(Path); + } + else + { + FileName = NewFileName; + } + } + + item.EnsureProperties(i => i.AttachmentFiles); + + IAttachment addedAttachment = null; + switch (ParameterSetName) + { + case ParameterSet_ASFILE: + addedAttachment = item.AttachmentFiles.AddAsync(FileName, File.OpenRead(Path)).GetAwaiter().GetResult(); + WriteObject(addedAttachment); + break; + + case ParameterSet_ASTEXT: + using (var stream = new MemoryStream()) + { + using (var writer = new StreamWriter(stream)) + { + writer.Write(Content); + writer.Flush(); + stream.Position = 0; + addedAttachment = item.AttachmentFiles.AddAsync(FileName, stream).GetAwaiter().GetResult(); + WriteObject(addedAttachment); + } + } + break; + + default: + addedAttachment = item.AttachmentFiles.AddAsync(FileName, Stream).GetAwaiter().GetResult(); + WriteObject(addedAttachment); + break; + } + } + } +} diff --git a/src/Commands/Lists/RemoveListItemAttachment.cs b/src/Commands/Lists/RemoveListItemAttachment.cs new file mode 100644 index 000000000..6f58f20d4 --- /dev/null +++ b/src/Commands/Lists/RemoveListItemAttachment.cs @@ -0,0 +1,91 @@ +using PnP.Core.Model.SharePoint; +using PnP.Core.QueryModel; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Properties; +using System.Linq; +using System.Management.Automation; + +namespace PnP.PowerShell.Commands.Lists +{ + [Cmdlet(VerbsCommon.Remove, "PnPListItemAttachment")] + public class RemoveListItemAttachment : PnPWebCmdlet + { + private const string ParameterSet_SINGLE = "Single"; + private const string ParameterSet_Multiple = "Multiple"; + + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, ParameterSetName = ParameterSet_SINGLE)] + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, ParameterSetName = ParameterSet_Multiple)] + public ListPipeBind List; + + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 1, ParameterSetName = ParameterSet_SINGLE)] + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 1, ParameterSetName = ParameterSet_Multiple)] + public ListItemPipeBind Identity; + + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_SINGLE)] + [ValidateNotNullOrEmpty] + public string FileName = string.Empty; + + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_Multiple)] + public SwitchParameter All; + + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_SINGLE)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_Multiple)] + public SwitchParameter Recycle; + + [Parameter(Mandatory = false, ValueFromPipeline = true, ParameterSetName = ParameterSet_SINGLE)] + [Parameter(Mandatory = false, ValueFromPipeline = true, ParameterSetName = ParameterSet_Multiple)] + public SwitchParameter Force; + + protected override void ExecuteCmdlet() + { + IList list = List.GetList(PnPContext); + + if (list == null) + { + throw new PSArgumentException($"Cannot find list provided through -{nameof(List)}", nameof(List)); + } + + IListItem item = Identity.GetListItem(list); + + if (item == null) + { + throw new PSArgumentException($"Cannot find list item provided through -{nameof(Identity)}", nameof(Identity)); + } + + item.EnsureProperties(i => i.AttachmentFiles); + var files = item.AttachmentFiles.AsRequested(); + var removeText = Recycle.IsPresent ? "Recycle" : "Remove"; + if (All.IsPresent) + { + if (Force || ShouldContinue($"{removeText} all list item attachments?", Resources.Confirm)) + { + foreach (var file in files.ToList()) + { + if (Recycle.IsPresent) + { + file.Recycle(); + } + else + { + file.Delete(); + } + } + } + } + else + { + if (Force || ShouldContinue($"{removeText} all list item attachments?", Resources.Confirm)) + { + if (Recycle.IsPresent) + { + files?.ToList().Where(i => i.FileName == FileName)?.FirstOrDefault()?.Recycle(); + } + else + { + files?.ToList().Where(i => i.FileName == FileName)?.FirstOrDefault()?.Delete(); + } + } + } + } + } +} From e7f3776a54a688d0d79fd32a57d5a91adcd6b12c Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Fri, 3 Jun 2022 03:34:26 +0000 Subject: [PATCH 294/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index 05c7cb7a6..7617cdb5f 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -70ff8d3bb385bd392f7a5a958e0f82a95bed96fb \ No newline at end of file +86f62f666fb20932007b2183c2af26cc452dd188 \ No newline at end of file diff --git a/version.txt b/version.txt index c5f06fe2b..91c2e2b75 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.36 \ No newline at end of file +1.10.37 \ No newline at end of file From c7c53ccdbc81aca3be2204cc207b8f49b77f3a83 Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Sat, 4 Jun 2022 00:01:28 +0200 Subject: [PATCH 295/458] Adds allowTenantMoveWithDataMigration parameter --- documentation/Set-PnPPlannerConfiguration.md | 17 ++++++++++++++++- .../Model/Planner/PlannerTenantConfig.cs | 6 ++++++ src/Commands/Planner/SetPlannerConfiguration.cs | 7 +++++-- src/Commands/Utilities/PlannerUtility.cs | 3 ++- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/documentation/Set-PnPPlannerConfiguration.md b/documentation/Set-PnPPlannerConfiguration.md index eb38f98d1..7e398dd4a 100644 --- a/documentation/Set-PnPPlannerConfiguration.md +++ b/documentation/Set-PnPPlannerConfiguration.md @@ -18,7 +18,7 @@ Allows the Microsoft Planner configuration of the tenant to be set ## SYNTAX ``` -Set-PnPPlannerConfiguration [-IsPlannerAllowed ] [-AllowRosterCreation ] [-AllowTenantMoveWithDataLoss ] [-AllowPlannerMobilePushNotifications ] [-AllowCalendarSharing ] [-Connection ] [] +Set-PnPPlannerConfiguration [-IsPlannerAllowed ] [-AllowRosterCreation ] [-AllowTenantMoveWithDataLoss ] [-AllowTenantMoveWithDataMigration ] [-AllowPlannerMobilePushNotifications ] [-AllowCalendarSharing ] [-Connection ] [] ``` ## DESCRIPTION @@ -79,6 +79,21 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -AllowTenantMoveWithDataMigration +Configure whether a tenant move with data migration is authorized + +```yaml +Type: Boolean +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -AllowPlannerMobilePushNotifications Allows configuring whether the direct push notifications are enabled where contents of the push notification are being sent directly through Apple's or Google's services to get to the iOS or Android client diff --git a/src/Commands/Model/Planner/PlannerTenantConfig.cs b/src/Commands/Model/Planner/PlannerTenantConfig.cs index a549b6964..47231eb0a 100644 --- a/src/Commands/Model/Planner/PlannerTenantConfig.cs +++ b/src/Commands/Model/Planner/PlannerTenantConfig.cs @@ -25,6 +25,12 @@ public class PlannerTenantConfig [JsonPropertyName("allowTenantMoveWithDataLoss")] public bool? AllowTenantMoveWithDataLoss { get; set; } + /// + /// Indicates whether a tenant move with data migration is authorized. + /// + [JsonPropertyName("allowTenantMoveWithDataMigration")] + public bool? AllowTenantMoveWithDataMigration { get; set; } + /// /// Indicates whether the creation of Roster containers (Planner plans without Microsoft 365 Groups) is allowed /// diff --git a/src/Commands/Planner/SetPlannerConfiguration.cs b/src/Commands/Planner/SetPlannerConfiguration.cs index 1c15b868a..cff2e758c 100644 --- a/src/Commands/Planner/SetPlannerConfiguration.cs +++ b/src/Commands/Planner/SetPlannerConfiguration.cs @@ -3,7 +3,7 @@ using PnP.PowerShell.Commands.Utilities; using System.Management.Automation; -namespace PnP.PowerShell.Commands.PowerPlatform.PowerAutomate +namespace PnP.PowerShell.Commands.Planner { [Cmdlet(VerbsCommon.Set, "PnPPlannerConfiguration")] [RequiredMinimalApiPermissions("https://tasks.office.com/.default")] @@ -18,6 +18,9 @@ public class SetPlannerConfiguration : PnPGraphCmdlet [Parameter(Mandatory = false)] public bool? AllowTenantMoveWithDataLoss; + [Parameter(Mandatory = false)] + public bool? AllowTenantMoveWithDataMigration; + [Parameter(Mandatory = false)] public bool? AllowPlannerMobilePushNotifications; @@ -26,7 +29,7 @@ public class SetPlannerConfiguration : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var result = PlannerUtility.SetPlannerConfigAsync(HttpClient, AccessToken, IsPlannerAllowed, AllowCalendarSharing, AllowTenantMoveWithDataLoss, AllowRosterCreation, AllowPlannerMobilePushNotifications).GetAwaiter().GetResult(); + var result = PlannerUtility.SetPlannerConfigAsync(HttpClient, AccessToken, IsPlannerAllowed, AllowCalendarSharing, AllowTenantMoveWithDataLoss, AllowTenantMoveWithDataMigration, AllowRosterCreation, AllowPlannerMobilePushNotifications).GetAwaiter().GetResult(); WriteObject(result); } } diff --git a/src/Commands/Utilities/PlannerUtility.cs b/src/Commands/Utilities/PlannerUtility.cs index e0d8d4f6e..b10c8ea2f 100644 --- a/src/Commands/Utilities/PlannerUtility.cs +++ b/src/Commands/Utilities/PlannerUtility.cs @@ -322,13 +322,14 @@ public static async Task GetPlannerConfigAsync(HttpClient h /// HttpClient instance to use to send out requests /// AccessToken to use to authenticate the request /// PlannerTenantConfig - public static async Task SetPlannerConfigAsync(HttpClient httpClient, string accessToken, bool? isPlannerAllowed, bool? allowCalendarSharing, bool? allowTenantMoveWithDataLoss, bool? allowRosterCreation, bool? allowPlannerMobilePushNotifications) + public static async Task SetPlannerConfigAsync(HttpClient httpClient, string accessToken, bool? isPlannerAllowed, bool? allowCalendarSharing, bool? allowTenantMoveWithDataLoss, bool? allowTenantMoveWithDataMigration, bool? allowRosterCreation, bool? allowPlannerMobilePushNotifications) { var content = new PlannerTenantConfig { IsPlannerAllowed = isPlannerAllowed, AllowCalendarSharing = allowCalendarSharing, AllowTenantMoveWithDataLoss = allowTenantMoveWithDataLoss, + AllowTenantMoveWithDataMigration = allowTenantMoveWithDataMigration, AllowRosterCreation = allowRosterCreation, AllowPlannerMobilePushNotifications = allowPlannerMobilePushNotifications }; From 16943f4e3468849896a836343a67e938a47a9599 Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Sat, 4 Jun 2022 00:07:01 +0200 Subject: [PATCH 296/458] Reformulated description --- documentation/Set-PnPPlannerConfiguration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Set-PnPPlannerConfiguration.md b/documentation/Set-PnPPlannerConfiguration.md index 7e398dd4a..ea040e417 100644 --- a/documentation/Set-PnPPlannerConfiguration.md +++ b/documentation/Set-PnPPlannerConfiguration.md @@ -80,7 +80,7 @@ Accept wildcard characters: False ``` ### -AllowTenantMoveWithDataMigration -Configure whether a tenant move with data migration is authorized +Allows configuring whether a tenant move with data migration is authorized ```yaml Type: Boolean From 9f74da91a471f58aa89926c60f1c17173e8e6905 Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Sat, 4 Jun 2022 00:23:10 +0200 Subject: [PATCH 297/458] Fix namespace --- src/Commands/Planner/GetPlannerConfiguration.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Commands/Planner/GetPlannerConfiguration.cs b/src/Commands/Planner/GetPlannerConfiguration.cs index 811f6f075..81c338e4f 100644 --- a/src/Commands/Planner/GetPlannerConfiguration.cs +++ b/src/Commands/Planner/GetPlannerConfiguration.cs @@ -1,9 +1,9 @@ -using PnP.PowerShell.Commands.Attributes; +using System.Management.Automation; +using PnP.PowerShell.Commands.Attributes; using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Utilities; -using System.Management.Automation; -namespace PnP.PowerShell.Commands.PowerPlatform.PowerAutomate +namespace PnP.PowerShell.Commands.Planner { [Cmdlet(VerbsCommon.Get, "PnPPlannerConfiguration")] [RequiredMinimalApiPermissions("https://tasks.office.com/.default")] From 8295ed5322dcdb23edac77e03f02d8f1bd2866b1 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Sat, 4 Jun 2022 03:30:38 +0000 Subject: [PATCH 298/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index 7617cdb5f..a51107490 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -86f62f666fb20932007b2183c2af26cc452dd188 \ No newline at end of file +690847cb851c618cdbe2d02d933cb5768490b6bc \ No newline at end of file diff --git a/version.txt b/version.txt index 91c2e2b75..d39d04560 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.37 \ No newline at end of file +1.10.38 \ No newline at end of file From e8798f90db9aab4efeb599c1b9f6a82e0fa9e2c5 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Sun, 5 Jun 2022 03:28:10 +0000 Subject: [PATCH 299/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 6a785e33a..f43b791cf 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -6362a8cf147d3234ec246f5643364ecb107db811 \ No newline at end of file +73d3f30a26ac27803a360a523dc73f55237edda9 \ No newline at end of file diff --git a/version.txt b/version.txt index d39d04560..154eb8fc8 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.38 \ No newline at end of file +1.10.39 \ No newline at end of file From 290a05c0b04556c6d16b35f07536e0fd78d8c1fa Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Sun, 5 Jun 2022 16:59:25 +0200 Subject: [PATCH 300/458] Get plan by id --- documentation/Get-PnPPlannerPlan.md | 33 ++++++++++++++++++++++++-- src/Commands/Planner/GetPlannerPlan.cs | 32 +++++++++++++++++-------- 2 files changed, 53 insertions(+), 12 deletions(-) diff --git a/documentation/Get-PnPPlannerPlan.md b/documentation/Get-PnPPlannerPlan.md index 6854e607a..8394a11a2 100644 --- a/documentation/Get-PnPPlannerPlan.md +++ b/documentation/Get-PnPPlannerPlan.md @@ -20,11 +20,18 @@ Returns all or a specific Planner plan for a Microsoft 365 Group. ## SYNTAX +### By Group ```powershell Get-PnPPlannerPlan -Group [-Identity ] [-ResolveIdentities] [] ``` +### By Plan Id +```powershell +Get-PnPPlannerPlan -Id [-ResolveIdentities] + [] +``` + ## DESCRIPTION This cmdlet returns all or a specific Planner plan for a Microsoft 365 Group. @@ -44,6 +51,13 @@ Get-PnPPlannerPlan -Group "Marketing" -Identity "Conference Plan" Returns the specified plan for the Marketing group. +### Example 3 +```powershell +Get-PnPPlannerPlan -Id "gndWOTSK60GfPQfiDDj43JgACDCb" -ResolveIdentities +``` + +Rerturns the plan with specified ID with resolved identities. + ## PARAMETERS ### -Group @@ -51,7 +65,7 @@ Specify the group containing the plans ```yaml Type: PlannerGroupPipeBind -Parameter Sets: (All) +Parameter Sets: By Group Aliases: Required: True @@ -66,7 +80,7 @@ If specified the plan with this ID or Name will be returned. ```yaml Type: PlannerPlanPipeBind -Parameter Sets: (All) +Parameter Sets: By Group Aliases: Required: False @@ -76,6 +90,21 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Id +If specified the plan with this ID will be returned. + +```yaml +Type: String +Parameter Sets: By Plan Id +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -ResolveIdentities Show user display names instead of user IDs. diff --git a/src/Commands/Planner/GetPlannerPlan.cs b/src/Commands/Planner/GetPlannerPlan.cs index 2a334578d..0b3c99aac 100644 --- a/src/Commands/Planner/GetPlannerPlan.cs +++ b/src/Commands/Planner/GetPlannerPlan.cs @@ -1,9 +1,8 @@ -using System.Management.Automation; -using Microsoft.Graph; using PnP.PowerShell.Commands.Attributes; using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Base.PipeBinds; using PnP.PowerShell.Commands.Utilities; +using System.Management.Automation; namespace PnP.PowerShell.Commands.Planner { @@ -11,32 +10,45 @@ namespace PnP.PowerShell.Commands.Planner [RequiredMinimalApiPermissions("Group.Read.All")] public class GetPlannerPlan : PnPGraphCmdlet { - [Parameter(Mandatory = true)] + private const string ParameterName_BYGROUP = "By Group"; + private const string ParameterName_BYPLANID = "By Plan Id"; + + [Parameter(Mandatory = true, HelpMessage = "Specify the group id of group owning the plan.", ParameterSetName = ParameterName_BYGROUP)] public PlannerGroupPipeBind Group; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = false, HelpMessage = "Specify the name of the plan.", ParameterSetName = ParameterName_BYGROUP)] public PlannerPlanPipeBind Identity; + [Parameter(Mandatory = true, HelpMessage = "Specify the ID of the plan.", ParameterSetName = ParameterName_BYPLANID)] + public string Id; [Parameter(Mandatory = false)] public SwitchParameter ResolveIdentities; + protected override void ExecuteCmdlet() { - var groupId = Group.GetGroupId(HttpClient, AccessToken); - if (groupId != null) + if (ParameterSetName == ParameterName_BYGROUP) { - if (ParameterSpecified(nameof(Identity))) + var groupId = Group.GetGroupId(HttpClient, AccessToken); + if (groupId != null) { - WriteObject(Identity.GetPlanAsync(HttpClient, AccessToken, groupId, ResolveIdentities).GetAwaiter().GetResult()); + if (ParameterSpecified(nameof(Identity))) + { + WriteObject(Identity.GetPlanAsync(HttpClient, AccessToken, groupId, ResolveIdentities).GetAwaiter().GetResult()); + } + else + { + WriteObject(PlannerUtility.GetPlansAsync(HttpClient, AccessToken, groupId, ResolveIdentities).GetAwaiter().GetResult(), true); + } } else { - WriteObject(PlannerUtility.GetPlansAsync(HttpClient, AccessToken, groupId, ResolveIdentities).GetAwaiter().GetResult(), true); + throw new PSArgumentException("Group not found"); } } else { - throw new PSArgumentException("Group not found"); + WriteObject(PlannerUtility.GetPlanAsync(HttpClient, AccessToken, Id, ResolveIdentities).GetAwaiter().GetResult()); } } } From e167b2477d72d0e11b8190df82ee0f733466b7f9 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sun, 5 Jun 2022 23:05:46 +0300 Subject: [PATCH 301/458] Added Get-PnPListItemAttachments cmdlet --- CHANGELOG.md | 5 +- documentation/Get-PnPListItemAttachments.md | 114 ++++++++++++++++++ documentation/Remove-PnPListItemAttachment.md | 10 +- src/Commands/Lists/GetListItemAttachments.cs | 97 +++++++++++++++ 4 files changed, 219 insertions(+), 7 deletions(-) create mode 100644 documentation/Get-PnPListItemAttachments.md create mode 100644 src/Commands/Lists/GetListItemAttachments.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a3797dfb..479f6ad0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,8 +40,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added optional `-Connection` parameter to `Get-PnPConnection`, `Get-PnPContext` and `Set-PnPContext` which allows for using any of these for a specific connection [#1919](https://github.com/pnp/powershell/pull/1919) - Added `-IncludeDeprecated` parameter to `Get-PnPTerm` cmdlet to fetch deprecated terms if specified [#1903](https://github.com/pnp/powershell/pull/1903) - Added optional `-ValidateConnection` to `Connect-PnPOnline` which will check if the site you are connecting to exists and if not, will throw an exception [#1924](https://github.com/pnp/powershell/pull/1924) -- Added `Add-PnPListItemAttachment` cmdlet to provide ability to upload a file as an attachment to a SharePoint list item. -- Added `Remove-PnPListItemAttachment` cmdlet to provide ability to delete a list item attachment. +- Added `Add-PnPListItemAttachment` cmdlet to provide ability to upload a file as an attachment to a SharePoint list item. [#1932](https://github.com/pnp/powershell/pull/1932) +- Added `Remove-PnPListItemAttachment` cmdlet to provide ability to delete a list item attachment. [#1932](https://github.com/pnp/powershell/pull/1932) +- Added `Get-PnPListItemAttachments` cmdlet to download the attachments from a list item. [#1932](https://github.com/pnp/powershell/pull/1932) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) diff --git a/documentation/Get-PnPListItemAttachments.md b/documentation/Get-PnPListItemAttachments.md new file mode 100644 index 000000000..2150b881a --- /dev/null +++ b/documentation/Get-PnPListItemAttachments.md @@ -0,0 +1,114 @@ +--- +Module Name: PnP.PowerShell +schema: 2.0.0 +applicable: SharePoint Online +online version: https://pnp.github.io/powershell/cmdlets/Get-PnPListItemAttachments.html +external help file: PnP.PowerShell.dll-Help.xml +title: Get-PnPListItemAttachments +--- + +# Get-PnPListItemAttachments + +## SYNOPSIS +Downloads the list item attachments to a specified path on the file system. + +## SYNTAX + +### Get attachments from list item +```powershell +Get-PnPListItemAttachments [-List] [-Identity] [-Path ] [-Force ] [-Connection ] [] +``` + +## DESCRIPTION + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Get-PnPListItemAttachments -List "Demo List" -Identity 1 -Path "C:\temp" +``` + +Download all attachments from the list item with Id "1" in the "Demo List" SharePoint list and store it in the temp folder. + +### EXAMPLE 2 +```powershell +Get-PnPListItemAttachments -List "Demo List" -Identity 1 -Path "C:\temp" -Force +``` + +Download all attachments from the list item with Id "1" in the "Demo List" SharePoint list and store it in the temp folder and overwrite the files if they already exist. + +## PARAMETERS + +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Path +Specify the path on the local file system to download the list item attachments. + +```yaml +Type: String +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Force +Specifying the Force parameter will skip the confirmation question and overwrite the file in file system. + +```yaml +Type: String +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -List +The ID, Title or Url of the list. + +```yaml +Type: ListPipeBind +Parameter Sets: (All) + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Identity +The ID of the listitem, or actual ListItem object + +```yaml +Type: ListItemPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/documentation/Remove-PnPListItemAttachment.md b/documentation/Remove-PnPListItemAttachment.md index 235e9f51f..6eca0b962 100644 --- a/documentation/Remove-PnPListItemAttachment.md +++ b/documentation/Remove-PnPListItemAttachment.md @@ -30,35 +30,35 @@ Remove-PnPListItemAttachment [-List] [-Identity] i.AttachmentFiles); + + var attachmentFilesCollection = item.AttachmentFiles.AsRequested(); + + if (attachmentFilesCollection?.ToList().Count == 0) + { + WriteWarning($"No attachments found for the list item provided through -{nameof(Identity)}"); + } + else + { + // Enumerate over the attachments and download them + foreach (var attachment in attachmentFilesCollection) + { + string fileOut = System.IO.Path.Combine(Path, attachment.FileName); + + if (System.IO.File.Exists(fileOut) && !Force) + { + WriteWarning($"File '{attachment.FileName}' exists already in the specified path. Use the -Force parameter to overwrite the file."); + } + else + { + // Start the download + using (Stream downloadedContentStream = attachment.GetContentAsync().GetAwaiter().GetResult()) + { + // Download the file bytes in 2MB chunks and immediately write them to a file on disk + // This approach avoids the file being fully loaded in the process memory + var bufferSize = 2 * 1024 * 1024; // 2 MB buffer + + using (FileStream content = System.IO.File.Create(fileOut)) + { + byte[] buffer = new byte[bufferSize]; + int read; + while ((read = downloadedContentStream.ReadAsync(buffer, 0, buffer.Length).GetAwaiter().GetResult()) != 0) + { + content.Write(buffer, 0, read); + } + } + } + } + } + } + } + } +} From 9d6abaa5bab47f14ccb7bc7584fceba1a6256268 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Mon, 6 Jun 2022 03:38:38 +0000 Subject: [PATCH 302/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index f43b791cf..88d30b582 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -73d3f30a26ac27803a360a523dc73f55237edda9 \ No newline at end of file +a25618dbf25bc46617c5a298c7afc13c4c519c87 \ No newline at end of file diff --git a/version.txt b/version.txt index 154eb8fc8..294c0aa73 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.39 \ No newline at end of file +1.10.40 \ No newline at end of file From 91870b595d9a22a848118d093fa7deb046bb6b35 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 7 Jun 2022 03:40:42 +0000 Subject: [PATCH 303/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 88d30b582..669b10d86 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -a25618dbf25bc46617c5a298c7afc13c4c519c87 \ No newline at end of file +0eb36d58fc5b3f715f5ac2b3969dd8cea7b782a4 \ No newline at end of file diff --git a/version.txt b/version.txt index 294c0aa73..be032d7ca 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.40 \ No newline at end of file +1.10.41 \ No newline at end of file From 6068af1701e77302bcd97cd0bd1fe9fc3bc29cc0 Mon Sep 17 00:00:00 2001 From: Marc D Anderson Date: Tue, 7 Jun 2022 08:41:34 -0400 Subject: [PATCH 304/458] Update batching.md (#1940) Fixed links in **Cmdlets that support batching** section at the bottom. --- pages/articles/batching.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pages/articles/batching.md b/pages/articles/batching.md index 6f3fc3823..8506e3574 100644 --- a/pages/articles/batching.md +++ b/pages/articles/batching.md @@ -46,9 +46,9 @@ If during one of these chunks an exception occurs (for instance you are trying t As of release 1.7.63-nightly the following cmdlets support batching: -* [`Add-PnPListItem`](/documentation/Add-PnPListItem.md) -* [`Set-PnPListItem`](/documentation/Set-PnPListItem.md) -* [`Remove-PnPListItem`](/documentation/Remove-PnPListItem.md) -* [`Publish-PnPSyntexModel`](/documentation/Publish-PnPSyntexModel.md) -* [`Unpublish-PnPSyntexModel`](/documentation/Unpublish-PnPSyntexModel.md) -* [`Request-PnPSyntexClassifyAndExtract`](/documentation/Request-PnPSyntexClassifyAndExtract.md) +* [`Add-PnPListItem`](/powershell/cmdlets/Add-PnPListItem.html) +* [`Set-PnPListItem`](/powershell/cmdlets/Set-PnPListItem.html) +* [`Remove-PnPListItem`](/powershell/cmdlets/Remove-PnPListItem.html) +* [`Publish-PnPSyntexModel`](/powershell/cmdlets/Publish-PnPSyntexModel.html) +* [`Unpublish-PnPSyntexModel`](/powershell/cmdlets/Unpublish-PnPSyntexModel.html) +* [`Request-PnPSyntexClassifyAndExtract`](/powershell/cmdlets/Request-PnPSyntexClassifyAndExtract.html) From 4ca04ae54552817259f3acfdde7ac3098c68f15a Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Tue, 7 Jun 2022 14:42:51 +0200 Subject: [PATCH 305/458] Update Add-PnPTeamsUser.md (#1942) Adjusted for multiple parameter sets --- documentation/Add-PnPTeamsUser.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/documentation/Add-PnPTeamsUser.md b/documentation/Add-PnPTeamsUser.md index 680e86575..cbf05b819 100644 --- a/documentation/Add-PnPTeamsUser.md +++ b/documentation/Add-PnPTeamsUser.md @@ -19,11 +19,18 @@ Adds a user to an existing Microsoft Teams instance. ## SYNTAX +### User ```powershell Add-PnPTeamsUser -Team -Channel -User -Role [] ``` +### Users +```powershell +Add-PnPTeamsUser -Team -Users -Role [] +``` + ## DESCRIPTION +This cmdlet adds one or more users to an existing Team. ## EXAMPLES @@ -32,18 +39,22 @@ Add-PnPTeamsUser -Team -Channel -User Add-PnPTeamsUser -Team MyTeam -User john@doe.com -Role Owner ``` -Adds a user as an owner to the team +Adds a user as an owner to the team. ### EXAMPLE 2 ```powershell Add-PnPTeamsUser -Team MyTeam -User john@doe.com -Role Member ``` +Adds a user as a member to the team. + ### EXAMPLE 3 ```powershell Add-PnPTeamsUser -Team MyTeam -Users "john@doe.com","jane@doe.com" -Role Member ``` +Adds multiple users as members to the team. + ### EXAMPLE 4 ```powershell Add-PnPTeamsUser -Team MyTeam -User "jane@doe.com" -Role Member -Channel Private From b90dde145da8d9d643746813b162b3fcd762c42a Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Tue, 7 Jun 2022 14:43:33 +0200 Subject: [PATCH 306/458] Update Get-PnPFlow.md (#1943) typo --- documentation/Get-PnPFlow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Get-PnPFlow.md b/documentation/Get-PnPFlow.md index 2143e9bd5..81d12df6f 100644 --- a/documentation/Get-PnPFlow.md +++ b/documentation/Get-PnPFlow.md @@ -32,7 +32,7 @@ This cmdlet returns the flows for a given enviroment. $environment = Get-PnPPowerPlatformEnvironment Get-PnPFlow -Environment $environment ``` -This returns all the flows for a given power platform environment +This returns all the flows for a given Power Platform environment ### Example 2 ```powershell From 46ed68f62358e9cbac5dd741d2994cc34c048451 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Tue, 7 Jun 2022 15:45:16 +0300 Subject: [PATCH 307/458] Update CHANGELOG.md Added contributors info --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0d019560..b21c48b64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Contributors +- Arleta Wanat [PowershellScripts] +- Marc D Anderson [sympmarc] - dc366 [dc366] - Bart-Jan Dekker [bjdekker] - Aleksandr Sapozhkov [shurick81] From bc7fceea932cedbde29498fd5e30feb4d7d61b13 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Tue, 7 Jun 2022 16:13:12 +0300 Subject: [PATCH 308/458] Fix #1901 - docs update Fix #1901 , documentation typo which causes misunderstanding --- documentation/Set-PnPListItem.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Set-PnPListItem.md b/documentation/Set-PnPListItem.md index a73407ae3..0b6493c33 100644 --- a/documentation/Set-PnPListItem.md +++ b/documentation/Set-PnPListItem.md @@ -62,7 +62,7 @@ Sets the retention label in the list item with ID 1 in the "Demo List". ### EXAMPLE 5 ```powershell $batch = New-PnPBatch -for($i=0;$i -lt 100;$i++) +for($i=1;$i -lt 100;$i++) { Set-PnPListItem -List "Demo List" -Identity $i -Values @{"Title"="Updated Title"} -Batch $batch } From 5d633e40880716fda031addce6bc61dd44adb344 Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Tue, 7 Jun 2022 23:45:15 +0200 Subject: [PATCH 309/458] Extra parameters for set-plannertask --- documentation/Set-PnPPlannerTask.md | 38 +++++++++++- src/Commands/Model/Planner/PlannerTask.cs | 6 +- .../Model/Planner/PlannerTaskDetails.cs | 8 ++- src/Commands/Planner/SetPlannerTask.cs | 27 +++++++- src/Commands/Utilities/PlannerUtility.cs | 62 ++++++++++++------- 5 files changed, 113 insertions(+), 28 deletions(-) diff --git a/documentation/Set-PnPPlannerTask.md b/documentation/Set-PnPPlannerTask.md index c6d60b8fe..ae5f781d4 100644 --- a/documentation/Set-PnPPlannerTask.md +++ b/documentation/Set-PnPPlannerTask.md @@ -22,7 +22,7 @@ Updates an existing task ``` Set-PnPPlannerTask -TaskId [-Title ] [-Bucket ] [-PercentComplete ] [-DueDateTime ] [-StartDateTime ] - [-AssignedTo ] [-Description ] [] ``` @@ -152,7 +152,41 @@ Type: String[] Parameter Sets: (All) Aliases: -Required: True +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Priority +Sets the priority of the task. Value should be a number between 0 and 10. +- values 0 and 1 are interpreted as _Urgent_ +- values 2, 3 and 4 are interpreted as _Important_ +- values 5, 6 and 7 are interpreted as _Medium_ +- values 8, 9 and 10 are interpreted as _Low_ + +```yaml +Type: Int32 +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Description +Sets the description (notes) of the task. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False Position: Named Default value: None Accept pipeline input: False diff --git a/src/Commands/Model/Planner/PlannerTask.cs b/src/Commands/Model/Planner/PlannerTask.cs index bf8f4a0f7..450b2cebd 100644 --- a/src/Commands/Model/Planner/PlannerTask.cs +++ b/src/Commands/Model/Planner/PlannerTask.cs @@ -1,8 +1,8 @@ +using PnP.PowerShell.Commands.Model.Graph; +using PnP.PowerShell.Commands.Utilities.JSON; using System; using System.Collections.Generic; using System.Text.Json.Serialization; -using PnP.PowerShell.Commands.Model.Graph; -using PnP.PowerShell.Commands.Utilities.JSON; namespace PnP.PowerShell.Commands.Model.Planner { @@ -16,6 +16,7 @@ public class PlannerTask public string OrderHint { get; set; } public string AssigneePriority { get; set; } public int? PercentComplete { get; set; } + public int? Priority { get; set; } [JsonConverter(typeof(DateTimeISO8601Converter))] public DateTime? StartDateTime { get; set; } @@ -66,5 +67,4 @@ public class AppliedCategories [JsonExtensionData] public IDictionary AdditionalData { get; set; } } - } diff --git a/src/Commands/Model/Planner/PlannerTaskDetails.cs b/src/Commands/Model/Planner/PlannerTaskDetails.cs index 8b52ea7ae..9b5c04fda 100644 --- a/src/Commands/Model/Planner/PlannerTaskDetails.cs +++ b/src/Commands/Model/Planner/PlannerTaskDetails.cs @@ -1,13 +1,19 @@ +using PnP.PowerShell.Commands.Model.Graph; using System; using System.Collections.Generic; using System.Text.Json.Serialization; -using PnP.PowerShell.Commands.Model.Graph; namespace PnP.PowerShell.Commands.Model.Planner { public class PlannerTaskDetails { + [JsonPropertyName("@odata.etag")] + public string ETag { get; set; } + + public string Id { get; set; } + public string Description { get; set; } + public string PreviewType { get; set; } public Dictionary References { get; set; } diff --git a/src/Commands/Planner/SetPlannerTask.cs b/src/Commands/Planner/SetPlannerTask.cs index 2a29d8647..ceb534c6a 100644 --- a/src/Commands/Planner/SetPlannerTask.cs +++ b/src/Commands/Planner/SetPlannerTask.cs @@ -8,7 +8,7 @@ using PnP.PowerShell.Commands.Utilities; using PnP.PowerShell.Commands.Utilities.REST; -namespace PnP.PowerShell.Commands.Graph +namespace PnP.PowerShell.Commands.Planner { [Cmdlet(VerbsCommon.Set, "PnPPlannerTask")] [RequiredMinimalApiPermissions("Group.ReadWrite.All")] @@ -26,12 +26,18 @@ public class SetPlannerTask : PnPGraphCmdlet [Parameter(Mandatory = false)] public int PercentComplete; + [Parameter(Mandatory = false)] + public int Priority; + [Parameter(Mandatory = false)] public DateTime DueDateTime; [Parameter(Mandatory = false)] public DateTime StartDateTime; + [Parameter(Mandatory = false)] + public string Description; + [Parameter(Mandatory = false)] public string[] AssignedTo; @@ -57,14 +63,27 @@ protected override void ExecuteCmdlet() { plannerTask.PercentComplete = PercentComplete; } + + if (ParameterSpecified(nameof(Priority))) + { + if (Priority < 0 || Priority > 10) + { + throw new PSArgumentException($"Parameter '{nameof(Priority)}' must be a number between 0 and 10."); + } + + plannerTask.Priority = Priority; + } + if (ParameterSpecified(nameof(DueDateTime))) { plannerTask.DueDateTime = DueDateTime.ToUniversalTime(); } + if (ParameterSpecified(nameof(StartDateTime))) { plannerTask.StartDateTime = StartDateTime.ToUniversalTime(); } + if (ParameterSpecified(nameof(AssignedTo))) { plannerTask.Assignments = new System.Collections.Generic.Dictionary(); @@ -88,6 +107,12 @@ protected override void ExecuteCmdlet() PlannerUtility.UpdateTaskAsync(HttpClient, AccessToken, existingTask, plannerTask).GetAwaiter().GetResult(); + + if (ParameterSpecified(nameof(Description))) + { + var existingTaskDetails = PlannerUtility.GetTaskDetailsAsync(HttpClient, AccessToken, TaskId, false).GetAwaiter().GetResult(); + PlannerUtility.UpdateTaskDetailsAsync(HttpClient, AccessToken, existingTaskDetails, Description).GetAwaiter().GetResult(); + } } else { diff --git a/src/Commands/Utilities/PlannerUtility.cs b/src/Commands/Utilities/PlannerUtility.cs index e0d8d4f6e..1bd722a62 100644 --- a/src/Commands/Utilities/PlannerUtility.cs +++ b/src/Commands/Utilities/PlannerUtility.cs @@ -125,29 +125,40 @@ public static async Task GetTaskAsync(HttpClient httpClient, string } if (includeDetails) { - var taskDetails = await GraphHelper.GetAsync(httpClient, $"v1.0/planner/tasks/{taskId}/details", accessToken); - if (resolveDisplayNames) + var taskDetails = await GetTaskDetailsAsync(httpClient, accessToken, taskId, resolveDisplayNames); + task.Details = taskDetails; + } + return task; + } + + public static async Task GetTaskDetailsAsync(HttpClient httpClient, string accessToken, string taskId, bool resolveDisplayNames) + { + var taskDetails = await GraphHelper.GetAsync(httpClient, $"v1.0/planner/tasks/{taskId}/details", accessToken); + if (!resolveDisplayNames) + return taskDetails; + + var newItems = new Dictionary(); + foreach (var checklistItem in taskDetails.Checklist) + { + var newCheckListItem = new PlannerTaskCheckListItem { - Dictionary newItems = new Dictionary(); - foreach (var checklistItem in taskDetails.Checklist) + IsChecked = checklistItem.Value.IsChecked, + LastModifiedDateTime = checklistItem.Value.LastModifiedDateTime, + OrderHint = checklistItem.Value.OrderHint, + Title = checklistItem.Value.Title + }; + if (checklistItem.Value.LastModifiedBy != null) + { + newCheckListItem.LastModifiedBy = new IdentitySet { - var newCheckListItem = new PlannerTaskCheckListItem(); - newCheckListItem.IsChecked = checklistItem.Value.IsChecked; - newCheckListItem.LastModifiedDateTime = checklistItem.Value.LastModifiedDateTime; - newCheckListItem.OrderHint = checklistItem.Value.OrderHint; - newCheckListItem.Title = checklistItem.Value.Title; - if (checklistItem.Value.LastModifiedBy != null) - { - newCheckListItem.LastModifiedBy = new IdentitySet(); - newCheckListItem.LastModifiedBy.User = await ResolveIdentityAsync(httpClient, accessToken, checklistItem.Value.LastModifiedBy.User); - } - newItems.Add(checklistItem.Key, newCheckListItem); - } - taskDetails.Checklist = newItems; + User = await ResolveIdentityAsync(httpClient, accessToken, checklistItem.Value.LastModifiedBy.User) + }; } - task.Details = taskDetails; + newItems.Add(checklistItem.Key, newCheckListItem); } - return task; + taskDetails.Checklist = newItems; + + return taskDetails; } public static async Task AddTaskAsync(HttpClient httpClient, string accessToken, string planId, string bucketId, string title, string[] assignedTo = null) @@ -186,9 +197,18 @@ public static async Task DeleteTaskAsync(HttpClient httpClient, string accessTok public static async Task UpdateTaskAsync(HttpClient httpClient, string accessToken, PlannerTask taskToUpdate, PlannerTask task) { - - return await GraphHelper.PatchAsync(httpClient, accessToken, $"v1.0/planner/tasks/{taskToUpdate.Id}", task, new Dictionary() { { "IF-MATCH", taskToUpdate.ETag } }); + return await GraphHelper.PatchAsync(httpClient, accessToken, $"v1.0/planner/tasks/{taskToUpdate.Id}", task, new Dictionary { { "IF-MATCH", taskToUpdate.ETag } }); } + + public static async Task UpdateTaskDetailsAsync(HttpClient httpClient, string accessToken, PlannerTaskDetails taskToUpdate, string description) + { + var body = new PlannerTaskDetails + { + Description = description, + }; + await GraphHelper.PatchAsync(httpClient, accessToken, $"v1.0/planner/tasks/{taskToUpdate.Id}/details", body, new Dictionary { { "IF-MATCH", taskToUpdate.ETag } }); + } + #endregion #region Rosters From 84948fe6e0cf4d8f0720a63529043d06c88342c3 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Wed, 8 Jun 2022 03:46:44 +0000 Subject: [PATCH 310/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 669b10d86..18eb9c8cd 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -0eb36d58fc5b3f715f5ac2b3969dd8cea7b782a4 \ No newline at end of file +66909f7142f9eb289184c3e5a20064ed2bc583f7 \ No newline at end of file diff --git a/version.txt b/version.txt index be032d7ca..3ebe6943a 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.41 \ No newline at end of file +1.10.42 \ No newline at end of file From 4abb65e194b815c44ffd45974b154e415a6fdb39 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 8 Jun 2022 17:35:07 +0200 Subject: [PATCH 311/458] Changed supression of script errors to false (#1933) Co-authored-by: = <=> --- CHANGELOG.md | 1 + src/Commands/Base/PnPConnection.cs | 2 +- src/Commands/Utilities/BrowserHelper.cs | 7 +++---- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b21c48b64..f85b301c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - The cmdlets `Remove-PnPFile`, `Remove-PnPFolder`, `Move-PnPListItemToRecycleBin`, `Remove-PnPList`, `Remove-PnPListItem` and `Remove-PnPPage` will now return the corresponding recycle bin item if they get deleted to the recycle bin. Before they would not return anything. [#1783](https://github.com/pnp/powershell/pull/1783) - Cmdlets backed by a Microsoft Graph call will now return detailed information when the Graph call fails [#1923](https://github.com/pnp/powershell/pull/1923) - Changed `Get-PnPPlannerBucket` to return the buckets in the correct (reversed) order as you see them through the web interface [#1922](https://github.com/pnp/powershell/pull/1922) +- Changed `Connect-PnPOnline -Interactive` and `Connect-PnPOnline -DeviceLogin` to no longer suppress errors which should allow for certificate logins to be used ### Fixed diff --git a/src/Commands/Base/PnPConnection.cs b/src/Commands/Base/PnPConnection.cs index 985ffd856..f05db3cbc 100644 --- a/src/Commands/Base/PnPConnection.cs +++ b/src/Commands/Base/PnPConnection.cs @@ -244,7 +244,7 @@ internal static PnPConnection CreateWithDeviceLogin(string clientId, string url, { ClipboardService.SetText(deviceCodeResult.UserCode); messageWriter.WriteWarning($"\n\nCode {deviceCodeResult.UserCode} has been copied to your clipboard\n\n"); - BrowserHelper.GetWebBrowserPopup(deviceCodeResult.VerificationUrl, "Please log in", cancellationTokenSource: cancellationTokenSource, cancelOnClose: false); + BrowserHelper.GetWebBrowserPopup(deviceCodeResult.VerificationUrl, "Please log in", cancellationTokenSource: cancellationTokenSource, cancelOnClose: false, scriptErrorsSuppressed: false); } else { diff --git a/src/Commands/Utilities/BrowserHelper.cs b/src/Commands/Utilities/BrowserHelper.cs index 371bee9eb..e493eb3c0 100644 --- a/src/Commands/Utilities/BrowserHelper.cs +++ b/src/Commands/Utilities/BrowserHelper.cs @@ -172,7 +172,7 @@ internal enum UrlMatchType Contains } - internal static bool GetWebBrowserPopup(string siteUrl, string title, (string url, UrlMatchType matchType)[] closeUrls = null, bool noThreadJoin = false, CancellationTokenSource cancellationTokenSource = null, bool cancelOnClose = true) + internal static bool GetWebBrowserPopup(string siteUrl, string title, (string url, UrlMatchType matchType)[] closeUrls = null, bool noThreadJoin = false, CancellationTokenSource cancellationTokenSource = null, bool cancelOnClose = true, bool scriptErrorsSuppressed = true) { bool success = false; #if Windows @@ -186,12 +186,11 @@ internal static bool GetWebBrowserPopup(string siteUrl, string title, (string ur cancellationTokenSource?.Token.Register(() => { form.Invoke((System.Windows.Forms.MethodInvoker)(() => form.Close())); - //form.Close(); }); var browser = new System.Windows.Forms.WebBrowser { - ScriptErrorsSuppressed = true, + ScriptErrorsSuppressed = scriptErrorsSuppressed, Dock = System.Windows.Forms.DockStyle.Fill }; var assembly = typeof(BrowserHelper).Assembly; @@ -323,7 +322,7 @@ internal static void OpenBrowserForInteractiveLogin(string url, int port, bool u { if (OperatingSystem.IsWindows() && usePopup) { - BrowserHelper.GetWebBrowserPopup(url, "Please login", new[] { ($"http://localhost:{port}/?code=", BrowserHelper.UrlMatchType.StartsWith) }, noThreadJoin: true, cancellationTokenSource: cancellationTokenSource, cancelOnClose: true); + BrowserHelper.GetWebBrowserPopup(url, "Please login", new[] { ($"http://localhost:{port}/?code=", BrowserHelper.UrlMatchType.StartsWith) }, noThreadJoin: true, cancellationTokenSource: cancellationTokenSource, cancelOnClose: true, scriptErrorsSuppressed: false); } else { From 8bf4eaa3d905d2a1f4f46ad699b77f1d24a7743d Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Wed, 8 Jun 2022 17:36:29 +0200 Subject: [PATCH 312/458] Update Get-PnPList.md (#1951) Added example and parameter description --- documentation/Get-PnPList.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/documentation/Get-PnPList.md b/documentation/Get-PnPList.md index 8a538c751..bfbcbf584 100644 --- a/documentation/Get-PnPList.md +++ b/documentation/Get-PnPList.md @@ -20,6 +20,7 @@ Get-PnPList [[-Identity] ] [-ThrowExceptionIfListNotFound] ``` ## DESCRIPTION +This cmdlet returns lists in the current web. ## EXAMPLES @@ -51,6 +52,13 @@ Get-PnPList | Where-Object {$_.RootFolder.ServerRelativeUrl -like "/lists/*"} This examples shows how to do wildcard searches on the list URL. It returns all lists whose URL starts with "/lists/" This could also be used to search for strings inside of the URL. +### EXAMPLE 5 +```powershell +Get-PnPList -Includes HasUniqueRoleAssignments +``` + +This examples shows how to retrieve additional properties of the list. + ## PARAMETERS ### -Identity @@ -81,6 +89,20 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Includes +List of properties + +```yaml +Type: String[] +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Connection Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. @@ -97,4 +119,4 @@ Accept wildcard characters: False ## RELATED LINKS -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) From 245dc2cc857541651e5cdd2d324725cce0dda3e3 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Wed, 8 Jun 2022 18:39:08 +0300 Subject: [PATCH 313/458] Fix for gov cloud (#1944) --- src/Commands/Base/PnPSharePointCmdlet.cs | 2 +- src/Commands/Utilities/TeamsUtility.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Commands/Base/PnPSharePointCmdlet.cs b/src/Commands/Base/PnPSharePointCmdlet.cs index 42a6cce56..dc3a6b594 100644 --- a/src/Commands/Base/PnPSharePointCmdlet.cs +++ b/src/Commands/Base/PnPSharePointCmdlet.cs @@ -131,7 +131,7 @@ public string GraphAccessToken { if (Connection?.ConnectionMethod == ConnectionMethod.ManagedIdentity) { - return TokenHandler.GetManagedIdentityTokenAsync(this, HttpClient, $"https://graph.microsoft.com/").GetAwaiter().GetResult(); + return TokenHandler.GetManagedIdentityTokenAsync(this, HttpClient, $"https://{Connection.GraphEndPoint}/").GetAwaiter().GetResult(); } else { diff --git a/src/Commands/Utilities/TeamsUtility.cs b/src/Commands/Utilities/TeamsUtility.cs index ced2b994d..37b8792f2 100644 --- a/src/Commands/Utilities/TeamsUtility.cs +++ b/src/Commands/Utilities/TeamsUtility.cs @@ -719,7 +719,7 @@ public static async Task AddChannelMemberAsync(HttpClient htt { var channelMember = new TeamChannelMember { - UserIdentifier = $"https://graph.microsoft.com/v1.0/users('{upn}')", + UserIdentifier = $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/users('{upn}')", }; // The role for the user. Must be owner or empty. From f026018873cb7779cce5ee14567b68239fc12de0 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Wed, 8 Jun 2022 18:40:33 +0300 Subject: [PATCH 314/458] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f85b301c5..aa3f706eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,6 +68,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Connect-PnPOnline -ReturnConnection` also setting the current connection instead of just the returned connection [#1919](https://github.com/pnp/powershell/pull/1919) - Fixed `Disconnect-PnPOnline -Connection` also disconnecting other connections next to the provided connection [#1919](https://github.com/pnp/powershell/pull/1919) - Fixed `Set-PnPContext` not properly applying the provided context [#1919](https://github.com/pnp/powershell/pull/1919) +- Fixed Graph endpoints for non-commercial clouds for Managed Identity and Teams cmdlets [#1944](https://github.com/pnp/powershell/pull/1944) ### Removed - Removed `Get-PnPAvailableClientSideComponents`. Use `Get-PnPPageComponent -Page -ListAvailable` instead. [#1833](https://github.com/pnp/powershell/pull/1833) From 82a05a0dd8a2a76a7a5e8ac4511ac04842559ebd Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Wed, 8 Jun 2022 18:41:24 +0300 Subject: [PATCH 315/458] Improve Graph methods (#1938) --- .../Utilities/ServiceHealthUtility.cs | 42 +++---------------- src/Commands/Utilities/TeamsUtility.cs | 2 +- 2 files changed, 7 insertions(+), 37 deletions(-) diff --git a/src/Commands/Utilities/ServiceHealthUtility.cs b/src/Commands/Utilities/ServiceHealthUtility.cs index 84d0e6550..3f7f9612f 100644 --- a/src/Commands/Utilities/ServiceHealthUtility.cs +++ b/src/Commands/Utilities/ServiceHealthUtility.cs @@ -19,18 +19,8 @@ internal static class ServiceHealthUtility /// List with objects public static async Task> GetServiceUpdateMessagesAsync(HttpClient httpClient, string accessToken) { - var returnCollection = new List(); - var collection = await GraphHelper.GetAsync>(httpClient, $"v1.0/admin/serviceAnnouncement/messages", accessToken); - if (collection != null && collection.Items.Any()) - { - returnCollection = collection.Items.ToList(); - while (!string.IsNullOrEmpty(collection.NextLink)) - { - collection = await GraphHelper.GetAsync>(httpClient, collection.NextLink, accessToken); - returnCollection.AddRange(collection.Items); - } - } - return returnCollection; + var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/admin/serviceAnnouncement/messages", accessToken); + return collection; } /// @@ -214,18 +204,8 @@ public static async Task SetServiceUpdateMessageAsNotfavoriteByIdAsync(str /// List with objects public static async Task> GetServiceHealthIssuesAsync(HttpClient httpClient, string accessToken) { - var returnCollection = new List(); - var collection = await GraphHelper.GetAsync>(httpClient, $"v1.0/admin/serviceAnnouncement/issues", accessToken); - if (collection != null && collection.Items.Any()) - { - returnCollection = collection.Items.ToList(); - while (!string.IsNullOrEmpty(collection.NextLink)) - { - collection = await GraphHelper.GetAsync>(httpClient, collection.NextLink, accessToken); - returnCollection.AddRange(collection.Items); - } - } - return returnCollection; + var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/admin/serviceAnnouncement/issues", accessToken); + return collection; } /// @@ -253,18 +233,8 @@ public static async Task GetServiceHealthIssueByIdAsync(stri /// List with objects public static async Task> GetServiceCurrentHealthAsync(HttpClient httpClient, string accessToken) { - var returnCollection = new List(); - var collection = await GraphHelper.GetAsync>(httpClient, $"v1.0/admin/serviceAnnouncement/healthOverviews", accessToken); - if (collection != null && collection.Items.Any()) - { - returnCollection = collection.Items.ToList(); - while (!string.IsNullOrEmpty(collection.NextLink)) - { - collection = await GraphHelper.GetAsync>(httpClient, collection.NextLink, accessToken); - returnCollection.AddRange(collection.Items); - } - } - return returnCollection; + var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/admin/serviceAnnouncement/healthOverviews", accessToken); + return collection; } /// diff --git a/src/Commands/Utilities/TeamsUtility.cs b/src/Commands/Utilities/TeamsUtility.cs index 37b8792f2..eba4cc0ef 100644 --- a/src/Commands/Utilities/TeamsUtility.cs +++ b/src/Commands/Utilities/TeamsUtility.cs @@ -674,7 +674,7 @@ public static async Task GetMessageReplyAsync(HttpClien public static async Task UpdateChannelAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, TeamChannel channel) { - return await GraphHelper.PatchAsync(httpClient, accessToken, $"beta/teams/{groupId}/channels/{channelId}", channel); + return await GraphHelper.PatchAsync(httpClient, accessToken, $"v1.0/teams/{groupId}/channels/{channelId}", channel); } #endregion From 9d8704152a6fcca87e0de2b2ce08df65db66c35b Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Wed, 8 Jun 2022 18:44:12 +0300 Subject: [PATCH 316/458] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa3f706eb..b33efcc22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,7 +49,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - The cmdlets `Remove-PnPFile`, `Remove-PnPFolder`, `Move-PnPListItemToRecycleBin`, `Remove-PnPList`, `Remove-PnPListItem` and `Remove-PnPPage` will now return the corresponding recycle bin item if they get deleted to the recycle bin. Before they would not return anything. [#1783](https://github.com/pnp/powershell/pull/1783) - Cmdlets backed by a Microsoft Graph call will now return detailed information when the Graph call fails [#1923](https://github.com/pnp/powershell/pull/1923) - Changed `Get-PnPPlannerBucket` to return the buckets in the correct (reversed) order as you see them through the web interface [#1922](https://github.com/pnp/powershell/pull/1922) -- Changed `Connect-PnPOnline -Interactive` and `Connect-PnPOnline -DeviceLogin` to no longer suppress errors which should allow for certificate logins to be used +- Changed `Connect-PnPOnline -Interactive` and `Connect-PnPOnline -DeviceLogin` to no longer suppress errors which should allow for certificate logins to be used. [#1933](https://github.com/pnp/powershell/pull/1933) +- `Set-PnPTeamsChannel` now uses the Graph v1 endpoint, previously it used the beta endpoint. [#1938](https://github.com/pnp/powershell/pull/1938) +- Service Health cmdlets have been improved, now are consistent with other cmdlets to handle pagination [#1938](https://github.com/pnp/powershell/pull/1938) ### Fixed From b0005adce4502971c5581ef5ebd27cb81ecd23bb Mon Sep 17 00:00:00 2001 From: Marc D Anderson Date: Wed, 8 Jun 2022 13:13:55 -0400 Subject: [PATCH 317/458] Added batching badge, cleaned up MD --- documentation/Add-PnPListItem.md | 2 ++ documentation/Publish-PnPSyntexModel.md | 18 ++++++++++- documentation/Remove-PnPListItem.md | 15 ++++++++- .../Request-PnPSyntexClassifyAndExtract.md | 17 +++++++++- documentation/Set-PnPListItem.md | 30 ++++++++++++++---- documentation/Unpublish-PnPSyntexModel.md | 16 ++++++++++ pages/articles/batching.md | 12 ++++--- pages/images/batching/Batching.png | Bin 0 -> 8519 bytes 8 files changed, 96 insertions(+), 14 deletions(-) create mode 100644 pages/images/batching/Batching.png diff --git a/documentation/Add-PnPListItem.md b/documentation/Add-PnPListItem.md index 84a35c1e9..13d222dfc 100644 --- a/documentation/Add-PnPListItem.md +++ b/documentation/Add-PnPListItem.md @@ -9,6 +9,8 @@ title: Add-PnPListItem # Add-PnPListItem +:::image type="content" source="../pages/images/batching/Batching.png" alt-text="Supports batching"::: + ## SYNOPSIS Adds an item to the list and sets the creation time to the current date and time. The author is set to the current authenticated user executing the cmdlet. In order to set the author to a different user, please refer to Set-PnPListItem. diff --git a/documentation/Publish-PnPSyntexModel.md b/documentation/Publish-PnPSyntexModel.md index 0bd5c7e2f..03e30fbc0 100644 --- a/documentation/Publish-PnPSyntexModel.md +++ b/documentation/Publish-PnPSyntexModel.md @@ -9,7 +9,10 @@ online version: https://pnp.github.io/powershell/cmdlets/Get-PnPPage.html # Publish-PnPSyntexModel +:::image type="content" source="../pages/images/batching/Batching.png" alt-text="Supports batching"::: + ## SYNOPSIS + Publishes a SharePoint Syntex models to a list. This cmdlet only works when you've connected to a SharePoint Syntex Content Center site. @@ -17,23 +20,27 @@ This cmdlet only works when you've connected to a SharePoint Syntex Content Cent ## SYNTAX ### Single + ```powershell Publish-PnPSyntexModel -Model -ListWebUrl -List [-PublicationViewOption ] [-Connection ] [] ``` ### Batched + ```powershell Publish-PnPSyntexModel -Model -TargetSiteUrl -TargetWebServerRelativeUrl -TargetLibraryServerRelativeUrl -Batch [-PublicationViewOption ] [-Connection ] [] ``` ## DESCRIPTION + This command publishes a SharePoint Syntex content understanding models to a list. ## EXAMPLES ### EXAMPLE 1 + ```powershell Publish-PnPSyntexModel -Model "Invoice model" -ListWebUrl "https://contoso.sharepoint.com/sites/finance" -List "Documents" ``` @@ -41,6 +48,7 @@ Publish-PnPSyntexModel -Model "Invoice model" -ListWebUrl "https://contoso.share Publishes the content understanding model named "Invoice model" to the list named "Documents" in the /sites/finance web. ### EXAMPLE 2 + ```powershell Publish-PnPSyntexModel -Model "Invoice model" -TargetSiteUrl "https://contoso.sharepoint.com/sites/finance" -TargetWebServerRelativeUrl "/sites/finance" -TargetLibraryServerRelativeUrl "/sites/finance/shared%20documents" -Batch $batch ``` @@ -50,6 +58,7 @@ Adds the publishing of the content understanding model named "Invoice model" to ## PARAMETERS ### -Connection + Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. ```yaml @@ -64,6 +73,7 @@ Accept wildcard characters: False ``` ### -Model + The name or id of the SharePoint Syntex model. ```yaml @@ -78,6 +88,7 @@ Accept wildcard characters: False ``` ### -ListWebUrl + Url of the web hosting the list to publish the model to. ```yaml @@ -93,6 +104,7 @@ Accept wildcard characters: False ``` ### -List + The name or id of the list to publish the model to. ```yaml @@ -107,6 +119,7 @@ Accept wildcard characters: False ``` ### -TargetSiteUrl + The fully qualified URL of the site collection hosting the library to publish the model to. ```yaml @@ -122,6 +135,7 @@ Accept wildcard characters: False ``` ### -TargetWebServerRelativeUrl + The server relative url of the web hosting the library to publish the model to. ```yaml @@ -137,6 +151,7 @@ Accept wildcard characters: False ``` ### -TargetLibraryServerRelativeUrl + The server relative url of the library to publish the model to. ```yaml @@ -152,6 +167,7 @@ Accept wildcard characters: False ``` ### -Batch + The batch to add this publish request to. ```yaml @@ -167,6 +183,7 @@ Accept wildcard characters: False ``` ### -PublicationViewOption + The view options to apply when publishing the model to the list. ```yaml @@ -184,4 +201,3 @@ Accept wildcard characters: False ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - diff --git a/documentation/Remove-PnPListItem.md b/documentation/Remove-PnPListItem.md index 1c203ccb8..97be0cb4d 100644 --- a/documentation/Remove-PnPListItem.md +++ b/documentation/Remove-PnPListItem.md @@ -9,17 +9,22 @@ online version: https://pnp.github.io/powershell/cmdlets/Remove-PnPListItem.html # Remove-PnPListItem +:::image type="content" source="../pages/images/batching/Batching.png" alt-text="Supports batching"::: + ## SYNOPSIS + Deletes an item from a list ## SYNTAX ### Single + ```powershell Remove-PnPListItem [-List] -Identity [-Recycle] [-Force] ``` ### Batched + ```powershell Remove-PnPListItem [-List] -Identity -Batch [-Recycle] ``` @@ -36,6 +41,7 @@ Remove-PnPListItem -List "Demo List" -Identity "1" -Force Removes the listitem with id "1" from the "Demo List" list ### EXAMPLE 2 + ```powershell Remove-PnPListItem -List "Demo List" -Identity "1" -Force -Recycle ``` @@ -43,6 +49,7 @@ Remove-PnPListItem -List "Demo List" -Identity "1" -Force -Recycle Removes the listitem with id "1" from the "Demo List" list and saves it in the Recycle Bin ### EXAMPLE 3 + ```powershell $batch = New-PnPBatch 1..50 | Foreach-Object{Remove-PnPListItem -List "DemoList" -Identity $_ -Batch $batch} @@ -52,6 +59,7 @@ Invoke-PnPBatch -Batch $batch Removes all the items with Id 1 to Id 50 in the "Demo List" list ### EXAMPLE 4 + ```powershell Remove-PnPListItem -List "Demo List" ``` @@ -61,6 +69,7 @@ Removes all items from the "Demlo List" list after asking for confirmation ## PARAMETERS ### -Connection + Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. ```yaml @@ -75,6 +84,7 @@ Accept wildcard characters: False ``` ### -Force + Specifying the Force parameter will skip the confirmation question ```yaml @@ -89,6 +99,7 @@ Accept wildcard characters: False ``` ### -Identity + The ID of the listitem, or actual ListItem object ```yaml @@ -103,6 +114,7 @@ Accept wildcard characters: False ``` ### -List + The ID, Title or Url of the list ```yaml @@ -117,6 +129,7 @@ Accept wildcard characters: False ``` ### -Recycle + When provided, items will be sent to the recycle bin. When omitted, items will permanently be deleted. ```yaml @@ -131,6 +144,7 @@ Accept wildcard characters: False ``` ### -Batch + Batch object used to add items in a batched manner. See examples on how to use this. ```yaml @@ -147,4 +161,3 @@ Accept wildcard characters: False ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - diff --git a/documentation/Request-PnPSyntexClassifyAndExtract.md b/documentation/Request-PnPSyntexClassifyAndExtract.md index b0dc1aead..1619ee721 100644 --- a/documentation/Request-PnPSyntexClassifyAndExtract.md +++ b/documentation/Request-PnPSyntexClassifyAndExtract.md @@ -9,24 +9,30 @@ online version: https://pnp.github.io/powershell/cmdlets/Get-PnPPage.html # Request-PnPSyntexClassifyAndExtract +:::image type="content" source="../pages/images/batching/Batching.png" alt-text="Supports batching"::: + ## SYNOPSIS + Requests for a file, folder or all files in a library to be classified and extracted via the published SharePoint Syntex models on the libraries hosting the files. ## SYNTAX ### File + ```powershell Request-PnPSyntexClassifyAndExtract -FileUrl [-Batch ] [-Connection ] [] ``` ### Folder + ```powershell Request-PnPSyntexClassifyAndExtract -Folder [-Connection ] [] ``` ### List + ```powershell Request-PnPSyntexClassifyAndExtract -List [-OffPeak ] [-Force ] [-Connection ] [] @@ -41,6 +47,7 @@ When the list contains more than 5000 files or when using the folder parameter t ## EXAMPLES ### EXAMPLE 1 + ```powershell Request-PnPSyntexClassifyAndExtract -FileUrl "/sites/finance/invoices/invoice1.docx" ``` @@ -48,6 +55,7 @@ Request-PnPSyntexClassifyAndExtract -FileUrl "/sites/finance/invoices/invoice1.d Requests the classification and extraction of invoice1.docx in library "Invoices". ### EXAMPLE 2 + ```powershell Request-PnPSyntexClassifyAndExtract -List "Invoices" ``` @@ -55,6 +63,7 @@ Request-PnPSyntexClassifyAndExtract -List "Invoices" Requests the classification and extraction of all files in library "Invoices" that never were classified and extracted before. ### EXAMPLE 3 + ```powershell Request-PnPSyntexClassifyAndExtract -Folder (Get-PnPFolder -Url "invoices/Q1/jan") ``` @@ -64,6 +73,7 @@ Requests the classification and extraction of all files in the folder "jan" in l ## PARAMETERS ### -Connection + Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. ```yaml @@ -78,6 +88,7 @@ Accept wildcard characters: False ``` ### -List + The name or list holding the files to classify and extract ```yaml @@ -92,6 +103,7 @@ Accept wildcard characters: False ``` ### -Force + If set, then all files (even if classified and extracted before) are classified and extracted. If the list contains more than 5000 items this option will not apply and off-peak processing is used. ```yaml @@ -106,6 +118,7 @@ Accept wildcard characters: False ``` ### -OffPeak + If set, then the files to classify are sent to the off peak queue without enumerating them. If the list contains more than 5000 items then off-peak processing is always used. ```yaml @@ -120,6 +133,7 @@ Accept wildcard characters: False ``` ### -Folder + The folder holding the files to classify and extract. When using this parameter, files will be send to the off peak queue. ```yaml @@ -134,6 +148,7 @@ Accept wildcard characters: False ``` ### -FileUrl + The server relative URL of the file to be classified and extracted. ```yaml @@ -149,6 +164,7 @@ Accept wildcard characters: False ``` ### -Batch + The batch to add this file classification and extraction request to. ```yaml @@ -163,7 +179,6 @@ Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` - ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) diff --git a/documentation/Set-PnPListItem.md b/documentation/Set-PnPListItem.md index 0b6493c33..70c5920fc 100644 --- a/documentation/Set-PnPListItem.md +++ b/documentation/Set-PnPListItem.md @@ -9,29 +9,34 @@ online version: https://pnp.github.io/powershell/cmdlets/Set-PnPListItem.html # Set-PnPListItem +:::image type="content" source="../pages/images/batching/Batching.png" alt-text="Supports batching"::: + ## SYNOPSIS + Updates a list item ## SYNTAX ### Single + ```powershell Set-PnPListItem [-List] -Identity [-ContentType ] [-Values ] [-UpdateType ] [-Label ] [-ClearLabel] [-Connection ] ``` ### Batched + ```powershell Set-PnPListItem [-List] -Identity -Batch [-ContentType ] [-Values ] [-UpdateType [-UpdateOverwriteVersion] [-Connection ] ``` - ## DESCRIPTION ## EXAMPLES ### EXAMPLE 1 + ```powershell Set-PnPListItem -List "Demo List" -Identity 1 -Values @{"Title" = "Test Title"; "Category"="Test Category"} ``` @@ -39,6 +44,7 @@ Set-PnPListItem -List "Demo List" -Identity 1 -Values @{"Title" = "Test Title"; Sets fields value in the list item with ID 1 in the "Demo List". It sets both the Title and Category fields with the specified values. Notice, use the internal names of fields. ### EXAMPLE 2 + ```powershell Set-PnPListItem -List "Demo List" -Identity 1 -ContentType "Company" -Values @{"Title" = "Test Title"; "Category"="Test Category"} ``` @@ -46,6 +52,7 @@ Set-PnPListItem -List "Demo List" -Identity 1 -ContentType "Company" -Values @{" Sets fields value in the list item with ID 1 in the "Demo List". It sets the content type of the item to "Company" and it sets both the Title and Category fields with the specified values. Notice, use the internal names of fields. ### EXAMPLE 3 + ```powershell Set-PnPListItem -List "Demo List" -Identity $item -Values @{"Title" = "Test Title"; "Category"="Test Category"} ``` @@ -53,6 +60,7 @@ Set-PnPListItem -List "Demo List" -Identity $item -Values @{"Title" = "Test Titl Sets fields value in the list item which has been retrieved by for instance Get-PnPListItem. It sets the content type of the item to "Company" and it sets both the Title and Category fields with the specified values. Notice, use the internal names of fields. ### EXAMPLE 4 + ```powershell Set-PnPListItem -List "Demo List" -Identity 1 -Label "Public" ``` @@ -60,6 +68,7 @@ Set-PnPListItem -List "Demo List" -Identity 1 -Label "Public" Sets the retention label in the list item with ID 1 in the "Demo List". ### EXAMPLE 5 + ```powershell $batch = New-PnPBatch for($i=1;$i -lt 100;$i++) @@ -72,6 +81,7 @@ Invoke-PnPBatch -Batch $batch This example updates the items with ids 0 to 100 with a new title in a batched manner. ### EXAMPLE 6 + ```powershell Set-PnPListItem -List "Demo List" -Identity 1 -Values @{"Editor"="testuser@domain.com"} -UpdateType UpdateOverwriteVersion ``` @@ -81,6 +91,7 @@ This example updates the modified by value of the list item and does not increas ## PARAMETERS ### -Batch + Optional batch object used to add items in a batched manner. See examples on how to use this. ```yaml @@ -94,6 +105,7 @@ Accept wildcard characters: False ``` ### -Connection + Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. ```yaml @@ -108,6 +120,7 @@ Accept wildcard characters: False ``` ### -ContentType + Specify either the name, ID or an actual content type ```yaml @@ -122,6 +135,7 @@ Accept wildcard characters: False ``` ### -Identity + The ID of the listitem, or actual ListItem object ```yaml @@ -136,6 +150,7 @@ Accept wildcard characters: False ``` ### -Label + The name of the retention label. ```yaml @@ -150,6 +165,7 @@ Accept wildcard characters: False ``` ### -List + The ID, Title or Url of the list. ```yaml @@ -164,7 +180,9 @@ Accept wildcard characters: False ``` ### -UpdateType -Specifies the update type to use when updating the listitem. Possible values are "Update", "SystemUpdate", "UpdateOverwriteVersion". + +Specifies the update type to use when updating the listitem. Possible values are "Update", "SystemUpdate", "UpdateOverwriteVersion". + * Update: Sets field values and creates a new version if versioning is enabled for the list * SystemUpdate: Sets field values and does not create a new version. Any events on the list will trigger. * UpdateOverwriteVersion: Sets field values and does not create a new version. No events on the list will trigger. @@ -181,6 +199,7 @@ Accept wildcard characters: False ``` ### -UpdateOverwriteVersion + Update the item without creating a new version. It will not trigger events registered on the list. ```yaml @@ -195,6 +214,7 @@ Accept wildcard characters: False ``` ### -ClearLabel + Clears the retention label of the item. ```yaml @@ -209,6 +229,7 @@ Accept wildcard characters: False ``` ### -Values + Use the internal names of the fields when specifying field names. Single line of text: -Values @{"TextField" = "Title New"} @@ -260,9 +281,6 @@ Accept pipeline input: False Accept wildcard characters: False ``` - - ## RELATED LINKS -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/documentation/Unpublish-PnPSyntexModel.md b/documentation/Unpublish-PnPSyntexModel.md index ccaebec3b..e02b9d97c 100644 --- a/documentation/Unpublish-PnPSyntexModel.md +++ b/documentation/Unpublish-PnPSyntexModel.md @@ -9,7 +9,10 @@ online version: https://pnp.github.io/powershell/cmdlets/Get-PnPPage.html # Unpublish-PnPSyntexModel +:::image type="content" source="../pages/images/batching/Batching.png" alt-text="Supports batching"::: + ## SYNOPSIS + Unpublishes a SharePoint Syntex model from a list. This cmdlet only works when you've connected to a SharePoint Syntex Content Center site. @@ -17,23 +20,27 @@ This cmdlet only works when you've connected to a SharePoint Syntex Content Cent ## SYNTAX ### Single + ```powershell Unpublish-PnPSyntexModel -Model -ListWebUrl -List [-Connection ] [] ``` ### Batched + ```powershell Unpublish-PnPSyntexModel -Model -TargetSiteUrl -TargetWebServerRelativeUrl -TargetLibraryServerRelativeUrl -Batch [-Connection ] [] ``` ## DESCRIPTION + This command unpublishes a SharePoint Syntex content understanding models from a list. ## EXAMPLES ### EXAMPLE 1 + ```powershell Unpublish-PnPSyntexModel -Model "Invoice model" -ListWebUrl "https://contoso.sharepoint.com/sites/finance" -List "Documents" ``` @@ -41,6 +48,7 @@ Unpublish-PnPSyntexModel -Model "Invoice model" -ListWebUrl "https://contoso.sha Unpublishes the content understanding model named "Invoice model" from the list named "Documents" in the /sites/finance web. ### EXAMPLE 2 + ```powershell Unpublish-PnPSyntexModel -Model "Invoice model" -TargetSiteUrl "https://contoso.sharepoint.com/sites/finance" -TargetWebServerRelativeUrl "/sites/finance" -TargetLibraryServerRelativeUrl "/sites/finance/shared%20documents" -Batch $batch ``` @@ -50,6 +58,7 @@ Adds the unpublishing of the content understanding model named "Invoice model" f ## PARAMETERS ### -Connection + Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. ```yaml @@ -64,6 +73,7 @@ Accept wildcard characters: False ``` ### -Model + The name or id of the SharePoint Syntex model. ```yaml @@ -78,6 +88,7 @@ Accept wildcard characters: False ``` ### -ListWebUrl + Url of the web hosting the list to unpublish the model from. ```yaml @@ -93,6 +104,7 @@ Accept wildcard characters: False ``` ### -List + The name or id of the list to unpublish the model from. ```yaml @@ -107,6 +119,7 @@ Accept wildcard characters: False ``` ### -TargetSiteUrl + The fully qualified URL of the site collection hosting the library to unpublish the model from. ```yaml @@ -122,6 +135,7 @@ Accept wildcard characters: False ``` ### -TargetWebServerRelativeUrl + The server relative url of the web hosting the library to unpublish the model from. ```yaml @@ -137,6 +151,7 @@ Accept wildcard characters: False ``` ### -TargetLibraryServerRelativeUrl + The server relative url of the library to unpublish the model from. ```yaml @@ -152,6 +167,7 @@ Accept wildcard characters: False ``` ### -Batch + The batch to add this unpublish request to. ```yaml diff --git a/pages/articles/batching.md b/pages/articles/batching.md index 8506e3574..160b915e4 100644 --- a/pages/articles/batching.md +++ b/pages/articles/batching.md @@ -1,6 +1,6 @@ # Batching in PnP PowerShell -Some cmdlets support batching, but there are a few things to point our here. Let me first explain the difference between a normal request and a batched request. +Some cmdlets support batching, but there are a few things to point our here. Let me first explain the difference between a normal request and a batched request. ## Adding items without batching (CSOM) @@ -15,7 +15,7 @@ If you add a list item with Add-PnPListItem without batching, the following happ 1. We loop through the values you specified to set on the item and depending on the type of field we format the value ready to be added to the item (this -can- introduce additional CSOM requests in the case of taxonomy fields for instance) 1. We update the item (this will be a CSOM request to the server) -All in all you see we make around 6 requests, allthough the exact number depends a bit on what type of fields you are setting. So if you loop through 100 items, you get at least 600 requests. +All in all you see we make around 6 requests, although the exact number depends a bit on what type of fields you are setting. So if you loop through 100 items, you get at least 600 requests. ## Adding a item with batching (REST) @@ -33,18 +33,20 @@ When you use batching, these number of requests are much lower. This is what hap This means that for every new item there is one request + a few initial request to retrieve the list. -Then when you execute `Invoke-Batch` we will check out how many items there are in the batch and will split them up accordingly to follow the request limits provided. You'll see that large batches are split up in chunks of 100 requests. This means that if you, say add 100 items, you will issue around 6 requests -in total-. +Then when you execute `Invoke-Batch` we will check out how many items there are in the batch and will split them up accordingly to follow the request limits provided. You'll see that large batches are split up in chunks of 100 requests. This means that if you, say add 100 items, you will issue around 6 requests -in total-. ## Large batches and exceptions + Depending on the type of requests you are making batches can be split up in 'chunks'. For instance, if the tasks you perform are towards SharePoint and behind the scenes use the REST api of SharePoint a batch larger then 100 requests will be split up in chunks of 100 requests. When behind the scenes the Microsoft Graph is being called then the batch requests will be split up in chunks of 20 requests. -For instance, removing 520 list items will result in 6 separate requests, the first 5 containing 100 remove requests, the last one containing 20 remove requests. +For instance, removing 520 list items will result in 6 separate requests, the first 5 containing 100 remove requests, the last one containing 20 remove requests. If during one of these chunks an exception occurs (for instance you are trying to delete an item that does not exist), by default we will continue processing the remaining batch chunks. If you want to stop processing these remaining batches immediately after an exception occurs, use the `-StopOnException` switch on `Invoke-PnPBatch` cmdlet. ## Cmdlets that support batching -As of release 1.7.63-nightly the following cmdlets support batching: +The following cmdlets support batching. (This support began with the 1.7.63-nightly release.) All cmdlets which support batching will display the batching badge: +:::image type="content" source="../images/batching/Batching.png" alt-text="Supports batching"::: * [`Add-PnPListItem`](/powershell/cmdlets/Add-PnPListItem.html) * [`Set-PnPListItem`](/powershell/cmdlets/Set-PnPListItem.html) diff --git a/pages/images/batching/Batching.png b/pages/images/batching/Batching.png new file mode 100644 index 0000000000000000000000000000000000000000..2fee53aa1de0a5c45879060f241cbf8a82b785a9 GIT binary patch literal 8519 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 F{{WW|`nLc8 literal 0 HcmV?d00001 From 6df5fedea8b0afa693ad22dfee4cf40846862394 Mon Sep 17 00:00:00 2001 From: Yuriy Samorodov Date: Wed, 8 Jun 2022 22:37:01 +0300 Subject: [PATCH 318/458] Update AddTeamsUser.cs (#1953) Add-PnPTeamUser should treat Channel param as optional --- src/Commands/Teams/AddTeamsUser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Commands/Teams/AddTeamsUser.cs b/src/Commands/Teams/AddTeamsUser.cs index da595a475..190c26bb1 100644 --- a/src/Commands/Teams/AddTeamsUser.cs +++ b/src/Commands/Teams/AddTeamsUser.cs @@ -18,7 +18,7 @@ public class AddTeamsUser : PnPGraphCmdlet [Parameter(Mandatory = true, ParameterSetName = ParamSet_ByMultipleUsers)] public TeamsTeamPipeBind Team; - [Parameter(Mandatory = true, ParameterSetName = ParamSet_ByUser)] + [Parameter(Mandatory = false, ParameterSetName = ParamSet_ByUser)] public TeamsChannelPipeBind Channel; [Parameter(Mandatory = true, ParameterSetName = ParamSet_ByUser)] @@ -77,4 +77,4 @@ protected override void ExecuteCmdlet() } } } -} \ No newline at end of file +} From 06d0ce8ed74e0cc369298919bcc2a50cfb3f5665 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Wed, 8 Jun 2022 22:39:09 +0300 Subject: [PATCH 319/458] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b33efcc22..9b30a9c1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,6 +71,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Disconnect-PnPOnline -Connection` also disconnecting other connections next to the provided connection [#1919](https://github.com/pnp/powershell/pull/1919) - Fixed `Set-PnPContext` not properly applying the provided context [#1919](https://github.com/pnp/powershell/pull/1919) - Fixed Graph endpoints for non-commercial clouds for Managed Identity and Teams cmdlets [#1944](https://github.com/pnp/powershell/pull/1944) +- Fixed `Add-PnPTeamsUser`, the parameter `-Channel` is now not required. [#1953](https://github.com/pnp/powershell/pull/1953) ### Removed - Removed `Get-PnPAvailableClientSideComponents`. Use `Get-PnPPageComponent -Page -ListAvailable` instead. [#1833](https://github.com/pnp/powershell/pull/1833) From 8bfaf7d43e4e5354eb4c62ca9053bf91ceca093c Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Thu, 9 Jun 2022 01:02:21 +0200 Subject: [PATCH 320/458] Fix parse error --- src/Commands/Model/Planner/PlannerTask.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Commands/Model/Planner/PlannerTask.cs b/src/Commands/Model/Planner/PlannerTask.cs index bf8f4a0f7..50b617ef9 100644 --- a/src/Commands/Model/Planner/PlannerTask.cs +++ b/src/Commands/Model/Planner/PlannerTask.cs @@ -32,7 +32,7 @@ public class PlannerTask [JsonConverter(typeof(DateTimeISO8601Converter))] public DateTime? CompletedDateTime { get; set; } - public string CompletedBy { get; set; } + public IdentitySet CompletedBy { get; set; } public int? ReferenceCount { get; set; } public int? CheckListItemCount { get; set; } public int? ActiveChecklistItemCount { get; set; } From 3b697afb8c8bf039ba6be02073a9f96fac62f330 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 9 Jun 2022 03:48:43 +0000 Subject: [PATCH 321/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index a51107490..4c6080b57 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -690847cb851c618cdbe2d02d933cb5768490b6bc \ No newline at end of file +71581e5b9c35a1e1e79106051d6b12cef1f4134b \ No newline at end of file diff --git a/version.txt b/version.txt index 3ebe6943a..450854796 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.42 \ No newline at end of file +1.10.43 \ No newline at end of file From 94f8f3010e28646a5914d3c48d3aa39e3317953f Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 9 Jun 2022 17:11:53 +0300 Subject: [PATCH 322/458] Update CHANGELOG.md Updated contributors --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b30a9c1a..c123b3d46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,6 +79,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Contributors +- Yuriy Samorodov [YuriySamorodov] - Arleta Wanat [PowershellScripts] - Marc D Anderson [sympmarc] - dc366 [dc366] From 7a87ce3e11103285566a903213610c24994c90fe Mon Sep 17 00:00:00 2001 From: = <=> Date: Thu, 9 Jun 2022 23:42:47 +0200 Subject: [PATCH 323/458] Small additions to the code --- documentation/Add-PnPListItemAttachment.md | 39 ++++++------ documentation/Get-PnPListItemAttachments.md | 62 +++++++++---------- documentation/Remove-PnPListItemAttachment.md | 34 +++++----- ...ttachments.cs => GetListItemAttachment.cs} | 14 ++--- .../Lists/RemoveListItemAttachment.cs | 36 ++++++++--- 5 files changed, 102 insertions(+), 83 deletions(-) rename src/Commands/Lists/{GetListItemAttachments.cs => GetListItemAttachment.cs} (84%) diff --git a/documentation/Add-PnPListItemAttachment.md b/documentation/Add-PnPListItemAttachment.md index 2c9f37034..3393c9242 100644 --- a/documentation/Add-PnPListItemAttachment.md +++ b/documentation/Add-PnPListItemAttachment.md @@ -24,13 +24,15 @@ Add-PnPListItemAttachment [-List] [-Identity] Add-PnPListItemAttachment [-List] [-Identity] [-FileName ] [-Stream ] [-Connection ] [] ``` -### Upload attachment file from text +### Create attachment file from text ```powershell Add-PnPListItemAttachment [-List] [-Identity] [-FileName ] [-Content ] [-Connection ] [] ``` ## DESCRIPTION +This cmdlet allows adding a file as an attachment to a list item in a SharePoint Online list. + ## EXAMPLES ### EXAMPLE 1 @@ -57,20 +59,6 @@ Adds a new attachment to the list item with Id "1" in the "Demo List" SharePoint ## PARAMETERS -### -Connection -Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. - -```yaml -Type: PnPConnection -Parameter Sets: (All) - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - ### -Content Specify text of the attachment for the list item. @@ -114,7 +102,7 @@ Accept wildcard characters: False ``` ### -FileName -Name for attachment file +Filename to give to the attachment file on SharePoint ```yaml Type: String @@ -141,9 +129,8 @@ Accept pipeline input: False Accept wildcard characters: False ``` - ### -List -The ID, Title or Url of the list. +The ID, Title or Url of the list. Note that when providing the name of the list, the name is case-sensitive. ```yaml Type: ListPipeBind @@ -157,7 +144,7 @@ Accept wildcard characters: False ``` ### -Identity -The ID of the listitem, or actual ListItem object +The ID of the listitem, or actual ListItem object to add the attachment to. ```yaml Type: ListItemPipeBind @@ -170,6 +157,20 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/documentation/Get-PnPListItemAttachments.md b/documentation/Get-PnPListItemAttachments.md index 2150b881a..ca59de1ea 100644 --- a/documentation/Get-PnPListItemAttachments.md +++ b/documentation/Get-PnPListItemAttachments.md @@ -2,12 +2,12 @@ Module Name: PnP.PowerShell schema: 2.0.0 applicable: SharePoint Online -online version: https://pnp.github.io/powershell/cmdlets/Get-PnPListItemAttachments.html +online version: https://pnp.github.io/powershell/cmdlets/Get-PnPListItemAttachment.html external help file: PnP.PowerShell.dll-Help.xml -title: Get-PnPListItemAttachments +title: Get-PnPListItemAttachment --- -# Get-PnPListItemAttachments +# Get-PnPListItemAttachment ## SYNOPSIS Downloads the list item attachments to a specified path on the file system. @@ -16,7 +16,7 @@ Downloads the list item attachments to a specified path on the file system. ### Get attachments from list item ```powershell -Get-PnPListItemAttachments [-List] [-Identity] [-Path ] [-Force ] [-Connection ] [] +Get-PnPListItemAttachment [-List] [-Identity] [-Path ] [-Force ] [-Connection ] [] ``` ## DESCRIPTION @@ -25,25 +25,25 @@ Get-PnPListItemAttachments [-List] [-Identity] ### EXAMPLE 1 ```powershell -Get-PnPListItemAttachments -List "Demo List" -Identity 1 -Path "C:\temp" +Get-PnPListItemAttachment -List "Demo List" -Identity 1 -Path "C:\temp" ``` -Download all attachments from the list item with Id "1" in the "Demo List" SharePoint list and store it in the temp folder. +Downloads all attachments from the list item with Id "1" in the "Demo List" SharePoint list and stores them in the temp folder. ### EXAMPLE 2 ```powershell -Get-PnPListItemAttachments -List "Demo List" -Identity 1 -Path "C:\temp" -Force +Get-PnPListItemAttachment -List "Demo List" -Identity 1 -Path "C:\temp" -Force ``` -Download all attachments from the list item with Id "1" in the "Demo List" SharePoint list and store it in the temp folder and overwrite the files if they already exist. +Downloads all attachments from the list item with Id "1" in the "Demo List" SharePoint list and stores them in the temp folder overwriting the files if they already exist. ## PARAMETERS -### -Connection -Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. +### -Path +Specify the path on the local file system to download the list item attachments to. ```yaml -Type: PnPConnection +Type: String Parameter Sets: (All) Required: False @@ -53,56 +53,56 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -Path -Specify the path on the local file system to download the list item attachments. +### -List +The ID, Title or Url of the list. Note that when providing the name of the list, the name is case-sensitive. ```yaml -Type: String +Type: ListPipeBind Parameter Sets: (All) -Required: False -Position: Named +Required: True +Position: 0 Default value: None -Accept pipeline input: False +Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` -### -Force -Specifying the Force parameter will skip the confirmation question and overwrite the file in file system. +### -Identity +The ID of the listitem, or actual ListItem object ```yaml -Type: String +Type: ListItemPipeBind Parameter Sets: (All) -Required: False +Required: True Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False ``` -### -List -The ID, Title or Url of the list. +### -Force +Specifying the Force parameter will skip the confirmation question and overwrite the files on the local disk, if they already exist. ```yaml -Type: ListPipeBind +Type: String Parameter Sets: (All) -Required: True -Position: 0 +Required: False +Position: Named Default value: None -Accept pipeline input: True (ByValue) +Accept pipeline input: False Accept wildcard characters: False ``` -### -Identity -The ID of the listitem, or actual ListItem object +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. ```yaml -Type: ListItemPipeBind +Type: PnPConnection Parameter Sets: (All) -Required: True +Required: False Position: Named Default value: None Accept pipeline input: False diff --git a/documentation/Remove-PnPListItemAttachment.md b/documentation/Remove-PnPListItemAttachment.md index 6eca0b962..4aa7e2607 100644 --- a/documentation/Remove-PnPListItemAttachment.md +++ b/documentation/Remove-PnPListItemAttachment.md @@ -65,22 +65,8 @@ Permanently deletes all attachments from the list item with Id "1" in the "Demo ## PARAMETERS -### -Connection -Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. - -```yaml -Type: PnPConnection -Parameter Sets: (All) - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - ### -FileName -Specify name of the attachment to delete from list item +Specify name of the attachment to delete from list item. The filename is not case sensitive. ```yaml Type: String @@ -94,7 +80,7 @@ Accept wildcard characters: False ``` ### -All -Specify if you want to delete all list item attachments. +Specify if you want to delete or recycle all the list item attachments. ```yaml Type: SwitchParameter @@ -136,7 +122,7 @@ Accept wildcard characters: False ``` ### -List -The ID, Title or Url of the list. +The ID, Title or Url of the list. Note that when providing the name of the list, the list name is case-sensitive. ```yaml Type: ListPipeBind @@ -163,6 +149,20 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/src/Commands/Lists/GetListItemAttachments.cs b/src/Commands/Lists/GetListItemAttachment.cs similarity index 84% rename from src/Commands/Lists/GetListItemAttachments.cs rename to src/Commands/Lists/GetListItemAttachment.cs index 7856e48e7..e8ddc5c74 100644 --- a/src/Commands/Lists/GetListItemAttachments.cs +++ b/src/Commands/Lists/GetListItemAttachment.cs @@ -7,8 +7,8 @@ namespace PnP.PowerShell.Commands.Lists { - [Cmdlet(VerbsCommon.Get, "PnPListItemAttachments")] - public class GetListItemAttachments : PnPWebCmdlet + [Cmdlet(VerbsCommon.Get, "PnPListItemAttachment")] + public class GetListItemAttachment : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] public ListPipeBind List; @@ -29,14 +29,14 @@ protected override void ExecuteCmdlet() if (list == null) { - throw new PSArgumentException($"Cannot find list provided through -{nameof(List)}", nameof(List)); + throw new PSArgumentException($"Cannot find the list provided through -{nameof(List)}", nameof(List)); } IListItem item = Identity.GetListItem(list); if (item == null) { - throw new PSArgumentException($"Cannot find list item provided through -{nameof(Identity)}", nameof(Identity)); + throw new PSArgumentException($"Cannot find the list item provided through -{nameof(Identity)}", nameof(Identity)); } if (string.IsNullOrEmpty(Path)) @@ -53,9 +53,9 @@ protected override void ExecuteCmdlet() item.EnsureProperties(i => i.AttachmentFiles); - var attachmentFilesCollection = item.AttachmentFiles.AsRequested(); + var attachmentFilesCollection = item.AttachmentFiles.AsRequested().ToArray(); - if (attachmentFilesCollection?.ToList().Count == 0) + if (attachmentFilesCollection.Length == 0) { WriteWarning($"No attachments found for the list item provided through -{nameof(Identity)}"); } @@ -68,7 +68,7 @@ protected override void ExecuteCmdlet() if (System.IO.File.Exists(fileOut) && !Force) { - WriteWarning($"File '{attachment.FileName}' exists already in the specified path. Use the -Force parameter to overwrite the file."); + WriteWarning($"File '{attachment.FileName}' exists already in the specified path. This file will be skipped. Use the -Force parameter to overwrite the file in the specified path."); } else { diff --git a/src/Commands/Lists/RemoveListItemAttachment.cs b/src/Commands/Lists/RemoveListItemAttachment.cs index 6f58f20d4..b29b24c39 100644 --- a/src/Commands/Lists/RemoveListItemAttachment.cs +++ b/src/Commands/Lists/RemoveListItemAttachment.cs @@ -2,6 +2,7 @@ using PnP.Core.QueryModel; using PnP.PowerShell.Commands.Base.PipeBinds; using PnP.PowerShell.Commands.Properties; +using System; using System.Linq; using System.Management.Automation; @@ -53,11 +54,18 @@ protected override void ExecuteCmdlet() } item.EnsureProperties(i => i.AttachmentFiles); - var files = item.AttachmentFiles.AsRequested(); + var files = item.AttachmentFiles.AsRequested().ToArray(); var removeText = Recycle.IsPresent ? "Recycle" : "Remove"; + + if(files.Length == 0) + { + WriteWarning($"No attachments found on the list item that can be {removeText.ToLower()}d"); + return; + } + if (All.IsPresent) { - if (Force || ShouldContinue($"{removeText} all list item attachments?", Resources.Confirm)) + if (Force || ShouldContinue($"{removeText} {(files.Length != 1 ? $"all {files.Length}" : "the")} list item attachment{(files.Length != 1 ? "s" : "")}?", Resources.Confirm)) { foreach (var file in files.ToList()) { @@ -74,15 +82,25 @@ protected override void ExecuteCmdlet() } else { - if (Force || ShouldContinue($"{removeText} all list item attachments?", Resources.Confirm)) + // Try to locate the attachment file that needs to be deleted + var fileToDelete = files.FirstOrDefault(i => i.FileName.Equals(FileName, StringComparison.InvariantCultureIgnoreCase)); + + if(fileToDelete == null) { - if (Recycle.IsPresent) - { - files?.ToList().Where(i => i.FileName == FileName)?.FirstOrDefault()?.Recycle(); - } - else + WriteWarning($"No attachment found with the name '{FileName}'"); + } + else + { + if (Force || ShouldContinue($"{removeText} list item attachment '{fileToDelete.FileName}'?", Resources.Confirm)) { - files?.ToList().Where(i => i.FileName == FileName)?.FirstOrDefault()?.Delete(); + if (Recycle.IsPresent) + { + fileToDelete.Recycle(); + } + else + { + fileToDelete.Delete(); + } } } } From dcbd28eea0ee0ec7c09cec5769f47265532eb8cf Mon Sep 17 00:00:00 2001 From: = <=> Date: Thu, 9 Jun 2022 23:44:36 +0200 Subject: [PATCH 324/458] Changed cmdlet name to singular to align with other cmdlets --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 479f6ad0c..e0595d33a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,7 +42,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added optional `-ValidateConnection` to `Connect-PnPOnline` which will check if the site you are connecting to exists and if not, will throw an exception [#1924](https://github.com/pnp/powershell/pull/1924) - Added `Add-PnPListItemAttachment` cmdlet to provide ability to upload a file as an attachment to a SharePoint list item. [#1932](https://github.com/pnp/powershell/pull/1932) - Added `Remove-PnPListItemAttachment` cmdlet to provide ability to delete a list item attachment. [#1932](https://github.com/pnp/powershell/pull/1932) -- Added `Get-PnPListItemAttachments` cmdlet to download the attachments from a list item. [#1932](https://github.com/pnp/powershell/pull/1932) +- Added `Get-PnPListItemAttachment` cmdlet to download the attachments from a list item. [#1932](https://github.com/pnp/powershell/pull/1932) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) From 8c556cb5e17d4145ac022c847d581ada22912713 Mon Sep 17 00:00:00 2001 From: = <=> Date: Thu, 9 Jun 2022 23:55:32 +0200 Subject: [PATCH 325/458] Added changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0d019560..c30f6f297 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added optional `-Connection` parameter to `Get-PnPConnection`, `Get-PnPContext` and `Set-PnPContext` which allows for using any of these for a specific connection [#1919](https://github.com/pnp/powershell/pull/1919) - Added `-IncludeDeprecated` parameter to `Get-PnPTerm` cmdlet to fetch deprecated terms if specified [#1903](https://github.com/pnp/powershell/pull/1903) - Added optional `-ValidateConnection` to `Connect-PnPOnline` which will check if the site you are connecting to exists and if not, will throw an exception [#1924](https://github.com/pnp/powershell/pull/1924) +- Added `AllowTenantMoveWithDataMigration` to `Get-PnPPlannerConfiguration` and `Set-PnPPlannerConfiguration` [#1934](https://github.com/pnp/powershell/pull/1934) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) From 0452dde78f7fd180d8d25f94ce4ff5c42506c24b Mon Sep 17 00:00:00 2001 From: = <=> Date: Fri, 10 Jun 2022 00:00:01 +0200 Subject: [PATCH 326/458] Adding changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c30f6f297..a1bf0435a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `-IncludeDeprecated` parameter to `Get-PnPTerm` cmdlet to fetch deprecated terms if specified [#1903](https://github.com/pnp/powershell/pull/1903) - Added optional `-ValidateConnection` to `Connect-PnPOnline` which will check if the site you are connecting to exists and if not, will throw an exception [#1924](https://github.com/pnp/powershell/pull/1924) - Added `AllowTenantMoveWithDataMigration` to `Get-PnPPlannerConfiguration` and `Set-PnPPlannerConfiguration` [#1934](https://github.com/pnp/powershell/pull/1934) +- Added the ability to retrieve a Planner plan by only its Id using `Get-PnPPlannerPlan -Identity ` [#1935](https://github.com/pnp/powershell/pull/1935) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) From 2e1dccf5828994e74bb3d8c728bff8615655d239 Mon Sep 17 00:00:00 2001 From: = <=> Date: Fri, 10 Jun 2022 00:05:30 +0200 Subject: [PATCH 327/458] Added changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0d019560..ba16a9201 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added optional `-Connection` parameter to `Get-PnPConnection`, `Get-PnPContext` and `Set-PnPContext` which allows for using any of these for a specific connection [#1919](https://github.com/pnp/powershell/pull/1919) - Added `-IncludeDeprecated` parameter to `Get-PnPTerm` cmdlet to fetch deprecated terms if specified [#1903](https://github.com/pnp/powershell/pull/1903) - Added optional `-ValidateConnection` to `Connect-PnPOnline` which will check if the site you are connecting to exists and if not, will throw an exception [#1924](https://github.com/pnp/powershell/pull/1924) +- Added `-Description` and `-Priority` to `Set-PnPPlannerTask` [#1947](https://github.com/pnp/powershell/pull/1947) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) From e3390faecd2e9aaa9323e34a0ffbe51b218391ec Mon Sep 17 00:00:00 2001 From: = <=> Date: Fri, 10 Jun 2022 00:09:14 +0200 Subject: [PATCH 328/458] Adding changelog entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b30a9c1a..cfad00476 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,7 +54,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Service Health cmdlets have been improved, now are consistent with other cmdlets to handle pagination [#1938](https://github.com/pnp/powershell/pull/1938) ### Fixed - - Fixed `Get-PnPTenantSite` cmdlet so that it will return data even if the template name is specified in a different case. [#1773](https://github.com/pnp/powershell/pull/1773) - Fixed `Add-PnPDocumentSet` cmdlet so that it will support Document Set Content Type Id specified at the web level. [#1796](https://github.com/pnp/powershell/pull/1796) - Fixed `Get-PnPGroup` , `Get-PnPGroupPermissions` and `Set-PnPGroupPermissions ` cmdlets by making them more consistent. They will also throw error if a group is not found. [#1808](https://github.com/pnp/powershell/pull/1808) @@ -72,6 +71,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Set-PnPContext` not properly applying the provided context [#1919](https://github.com/pnp/powershell/pull/1919) - Fixed Graph endpoints for non-commercial clouds for Managed Identity and Teams cmdlets [#1944](https://github.com/pnp/powershell/pull/1944) - Fixed `Add-PnPTeamsUser`, the parameter `-Channel` is now not required. [#1953](https://github.com/pnp/powershell/pull/1953) +- Fixed `Get-PnPPlannerTask` throwing an object reference exception for completed tasks [#1956](https://github.com/pnp/powershell/issues/1956) ### Removed - Removed `Get-PnPAvailableClientSideComponents`. Use `Get-PnPPageComponent -Page -ListAvailable` instead. [#1833](https://github.com/pnp/powershell/pull/1833) From 4657a8698ed08e066741eda587a0ddf486b60887 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Fri, 10 Jun 2022 03:38:55 +0000 Subject: [PATCH 329/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index 4c6080b57..2dd242773 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -71581e5b9c35a1e1e79106051d6b12cef1f4134b \ No newline at end of file +14bd92f791e0c9cc37e009583d77ea1b39e3e183 \ No newline at end of file diff --git a/version.txt b/version.txt index 450854796..fdadb85f6 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.43 \ No newline at end of file +1.10.44 \ No newline at end of file From 5c27058d3cf1d46ea24fada0b08541aed96fb305 Mon Sep 17 00:00:00 2001 From: James May Date: Fri, 10 Jun 2022 14:05:44 +1000 Subject: [PATCH 330/458] SetList: receive list from pipeline --- src/Commands/Lists/SetList.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Commands/Lists/SetList.cs b/src/Commands/Lists/SetList.cs index 958f3af1e..f0fe83804 100644 --- a/src/Commands/Lists/SetList.cs +++ b/src/Commands/Lists/SetList.cs @@ -10,7 +10,7 @@ namespace PnP.PowerShell.Commands.Lists [OutputType(typeof(List))] public class SetList : PnPWebCmdlet { - [Parameter(Mandatory = true)] + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] public ListPipeBind Identity; [Parameter(Mandatory = false)] From bd2ad5bd7599fe5fefff44edd94b21b2fedba16b Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Fri, 10 Jun 2022 09:16:21 +0200 Subject: [PATCH 331/458] Fixing issue where connecting after disconnecting would throw an exception (#1958) Co-authored-by: = <=> --- src/Commands/Base/ConnectOnline.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Commands/Base/ConnectOnline.cs b/src/Commands/Base/ConnectOnline.cs index 8f85073b4..9ea862d15 100644 --- a/src/Commands/Base/ConnectOnline.cs +++ b/src/Commands/Base/ConnectOnline.cs @@ -665,8 +665,8 @@ protected override void StopProcessing() private void ReuseAuthenticationManager() { - var contextSettings = PnPConnection.Current.Context.GetContextSettings(); - PnPConnection.CachedAuthenticationManager = contextSettings.AuthenticationManager; + var contextSettings = PnPConnection.Current.Context?.GetContextSettings(); + PnPConnection.CachedAuthenticationManager = contextSettings?.AuthenticationManager; } #endregion } From eee6f615902c96f5a35e3a63d94e6dae0a256287 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Fri, 10 Jun 2022 10:19:26 +0300 Subject: [PATCH 332/458] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92a1ba930..5ff71cab7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added capability to Debug the module in Visual Studio. [#1880](https://github.com/pnp/powershell/pull/1880) - Added `Set-PnPTeamsChannelUser` cmdlet to update the role of user in a private channel. [#1865](https://github.com/pnp/powershell/pull/1865) - Added `Restart-PnPFlowRun` which allows for a failed Power Automate flow run to be retried [#1915](https://github.com/pnp/powershell/pull/1915) -- Added optional `-Connection` parameter to `Get-PnPConnection`, `Get-PnPContext` and `Set-PnPContext` which allows for using any of these for a specific connection [#1919](https://github.com/pnp/powershell/pull/1919) +- Added optional `-Connection` parameter to `Get-PnPConnection`, `Get-PnPContext` and `Set-PnPContext` which allows for using any of these for a specific connection [#1919](https://github.com/pnp/powershell/pull/1919) , [#1958](https://github.com/pnp/powershell/pull/1958) - Added `-IncludeDeprecated` parameter to `Get-PnPTerm` cmdlet to fetch deprecated terms if specified [#1903](https://github.com/pnp/powershell/pull/1903) - Added `-IncludeContentType` parameter, which if specified will retrieve content type information of the list items. [#1921](https://github.com/pnp/powershell/pull/1921) - Added optional `-ValidateConnection` to `Connect-PnPOnline` which will check if the site you are connecting to exists and if not, will throw an exception [#1924](https://github.com/pnp/powershell/pull/1924) From 476b215ecc474c551978a3ad639210b1aeaba882 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 11 Jun 2022 03:35:20 +0000 Subject: [PATCH 333/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index 2dd242773..93fe8a88c 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -14bd92f791e0c9cc37e009583d77ea1b39e3e183 \ No newline at end of file +0372265fb4fc622e398033e57ad8e9d601578125 \ No newline at end of file diff --git a/version.txt b/version.txt index fdadb85f6..4d3011c68 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.44 \ No newline at end of file +1.10.45 \ No newline at end of file From f9df9c2ca1629569007ce0d38aea9780fb590aad Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Sun, 12 Jun 2022 03:37:37 +0000 Subject: [PATCH 334/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index 93fe8a88c..d2fb231dd 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -0372265fb4fc622e398033e57ad8e9d601578125 \ No newline at end of file +c9df0671671363a7c5e739ad59746b9b4ec556cf \ No newline at end of file diff --git a/version.txt b/version.txt index 4d3011c68..97385b66b 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.45 \ No newline at end of file +1.10.46 \ No newline at end of file From 4c26a48882b4cbe70bb88e29f141d0c7fca0863c Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sun, 12 Jun 2022 23:03:07 +0300 Subject: [PATCH 335/458] Updated as per review comments --- CHANGELOG.md | 2 +- documentation/Get-PnPField.md | 26 +++++- src/Commands/Fields/GetField.cs | 158 +++++++++++++++++--------------- 3 files changed, 109 insertions(+), 77 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04497d14a..2dbcc4782 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,10 +47,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Add-PnPListItemAttachment` cmdlet to provide ability to upload a file as an attachment to a SharePoint list item. [#1932](https://github.com/pnp/powershell/pull/1932) - Added `Remove-PnPListItemAttachment` cmdlet to provide ability to delete a list item attachment. [#1932](https://github.com/pnp/powershell/pull/1932) - Added `Get-PnPListItemAttachment` cmdlet to download the attachments from a list item. [#1932](https://github.com/pnp/powershell/pull/1932) +- Added `-ReturnTyped` parameter to `Get-PnPField` cmdlet so that it returns specific type instead of the generic field type. [#1888] (https://github.com/pnp/powershell/pull/1888) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) -- Changed `Get-PnPField` now returns specific type instead of the generic type. [#1888] (https://github.com/pnp/powershell/pull/1888) - Changed `Add-PnPField` now returns specific type taxonomy field type instead of the generic type. [#1888] (https://github.com/pnp/powershell/pull/1888) - Changed `Get-PnPOrgAssetsLibrary` to return a proper value of the organisation assets libraries. [#1889](https://github.com/pnp/powershell/pull/1889) - Bumped .NET Framework version to 4.6.2 as the 4.6.1 is not supported anymore. [#1856](https://github.com/pnp/powershell/pull/1856) diff --git a/documentation/Get-PnPField.md b/documentation/Get-PnPField.md index 7d556c08f..75cbff739 100644 --- a/documentation/Get-PnPField.md +++ b/documentation/Get-PnPField.md @@ -16,7 +16,7 @@ Returns a field from a list or site ## SYNTAX ```powershell -Get-PnPField [-List ] [[-Identity] ] [-Group ] [-InSiteHierarchy] +Get-PnPField [-List ] [[-Identity] ] [-Group ] [-InSiteHierarchy] [-ReturnTyped] [-Connection ] [-Includes ] [] ``` @@ -48,6 +48,14 @@ Get-PnPField -Group "Custom Columns" Gets all the fields for the group called Custom Columns for the site currently connected to +### EXAMPLE 4 + +```powershell +Get-PnPField -List "Demo list" -Identity "Speakers" -ReturnTyped +``` + +Gets the speakers field from the list Demo list and returns it as a typed field. So, if the field type is User, it will be returned as FieldUser. + ## PARAMETERS ### -Connection @@ -125,6 +133,22 @@ Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` +### -ReturnTyped + +Returns the field as the specific field type instead of the generic field type when used with List parameter. +For example, if the field type is User, it will be returned as FieldUser instead of generic Field type. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) diff --git a/src/Commands/Fields/GetField.cs b/src/Commands/Fields/GetField.cs index c4535f52a..8dd1063b8 100644 --- a/src/Commands/Fields/GetField.cs +++ b/src/Commands/Fields/GetField.cs @@ -23,6 +23,9 @@ public class GetField : PnPWebRetrievalsCmdlet [Parameter(Mandatory = false, ValueFromPipeline = false)] public SwitchParameter InSiteHierarchy; + [Parameter(Mandatory = false, ValueFromPipeline = false)] + public SwitchParameter ReturnTyped; + protected override void ExecuteCmdlet() { if (List != null) @@ -53,85 +56,90 @@ protected override void ExecuteCmdlet() ClientContext.Load(field, RetrievalExpressions); ClientContext.ExecuteQueryRetry(); - switch (field.FieldTypeKind) + if (ReturnTyped.IsPresent) { - case FieldType.DateTime: - { - WriteObject(ClientContext.CastTo(field)); - break; - } - case FieldType.Choice: - { - WriteObject(ClientContext.CastTo(field)); - break; - } - case FieldType.Calculated: - { - WriteObject(ClientContext.CastTo(field)); - break; - } - case FieldType.Computed: - { - WriteObject(ClientContext.CastTo(field)); - break; - } - case FieldType.Geolocation: - { - WriteObject(ClientContext.CastTo(field)); - break; - - } - case FieldType.User: - { - WriteObject(ClientContext.CastTo(field)); - break; - } - case FieldType.Currency: - { - WriteObject(ClientContext.CastTo(field)); - break; - } - case FieldType.Guid: - { - WriteObject(ClientContext.CastTo(field)); - break; - } - case FieldType.URL: - { - WriteObject(ClientContext.CastTo(field)); - break; - } - case FieldType.Lookup: - { - WriteObject(ClientContext.CastTo(field)); - break; - } - case FieldType.MultiChoice: - { - WriteObject(ClientContext.CastTo(field)); - break; - } - case FieldType.Number: - { - WriteObject(ClientContext.CastTo(field)); - break; - } - case FieldType.Invalid: - { - if (field.TypeAsString.StartsWith("TaxonomyFieldType")) + switch (field.FieldTypeKind) + { + case FieldType.DateTime: { - WriteObject(ClientContext.CastTo(field)); + WriteObject(ClientContext.CastTo(field)); break; } - goto default; - } - default: - { - WriteObject(field); - break; - } + case FieldType.Choice: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.Calculated: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.Computed: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.Geolocation: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.User: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.Currency: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.Guid: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.URL: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.Lookup: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.MultiChoice: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.Number: + { + WriteObject(ClientContext.CastTo(field)); + break; + } + case FieldType.Invalid: + { + if (field.TypeAsString.StartsWith("TaxonomyFieldType")) + { + WriteObject(ClientContext.CastTo(field)); + break; + } + goto default; + } + default: + { + WriteObject(field); + break; + } + } + } + else + { + WriteObject(field); } - } else if (fieldCollection != null) { @@ -212,7 +220,7 @@ protected override void ExecuteCmdlet() } ClientContext.Load(field, RetrievalExpressions); ClientContext.ExecuteQueryRetry(); - + switch (field.FieldTypeKind) { case FieldType.DateTime: From 8474c2ba8430fe749795f1026abf3d412e894cc7 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sun, 12 Jun 2022 23:28:02 +0300 Subject: [PATCH 336/458] #1954 - added a new cmdlet to add views from XML --- CHANGELOG.md | 1 + documentation/Add-PnPViewsFromXML.md | 136 +++++++++++++++++++++++++++ src/Commands/Lists/AddViewXML.cs | 29 ++++++ 3 files changed, 166 insertions(+) create mode 100644 documentation/Add-PnPViewsFromXML.md create mode 100644 src/Commands/Lists/AddViewXML.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ff71cab7..1bf4ef680 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Add-PnPListItemAttachment` cmdlet to provide ability to upload a file as an attachment to a SharePoint list item. [#1932](https://github.com/pnp/powershell/pull/1932) - Added `Remove-PnPListItemAttachment` cmdlet to provide ability to delete a list item attachment. [#1932](https://github.com/pnp/powershell/pull/1932) - Added `Get-PnPListItemAttachment` cmdlet to download the attachments from a list item. [#1932](https://github.com/pnp/powershell/pull/1932) +- Added `Add-PnPViewsFromXML` cmdlet to create multiple views in a list from an XML string. ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) diff --git a/documentation/Add-PnPViewsFromXML.md b/documentation/Add-PnPViewsFromXML.md new file mode 100644 index 000000000..c2739eb00 --- /dev/null +++ b/documentation/Add-PnPViewsFromXML.md @@ -0,0 +1,136 @@ +--- +Module Name: PnP.PowerShell +schema: 2.0.0 +applicable: SharePoint Online +online version: https://pnp.github.io/powershell/cmdlets/Add-PnPViewsFromXML.html +external help file: PnP.PowerShell.dll-Help.xml +title: Add-PnPViewsFromXML +--- + +# Add-PnPViewsFromXML + +## SYNOPSIS +Adds a view(s) to a list from an XML string. + +## SYNTAX + +```powershell +Add-PnPViewsFromXML [-List] [-ViewsXML ] + [-Connection ] [] +``` + +## DESCRIPTION + +## EXAMPLES + +### EXAMPLE 1 +```powershell + +$viewsXML = @" + + + +"@ + +Add-PnPViewsFromXML -List "Demo List" -ViewsXML $viewsXML +``` + +Adds a view named "Demo view" to the "Demo List" list from the XML string. + +### EXAMPLE 2 +```powershell + +$viewsXML = @" + + + + + +"@ + +Add-PnPViewsFromXML -List "Demo List" -ViewsXML $viewsXML +``` + +Adds a view named "Demo view" and "Created By Me" to the "Demo List" list from the XML string. + +## PARAMETERS + +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -List +The ID or Url of the list. + +```yaml +Type: ListPipeBind +Parameter Sets: (All) + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -ViewsXML +The XML string of the Views that you want to create in a list. + +```yaml +Type: string +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) + + diff --git a/src/Commands/Lists/AddViewXML.cs b/src/Commands/Lists/AddViewXML.cs new file mode 100644 index 000000000..6e212ba20 --- /dev/null +++ b/src/Commands/Lists/AddViewXML.cs @@ -0,0 +1,29 @@ +using Microsoft.SharePoint.Client; +using PnP.PowerShell.Commands.Base.PipeBinds; +using System; +using System.Collections.Generic; +using System.Management.Automation; +using System.Text; + +namespace PnP.PowerShell.Commands.Lists +{ + [Cmdlet(VerbsCommon.Add, "PnPViewsFromXML")] + [OutputType(typeof(void))] + public class AddViewXML : PnPWebCmdlet + { + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] + public ListPipeBind List; + + [Parameter(Mandatory = true)] + public string ViewsXML; + + protected override void ExecuteCmdlet() + { + var list = List.GetList(CurrentWeb); + if (list != null) + { + list.CreateViewsFromXMLString(ViewsXML); + } + } + } +} From 0678729f9f38bf188898b3a32797b1d40cf3b443 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sun, 12 Jun 2022 23:29:19 +0300 Subject: [PATCH 337/458] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bf4ef680..a8aabf0a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,7 +47,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Add-PnPListItemAttachment` cmdlet to provide ability to upload a file as an attachment to a SharePoint list item. [#1932](https://github.com/pnp/powershell/pull/1932) - Added `Remove-PnPListItemAttachment` cmdlet to provide ability to delete a list item attachment. [#1932](https://github.com/pnp/powershell/pull/1932) - Added `Get-PnPListItemAttachment` cmdlet to download the attachments from a list item. [#1932](https://github.com/pnp/powershell/pull/1932) -- Added `Add-PnPViewsFromXML` cmdlet to create multiple views in a list from an XML string. +- Added `Add-PnPViewsFromXML` cmdlet to create multiple views in a list from an XML string. [#1963](https://github.com/pnp/powershell/pull/1963) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) From 4f3680dd74163379c0735e025089076db8ba8414 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sun, 12 Jun 2022 23:58:30 +0300 Subject: [PATCH 338/458] Fixed to a better filter --- src/Commands/UserProfiles/GetUserOneDriveQuota.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Commands/UserProfiles/GetUserOneDriveQuota.cs b/src/Commands/UserProfiles/GetUserOneDriveQuota.cs index 1a7b9d56b..56bd5fd40 100644 --- a/src/Commands/UserProfiles/GetUserOneDriveQuota.cs +++ b/src/Commands/UserProfiles/GetUserOneDriveQuota.cs @@ -34,7 +34,8 @@ protected override void ExecuteCmdlet() { IncludePersonalSite = PersonalSiteFilter.Include, IncludeDetail = true, - Template = "SPSPERS" + Template = "SPSPERS", + Filter = $"Url -eq '{personalSiteUrl.TrimEnd('/')}'" }; var sitesList = Tenant.GetSitePropertiesFromSharePointByFilters(filter); @@ -54,7 +55,7 @@ protected override void ExecuteCmdlet() } else { - WriteWarning("Couldn't find quota for the specified personal site"); + WriteWarning($"Couldn't find onedrive quota for the account: {Account} "); } } } From 3aefed4cf54ca93d9a0c3f69065eecadfc6d0945 Mon Sep 17 00:00:00 2001 From: Milan Holemans <11723921+milanholemans@users.noreply.github.com> Date: Sun, 12 Jun 2022 22:59:42 +0200 Subject: [PATCH 339/458] Adds extra parameters to Add-PnPPlannerTask --- documentation/Add-PnPPlannerTask.md | 89 +++++++++++++++- src/Commands/Planner/AddPlannerTask.cs | 125 ++++++++++++++++++----- src/Commands/Utilities/PlannerUtility.cs | 24 +---- 3 files changed, 187 insertions(+), 51 deletions(-) diff --git a/documentation/Add-PnPPlannerTask.md b/documentation/Add-PnPPlannerTask.md index 2842abfaf..05360796d 100644 --- a/documentation/Add-PnPPlannerTask.md +++ b/documentation/Add-PnPPlannerTask.md @@ -21,12 +21,18 @@ Adds a new task to a planner bucket ### By Group ```powershell -Add-PnPPlannerTask -Group -Plan -Bucket -Title [-AssignedTo ] +Add-PnPPlannerTask -Group -Plan -Bucket -Title +[-PercentComplete ] [-DueDateTime ] [-StartDateTime ] + [-AssignedTo ] [-Description ] + [] ``` ### By Plan Id ```powershell -Add-PnPPlannerTask -Bucket -PlanId -Title [-AssignedTo ] +Add-PnPPlannerTask -Bucket -PlanId -Title +[-PercentComplete ] [-DueDateTime ] [-StartDateTime ] + [-AssignedTo ] [-Description ] + [] ``` ## DESCRIPTION @@ -148,6 +154,85 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -StartDateTime +Defines the start date of the task. + +```yaml +Type: DateTime +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -DueDateTime +Specify the due date. + +```yaml +Type: DateTime +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -PercentComplete +Defines the percentage of completeness of the task. + +```yaml +Type: Int32 +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Priority +Sets the priority of the task. Value should be a number between 0 and 10. +- values 0 and 1 are interpreted as _Urgent_ +- values 2, 3 and 4 are interpreted as _Important_ +- values 5, 6 and 7 are interpreted as _Medium_ +- values 8, 9 and 10 are interpreted as _Low_ + +```yaml +Type: Int32 +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Description +Sets the description (notes) of the task. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). diff --git a/src/Commands/Planner/AddPlannerTask.cs b/src/Commands/Planner/AddPlannerTask.cs index 30bb48472..7cb566ea0 100644 --- a/src/Commands/Planner/AddPlannerTask.cs +++ b/src/Commands/Planner/AddPlannerTask.cs @@ -1,10 +1,15 @@ -using System.Management.Automation; using PnP.PowerShell.Commands.Attributes; using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Model.Planner; using PnP.PowerShell.Commands.Utilities; +using PnP.PowerShell.Commands.Utilities.REST; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation; -namespace SharePointPnP.PowerShell.Commands.Graph +namespace PnP.PowerShell.Commands.Planner { [Cmdlet(VerbsCommon.Add, "PnPPlannerTask")] [RequiredMinimalApiPermissions("Group.ReadWrite.All")] @@ -28,53 +33,119 @@ public class AddPlannerTask : PnPGraphCmdlet [Parameter(Mandatory = true, ParameterSetName = ParameterAttribute.AllParameterSets)] public string Title; + [Parameter(Mandatory = false)] + public int PercentComplete; + + [Parameter(Mandatory = false)] + public int Priority; + + [Parameter(Mandatory = false)] + public DateTime DueDateTime; + + [Parameter(Mandatory = false)] + public DateTime StartDateTime; + + [Parameter(Mandatory = false)] + public string Description; + [Parameter(Mandatory = false, ParameterSetName = ParameterAttribute.AllParameterSets)] public string[] AssignedTo; + protected override void ExecuteCmdlet() { + PlannerTask createdTask; + var newTask = new PlannerTask + { + Title = Title + }; + + if (ParameterSpecified(nameof(PercentComplete))) + { + if (PercentComplete < 0 || PercentComplete > 100) + { + throw new PSArgumentException($"{nameof(PercentComplete)} value must be between 0 and 100.", nameof(PercentComplete)); + } + newTask.PercentComplete = PercentComplete; + } - if (ParameterSetName == ParameterName_BYGROUP) + if (ParameterSpecified(nameof(Priority))) { - var groupId = Group.GetGroupId(HttpClient, AccessToken); - if (groupId != null) + if (Priority < 0 || Priority > 10) { - var planId = Plan.GetIdAsync(HttpClient, AccessToken, groupId).GetAwaiter().GetResult(); + throw new PSArgumentException($"{nameof(Priority)} value must be between 0 and 10.", nameof(Priority)); + } + newTask.Priority = Priority; + } - if (planId != null) - { - var bucket = Bucket.GetBucket(HttpClient, AccessToken, planId); - if (bucket != null) - { - PlannerUtility.AddTaskAsync(HttpClient, AccessToken, planId, bucket.Id, Title, AssignedTo).GetAwaiter().GetResult(); - } - else - { - throw new PSArgumentException("Bucket not found", nameof(Bucket)); - } + if (ParameterSpecified(nameof(StartDateTime))) + { + newTask.StartDateTime = StartDateTime.ToUniversalTime(); + } - } - else + if (ParameterSpecified(nameof(DueDateTime))) + { + newTask.DueDateTime = DueDateTime.ToUniversalTime(); + } + + if (ParameterSpecified(nameof(AssignedTo))) + { + newTask.Assignments = new Dictionary(); + var chunks = AssignedTo.Chunk(20); + foreach (var chunk in chunks) + { + var userIds = BatchUtility.GetPropertyBatchedAsync(HttpClient, AccessToken, chunk.ToArray(), "/users/{0}", "id").GetAwaiter().GetResult(); + foreach (var userId in userIds) { - throw new PSArgumentException("Plan not found", nameof(Plan)); + newTask.Assignments.Add(userId.Value, new TaskAssignment()); } } - else + } + + // By Group + if (ParameterSetName == ParameterName_BYGROUP) + { + var groupId = Group.GetGroupId(HttpClient, AccessToken); + if (groupId == null) { throw new PSArgumentException("Group not found", nameof(Group)); } + + var planId = Plan.GetIdAsync(HttpClient, AccessToken, groupId).GetAwaiter().GetResult(); + if (planId == null) + { + throw new PSArgumentException("Plan not found", nameof(Plan)); + } + newTask.PlanId = planId; + + var bucket = Bucket.GetBucket(HttpClient, AccessToken, planId); + if (bucket == null) + { + throw new PSArgumentException("Bucket not found", nameof(Bucket)); + } + newTask.BucketId = bucket.Id; + + createdTask = PlannerUtility.AddTaskAsync(HttpClient, AccessToken, newTask).GetAwaiter().GetResult(); } - else if (ParameterSetName == ParameterName_BYPLANID) + // By PlanId + else { var bucket = Bucket.GetBucket(HttpClient, AccessToken, PlanId); - if (bucket != null) - { - PlannerUtility.AddTaskAsync(HttpClient, AccessToken, PlanId, bucket.Id, Title).GetAwaiter().GetResult(); - } - else + if (bucket == null) { throw new PSArgumentException("Bucket not found", nameof(Bucket)); } + + newTask.PlanId = PlanId; + newTask.BucketId = bucket.Id; + + createdTask = PlannerUtility.AddTaskAsync(HttpClient, AccessToken, newTask).GetAwaiter().GetResult(); + } + + if (ParameterSpecified(nameof(Description))) + { + var existingTaskDetails = PlannerUtility.GetTaskDetailsAsync(HttpClient, AccessToken, createdTask.Id, false).GetAwaiter().GetResult(); + PlannerUtility.UpdateTaskDetailsAsync(HttpClient, AccessToken, existingTaskDetails, Description).GetAwaiter().GetResult(); } } } diff --git a/src/Commands/Utilities/PlannerUtility.cs b/src/Commands/Utilities/PlannerUtility.cs index 2b54438e7..92b0c71dc 100644 --- a/src/Commands/Utilities/PlannerUtility.cs +++ b/src/Commands/Utilities/PlannerUtility.cs @@ -161,29 +161,9 @@ public static async Task GetTaskDetailsAsync(HttpClient http return taskDetails; } - public static async Task AddTaskAsync(HttpClient httpClient, string accessToken, string planId, string bucketId, string title, string[] assignedTo = null) + public static async Task AddTaskAsync(HttpClient httpClient, string accessToken, PlannerTask task) { - StringContent stringContent = null; - if (assignedTo != null) - { - var assignments = new Dictionary(); - var chunks = BatchUtility.Chunk(assignedTo, 20); - foreach (var chunk in chunks) - { - var results = await BatchUtility.GetPropertyBatchedAsync(httpClient, accessToken, chunk.ToArray(), "/users/{0}", "id"); - foreach (var userid in results.Select(r => r.Value)) - { - assignments.Add(userid, new Model.Planner.PlannerAssignedToUser()); - } - } - stringContent = new StringContent(JsonSerializer.Serialize(new { planId = planId, bucketId = bucketId, title = title, assignments = assignments })); - } - else - { - stringContent = new StringContent(JsonSerializer.Serialize(new { planId = planId, bucketId = bucketId, title = title })); - } - stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - return await GraphHelper.PostAsync(httpClient, "v1.0/planner/tasks", stringContent, accessToken); + return await GraphHelper.PostAsync(httpClient, "v1.0/planner/tasks", task, accessToken); } public static async Task DeleteTaskAsync(HttpClient httpClient, string accessToken, string taskId) From 2041e1a5cf589f11aebd0e002f600c1ba4c92a52 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Mon, 13 Jun 2022 03:49:20 +0000 Subject: [PATCH 340/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 18eb9c8cd..52a7a4356 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -66909f7142f9eb289184c3e5a20064ed2bc583f7 \ No newline at end of file +e1f7e40500ae6265d6014d22d288803a2f866228 \ No newline at end of file diff --git a/version.txt b/version.txt index 97385b66b..65bf39c9f 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.46 \ No newline at end of file +1.10.47 \ No newline at end of file From 4103d9217e3dff10a60f1f1d2b6054b8bb86c9fd Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Mon, 13 Jun 2022 08:21:16 +0200 Subject: [PATCH 341/458] Update Update-PnPUserType.md --- documentation/Update-PnPUserType.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Update-PnPUserType.md b/documentation/Update-PnPUserType.md index 94bd221ea..5ce4ce2a5 100644 --- a/documentation/Update-PnPUserType.md +++ b/documentation/Update-PnPUserType.md @@ -10,7 +10,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Update-PnPUserType.html # Update-PnPUserType ## SYNOPSIS -Updates an user's UserType across all SharePoint online sites. +Updates a user's UserType across all SharePoint Online sites. ## SYNTAX From 6358c7ed5909cb19e35744ac36bcb0b5800e026a Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Mon, 13 Jun 2022 08:39:30 +0200 Subject: [PATCH 342/458] Update Update-PnPTeamsUser.md --- documentation/Update-PnPTeamsUser.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/documentation/Update-PnPTeamsUser.md b/documentation/Update-PnPTeamsUser.md index b61e7b148..9ff9647fa 100644 --- a/documentation/Update-PnPTeamsUser.md +++ b/documentation/Update-PnPTeamsUser.md @@ -24,6 +24,7 @@ Update-PnPTeamsUser -Team -User -Role [ -User -Role [ Date: Mon, 13 Jun 2022 09:04:59 +0200 Subject: [PATCH 343/458] Update Update-PnPTeamsApp.md --- documentation/Update-PnPTeamsApp.md | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/Update-PnPTeamsApp.md b/documentation/Update-PnPTeamsApp.md index 0b13e25bd..29d34bbec 100644 --- a/documentation/Update-PnPTeamsApp.md +++ b/documentation/Update-PnPTeamsApp.md @@ -37,6 +37,7 @@ Updates the specified app in the teams app catalog with the contents of the refe ## PARAMETERS ### -Identity +Specify the name, id or external id of the app. ```yaml Type: TeamsAppPipeBind From 81e5147b7f9218857207a58194bc419c3354ab1c Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Mon, 13 Jun 2022 09:42:42 +0200 Subject: [PATCH 344/458] Update Update-PnPSiteDesignFromWeb.md --- documentation/Update-PnPSiteDesignFromWeb.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/documentation/Update-PnPSiteDesignFromWeb.md b/documentation/Update-PnPSiteDesignFromWeb.md index 76bf3d9da..a6c9b0e6e 100644 --- a/documentation/Update-PnPSiteDesignFromWeb.md +++ b/documentation/Update-PnPSiteDesignFromWeb.md @@ -19,9 +19,15 @@ Updates an existing Site Design on the current tenant based on the site provided ## SYNTAX +### Specific components ```powershell -Update-PnPSiteDesignFromWeb -Identity -Url [-Lists ] [-IncludeBranding] [-IncludeLinksToExportedItems] - [-IncludeRegionalSettings] [-IncludeSiteExternalSharingCapability] [-IncludeTheme] [-IncludeAll] +Update-PnPSiteDesignFromWeb -Identity -Url [-Lists ] [-IncludeBranding ] [-IncludeLinksToExportedItems ] + [-IncludeRegionalSettings ] [-IncludeSiteExternalSharingCapability ] [-IncludeTheme ] [-Connection ] +``` + +### All components +```powershell +Update-PnPSiteDesignFromWeb -Identity -Url [-Lists ] [-IncludeAll ] [-Connection ] ``` @@ -183,4 +189,4 @@ Accept wildcard characters: False ## RELATED LINKS -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) From dfb377a99563d8fb442863f1790e92bafc19db24 Mon Sep 17 00:00:00 2001 From: Marc D Anderson Date: Mon, 13 Jun 2022 15:07:38 -0400 Subject: [PATCH 345/458] Added badge for batchng --- documentation/Publish-PnPSyntexModel.md | 2 +- .../Request-PnPSyntexClassifyAndExtract.md | 2 +- pages/images/batching/Batch 2.png | Bin 0 -> 7939 bytes pages/images/batching/Batching.png | Bin 8519 -> 44943 bytes 4 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 pages/images/batching/Batch 2.png diff --git a/documentation/Publish-PnPSyntexModel.md b/documentation/Publish-PnPSyntexModel.md index 03e30fbc0..6e36467f8 100644 --- a/documentation/Publish-PnPSyntexModel.md +++ b/documentation/Publish-PnPSyntexModel.md @@ -9,7 +9,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Get-PnPPage.html # Publish-PnPSyntexModel -:::image type="content" source="../pages/images/batching/Batching.png" alt-text="Supports batching"::: +:::image type="content" source="../pages/images/batching/Batch 2.png" alt-text="Supports batching"::: ## SYNOPSIS diff --git a/documentation/Request-PnPSyntexClassifyAndExtract.md b/documentation/Request-PnPSyntexClassifyAndExtract.md index 1619ee721..6f8fb5d20 100644 --- a/documentation/Request-PnPSyntexClassifyAndExtract.md +++ b/documentation/Request-PnPSyntexClassifyAndExtract.md @@ -9,7 +9,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Get-PnPPage.html # Request-PnPSyntexClassifyAndExtract -:::image type="content" source="../pages/images/batching/Batching.png" alt-text="Supports batching"::: +:::image type="content" source="../pages/images/batching/Batch 2.png" alt-text="Supports batching"::: ## SYNOPSIS diff --git a/pages/images/batching/Batch 2.png b/pages/images/batching/Batch 2.png new file mode 100644 index 0000000000000000000000000000000000000000..66a99427ebcef1bd9a082a33914b490465c6b348 GIT binary patch literal 7939 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 NUG4ku5>4Az{|ET#nMwcv literal 0 HcmV?d00001 diff --git a/pages/images/batching/Batching.png b/pages/images/batching/Batching.png index 2fee53aa1de0a5c45879060f241cbf8a82b785a9..35bc5e278306e2a704b35170add12e68aef0727b 100644 GIT binary patch literal 44943 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∨12VJ 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 RH_4!Y@pScbS?83{1ORISsSp4F literal 8519 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 F{{WW|`nLc8 From 010dfe1052a537516a926f1037be72d0a9aa5951 Mon Sep 17 00:00:00 2001 From: Marc D Anderson Date: Mon, 13 Jun 2022 15:09:35 -0400 Subject: [PATCH 346/458] Fix --- documentation/Publish-PnPSyntexModel.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documentation/Publish-PnPSyntexModel.md b/documentation/Publish-PnPSyntexModel.md index 6e36467f8..7f444634d 100644 --- a/documentation/Publish-PnPSyntexModel.md +++ b/documentation/Publish-PnPSyntexModel.md @@ -9,6 +9,8 @@ online version: https://pnp.github.io/powershell/cmdlets/Get-PnPPage.html # Publish-PnPSyntexModel +![Supports batching]("../pages/images/batching/Batch 2.png") + :::image type="content" source="../pages/images/batching/Batch 2.png" alt-text="Supports batching"::: ## SYNOPSIS From d123fc987bb97ea4258fde953551e842aa345645 Mon Sep 17 00:00:00 2001 From: Marc D Anderson Date: Mon, 13 Jun 2022 15:11:49 -0400 Subject: [PATCH 347/458] Fix --- documentation/Publish-PnPSyntexModel.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Publish-PnPSyntexModel.md b/documentation/Publish-PnPSyntexModel.md index 7f444634d..5f6d671e5 100644 --- a/documentation/Publish-PnPSyntexModel.md +++ b/documentation/Publish-PnPSyntexModel.md @@ -9,7 +9,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Get-PnPPage.html # Publish-PnPSyntexModel -![Supports batching]("../pages/images/batching/Batch 2.png") +![Supports batching](../pages/images/batching/Batch 2.png) :::image type="content" source="../pages/images/batching/Batch 2.png" alt-text="Supports batching"::: From 68fe33a960730bedea02cc780fb0650ac596b190 Mon Sep 17 00:00:00 2001 From: Marc D Anderson Date: Mon, 13 Jun 2022 15:17:14 -0400 Subject: [PATCH 348/458] Fix --- documentation/Publish-PnPSyntexModel.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Publish-PnPSyntexModel.md b/documentation/Publish-PnPSyntexModel.md index 5f6d671e5..6202c0077 100644 --- a/documentation/Publish-PnPSyntexModel.md +++ b/documentation/Publish-PnPSyntexModel.md @@ -9,7 +9,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Get-PnPPage.html # Publish-PnPSyntexModel -![Supports batching](../pages/images/batching/Batch 2.png) +![Maturity Model for Microsoft 365](/pages/images/batching/Batch%202.png) :::image type="content" source="../pages/images/batching/Batch 2.png" alt-text="Supports batching"::: From fee28c15db65f00a2de665c3cf5aef6bd4c959f2 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Mon, 13 Jun 2022 22:24:33 +0300 Subject: [PATCH 349/458] Feature #1972 - additional param to Set-PnPList --- CHANGELOG.md | 2 +- documentation/Set-PnPList.md | 14 +++++++++++++- src/Commands/Lists/SetList.cs | 11 ++++++++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ff71cab7..50aaf8127 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,7 +47,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Add-PnPListItemAttachment` cmdlet to provide ability to upload a file as an attachment to a SharePoint list item. [#1932](https://github.com/pnp/powershell/pull/1932) - Added `Remove-PnPListItemAttachment` cmdlet to provide ability to delete a list item attachment. [#1932](https://github.com/pnp/powershell/pull/1932) - Added `Get-PnPListItemAttachment` cmdlet to download the attachments from a list item. [#1932](https://github.com/pnp/powershell/pull/1932) - +- Added `-ExemptFromBlockDownloadOfNonViewableFiles` parameter to `Set-PnPList` cmdlet to configure access capabilites for unmanaged devices at list level. ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) - Changed `Get-PnPOrgAssetsLibrary` to return a proper value of the organisation assets libraries. [#1889](https://github.com/pnp/powershell/pull/1889) diff --git a/documentation/Set-PnPList.md b/documentation/Set-PnPList.md index fe064de0b..d80e359fc 100644 --- a/documentation/Set-PnPList.md +++ b/documentation/Set-PnPList.md @@ -21,7 +21,7 @@ Set-PnPList -Identity [-EnableContentTypes ] [-BreakRole [-EnableAttachments ] [-EnableFolderCreation ] [-EnableVersioning ] [-EnableMinorVersions ] [-MajorVersions ] [-MinorVersions ] [-EnableModeration ] [-ReadSecurity ] [-WriteSecurity ] - [-NoCrawl] [-Connection ] [] + [-NoCrawl] [-ExemptFromBlockDownloadOfNonViewableFiles ] [-Connection ] [] ``` ## DESCRIPTION @@ -381,7 +381,19 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -ExemptFromBlockDownloadOfNonViewableFiles +Boolean parameter , if specified allows to configure access capabilites for unmanaged devices at list level. +```yaml +Type: Boolean +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` ## RELATED LINKS diff --git a/src/Commands/Lists/SetList.cs b/src/Commands/Lists/SetList.cs index f0fe83804..d96f81186 100644 --- a/src/Commands/Lists/SetList.cs +++ b/src/Commands/Lists/SetList.cs @@ -77,13 +77,16 @@ public bool [Parameter(Mandatory = false)] public SwitchParameter NoCrawl; + [Parameter(Mandatory = false)] + public bool ExemptFromBlockDownloadOfNonViewableFiles; + protected override void ExecuteCmdlet() { var list = Identity.GetList(CurrentWeb); if (list != null) { - list.EnsureProperties(l => l.EnableAttachments, l => l.EnableVersioning, l => l.EnableMinorVersions, l => l.Hidden, l => l.EnableModeration, l => l.BaseType, l => l.HasUniqueRoleAssignments, l => l.ContentTypesEnabled); + list.EnsureProperties(l => l.EnableAttachments, l => l.EnableVersioning, l => l.EnableMinorVersions, l => l.Hidden, l => l.EnableModeration, l => l.BaseType, l => l.HasUniqueRoleAssignments, l => l.ContentTypesEnabled, l => l.ExemptFromBlockDownloadOfNonViewableFiles); var enableVersioning = list.EnableVersioning; var enableMinorVersions = list.EnableMinorVersions; @@ -187,6 +190,12 @@ protected override void ExecuteCmdlet() updateRequired = true; } + if (ParameterSpecified(nameof(ExemptFromBlockDownloadOfNonViewableFiles))) + { + list.SetExemptFromBlockDownloadOfNonViewableFiles(ExemptFromBlockDownloadOfNonViewableFiles); + updateRequired = true; + } + if (updateRequired) { list.Update(); From e8ab369940153dda05e73c1d0676d1893d50b447 Mon Sep 17 00:00:00 2001 From: Marc D Anderson Date: Mon, 13 Jun 2022 17:09:15 -0400 Subject: [PATCH 350/458] Fix --- documentation/Publish-PnPSyntexModel.md | 4 ++-- .../Request-PnPSyntexClassifyAndExtract.md | 4 ++-- documentation/Unpublish-PnPSyntexModel.md | 2 +- pages/articles/batching.md | 5 +++-- pages/images/batching/Batch 2.png | Bin 7939 -> 6055 bytes pages/images/batching/Batching2.png | Bin 0 -> 6055 bytes 6 files changed, 8 insertions(+), 7 deletions(-) create mode 100644 pages/images/batching/Batching2.png diff --git a/documentation/Publish-PnPSyntexModel.md b/documentation/Publish-PnPSyntexModel.md index 6202c0077..c29d916e0 100644 --- a/documentation/Publish-PnPSyntexModel.md +++ b/documentation/Publish-PnPSyntexModel.md @@ -4,12 +4,12 @@ title: Publish-PnPSyntexModel schema: 2.0.0 applicable: SharePoint Online external help file: PnP.PowerShell.dll-Help.xml -online version: https://pnp.github.io/powershell/cmdlets/Get-PnPPage.html +online version: https://pnp.github.io/powershell/cmdlets/Publish-PnPSyntexModel.html --- # Publish-PnPSyntexModel -![Maturity Model for Microsoft 365](/pages/images/batching/Batch%202.png) +![Maturity Model for Microsoft 365](/pages/images/batching/Batching2.png) :::image type="content" source="../pages/images/batching/Batch 2.png" alt-text="Supports batching"::: diff --git a/documentation/Request-PnPSyntexClassifyAndExtract.md b/documentation/Request-PnPSyntexClassifyAndExtract.md index 6f8fb5d20..0dcbec851 100644 --- a/documentation/Request-PnPSyntexClassifyAndExtract.md +++ b/documentation/Request-PnPSyntexClassifyAndExtract.md @@ -9,12 +9,12 @@ online version: https://pnp.github.io/powershell/cmdlets/Get-PnPPage.html # Request-PnPSyntexClassifyAndExtract -:::image type="content" source="../pages/images/batching/Batch 2.png" alt-text="Supports batching"::: - ## SYNOPSIS Requests for a file, folder or all files in a library to be classified and extracted via the published SharePoint Syntex models on the libraries hosting the files. +:::image type="content" source="../pages/images/batching/Batch 2.png" alt-text="Supports batching"::: + ## SYNTAX ### File diff --git a/documentation/Unpublish-PnPSyntexModel.md b/documentation/Unpublish-PnPSyntexModel.md index e02b9d97c..a3963689a 100644 --- a/documentation/Unpublish-PnPSyntexModel.md +++ b/documentation/Unpublish-PnPSyntexModel.md @@ -4,7 +4,7 @@ title: Unpublish-PnPSyntexModel schema: 2.0.0 applicable: SharePoint Online external help file: PnP.PowerShell.dll-Help.xml -online version: https://pnp.github.io/powershell/cmdlets/Get-PnPPage.html +online version: https://pnp.github.io/powershell/cmdlets/UnPublish-PnPSyntexModel.html --- # Unpublish-PnPSyntexModel diff --git a/pages/articles/batching.md b/pages/articles/batching.md index 160b915e4..044cebcab 100644 --- a/pages/articles/batching.md +++ b/pages/articles/batching.md @@ -45,8 +45,9 @@ If during one of these chunks an exception occurs (for instance you are trying t ## Cmdlets that support batching -The following cmdlets support batching. (This support began with the 1.7.63-nightly release.) All cmdlets which support batching will display the batching badge: -:::image type="content" source="../images/batching/Batching.png" alt-text="Supports batching"::: +The following cmdlets support batching. (This support began with the 1.7.63-nightly release.) All cmdlets which support batching display the batching badge: + +:::image type="content" source="../images/batching/Batch 2.png" alt-text="Supports batching"::: * [`Add-PnPListItem`](/powershell/cmdlets/Add-PnPListItem.html) * [`Set-PnPListItem`](/powershell/cmdlets/Set-PnPListItem.html) diff --git a/pages/images/batching/Batch 2.png b/pages/images/batching/Batch 2.png index 66a99427ebcef1bd9a082a33914b490465c6b348..6a23be542b3ebe77fa425a3e4146f852c2540bd0 100644 GIT binary patch literal 6055 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 NUG4ku5>4Az{|ET#nMwcv diff --git a/pages/images/batching/Batching2.png b/pages/images/batching/Batching2.png new file mode 100644 index 0000000000000000000000000000000000000000..10dd56b3f0b1586339afd23ef2d8cfc8dabc8364 GIT binary patch literal 6055 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 F{s;T-q89)F literal 0 HcmV?d00001 From 62f200d9f5e5a19f3c4e45f9cc533e5a7cc6c9b6 Mon Sep 17 00:00:00 2001 From: Marc D Anderson Date: Mon, 13 Jun 2022 17:14:41 -0400 Subject: [PATCH 351/458] Fix --- .../Request-PnPSyntexClassifyAndExtract.md | 2 +- pages/images/batching/Batch175.png | Bin 0 -> 9101 bytes pages/images/batching/Batch80.png | Bin 0 -> 5044 bytes 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 pages/images/batching/Batch175.png create mode 100644 pages/images/batching/Batch80.png diff --git a/documentation/Request-PnPSyntexClassifyAndExtract.md b/documentation/Request-PnPSyntexClassifyAndExtract.md index 0dcbec851..f2a8ce5cb 100644 --- a/documentation/Request-PnPSyntexClassifyAndExtract.md +++ b/documentation/Request-PnPSyntexClassifyAndExtract.md @@ -13,7 +13,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Get-PnPPage.html Requests for a file, folder or all files in a library to be classified and extracted via the published SharePoint Syntex models on the libraries hosting the files. -:::image type="content" source="../pages/images/batching/Batch 2.png" alt-text="Supports batching"::: +:::image type="content" source="../pages/images/batching/Batch175.png" alt-text="Supports batching"::: ## SYNTAX diff --git a/pages/images/batching/Batch175.png b/pages/images/batching/Batch175.png new file mode 100644 index 0000000000000000000000000000000000000000..1e580cbc038691cd07ebf5f403e8ee2939021e64 GIT binary patch literal 9101 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 literal 0 HcmV?d00001 From 6fa0f11b2e1f964c63a2656734c0473ad5f990c8 Mon Sep 17 00:00:00 2001 From: Marc D Anderson Date: Mon, 13 Jun 2022 17:16:01 -0400 Subject: [PATCH 352/458] fix --- documentation/Publish-PnPSyntexModel.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Publish-PnPSyntexModel.md b/documentation/Publish-PnPSyntexModel.md index c29d916e0..bef9bfd7e 100644 --- a/documentation/Publish-PnPSyntexModel.md +++ b/documentation/Publish-PnPSyntexModel.md @@ -9,7 +9,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Publish-PnPSyntexModel. # Publish-PnPSyntexModel -![Maturity Model for Microsoft 365](/pages/images/batching/Batching2.png) +![Maturity Model for Microsoft 365](/pages/images/batching/Batch175.png) :::image type="content" source="../pages/images/batching/Batch 2.png" alt-text="Supports batching"::: From c6e8fdfb5284460c78fe2f9419d94ecf8cd54ec7 Mon Sep 17 00:00:00 2001 From: Marc D Anderson Date: Mon, 13 Jun 2022 17:17:16 -0400 Subject: [PATCH 353/458] fix --- documentation/Publish-PnPSyntexModel.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/documentation/Publish-PnPSyntexModel.md b/documentation/Publish-PnPSyntexModel.md index bef9bfd7e..78fa907a0 100644 --- a/documentation/Publish-PnPSyntexModel.md +++ b/documentation/Publish-PnPSyntexModel.md @@ -9,16 +9,14 @@ online version: https://pnp.github.io/powershell/cmdlets/Publish-PnPSyntexModel. # Publish-PnPSyntexModel -![Maturity Model for Microsoft 365](/pages/images/batching/Batch175.png) - -:::image type="content" source="../pages/images/batching/Batch 2.png" alt-text="Supports batching"::: - ## SYNOPSIS Publishes a SharePoint Syntex models to a list. This cmdlet only works when you've connected to a SharePoint Syntex Content Center site. +![Maturity Model for Microsoft 365](/pages/images/batching/Batch175.png) + ## SYNTAX ### Single From 6f66d41c03abfb0ed65ef75019808092c3a7549f Mon Sep 17 00:00:00 2001 From: Marc D Anderson Date: Mon, 13 Jun 2022 17:36:13 -0400 Subject: [PATCH 354/458] Added batching badge to all relevant docs --- documentation/Add-PnPListItem.md | 24 +++++++++++++----- documentation/Invoke-PnPBatch.md | 12 +++++++-- documentation/New-PnPBatch.md | 7 ++++- documentation/Publish-PnPSyntexModel.md | 2 +- documentation/Remove-PnPListItem.md | 5 ++-- .../Request-PnPSyntexClassifyAndExtract.md | 2 +- documentation/Set-PnPListItem.md | 4 +-- documentation/Unpublish-PnPSyntexModel.md | 4 +-- pages/articles/batching.md | 4 +-- pages/images/batching/Batch 2.png | Bin 6055 -> 0 bytes pages/images/batching/Batch175.png | Bin 9101 -> 0 bytes pages/images/batching/Batch80.png | Bin 5044 -> 0 bytes pages/images/batching/Batching.png | Bin 44943 -> 9101 bytes pages/images/batching/Batching2.png | Bin 6055 -> 0 bytes 14 files changed, 45 insertions(+), 19 deletions(-) delete mode 100644 pages/images/batching/Batch 2.png delete mode 100644 pages/images/batching/Batch175.png delete mode 100644 pages/images/batching/Batch80.png delete mode 100644 pages/images/batching/Batching2.png diff --git a/documentation/Add-PnPListItem.md b/documentation/Add-PnPListItem.md index 13d222dfc..8a0dd28e3 100644 --- a/documentation/Add-PnPListItem.md +++ b/documentation/Add-PnPListItem.md @@ -9,20 +9,23 @@ title: Add-PnPListItem # Add-PnPListItem -:::image type="content" source="../pages/images/batching/Batching.png" alt-text="Supports batching"::: - ## SYNOPSIS + Adds an item to the list and sets the creation time to the current date and time. The author is set to the current authenticated user executing the cmdlet. In order to set the author to a different user, please refer to Set-PnPListItem. +[![Maturity Model for Microsoft 365](/pages/images/batching/Batching.png)](../pages/articles/batching.md) + ## SYNTAX ### Single + ```powershell Add-PnPListItem [-List] [-ContentType ] [-Values ] [-Folder ] [-Label ] [-Connection ] [] ``` ### Batched + ```powershell Add-PnPListItem [-List] -Batch [-ContentType ] [-Values ] [-Folder ] [-Connection ] [] @@ -33,6 +36,7 @@ Add-PnPListItem [-List] -Batch [-ContentType [-Details] [-StopOnException] [-Force] ``` ## DESCRIPTION + Executes any queued actions / changes in the batch. ## EXAMPLES ### EXAMPLE 1 + ```powershell $batch = New-PnPBatch Add-PnPListItem -List "DemoList" -Values @{"Title"="Demo Item 1"} -Batch $batch @@ -37,6 +42,7 @@ This will add the 3 defined list items in the batch. ## PARAMETERS ### -Batch + The batch to execute ```yaml @@ -51,6 +57,7 @@ Accept wildcard characters: False ``` ### -Force + Once a batch has been executed you cannot execute it again. Using -Force will allow you to reexecute the batch again. ```yaml @@ -65,6 +72,7 @@ Accept wildcard characters: False ``` ### -Details + Will return detailed information of the batch executed. ```yaml @@ -79,6 +87,7 @@ Accept wildcard characters: False ``` ### -StopOnException + By default the batch will be fully executed. If any exceptions occur during this process they will be listed after the full run. Specify this switch to stop immediately after an exception occurs. The rest of the batch will be skipped then. ```yaml @@ -95,4 +104,3 @@ Accept wildcard characters: False ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - diff --git a/documentation/New-PnPBatch.md b/documentation/New-PnPBatch.md index 609376731..1d2abe667 100644 --- a/documentation/New-PnPBatch.md +++ b/documentation/New-PnPBatch.md @@ -10,8 +10,11 @@ online version: https://pnp.github.io/powershell/cmdlets/New-PnPBatch.html # New-PnPBatch ## SYNOPSIS + Creates a new batch +[![Maturity Model for Microsoft 365](/pages/images/batching/Batching.png)](../pages/articles/batching.md) + ## SYNTAX ```powershell @@ -19,11 +22,13 @@ New-PnPBatch [-RetainRequests] ``` ## DESCRIPTION + Creates a new batch to be used by cmdlets that support batching. The requests in the batch are clear after execute Invoke-PnPBatch, unless you specify -RetainRequests. That allows you to execute batch multiple times. ## EXAMPLES ### EXAMPLE 1 + ```powershell $batch = New-PnPBatch Add-PnPListItem -List "DemoList" -Values @{"Title"="Demo Item 1"} -Batch $batch @@ -35,6 +40,7 @@ Invoke-PnPBatch -Batch $batch This will add the 3 defined list items in the batch. ### EXAMPLE 2 + ```powershell $batch = New-PnPBatch 1..50 | Foreach-Object{Remove-PnPListItem -List "DemoList" -Identity $_ -Batch $batch} @@ -61,4 +67,3 @@ Accept wildcard characters: False ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - diff --git a/documentation/Publish-PnPSyntexModel.md b/documentation/Publish-PnPSyntexModel.md index 78fa907a0..3809f4397 100644 --- a/documentation/Publish-PnPSyntexModel.md +++ b/documentation/Publish-PnPSyntexModel.md @@ -15,7 +15,7 @@ Publishes a SharePoint Syntex models to a list. This cmdlet only works when you've connected to a SharePoint Syntex Content Center site. -![Maturity Model for Microsoft 365](/pages/images/batching/Batch175.png) +[![Maturity Model for Microsoft 365](/pages/images/batching/Batching.png)](../pages/articles/batching.md) ## SYNTAX diff --git a/documentation/Remove-PnPListItem.md b/documentation/Remove-PnPListItem.md index 97be0cb4d..6023c3e65 100644 --- a/documentation/Remove-PnPListItem.md +++ b/documentation/Remove-PnPListItem.md @@ -9,12 +9,12 @@ online version: https://pnp.github.io/powershell/cmdlets/Remove-PnPListItem.html # Remove-PnPListItem -:::image type="content" source="../pages/images/batching/Batching.png" alt-text="Supports batching"::: - ## SYNOPSIS Deletes an item from a list +[![Maturity Model for Microsoft 365](/pages/images/batching/Batching.png)](../pages/articles/batching.md) + ## SYNTAX ### Single @@ -34,6 +34,7 @@ Remove-PnPListItem [-List] -Identity -Batch

1|MBZe*j=&|F407g4bM>B3*!(_COpJ! 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 diff --git a/pages/images/batching/Batching.png b/pages/images/batching/Batching.png index 35bc5e278306e2a704b35170add12e68aef0727b..1e580cbc038691cd07ebf5f403e8ee2939021e64 100644 GIT binary patch delta 7710 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∨12VJ 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 RH_4!Y@pScbS?83{1ORISsSp4F diff --git a/pages/images/batching/Batching2.png b/pages/images/batching/Batching2.png deleted file mode 100644 index 10dd56b3f0b1586339afd23ef2d8cfc8dabc8364..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6055 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 F{s;T-q89)F From 3f8295020ee5228309a1c1e9a7116faf2c046888 Mon Sep 17 00:00:00 2001 From: Jim Duncan Date: Mon, 13 Jun 2022 15:03:09 -0700 Subject: [PATCH 355/458] Fixed typos --- documentation/Add-PnPSiteScriptPackage.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/Add-PnPSiteScriptPackage.md b/documentation/Add-PnPSiteScriptPackage.md index 2a32f2f15..650a76b4c 100644 --- a/documentation/Add-PnPSiteScriptPackage.md +++ b/documentation/Add-PnPSiteScriptPackage.md @@ -15,7 +15,7 @@ title: Add-PnPSiteScriptPackage * SharePoint: Access to the SharePoint Tenant Administration site -Creates a new Site Script Package on the current tenant. Site script packages can contain files in addition to the site scripts which can be used to upload files to sites om which a site design gets applied. +Creates a new Site Script Package on the current tenant. Site script packages can contain files in addition to the site scripts which can be used to upload files to sites on which a site template gets applied. ## SYNTAX @@ -50,7 +50,7 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -ConContentPathtent +### -ContentPath The full path to the locally stored Site Script Package to upload ```yaml From 6a15301c5913df437def4883e5737d952751bf4b Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 14 Jun 2022 04:04:51 +0000 Subject: [PATCH 356/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 52a7a4356..4e1947a29 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -e1f7e40500ae6265d6014d22d288803a2f866228 \ No newline at end of file +69cfe6fca3a01a894166a853e0a85a82635f0861 \ No newline at end of file diff --git a/version.txt b/version.txt index 65bf39c9f..7a9a17130 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.47 \ No newline at end of file +1.10.48 \ No newline at end of file From 5bc323062ac6dc8ffbdf26d1c04c4e619f6aad73 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 14 Jun 2022 14:06:57 +0200 Subject: [PATCH 357/458] Added changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ff71cab7..0f615e3b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Add-PnPListItemAttachment` cmdlet to provide ability to upload a file as an attachment to a SharePoint list item. [#1932](https://github.com/pnp/powershell/pull/1932) - Added `Remove-PnPListItemAttachment` cmdlet to provide ability to delete a list item attachment. [#1932](https://github.com/pnp/powershell/pull/1932) - Added `Get-PnPListItemAttachment` cmdlet to download the attachments from a list item. [#1932](https://github.com/pnp/powershell/pull/1932) +- Added `-PercentComplete`, `-Priority`, `-StartDateTime`, `-DueDateTime` and `-Description` to `Add-PnPPlannerTask` [#1964](https://github.com/pnp/powershell/pull/1964) ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) From 92459823a261b5666ea74f2bed4925c4b820b520 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 14 Jun 2022 14:12:54 +0200 Subject: [PATCH 358/458] Added entry to contributors --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ff71cab7..de0d58d16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -86,6 +86,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Contributors +- Arleta Wanat [PowershellScripts] - Yuriy Samorodov [YuriySamorodov] - Arleta Wanat [PowershellScripts] - Marc D Anderson [sympmarc] From 31e31baa9ef68e212c9db5ae3fc99be3ee1e11fc Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 14 Jun 2022 14:22:48 +0200 Subject: [PATCH 359/458] Adding to contributors section --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ff71cab7..7454e1490 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -86,6 +86,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Contributors +- Jim Duncan [sparkitect] - Yuriy Samorodov [YuriySamorodov] - Arleta Wanat [PowershellScripts] - Marc D Anderson [sympmarc] From 0edfddeef419fc8e4f3c752abaf486604d429864 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 14 Jun 2022 14:31:53 +0200 Subject: [PATCH 360/458] Small textual changes --- CHANGELOG.md | 3 ++- documentation/Set-PnPList.md | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50aaf8127..2f843cddc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,7 +47,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Add-PnPListItemAttachment` cmdlet to provide ability to upload a file as an attachment to a SharePoint list item. [#1932](https://github.com/pnp/powershell/pull/1932) - Added `Remove-PnPListItemAttachment` cmdlet to provide ability to delete a list item attachment. [#1932](https://github.com/pnp/powershell/pull/1932) - Added `Get-PnPListItemAttachment` cmdlet to download the attachments from a list item. [#1932](https://github.com/pnp/powershell/pull/1932) -- Added `-ExemptFromBlockDownloadOfNonViewableFiles` parameter to `Set-PnPList` cmdlet to configure access capabilites for unmanaged devices at list level. +- Added `-ExemptFromBlockDownloadOfNonViewableFiles` parameter to `Set-PnPList` cmdlet to configure access capabilites for unmanaged devices at list level. [#1973](https://github.com/pnp/powershell/pull/1973) + ### Changed - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) - Changed `Get-PnPOrgAssetsLibrary` to return a proper value of the organisation assets libraries. [#1889](https://github.com/pnp/powershell/pull/1889) diff --git a/documentation/Set-PnPList.md b/documentation/Set-PnPList.md index d80e359fc..eea32803c 100644 --- a/documentation/Set-PnPList.md +++ b/documentation/Set-PnPList.md @@ -25,6 +25,7 @@ Set-PnPList -Identity [-EnableContentTypes ] [-BreakRole ``` ## DESCRIPTION +Allows the configuration of a specific SharePoint Online list to be set. ## EXAMPLES @@ -382,7 +383,7 @@ Accept wildcard characters: False ``` ### -ExemptFromBlockDownloadOfNonViewableFiles -Boolean parameter , if specified allows to configure access capabilites for unmanaged devices at list level. +Allows to configure access capabilities for unmanaged devices for the list. If set to $true, the list will be accessible by unmanaged devices as well. For more information, see [SharePoint and OneDrive unmanaged device access controls for administrators](https://docs.microsoft.com/sharepoint/control-access-from-unmanaged-devices). ```yaml Type: Boolean @@ -397,5 +398,4 @@ Accept wildcard characters: False ## RELATED LINKS -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file From e18a34e69576b58d53ca595663d5712af1d66b39 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Tue, 14 Jun 2022 23:13:41 +0300 Subject: [PATCH 361/458] Feature: Added Set-PnPContentType cmdlet --- CHANGELOG.md | 82 ++++++-- documentation/Set-PnPContentType.md | 222 ++++++++++++++++++++ src/Commands/ContentTypes/SetContentType.cs | 111 ++++++++++ 3 files changed, 398 insertions(+), 17 deletions(-) create mode 100644 documentation/Set-PnPContentType.md create mode 100644 src/Commands/ContentTypes/SetContentType.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ff71cab7..f9b2fc9ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ # PnP.PowerShell Changelog + *Please do not commit changes to this file, it is maintained by the repo owner.* All notable changes to this project will be documented in this file. @@ -8,6 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ## [Current Nightly] ### Added + - Added `-Wait` and `-Verbose` optional paramarers to `New-PnPUPABulkImportJob` [#1752](https://github.com/pnp/powershell/pull/1752) - Added `Add-PnPTeamsChannelUser` which allows members and owners to be added to private channels in Teams [#1735](https://github.com/pnp/powershell/pull/1735) - Added `Channel` parameter to `Add-PnPTeamsUser` cmdlet which if specified will allow owners and members to be added to private channels in a Teams Team. [#1772](https://github.com/pnp/powershell/pull/1772) @@ -47,8 +49,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Add-PnPListItemAttachment` cmdlet to provide ability to upload a file as an attachment to a SharePoint list item. [#1932](https://github.com/pnp/powershell/pull/1932) - Added `Remove-PnPListItemAttachment` cmdlet to provide ability to delete a list item attachment. [#1932](https://github.com/pnp/powershell/pull/1932) - Added `Get-PnPListItemAttachment` cmdlet to download the attachments from a list item. [#1932](https://github.com/pnp/powershell/pull/1932) +- Added `Set-PnPContentType` cmdlet to update the properties of the Content Types in a list or a web. ### Changed + - Changed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to map users based on their Ids instead which should resolve some issues around user identities reporting not to exist. You can use the new `-IdType` option to switch it back to `PrincipalName` if needed. [#1752](https://github.com/pnp/powershell/pull/1752) - Changed `Get-PnPOrgAssetsLibrary` to return a proper value of the organisation assets libraries. [#1889](https://github.com/pnp/powershell/pull/1889) - Bumped .NET Framework version to 4.6.2 as the 4.6.1 is not supported anymore. [#1856](https://github.com/pnp/powershell/pull/1856) @@ -61,9 +65,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Service Health cmdlets have been improved, now are consistent with other cmdlets to handle pagination [#1938](https://github.com/pnp/powershell/pull/1938) ### Fixed + - Fixed `Get-PnPTenantSite` cmdlet so that it will return data even if the template name is specified in a different case. [#1773](https://github.com/pnp/powershell/pull/1773) - Fixed `Add-PnPDocumentSet` cmdlet so that it will support Document Set Content Type Id specified at the web level. [#1796](https://github.com/pnp/powershell/pull/1796) -- Fixed `Get-PnPGroup` , `Get-PnPGroupPermissions` and `Set-PnPGroupPermissions ` cmdlets by making them more consistent. They will also throw error if a group is not found. [#1808](https://github.com/pnp/powershell/pull/1808) +- Fixed `Get-PnPGroup` , `Get-PnPGroupPermissions` and `Set-PnPGroupPermissions` cmdlets by making them more consistent. They will also throw error if a group is not found. [#1808](https://github.com/pnp/powershell/pull/1808) - Fixed `Get-PnPFile` issue with every 3rd file download in PS 5. - Fixed `Add-PnPContentTypesFromContentTypeHub`, if `Site` parameter is specified, it will be used now to sync content types from content type hub site. - Fixed `Get-PnPTeamsTeam`, the cmdlet now also returns additional properties like `WebUrl, CreatedDateTime, InternalId` [#1825](https://github.com/pnp/powershell/pull/1825) @@ -81,6 +86,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Get-PnPPlannerTask` throwing an object reference exception for completed tasks [#1956](https://github.com/pnp/powershell/issues/1956) ### Removed + - Removed `Get-PnPAvailableClientSideComponents`. Use `Get-PnPPageComponent -Page -ListAvailable` instead. [#1833](https://github.com/pnp/powershell/pull/1833) - Removed `NextLink` property from `Get-PnPAzureADUser` cmdlet, as it was causing confusion. [#1930](https://github.com/pnp/powershell/pull/1930) @@ -185,7 +191,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Michael Vasiloff [mikevasiloff] - [svermaak] -- Russell Gove [russgove] +- Russell Gove [russgove] - Mike Park [mikeparkie] - Jerker Vallbo [jerval53] - Gaurav Mahajan [mahajangaurav] @@ -210,6 +216,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ## [1.9.0] ### Added + - Added `Get-PnPTenantInstance` which will return one or more tenant instances, depending if you have a multi-geo or single-geo (default) tenant. - Added optional `-ScheduledPublishDate` parameter to `Add-PnPPage` and `Set-PnPPage` to allow for scheduling a page to be published - Added `-RemoveScheduledPublish` to `Set-PnPPage` to allow for a page publish schedule to be removed @@ -227,7 +234,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added ability to add multiple users to a Teams team in the `Add-PnPTeamsUser` cmdlet - Added `-Credentials $cred` or `-CurrentCredentials` to be allowed to be used in combination with `Connect-PnPOnline -SPOManagementshell` - Added `-InformationBarriersMode` in the `Set-PnPTenantSite` cmdlet which allows fine tuning of the information barriers mode per site collection -- Added `-InformationBarriersSuspension` in the `Set-PnPTenant` cmdlet which allows information barriers to be enabled or disabled in a tenant +- Added `-InformationBarriersSuspension` in the `Set-PnPTenant` cmdlet which allows information barriers to be enabled or disabled in a tenant - Added `-Recycle` parameter to `Remove-PnPPage` to delete the page and send it to the recycle bin. This prevents permanently deleting the page and you can also restore it. - Added `-DemoteNewsArticle` parameter to the `Set-PnPPage` cmdlet to demote an existing news post to a regular page. - Added `-Translate` and `-TranslationLanguageCodes` parameters to `Set-PnPPage` and `Add-PnPPage`. This enables multilingual page creation in sites. @@ -248,6 +255,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `-BookmarkStatus` parameter to `Get-PnPSearchConfiguration` cmdlet to call REST endpoint to fetch promoted results defined via query rules and output them in Bookmark supported CSV format. ### Changed + - Improved `Get-PnPFile` cmdlet to handle large file downloads - Updated `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` to also allow results from `Get-PnPAzureADUser -Delta` to be provided through `-Users`. - A clearer error message will now be returned when using `Add-PnPListItem -List` and specifying an invalid list name. @@ -257,15 +265,16 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Improved documentation of `Get-PnPTaxonomyItem` with addition of new example and removing obsolete parameters. - Improved documentation of `Get-PnPTerm`, fixed typos. - Improved `Add-PnPHubToHubAssociation`. It will now throw error if both, source and destination, sites are not Hub sites, currently it fails silently without any information to the user. [#1390](https://github.com/pnp/powershell/pull/1390) - + ### Fixed + - Fixed `Get-PnPGroupMember -User` not properly returning the specified user - Fixed group member retrieval through `Get-PnPAzureADGroupOwner` and `Get-PnPAzureAdGroupMember` throwing an exception when a security group has been placed in the Azure Active Directory group being queried - Fixed an issue where `Set-PnPPage` would not be able to find a page if you would start the `-Identity` with a forward slash - Fixed an issue where `Set-PnPPage` would not return its parent Folder - Fixed `Set-PnPListItem` not working when using `Label` and `Values` parameters together - Fixed documentation for `Get-PnPFlow` and `Enable-PnPFlow` cmdlets -- Fixed issue with `Add-PnPListFoldersToProvisioningTemplate` not working when having nested folder structure +- Fixed issue with `Add-PnPListFoldersToProvisioningTemplate` not working when having nested folder structure - Fixed documentation for `Get-PnPFlow` and `Enable-PnPFlow` cmdlets - Fixed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` not being able to deal with multi value properties on the Azure Active Directory side, such as `BusinessPhones` - Fixed `Add-PnPListItem` issue with setting MultiChoice columns when using `-Batch` parameter @@ -279,10 +288,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Get-PnPTeamsTab` issue with missing TeamsApp object values. It will now populate TeamsApp object with Id, DisplayName, ExternalId and DistributionMethod properties if available. [#1459](https://github.com/pnp/powershell/pull/1459) ### Removed + - Removed `Add-PnPClientSidePage` as that was marked deprecated. Use `Add-PnPPage` instead. - Removed `Get-PnPSubWebs` as that was marked deprecated a year ago. Use `Get-PnPSubWeb` instead. [#1394](https://github.com/pnp/powershell/pull/1394) ### Contributors + - Mikael Svenson [wobba] - Koen Zomers [koenzomers] - Bert Jansen [jansenbe] @@ -309,6 +320,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ## [1.8.0] ### Added + - Added flexibility to mix and pipe `Add\Get\Remove-PnPListItem` with `Get-PnPList` - Added ability to remove all list items from a list using `Remove-PnPListItem -List ` and not providing a list item identifier. - Added `Get-PnPMessageCenterAnnouncent`, `Get-PnPServiceCurrentHealth` and `Get-PnPServiceHealthIssue` cmdlets which pull their data out of the Microsoft Graph API and are replacing the former `Get-PnPOffice365CurrentServiceStatus`, `Get-PnPOffice365HistoricalServiceStatus` and `Get-PnPoffice365ServiceMessage` cmdlets which pull their data from the Office Health and Communications API which is to be deprecated on December 17, 2021. If you're using any of these last three cmdlets, please rewrite your functionality to start using one of the first three cmdlets before this date. @@ -324,6 +336,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` cmdlet which allows direct synchronization of user profile properties of choice between user profiles in Azure Active Directory and their SharePoint Online User Profile Service user profile equivallents ### Changed + - Renamed `Get-PnPFlowEnvironment` to `Get-PnPPowerAutomateEnvironment` - Changed `Get-PnPSiteScriptFromWeb` to get a site script of the currently connected to site if `-Url` is omitted. - Improved `Find-PnPFile` error message @@ -335,6 +348,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Changed that `Get-PnPUnifiedAuditLog` returns the error being returned by the Office Management API service, in case something goes wrong [#1631](https://github.com/pnp/powershell/pull/1631) ### Fixed + - Fixed `Get-PnPChangeLog -Nightly` not returning anything - Fixed issue with `Get-PnPUser -Identity x` ignoring additional requested attributes using `-Includes` - Fixed issue with `Set-PnPDefaultColumnValues -List "Documents" -Folder "Földer" -Field "Text" -Value "123"` not working when having a folder name with special characters in it. @@ -350,6 +364,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed an issue where running `Get-PnPSiteDesign -Identity` passing in an identifier that did not exist would return an exception [#1622](https://github.com/pnp/powershell/pull/1622) ### Removed + - Removed `ConvertTo-PnPClientSidePage` cmdlet as it has been replaced by `ConvertTo-PnPPage` this option is not enabled yet on your tenant in which case trying to set it results in to `Set-PnPTenant: The requested operation is part of an experimental feature that is not supported in the current environment.`. In that case try again later. - Removed `Add-PnPUserToGroup` as it has been replaced by `Add-PnPGroupMember` @@ -388,6 +403,7 @@ this option is not enabled yet on your tenant in which case trying to set it res - Added `-DisableBackToClassic` option to Set-PnPTenant ### Contributors + - [thomassmart] - Bert Jansen [jansenbe] @@ -404,6 +420,7 @@ this option is not enabled yet on your tenant in which case trying to set it res ### Changed ### Contributors + - Bert Jansen [jansenbe] - Koen Zomers [koenzomers] - Gautam Sheth [gautamdsheth] @@ -412,6 +429,7 @@ this option is not enabled yet on your tenant in which case trying to set it res ## [1.4.0] ### Added + - Added `-IncludeOwners` to `Get-PnPMicrosoft365Group`. - Added `-AssignedTo` to `Add-PnPPlannerTask` and `Set-PnPPlannerTask` allowing you to assign users to a task. - Added `Get-PnPAzureADApp`, `Get-PnPAzureADAppPermission` and `Remove-PnPAzureADApp` to manage Azure AD apps. @@ -422,6 +440,7 @@ this option is not enabled yet on your tenant in which case trying to set it res - Added `-SkipHiddenWebParts` parameter to the `ConvertTo-PnPPage` cmdlet that allows to skip hidden webparts during page transformation ### Changed + - Improved batching speed when creating or updating multiple items that set similar values for taxonomy fields. - Changed `Register-PnPAzureADApp` registration to by default turn on the ability to authenticate with credentials for a newly created Azure App registration (`allowPublicClient: true`). - Refactored `Register-PnPAzureADApp`. Marked `-Scopes` as obsolete and introduced `-GraphApplicationPermissions`, `-GraphDelegatePermissions`, `-SharePointApplicationPermissions` and `-SharePointDelegatePermissions`. Added additional permission scopes. @@ -430,6 +449,7 @@ this option is not enabled yet on your tenant in which case trying to set it res - Documentation updates ### Contributors + - Mahesh Chintha [chinthamahesh] - John Bontjer [JohnBontjer] - Todd Klindt [ToddKlindt] @@ -444,6 +464,7 @@ this option is not enabled yet on your tenant in which case trying to set it res ## [1.3.0] ### Added + - Added `-HideTitleInHeader` parameter to `Set-PnPWeb` to hide or show the title in the header. Use `-HideTitleInHeader` to hide it and `-HideTitleInHeader:$false` to show it. - Added `-ShowContentUrl` parameter to `Register-PnPManagementShellAccess` retrieve the url to consent to the PnP Management Shell application by an administrator. - Added `-IsFavoriteByDefault` parameter on Set-PnPTeamsChannel and Add-PnPTeamsChannel @@ -451,6 +472,7 @@ this option is not enabled yet on your tenant in which case trying to set it res - Added `-Interactive` login option to `Connect-PnPOnline` which is similar to `-UseWebLogin` but without the limitations of the latter. The `-UseWebLogin` is using cookie based authentication towards SharePoint and cannot access Graph tokens. Using `-Interactive` we use Azure AD Authentication and as a result we are able to acquire Graph tokens. ### Changed + - Fixed certificate clean up issue on Windows platform when using `Connect-PnPOnline` with a certificate. - Fixed issues with `Register-PnPAzureADApp` when using the various auth options (-DeviceLogin / -Interactive) - Renamed the `-PnPManagementShell` option to `-DeviceLogin` on `Connect-PnPOnline`. `-PnPManagementShell` is still available as an alias. @@ -462,28 +484,30 @@ this option is not enabled yet on your tenant in which case trying to set it res - Get-PnPUser and any other cmdlet that takes a UserPipeBind parameter as input now allows users to be specified by name besides loginname or id. - Fixed issue where retrieving a single site with Get-PnPTenantSite vs retrieving all sites showed different properties. - Invoke-PnPSPRestMethod now returns usable objects -- Updated `Set-PnPListItem` to have an `UpdateType` parameter. Obsoleted `SystemUpdate`. Also updated the backend logic so can now also specify `UpdateOverwriteVersion` to update the editor, author, modified and created fields. +- Updated `Set-PnPListItem` to have an `UpdateType` parameter. Obsoleted `SystemUpdate`. Also updated the backend logic so can now also specify `UpdateOverwriteVersion` to update the editor, author, modified and created fields. - `Register-PnPAzureADApp` now outputs the base64 encoded version of the certificate which can be used with `Connect-PnPOnline -ClientId -CertificateBase64Encoded` -- Fixed issue with moving and copying files to subfolder, Issue #165. +- Fixed issue with moving and copying files to subfolder, Issue #165. - fixed issue where Get-PnPTenantSite was not returning all properties correct, Issue #151 - Added `-Interactive` login option to Register-PnPManagementApp which allows for an interactive authentication flow not using device login for environments that require Multi-Factor Authentication. - Updated all Microsoft365Group cmdlets to only load the SiteUrl of the underlying Microsoft 365 Group where required. This means that `Get-PnPMicrosoft365Group -Identity` will not by default load the site url. Specify `-IncludeSiteUrl` to include it. ### Contributors + - Mike Jensen [michael-jensen] - Koen Zomers [koenzomers] - Gautam Sheth [gautamdsheth] - Todd Klindt [ToddKlindt] - Giacomo Pozzoni [jackpoz] - ## [1.2.0] ### Added + - Added `-NoWait` switch to `Copy-PnPFile` and `Move-PnPFile` - Added `Receive-PnPCopyMoveJobStatus` cmdlet which works in combination with the `-NoWait` parameter on `Copy-PnPFile` and `Move-PnPFile`. See the documentation for usage. ### Changed + - Fixed issue with `Invoke-PnPSPRestMethod` to throw an non authorized exception in certain scenarios. - Fixed issue with using `-UseWebLogin` and site names longer than the length of the managed path it resides in. - Fixed issue with tenant admin site detection on environment with vanity domains @@ -493,10 +517,12 @@ this option is not enabled yet on your tenant in which case trying to set it res ## [1.1.3-nightly] ### Added + - Added `-NoWait` switch to `Copy-PnPFile` and `Move-PnPFile` - Added `Receive-PnPCopyMoveJobStatus` cmdlet which works in combination with the `-NoWait` parameter on `Copy-PnPFile` and `Move-PnPFile`. See the documentation for usage. ### Changed + - Fixed issue with `Invoke-PnPSPRestMethod` to throw an non authorized exception in certain scenarios. - Fixed issue with using `-UseWebLogin` and site names longer than the length of the managed path it resides in. @@ -515,48 +541,51 @@ this option is not enabled yet on your tenant in which case trying to set it res First released version of PnP PowerShell - ## [0.3.40-nightly] ### Added + - Added `Get-PnPFlow`, `Get-PnPFlowEnvironment`, `Enable-PnPFlow`, `Disable-PnPFlow`, `Remove-PnPFlow`, `Export-PnPFlow` cmdlets ### Changed + - Documentation updates ### Contributors -- Yannick Reekmans [YannickRe] +- Yannick Reekmans [YannickRe] ## [0.3.38-nightly] ### Added + - Reintroduced `-CertificateBase64Encoded` on `Connect-PnPOnline` ### Changed ### Contributors - ## [0.3.37-nightly] ### Added ### Changed -- Reorganized Connect-PnPOnline and simplified/cleared up usage. See https://pnp.github.io/powershell/cmdlets/connect-pnponline.html and https://pnp.github.io/powershell/articles/connecting.html for more information. + +- Reorganized Connect-PnPOnline and simplified/cleared up usage. See and for more information. - Reorganized internals with regards to access token handling. ### Contributors - ## [0.3.36-nightly] ### Added ### Changed + - Fixed issue with `Set-PnPGroupPermissions` not removing roles from list correctly. ### Contributors + - Leon Armston [leonarmston] - Koen Zomers [koenzomers] @@ -565,63 +594,72 @@ First released version of PnP PowerShell ### Added ### Changed + - Updated certificate handling for `Register-PnPAzureADApp` and `New-PnPAzureCertificate` - Updated `Register-PnPAzureApp` to use popup windows on Microsoft Windows. Added the `-NoPopup` switch to disable this behavior. -- Updated `Invoke-PnPBatch` to fully execute a batch by default if one of the requests in the large batch throws an exception. Specify the `-StopOnException` switch to immmediately stop after an exception occurs. The rest of the batch will be skipped where possible. See https://pnp.github.io/powershell/articles/batching for more information. +- Updated `Invoke-PnPBatch` to fully execute a batch by default if one of the requests in the large batch throws an exception. Specify the `-StopOnException` switch to immmediately stop after an exception occurs. The rest of the batch will be skipped where possible. See for more information. - Documentation updates ### Contributors + - Leon Armston [leonarmston] ## [0.3.33-nightly] ### Added + - Added -ClientSideHostProperties to `Set-PnPApplicationCustomizer` - Documentation updates for Teams cmdlets ### Changed ### Contributors -- Leon Armston [leonarmston] +- Leon Armston [leonarmston] ## [0.3.32-nightly] ### Added + - Added batching support to `Remove-PnPListItem` ### Changed ### Contributors - ## [0.3.31-nightly] ### Added + - Added initial batching support. See `New-PnPBatch`, `Invoke-PnPBatch`, `Add-PnPListItem` and `Set-PnPListItem` - Updated documentation ### Changed -- Deprecated the use of the `-Web` cmdlet parameters due to API reasons. Use `Connect-PnPOnline -Url [fullsubweburl]` instead to connect to a subweb. + +- Deprecated the use of the `-Web` cmdlet parameters due to API reasons. Use `Connect-PnPOnline -Url [fullsubweburl]` instead to connect to a subweb. - Updated `Get-PnPLabel` to allow returning available compliance tags for a site - Updated several cmdlets to use the Code SDK behind the scenes ### Contributors + - Veronique Lengelle [veronicageek] - Leon Armston [leonarmston] ## [0.3.27-nightly] ### Added + - Added `Get-PnPListPermissions` cmdlet. ### Changed + - Fixed issue where using `Connect-PnPOnline` in a loop would through after several iterations an exception message from MSAL not being able to retrieve a token due to a looped request. We fixed this by trying to reuse the in-memory token cache in scenarios where non-interactive logins are being used. - `Connect-PnPOnline -Url [url] -PnPManagementShell -LaunchBrowser` will not try to attempt to close the popup window automatically anymore. - `Set-PnPLabel` will now check first if a label is available. - Documentation fixes ### Contributors + - Leon Armston [leonarmston] - Bhishma Bhandari [bhishma] @@ -640,6 +678,7 @@ First released version of PnP PowerShell ### Added ### Changed + - Fixed issue when using `Connect-PnPOnline` using either `-UseWebLogin` or `-SPOManagementShell` and invoking a site template containing modern pages, or when trying to create or update modern pages using the PnP Cmdlets. ## [0.3.20-nightly] @@ -647,20 +686,24 @@ First released version of PnP PowerShell ### Added ### Changed + - `Register-PnPManagementShellAccess` will not automatically close the window after consent anymore. - `Connect-PnPOnline -UseWebLogin` now allows you to return the connection with `-ReturnConnection` [PR #71](https://github.com/pnp/powershell/71) - `Remove-PnPTermGroup` now includes a `-Force` parameter [PR #70](https://github.com/pnp/powershell/pull/70) - `Get-PnPListItem` now can filter on both the GUID or the UniqueId value by specifying the -UniqueId parameter. [PR #68](https://github.com/pnp/powershell/pull/68) ### Contributors + - Gautam Sheth [gautamdsheth] ## [0.3.*-nightly] ### Added + - Added `-ReadSecurity` and `-WriteSecurity` to `Set-PnPList` cmdlet (0.3.15) ### Changed + - Renamed `Add-PnPClientSidePage` to `Add-PnPPage` (0.3.15) - Renamed `Add-PnPClientSidePageSection` to `Add-PnPPageSection` (0.3.15) - Renamed `Add-PnPClientSideText` to `Add-PnPPageTextPart` (0.3.15) @@ -688,6 +731,7 @@ First released version of PnP PowerShell - Added `GrouplessTeamSite` option to `-WebTemplate` parameter for `Add-PnPSiteDesign` and `Set-PnPSiteDesign` (0.3.6) ### Contributors + - Gautam Sheth [gautamdsheth] - Todd Klindt [toddklindt] - Michael Jensen [michael-jensen] @@ -698,6 +742,7 @@ First released version of PnP PowerShell ## [0.2.*-nightly] ### Added + - Added `Convert-PnPSiteTemplateToMarkdown` to convert an existing XML based template to a markdown report. Notice that this is work in progress and the functionality will increase. See also the 'Changed' section below for information about `Get-PnPSiteTemplate` (0.3.5) - Added `-UseWeblogin` and `-ForceAuthentication` to `Connect-PnPOnline` to allow using Multi-Factor Authentication. Notice this uses cookie based authentication, which is limited in its functionality. Using -UseWebLogin we will for instance not be able to acquire an access token for the Graph, and as a result none of the Graph related cmdlets will work. Also some of the functionality of the provisioning engine (`Get-PnPSiteTemplate`, `Get-PnPTenantTemplate`, `Invoke-PnPSiteTemplate`, `Invoke-PnPTenantTemplate`) will not work because of this reason. The cookies will in general expire within a few days and if you use `-UseWebLogin` within that time popup window will appear that will dissappear immediately, this is expected. Use `-ForceAuthentication` to reset the authentication cookies and force a new login. (0.2.25) - Allowed to specify -ClientId when logging in with credentials so people can use their own Azure AD apps and credentials for authentication towards SharePoint Online (0.2.17) @@ -713,6 +758,7 @@ First released version of PnP PowerShell ## [0.1.*-nightly] ### Added + - Added `Add-PnPHubToHubAssociation` cmdlet. - Added `Export-PnPUserInfo` cmdlet. - Added `Add-PnPSiteScriptPackage` cmdlet. @@ -768,6 +814,7 @@ First released version of PnP PowerShell - Added `Set-PnPPlannerTask` cmdlet (0.1.18) ### Changed + - Added filename support for .md file with `Get-PnPSiteTemplate` to generate a markdown file. e.g. you can now execute for instance `Get-PnPSiteTemplate -Out .\myreport.md -Handlers Lists,ContentTypes,Features,WebSettings` to generate an markdown report of those artifacts. This is work in progress. - Fixed issue with `-UseWebLogin` throws a 403 error when connecting to a different site collection than the root site collection. - Removed `Enable-PnPPowerShellTelemetry` and `Disable-PnPPowerShellTelemetry`. See [Configure PnP PowerShell](https://pnp.github.io/powershell) for more information on how to enable or disable telemetry collection (0.2.22) @@ -845,6 +892,7 @@ First released version of PnP PowerShell - Fixed several issues with `Get-PnPSubwebs` and added optional parameter `-IncludeRootWeb` [PR #3011](https://github.com/pnp/PnP-PowerShell/pull/3011) ### Contributors + - Koen Zomers [koenzomers] - Carlos Marins Jr [kadu-jr] - Aimery Thomas [a1mery] diff --git a/documentation/Set-PnPContentType.md b/documentation/Set-PnPContentType.md new file mode 100644 index 000000000..80c6ce2dc --- /dev/null +++ b/documentation/Set-PnPContentType.md @@ -0,0 +1,222 @@ +--- +Module Name: PnP.PowerShell +title: Set-PnPContentType +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Set-PnPContentType.html +--- + +# Set-PnPContentType + +## SYNOPSIS + +Updates a content type in a web or a list + +## SYNTAX + +```powershell +Set-PnPContentType [-Identity] [-List] [-InSiteHierarchy] +[-UpdateChildren] [-Name] [-Description] [-Group] +[-Hidden] [-ReadOnly] [-Sealed] + [-Connection ] [] +``` + +## DESCRIPTION + +## EXAMPLES + +### EXAMPLE 1 + +```powershell +Set-PnPContentType -Identity "Project Document" -UpdateChildren -Name "Project Documentation" -Description "Documentation for projects" +``` + +This will update a content type called "Project Document" in the current web and rename it to "Project Documentation" and change its description to "Documentation for projects" + +### EXAMPLE 2 + +```powershell +Set-PnPContentType -Identity "Project Document" -UpdateChildren -Group "Custom Content Types" -Hidden +``` + +This will update a content type called "Project Document" in the current web, make it hidden and change its group to "Custom Content Types". + +### EXAMPLE 3 + +```powershell +Set-PnPContentType -Identity "Project Document" -List "Projects" -Name "Project Documentation" -Description "Documentation for projects" +``` + +This will update a content type called "Project Document" in the list called "Projects" in the current web and rename it to "Project Documentation" and change its description to "Documentation for projects". + +## PARAMETERS + +### -Connection + +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Identity + +The name or ID of the content type to update + +```yaml +Type: ContentTypePipeBind +Parameter Sets: (All) + +Required: True +Position: 0 +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -List + +The list in which the content type to be updated resides. + +```yaml +Type: ListPipeBind +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -InSiteHierarchy + +Search site hierarchy for content types + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -UpdateChildren + +Specify if you want to update the child content types + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Name + +The updated name of the content type. + +```yaml +Type: String +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Description + +The updated description of the content type. + +```yaml +Type: String +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Group + +The updated group to which the content type belongs. + +```yaml +Type: String +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Hidden + +Specify if you want to hide the content type. + +```yaml +Type: Boolean +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ReadOnly + +Specify if you want to set the content type as read only. + +```yaml +Type: Boolean +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Sealed + +Specify if you want to seal the content type. + +```yaml +Type: Boolean +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) diff --git a/src/Commands/ContentTypes/SetContentType.cs b/src/Commands/ContentTypes/SetContentType.cs new file mode 100644 index 000000000..0a50fab34 --- /dev/null +++ b/src/Commands/ContentTypes/SetContentType.cs @@ -0,0 +1,111 @@ +using Microsoft.SharePoint.Client; +using PnP.PowerShell.Commands.Base.PipeBinds; +using System.Management.Automation; + +namespace PnP.PowerShell.Commands.ContentTypes +{ + [Cmdlet(VerbsCommon.Set, "PnPContentType")] + public class SetContentType : PnPWebCmdlet + { + [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true)] + public ContentTypePipeBind Identity; + + [Parameter(Mandatory = false, ValueFromPipeline = true)] + [ValidateNotNullOrEmpty] + public ListPipeBind List; + + [Parameter(Mandatory = false, ValueFromPipeline = false)] + public SwitchParameter InSiteHierarchy; + + [Parameter(Mandatory = false, ValueFromPipeline = false)] + public SwitchParameter UpdateChildren; + + [Parameter(Mandatory = false, ValueFromPipeline = false)] + public string Name; + + [Parameter(Mandatory = false, ValueFromPipeline = false)] + public string Description; + + [Parameter(Mandatory = false, ValueFromPipeline = false)] + public string Group; + + [Parameter(Mandatory = false, ValueFromPipeline = false)] + public bool Hidden; + + [Parameter(Mandatory = false, ValueFromPipeline = false)] + public bool ReadOnly; + + [Parameter(Mandatory = false, ValueFromPipeline = false)] + public bool Sealed; + + protected override void ExecuteCmdlet() + { + ContentType ct = null; + List list = null; + if (List != null) + { + list = List?.GetListOrThrow(nameof(List), CurrentWeb); + + ct = Identity.GetContentTypeOrError(this, nameof(Identity), list); + } + else + { + ct = Identity?.GetContentTypeOrThrow(nameof(Identity), CurrentWeb, InSiteHierarchy); + } + + bool updateRequired = false; + if (ct != null) + { + if (ParameterSpecified(nameof(Name))) + { + ct.Name = Name; + updateRequired = true; + } + + if (ParameterSpecified(nameof(Description))) + { + ct.Description = Description; + updateRequired = true; + } + + if (ParameterSpecified(nameof(Group))) + { + ct.Group = Group; + updateRequired = true; + } + + if (ParameterSpecified(nameof(Hidden))) + { + ct.Hidden = Hidden; + updateRequired = true; + } + + if (ParameterSpecified(nameof(ReadOnly))) + { + ct.ReadOnly = ReadOnly; + updateRequired = true; + } + + if (ParameterSpecified(nameof(Sealed))) + { + ct.Sealed = Sealed; + updateRequired = true; + } + + if (updateRequired) + { + if(list != null) + { + ct.Update(false); + } + else + { + ct.Update(UpdateChildren); + } + ClientContext.ExecuteQueryRetry(); + WriteObject(ct); + } + } + } + } +} From 0b9a613f7a481016248939b92daf908b76c0519a Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 15 Jun 2022 01:15:58 +0200 Subject: [PATCH 362/458] Added verbose logging --- src/Commands/ContentTypes/SetContentType.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Commands/ContentTypes/SetContentType.cs b/src/Commands/ContentTypes/SetContentType.cs index 0a50fab34..92b461961 100644 --- a/src/Commands/ContentTypes/SetContentType.cs +++ b/src/Commands/ContentTypes/SetContentType.cs @@ -96,15 +96,21 @@ protected override void ExecuteCmdlet() { if(list != null) { + WriteVerbose("Updating content type on list"); ct.Update(false); } else { + WriteVerbose("Updating site content type"); ct.Update(UpdateChildren); } ClientContext.ExecuteQueryRetry(); WriteObject(ct); } + else + { + WriteVerbose("No changes to make"); + } } } } From dc93cba7709ca55cb0d884fa983562fcf5a396b4 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 15 Jun 2022 01:18:27 +0200 Subject: [PATCH 363/458] Adding -Verbose option to documentation --- documentation/Set-PnPContentType.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/documentation/Set-PnPContentType.md b/documentation/Set-PnPContentType.md index 80c6ce2dc..c6bd7639b 100644 --- a/documentation/Set-PnPContentType.md +++ b/documentation/Set-PnPContentType.md @@ -19,11 +19,13 @@ Updates a content type in a web or a list Set-PnPContentType [-Identity] [-List] [-InSiteHierarchy] [-UpdateChildren] [-Name] [-Description] [-Group] [-Hidden] [-ReadOnly] [-Sealed] - [-Connection ] [] + [-Connection ] [-Verbose] [] ``` ## DESCRIPTION +Allows modification of the settings of a content type in a list or site. + ## EXAMPLES ### EXAMPLE 1 @@ -217,6 +219,20 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Verbose +When provided, additional debug statements will be shown while updating the content type. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) From 987070bdfb699e50622ed41520f1822096d85144 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 15 Jun 2022 01:23:40 +0200 Subject: [PATCH 364/458] Minor textual changes --- documentation/Add-PnPViewsFromXML.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/documentation/Add-PnPViewsFromXML.md b/documentation/Add-PnPViewsFromXML.md index c2739eb00..acf273d4f 100644 --- a/documentation/Add-PnPViewsFromXML.md +++ b/documentation/Add-PnPViewsFromXML.md @@ -10,7 +10,7 @@ title: Add-PnPViewsFromXML # Add-PnPViewsFromXML ## SYNOPSIS -Adds a view(s) to a list from an XML string. +Adds one or more views to a list from an XML string. ## SYNTAX @@ -21,6 +21,8 @@ Add-PnPViewsFromXML [-List] [-ViewsXML ] ## DESCRIPTION +This cmdlet allows the creation of one or more views on a SharePoint Online list based on passing in an XML definition with the view details. + ## EXAMPLES ### EXAMPLE 1 @@ -32,7 +34,7 @@ $viewsXML = @"             @@ -43,7 +45,7 @@ $viewsXML = @" Add-PnPViewsFromXML -List "Demo List" -ViewsXML $viewsXML ``` -Adds a view named "Demo view" to the "Demo List" list from the XML string. +Adds one view named "Demo view" to the "Demo List" list from the XML string. ### EXAMPLE 2 ```powershell @@ -71,7 +73,7 @@ $viewsXML = @"             @@ -82,7 +84,7 @@ $viewsXML = @" Add-PnPViewsFromXML -List "Demo List" -ViewsXML $viewsXML ``` -Adds a view named "Demo view" and "Created By Me" to the "Demo List" list from the XML string. +Adds two views named "Demo view" and "Created By Me" to the "Demo List" list from the XML string. ## PARAMETERS @@ -101,7 +103,7 @@ Accept wildcard characters: False ``` ### -List -The ID or Url of the list. +The ID or Url of the list to add the view to. ```yaml Type: ListPipeBind @@ -115,7 +117,7 @@ Accept wildcard characters: False ``` ### -ViewsXML -The XML string of the Views that you want to create in a list. +The XML string of the view(s) that you want to add to the list. ```yaml Type: string @@ -128,9 +130,6 @@ Accept pipeline input: False Accept wildcard characters: False ``` - ## RELATED LINKS -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - - +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file From 9acffeac5c991bac810f9bd421934efe90e38005 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 15 Jun 2022 01:26:09 +0200 Subject: [PATCH 365/458] Minor textual change --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 348d52f55..a9e27b26f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,7 +49,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Add-PnPListItemAttachment` cmdlet to provide ability to upload a file as an attachment to a SharePoint list item. [#1932](https://github.com/pnp/powershell/pull/1932) - Added `Remove-PnPListItemAttachment` cmdlet to provide ability to delete a list item attachment. [#1932](https://github.com/pnp/powershell/pull/1932) - Added `Get-PnPListItemAttachment` cmdlet to download the attachments from a list item. [#1932](https://github.com/pnp/powershell/pull/1932) -- Added `Add-PnPViewsFromXML` cmdlet to create multiple views in a list from an XML string. [#1963](https://github.com/pnp/powershell/pull/1963) +- Added `Add-PnPViewsFromXML` cmdlet to create one or more views in a list based on an XML string. [#1963](https://github.com/pnp/powershell/pull/1963) - Added `-ExemptFromBlockDownloadOfNonViewableFiles` parameter to `Set-PnPList` cmdlet to configure access capabilites for unmanaged devices at list level. [#1973](https://github.com/pnp/powershell/pull/1973) - Added `-PercentComplete`, `-Priority`, `-StartDateTime`, `-DueDateTime` and `-Description` to `Add-PnPPlannerTask` [#1964](https://github.com/pnp/powershell/pull/1964) - Added `Set-PnPContentType` cmdlet to update the properties of the Content Types in a list or a web. [#1981](https://github.com/pnp/powershell/pull/1981) From 118c6d0613e78c5688f07f482f15608cfee81ae1 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 15 Jun 2022 01:30:10 +0200 Subject: [PATCH 366/458] Adding changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ff71cab7..a45d2d6ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,6 +79,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed Graph endpoints for non-commercial clouds for Managed Identity and Teams cmdlets [#1944](https://github.com/pnp/powershell/pull/1944) - Fixed `Add-PnPTeamsUser`, the parameter `-Channel` is now not required. [#1953](https://github.com/pnp/powershell/pull/1953) - Fixed `Get-PnPPlannerTask` throwing an object reference exception for completed tasks [#1956](https://github.com/pnp/powershell/issues/1956) +- Fixed `Get-PnPUserOneDriveQuota` returning the maximum possible quota instead of the actual configured quota on a OneDrive for Business site [#1902](https://github.com/pnp/powershell/pull/1902) ### Removed - Removed `Get-PnPAvailableClientSideComponents`. Use `Get-PnPPageComponent -Page -ListAvailable` instead. [#1833](https://github.com/pnp/powershell/pull/1833) From beb8874417c48b4776b0a79c7bbe44f31ccc4edf Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 15 Jun 2022 01:35:12 +0200 Subject: [PATCH 367/458] Updated documentation --- documentation/Get-PnPUserOneDriveQuota.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/documentation/Get-PnPUserOneDriveQuota.md b/documentation/Get-PnPUserOneDriveQuota.md index 5f9318874..a5e2f629c 100644 --- a/documentation/Get-PnPUserOneDriveQuota.md +++ b/documentation/Get-PnPUserOneDriveQuota.md @@ -15,7 +15,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Get-PnPUserOneDriveQuot * SharePoint: Access to the SharePoint Tenant Administration site -Retrieves the current quota set on the OneDrive for Business site for a specific user +Retrieves the current quota set on the OneDrive for Business site for a specific user in bytes. ## SYNTAX @@ -24,7 +24,7 @@ Get-PnPUserOneDriveQuota [-Account] [-Connection ] [-admin.sharepoint.com) with Connect-PnPOnline in order to use this cmdlet. +This command allows you to request the quota set on the OneDrive for Business site of a specific user. ## EXAMPLES @@ -33,7 +33,14 @@ This command allows you to request the quota set on the OneDrive for Business si Get-PnPUserOneDriveQuota -Account 'user@domain.com' ``` -Returns the quota set on the OneDrive for Business site for the specified user +Returns the quota set on the OneDrive for Business site for the specified user in bytes + +### EXAMPLE 2 +```powershell +(Get-PnPUserOneDriveQuota -Account 'user@domain.com') / 1gb +``` + +Returns the quota set on the OneDrive for Business site for the specified user in gigabytes ## PARAMETERS @@ -67,5 +74,4 @@ Accept wildcard characters: False ## RELATED LINKS -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file From 551ca5ea075c6fb8e1df79038cb9939a98f495ac Mon Sep 17 00:00:00 2001 From: James May Date: Wed, 15 Jun 2022 13:04:53 +1000 Subject: [PATCH 368/458] Remove-PnPListItem: fix parametersets --- src/Commands/Lists/RemoveListItem.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Commands/Lists/RemoveListItem.cs b/src/Commands/Lists/RemoveListItem.cs index ab0485f73..8fe6e2732 100644 --- a/src/Commands/Lists/RemoveListItem.cs +++ b/src/Commands/Lists/RemoveListItem.cs @@ -19,7 +19,7 @@ namespace PnP.PowerShell.Commands.Lists )] public class RemoveListItem : PnPWebCmdlet { - const string ParameterSet_ALL_DELETE = "Delete single item in list"; + const string ParameterSet_ALL_DELETE = "Delete all items in list"; const string ParameterSet_ALL_RECYCLE = "Recycle all items in list"; const string ParameterSet_BATCHED = "Batched"; @@ -29,6 +29,8 @@ public class RemoveListItem : PnPWebCmdlet const string ParameterSet_SINGLE_List_RECYCLE = "Recycle single item in list"; const string ParameterSet_SINGLE_ListItem_RECYCLE = "Recycle single item"; + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, ParameterSetName = ParameterSet_ALL_DELETE)] + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, ParameterSetName = ParameterSet_ALL_RECYCLE)] [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, ParameterSetName = ParameterSet_BATCHED)] [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, ParameterSetName = ParameterSet_SINGLE_List_DELETE)] [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0, ParameterSetName = ParameterSet_SINGLE_List_RECYCLE)] @@ -43,10 +45,10 @@ public class RemoveListItem : PnPWebCmdlet [ValidateNotNull] public ListItemPipeBind Identity; - [Parameter(ParameterSetName = ParameterSet_ALL_RECYCLE)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_ALL_RECYCLE)] [Parameter(ParameterSetName = ParameterSet_BATCHED)] - [Parameter(ParameterSetName = ParameterSet_SINGLE_List_RECYCLE)] - [Parameter(ParameterSetName = ParameterSet_SINGLE_ListItem_RECYCLE)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_SINGLE_List_RECYCLE)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_SINGLE_ListItem_RECYCLE)] public SwitchParameter Recycle; [Parameter(ParameterSetName = ParameterSet_ALL_DELETE)] From 7584fc02fae58735a902ffc5786977b07f93a7e3 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 15 Jun 2022 03:53:04 +0000 Subject: [PATCH 369/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index d2fb231dd..eea70698f 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -c9df0671671363a7c5e739ad59746b9b4ec556cf \ No newline at end of file +a56428650313f3b7443ab54db645cedae2a3a7e2 \ No newline at end of file diff --git a/version.txt b/version.txt index 7a9a17130..23f20f5fb 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.48 \ No newline at end of file +1.10.49 \ No newline at end of file From aed80d778daed635fb01f112e391098101037599 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 15 Jun 2022 09:11:20 +0200 Subject: [PATCH 370/458] Allow connection param on all cmdlets (#1982) * Connection param has been moved up in the stack so it can be used with any cmdlet * Changed GraphHelper to require a PnPConnection instead of a HttpClient. The HttpClient is now part of a PnPConnection. The RestHelper can be used to make Http calls without an active PnPConnection instance. * Added changelog entry * Fixing PnPAdminCmdlets to also work properly with -Connection * Moved Azure Application Insights tracking to a higher level to not only track the usage of the SharePoint cmdlets, but all cmdlets inheriting from PnPConnectedCmdlet * Trying to fix documentation build error * Added the -Connection parameter back to Disconnect-PnPOnline and made it obsolete --- CHANGELOG.md | 4 +- documentation/Disconnect-PnPOnline.md | 28 +-- src/Commands/Admin/PublishCompanyApp.cs | 2 +- .../Apps/GetAzureADAppSitePermission.cs | 6 +- .../Apps/GrantAzureADAppSitePermission.cs | 4 +- .../GrantTenantServicePrincipalPermission.cs | 6 +- .../Apps/RevokeAzureADAppSitePermission.cs | 4 +- .../RevokeTenantServicePrincipalPermission.cs | 4 +- .../Apps/SetAzureADAppSitePermission.cs | 4 +- src/Commands/AzureAD/GetAzureADApp.cs | 4 +- .../AzureAD/GetAzureADAppPermission.cs | 4 +- src/Commands/AzureAD/GetAzureADUser.cs | 4 +- src/Commands/AzureAD/RegisterAzureADApp.cs | 4 +- src/Commands/AzureAD/RemoveAzureADApp.cs | 8 +- src/Commands/AzureAD/SetAzureADGroup.cs | 2 +- src/Commands/Base/DisconnectOnline.cs | 38 +-- src/Commands/Base/GetAccessToken.cs | 6 +- .../Base/PipeBinds/AzureADAppPipeBind.cs | 8 +- .../PipeBinds/Microsoft365GroupPipeBind.cs | 24 +- .../Base/PipeBinds/PlannerBucketPipeBind.cs | 5 +- .../Base/PipeBinds/PlannerGroupPipeBind.cs | 7 +- .../Base/PipeBinds/PlannerPlanPipeBind.cs | 15 +- .../Base/PipeBinds/PlannerRosterPipeBind.cs | 6 +- src/Commands/Base/PipeBinds/SitePipeBind.cs | 5 +- .../Base/PipeBinds/TeamsAppPipeBind.cs | 8 +- .../PipeBinds/TeamsChannelMemberPipeBind.cs | 10 +- .../Base/PipeBinds/TeamsChannelPipeBind.cs | 8 +- .../Base/PipeBinds/TeamsTabPipeBind.cs | 13 +- .../Base/PipeBinds/TeamsTeamPipeBind.cs | 20 +- src/Commands/Base/PnPAdminCmdlet.cs | 36 +-- src/Commands/Base/PnPConnectedCmdlet.cs | 25 +- src/Commands/Base/PnPConnection.cs | 9 + src/Commands/Base/PnPGraphCmdlet.cs | 29 +-- .../Base/PnPOfficeManagementApiCmdlet.cs | 14 +- src/Commands/Base/PnPSharePointCmdlet.cs | 37 ++- src/Commands/Base/PnPWebCmdlet.cs | 18 +- src/Commands/Base/TokenHandling.cs | 4 +- src/Commands/Graph/InvokeGraphMethod.cs | 12 +- src/Commands/Lists/CopyList.cs | 2 +- .../ManagementApi/GetUnifiedAuditLog.cs | 10 +- .../AddMicrosoft365GroupMember.cs | 2 +- .../AddMicrosoft365GroupOwner.cs | 6 +- .../ClearMicrosoft365GroupMember.cs | 2 +- .../ClearMicrosoft365GroupOwner.cs | 6 +- .../GetDeletedMicrosoft365Group.cs | 4 +- .../GetMicrosoft365Group.cs | 4 +- .../GetMicrosoft365GroupMember.cs | 2 +- .../GetMicrosoft365GroupOwner.cs | 2 +- .../GetMicrosoft365GroupSettingTemplates.cs | 4 +- .../GetMicrosoft365GroupSettings.cs | 6 +- .../NewMicrosoft365Group.cs | 11 +- .../NewMicrosoft365GroupSettings.cs | 6 +- .../RemoveDeletedMicrosoft365Group.cs | 2 +- .../RemoveMicrosoft365Group.cs | 2 +- .../RemoveMicrosoft365GroupMember.cs | 6 +- .../RemoveMicrosoft365GroupOwner.cs | 2 +- .../RemoveMicrosoft365GroupSettings.cs | 6 +- .../ResetMicrosoft365GroupExpiration.cs | 2 +- .../RestoreDeletedMicrosoft365Group.cs | 2 +- .../SetMicrosoft365Group.cs | 16 +- .../SetMicrosoft365GroupSettings.cs | 6 +- src/Commands/Planner/AddPlannerBucket.cs | 8 +- src/Commands/Planner/AddPlannerRoster.cs | 2 +- .../Planner/AddPlannerRosterMember.cs | 4 +- src/Commands/Planner/AddPlannerTask.cs | 18 +- src/Commands/Planner/GetPlannerBucket.cs | 10 +- .../Planner/GetPlannerConfiguration.cs | 2 +- src/Commands/Planner/GetPlannerPlan.cs | 8 +- .../Planner/GetPlannerRosterMember.cs | 4 +- src/Commands/Planner/GetPlannerRosterPlan.cs | 6 +- src/Commands/Planner/GetPlannerTask.cs | 12 +- src/Commands/Planner/GetPlannerUserPolicy.cs | 2 +- src/Commands/Planner/NewPlannerPlan.cs | 4 +- src/Commands/Planner/RemovePlannerBucket.cs | 12 +- src/Commands/Planner/RemovePlannerPlan.cs | 6 +- src/Commands/Planner/RemovePlannerRoster.cs | 4 +- .../Planner/RemovePlannerRosterMember.cs | 4 +- src/Commands/Planner/RemovePlannerTask.cs | 2 +- src/Commands/Planner/SetPlannerBucket.cs | 12 +- .../Planner/SetPlannerConfiguration.cs | 2 +- src/Commands/Planner/SetPlannerPlan.cs | 10 +- src/Commands/Planner/SetPlannerTask.cs | 12 +- src/Commands/Planner/SetPlannerUserPolicy.cs | 2 +- .../GetPowerPlatformEnvironment.cs | 2 +- .../PowerAutomate/DisableFlow.cs | 2 +- .../PowerPlatform/PowerAutomate/EnableFlow.cs | 2 +- .../PowerPlatform/PowerAutomate/ExportFlow.cs | 8 +- .../PowerPlatform/PowerAutomate/GetFlow.cs | 4 +- .../PowerPlatform/PowerAutomate/GetFlowRun.cs | 4 +- .../PowerPlatform/PowerAutomate/RemoveFlow.cs | 2 +- .../PowerAutomate/RestartFlowRun.cs | 4 +- .../PowerAutomate/StopFlowRun.cs | 2 +- src/Commands/Properties/Resources.Designer.cs | 14 +- src/Commands/Properties/Resources.resx | 7 +- src/Commands/Provider/SPOProvider.cs | 4 +- .../GetMessageCenterAnnouncement.cs | 4 +- .../ServiceHealth/GetServiceCurrentHealth.cs | 4 +- .../ServiceHealth/GetServiceHealthIssue.cs | 4 +- .../SetMessageCenterAnnouncementAsArchived.cs | 6 +- .../SetMessageCenterAnnouncementAsFavorite.cs | 6 +- ...tMessageCenterAnnouncementAsNotArchived.cs | 6 +- ...tMessageCenterAnnouncementAsNotFavorite.cs | 6 +- .../SetMessageCenterAnnouncementAsRead.cs | 6 +- .../SetMessageCenterAnnouncementAsUnread.cs | 6 +- src/Commands/Site/AddTeamsTeam.cs | 2 +- src/Commands/SiteDesigns/InvokeSiteScript.cs | 4 +- src/Commands/Teams/AddTeamsChannel.cs | 5 +- src/Commands/Teams/AddTeamsChannelUser.cs | 6 +- src/Commands/Teams/AddTeamsTab.cs | 6 +- src/Commands/Teams/AddTeamsUser.cs | 10 +- src/Commands/Teams/CopyTeamsTeam.cs | 4 +- src/Commands/Teams/GetTeamsApp.cs | 4 +- src/Commands/Teams/GetTeamsChannel.cs | 12 +- .../Teams/GetTeamsChannelFilesFolder.cs | 8 +- src/Commands/Teams/GetTeamsChannelMessage.cs | 8 +- .../Teams/GetTeamsChannelMessageReply.cs | 8 +- src/Commands/Teams/GetTeamsChannelUser.cs | 8 +- src/Commands/Teams/GetTeamsPrimaryChannel.cs | 10 +- src/Commands/Teams/GetTeamsTab.cs | 8 +- src/Commands/Teams/GetTeamsTeam.cs | 6 +- src/Commands/Teams/GetTeamsUser.cs | 10 +- src/Commands/Teams/NewTeamsApp.cs | 2 +- src/Commands/Teams/NewTeamsTeam.cs | 2 +- src/Commands/Teams/RemoveTeamsApp.cs | 4 +- src/Commands/Teams/RemoveTeamsChannel.cs | 6 +- src/Commands/Teams/RemoveTeamsChannelUser.cs | 8 +- src/Commands/Teams/RemoveTeamsTab.cs | 8 +- src/Commands/Teams/RemoveTeamsTeam.cs | 4 +- src/Commands/Teams/RemoveTeamsUser.cs | 4 +- src/Commands/Teams/SetTeamsChannel.cs | 6 +- src/Commands/Teams/SetTeamsChannelUser.cs | 8 +- src/Commands/Teams/SetTeamsTab.cs | 8 +- src/Commands/Teams/SetTeamsTeam.cs | 8 +- .../Teams/SetTeamsTeamArchivedState.cs | 6 +- src/Commands/Teams/SetTeamsTeamPicture.cs | 4 +- .../Teams/SubmitTeamsChannelMessage.cs | 6 +- src/Commands/Teams/UpdateTeamsApp.cs | 4 +- src/Commands/Teams/UpdateTeamsUser.cs | 8 +- .../Utilities/Microsoft365GroupsUtility.cs | 208 ++++++++-------- src/Commands/Utilities/PlannerUtility.cs | 161 ++++++------- src/Commands/Utilities/REST/GraphBatch.cs | 9 +- src/Commands/Utilities/REST/GraphHelper.cs | 127 +++++----- .../Utilities/ServiceHealthUtility.cs | 87 ++++--- src/Commands/Utilities/SiteTemplates.cs | 13 +- src/Commands/Utilities/TeamsUtility.cs | 222 +++++++++--------- src/Commands/Web/SetWebHeader.cs | 4 +- 146 files changed, 894 insertions(+), 957 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ec926f66..8db151c42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,7 +67,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Changed `Get-PnPPlannerBucket` to return the buckets in the correct (reversed) order as you see them through the web interface [#1922](https://github.com/pnp/powershell/pull/1922) - Changed `Connect-PnPOnline -Interactive` and `Connect-PnPOnline -DeviceLogin` to no longer suppress errors which should allow for certificate logins to be used. [#1933](https://github.com/pnp/powershell/pull/1933) - `Set-PnPTeamsChannel` now uses the Graph v1 endpoint, previously it used the beta endpoint. [#1938](https://github.com/pnp/powershell/pull/1938) -- Service Health cmdlets have been improved, now are consistent with other cmdlets to handle pagination [#1938](https://github.com/pnp/powershell/pull/1938) +- Service Health cmdlets have been improved and are now consistent with other cmdlets to handle pagination [#1938](https://github.com/pnp/powershell/pull/1938) +- Changed that every cmdlet now supports passing in a specific connection using `-Connection`. If omitted, the default connection will be used. [#1949](https://github.com/pnp/powershell/pull/1949) ### Fixed @@ -95,6 +96,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Removed `Get-PnPAvailableClientSideComponents`. Use `Get-PnPPageComponent -Page -ListAvailable` instead. [#1833](https://github.com/pnp/powershell/pull/1833) - Removed `NextLink` property from `Get-PnPAzureADUser` cmdlet, as it was causing confusion. [#1930](https://github.com/pnp/powershell/pull/1930) +- Deprecated the `-Connection` parameter of `Disconnect-PnPOnline` cmdlet, as it was technically not capable of clearing a connection reference anyway [#1949](https://github.com/pnp/powershell/pull/1949) ### Contributors diff --git a/documentation/Disconnect-PnPOnline.md b/documentation/Disconnect-PnPOnline.md index 20f02d738..6f0f0d787 100644 --- a/documentation/Disconnect-PnPOnline.md +++ b/documentation/Disconnect-PnPOnline.md @@ -10,19 +10,19 @@ title: Disconnect-PnPOnline # Disconnect-PnPOnline ## SYNOPSIS -Disconnects the context. +Disconnects the current connection and clears its token cache. ## SYNTAX ```powershell -Disconnect-PnPOnline [-Connection ] [] +Disconnect-PnPOnline [] ``` ## DESCRIPTION ->Note: in general it is not recommended nor needed to use this cmdlet. +Disconnects the current connection and clears its token cache. It will require you to build up a new connection again using [Connect-PnPOnline](Connect-PnPOnline.html) in order to use any of the PnP PowerShell cmdlets. You will have to reauthenticate. If instead you simply want to connect to another site collection within the same tenant using the same credentials you used previously, do not use this cmdlet but instead use `Connect-PnPOnline -Url https://tenant.sharepoint.com/sites/othersite` instead without disconnecting. It will try to reuse the existing authentication method and cached credentials. -Disconnects the current context and requires you to build up a new connection in order to use the Cmdlets again. Using Connect-PnPOnline to connect to a different site has the same effect and it's rarely needed to use Disconnect-PnPOnline. Notice that if you use Disconnect-PnPOnline the internal access token cache will be cleared too. This means that if you loop through many Connect-PnPOnline and subsequent Disconnect-PnPOnline statements a full request to the Azure AD is being made to acquire a token. This will cause throttling to occur and the script will stop. +Note that this cmdlet does not support passing in a specific connection to disconnect. If you wish to dispose a specific connection you have set up in a variable using `$variable = Connect-PnPOnline -ReturnConnection`, just dispose that variable using `$variable = $null` and it will be cleared from memory. ## EXAMPLES @@ -31,26 +31,10 @@ Disconnects the current context and requires you to build up a new connection in Disconnect-PnPOnline ``` -This will clear out all active tokens +This will clear out all active tokens from the current connection ## PARAMETERS -### -Connection -Connection to be used by cmdlet - -```yaml -Type: PnPConnection -Parameter Sets: (All) - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - ## RELATED LINKS -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - - +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/src/Commands/Admin/PublishCompanyApp.cs b/src/Commands/Admin/PublishCompanyApp.cs index c77c85c84..3c417d78c 100644 --- a/src/Commands/Admin/PublishCompanyApp.cs +++ b/src/Commands/Admin/PublishCompanyApp.cs @@ -184,7 +184,7 @@ protected override void ExecuteCmdlet() if (!NoUpload) { var bytes = System.IO.File.ReadAllBytes(System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, $"{AppName}.zip")); - TeamsUtility.AddAppAsync(HttpClient, GraphAccessToken, bytes).GetAwaiter().GetResult(); + TeamsUtility.AddAppAsync(Connection, GraphAccessToken, bytes).GetAwaiter().GetResult(); WriteObject($"Teams app uploaded to teams app Store."); } } diff --git a/src/Commands/Apps/GetAzureADAppSitePermission.cs b/src/Commands/Apps/GetAzureADAppSitePermission.cs index 0a575892a..6ae9cee02 100644 --- a/src/Commands/Apps/GetAzureADAppSitePermission.cs +++ b/src/Commands/Apps/GetAzureADAppSitePermission.cs @@ -35,7 +35,7 @@ protected override void ExecuteCmdlet() Guid siteId = Guid.Empty; if (ParameterSpecified(nameof(Site))) { - siteId = Site.GetSiteIdThroughGraph(HttpClient, AccessToken); + siteId = Site.GetSiteIdThroughGraph(Connection, AccessToken); } else { @@ -47,7 +47,7 @@ protected override void ExecuteCmdlet() if (!ParameterSpecified(nameof(PermissionId))) { // all permissions - var results = GraphHelper.GetResultCollectionAsync(HttpClient, $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/sites/{siteId}/permissions", AccessToken).GetAwaiter().GetResult(); + var results = GraphHelper.GetResultCollectionAsync(Connection, $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/sites/{siteId}/permissions", AccessToken).GetAwaiter().GetResult(); if (results.Any()) { var convertedResults = results.Select(i => i.Convert()); @@ -64,7 +64,7 @@ protected override void ExecuteCmdlet() } else { - var results = GraphHelper.GetAsync(HttpClient, $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/sites/{siteId}/permissions/{PermissionId}", AccessToken).GetAwaiter().GetResult(); + var results = GraphHelper.GetAsync(Connection, $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/sites/{siteId}/permissions/{PermissionId}", AccessToken).GetAwaiter().GetResult(); WriteObject(results.Convert()); } } diff --git a/src/Commands/Apps/GrantAzureADAppSitePermission.cs b/src/Commands/Apps/GrantAzureADAppSitePermission.cs index 7382b5c83..9537d69af 100644 --- a/src/Commands/Apps/GrantAzureADAppSitePermission.cs +++ b/src/Commands/Apps/GrantAzureADAppSitePermission.cs @@ -34,7 +34,7 @@ protected override void ExecuteCmdlet() Guid siteId = Guid.Empty; if (ParameterSpecified(nameof(Site))) { - siteId = Site.GetSiteIdThroughGraph(HttpClient, AccessToken); + siteId = Site.GetSiteIdThroughGraph(Connection, AccessToken); } else { @@ -56,7 +56,7 @@ protected override void ExecuteCmdlet() } }; - var results = PnP.PowerShell.Commands.Utilities.REST.RestHelper.PostAsync(HttpClient, $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/sites/{siteId}/permissions", AccessToken, payload).GetAwaiter().GetResult(); + var results = PnP.PowerShell.Commands.Utilities.REST.RestHelper.PostAsync(Connection.HttpClient, $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/sites/{siteId}/permissions", AccessToken, payload).GetAwaiter().GetResult(); WriteObject(results.Convert()); } } diff --git a/src/Commands/Apps/GrantTenantServicePrincipalPermission.cs b/src/Commands/Apps/GrantTenantServicePrincipalPermission.cs index 04ed76c18..daf4fecac 100644 --- a/src/Commands/Apps/GrantTenantServicePrincipalPermission.cs +++ b/src/Commands/Apps/GrantTenantServicePrincipalPermission.cs @@ -1,11 +1,7 @@ using Microsoft.Online.SharePoint.TenantAdministration.Internal; using Microsoft.SharePoint.Client; -using PnP.Framework.ALM; -using PnP.Framework.Enums; using PnP.PowerShell.Commands.Attributes; using PnP.PowerShell.Commands.Base; -using PnP.PowerShell.Commands.Enums; -using PnP.PowerShell.Commands.Model; using PnP.PowerShell.Commands.Utilities; using PnP.PowerShell.Commands.Utilities.REST; using System.Linq; @@ -30,7 +26,7 @@ protected override void ExecuteCmdlet() { var spoWebAppServicePrincipal = new SPOWebAppServicePrincipal(tenantContext); var appId = spoWebAppServicePrincipal.EnsureProperty(a => a.AppId); - var results = GraphHelper.GetAsync>(this.HttpClient, $"/v1.0/servicePrincipals?$filter=appId eq '{appId}'&$select=id", AccessToken).GetAwaiter().GetResult(); + var results = GraphHelper.GetAsync>(Connection, $"/v1.0/servicePrincipals?$filter=appId eq '{appId}'&$select=id", AccessToken).GetAwaiter().GetResult(); if (results.Items.Any()) { var servicePrincipal = results.Items.First(); diff --git a/src/Commands/Apps/RevokeAzureADAppSitePermission.cs b/src/Commands/Apps/RevokeAzureADAppSitePermission.cs index a8ffefc39..90a71a2df 100644 --- a/src/Commands/Apps/RevokeAzureADAppSitePermission.cs +++ b/src/Commands/Apps/RevokeAzureADAppSitePermission.cs @@ -26,7 +26,7 @@ protected override void ExecuteCmdlet() Guid siteId = Guid.Empty; if (ParameterSpecified(nameof(Site))) { - siteId = Site.GetSiteIdThroughGraph(HttpClient, AccessToken); + siteId = Site.GetSiteIdThroughGraph(Connection, AccessToken); } else { @@ -37,7 +37,7 @@ protected override void ExecuteCmdlet() { if (Force || ShouldContinue("Are you sure you want to revoke the permissions?", string.Empty)) { - var results = PnP.PowerShell.Commands.Utilities.REST.RestHelper.DeleteAsync(HttpClient, $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/sites/{siteId}/permissions/{PermissionId}", AccessToken).GetAwaiter().GetResult(); + var results = PnP.PowerShell.Commands.Utilities.REST.RestHelper.DeleteAsync(Connection.HttpClient, $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/sites/{siteId}/permissions/{PermissionId}", AccessToken).GetAwaiter().GetResult(); } } } diff --git a/src/Commands/Apps/RevokeTenantServicePrincipalPermission.cs b/src/Commands/Apps/RevokeTenantServicePrincipalPermission.cs index f5514322b..4659e29d1 100644 --- a/src/Commands/Apps/RevokeTenantServicePrincipalPermission.cs +++ b/src/Commands/Apps/RevokeTenantServicePrincipalPermission.cs @@ -31,7 +31,7 @@ protected override void ExecuteCmdlet() { var spoWebAppServicePrincipal = new SPOWebAppServicePrincipal(tenantContext); var appId = spoWebAppServicePrincipal.EnsureProperty(a => a.AppId); - var results = GraphHelper.GetAsync>(this.HttpClient, $"/v1.0/servicePrincipals?$filter=appId eq '{appId}'&$select=id", AccessToken).GetAwaiter().GetResult(); + var results = GraphHelper.GetAsync>(Connection, $"/v1.0/servicePrincipals?$filter=appId eq '{appId}'&$select=id", AccessToken).GetAwaiter().GetResult(); if (results.Items.Any()) { if (Force || ShouldContinue($"Revoke permission {Scope}?", "Continue")) @@ -53,6 +53,4 @@ private class ServicePrincipal public string Id { get; set; } } } - - } \ No newline at end of file diff --git a/src/Commands/Apps/SetAzureADAppSitePermission.cs b/src/Commands/Apps/SetAzureADAppSitePermission.cs index cd4b04d4d..20738b65b 100644 --- a/src/Commands/Apps/SetAzureADAppSitePermission.cs +++ b/src/Commands/Apps/SetAzureADAppSitePermission.cs @@ -29,7 +29,7 @@ protected override void ExecuteCmdlet() Guid siteId = Guid.Empty; if (ParameterSpecified(nameof(Site))) { - siteId = Site.GetSiteIdThroughGraph(HttpClient, AccessToken); + siteId = Site.GetSiteIdThroughGraph(Connection, AccessToken); } else { @@ -44,7 +44,7 @@ protected override void ExecuteCmdlet() roles = Permissions.Select(p => p.ToLower()).ToArray() }; - var results = PnP.PowerShell.Commands.Utilities.REST.RestHelper.PatchAsync(HttpClient, $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/sites/{siteId}/permissions/{PermissionId}", AccessToken, payload).GetAwaiter().GetResult(); + var results = PnP.PowerShell.Commands.Utilities.REST.RestHelper.PatchAsync(Connection.HttpClient, $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/sites/{siteId}/permissions/{PermissionId}", AccessToken, payload).GetAwaiter().GetResult(); WriteObject(results.Convert()); } } diff --git a/src/Commands/AzureAD/GetAzureADApp.cs b/src/Commands/AzureAD/GetAzureADApp.cs index 6a33e3980..03e5076bd 100644 --- a/src/Commands/AzureAD/GetAzureADApp.cs +++ b/src/Commands/AzureAD/GetAzureADApp.cs @@ -20,12 +20,12 @@ protected override void ExecuteCmdlet() { if (ParameterSpecified(nameof(Identity))) { - WriteObject(Identity.GetApp(this, HttpClient, AccessToken)); + WriteObject(Identity.GetApp(this, Connection, AccessToken)); } else { List apps = new List(); - var result = GraphHelper.GetResultCollectionAsync(HttpClient, "/v1.0/applications", AccessToken).GetAwaiter().GetResult(); + var result = GraphHelper.GetResultCollectionAsync(Connection, "/v1.0/applications", AccessToken).GetAwaiter().GetResult(); WriteObject(result, true); } } diff --git a/src/Commands/AzureAD/GetAzureADAppPermission.cs b/src/Commands/AzureAD/GetAzureADAppPermission.cs index 5472d1862..1ac493082 100644 --- a/src/Commands/AzureAD/GetAzureADAppPermission.cs +++ b/src/Commands/AzureAD/GetAzureADAppPermission.cs @@ -20,12 +20,12 @@ protected override void ExecuteCmdlet() { if (ParameterSpecified(nameof(Identity))) { - WriteObject(ConvertToPSObject(Identity.GetApp(this, HttpClient, AccessToken))); + WriteObject(ConvertToPSObject(Identity.GetApp(this, Connection, AccessToken))); } else { List apps = new List(); - var result = GraphHelper.GetResultCollectionAsync(HttpClient, "/v1.0/applications", AccessToken).GetAwaiter().GetResult(); + var result = GraphHelper.GetResultCollectionAsync(Connection, "/v1.0/applications", AccessToken).GetAwaiter().GetResult(); if (result != null && result.Any()) { apps.AddRange(result.Select(p => ConvertToPSObject(p))); diff --git a/src/Commands/AzureAD/GetAzureADUser.cs b/src/Commands/AzureAD/GetAzureADUser.cs index 752b02548..651b71c25 100644 --- a/src/Commands/AzureAD/GetAzureADUser.cs +++ b/src/Commands/AzureAD/GetAzureADUser.cs @@ -54,9 +54,9 @@ public class GetAzureADUser : PnPGraphCmdlet protected override void ExecuteCmdlet() { - if (PnPConnection.Current.ClientId == PnPConnection.PnPManagementShellClientId) + if (Connection.ClientId == PnPConnection.PnPManagementShellClientId) { - PnPConnection.Current.Scopes = new[] { "Directory.ReadWrite.All" }; + Connection.Scopes = new[] { "Directory.ReadWrite.All" }; } if(ParameterSpecified(nameof(IgnoreDefaultProperties)) && !ParameterSpecified(nameof(Select))) diff --git a/src/Commands/AzureAD/RegisterAzureADApp.cs b/src/Commands/AzureAD/RegisterAzureADApp.cs index 2236e7b3b..c5ec2b1a3 100644 --- a/src/Commands/AzureAD/RegisterAzureADApp.cs +++ b/src/Commands/AzureAD/RegisterAzureADApp.cs @@ -551,7 +551,7 @@ private X509Certificate2 GetCertificate(PSObject record) private bool AppExists(string appName, HttpClient httpClient, string token) { Host.UI.Write(ConsoleColor.Yellow, Host.UI.RawUI.BackgroundColor, $"Checking if application '{appName}' does not exist yet..."); - var azureApps = GraphHelper.GetAsync>(httpClient, $@"https://{PnP.Framework.AuthenticationManager.GetGraphEndPoint(AzureEnvironment)}/v1.0/applications?$filter=displayName eq '{appName}'&$select=Id", token).GetAwaiter().GetResult(); + var azureApps = RestHelper.GetAsync>(httpClient, $@"https://{PnP.Framework.AuthenticationManager.GetGraphEndPoint(AzureEnvironment)}/v1.0/applications?$filter=displayName eq '{appName}'&$select=Id", token).GetAwaiter().GetResult(); if (azureApps != null && azureApps.Items.Any()) { Host.UI.WriteLine(); @@ -597,7 +597,7 @@ private AzureADApp CreateApp(string loginEndPoint, HttpClient httpClient, string var requestContent = new StringContent(JsonSerializer.Serialize(payload)); requestContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - var azureApp = GraphHelper.PostAsync(httpClient, $"https://{AuthenticationManager.GetGraphEndPoint(AzureEnvironment)}/v1.0/applications", requestContent, token).GetAwaiter().GetResult(); + var azureApp = RestHelper.PostAsync(httpClient, $"https://{AuthenticationManager.GetGraphEndPoint(AzureEnvironment)}/v1.0/applications", token, requestContent).GetAwaiter().GetResult(); if (azureApp != null) { Host.UI.WriteLine(ConsoleColor.Yellow, Host.UI.RawUI.BackgroundColor, $"App {azureApp.DisplayName} with id {azureApp.AppId} created."); diff --git a/src/Commands/AzureAD/RemoveAzureADApp.cs b/src/Commands/AzureAD/RemoveAzureADApp.cs index 0865d288e..89740a5f4 100644 --- a/src/Commands/AzureAD/RemoveAzureADApp.cs +++ b/src/Commands/AzureAD/RemoveAzureADApp.cs @@ -1,11 +1,7 @@ -using System.Collections.Generic; -using System.Linq; using System.Management.Automation; using PnP.PowerShell.Commands.Attributes; using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Base.PipeBinds; -using PnP.PowerShell.Commands.Model; -using PnP.PowerShell.Commands.Utilities.REST; namespace PnP.PowerShell.Commands.AzureAD { @@ -21,11 +17,11 @@ public class RemoveAzureADApp : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var app = Identity.GetApp(this, HttpClient, AccessToken); + var app = Identity.GetApp(this, Connection, AccessToken); if (Force || ShouldContinue($"Remove app '{app.DisplayName}' with id '{app.Id}'", string.Empty)) { - Utilities.REST.GraphHelper.DeleteAsync(HttpClient, $"/v1.0/applications/{app.Id}", AccessToken).GetAwaiter().GetResult(); + Utilities.REST.GraphHelper.DeleteAsync(Connection, $"/v1.0/applications/{app.Id}", AccessToken).GetAwaiter().GetResult(); } } } diff --git a/src/Commands/AzureAD/SetAzureADGroup.cs b/src/Commands/AzureAD/SetAzureADGroup.cs index db52706d1..8b128e251 100644 --- a/src/Commands/AzureAD/SetAzureADGroup.cs +++ b/src/Commands/AzureAD/SetAzureADGroup.cs @@ -68,7 +68,7 @@ protected override void ExecuteCmdlet() if (ParameterSpecified(nameof(HideFromAddressLists)) || ParameterSpecified(nameof(HideFromOutlookClients))) { // For this scenario a separate call needs to be made - Utilities.Microsoft365GroupsUtility.SetVisibilityAsync(HttpClient, AccessToken, new Guid(group.Id), HideFromAddressLists, HideFromOutlookClients).GetAwaiter().GetResult(); + Utilities.Microsoft365GroupsUtility.SetVisibilityAsync(Connection, AccessToken, new Guid(group.Id), HideFromAddressLists, HideFromOutlookClients).GetAwaiter().GetResult(); } } catch(Exception e) diff --git a/src/Commands/Base/DisconnectOnline.cs b/src/Commands/Base/DisconnectOnline.cs index c611a82f2..f594ab8b0 100644 --- a/src/Commands/Base/DisconnectOnline.cs +++ b/src/Commands/Base/DisconnectOnline.cs @@ -12,28 +12,29 @@ namespace PnP.PowerShell.Commands.Base public class DisconnectOnline : PSCmdlet { [Parameter(Mandatory = false)] + [Obsolete("The -Connection parameter technically cannot function and therefore will be removed in a future version. Read the documentation of this cmdlet how to best deal with this: https://pnp.github.io/powershell/cmdlets/Disconnect-PnPOnline.html")] public PnPConnection Connection = null; protected override void ProcessRecord() { - // If no specific connection has been passed in, take the connection from the current context - var connection = Connection ?? PnPConnection.Current; + if(PnPConnection.Current == null) + { + throw new InvalidOperationException(Properties.Resources.NoConnectionToDisconnect); + } - if (connection?.Certificate != null) + Environment.SetEnvironmentVariable("PNPPSHOST", string.Empty); + Environment.SetEnvironmentVariable("PNPPSSITE", string.Empty); + + if (PnPConnection.Current.Certificate != null) { - if (connection != null && connection.DeleteCertificateFromCacheOnDisconnect) + if (PnPConnection.Current.DeleteCertificateFromCacheOnDisconnect) { - PnPConnection.CleanupCryptoMachineKey(connection.Certificate); + PnPConnection.CleanupCryptoMachineKey(PnPConnection.Current.Certificate); } - connection.Certificate = null; + PnPConnection.Current.Certificate = null; } - var success = DisconnectProvidedService(ref connection); - - if (!success) - { - throw new InvalidOperationException(Properties.Resources.NoConnectionToDisconnect); - } + PnPConnection.Current = null; var provider = SessionState.Provider.GetAll().FirstOrDefault(p => p.Name.Equals(SPOProvider.PSProviderName, StringComparison.InvariantCultureIgnoreCase)); if (provider != null) @@ -46,18 +47,5 @@ protected override void ProcessRecord() } } } - - internal static bool DisconnectProvidedService(ref PnPConnection connection) - { - Environment.SetEnvironmentVariable("PNPPSHOST", string.Empty); - Environment.SetEnvironmentVariable("PNPPSSITE", string.Empty); - if (connection == null) - { - return false; - } - connection.Context = null; - connection = null; - return true; - } } } \ No newline at end of file diff --git a/src/Commands/Base/GetAccessToken.cs b/src/Commands/Base/GetAccessToken.cs index 006280de8..c26d349b2 100644 --- a/src/Commands/Base/GetAccessToken.cs +++ b/src/Commands/Base/GetAccessToken.cs @@ -46,16 +46,16 @@ protected override void ExecuteCmdlet() accessTokenValue = AccessToken; break; case ResourceTypeName.SharePoint: - accessTokenValue = TokenHandler.GetAccessToken(null, PnPConnection.Current?.Context?.Url?.TrimEnd('/') + "/.default"); + accessTokenValue = TokenHandler.GetAccessToken(null, PnPConnection.Current?.Context?.Url?.TrimEnd('/') + "/.default", Connection); break; case ResourceTypeName.ARM: - accessTokenValue = TokenHandler.GetAccessToken(null, "https://management.azure.com/.default"); + accessTokenValue = TokenHandler.GetAccessToken(null, "https://management.azure.com/.default", Connection); break; } } else if (ParameterSetName == ResourceUrlParam) { - accessTokenValue = TokenHandler.GetAccessToken(null, ResourceUrl); + accessTokenValue = TokenHandler.GetAccessToken(null, ResourceUrl, Connection); } if (Decoded.IsPresent) diff --git a/src/Commands/Base/PipeBinds/AzureADAppPipeBind.cs b/src/Commands/Base/PipeBinds/AzureADAppPipeBind.cs index c96f6756f..abe9d3b76 100644 --- a/src/Commands/Base/PipeBinds/AzureADAppPipeBind.cs +++ b/src/Commands/Base/PipeBinds/AzureADAppPipeBind.cs @@ -29,24 +29,24 @@ public AzureADAppPipeBind(string name) } } - public AzureADApp GetApp(BasePSCmdlet cmdlet, HttpClient httpClient, string accessToken) + public AzureADApp GetApp(BasePSCmdlet cmdlet, PnPConnection connection, string accessToken) { if (_id != Guid.Empty) { - var results = Utilities.REST.GraphHelper.GetAsync>(httpClient, $"/v1.0/applications?$filter=appId eq '{_id}'", accessToken).GetAwaiter().GetResult(); + var results = Utilities.REST.GraphHelper.GetAsync>(connection, $"/v1.0/applications?$filter=appId eq '{_id}'", accessToken).GetAwaiter().GetResult(); if (results != null && results.Items.Any()) { return results.Items.First(); } else { - return Utilities.REST.GraphHelper.GetAsync(httpClient, $"/v1.0/applications/{_id}", accessToken).GetAwaiter().GetResult(); + return Utilities.REST.GraphHelper.GetAsync(connection, $"/v1.0/applications/{_id}", accessToken).GetAwaiter().GetResult(); } } if (!string.IsNullOrEmpty(_name)) { - var results = Utilities.REST.GraphHelper.GetAsync>(httpClient, $"/v1.0/applications?$filter=displayName eq '{_name}'", accessToken).GetAwaiter().GetResult(); + var results = Utilities.REST.GraphHelper.GetAsync>(connection, $"/v1.0/applications?$filter=displayName eq '{_name}'", accessToken).GetAwaiter().GetResult(); if (results != null && results.Items.Any()) { return results.Items.First(); diff --git a/src/Commands/Base/PipeBinds/Microsoft365GroupPipeBind.cs b/src/Commands/Base/PipeBinds/Microsoft365GroupPipeBind.cs index 3ef5853e5..9f6997d52 100644 --- a/src/Commands/Base/PipeBinds/Microsoft365GroupPipeBind.cs +++ b/src/Commands/Base/PipeBinds/Microsoft365GroupPipeBind.cs @@ -48,25 +48,25 @@ public Microsoft365GroupPipeBind(Guid guid) public Guid GroupId => _groupId; - public Microsoft365Group GetGroup(HttpClient httpClient, string accessToken, bool includeSite, bool includeOwners) + public Microsoft365Group GetGroup(PnPConnection connection, string accessToken, bool includeSite, bool includeOwners) { Microsoft365Group group = null; if (Group != null) { - group = Microsoft365GroupsUtility.GetGroupAsync(httpClient, _group.Id.Value, accessToken, includeSite, includeOwners).GetAwaiter().GetResult(); + group = Microsoft365GroupsUtility.GetGroupAsync(connection, _group.Id.Value, accessToken, includeSite, includeOwners).GetAwaiter().GetResult(); } else if (_groupId != Guid.Empty) { - group = Microsoft365GroupsUtility.GetGroupAsync(httpClient, _groupId, accessToken, includeSite, includeOwners).GetAwaiter().GetResult(); + group = Microsoft365GroupsUtility.GetGroupAsync(connection, _groupId, accessToken, includeSite, includeOwners).GetAwaiter().GetResult(); } else if (!string.IsNullOrEmpty(DisplayName)) { - group = Microsoft365GroupsUtility.GetGroupAsync(httpClient, DisplayName, accessToken, includeSite, includeOwners).GetAwaiter().GetResult(); + group = Microsoft365GroupsUtility.GetGroupAsync(connection, DisplayName, accessToken, includeSite, includeOwners).GetAwaiter().GetResult(); } return group; } - public Guid GetGroupId(HttpClient httpClient, string accessToken) + public Guid GetGroupId(PnPConnection connection, string accessToken) { if (Group != null) { @@ -78,7 +78,7 @@ public Guid GetGroupId(HttpClient httpClient, string accessToken) } else if (!string.IsNullOrEmpty(DisplayName)) { - var group = Microsoft365GroupsUtility.GetGroupAsync(httpClient, DisplayName, accessToken, false, false).GetAwaiter().GetResult(); + var group = Microsoft365GroupsUtility.GetGroupAsync(connection, DisplayName, accessToken, false, false).GetAwaiter().GetResult(); if (group != null) { return group.Id.Value; @@ -88,24 +88,24 @@ public Guid GetGroupId(HttpClient httpClient, string accessToken) //return Guid.Empty; } - public Microsoft365Group GetDeletedGroup(HttpClient httpClient, string accessToken) + public Microsoft365Group GetDeletedGroup(PnPConnection connection, string accessToken) { if (_group != null) { - return Microsoft365GroupsUtility.GetDeletedGroupAsync(httpClient, _group.Id.Value, accessToken).GetAwaiter().GetResult(); + return Microsoft365GroupsUtility.GetDeletedGroupAsync(connection, _group.Id.Value, accessToken).GetAwaiter().GetResult(); } else if (_groupId != Guid.Empty) { - return Microsoft365GroupsUtility.GetDeletedGroupAsync(httpClient, _groupId, accessToken).GetAwaiter().GetResult(); + return Microsoft365GroupsUtility.GetDeletedGroupAsync(connection, _groupId, accessToken).GetAwaiter().GetResult(); } else if (!string.IsNullOrEmpty(_displayName)) { - return Microsoft365GroupsUtility.GetDeletedGroupAsync(httpClient, _displayName, accessToken).GetAwaiter().GetResult(); + return Microsoft365GroupsUtility.GetDeletedGroupAsync(connection, _displayName, accessToken).GetAwaiter().GetResult(); } return null; } - public Guid GetDeletedGroupId(HttpClient httpClient, string accessToken) + public Guid GetDeletedGroupId(PnPConnection connection, string accessToken) { if (_group != null) { @@ -117,7 +117,7 @@ public Guid GetDeletedGroupId(HttpClient httpClient, string accessToken) } else if (!string.IsNullOrEmpty(_displayName)) { - var group = Microsoft365GroupsUtility.GetDeletedGroupAsync(httpClient, _displayName, accessToken).GetAwaiter().GetResult(); + var group = Microsoft365GroupsUtility.GetDeletedGroupAsync(connection, _displayName, accessToken).GetAwaiter().GetResult(); if (group != null) { return group.Id.Value; diff --git a/src/Commands/Base/PipeBinds/PlannerBucketPipeBind.cs b/src/Commands/Base/PipeBinds/PlannerBucketPipeBind.cs index f1ef61113..c4c796e29 100644 --- a/src/Commands/Base/PipeBinds/PlannerBucketPipeBind.cs +++ b/src/Commands/Base/PipeBinds/PlannerBucketPipeBind.cs @@ -1,7 +1,6 @@ using System; using System.Linq; using System.Management.Automation; -using System.Net.Http; using PnP.PowerShell.Commands.Model.Graph; using PnP.PowerShell.Commands.Model.Planner; using PnP.PowerShell.Commands.Utilities; @@ -35,7 +34,7 @@ public string GetId() } } - public PlannerBucket GetBucket(HttpClient httpClient, string accessToken, string planId) + public PlannerBucket GetBucket(PnPConnection connection, string accessToken, string planId) { // first try to get the bucket by id if (_bucket != null) @@ -46,7 +45,7 @@ public PlannerBucket GetBucket(HttpClient httpClient, string accessToken, string { try { - var buckets = PlannerUtility.GetBucketsAsync(httpClient, accessToken, planId).GetAwaiter().GetResult(); + var buckets = PlannerUtility.GetBucketsAsync(connection, accessToken, planId).GetAwaiter().GetResult(); if (buckets != null) { PlannerBucket bucket = null; diff --git a/src/Commands/Base/PipeBinds/PlannerGroupPipeBind.cs b/src/Commands/Base/PipeBinds/PlannerGroupPipeBind.cs index 4fbc6bbd5..4f308d30d 100644 --- a/src/Commands/Base/PipeBinds/PlannerGroupPipeBind.cs +++ b/src/Commands/Base/PipeBinds/PlannerGroupPipeBind.cs @@ -1,7 +1,6 @@ using System; using System.Linq; using System.Management.Automation; -using System.Net.Http; using PnP.PowerShell.Commands.Utilities.REST; namespace PnP.PowerShell.Commands.Base.PipeBinds @@ -39,7 +38,7 @@ public PlannerGroupPipeBind(Model.Graph.Group group) _id = group.Id; } - public string GetGroupId(HttpClient httpClient, string accessToken) + public string GetGroupId(PnPConnection connection, string accessToken) { if (!string.IsNullOrEmpty(_id)) { @@ -47,7 +46,7 @@ public string GetGroupId(HttpClient httpClient, string accessToken) } else { - var collection = GraphHelper.GetAsync>(httpClient, $"v1.0/groups?$filter=mailNickname eq '{_stringValue}'&$select=Id", accessToken).GetAwaiter().GetResult(); + var collection = GraphHelper.GetAsync>(connection, $"v1.0/groups?$filter=mailNickname eq '{_stringValue}'&$select=Id", accessToken).GetAwaiter().GetResult(); if (collection != null && collection.Items.Any()) { return collection.Items.First().Id; @@ -55,7 +54,7 @@ public string GetGroupId(HttpClient httpClient, string accessToken) else { // find the team by displayName - var byDisplayNamecollection = GraphHelper.GetAsync>(httpClient, $"v1.0/groups?$filter=displayName eq '{_stringValue}'&$select=Id", accessToken).GetAwaiter().GetResult(); + var byDisplayNamecollection = GraphHelper.GetAsync>(connection, $"v1.0/groups?$filter=displayName eq '{_stringValue}'&$select=Id", accessToken).GetAwaiter().GetResult(); if (byDisplayNamecollection != null && byDisplayNamecollection.Items.Any()) { if (byDisplayNamecollection.Items.Count() == 1) diff --git a/src/Commands/Base/PipeBinds/PlannerPlanPipeBind.cs b/src/Commands/Base/PipeBinds/PlannerPlanPipeBind.cs index 86f45b1e6..ada835a7f 100644 --- a/src/Commands/Base/PipeBinds/PlannerPlanPipeBind.cs +++ b/src/Commands/Base/PipeBinds/PlannerPlanPipeBind.cs @@ -1,7 +1,5 @@ -using System; using System.Linq; using System.Management.Automation; -using System.Net.Http; using System.Threading.Tasks; using PnP.PowerShell.Commands.Model.Graph; using PnP.PowerShell.Commands.Model.Planner; @@ -15,7 +13,6 @@ public sealed class PlannerPlanPipeBind private readonly PlannerPlan _plan; public PlannerPlanPipeBind() { - } public PlannerPlanPipeBind(string input) @@ -28,7 +25,7 @@ public PlannerPlanPipeBind(PlannerPlan plan) _plan = plan; } - public async Task GetPlanAsync(HttpClient httpClient, string accessToken, string groupId, bool resolveIdentities) + public async Task GetPlanAsync(PnPConnection connection, string accessToken, string groupId, bool resolveIdentities) { if (_plan != null) { @@ -37,11 +34,11 @@ public async Task GetPlanAsync(HttpClient httpClient, string access // first try to get the plan by id try { - return await PlannerUtility.GetPlanAsync(httpClient, accessToken, _id, resolveIdentities); + return await PlannerUtility.GetPlanAsync(connection, accessToken, _id, resolveIdentities); } catch (GraphException) { - var plans = await PlannerUtility.GetPlansAsync(httpClient, accessToken, groupId, resolveIdentities); + var plans = await PlannerUtility.GetPlansAsync(connection, accessToken, groupId, resolveIdentities); if (plans != null && plans.Any()) { var collection = plans.Where(p => p.Title.Equals(_id)); @@ -58,7 +55,7 @@ public async Task GetPlanAsync(HttpClient httpClient, string access return null; } - public async Task GetIdAsync(HttpClient httpClient, string accessToken, string groupId) + public async Task GetIdAsync(PnPConnection connection, string accessToken, string groupId) { if (_plan != null) { @@ -67,12 +64,12 @@ public async Task GetIdAsync(HttpClient httpClient, string accessToken, // first try to get the plan by id try { - var plan = await PlannerUtility.GetPlanAsync(httpClient, accessToken, _id, false); + var plan = await PlannerUtility.GetPlanAsync(connection, accessToken, _id, false); return plan.Id; } catch (GraphException) { - var plans = await PlannerUtility.GetPlansAsync(httpClient, accessToken, groupId, false); + var plans = await PlannerUtility.GetPlansAsync(connection, accessToken, groupId, false); if (plans != null && plans.Any()) { var collection = plans.Where(p => p.Title.Equals(_id)); diff --git a/src/Commands/Base/PipeBinds/PlannerRosterPipeBind.cs b/src/Commands/Base/PipeBinds/PlannerRosterPipeBind.cs index f4a31d8a3..dcc8f9a4e 100644 --- a/src/Commands/Base/PipeBinds/PlannerRosterPipeBind.cs +++ b/src/Commands/Base/PipeBinds/PlannerRosterPipeBind.cs @@ -1,4 +1,3 @@ -using System.Net.Http; using System.Threading.Tasks; using PnP.PowerShell.Commands.Model.Planner; using PnP.PowerShell.Commands.Utilities; @@ -11,7 +10,6 @@ public sealed class PlannerRosterPipeBind private readonly PlannerRoster _roster; public PlannerRosterPipeBind() { - } public PlannerRosterPipeBind(string input) @@ -24,13 +22,13 @@ public PlannerRosterPipeBind(PlannerRoster roster) _roster = roster; } - public async Task GetPlannerRosterAsync(HttpClient httpClient, string accessToken) + public async Task GetPlannerRosterAsync(PnPConnection connection, string accessToken) { if (_roster != null) { return _roster; } - return await PlannerUtility.GetRosterAsync(httpClient, accessToken, _id); + return await PlannerUtility.GetRosterAsync(connection, accessToken, _id); } } } diff --git a/src/Commands/Base/PipeBinds/SitePipeBind.cs b/src/Commands/Base/PipeBinds/SitePipeBind.cs index 8b979ceb1..45da4e576 100644 --- a/src/Commands/Base/PipeBinds/SitePipeBind.cs +++ b/src/Commands/Base/PipeBinds/SitePipeBind.cs @@ -1,6 +1,5 @@ using Microsoft.SharePoint.Client; using System; -using System.Net.Http; using System.Text.Json; namespace PnP.PowerShell.Commands.Base.PipeBinds @@ -57,7 +56,7 @@ public SitePipeBind(Microsoft.SharePoint.Client.Site site) public Guid Id => _id; - public Guid GetSiteIdThroughGraph(HttpClient httpClient, string accesstoken) + public Guid GetSiteIdThroughGraph(PnPConnection connection, string accesstoken) { if (_site != null) { @@ -71,7 +70,7 @@ public Guid GetSiteIdThroughGraph(HttpClient httpClient, string accesstoken) { var uri = new Uri(_url); - var result = Utilities.REST.RestHelper.GetAsync(httpClient, $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/sites/{uri.Host}:{uri.LocalPath}", accesstoken).GetAwaiter().GetResult(); + var result = Utilities.REST.RestHelper.GetAsync(connection.HttpClient, $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/sites/{uri.Host}:{uri.LocalPath}", accesstoken).GetAwaiter().GetResult(); if (!string.IsNullOrEmpty(result)) { var resultElement = JsonSerializer.Deserialize(result); diff --git a/src/Commands/Base/PipeBinds/TeamsAppPipeBind.cs b/src/Commands/Base/PipeBinds/TeamsAppPipeBind.cs index d0df897e6..3cbde28a7 100644 --- a/src/Commands/Base/PipeBinds/TeamsAppPipeBind.cs +++ b/src/Commands/Base/PipeBinds/TeamsAppPipeBind.cs @@ -38,18 +38,18 @@ public TeamsAppPipeBind(string input) public string StringValue => _stringValue; - public TeamApp GetApp(HttpClient httpClient, string accessToken) + public TeamApp GetApp(PnPConnection connection, string accessToken) { if (Id != Guid.Empty) { - var collection = GraphHelper.GetAsync>(httpClient, $"v1.0/appCatalogs/teamsApps?$filter=id eq '{_id}'", accessToken).GetAwaiter().GetResult(); + var collection = GraphHelper.GetAsync>(connection, $"v1.0/appCatalogs/teamsApps?$filter=id eq '{_id}'", accessToken).GetAwaiter().GetResult(); if (collection != null && collection.Items.Any()) { return collection.Items.First(); } else { - collection = GraphHelper.GetAsync>(httpClient, $"v1.0/appCatalogs/teamsApps?$filter=externalId eq '{_id}'", accessToken).GetAwaiter().GetResult(); + collection = GraphHelper.GetAsync>(connection, $"v1.0/appCatalogs/teamsApps?$filter=externalId eq '{_id}'", accessToken).GetAwaiter().GetResult(); if (collection != null && collection.Items.Any()) { return collection.Items.First(); @@ -58,7 +58,7 @@ public TeamApp GetApp(HttpClient httpClient, string accessToken) } else { - var collection = GraphHelper.GetAsync>(httpClient, $"v1.0/appCatalogs/teamsApps?$filter=displayName eq '{_stringValue}'", accessToken).GetAwaiter().GetResult(); + var collection = GraphHelper.GetAsync>(connection, $"v1.0/appCatalogs/teamsApps?$filter=displayName eq '{_stringValue}'", accessToken).GetAwaiter().GetResult(); if (collection != null && collection.Items.Any()) { if (collection.Items.Count() == 1) diff --git a/src/Commands/Base/PipeBinds/TeamsChannelMemberPipeBind.cs b/src/Commands/Base/PipeBinds/TeamsChannelMemberPipeBind.cs index 6796f5c13..76a21c806 100644 --- a/src/Commands/Base/PipeBinds/TeamsChannelMemberPipeBind.cs +++ b/src/Commands/Base/PipeBinds/TeamsChannelMemberPipeBind.cs @@ -40,7 +40,7 @@ public TeamsChannelMemberPipeBind(TeamChannelMember membership) _membership = membership; } - public async Task GetIdAsync(HttpClient httpClient, string accessToken, string groupId, string channelId) + public async Task GetIdAsync(PnPConnection Connection, string accessToken, string groupId, string channelId) { if (!string.IsNullOrEmpty(_id)) { @@ -52,7 +52,7 @@ public async Task GetIdAsync(HttpClient httpClient, string accessToken, return _membership.Id; } - var memberships = await TeamsUtility.GetChannelMembersAsync(httpClient, accessToken, groupId, channelId); + var memberships = await TeamsUtility.GetChannelMembersAsync(Connection, accessToken, groupId, channelId); if (!string.IsNullOrEmpty(_userUpn)) { return memberships.FirstOrDefault(m => _userUpn.Equals(m.Email, StringComparison.OrdinalIgnoreCase))?.Id; @@ -61,7 +61,7 @@ public async Task GetIdAsync(HttpClient httpClient, string accessToken, return memberships.FirstOrDefault(m => !string.IsNullOrEmpty(m.UserId) && _userId.Equals(m.UserId, StringComparison.OrdinalIgnoreCase))?.Id; } - public async Task GetMembershipAsync(HttpClient httpClient, string accessToken, string groupId, string channelId) + public async Task GetMembershipAsync(PnPConnection Connection, string accessToken, string groupId, string channelId) { if (_membership != null) { @@ -70,10 +70,10 @@ public async Task GetMembershipAsync(HttpClient httpClient, s if (!string.IsNullOrEmpty(_id)) { - return await TeamsUtility.GetChannelMemberAsync(httpClient, accessToken, groupId, channelId, _id); + return await TeamsUtility.GetChannelMemberAsync(Connection, accessToken, groupId, channelId, _id); } - var memberships = await TeamsUtility.GetChannelMembersAsync(httpClient, accessToken, groupId, channelId); + var memberships = await TeamsUtility.GetChannelMembersAsync(Connection, accessToken, groupId, channelId); if (!string.IsNullOrEmpty(_userUpn)) { return memberships.FirstOrDefault(m => _userUpn.Equals(m.Email, StringComparison.OrdinalIgnoreCase)); diff --git a/src/Commands/Base/PipeBinds/TeamsChannelPipeBind.cs b/src/Commands/Base/PipeBinds/TeamsChannelPipeBind.cs index 1a8c8f474..464691f06 100644 --- a/src/Commands/Base/PipeBinds/TeamsChannelPipeBind.cs +++ b/src/Commands/Base/PipeBinds/TeamsChannelPipeBind.cs @@ -36,7 +36,7 @@ public TeamsChannelPipeBind(Model.Teams.TeamChannel channel) public string Id => _id; - public string GetId(HttpClient httpClient, string accessToken, string groupId) + public string GetId(PnPConnection connection, string accessToken, string groupId) { if (!string.IsNullOrEmpty(_id)) { @@ -44,14 +44,14 @@ public string GetId(HttpClient httpClient, string accessToken, string groupId) } else { - var channels = TeamsUtility.GetChannelsAsync(accessToken, httpClient, groupId).GetAwaiter().GetResult(); + var channels = TeamsUtility.GetChannelsAsync(accessToken, connection, groupId).GetAwaiter().GetResult(); return channels.FirstOrDefault(c => c.DisplayName.Equals(_displayName, StringComparison.OrdinalIgnoreCase))?.Id; } } - public TeamChannel GetChannel(HttpClient httpClient, string accessToken, string groupId) + public TeamChannel GetChannel(PnPConnection connection, string accessToken, string groupId) { - var channels = TeamsUtility.GetChannelsAsync(accessToken, httpClient, groupId).GetAwaiter().GetResult(); + var channels = TeamsUtility.GetChannelsAsync(accessToken, connection, groupId).GetAwaiter().GetResult(); if(channels != null && channels.Any()) { if(!string.IsNullOrEmpty(_id)) diff --git a/src/Commands/Base/PipeBinds/TeamsTabPipeBind.cs b/src/Commands/Base/PipeBinds/TeamsTabPipeBind.cs index c83b1cf7e..17f6b1a72 100644 --- a/src/Commands/Base/PipeBinds/TeamsTabPipeBind.cs +++ b/src/Commands/Base/PipeBinds/TeamsTabPipeBind.cs @@ -1,13 +1,8 @@ -using Microsoft.SharePoint.Client; -using PnP.PowerShell.Commands.Model.Teams; +using PnP.PowerShell.Commands.Model.Teams; using PnP.PowerShell.Commands.Utilities; using System; -using System.Collections.Generic; using System.Linq; using System.Management.Automation; -using System.Net; -using System.Net.Http; -using System.Web; namespace PnP.PowerShell.Commands.Base.PipeBinds { @@ -44,7 +39,7 @@ public TeamsTabPipeBind(TeamTab tab) public string Id => _id; - public TeamTab GetTab(BasePSCmdlet cmdlet, HttpClient httpClient, string accessToken, string groupId, string channelId) + public TeamTab GetTab(BasePSCmdlet cmdlet, PnPConnection Connection, string accessToken, string groupId, string channelId) { if (_tab != null) { @@ -52,10 +47,10 @@ public TeamTab GetTab(BasePSCmdlet cmdlet, HttpClient httpClient, string accessT } else { - var tab = TeamsUtility.GetTabAsync(accessToken, httpClient, groupId, channelId, _id).GetAwaiter().GetResult(); + var tab = TeamsUtility.GetTabAsync(accessToken, Connection, groupId, channelId, _id).GetAwaiter().GetResult(); if (string.IsNullOrEmpty(tab.Id)) { - var tabs = TeamsUtility.GetTabsAsync(accessToken, httpClient, groupId, channelId).GetAwaiter().GetResult(); + var tabs = TeamsUtility.GetTabsAsync(accessToken, Connection, groupId, channelId).GetAwaiter().GetResult(); if (tabs != null) { // find the tab by id diff --git a/src/Commands/Base/PipeBinds/TeamsTeamPipeBind.cs b/src/Commands/Base/PipeBinds/TeamsTeamPipeBind.cs index d034beefa..16ee88dfd 100644 --- a/src/Commands/Base/PipeBinds/TeamsTeamPipeBind.cs +++ b/src/Commands/Base/PipeBinds/TeamsTeamPipeBind.cs @@ -4,7 +4,6 @@ using System; using System.Linq; using System.Management.Automation; -using System.Net.Http; using PnP.PowerShell.Commands.Utilities; namespace PnP.PowerShell.Commands.Base.PipeBinds @@ -42,7 +41,7 @@ public TeamsTeamPipeBind(Team team) _id = team.GroupId; } - public string GetGroupId(HttpClient httpClient, string accessToken) + public string GetGroupId(PnPConnection connection, string accessToken) { if (!string.IsNullOrEmpty(_id)) { @@ -50,7 +49,7 @@ public string GetGroupId(HttpClient httpClient, string accessToken) } else { - var collection = GraphHelper.GetAsync>(httpClient, $"beta/groups?$filter=(resourceProvisioningOptions/Any(x:x eq 'Team') and mailNickname eq '{UrlUtilities.UrlEncode(_stringValue)}')&$select=Id", accessToken).GetAwaiter().GetResult(); + var collection = GraphHelper.GetAsync>(connection, $"beta/groups?$filter=(resourceProvisioningOptions/Any(x:x eq 'Team') and mailNickname eq '{UrlUtilities.UrlEncode(_stringValue)}')&$select=Id", accessToken).GetAwaiter().GetResult(); if (collection != null && collection.Items.Any()) { return collection.Items.First().Id; @@ -58,7 +57,7 @@ public string GetGroupId(HttpClient httpClient, string accessToken) else { // find the team by displayName - var byDisplayNamecollection = GraphHelper.GetAsync>(httpClient, $"beta/groups?$filter=(resourceProvisioningOptions/Any(x:x eq 'Team') and displayName eq '{UrlUtilities.UrlEncode(_stringValue)}')&$select=Id", accessToken).GetAwaiter().GetResult(); + var byDisplayNamecollection = GraphHelper.GetAsync>(connection, $"beta/groups?$filter=(resourceProvisioningOptions/Any(x:x eq 'Team') and displayName eq '{UrlUtilities.UrlEncode(_stringValue)}')&$select=Id", accessToken).GetAwaiter().GetResult(); if (byDisplayNamecollection != null && byDisplayNamecollection.Items.Any()) { if (byDisplayNamecollection.Items.Count() == 1) @@ -75,22 +74,22 @@ public string GetGroupId(HttpClient httpClient, string accessToken) } } - public Team GetTeam(HttpClient httpClient, string accessToken) + public Team GetTeam(PnPConnection connection, string accessToken) { try { if (!string.IsNullOrEmpty(_id)) { - return GraphHelper.GetAsync(httpClient, $"v1.0/teams/{_id}", accessToken, false).GetAwaiter().GetResult(); + return GraphHelper.GetAsync(connection, $"v1.0/teams/{_id}", accessToken, false).GetAwaiter().GetResult(); } else { - var collection = GraphHelper.GetAsync>(httpClient, $"beta/groups?$filter=(resourceProvisioningOptions/Any(x:x eq 'Team') and displayName eq '{_stringValue}')&$select=Id", accessToken).GetAwaiter().GetResult(); + var collection = GraphHelper.GetAsync>(connection, $"beta/groups?$filter=(resourceProvisioningOptions/Any(x:x eq 'Team') and displayName eq '{_stringValue}')&$select=Id", accessToken).GetAwaiter().GetResult(); if (collection != null && collection.Items.Any()) { if (collection.Items.Count() == 1) { - return GraphHelper.GetAsync(httpClient, $"v1.0/teams/{collection.Items.First().Id}", accessToken, false).GetAwaiter().GetResult(); + return GraphHelper.GetAsync(connection, $"v1.0/teams/{collection.Items.First().Id}", accessToken, false).GetAwaiter().GetResult(); } else { @@ -99,10 +98,10 @@ public Team GetTeam(HttpClient httpClient, string accessToken) } else { - collection = GraphHelper.GetAsync>(httpClient, $"beta/groups?$filter=(resourceProvisioningOptions/Any(x:x eq 'Team') and mailNickname eq '{_stringValue}')&$select=Id", accessToken).GetAwaiter().GetResult(); + collection = GraphHelper.GetAsync>(connection, $"beta/groups?$filter=(resourceProvisioningOptions/Any(x:x eq 'Team') and mailNickname eq '{_stringValue}')&$select=Id", accessToken).GetAwaiter().GetResult(); if (collection != null && collection.Items.Count() == 1) { - return GraphHelper.GetAsync(httpClient, $"v1.0/teams/{collection.Items.First().Id}", accessToken, false).GetAwaiter().GetResult(); + return GraphHelper.GetAsync(connection, $"v1.0/teams/{collection.Items.First().Id}", accessToken, false).GetAwaiter().GetResult(); } } } @@ -113,6 +112,5 @@ public Team GetTeam(HttpClient httpClient, string accessToken) } return null; } - } } diff --git a/src/Commands/Base/PnPAdminCmdlet.cs b/src/Commands/Base/PnPAdminCmdlet.cs index 1e55a7ee2..e510fc08c 100644 --- a/src/Commands/Base/PnPAdminCmdlet.cs +++ b/src/Commands/Base/PnPAdminCmdlet.cs @@ -4,7 +4,6 @@ using Microsoft.Online.SharePoint.TenantAdministration; using Microsoft.SharePoint.Client; using PnP.PowerShell.Commands.Enums; -using Resources = PnP.PowerShell.Commands.Properties.Resources; namespace PnP.PowerShell.Commands.Base { @@ -40,9 +39,9 @@ public Tenant Tenant ///

private void IsDeviceLogin(string tenantAdminUrl) { - if (PnPConnection.Current.ConnectionMethod == Model.ConnectionMethod.DeviceLogin) + if (Connection.ConnectionMethod == Model.ConnectionMethod.DeviceLogin) { - if (tenantAdminUrl != PnPConnection.Current.Url) + if (tenantAdminUrl != Connection.Url) { throw new PSInvalidOperationException($"You used a device login connection to authenticate to SharePoint. We do not support automatically switching context to the tenant administration site which is required to execute this cmdlet. Please use Connect-PnPOnline and connect to '{tenantAdminUrl}' with the appropriate connection parameters"); } @@ -56,24 +55,15 @@ protected override void BeginProcessing() { base.BeginProcessing(); - if (PnPConnection.Current == null) - { - throw new InvalidOperationException(Resources.NoSharePointConnection); - } - if (ClientContext == null) - { - throw new InvalidOperationException(Resources.NoSharePointConnection); - } - // Keep an instance of the client context which is currently active before elevating to an admin client context so we can restore it afterwards - SiteContext = PnPConnection.Current.Context; + SiteContext = Connection.Context; - PnPConnection.Current.CacheContext(); + Connection.CacheContext(); - if (PnPConnection.Current.TenantAdminUrl != null && - (PnPConnection.Current.ConnectionType == ConnectionType.O365)) + if (Connection.TenantAdminUrl != null && + (Connection.ConnectionType == ConnectionType.O365)) { - var uri = new Uri(PnPConnection.Current.Url); + var uri = new Uri(Connection.Url); var uriParts = uri.Host.Split('.'); if (uriParts[0].ToLower().EndsWith("-admin")) { @@ -83,15 +73,15 @@ protected override void BeginProcessing() { _baseUri = new Uri($"{uri.Scheme}://{uri.Authority}"); } - IsDeviceLogin(PnPConnection.Current.TenantAdminUrl); - PnPConnection.Current.CloneContext(PnPConnection.Current.TenantAdminUrl); + IsDeviceLogin(Connection.TenantAdminUrl); + Connection.CloneContext(Connection.TenantAdminUrl); } else { Uri uri = new Uri(ClientContext.Url); var uriParts = uri.Host.Split('.'); if (!uriParts[0].EndsWith("-admin") && - PnPConnection.Current.ConnectionType == ConnectionType.O365) + Connection.ConnectionType == ConnectionType.O365) { _baseUri = new Uri($"{uri.Scheme}://{uri.Authority}"); @@ -100,8 +90,8 @@ protected override void BeginProcessing() var adminUrl = $"https://{tenantName}-admin.{string.Join(".", uriParts.Skip(1))}"; IsDeviceLogin(adminUrl); - PnPConnection.Current.Context = - PnPConnection.Current.CloneContext(adminUrl); + Connection.Context = + Connection.CloneContext(adminUrl); } else { @@ -118,7 +108,7 @@ protected override void EndProcessing() base.EndProcessing(); // Restore the client context to the context which was used before the admin context elevation - PnPConnection.Current.Context = SiteContext; + Connection.Context = SiteContext; } } } \ No newline at end of file diff --git a/src/Commands/Base/PnPConnectedCmdlet.cs b/src/Commands/Base/PnPConnectedCmdlet.cs index c6b5eb630..19535ceb7 100644 --- a/src/Commands/Base/PnPConnectedCmdlet.cs +++ b/src/Commands/Base/PnPConnectedCmdlet.cs @@ -1,5 +1,5 @@ using System; -using System.Net.Http; +using System.Management.Automation; namespace PnP.PowerShell.Commands.Base { @@ -8,6 +8,11 @@ namespace PnP.PowerShell.Commands.Base ///
public abstract class PnPConnectedCmdlet : BasePSCmdlet { + // do not remove '#!#99' + [Parameter(Mandatory = false, HelpMessage = "Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection.")] + public PnPConnection Connection = null; + // do not remove '#!#99' + protected override void BeginProcessing() { BeginProcessing(false); @@ -15,18 +20,28 @@ protected override void BeginProcessing() protected void BeginProcessing(bool skipConnectedValidation) { - base.BeginProcessing(); + base.BeginProcessing(); + + // If a specific connection has been provided, use that, otherwise use the current connection + if(Connection == null) + { + Connection = PnPConnection.Current; + } + + // Track the execution of the cmdlet in Azure Application Insights + if (Connection != null && Connection.ApplicationInsights != null) + { + Connection.ApplicationInsights.TrackEvent(MyInvocation.MyCommand.Name); + } // Check if we should ensure that we are connected if(skipConnectedValidation) return; // Ensure there is an active connection - if (PnPConnection.Current == null) + if (Connection == null) { throw new InvalidOperationException(Properties.Resources.NoConnection); } } - - public HttpClient HttpClient => PnP.Framework.Http.PnPHttpClient.Instance.GetHttpClient(); } } diff --git a/src/Commands/Base/PnPConnection.cs b/src/Commands/Base/PnPConnection.cs index f05db3cbc..4e062d732 100644 --- a/src/Commands/Base/PnPConnection.cs +++ b/src/Commands/Base/PnPConnection.cs @@ -17,6 +17,7 @@ using PnP.PowerShell.Commands.Utilities; using System.Threading.Tasks; using System.Threading; +using System.Net.Http; namespace PnP.PowerShell.Commands.Base { @@ -34,6 +35,10 @@ public class PnPConnection #region Properties + /// + /// Returns a reusable HTTPClient that can be used to make HTTP calls on this connection instance + /// + internal HttpClient HttpClient => PnP.Framework.Http.PnPHttpClient.Instance.GetHttpClient(); private PnPContext pnpContext { get; set; } @@ -64,7 +69,11 @@ internal PnPContext PnPContext internal static List ContextCache { get; set; } + /// + /// Connection instance which is set by connecting without -ReturnConnection + /// public static PnPConnection Current { get; internal set; } + public ConnectionType ConnectionType { get; protected set; } /// diff --git a/src/Commands/Base/PnPGraphCmdlet.cs b/src/Commands/Base/PnPGraphCmdlet.cs index 44ccc07ff..2805494bd 100644 --- a/src/Commands/Base/PnPGraphCmdlet.cs +++ b/src/Commands/Base/PnPGraphCmdlet.cs @@ -1,11 +1,7 @@ using Microsoft.Graph; using Microsoft.SharePoint.Client; using PnP.Core.Services; -using PnP.PowerShell.Commands.Attributes; using PnP.PowerShell.Commands.Model; -using PnP.PowerShell.Commands.Properties; -using System; -using System.Collections.Generic; using System.Management.Automation; using System.Net.Http.Headers; using System.Threading.Tasks; @@ -20,23 +16,19 @@ public abstract class PnPGraphCmdlet : PnPConnectedCmdlet /// /// Reference the the SharePoint context on the current connection. If NULL it means there is no SharePoint context available on the current connection. /// - public ClientContext ClientContext => Connection?.Context ?? PnPConnection.Current.Context; + public ClientContext ClientContext => Connection?.Context; - public PnPContext PnPContext => Connection?.PnPContext ?? PnPConnection.Current.PnPContext; - - // do not remove '#!#99' - [Parameter(Mandatory = false, HelpMessage = "Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection.")] - public PnPConnection Connection = null; - // do not remove '#!#99' + public PnPContext PnPContext => Connection?.PnPContext; private GraphServiceClient serviceClient; protected override void BeginProcessing() { base.BeginProcessing(); - if (PnPConnection.Current?.Context != null) + + if (Connection?.Context != null) { - var contextSettings = PnPConnection.Current.Context.GetContextSettings(); + var contextSettings = Connection.Context.GetContextSettings(); if (contextSettings?.Type == Framework.Utilities.Context.ClientContextType.Cookie || contextSettings?.Type == Framework.Utilities.Context.ClientContextType.SharePointACSAppOnly) { var typeString = contextSettings?.Type == Framework.Utilities.Context.ClientContextType.Cookie ? "WebLogin/Cookie" : "ACS"; @@ -52,15 +44,15 @@ public string AccessToken { get { - if (PnPConnection.Current?.ConnectionMethod == ConnectionMethod.ManagedIdentity) + if (Connection?.ConnectionMethod == ConnectionMethod.ManagedIdentity) { - return TokenHandler.GetManagedIdentityTokenAsync(this, HttpClient, $"https://{PnPConnection.Current.GraphEndPoint}/").GetAwaiter().GetResult(); + return TokenHandler.GetManagedIdentityTokenAsync(this, Connection.HttpClient, $"https://{Connection.GraphEndPoint}/").GetAwaiter().GetResult(); } else { - if (PnPConnection.Current?.Context != null) + if (Connection?.Context != null) { - return TokenHandler.GetAccessToken(GetType(), $"https://{PnPConnection.Current.GraphEndPoint}/.default"); + return TokenHandler.GetAccessToken(GetType(), $"https://{Connection.GraphEndPoint}/.default", Connection); } } @@ -74,7 +66,7 @@ internal GraphServiceClient ServiceClient { if (serviceClient == null) { - var baseUrl = $"https://{PnPConnection.Current.GraphEndPoint}/v1.0"; + var baseUrl = $"https://{Connection.GraphEndPoint}/v1.0"; serviceClient = new GraphServiceClient(baseUrl, new DelegateAuthenticationProvider( async (requestMessage) => { @@ -91,6 +83,5 @@ await Task.Run(() => return serviceClient; } } - } } \ No newline at end of file diff --git a/src/Commands/Base/PnPOfficeManagementApiCmdlet.cs b/src/Commands/Base/PnPOfficeManagementApiCmdlet.cs index 870ac46be..41f630d78 100644 --- a/src/Commands/Base/PnPOfficeManagementApiCmdlet.cs +++ b/src/Commands/Base/PnPOfficeManagementApiCmdlet.cs @@ -1,8 +1,4 @@ -using PnP.PowerShell.Commands.Attributes; -using PnP.PowerShell.Commands.Model; -using PnP.PowerShell.Commands.Properties; -using System; -using System.Collections.Generic; +using System; using System.Management.Automation; using Microsoft.SharePoint.Client; using System.Linq; @@ -21,9 +17,9 @@ public string AccessToken { get { - if (PnPConnection.Current?.Context != null) + if (Connection?.Context != null) { - return TokenHandler.GetAccessToken(GetType(), "https://manage.office.com/.default"); + return TokenHandler.GetAccessToken(GetType(), "https://manage.office.com/.default", Connection); } return null; } @@ -32,9 +28,9 @@ public string AccessToken protected override void BeginProcessing() { base.BeginProcessing(); - if (PnPConnection.Current?.Context != null) + if (Connection?.Context != null) { - if (PnPConnection.Current.Context.GetContextSettings().Type == Framework.Utilities.Context.ClientContextType.Cookie) + if (Connection?.Context.GetContextSettings().Type == Framework.Utilities.Context.ClientContextType.Cookie) { throw new PSInvalidOperationException("This cmdlet not work with a WebLogin/Cookie based connection towards SharePoint."); } diff --git a/src/Commands/Base/PnPSharePointCmdlet.cs b/src/Commands/Base/PnPSharePointCmdlet.cs index dc3a6b594..6f7050bd6 100644 --- a/src/Commands/Base/PnPSharePointCmdlet.cs +++ b/src/Commands/Base/PnPSharePointCmdlet.cs @@ -22,36 +22,32 @@ public abstract class PnPSharePointCmdlet : PnPConnectedCmdlet /// public ClientContext ClientContext => Connection?.Context; + /// + /// Reference the the PnP context on the current connection. If NULL it means there is no PnP context available on the current connection. + /// public PnPContext PnPContext => Connection?.PnPContext ?? Connection.PnPContext; - public new HttpClient HttpClient => PnP.Framework.Http.PnPHttpClient.Instance.GetHttpClient(ClientContext); - - // do not remove '#!#99' - [Parameter(Mandatory = false, HelpMessage = "Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection.")] - public PnPConnection Connection = null; - // do not remove '#!#99' + /// + /// HttpClient based off of the ClientContext that can be used to make raw HTTP calls to SharePoint Online + /// + public HttpClient HttpClient => PnP.Framework.Http.PnPHttpClient.Instance.GetHttpClient(ClientContext); protected override void BeginProcessing() { // Call the base but instruct it not to check if there's an active connection as we will do that in this method already base.BeginProcessing(true); - // If a specific connection has been provided, use that, otherwise use the current connection - if(Connection == null) - { - Connection = PnPConnection.Current; - } - - // Track the execution of the cmdlet - if (Connection != null && Connection.ApplicationInsights != null) - { - Connection.ApplicationInsights.TrackEvent(MyInvocation.MyCommand.Name); - } - // Ensure there is an active connection to work with if (Connection == null || ClientContext == null) { - throw new InvalidOperationException(Resources.NoSharePointConnection); + if (ParameterSpecified(nameof(Connection))) + { + throw new InvalidOperationException(Resources.NoSharePointConnectionInProvidedConnection); + } + else + { + throw new InvalidOperationException(Resources.NoDefaultSharePointConnection); + } } } @@ -137,7 +133,7 @@ public string GraphAccessToken { if (Connection?.Context != null) { - return TokenHandler.GetAccessToken(GetType(), $"https://{Connection.GraphEndPoint}/.default"); + return TokenHandler.GetAccessToken(GetType(), $"https://{Connection.GraphEndPoint}/.default", Connection); } } @@ -168,6 +164,5 @@ protected void PollOperation(SpoOperation spoOperation) } WriteWarning("SharePoint Operation Wait Interrupted"); } - } } diff --git a/src/Commands/Base/PnPWebCmdlet.cs b/src/Commands/Base/PnPWebCmdlet.cs index 78d6e7e4c..876639543 100644 --- a/src/Commands/Base/PnPWebCmdlet.cs +++ b/src/Commands/Base/PnPWebCmdlet.cs @@ -1,9 +1,7 @@ using System; -using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Base.PipeBinds; using System.Management.Automation; using Microsoft.SharePoint.Client; -using PnP.PowerShell.Commands.Extensions; namespace PnP.PowerShell.Commands @@ -48,20 +46,20 @@ private Web GetWeb() { var subWeb = Web.GetWeb(ClientContext); subWeb.EnsureProperty(w => w.Url); - PnPConnection.Current.CloneContext(subWeb.Url); - web = PnPConnection.Current.Context.Web; + Connection.CloneContext(subWeb.Url); + web = Connection.Context.Web; } #pragma warning restore CS0618 else { - if (PnPConnection.Current.Context.Url != PnPConnection.Current.Url) + if (Connection.Context.Url != Connection.Url) { - PnPConnection.Current.RestoreCachedContext(PnPConnection.Current.Url); + Connection.RestoreCachedContext(Connection.Url); } web = ClientContext.Web; } - PnPConnection.Current.Context.ExecuteQueryRetry(); + Connection.Context.ExecuteQueryRetry(); return web; } @@ -69,16 +67,16 @@ private Web GetWeb() protected override void EndProcessing() { base.EndProcessing(); - if (PnPConnection.Current.Context.Url != PnPConnection.Current.Url) + if (Connection.Context.Url != Connection.Url) { - PnPConnection.Current.RestoreCachedContext(PnPConnection.Current.Url); + Connection.RestoreCachedContext(Connection.Url); } } protected override void BeginProcessing() { base.BeginProcessing(); - PnPConnection.Current.CacheContext(); + Connection.CacheContext(); } } } \ No newline at end of file diff --git a/src/Commands/Base/TokenHandling.cs b/src/Commands/Base/TokenHandling.cs index b4378d65f..eb17df992 100644 --- a/src/Commands/Base/TokenHandling.cs +++ b/src/Commands/Base/TokenHandling.cs @@ -48,9 +48,9 @@ internal static void ValidateTokenForPermissions(Type cmdletType, string token) } } - internal static string GetAccessToken(Type cmdletType, string appOnlyDefaultScope) + internal static string GetAccessToken(Type cmdletType, string appOnlyDefaultScope, PnPConnection connection) { - var contextSettings = PnPConnection.Current.Context.GetContextSettings(); + var contextSettings = connection.Context.GetContextSettings(); var authManager = contextSettings.AuthenticationManager; if (authManager != null) { diff --git a/src/Commands/Graph/InvokeGraphMethod.cs b/src/Commands/Graph/InvokeGraphMethod.cs index d82781e12..e7e36c4fd 100644 --- a/src/Commands/Graph/InvokeGraphMethod.cs +++ b/src/Commands/Graph/InvokeGraphMethod.cs @@ -179,7 +179,7 @@ private void WriteGraphResult(string result) private void GetRequest() { - var result = GraphHelper.GetAsync(HttpClient, Url, AccessToken, AdditionalHeaders).GetAwaiter().GetResult(); + var result = GraphHelper.GetAsync(Connection, Url, AccessToken, AdditionalHeaders).GetAwaiter().GetResult(); if (Raw.IsPresent) { WriteObject(result); @@ -203,7 +203,7 @@ private void GetRequest() break; } var nextLink = nextLinkProperty.ToString(); - result = GraphHelper.GetAsync(HttpClient, nextLink, AccessToken, AdditionalHeaders).GetAwaiter().GetResult(); + result = GraphHelper.GetAsync(Connection, nextLink, AccessToken, AdditionalHeaders).GetAwaiter().GetResult(); element = JsonSerializer.Deserialize(result); dynamic nextObj = Deserialize(element); if (nextObj != null && nextObj.value != null && (nextObj.value is List)) @@ -228,28 +228,28 @@ private void GetRequest() private void PostRequest() { - var response = GraphHelper.PostAsync(HttpClient, Url, AccessToken, GetHttpContent(), AdditionalHeaders).GetAwaiter().GetResult(); + var response = GraphHelper.PostAsync(Connection, Url, AccessToken, GetHttpContent(), AdditionalHeaders).GetAwaiter().GetResult(); var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); WriteGraphResult(result); } private void PutRequest() { - var response = GraphHelper.PutAsync(HttpClient, Url, AccessToken, GetHttpContent(), AdditionalHeaders).GetAwaiter().GetResult(); + var response = GraphHelper.PutAsync(Connection, Url, AccessToken, GetHttpContent(), AdditionalHeaders).GetAwaiter().GetResult(); var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); WriteGraphResult(result); } private void PatchRequest() { - var response = GraphHelper.PatchAsync(HttpClient, AccessToken, GetHttpContent(), Url, AdditionalHeaders).GetAwaiter().GetResult(); + var response = GraphHelper.PatchAsync(Connection, AccessToken, GetHttpContent(), Url, AdditionalHeaders).GetAwaiter().GetResult(); var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); WriteGraphResult(result); } private void DeleteRequest() { - var response = GraphHelper.DeleteAsync(HttpClient, Url, AccessToken, AdditionalHeaders).GetAwaiter().GetResult(); + var response = GraphHelper.DeleteAsync(Connection, Url, AccessToken, AdditionalHeaders).GetAwaiter().GetResult(); var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); WriteGraphResult(result); } diff --git a/src/Commands/Lists/CopyList.cs b/src/Commands/Lists/CopyList.cs index 7b48b557b..17a64b6db 100644 --- a/src/Commands/Lists/CopyList.cs +++ b/src/Commands/Lists/CopyList.cs @@ -96,7 +96,7 @@ protected override void ExecuteCmdlet() // Execute site script on destination site so the list will be created WriteVerbose($"Executing site script to site at {DestinationWebUrl}"); - var actionResults = PnP.PowerShell.Commands.Utilities.SiteTemplates.InvokeSiteScript(HttpClient, AccessToken, script, DestinationWebUrl).GetAwaiter().GetResult().Items.ToArray(); + var actionResults = PnP.PowerShell.Commands.Utilities.SiteTemplates.InvokeSiteScript(Connection, AccessToken, script, DestinationWebUrl).GetAwaiter().GetResult().Items.ToArray(); // Ensure site script actions have been executed if(actionResults.Length == 0) diff --git a/src/Commands/ManagementApi/GetUnifiedAuditLog.cs b/src/Commands/ManagementApi/GetUnifiedAuditLog.cs index bafebdf1e..20c07fa10 100644 --- a/src/Commands/ManagementApi/GetUnifiedAuditLog.cs +++ b/src/Commands/ManagementApi/GetUnifiedAuditLog.cs @@ -57,7 +57,7 @@ protected string ContentTypeString private IEnumerable GetSubscriptions() { var url = $"{ApiUrl}/subscriptions/list"; - return GraphHelper.GetAsync>(HttpClient, url, AccessToken).GetAwaiter().GetResult(); + return GraphHelper.GetAsync>(Connection, url, AccessToken).GetAwaiter().GetResult(); } private void EnsureSubscription(string contentType) @@ -66,7 +66,7 @@ private void EnsureSubscription(string contentType) var subscription = subscriptions.FirstOrDefault(s => s.ContentType == contentType); if (subscription == null) { - subscription = GraphHelper.PostAsync(HttpClient, $"{ApiUrl}/subscriptions/start?contentType={contentType}&PublisherIdentifier={TenantId}", AccessToken).GetAwaiter().GetResult(); + subscription = GraphHelper.PostAsync(Connection, $"{ApiUrl}/subscriptions/start?contentType={contentType}&PublisherIdentifier={TenantId}", AccessToken).GetAwaiter().GetResult(); if (!subscription.Status.Equals("enabled", StringComparison.OrdinalIgnoreCase)) { throw new Exception($"Cannot enable subscription for {contentType}"); @@ -89,7 +89,7 @@ protected override void ExecuteCmdlet() } List subscriptionContents = new List(); - var subscriptionResponse = GraphHelper.GetResponseAsync(HttpClient, url, AccessToken).GetAwaiter().GetResult(); + var subscriptionResponse = GraphHelper.GetResponseAsync(Connection, url, AccessToken).GetAwaiter().GetResult(); var content = subscriptionResponse.Content.ReadAsStringAsync().GetAwaiter().GetResult(); if (subscriptionResponse.IsSuccessStatusCode) @@ -97,7 +97,7 @@ protected override void ExecuteCmdlet() subscriptionContents.AddRange(System.Text.Json.JsonSerializer.Deserialize>(content, new System.Text.Json.JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase })); while (subscriptionResponse.Headers.Contains("NextPageUri")) { - subscriptionResponse = GraphHelper.GetResponseAsync(HttpClient, subscriptionResponse.Headers.GetValues("NextPageUri").First(), AccessToken).GetAwaiter().GetResult(); + subscriptionResponse = GraphHelper.GetResponseAsync(Connection, subscriptionResponse.Headers.GetValues("NextPageUri").First(), AccessToken).GetAwaiter().GetResult(); if (subscriptionResponse.IsSuccessStatusCode) { content = subscriptionResponse.Content.ReadAsStringAsync().GetAwaiter().GetResult(); @@ -115,7 +115,7 @@ protected override void ExecuteCmdlet() { foreach (var subscriptionContent in subscriptionContents) { - var logs = GraphHelper.GetAsync>(HttpClient, subscriptionContent.ContentUri, AccessToken, false).GetAwaiter().GetResult(); + var logs = GraphHelper.GetAsync>(Connection, subscriptionContent.ContentUri, AccessToken, false).GetAwaiter().GetResult(); WriteObject(logs, true); } } diff --git a/src/Commands/Microsoft365Groups/AddMicrosoft365GroupMember.cs b/src/Commands/Microsoft365Groups/AddMicrosoft365GroupMember.cs index ae101a10c..21012ee0a 100644 --- a/src/Commands/Microsoft365Groups/AddMicrosoft365GroupMember.cs +++ b/src/Commands/Microsoft365Groups/AddMicrosoft365GroupMember.cs @@ -23,7 +23,7 @@ public class AddMicrosoft365GroupMember : PnPGraphCmdlet protected override void ExecuteCmdlet() { - Microsoft365GroupsUtility.AddMembersAsync(HttpClient, Identity.GetGroupId(HttpClient, AccessToken), Users, AccessToken, RemoveExisting).GetAwaiter().GetResult(); + Microsoft365GroupsUtility.AddMembersAsync(Connection, Identity.GetGroupId(Connection, AccessToken), Users, AccessToken, RemoveExisting).GetAwaiter().GetResult(); } } } \ No newline at end of file diff --git a/src/Commands/Microsoft365Groups/AddMicrosoft365GroupOwner.cs b/src/Commands/Microsoft365Groups/AddMicrosoft365GroupOwner.cs index bcdccab37..4d214c822 100644 --- a/src/Commands/Microsoft365Groups/AddMicrosoft365GroupOwner.cs +++ b/src/Commands/Microsoft365Groups/AddMicrosoft365GroupOwner.cs @@ -1,6 +1,4 @@ -using PnP.Framework.Entities; -using PnP.Framework.Graph; -using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Attributes; using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Base.PipeBinds; using PnP.PowerShell.Commands.Utilities; @@ -23,7 +21,7 @@ public class AddMicrosoft365GroupOwner : PnPGraphCmdlet protected override void ExecuteCmdlet() { - Microsoft365GroupsUtility.AddOwnersAsync(HttpClient, Identity.GetGroupId(HttpClient, AccessToken), Users, AccessToken, RemoveExisting).GetAwaiter().GetResult(); + Microsoft365GroupsUtility.AddOwnersAsync(Connection, Identity.GetGroupId(Connection, AccessToken), Users, AccessToken, RemoveExisting).GetAwaiter().GetResult(); } } } \ No newline at end of file diff --git a/src/Commands/Microsoft365Groups/ClearMicrosoft365GroupMember.cs b/src/Commands/Microsoft365Groups/ClearMicrosoft365GroupMember.cs index d55c77ab6..80132c9d7 100644 --- a/src/Commands/Microsoft365Groups/ClearMicrosoft365GroupMember.cs +++ b/src/Commands/Microsoft365Groups/ClearMicrosoft365GroupMember.cs @@ -17,7 +17,7 @@ public class ClearMicrosoft365GroupMember : PnPGraphCmdlet protected override void ExecuteCmdlet() { - Microsoft365GroupsUtility.ClearMembersAsync(HttpClient, Identity.GetGroupId(HttpClient, AccessToken), AccessToken).GetAwaiter().GetResult(); + Microsoft365GroupsUtility.ClearMembersAsync(Connection, Identity.GetGroupId(Connection, AccessToken), AccessToken).GetAwaiter().GetResult(); } } } \ No newline at end of file diff --git a/src/Commands/Microsoft365Groups/ClearMicrosoft365GroupOwner.cs b/src/Commands/Microsoft365Groups/ClearMicrosoft365GroupOwner.cs index b83eecfa8..6ca4bbdbc 100644 --- a/src/Commands/Microsoft365Groups/ClearMicrosoft365GroupOwner.cs +++ b/src/Commands/Microsoft365Groups/ClearMicrosoft365GroupOwner.cs @@ -16,9 +16,9 @@ public class ClearMicrosoft365GroupOwner : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Identity.GetGroupId(HttpClient, AccessToken); - Microsoft365GroupsUtility.ClearOwnersAsync(HttpClient, groupId, AccessToken).GetAwaiter().GetResult(); - var owners = Microsoft365GroupsUtility.GetOwnersAsync(HttpClient, groupId, AccessToken).GetAwaiter().GetResult(); + var groupId = Identity.GetGroupId(Connection, AccessToken); + Microsoft365GroupsUtility.ClearOwnersAsync(Connection, groupId, AccessToken).GetAwaiter().GetResult(); + var owners = Microsoft365GroupsUtility.GetOwnersAsync(Connection, groupId, AccessToken).GetAwaiter().GetResult(); if (owners != null && owners.Any()) { WriteWarning($"Clearing all owners is not possible as there will always have to be at least one owner. To changed the owners with new owners use Set-PnPMicrosoft365GroupOwner -Identity {groupId} -Owners \"newowner@domain.com\""); diff --git a/src/Commands/Microsoft365Groups/GetDeletedMicrosoft365Group.cs b/src/Commands/Microsoft365Groups/GetDeletedMicrosoft365Group.cs index b959eb47d..867715daa 100644 --- a/src/Commands/Microsoft365Groups/GetDeletedMicrosoft365Group.cs +++ b/src/Commands/Microsoft365Groups/GetDeletedMicrosoft365Group.cs @@ -18,11 +18,11 @@ protected override void ExecuteCmdlet() { if (Identity != null) { - WriteObject(Identity.GetDeletedGroup(HttpClient, AccessToken)); + WriteObject(Identity.GetDeletedGroup(Connection, AccessToken)); } else { - var groups = Microsoft365GroupsUtility.GetDeletedGroupsAsync(HttpClient, AccessToken).GetAwaiter().GetResult(); + var groups = Microsoft365GroupsUtility.GetDeletedGroupsAsync(Connection, AccessToken).GetAwaiter().GetResult(); WriteObject(groups.OrderBy(g => g.DisplayName), true); } } diff --git a/src/Commands/Microsoft365Groups/GetMicrosoft365Group.cs b/src/Commands/Microsoft365Groups/GetMicrosoft365Group.cs index 22d2ca671..9cd2b7a7b 100644 --- a/src/Commands/Microsoft365Groups/GetMicrosoft365Group.cs +++ b/src/Commands/Microsoft365Groups/GetMicrosoft365Group.cs @@ -41,12 +41,12 @@ protected override void ExecuteCmdlet() if (Identity != null) { - var group = Identity.GetGroup(HttpClient, AccessToken, includeSiteUrl, IncludeOwners); + var group = Identity.GetGroup(Connection, AccessToken, includeSiteUrl, IncludeOwners); WriteObject(group); } else { - var groups = Microsoft365GroupsUtility.GetGroupsAsync(HttpClient, AccessToken, includeSiteUrl, IncludeOwners).GetAwaiter().GetResult(); + var groups = Microsoft365GroupsUtility.GetGroupsAsync(Connection, AccessToken, includeSiteUrl, IncludeOwners).GetAwaiter().GetResult(); WriteObject(groups.OrderBy(p => p.DisplayName), true); } diff --git a/src/Commands/Microsoft365Groups/GetMicrosoft365GroupMember.cs b/src/Commands/Microsoft365Groups/GetMicrosoft365GroupMember.cs index 5ac4bd8fd..9974527ff 100644 --- a/src/Commands/Microsoft365Groups/GetMicrosoft365GroupMember.cs +++ b/src/Commands/Microsoft365Groups/GetMicrosoft365GroupMember.cs @@ -17,7 +17,7 @@ public class GetMicrosoft365GroupMember : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var members = Microsoft365GroupsUtility.GetMembersAsync(HttpClient, Identity.GetGroupId(HttpClient, AccessToken), AccessToken).GetAwaiter().GetResult(); + var members = Microsoft365GroupsUtility.GetMembersAsync(Connection, Identity.GetGroupId(Connection, AccessToken), AccessToken).GetAwaiter().GetResult(); WriteObject(members?.OrderBy(m => m.DisplayName), true); } } diff --git a/src/Commands/Microsoft365Groups/GetMicrosoft365GroupOwner.cs b/src/Commands/Microsoft365Groups/GetMicrosoft365GroupOwner.cs index a0597f97e..487b7aa9f 100644 --- a/src/Commands/Microsoft365Groups/GetMicrosoft365GroupOwner.cs +++ b/src/Commands/Microsoft365Groups/GetMicrosoft365GroupOwner.cs @@ -17,7 +17,7 @@ public class GetMicrosoft365GroupOwner : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var owners = Microsoft365GroupsUtility.GetOwnersAsync(HttpClient, Identity.GetGroupId(HttpClient, AccessToken), AccessToken).GetAwaiter().GetResult(); + var owners = Microsoft365GroupsUtility.GetOwnersAsync(Connection, Identity.GetGroupId(Connection, AccessToken), AccessToken).GetAwaiter().GetResult(); WriteObject(owners?.OrderBy(o => o.DisplayName), true); } } diff --git a/src/Commands/Microsoft365Groups/GetMicrosoft365GroupSettingTemplates.cs b/src/Commands/Microsoft365Groups/GetMicrosoft365GroupSettingTemplates.cs index d5080c7a6..cc110f0a3 100644 --- a/src/Commands/Microsoft365Groups/GetMicrosoft365GroupSettingTemplates.cs +++ b/src/Commands/Microsoft365Groups/GetMicrosoft365GroupSettingTemplates.cs @@ -16,12 +16,12 @@ protected override void ExecuteCmdlet() { if (Identity != null) { - var groupSettingTemplate = Microsoft365GroupsUtility.GetGroupTemplateSettingsAsync(HttpClient, AccessToken, Identity).GetAwaiter().GetResult(); + var groupSettingTemplate = Microsoft365GroupsUtility.GetGroupTemplateSettingsAsync(Connection, AccessToken, Identity).GetAwaiter().GetResult(); WriteObject(groupSettingTemplate); } else { - var groupSettingTemplates = Microsoft365GroupsUtility.GetGroupTemplateSettingsAsync(HttpClient, AccessToken).GetAwaiter().GetResult(); + var groupSettingTemplates = Microsoft365GroupsUtility.GetGroupTemplateSettingsAsync(Connection, AccessToken).GetAwaiter().GetResult(); WriteObject(groupSettingTemplates?.Value, true); } } diff --git a/src/Commands/Microsoft365Groups/GetMicrosoft365GroupSettings.cs b/src/Commands/Microsoft365Groups/GetMicrosoft365GroupSettings.cs index db1119cca..79aeda295 100644 --- a/src/Commands/Microsoft365Groups/GetMicrosoft365GroupSettings.cs +++ b/src/Commands/Microsoft365Groups/GetMicrosoft365GroupSettings.cs @@ -18,13 +18,13 @@ protected override void ExecuteCmdlet() { if (Identity != null) { - var groupId = Identity.GetGroupId(HttpClient, AccessToken); - var groupSettings = Microsoft365GroupsUtility.GetGroupSettingsAsync(HttpClient, AccessToken, groupId.ToString()).GetAwaiter().GetResult(); + var groupId = Identity.GetGroupId(Connection, AccessToken); + var groupSettings = Microsoft365GroupsUtility.GetGroupSettingsAsync(Connection, AccessToken, groupId.ToString()).GetAwaiter().GetResult(); WriteObject(groupSettings?.Value, true); } else { - var groupSettings = Microsoft365GroupsUtility.GetGroupSettingsAsync(HttpClient, AccessToken).GetAwaiter().GetResult(); + var groupSettings = Microsoft365GroupsUtility.GetGroupSettingsAsync(Connection, AccessToken).GetAwaiter().GetResult(); WriteObject(groupSettings?.Value, true); } } diff --git a/src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs b/src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs index cc7ddae5f..45891aa1a 100644 --- a/src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs +++ b/src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs @@ -1,14 +1,11 @@ using Microsoft.SharePoint.Client; -using PnP.Framework.Graph; using PnP.PowerShell.Commands.Attributes; using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Enums; using PnP.PowerShell.Commands.Model; -using PnP.PowerShell.Commands.Properties; using PnP.PowerShell.Commands.Utilities; using System; using System.Collections.Generic; -using System.Linq; using System.Management.Automation; namespace PnP.PowerShell.Commands.Microsoft365Groups @@ -67,7 +64,7 @@ protected override void ExecuteCmdlet() if (!Force) { - var candidate = Microsoft365GroupsUtility.GetGroupAsync(HttpClient, MailNickname, AccessToken, false, false).GetAwaiter().GetResult(); + var candidate = Microsoft365GroupsUtility.GetGroupAsync(Connection, MailNickname, AccessToken, false, false).GetAwaiter().GetResult(); forceCreation = candidate == null || ShouldContinue($"The Microsoft 365 Group '{MailNickname} already exists. Do you want to create a new one?", Properties.Resources.Confirm); } else @@ -129,14 +126,14 @@ protected override void ExecuteCmdlet() } } - var group = Microsoft365GroupsUtility.CreateAsync(HttpClient, AccessToken, newGroup, CreateTeam, LogoPath, Owners, Members, HideFromAddressLists, HideFromOutlookClients, Labels).GetAwaiter().GetResult(); + var group = Microsoft365GroupsUtility.CreateAsync(Connection, AccessToken, newGroup, CreateTeam, LogoPath, Owners, Members, HideFromAddressLists, HideFromOutlookClients, Labels).GetAwaiter().GetResult(); if (ParameterSpecified(nameof(HideFromAddressLists)) || ParameterSpecified(nameof(HideFromOutlookClients))) { - Microsoft365GroupsUtility.SetVisibilityAsync(HttpClient, AccessToken, group.Id.Value, HideFromAddressLists, HideFromOutlookClients).GetAwaiter().GetResult(); + Microsoft365GroupsUtility.SetVisibilityAsync(Connection, AccessToken, group.Id.Value, HideFromAddressLists, HideFromOutlookClients).GetAwaiter().GetResult(); } - var updatedGroup = Microsoft365GroupsUtility.GetGroupAsync(HttpClient, group.Id.Value, AccessToken, true, false).GetAwaiter().GetResult(); + var updatedGroup = Microsoft365GroupsUtility.GetGroupAsync(Connection, group.Id.Value, AccessToken, true, false).GetAwaiter().GetResult(); WriteObject(updatedGroup); } diff --git a/src/Commands/Microsoft365Groups/NewMicrosoft365GroupSettings.cs b/src/Commands/Microsoft365Groups/NewMicrosoft365GroupSettings.cs index 153de921a..59d0cd238 100644 --- a/src/Commands/Microsoft365Groups/NewMicrosoft365GroupSettings.cs +++ b/src/Commands/Microsoft365Groups/NewMicrosoft365GroupSettings.cs @@ -32,17 +32,17 @@ protected override void ExecuteCmdlet() { if (Identity != null) { - var groupId = Identity.GetGroupId(HttpClient, AccessToken); + var groupId = Identity.GetGroupId(Connection, AccessToken); var groupSettingObject = GroupSettingsObject(); - var responseValue = Microsoft365GroupsUtility.CreateGroupSetting(HttpClient, AccessToken, groupId.ToString(), groupSettingObject).GetAwaiter().GetResult(); + var responseValue = Microsoft365GroupsUtility.CreateGroupSetting(Connection, AccessToken, groupId.ToString(), groupSettingObject).GetAwaiter().GetResult(); WriteObject(responseValue); } else { var groupSettingObject = GroupSettingsObject(); - var responseValue = Microsoft365GroupsUtility.CreateGroupSetting(HttpClient, AccessToken, groupSettingObject).GetAwaiter().GetResult(); + var responseValue = Microsoft365GroupsUtility.CreateGroupSetting(Connection, AccessToken, groupSettingObject).GetAwaiter().GetResult(); WriteObject(responseValue); } } diff --git a/src/Commands/Microsoft365Groups/RemoveDeletedMicrosoft365Group.cs b/src/Commands/Microsoft365Groups/RemoveDeletedMicrosoft365Group.cs index 144ef5bab..be840a7e8 100644 --- a/src/Commands/Microsoft365Groups/RemoveDeletedMicrosoft365Group.cs +++ b/src/Commands/Microsoft365Groups/RemoveDeletedMicrosoft365Group.cs @@ -16,7 +16,7 @@ public class RemoveDeletedMicrosoft365Group : PnPGraphCmdlet protected override void ExecuteCmdlet() { - Microsoft365GroupsUtility.PermanentlyDeleteDeletedGroupAsync(HttpClient, Identity.GetDeletedGroupId(HttpClient, AccessToken), AccessToken).GetAwaiter().GetResult(); + Microsoft365GroupsUtility.PermanentlyDeleteDeletedGroupAsync(Connection, Identity.GetDeletedGroupId(Connection, AccessToken), AccessToken).GetAwaiter().GetResult(); } } } \ No newline at end of file diff --git a/src/Commands/Microsoft365Groups/RemoveMicrosoft365Group.cs b/src/Commands/Microsoft365Groups/RemoveMicrosoft365Group.cs index 30decc769..83fc56c88 100644 --- a/src/Commands/Microsoft365Groups/RemoveMicrosoft365Group.cs +++ b/src/Commands/Microsoft365Groups/RemoveMicrosoft365Group.cs @@ -17,7 +17,7 @@ public class RemoveMicrosoft365Group : PnPGraphCmdlet protected override void ExecuteCmdlet() { - Microsoft365GroupsUtility.RemoveGroupAsync(HttpClient, Identity.GetGroupId(HttpClient, AccessToken), AccessToken).GetAwaiter().GetResult(); + Microsoft365GroupsUtility.RemoveGroupAsync(Connection, Identity.GetGroupId(Connection, AccessToken), AccessToken).GetAwaiter().GetResult(); } } } \ No newline at end of file diff --git a/src/Commands/Microsoft365Groups/RemoveMicrosoft365GroupMember.cs b/src/Commands/Microsoft365Groups/RemoveMicrosoft365GroupMember.cs index 3edd30710..c1f3f5ffb 100644 --- a/src/Commands/Microsoft365Groups/RemoveMicrosoft365GroupMember.cs +++ b/src/Commands/Microsoft365Groups/RemoveMicrosoft365GroupMember.cs @@ -1,6 +1,4 @@ -using PnP.Framework.Entities; -using PnP.Framework.Graph; -using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Attributes; using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Base.PipeBinds; using PnP.PowerShell.Commands.Utilities; @@ -20,7 +18,7 @@ public class RemoveMicrosoft365GroupMember : PnPGraphCmdlet protected override void ExecuteCmdlet() { - Microsoft365GroupsUtility.RemoveMembersAsync(HttpClient, Identity.GetGroupId(HttpClient, AccessToken), Users, AccessToken).GetAwaiter().GetResult(); + Microsoft365GroupsUtility.RemoveMembersAsync(Connection, Identity.GetGroupId(Connection, AccessToken), Users, AccessToken).GetAwaiter().GetResult(); } } } \ No newline at end of file diff --git a/src/Commands/Microsoft365Groups/RemoveMicrosoft365GroupOwner.cs b/src/Commands/Microsoft365Groups/RemoveMicrosoft365GroupOwner.cs index 1b584b29f..9b4dec546 100644 --- a/src/Commands/Microsoft365Groups/RemoveMicrosoft365GroupOwner.cs +++ b/src/Commands/Microsoft365Groups/RemoveMicrosoft365GroupOwner.cs @@ -20,7 +20,7 @@ public class RemoveMicrosoft365GroupOwner : PnPGraphCmdlet protected override void ExecuteCmdlet() { - Microsoft365GroupsUtility.RemoveOwnersAsync(HttpClient, Identity.GetGroupId(HttpClient, AccessToken), Users, AccessToken).GetAwaiter().GetResult(); + Microsoft365GroupsUtility.RemoveOwnersAsync(Connection, Identity.GetGroupId(Connection, AccessToken), Users, AccessToken).GetAwaiter().GetResult(); } } } \ No newline at end of file diff --git a/src/Commands/Microsoft365Groups/RemoveMicrosoft365GroupSettings.cs b/src/Commands/Microsoft365Groups/RemoveMicrosoft365GroupSettings.cs index 27e827e4c..7c981b1bb 100644 --- a/src/Commands/Microsoft365Groups/RemoveMicrosoft365GroupSettings.cs +++ b/src/Commands/Microsoft365Groups/RemoveMicrosoft365GroupSettings.cs @@ -22,12 +22,12 @@ protected override void ExecuteCmdlet() { if (Group != null) { - var groupId = Group.GetGroupId(HttpClient, AccessToken); - Microsoft365GroupsUtility.RemoveGroupSetting(HttpClient, AccessToken, Identity, groupId.ToString()).GetAwaiter().GetResult(); + var groupId = Group.GetGroupId(Connection, AccessToken); + Microsoft365GroupsUtility.RemoveGroupSetting(Connection, AccessToken, Identity, groupId.ToString()).GetAwaiter().GetResult(); } else { - Microsoft365GroupsUtility.RemoveGroupSetting(HttpClient, AccessToken, Identity).GetAwaiter().GetResult(); + Microsoft365GroupsUtility.RemoveGroupSetting(Connection, AccessToken, Identity).GetAwaiter().GetResult(); } } } diff --git a/src/Commands/Microsoft365Groups/ResetMicrosoft365GroupExpiration.cs b/src/Commands/Microsoft365Groups/ResetMicrosoft365GroupExpiration.cs index 4d0ef0d4f..18bf50c7c 100644 --- a/src/Commands/Microsoft365Groups/ResetMicrosoft365GroupExpiration.cs +++ b/src/Commands/Microsoft365Groups/ResetMicrosoft365GroupExpiration.cs @@ -17,7 +17,7 @@ public class ResetMicrosoft365GroupExpiration : PnPGraphCmdlet protected override void ExecuteCmdlet() { - Microsoft365GroupsUtility.RenewAsync(HttpClient, AccessToken, Identity.GetGroupId(HttpClient, AccessToken)).GetAwaiter().GetResult(); + Microsoft365GroupsUtility.RenewAsync(Connection, AccessToken, Identity.GetGroupId(Connection, AccessToken)).GetAwaiter().GetResult(); } } } \ No newline at end of file diff --git a/src/Commands/Microsoft365Groups/RestoreDeletedMicrosoft365Group.cs b/src/Commands/Microsoft365Groups/RestoreDeletedMicrosoft365Group.cs index 7c1fd699a..b7efff89f 100644 --- a/src/Commands/Microsoft365Groups/RestoreDeletedMicrosoft365Group.cs +++ b/src/Commands/Microsoft365Groups/RestoreDeletedMicrosoft365Group.cs @@ -16,7 +16,7 @@ public class RestoreDeletedMicrosoft365Group : PnPGraphCmdlet protected override void ExecuteCmdlet() { - WriteObject(Microsoft365GroupsUtility.RestoreDeletedGroupAsync(HttpClient, Identity.GetDeletedGroupId(HttpClient, AccessToken), AccessToken).GetAwaiter().GetResult()); + WriteObject(Microsoft365GroupsUtility.RestoreDeletedGroupAsync(Connection, Identity.GetDeletedGroupId(Connection, AccessToken), AccessToken).GetAwaiter().GetResult()); } } } \ No newline at end of file diff --git a/src/Commands/Microsoft365Groups/SetMicrosoft365Group.cs b/src/Commands/Microsoft365Groups/SetMicrosoft365Group.cs index 0881c03eb..98fc01456 100644 --- a/src/Commands/Microsoft365Groups/SetMicrosoft365Group.cs +++ b/src/Commands/Microsoft365Groups/SetMicrosoft365Group.cs @@ -54,7 +54,7 @@ public class SetMicrosoft365Group : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var group = Identity.GetGroup(HttpClient, AccessToken, false, false); + var group = Identity.GetGroup(Connection, AccessToken, false, false); if (group != null) @@ -77,17 +77,17 @@ protected override void ExecuteCmdlet() } if (changed) { - group = Microsoft365GroupsUtility.UpdateAsync(HttpClient, AccessToken, group).GetAwaiter().GetResult(); + group = Microsoft365GroupsUtility.UpdateAsync(Connection, AccessToken, group).GetAwaiter().GetResult(); } if (ParameterSpecified(nameof(Owners))) { - Microsoft365GroupsUtility.UpdateOwnersAsync(HttpClient, group.Id.Value, AccessToken, Owners).GetAwaiter().GetResult(); + Microsoft365GroupsUtility.UpdateOwnersAsync(Connection, group.Id.Value, AccessToken, Owners).GetAwaiter().GetResult(); } if (ParameterSpecified(nameof(Members))) { - Microsoft365GroupsUtility.UpdateMembersAsync(HttpClient, group.Id.Value, AccessToken, Members).GetAwaiter().GetResult(); + Microsoft365GroupsUtility.UpdateMembersAsync(Connection, group.Id.Value, AccessToken, Members).GetAwaiter().GetResult(); } if (ParameterSpecified(nameof(LogoPath))) @@ -96,14 +96,14 @@ protected override void ExecuteCmdlet() { LogoPath = Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, LogoPath); } - Microsoft365GroupsUtility.UploadLogoAsync(HttpClient, AccessToken, group.Id.Value, LogoPath).GetAwaiter().GetResult(); + Microsoft365GroupsUtility.UploadLogoAsync(Connection, AccessToken, group.Id.Value, LogoPath).GetAwaiter().GetResult(); } if (ParameterSpecified(nameof(CreateTeam))) { if (!group.ResourceProvisioningOptions.Contains("Team")) { - Microsoft365GroupsUtility.CreateTeamAsync(HttpClient, AccessToken, group.Id.Value).GetAwaiter().GetResult(); + Microsoft365GroupsUtility.CreateTeamAsync(Connection, AccessToken, group.Id.Value).GetAwaiter().GetResult(); } else { @@ -114,7 +114,7 @@ protected override void ExecuteCmdlet() if (ParameterSpecified(nameof(HideFromAddressLists)) || ParameterSpecified(nameof(HideFromOutlookClients))) { // For this scenario a separate call needs to be made - Microsoft365GroupsUtility.SetVisibilityAsync(HttpClient, AccessToken, group.Id.Value, HideFromAddressLists, HideFromOutlookClients).GetAwaiter().GetResult(); + Microsoft365GroupsUtility.SetVisibilityAsync(Connection, AccessToken, group.Id.Value, HideFromAddressLists, HideFromOutlookClients).GetAwaiter().GetResult(); } var assignedLabels = new List(); @@ -133,7 +133,7 @@ protected override void ExecuteCmdlet() }); } } - Microsoft365GroupsUtility.SetSensitivityLabelsAsync(HttpClient, AccessToken, group.Id.Value, assignedLabels).GetAwaiter().GetResult(); + Microsoft365GroupsUtility.SetSensitivityLabelsAsync(Connection, AccessToken, group.Id.Value, assignedLabels).GetAwaiter().GetResult(); } else { diff --git a/src/Commands/Microsoft365Groups/SetMicrosoft365GroupSettings.cs b/src/Commands/Microsoft365Groups/SetMicrosoft365GroupSettings.cs index 652c9a6a8..ccb9c5609 100644 --- a/src/Commands/Microsoft365Groups/SetMicrosoft365GroupSettings.cs +++ b/src/Commands/Microsoft365Groups/SetMicrosoft365GroupSettings.cs @@ -28,15 +28,15 @@ protected override void ExecuteCmdlet() { if (Group != null) { - var groupId = Group.GetGroupId(HttpClient, AccessToken); + var groupId = Group.GetGroupId(Connection, AccessToken); var groupSettingObject = GroupSettingsObject(); - Microsoft365GroupsUtility.UpdateGroupSetting(HttpClient, AccessToken, Identity, groupId.ToString(), groupSettingObject).GetAwaiter().GetResult(); + Microsoft365GroupsUtility.UpdateGroupSetting(Connection, AccessToken, Identity, groupId.ToString(), groupSettingObject).GetAwaiter().GetResult(); } else { var groupSettingObject = GroupSettingsObject(); - Microsoft365GroupsUtility.UpdateGroupSetting(HttpClient, AccessToken, Identity, groupSettingObject).GetAwaiter().GetResult(); + Microsoft365GroupsUtility.UpdateGroupSetting(Connection, AccessToken, Identity, groupSettingObject).GetAwaiter().GetResult(); } } diff --git a/src/Commands/Planner/AddPlannerBucket.cs b/src/Commands/Planner/AddPlannerBucket.cs index 5f7b28a6c..5dd7ea442 100644 --- a/src/Commands/Planner/AddPlannerBucket.cs +++ b/src/Commands/Planner/AddPlannerBucket.cs @@ -28,14 +28,14 @@ protected override void ExecuteCmdlet() { if (ParameterSetName == ParameterName_BYGROUP) { - var groupId = Group.GetGroupId(HttpClient, AccessToken); + var groupId = Group.GetGroupId(Connection, AccessToken); if (groupId != null) { - var planId = Plan.GetIdAsync(HttpClient, AccessToken, groupId).GetAwaiter().GetResult(); + var planId = Plan.GetIdAsync(Connection, AccessToken, groupId).GetAwaiter().GetResult(); if (planId != null) { - WriteObject(PlannerUtility.CreateBucketAsync(HttpClient, AccessToken, Name, planId).GetAwaiter().GetResult(), true); + WriteObject(PlannerUtility.CreateBucketAsync(Connection, AccessToken, Name, planId).GetAwaiter().GetResult(), true); } else { @@ -49,7 +49,7 @@ protected override void ExecuteCmdlet() } else if (ParameterSetName == ParameterName_BYPLANID) { - WriteObject(PlannerUtility.CreateBucketAsync(HttpClient, AccessToken, Name, PlanId).GetAwaiter().GetResult(), true); + WriteObject(PlannerUtility.CreateBucketAsync(Connection, AccessToken, Name, PlanId).GetAwaiter().GetResult(), true); } } } diff --git a/src/Commands/Planner/AddPlannerRoster.cs b/src/Commands/Planner/AddPlannerRoster.cs index 4c5e93d74..24118ee1f 100644 --- a/src/Commands/Planner/AddPlannerRoster.cs +++ b/src/Commands/Planner/AddPlannerRoster.cs @@ -11,7 +11,7 @@ public class AddPlannerRoster : PnPGraphCmdlet { protected override void ExecuteCmdlet() { - PlannerUtility.CreateRosterAsync(HttpClient, AccessToken).GetAwaiter().GetResult(); + PlannerUtility.CreateRosterAsync(Connection, AccessToken).GetAwaiter().GetResult(); } } } \ No newline at end of file diff --git a/src/Commands/Planner/AddPlannerRosterMember.cs b/src/Commands/Planner/AddPlannerRosterMember.cs index 41d1dedee..adb7c8b74 100644 --- a/src/Commands/Planner/AddPlannerRosterMember.cs +++ b/src/Commands/Planner/AddPlannerRosterMember.cs @@ -18,14 +18,14 @@ public class AddPlannerRosterMember : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var roster = Identity.GetPlannerRosterAsync(HttpClient, AccessToken).GetAwaiter().GetResult(); + var roster = Identity.GetPlannerRosterAsync(Connection, AccessToken).GetAwaiter().GetResult(); if(roster == null) { throw new PSArgumentException("Provided Planner Roster could not be found", nameof(Identity)); } - PlannerUtility.AddRosterMemberAsync(HttpClient, AccessToken, roster.Id, User).GetAwaiter().GetResult(); + PlannerUtility.AddRosterMemberAsync(Connection, AccessToken, roster.Id, User).GetAwaiter().GetResult(); } } } \ No newline at end of file diff --git a/src/Commands/Planner/AddPlannerTask.cs b/src/Commands/Planner/AddPlannerTask.cs index 7cb566ea0..5e64e8d76 100644 --- a/src/Commands/Planner/AddPlannerTask.cs +++ b/src/Commands/Planner/AddPlannerTask.cs @@ -94,7 +94,7 @@ protected override void ExecuteCmdlet() var chunks = AssignedTo.Chunk(20); foreach (var chunk in chunks) { - var userIds = BatchUtility.GetPropertyBatchedAsync(HttpClient, AccessToken, chunk.ToArray(), "/users/{0}", "id").GetAwaiter().GetResult(); + var userIds = BatchUtility.GetPropertyBatchedAsync(Connection, AccessToken, chunk.ToArray(), "/users/{0}", "id").GetAwaiter().GetResult(); foreach (var userId in userIds) { newTask.Assignments.Add(userId.Value, new TaskAssignment()); @@ -105,32 +105,32 @@ protected override void ExecuteCmdlet() // By Group if (ParameterSetName == ParameterName_BYGROUP) { - var groupId = Group.GetGroupId(HttpClient, AccessToken); + var groupId = Group.GetGroupId(Connection, AccessToken); if (groupId == null) { throw new PSArgumentException("Group not found", nameof(Group)); } - var planId = Plan.GetIdAsync(HttpClient, AccessToken, groupId).GetAwaiter().GetResult(); + var planId = Plan.GetIdAsync(Connection, AccessToken, groupId).GetAwaiter().GetResult(); if (planId == null) { throw new PSArgumentException("Plan not found", nameof(Plan)); } newTask.PlanId = planId; - var bucket = Bucket.GetBucket(HttpClient, AccessToken, planId); + var bucket = Bucket.GetBucket(Connection, AccessToken, planId); if (bucket == null) { throw new PSArgumentException("Bucket not found", nameof(Bucket)); } newTask.BucketId = bucket.Id; - createdTask = PlannerUtility.AddTaskAsync(HttpClient, AccessToken, newTask).GetAwaiter().GetResult(); + createdTask = PlannerUtility.AddTaskAsync(Connection, AccessToken, newTask).GetAwaiter().GetResult(); } // By PlanId else { - var bucket = Bucket.GetBucket(HttpClient, AccessToken, PlanId); + var bucket = Bucket.GetBucket(Connection, AccessToken, PlanId); if (bucket == null) { throw new PSArgumentException("Bucket not found", nameof(Bucket)); @@ -139,13 +139,13 @@ protected override void ExecuteCmdlet() newTask.PlanId = PlanId; newTask.BucketId = bucket.Id; - createdTask = PlannerUtility.AddTaskAsync(HttpClient, AccessToken, newTask).GetAwaiter().GetResult(); + createdTask = PlannerUtility.AddTaskAsync(Connection, AccessToken, newTask).GetAwaiter().GetResult(); } if (ParameterSpecified(nameof(Description))) { - var existingTaskDetails = PlannerUtility.GetTaskDetailsAsync(HttpClient, AccessToken, createdTask.Id, false).GetAwaiter().GetResult(); - PlannerUtility.UpdateTaskDetailsAsync(HttpClient, AccessToken, existingTaskDetails, Description).GetAwaiter().GetResult(); + var existingTaskDetails = PlannerUtility.GetTaskDetailsAsync(Connection, AccessToken, createdTask.Id, false).GetAwaiter().GetResult(); + PlannerUtility.UpdateTaskDetailsAsync(Connection, AccessToken, existingTaskDetails, Description).GetAwaiter().GetResult(); } } } diff --git a/src/Commands/Planner/GetPlannerBucket.cs b/src/Commands/Planner/GetPlannerBucket.cs index 969a6c3ee..e4bcbe386 100644 --- a/src/Commands/Planner/GetPlannerBucket.cs +++ b/src/Commands/Planner/GetPlannerBucket.cs @@ -29,19 +29,19 @@ protected override void ExecuteCmdlet() { if (ParameterSetName == ParameterName_BYGROUP) { - var groupId = Group.GetGroupId(HttpClient, AccessToken); + var groupId = Group.GetGroupId(Connection, AccessToken); if (groupId != null) { - var planId = Plan.GetIdAsync(HttpClient, AccessToken, groupId).GetAwaiter().GetResult(); + var planId = Plan.GetIdAsync(Connection, AccessToken, groupId).GetAwaiter().GetResult(); if (planId != null) { if (!ParameterSpecified(nameof(Identity))) { - WriteObject(PlannerUtility.GetBucketsAsync(HttpClient, AccessToken, planId).GetAwaiter().GetResult(), true); + WriteObject(PlannerUtility.GetBucketsAsync(Connection, AccessToken, planId).GetAwaiter().GetResult(), true); } else { - WriteObject(Identity.GetBucket(HttpClient, AccessToken, planId)); + WriteObject(Identity.GetBucket(Connection, AccessToken, planId)); } } else @@ -56,7 +56,7 @@ protected override void ExecuteCmdlet() } else { - WriteObject(PlannerUtility.GetBucketsAsync(HttpClient, AccessToken, PlanId).GetAwaiter().GetResult(), true); + WriteObject(PlannerUtility.GetBucketsAsync(Connection, AccessToken, PlanId).GetAwaiter().GetResult(), true); } } } diff --git a/src/Commands/Planner/GetPlannerConfiguration.cs b/src/Commands/Planner/GetPlannerConfiguration.cs index 81c338e4f..c16ddfa7e 100644 --- a/src/Commands/Planner/GetPlannerConfiguration.cs +++ b/src/Commands/Planner/GetPlannerConfiguration.cs @@ -11,7 +11,7 @@ public class GetPlannerConfiguration : PnPGraphCmdlet { protected override void ExecuteCmdlet() { - var result = PlannerUtility.GetPlannerConfigAsync(HttpClient, AccessToken).GetAwaiter().GetResult(); + var result = PlannerUtility.GetPlannerConfigAsync(Connection, AccessToken).GetAwaiter().GetResult(); WriteObject(result); } } diff --git a/src/Commands/Planner/GetPlannerPlan.cs b/src/Commands/Planner/GetPlannerPlan.cs index 0b3c99aac..cedca0801 100644 --- a/src/Commands/Planner/GetPlannerPlan.cs +++ b/src/Commands/Planner/GetPlannerPlan.cs @@ -29,16 +29,16 @@ protected override void ExecuteCmdlet() { if (ParameterSetName == ParameterName_BYGROUP) { - var groupId = Group.GetGroupId(HttpClient, AccessToken); + var groupId = Group.GetGroupId(Connection, AccessToken); if (groupId != null) { if (ParameterSpecified(nameof(Identity))) { - WriteObject(Identity.GetPlanAsync(HttpClient, AccessToken, groupId, ResolveIdentities).GetAwaiter().GetResult()); + WriteObject(Identity.GetPlanAsync(Connection, AccessToken, groupId, ResolveIdentities).GetAwaiter().GetResult()); } else { - WriteObject(PlannerUtility.GetPlansAsync(HttpClient, AccessToken, groupId, ResolveIdentities).GetAwaiter().GetResult(), true); + WriteObject(PlannerUtility.GetPlansAsync(Connection, AccessToken, groupId, ResolveIdentities).GetAwaiter().GetResult(), true); } } else @@ -48,7 +48,7 @@ protected override void ExecuteCmdlet() } else { - WriteObject(PlannerUtility.GetPlanAsync(HttpClient, AccessToken, Id, ResolveIdentities).GetAwaiter().GetResult()); + WriteObject(PlannerUtility.GetPlanAsync(Connection, AccessToken, Id, ResolveIdentities).GetAwaiter().GetResult()); } } } diff --git a/src/Commands/Planner/GetPlannerRosterMember.cs b/src/Commands/Planner/GetPlannerRosterMember.cs index 24cd265d2..19d3741e8 100644 --- a/src/Commands/Planner/GetPlannerRosterMember.cs +++ b/src/Commands/Planner/GetPlannerRosterMember.cs @@ -15,14 +15,14 @@ public class GetPlannerRosterMember : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var roster = Identity.GetPlannerRosterAsync(HttpClient, AccessToken).GetAwaiter().GetResult(); + var roster = Identity.GetPlannerRosterAsync(Connection, AccessToken).GetAwaiter().GetResult(); if(roster == null) { throw new PSArgumentException("Provided Planner Roster could not be found", nameof(Identity)); } - PlannerUtility.GetRosterMembersAsync(HttpClient, AccessToken, roster.Id).GetAwaiter().GetResult(); + PlannerUtility.GetRosterMembersAsync(Connection, AccessToken, roster.Id).GetAwaiter().GetResult(); } } } \ No newline at end of file diff --git a/src/Commands/Planner/GetPlannerRosterPlan.cs b/src/Commands/Planner/GetPlannerRosterPlan.cs index 2eba2256f..785975f82 100644 --- a/src/Commands/Planner/GetPlannerRosterPlan.cs +++ b/src/Commands/Planner/GetPlannerRosterPlan.cs @@ -24,16 +24,16 @@ protected override void ExecuteCmdlet() switch (ParameterSetName) { case ParameterSet_BYUSER: - WriteObject(PlannerUtility.GetRosterPlansByUserAsync(HttpClient, AccessToken, User).GetAwaiter().GetResult(), true); + WriteObject(PlannerUtility.GetRosterPlansByUserAsync(Connection, AccessToken, User).GetAwaiter().GetResult(), true); break; case ParameterSet_BYROSTER: - var plannerRoster = Identity.GetPlannerRosterAsync(HttpClient, AccessToken).GetAwaiter().GetResult(); + var plannerRoster = Identity.GetPlannerRosterAsync(Connection, AccessToken).GetAwaiter().GetResult(); if (plannerRoster == null) { throw new PSArgumentException($"Planner Roster provided through {nameof(Identity)} could not be found", nameof(Identity)); } - WriteObject(PlannerUtility.GetRosterPlansByRosterAsync(HttpClient, AccessToken, plannerRoster.Id).GetAwaiter().GetResult(), true); + WriteObject(PlannerUtility.GetRosterPlansByRosterAsync(Connection, AccessToken, plannerRoster.Id).GetAwaiter().GetResult(), true); break; } } diff --git a/src/Commands/Planner/GetPlannerTask.cs b/src/Commands/Planner/GetPlannerTask.cs index 22d6b134a..ee82bbc71 100644 --- a/src/Commands/Planner/GetPlannerTask.cs +++ b/src/Commands/Planner/GetPlannerTask.cs @@ -40,13 +40,13 @@ protected override void ExecuteCmdlet() { if (ParameterSetName == ParameterSetName_BYGROUP) { - var groupId = Group.GetGroupId(HttpClient, AccessToken); + var groupId = Group.GetGroupId(Connection, AccessToken); if (groupId != null) { - var planId = Plan.GetIdAsync(HttpClient, AccessToken, groupId).GetAwaiter().GetResult(); + var planId = Plan.GetIdAsync(Connection, AccessToken, groupId).GetAwaiter().GetResult(); if (planId != null) { - WriteObject(PlannerUtility.GetTasksAsync(HttpClient, AccessToken, planId, ResolveUserDisplayNames).GetAwaiter().GetResult(), true); + WriteObject(PlannerUtility.GetTasksAsync(Connection, AccessToken, planId, ResolveUserDisplayNames).GetAwaiter().GetResult(), true); } else { @@ -60,15 +60,15 @@ protected override void ExecuteCmdlet() } else if (ParameterSetName == ParameterSetName_BYPLANID) { - WriteObject(PlannerUtility.GetTasksAsync(HttpClient, AccessToken, PlanId, ResolveUserDisplayNames).GetAwaiter().GetResult(), true); + WriteObject(PlannerUtility.GetTasksAsync(Connection, AccessToken, PlanId, ResolveUserDisplayNames).GetAwaiter().GetResult(), true); } else if (ParameterSetName == ParameterSetName_BYBUCKET) { - WriteObject(PlannerUtility.GetBucketTasksAsync(HttpClient, AccessToken, Bucket.GetId(), ResolveUserDisplayNames).GetAwaiter().GetResult(), true); + WriteObject(PlannerUtility.GetBucketTasksAsync(Connection, AccessToken, Bucket.GetId(), ResolveUserDisplayNames).GetAwaiter().GetResult(), true); } else if (ParameterSetName == ParameterSetName_BYTASKID) { - WriteObject(PlannerUtility.GetTaskAsync(HttpClient, AccessToken, TaskId, ResolveUserDisplayNames, IncludeDetails).GetAwaiter().GetResult()); + WriteObject(PlannerUtility.GetTaskAsync(Connection, AccessToken, TaskId, ResolveUserDisplayNames, IncludeDetails).GetAwaiter().GetResult()); } } } diff --git a/src/Commands/Planner/GetPlannerUserPolicy.cs b/src/Commands/Planner/GetPlannerUserPolicy.cs index 18e3e30a1..078305557 100644 --- a/src/Commands/Planner/GetPlannerUserPolicy.cs +++ b/src/Commands/Planner/GetPlannerUserPolicy.cs @@ -13,7 +13,7 @@ public class GetPlannerUserPolicy : PnPGraphCmdlet public string Identity; protected override void ExecuteCmdlet() { - var result = PlannerUtility.GetPlannerUserPolicyAsync(HttpClient, AccessToken, Identity).GetAwaiter().GetResult(); + var result = PlannerUtility.GetPlannerUserPolicyAsync(Connection, AccessToken, Identity).GetAwaiter().GetResult(); WriteObject(result); } } diff --git a/src/Commands/Planner/NewPlannerPlan.cs b/src/Commands/Planner/NewPlannerPlan.cs index a88e2444c..959fd3021 100644 --- a/src/Commands/Planner/NewPlannerPlan.cs +++ b/src/Commands/Planner/NewPlannerPlan.cs @@ -17,10 +17,10 @@ public class NewPlannerPlan : PnPGraphCmdlet public string Title; protected override void ExecuteCmdlet() { - var groupId = Group.GetGroupId(HttpClient, AccessToken); + var groupId = Group.GetGroupId(Connection, AccessToken); if (groupId != null) { - WriteObject(PlannerUtility.CreatePlanAsync(HttpClient, AccessToken, groupId, Title).GetAwaiter().GetResult()); + WriteObject(PlannerUtility.CreatePlanAsync(Connection, AccessToken, groupId, Title).GetAwaiter().GetResult()); } else { diff --git a/src/Commands/Planner/RemovePlannerBucket.cs b/src/Commands/Planner/RemovePlannerBucket.cs index d5199e5c2..6c5ab9bf6 100644 --- a/src/Commands/Planner/RemovePlannerBucket.cs +++ b/src/Commands/Planner/RemovePlannerBucket.cs @@ -28,19 +28,19 @@ protected override void ExecuteCmdlet() { if (ParameterSetName == ParameterName_BYNAME) { - var groupId = Group.GetGroupId(HttpClient, AccessToken); + var groupId = Group.GetGroupId(Connection, AccessToken); if (groupId != null) { - var planId = Plan.GetIdAsync(HttpClient, AccessToken, groupId).GetAwaiter().GetResult(); + var planId = Plan.GetIdAsync(Connection, AccessToken, groupId).GetAwaiter().GetResult(); if (planId != null) { - var bucket = Identity.GetBucket(HttpClient, AccessToken, planId); + var bucket = Identity.GetBucket(Connection, AccessToken, planId); if (bucket != null) { if (ShouldProcess($"Remove bucket '{bucket.Name}'")) { - PlannerUtility.RemoveBucketAsync(HttpClient, AccessToken, bucket.Id).GetAwaiter().GetResult(); + PlannerUtility.RemoveBucketAsync(Connection, AccessToken, bucket.Id).GetAwaiter().GetResult(); } } else @@ -60,12 +60,12 @@ protected override void ExecuteCmdlet() } else if (ParameterSetName == ParameterName_BYBUCKETID) { - var bucket = Identity.GetBucket(HttpClient, AccessToken, BucketId); + var bucket = Identity.GetBucket(Connection, AccessToken, BucketId); if (bucket != null) { if (ShouldProcess($"Remove bucket '{bucket.Name}'")) { - PlannerUtility.RemoveBucketAsync(HttpClient, AccessToken, BucketId).GetAwaiter().GetResult(); + PlannerUtility.RemoveBucketAsync(Connection, AccessToken, BucketId).GetAwaiter().GetResult(); } } else diff --git a/src/Commands/Planner/RemovePlannerPlan.cs b/src/Commands/Planner/RemovePlannerPlan.cs index f2fcd5092..b12e1fc62 100644 --- a/src/Commands/Planner/RemovePlannerPlan.cs +++ b/src/Commands/Planner/RemovePlannerPlan.cs @@ -19,15 +19,15 @@ public class RemovePlannerPlan : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Group.GetGroupId(HttpClient, AccessToken); + var groupId = Group.GetGroupId(Connection, AccessToken); if (groupId != null) { - var planId = Identity.GetIdAsync(HttpClient, AccessToken, groupId).GetAwaiter().GetResult(); + var planId = Identity.GetIdAsync(Connection, AccessToken, groupId).GetAwaiter().GetResult(); if (!string.IsNullOrEmpty(planId)) { if (ShouldProcess($"Delete plan with id {planId}")) { - PlannerUtility.DeletePlanAsync(HttpClient, AccessToken, planId).GetAwaiter().GetResult(); + PlannerUtility.DeletePlanAsync(Connection, AccessToken, planId).GetAwaiter().GetResult(); } } else diff --git a/src/Commands/Planner/RemovePlannerRoster.cs b/src/Commands/Planner/RemovePlannerRoster.cs index b4a1cd461..45cf1d816 100644 --- a/src/Commands/Planner/RemovePlannerRoster.cs +++ b/src/Commands/Planner/RemovePlannerRoster.cs @@ -15,14 +15,14 @@ public class RemovePlannerRoster : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var roster = Identity.GetPlannerRosterAsync(HttpClient, AccessToken).GetAwaiter().GetResult(); + var roster = Identity.GetPlannerRosterAsync(Connection, AccessToken).GetAwaiter().GetResult(); if(roster == null) { throw new PSArgumentException("Provided Planner Roster could not be found", nameof(Identity)); } - PlannerUtility.DeleteRosterAsync(HttpClient, AccessToken, roster.Id).GetAwaiter().GetResult(); + PlannerUtility.DeleteRosterAsync(Connection, AccessToken, roster.Id).GetAwaiter().GetResult(); } } } \ No newline at end of file diff --git a/src/Commands/Planner/RemovePlannerRosterMember.cs b/src/Commands/Planner/RemovePlannerRosterMember.cs index 7ac23d493..35c5fa6e6 100644 --- a/src/Commands/Planner/RemovePlannerRosterMember.cs +++ b/src/Commands/Planner/RemovePlannerRosterMember.cs @@ -18,14 +18,14 @@ public class RemovePlannerRosterMember : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var roster = Identity.GetPlannerRosterAsync(HttpClient, AccessToken).GetAwaiter().GetResult(); + var roster = Identity.GetPlannerRosterAsync(Connection, AccessToken).GetAwaiter().GetResult(); if(roster == null) { throw new PSArgumentException("Provided Planner Roster could not be found", nameof(Identity)); } - PlannerUtility.RemoveRosterMemberAsync(HttpClient, AccessToken, roster.Id, User).GetAwaiter().GetResult(); + PlannerUtility.RemoveRosterMemberAsync(Connection, AccessToken, roster.Id, User).GetAwaiter().GetResult(); } } } \ No newline at end of file diff --git a/src/Commands/Planner/RemovePlannerTask.cs b/src/Commands/Planner/RemovePlannerTask.cs index 10a696953..c3216161b 100644 --- a/src/Commands/Planner/RemovePlannerTask.cs +++ b/src/Commands/Planner/RemovePlannerTask.cs @@ -15,7 +15,7 @@ public class RemovePlannerTask : PnPGraphCmdlet protected override void ExecuteCmdlet() { - PlannerUtility.DeleteTaskAsync(HttpClient, AccessToken, Task.Id).GetAwaiter().GetResult(); + PlannerUtility.DeleteTaskAsync(Connection, AccessToken, Task.Id).GetAwaiter().GetResult(); } } } \ No newline at end of file diff --git a/src/Commands/Planner/SetPlannerBucket.cs b/src/Commands/Planner/SetPlannerBucket.cs index ec2c1f7a9..7c6a35a9a 100644 --- a/src/Commands/Planner/SetPlannerBucket.cs +++ b/src/Commands/Planner/SetPlannerBucket.cs @@ -33,17 +33,17 @@ protected override void ExecuteCmdlet() { if (ParameterSetName == ParameterName_BYGROUP) { - var groupId = Group.GetGroupId(HttpClient, AccessToken); + var groupId = Group.GetGroupId(Connection, AccessToken); if (groupId != null) { - var planId = Plan.GetIdAsync(HttpClient, AccessToken, groupId).GetAwaiter().GetResult(); + var planId = Plan.GetIdAsync(Connection, AccessToken, groupId).GetAwaiter().GetResult(); if (planId != null) { - var bucket = Bucket.GetBucket(HttpClient, AccessToken, planId); + var bucket = Bucket.GetBucket(Connection, AccessToken, planId); if (bucket != null) { - WriteObject(PlannerUtility.UpdateBucketAsync(HttpClient, AccessToken, Name, bucket.Id).GetAwaiter().GetResult()); + WriteObject(PlannerUtility.UpdateBucketAsync(Connection, AccessToken, Name, bucket.Id).GetAwaiter().GetResult()); } else { @@ -62,10 +62,10 @@ protected override void ExecuteCmdlet() } else { - var bucket = Bucket.GetBucket(HttpClient, AccessToken, PlanId); + var bucket = Bucket.GetBucket(Connection, AccessToken, PlanId); if (bucket != null) { - WriteObject(PlannerUtility.UpdateBucketAsync(HttpClient, AccessToken, Name, bucket.Id).GetAwaiter().GetResult()); + WriteObject(PlannerUtility.UpdateBucketAsync(Connection, AccessToken, Name, bucket.Id).GetAwaiter().GetResult()); } else { diff --git a/src/Commands/Planner/SetPlannerConfiguration.cs b/src/Commands/Planner/SetPlannerConfiguration.cs index cff2e758c..9c36e2b56 100644 --- a/src/Commands/Planner/SetPlannerConfiguration.cs +++ b/src/Commands/Planner/SetPlannerConfiguration.cs @@ -29,7 +29,7 @@ public class SetPlannerConfiguration : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var result = PlannerUtility.SetPlannerConfigAsync(HttpClient, AccessToken, IsPlannerAllowed, AllowCalendarSharing, AllowTenantMoveWithDataLoss, AllowTenantMoveWithDataMigration, AllowRosterCreation, AllowPlannerMobilePushNotifications).GetAwaiter().GetResult(); + var result = PlannerUtility.SetPlannerConfigAsync(Connection, AccessToken, IsPlannerAllowed, AllowCalendarSharing, AllowTenantMoveWithDataLoss, AllowTenantMoveWithDataMigration, AllowRosterCreation, AllowPlannerMobilePushNotifications).GetAwaiter().GetResult(); WriteObject(result); } } diff --git a/src/Commands/Planner/SetPlannerPlan.cs b/src/Commands/Planner/SetPlannerPlan.cs index fc05405c8..bf8c69efc 100644 --- a/src/Commands/Planner/SetPlannerPlan.cs +++ b/src/Commands/Planner/SetPlannerPlan.cs @@ -29,13 +29,13 @@ protected override void ExecuteCmdlet() { if (ParameterSetName == ParameterName_BYGROUP) { - var groupId = Group.GetGroupId(HttpClient, AccessToken); + var groupId = Group.GetGroupId(Connection, AccessToken); if (groupId != null) { - var plan = Plan.GetPlanAsync(HttpClient, AccessToken, groupId, false).GetAwaiter().GetResult(); + var plan = Plan.GetPlanAsync(Connection, AccessToken, groupId, false).GetAwaiter().GetResult(); if (plan != null) { - WriteObject(PlannerUtility.UpdatePlanAsync(HttpClient, AccessToken, plan, Title).GetAwaiter().GetResult()); + WriteObject(PlannerUtility.UpdatePlanAsync(Connection, AccessToken, plan, Title).GetAwaiter().GetResult()); } else { @@ -49,10 +49,10 @@ protected override void ExecuteCmdlet() } else { - var plan = PlannerUtility.GetPlanAsync(HttpClient, AccessToken, PlanId, false).GetAwaiter().GetResult(); + var plan = PlannerUtility.GetPlanAsync(Connection, AccessToken, PlanId, false).GetAwaiter().GetResult(); if (plan != null) { - WriteObject(PlannerUtility.UpdatePlanAsync(HttpClient, AccessToken, plan, Title).GetAwaiter().GetResult()); + WriteObject(PlannerUtility.UpdatePlanAsync(Connection, AccessToken, plan, Title).GetAwaiter().GetResult()); } else { diff --git a/src/Commands/Planner/SetPlannerTask.cs b/src/Commands/Planner/SetPlannerTask.cs index ceb534c6a..e83fa65d2 100644 --- a/src/Commands/Planner/SetPlannerTask.cs +++ b/src/Commands/Planner/SetPlannerTask.cs @@ -43,7 +43,7 @@ public class SetPlannerTask : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var existingTask = PlannerUtility.GetTaskAsync(HttpClient, AccessToken, TaskId, false, false).GetAwaiter().GetResult(); + var existingTask = PlannerUtility.GetTaskAsync(Connection, AccessToken, TaskId, false, false).GetAwaiter().GetResult(); if (existingTask != null) { var plannerTask = new PlannerTask(); @@ -53,7 +53,7 @@ protected override void ExecuteCmdlet() } if (ParameterSpecified(nameof(Bucket))) { - var bucket = Bucket.GetBucket(HttpClient, AccessToken, existingTask.PlanId); + var bucket = Bucket.GetBucket(Connection, AccessToken, existingTask.PlanId); if (bucket != null) { plannerTask.BucketId = bucket.Id; @@ -90,7 +90,7 @@ protected override void ExecuteCmdlet() var chunks = BatchUtility.Chunk(AssignedTo, 20); foreach (var chunk in chunks) { - var userIds = BatchUtility.GetPropertyBatchedAsync(HttpClient, AccessToken, chunk.ToArray(), "/users/{0}", "id").GetAwaiter().GetResult(); + var userIds = BatchUtility.GetPropertyBatchedAsync(Connection, AccessToken, chunk.ToArray(), "/users/{0}", "id").GetAwaiter().GetResult(); foreach (var userId in userIds) { plannerTask.Assignments.Add(userId.Value, new TaskAssignment()); @@ -106,12 +106,12 @@ protected override void ExecuteCmdlet() } - PlannerUtility.UpdateTaskAsync(HttpClient, AccessToken, existingTask, plannerTask).GetAwaiter().GetResult(); + PlannerUtility.UpdateTaskAsync(Connection, AccessToken, existingTask, plannerTask).GetAwaiter().GetResult(); if (ParameterSpecified(nameof(Description))) { - var existingTaskDetails = PlannerUtility.GetTaskDetailsAsync(HttpClient, AccessToken, TaskId, false).GetAwaiter().GetResult(); - PlannerUtility.UpdateTaskDetailsAsync(HttpClient, AccessToken, existingTaskDetails, Description).GetAwaiter().GetResult(); + var existingTaskDetails = PlannerUtility.GetTaskDetailsAsync(Connection, AccessToken, TaskId, false).GetAwaiter().GetResult(); + PlannerUtility.UpdateTaskDetailsAsync(Connection, AccessToken, existingTaskDetails, Description).GetAwaiter().GetResult(); } } else diff --git a/src/Commands/Planner/SetPlannerUserPolicy.cs b/src/Commands/Planner/SetPlannerUserPolicy.cs index 02a8e9c95..fb2062bab 100644 --- a/src/Commands/Planner/SetPlannerUserPolicy.cs +++ b/src/Commands/Planner/SetPlannerUserPolicy.cs @@ -17,7 +17,7 @@ public class SetPlannerUserPolicy : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var result = PlannerUtility.SetPlannerUserPolicyAsync(HttpClient, AccessToken, Identity, BlockDeleteTasksNotCreatedBySelf).GetAwaiter().GetResult(); + var result = PlannerUtility.SetPlannerUserPolicyAsync(Connection, AccessToken, Identity, BlockDeleteTasksNotCreatedBySelf).GetAwaiter().GetResult(); WriteObject(result); } } diff --git a/src/Commands/PowerPlatform/Environment/GetPowerPlatformEnvironment.cs b/src/Commands/PowerPlatform/Environment/GetPowerPlatformEnvironment.cs index 644e56214..11260024b 100644 --- a/src/Commands/PowerPlatform/Environment/GetPowerPlatformEnvironment.cs +++ b/src/Commands/PowerPlatform/Environment/GetPowerPlatformEnvironment.cs @@ -17,7 +17,7 @@ public class GetPowerPlatformEnvironment : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var environments = GraphHelper.GetResultCollectionAsync(HttpClient, "https://management.azure.com/providers/Microsoft.ProcessSimple/environments?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); + var environments = GraphHelper.GetResultCollectionAsync(Connection, "https://management.azure.com/providers/Microsoft.ProcessSimple/environments?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); if(ParameterSpecified(nameof(IsDefault)) && IsDefault.HasValue) { diff --git a/src/Commands/PowerPlatform/PowerAutomate/DisableFlow.cs b/src/Commands/PowerPlatform/PowerAutomate/DisableFlow.cs index 7d9afc15c..b7e9bb471 100644 --- a/src/Commands/PowerPlatform/PowerAutomate/DisableFlow.cs +++ b/src/Commands/PowerPlatform/PowerAutomate/DisableFlow.cs @@ -23,7 +23,7 @@ protected override void ExecuteCmdlet() { var environmentName = Environment.GetName(); var flowName = Identity.GetName(); - RestHelper.PostAsync(HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple{(AsAdmin ? "/scopes/admin" : "")}/environments/{environmentName}/flows/{flowName}/stop?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); + RestHelper.PostAsync(Connection.HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple{(AsAdmin ? "/scopes/admin" : "")}/environments/{environmentName}/flows/{flowName}/stop?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); } } } \ No newline at end of file diff --git a/src/Commands/PowerPlatform/PowerAutomate/EnableFlow.cs b/src/Commands/PowerPlatform/PowerAutomate/EnableFlow.cs index 26c3f0ec0..8e38b9fd5 100644 --- a/src/Commands/PowerPlatform/PowerAutomate/EnableFlow.cs +++ b/src/Commands/PowerPlatform/PowerAutomate/EnableFlow.cs @@ -23,7 +23,7 @@ protected override void ExecuteCmdlet() { var environmentName = Environment.GetName(); var flowName = Identity.GetName(); - RestHelper.PostAsync(HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple{(AsAdmin ? "/scopes/admin" : "")}/environments/{environmentName}/flows/{flowName}/start?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); + RestHelper.PostAsync(Connection.HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple{(AsAdmin ? "/scopes/admin" : "")}/environments/{environmentName}/flows/{flowName}/start?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); } } } \ No newline at end of file diff --git a/src/Commands/PowerPlatform/PowerAutomate/ExportFlow.cs b/src/Commands/PowerPlatform/PowerAutomate/ExportFlow.cs index badc7f46e..0148a3f93 100644 --- a/src/Commands/PowerPlatform/PowerAutomate/ExportFlow.cs +++ b/src/Commands/PowerPlatform/PowerAutomate/ExportFlow.cs @@ -77,7 +77,7 @@ protected override void ExecuteCmdlet() $"/providers/Microsoft.Flow/flows/{flowName}" } }; - var wrapper = RestHelper.PostAsync(HttpClient, $"https://api.bap.microsoft.com/providers/Microsoft.BusinessAppPlatform/environments/{environmentName}/listPackageResources?api-version=2016-11-01", AccessToken, payload: postData).GetAwaiter().GetResult(); + var wrapper = RestHelper.PostAsync(Connection.HttpClient, $"https://api.bap.microsoft.com/providers/Microsoft.BusinessAppPlatform/environments/{environmentName}/listPackageResources?api-version=2016-11-01", AccessToken, payload: postData).GetAwaiter().GetResult(); if (wrapper.Status == Model.PowerPlatform.PowerAutomate.Enums.FlowExportStatus.Succeeded) { @@ -110,7 +110,7 @@ protected override void ExecuteCmdlet() resources = wrapper.Resources }; - var resultElement = RestHelper.PostAsync(HttpClient, $"https://api.bap.microsoft.com/providers/Microsoft.BusinessAppPlatform/environments/{environmentName}/exportPackage?api-version=2016-11-01", AccessToken, payload: exportPostData).GetAwaiter().GetResult(); + var resultElement = RestHelper.PostAsync(Connection.HttpClient, $"https://api.bap.microsoft.com/providers/Microsoft.BusinessAppPlatform/environments/{environmentName}/exportPackage?api-version=2016-11-01", AccessToken, payload: exportPostData).GetAwaiter().GetResult(); if (resultElement.TryGetProperty("status", out JsonElement statusElement)) { if (statusElement.GetString() == "Succeeded") @@ -123,7 +123,7 @@ protected override void ExecuteCmdlet() using (var requestMessage = new HttpRequestMessage(HttpMethod.Get, packageLink)) { //requestMessage.Headers.Add("Authorization", $"Bearer {AccessToken}"); - var response = HttpClient.SendAsync(requestMessage).GetAwaiter().GetResult(); + var response = Connection.HttpClient.SendAsync(requestMessage).GetAwaiter().GetResult(); var byteArray = response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult(); var fileName = string.Empty; if (ParameterSpecified(nameof(OutPath))) @@ -161,7 +161,7 @@ protected override void ExecuteCmdlet() } else { - var json = RestHelper.PostAsync(HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple/environments/{environmentName}/flows/{flowName}/exportToARMTemplate?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); + var json = RestHelper.PostAsync(Connection.HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple/environments/{environmentName}/flows/{flowName}/exportToARMTemplate?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); WriteObject(json); } } diff --git a/src/Commands/PowerPlatform/PowerAutomate/GetFlow.cs b/src/Commands/PowerPlatform/PowerAutomate/GetFlow.cs index 994ba25c9..5bd3bb913 100644 --- a/src/Commands/PowerPlatform/PowerAutomate/GetFlow.cs +++ b/src/Commands/PowerPlatform/PowerAutomate/GetFlow.cs @@ -28,12 +28,12 @@ protected override void ExecuteCmdlet() if (ParameterSpecified(nameof(Identity))) { var flowName = Identity.GetName(); - var result = GraphHelper.GetAsync(HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple{(AsAdmin ? "/scopes/admin" : "")}/environments/{environmentName}/flows/{flowName}?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); + var result = GraphHelper.GetAsync(Connection, $"https://management.azure.com/providers/Microsoft.ProcessSimple{(AsAdmin ? "/scopes/admin" : "")}/environments/{environmentName}/flows/{flowName}?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); WriteObject(result, false); } else { - var flows = GraphHelper.GetResultCollectionAsync(HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple{(AsAdmin ? "/scopes/admin" : "")}/environments/{environmentName}/flows?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); + var flows = GraphHelper.GetResultCollectionAsync(Connection, $"https://management.azure.com/providers/Microsoft.ProcessSimple{(AsAdmin ? "/scopes/admin" : "")}/environments/{environmentName}/flows?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); WriteObject(flows, true); } } diff --git a/src/Commands/PowerPlatform/PowerAutomate/GetFlowRun.cs b/src/Commands/PowerPlatform/PowerAutomate/GetFlowRun.cs index af8d4a20f..cd4bd7c3f 100644 --- a/src/Commands/PowerPlatform/PowerAutomate/GetFlowRun.cs +++ b/src/Commands/PowerPlatform/PowerAutomate/GetFlowRun.cs @@ -37,12 +37,12 @@ protected override void ExecuteCmdlet() if (ParameterSpecified(nameof(Identity))) { var flowRunName = Identity.GetName(); - var flowRun = GraphHelper.GetAsync(HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple/environments/{environmentName}/flows/{flowName}/runs/{flowRunName}?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); + var flowRun = GraphHelper.GetAsync(Connection, $"https://management.azure.com/providers/Microsoft.ProcessSimple/environments/{environmentName}/flows/{flowName}/runs/{flowRunName}?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); WriteObject(flowRun, false); } else { - var flowRuns = GraphHelper.GetResultCollectionAsync(HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple/environments/{environmentName}/flows/{flowName}/runs?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); + var flowRuns = GraphHelper.GetResultCollectionAsync(Connection, $"https://management.azure.com/providers/Microsoft.ProcessSimple/environments/{environmentName}/flows/{flowName}/runs?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); WriteObject(flowRuns, true); } } diff --git a/src/Commands/PowerPlatform/PowerAutomate/RemoveFlow.cs b/src/Commands/PowerPlatform/PowerAutomate/RemoveFlow.cs index 32817bbe3..b61e2938a 100644 --- a/src/Commands/PowerPlatform/PowerAutomate/RemoveFlow.cs +++ b/src/Commands/PowerPlatform/PowerAutomate/RemoveFlow.cs @@ -29,7 +29,7 @@ protected override void ExecuteCmdlet() if (Force || ShouldContinue($"Remove flow with name '{flowName}'?", "Remove flow")) { - var result = RestHelper.DeleteAsync>(HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple{(AsAdmin ? "/scopes/admin" : "")}/environments/{environmentName}/flows/{flowName}?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); + var result = RestHelper.DeleteAsync>(Connection.HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple{(AsAdmin ? "/scopes/admin" : "")}/environments/{environmentName}/flows/{flowName}?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); } } } diff --git a/src/Commands/PowerPlatform/PowerAutomate/RestartFlowRun.cs b/src/Commands/PowerPlatform/PowerAutomate/RestartFlowRun.cs index 13f61be61..3bf947246 100644 --- a/src/Commands/PowerPlatform/PowerAutomate/RestartFlowRun.cs +++ b/src/Commands/PowerPlatform/PowerAutomate/RestartFlowRun.cs @@ -48,8 +48,8 @@ protected override void ExecuteCmdlet() if (!Force && !ShouldContinue($"Restart flow run with name '{flowRunName}'?", Resources.Confirm)) return; - var triggers = GraphHelper.GetResultCollectionAsync(HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple/environments/{environmentName}/flows/{flowName}/triggers?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); - RestHelper.PostAsync(HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple/environments/{environmentName}/flows/{flowName}/triggers/{triggers.First().Name}/histories/{flowRunName}/resubmit?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); + var triggers = GraphHelper.GetResultCollectionAsync(Connection, $"https://management.azure.com/providers/Microsoft.ProcessSimple/environments/{environmentName}/flows/{flowName}/triggers?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); + RestHelper.PostAsync(Connection.HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple/environments/{environmentName}/flows/{flowName}/triggers/{triggers.First().Name}/histories/{flowRunName}/resubmit?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); } } } diff --git a/src/Commands/PowerPlatform/PowerAutomate/StopFlowRun.cs b/src/Commands/PowerPlatform/PowerAutomate/StopFlowRun.cs index 29c7411fa..6f5b063e7 100644 --- a/src/Commands/PowerPlatform/PowerAutomate/StopFlowRun.cs +++ b/src/Commands/PowerPlatform/PowerAutomate/StopFlowRun.cs @@ -45,7 +45,7 @@ protected override void ExecuteCmdlet() if (Force || ShouldContinue($"Stop flow run with name '{flowRunName}'?", Resources.Confirm)) { - RestHelper.PostAsync(HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple/environments/{environmentName}/flows/{flowName}/runs/{flowRunName}/cancel?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); + RestHelper.PostAsync(Connection.HttpClient, $"https://management.azure.com/providers/Microsoft.ProcessSimple/environments/{environmentName}/flows/{flowName}/runs/{flowRunName}/cancel?api-version=2016-11-01", AccessToken).GetAwaiter().GetResult(); } } } diff --git a/src/Commands/Properties/Resources.Designer.cs b/src/Commands/Properties/Resources.Designer.cs index 86fe930c3..887a83291 100644 --- a/src/Commands/Properties/Resources.Designer.cs +++ b/src/Commands/Properties/Resources.Designer.cs @@ -384,16 +384,22 @@ internal static string NoContextPresent { return ResourceManager.GetString("NoContextPresent", resourceCulture); } } - + /// /// Looks up a localized string similar to The current connection holds no SharePoint context. Please use one of the Connect-PnPOnline commands which uses the -Url argument to connect.. /// - internal static string NoSharePointConnection { + internal static string NoDefaultSharePointConnection { get { - return ResourceManager.GetString("NoSharePointConnection", resourceCulture); + return ResourceManager.GetString("NoDefaultSharePointConnection", resourceCulture); } } - + + internal static string NoSharePointConnectionInProvidedConnection { + get { + return ResourceManager.GetString("NoSharePointConnectionInProvidedConnection", resourceCulture); + } + } + /// /// Looks up a localized string similar to No Tenant Administration Url specified. Connect with Connect-PnPOnline and specify the TenantAdminUrl parameter.. /// diff --git a/src/Commands/Properties/Resources.resx b/src/Commands/Properties/Resources.resx index 78b0fcda7..eb4098761 100644 --- a/src/Commands/Properties/Resources.resx +++ b/src/Commands/Properties/Resources.resx @@ -150,9 +150,12 @@ No list found with id, title or url '{0}' - + The current connection holds no SharePoint context. Please use one of the Connect-PnPOnline commands which uses the -Url argument to connect. + + The provided connection through -Connection holds no SharePoint context. Please use one of the Connect-PnPOnline commands which uses the -Url argument to connect. + No connection to disconnect @@ -322,7 +325,7 @@ Unable to retrieve a token for {0}. Ensure you connect using one of the Connect-PnPOnline commands which uses the -ClientId argument or use Connect-PnPOnline -Scopes to connect. - There is currently no connection yet. Use Connect-PnPOnline to connect. + There is currently no connection yet. Use Connect-PnPOnline to connect or provide a valid connection using -Connection. No URL specified nor does the provided access token contain an audience diff --git a/src/Commands/Provider/SPOProvider.cs b/src/Commands/Provider/SPOProvider.cs index fec5ca70e..b162ce267 100644 --- a/src/Commands/Provider/SPOProvider.cs +++ b/src/Commands/Provider/SPOProvider.cs @@ -4,11 +4,9 @@ using System.Linq; using System.Management.Automation; using System.Management.Automation.Provider; -using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using Microsoft.SharePoint.Client; using PnP.PowerShell.Commands.Base; -using PnP.PowerShell.Commands.Extensions; using PnP.PowerShell.Commands.Provider.Parameters; using PnP.PowerShell.Commands.Provider.SPOProxy; using File = Microsoft.SharePoint.Client.File; @@ -61,7 +59,7 @@ protected override PSDriveInfo NewDrive(PSDriveInfo drive) } else { - WriteErrorInternal(PnPResources.NoSharePointConnection, drive.Root, ErrorCategory.ConnectionError); + WriteErrorInternal(PnPResources.NoDefaultSharePointConnection, drive.Root, ErrorCategory.ConnectionError); } if (web != null) diff --git a/src/Commands/ServiceHealth/GetMessageCenterAnnouncement.cs b/src/Commands/ServiceHealth/GetMessageCenterAnnouncement.cs index 93eec52b7..ae541ce7c 100644 --- a/src/Commands/ServiceHealth/GetMessageCenterAnnouncement.cs +++ b/src/Commands/ServiceHealth/GetMessageCenterAnnouncement.cs @@ -16,11 +16,11 @@ protected override void ExecuteCmdlet() { if (ParameterSpecified(nameof(Identity))) { - WriteObject(ServiceHealthUtility.GetServiceUpdateMessageByIdAsync(Identity, HttpClient, AccessToken).GetAwaiter().GetResult(), false); + WriteObject(ServiceHealthUtility.GetServiceUpdateMessageByIdAsync(Identity, Connection, AccessToken).GetAwaiter().GetResult(), false); } else { - WriteObject(ServiceHealthUtility.GetServiceUpdateMessagesAsync(HttpClient, AccessToken).GetAwaiter().GetResult(), true); + WriteObject(ServiceHealthUtility.GetServiceUpdateMessagesAsync(Connection, AccessToken).GetAwaiter().GetResult(), true); } } } diff --git a/src/Commands/ServiceHealth/GetServiceCurrentHealth.cs b/src/Commands/ServiceHealth/GetServiceCurrentHealth.cs index 93e1cd9eb..360bbbf4e 100644 --- a/src/Commands/ServiceHealth/GetServiceCurrentHealth.cs +++ b/src/Commands/ServiceHealth/GetServiceCurrentHealth.cs @@ -16,11 +16,11 @@ protected override void ExecuteCmdlet() { if (ParameterSpecified(nameof(Identity))) { - WriteObject(ServiceHealthUtility.GetServiceCurrentHealthByIdAsync(Identity, HttpClient, AccessToken).GetAwaiter().GetResult(), false); + WriteObject(ServiceHealthUtility.GetServiceCurrentHealthByIdAsync(Identity, Connection, AccessToken).GetAwaiter().GetResult(), false); } else { - WriteObject(ServiceHealthUtility.GetServiceCurrentHealthAsync(HttpClient, AccessToken).GetAwaiter().GetResult(), true); + WriteObject(ServiceHealthUtility.GetServiceCurrentHealthAsync(Connection, AccessToken).GetAwaiter().GetResult(), true); } } } diff --git a/src/Commands/ServiceHealth/GetServiceHealthIssue.cs b/src/Commands/ServiceHealth/GetServiceHealthIssue.cs index 413295cc4..d1042b6ca 100644 --- a/src/Commands/ServiceHealth/GetServiceHealthIssue.cs +++ b/src/Commands/ServiceHealth/GetServiceHealthIssue.cs @@ -16,11 +16,11 @@ protected override void ExecuteCmdlet() { if (ParameterSpecified(nameof(Identity))) { - WriteObject(ServiceHealthUtility.GetServiceHealthIssueByIdAsync(Identity, HttpClient, AccessToken).GetAwaiter().GetResult(), false); + WriteObject(ServiceHealthUtility.GetServiceHealthIssueByIdAsync(Identity, Connection, AccessToken).GetAwaiter().GetResult(), false); } else { - WriteObject(ServiceHealthUtility.GetServiceHealthIssuesAsync(HttpClient, AccessToken).GetAwaiter().GetResult(), true); + WriteObject(ServiceHealthUtility.GetServiceHealthIssuesAsync(Connection, AccessToken).GetAwaiter().GetResult(), true); } } } diff --git a/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsArchived.cs b/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsArchived.cs index 3fcdbeb93..1faa54163 100644 --- a/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsArchived.cs +++ b/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsArchived.cs @@ -17,18 +17,18 @@ protected override void ExecuteCmdlet() { if (ParameterSpecified(nameof(Identity))) { - WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsArchivedByIdAsync(Identity, HttpClient, AccessToken).GetAwaiter().GetResult(), true); + WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsArchivedByIdAsync(Identity, Connection, AccessToken).GetAwaiter().GetResult(), true); } else { // Retrieve all message center announcements - var messageCenterAnnouncements = ServiceHealthUtility.GetServiceUpdateMessagesAsync(HttpClient, AccessToken).GetAwaiter().GetResult(); + var messageCenterAnnouncements = ServiceHealthUtility.GetServiceUpdateMessagesAsync(Connection, AccessToken).GetAwaiter().GetResult(); // Create an array of the Ids of all message center announcements var messageCenterAnnouncementIds = messageCenterAnnouncements.Select(item => item.Id).ToArray(); // Mark all message center announcements as archived - WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsArchivedByIdAsync(messageCenterAnnouncementIds, HttpClient, AccessToken).GetAwaiter().GetResult(), true); + WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsArchivedByIdAsync(messageCenterAnnouncementIds, Connection, AccessToken).GetAwaiter().GetResult(), true); } } } diff --git a/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsFavorite.cs b/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsFavorite.cs index 4a13e0344..54e71d32e 100644 --- a/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsFavorite.cs +++ b/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsFavorite.cs @@ -17,18 +17,18 @@ protected override void ExecuteCmdlet() { if (ParameterSpecified(nameof(Identity))) { - WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsFavoriteByIdAsync(Identity, HttpClient, AccessToken).GetAwaiter().GetResult(), true); + WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsFavoriteByIdAsync(Identity, Connection, AccessToken).GetAwaiter().GetResult(), true); } else { // Retrieve all message center announcements - var messageCenterAnnouncements = ServiceHealthUtility.GetServiceUpdateMessagesAsync(HttpClient, AccessToken).GetAwaiter().GetResult(); + var messageCenterAnnouncements = ServiceHealthUtility.GetServiceUpdateMessagesAsync(Connection, AccessToken).GetAwaiter().GetResult(); // Create an array of the Ids of all message center announcements var messageCenterAnnouncementIds = messageCenterAnnouncements.Select(item => item.Id).ToArray(); // Mark all message center announcements as favorite - WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsFavoriteByIdAsync(messageCenterAnnouncementIds, HttpClient, AccessToken).GetAwaiter().GetResult(), true); + WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsFavoriteByIdAsync(messageCenterAnnouncementIds, Connection, AccessToken).GetAwaiter().GetResult(), true); } } } diff --git a/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsNotArchived.cs b/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsNotArchived.cs index 3640798ca..74c21bd15 100644 --- a/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsNotArchived.cs +++ b/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsNotArchived.cs @@ -17,18 +17,18 @@ protected override void ExecuteCmdlet() { if (ParameterSpecified(nameof(Identity))) { - WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsUnarchivedByIdAsync(Identity, HttpClient, AccessToken).GetAwaiter().GetResult(), true); + WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsUnarchivedByIdAsync(Identity, Connection, AccessToken).GetAwaiter().GetResult(), true); } else { // Retrieve all message center announcements - var messageCenterAnnouncements = ServiceHealthUtility.GetServiceUpdateMessagesAsync(HttpClient, AccessToken).GetAwaiter().GetResult(); + var messageCenterAnnouncements = ServiceHealthUtility.GetServiceUpdateMessagesAsync(Connection, AccessToken).GetAwaiter().GetResult(); // Create an array of the Ids of all message center announcements var messageCenterAnnouncementIds = messageCenterAnnouncements.Select(item => item.Id).ToArray(); // Mark all message center announcements as not archived - WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsUnarchivedByIdAsync(messageCenterAnnouncementIds, HttpClient, AccessToken).GetAwaiter().GetResult(), true); + WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsUnarchivedByIdAsync(messageCenterAnnouncementIds, Connection, AccessToken).GetAwaiter().GetResult(), true); } } } diff --git a/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsNotFavorite.cs b/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsNotFavorite.cs index 5105f6b4b..144f6440e 100644 --- a/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsNotFavorite.cs +++ b/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsNotFavorite.cs @@ -16,18 +16,18 @@ protected override void ExecuteCmdlet() { if (ParameterSpecified(nameof(Identity))) { - WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsNotfavoriteByIdAsync(Identity, HttpClient, AccessToken).GetAwaiter().GetResult(), true); + WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsNotfavoriteByIdAsync(Identity, Connection, AccessToken).GetAwaiter().GetResult(), true); } else { // Retrieve all message center announcements - var messageCenterAnnouncements = ServiceHealthUtility.GetServiceUpdateMessagesAsync(HttpClient, AccessToken).GetAwaiter().GetResult(); + var messageCenterAnnouncements = ServiceHealthUtility.GetServiceUpdateMessagesAsync(Connection, AccessToken).GetAwaiter().GetResult(); // Create an array of the Ids of all message center announcements var messageCenterAnnouncementIds = messageCenterAnnouncements.Select(item => item.Id).ToArray(); // Mark all message center announcements as not favorites - WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsNotfavoriteByIdAsync(messageCenterAnnouncementIds, HttpClient, AccessToken).GetAwaiter().GetResult(), true); + WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsNotfavoriteByIdAsync(messageCenterAnnouncementIds, Connection, AccessToken).GetAwaiter().GetResult(), true); } } } diff --git a/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsRead.cs b/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsRead.cs index 860cf70b8..1831ffebf 100644 --- a/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsRead.cs +++ b/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsRead.cs @@ -17,18 +17,18 @@ protected override void ExecuteCmdlet() { if (ParameterSpecified(nameof(Identity))) { - WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsReadByIdAsync(Identity, HttpClient, AccessToken).GetAwaiter().GetResult(), true); + WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsReadByIdAsync(Identity, Connection, AccessToken).GetAwaiter().GetResult(), true); } else { // Retrieve all message center announcements - var messageCenterAnnouncements = ServiceHealthUtility.GetServiceUpdateMessagesAsync(HttpClient, AccessToken).GetAwaiter().GetResult(); + var messageCenterAnnouncements = ServiceHealthUtility.GetServiceUpdateMessagesAsync(Connection, AccessToken).GetAwaiter().GetResult(); // Create an array of the Ids of all message center announcements var messageCenterAnnouncementIds = messageCenterAnnouncements.Select(item => item.Id).ToArray(); // Mark all message center announcements as read - WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsReadByIdAsync(messageCenterAnnouncementIds, HttpClient, AccessToken).GetAwaiter().GetResult(), true); + WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsReadByIdAsync(messageCenterAnnouncementIds, Connection, AccessToken).GetAwaiter().GetResult(), true); } } } diff --git a/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsUnread.cs b/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsUnread.cs index bd0648d13..b412b0d19 100644 --- a/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsUnread.cs +++ b/src/Commands/ServiceHealth/SetMessageCenterAnnouncementAsUnread.cs @@ -17,18 +17,18 @@ protected override void ExecuteCmdlet() { if (ParameterSpecified(nameof(Identity))) { - WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsUnreadByIdAsync(Identity, HttpClient, AccessToken).GetAwaiter().GetResult(), true); + WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsUnreadByIdAsync(Identity, Connection, AccessToken).GetAwaiter().GetResult(), true); } else { // Retrieve all message center announcements - var messageCenterAnnouncements = ServiceHealthUtility.GetServiceUpdateMessagesAsync(HttpClient, AccessToken).GetAwaiter().GetResult(); + var messageCenterAnnouncements = ServiceHealthUtility.GetServiceUpdateMessagesAsync(Connection, AccessToken).GetAwaiter().GetResult(); // Create an array of the Ids of all message center announcements var messageCenterAnnouncementIds = messageCenterAnnouncements.Select(item => item.Id).ToArray(); // Mark all message center announcements as unread - WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsUnreadByIdAsync(messageCenterAnnouncementIds, HttpClient, AccessToken).GetAwaiter().GetResult(), true); + WriteObject(ServiceHealthUtility.SetServiceUpdateMessageAsUnreadByIdAsync(messageCenterAnnouncementIds, Connection, AccessToken).GetAwaiter().GetResult(), true); } } } diff --git a/src/Commands/Site/AddTeamsTeam.cs b/src/Commands/Site/AddTeamsTeam.cs index 5e48b0901..e49d5acf4 100644 --- a/src/Commands/Site/AddTeamsTeam.cs +++ b/src/Commands/Site/AddTeamsTeam.cs @@ -26,7 +26,7 @@ protected override void ExecuteCmdlet() try { var groupId = ClientContext.Site.EnsureProperty(s => s.GroupId); - Microsoft365GroupsUtility.CreateTeamAsync(HttpClient, AccessToken, groupId).GetAwaiter().GetResult(); + Microsoft365GroupsUtility.CreateTeamAsync(Connection, AccessToken, groupId).GetAwaiter().GetResult(); } catch { diff --git a/src/Commands/SiteDesigns/InvokeSiteScript.cs b/src/Commands/SiteDesigns/InvokeSiteScript.cs index 366f3a8c8..b1064183b 100644 --- a/src/Commands/SiteDesigns/InvokeSiteScript.cs +++ b/src/Commands/SiteDesigns/InvokeSiteScript.cs @@ -54,7 +54,7 @@ protected override void ExecuteCmdlet() else { WriteVerbose($"Executing provided script"); - result = PnP.PowerShell.Commands.Utilities.SiteTemplates.InvokeSiteScript(HttpClient, AccessToken, Script, hostUrl).GetAwaiter().GetResult().Items; + result = PnP.PowerShell.Commands.Utilities.SiteTemplates.InvokeSiteScript(Connection, AccessToken, Script, hostUrl).GetAwaiter().GetResult().Items; } break; @@ -80,7 +80,7 @@ protected override void ExecuteCmdlet() else { WriteVerbose($"Executing site script '{script.Title}' ({script.Id})"); - result = PnP.PowerShell.Commands.Utilities.SiteTemplates.InvokeSiteScript(HttpClient, AccessToken, script, hostUrl).GetAwaiter().GetResult().Items; + result = PnP.PowerShell.Commands.Utilities.SiteTemplates.InvokeSiteScript(Connection, AccessToken, script, hostUrl).GetAwaiter().GetResult().Items; } } break; diff --git a/src/Commands/Teams/AddTeamsChannel.cs b/src/Commands/Teams/AddTeamsChannel.cs index fca56ecd8..d599fb097 100644 --- a/src/Commands/Teams/AddTeamsChannel.cs +++ b/src/Commands/Teams/AddTeamsChannel.cs @@ -2,7 +2,6 @@ using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Base.PipeBinds; using PnP.PowerShell.Commands.Model.Graph; -using PnP.PowerShell.Commands.Model.Teams; using PnP.PowerShell.Commands.Utilities; using System.Management.Automation; @@ -38,12 +37,12 @@ public class AddTeamsChannel : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (groupId != null) { try { - var channel = TeamsUtility.AddChannelAsync(AccessToken, HttpClient, groupId, DisplayName, Description, Private, OwnerUPN, IsFavoriteByDefault).GetAwaiter().GetResult(); + var channel = TeamsUtility.AddChannelAsync(AccessToken, Connection, groupId, DisplayName, Description, Private, OwnerUPN, IsFavoriteByDefault).GetAwaiter().GetResult(); WriteObject(channel); } catch (GraphException ex) diff --git a/src/Commands/Teams/AddTeamsChannelUser.cs b/src/Commands/Teams/AddTeamsChannelUser.cs index 3cb2f2457..50ccfb96b 100644 --- a/src/Commands/Teams/AddTeamsChannelUser.cs +++ b/src/Commands/Teams/AddTeamsChannelUser.cs @@ -26,13 +26,13 @@ public class AddTeamsChannelUser : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (groupId == null) { throw new PSArgumentException("Group not found"); } - var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + var channelId = Channel.GetId(Connection, AccessToken, groupId); if (channelId == null) { throw new PSArgumentException("Channel not found"); @@ -40,7 +40,7 @@ protected override void ExecuteCmdlet() try { - TeamsUtility.AddChannelMemberAsync(HttpClient, AccessToken, groupId, channelId, User, Role).GetAwaiter().GetResult(); + TeamsUtility.AddChannelMemberAsync(Connection, AccessToken, groupId, channelId, User, Role).GetAwaiter().GetResult(); } catch (GraphException ex) { diff --git a/src/Commands/Teams/AddTeamsTab.cs b/src/Commands/Teams/AddTeamsTab.cs index 2d95ba182..cf2f30e10 100644 --- a/src/Commands/Teams/AddTeamsTab.cs +++ b/src/Commands/Teams/AddTeamsTab.cs @@ -60,10 +60,10 @@ public object GetDynamicParameters() protected override void ExecuteCmdlet() { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (groupId != null) { - var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + var channelId = Channel.GetId(Connection, AccessToken, groupId); if (channelId != null) { try @@ -100,7 +100,7 @@ protected override void ExecuteCmdlet() break; } } - WriteObject(TeamsUtility.AddTabAsync(HttpClient, AccessToken, groupId, channelId, DisplayName, Type, teamsAppId, entityId, contentUrl, removeUrl, webSiteUrl).GetAwaiter().GetResult()); + WriteObject(TeamsUtility.AddTabAsync(Connection, AccessToken, groupId, channelId, DisplayName, Type, teamsAppId, entityId, contentUrl, removeUrl, webSiteUrl).GetAwaiter().GetResult()); } catch (GraphException ex) { diff --git a/src/Commands/Teams/AddTeamsUser.cs b/src/Commands/Teams/AddTeamsUser.cs index 190c26bb1..f74310f0b 100644 --- a/src/Commands/Teams/AddTeamsUser.cs +++ b/src/Commands/Teams/AddTeamsUser.cs @@ -33,29 +33,29 @@ public class AddTeamsUser : PnPGraphCmdlet public string Role; protected override void ExecuteCmdlet() { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (groupId != null) { try { if (ParameterSpecified(nameof(Channel))) { - var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + var channelId = Channel.GetId(Connection, AccessToken, groupId); if (channelId == null) { throw new PSArgumentException("Channel not found"); } - TeamsUtility.AddChannelMemberAsync(HttpClient, AccessToken, groupId, channelId, User, Role).GetAwaiter().GetResult(); + TeamsUtility.AddChannelMemberAsync(Connection, AccessToken, groupId, channelId, User, Role).GetAwaiter().GetResult(); } else { if (ParameterSetName == ParamSet_ByUser) { - TeamsUtility.AddUserAsync(HttpClient, AccessToken, groupId, User, Role).GetAwaiter().GetResult(); + TeamsUtility.AddUserAsync(Connection, AccessToken, groupId, User, Role).GetAwaiter().GetResult(); } else { - TeamsUtility.AddUsersAsync(HttpClient, AccessToken, groupId, Users, Role).GetAwaiter().GetResult(); + TeamsUtility.AddUsersAsync(Connection, AccessToken, groupId, Users, Role).GetAwaiter().GetResult(); } } } diff --git a/src/Commands/Teams/CopyTeamsTeam.cs b/src/Commands/Teams/CopyTeamsTeam.cs index 48be4d2bf..d61909ef1 100644 --- a/src/Commands/Teams/CopyTeamsTeam.cs +++ b/src/Commands/Teams/CopyTeamsTeam.cs @@ -48,7 +48,7 @@ public class CopyTeamsTeam : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Identity.GetGroupId(HttpClient, AccessToken); + var groupId = Identity.GetGroupId(Connection, AccessToken); if (groupId == null) { @@ -70,7 +70,7 @@ protected override void ExecuteCmdlet() * but currently ignored and can't be set by user */ teamClone.MailNickName = DisplayName; teamClone.Visibility = (GroupVisibility)Enum.Parse(typeof(GroupVisibility), Visibility.ToString()); - TeamsUtility.CloneTeamAsync(AccessToken, HttpClient, groupId, teamClone).GetAwaiter().GetResult(); + TeamsUtility.CloneTeamAsync(AccessToken, Connection, groupId, teamClone).GetAwaiter().GetResult(); } } } diff --git a/src/Commands/Teams/GetTeamsApp.cs b/src/Commands/Teams/GetTeamsApp.cs index 545285a98..5d3ef359c 100644 --- a/src/Commands/Teams/GetTeamsApp.cs +++ b/src/Commands/Teams/GetTeamsApp.cs @@ -20,7 +20,7 @@ protected override void ExecuteCmdlet() { if (ParameterSpecified(nameof(Identity))) { - var app = Identity.GetApp(HttpClient, AccessToken); + var app = Identity.GetApp(Connection, AccessToken); if (app != null) { WriteObject(app); @@ -28,7 +28,7 @@ protected override void ExecuteCmdlet() } else { - WriteObject(TeamsUtility.GetAppsAsync(AccessToken, HttpClient).GetAwaiter().GetResult(), true); + WriteObject(TeamsUtility.GetAppsAsync(AccessToken, Connection).GetAwaiter().GetResult(), true); } } } diff --git a/src/Commands/Teams/GetTeamsChannel.cs b/src/Commands/Teams/GetTeamsChannel.cs index ed28125d8..a9c99c312 100644 --- a/src/Commands/Teams/GetTeamsChannel.cs +++ b/src/Commands/Teams/GetTeamsChannel.cs @@ -1,11 +1,7 @@ -using PnP.Framework.Graph; -using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Attributes; using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Base.PipeBinds; using PnP.PowerShell.Commands.Utilities; -using System; -using System.Collections.Generic; -using System.Linq; using System.Management.Automation; namespace PnP.PowerShell.Commands.Teams @@ -23,16 +19,16 @@ public class GetTeamsChannel : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (groupId != null) { if (ParameterSpecified(nameof(Identity))) { - WriteObject(Identity.GetChannel(HttpClient, AccessToken, groupId)); + WriteObject(Identity.GetChannel(Connection, AccessToken, groupId)); } else { - WriteObject(TeamsUtility.GetChannelsAsync(AccessToken, HttpClient, groupId).GetAwaiter().GetResult()); + WriteObject(TeamsUtility.GetChannelsAsync(AccessToken, Connection, groupId).GetAwaiter().GetResult()); } } else { diff --git a/src/Commands/Teams/GetTeamsChannelFilesFolder.cs b/src/Commands/Teams/GetTeamsChannelFilesFolder.cs index f18a94b55..034e8dab6 100644 --- a/src/Commands/Teams/GetTeamsChannelFilesFolder.cs +++ b/src/Commands/Teams/GetTeamsChannelFilesFolder.cs @@ -1,8 +1,6 @@ using PnP.PowerShell.Commands.Attributes; using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Base.PipeBinds; -using System; -using System.Collections.Generic; using System.Management.Automation; namespace PnP.PowerShell.Commands.Teams @@ -19,17 +17,17 @@ public class GetTeamsChannelFilesFolder : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (groupId != null) { - var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + var channelId = Channel.GetId(Connection, AccessToken, groupId); if (channelId == null) { throw new PSArgumentException("Channel not found"); } - WriteObject(Utilities.TeamsUtility.GetChannelsFilesFolderAsync(HttpClient, AccessToken, groupId, channelId).GetAwaiter().GetResult()); + WriteObject(Utilities.TeamsUtility.GetChannelsFilesFolderAsync(Connection, AccessToken, groupId, channelId).GetAwaiter().GetResult()); } else diff --git a/src/Commands/Teams/GetTeamsChannelMessage.cs b/src/Commands/Teams/GetTeamsChannelMessage.cs index a172af916..d234e945c 100644 --- a/src/Commands/Teams/GetTeamsChannelMessage.cs +++ b/src/Commands/Teams/GetTeamsChannelMessage.cs @@ -24,13 +24,13 @@ public class GetTeamsChannelMessage : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (groupId == null) { throw new PSArgumentException("Team not found"); } - var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + var channelId = Channel.GetId(Connection, AccessToken, groupId); if (channelId == null) { throw new PSArgumentException("Channel not found"); @@ -43,12 +43,12 @@ protected override void ExecuteCmdlet() throw new PSArgumentException($"Don't specify {nameof(IncludeDeleted)} when using the {nameof(Identity)} parameter."); } - var message = TeamsUtility.GetMessageAsync(HttpClient, AccessToken, groupId, channelId, Identity.GetId()).GetAwaiter().GetResult(); + var message = TeamsUtility.GetMessageAsync(Connection, AccessToken, groupId, channelId, Identity.GetId()).GetAwaiter().GetResult(); WriteObject(message); } else { - var messages = TeamsUtility.GetMessagesAsync(HttpClient, AccessToken, groupId, channelId, IncludeDeleted).GetAwaiter().GetResult(); + var messages = TeamsUtility.GetMessagesAsync(Connection, AccessToken, groupId, channelId, IncludeDeleted).GetAwaiter().GetResult(); WriteObject(messages, true); } } diff --git a/src/Commands/Teams/GetTeamsChannelMessageReply.cs b/src/Commands/Teams/GetTeamsChannelMessageReply.cs index 04b461c20..fd268439c 100644 --- a/src/Commands/Teams/GetTeamsChannelMessageReply.cs +++ b/src/Commands/Teams/GetTeamsChannelMessageReply.cs @@ -27,13 +27,13 @@ public class GetTeamsChannelMessageReply : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (groupId == null) { throw new PSArgumentException("Group not found"); } - var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + var channelId = Channel.GetId(Connection, AccessToken, groupId); if (channelId == null) { throw new PSArgumentException("Channel not found"); @@ -54,12 +54,12 @@ protected override void ExecuteCmdlet() throw new PSArgumentException($"Don't specify {nameof(IncludeDeleted)} when using the {nameof(Identity)} parameter."); } - var reply = TeamsUtility.GetMessageReplyAsync(HttpClient, AccessToken, groupId, channelId, messageId, Identity.GetId()).GetAwaiter().GetResult(); + var reply = TeamsUtility.GetMessageReplyAsync(Connection, AccessToken, groupId, channelId, messageId, Identity.GetId()).GetAwaiter().GetResult(); WriteObject(reply); } else { - var replies = TeamsUtility.GetMessageRepliesAsync(HttpClient, AccessToken, groupId, channelId, messageId, IncludeDeleted).GetAwaiter().GetResult(); + var replies = TeamsUtility.GetMessageRepliesAsync(Connection, AccessToken, groupId, channelId, messageId, IncludeDeleted).GetAwaiter().GetResult(); WriteObject(replies, true); } } diff --git a/src/Commands/Teams/GetTeamsChannelUser.cs b/src/Commands/Teams/GetTeamsChannelUser.cs index c3711d8bc..168f9411b 100644 --- a/src/Commands/Teams/GetTeamsChannelUser.cs +++ b/src/Commands/Teams/GetTeamsChannelUser.cs @@ -25,13 +25,13 @@ public class GetTeamsChannelUser : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (groupId == null) { throw new PSArgumentException("Group not found"); } - var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + var channelId = Channel.GetId(Connection, AccessToken, groupId); if (channelId == null) { throw new PSArgumentException("Channel not found"); @@ -39,11 +39,11 @@ protected override void ExecuteCmdlet() if (ParameterSpecified(nameof(Identity))) { - WriteObject(Identity.GetMembershipAsync(HttpClient, AccessToken, groupId, channelId).GetAwaiter().GetResult()); + WriteObject(Identity.GetMembershipAsync(Connection, AccessToken, groupId, channelId).GetAwaiter().GetResult()); } else { - WriteObject(TeamsUtility.GetChannelMembersAsync(HttpClient, AccessToken, groupId, channelId, Role).GetAwaiter().GetResult(), true); + WriteObject(TeamsUtility.GetChannelMembersAsync(Connection, AccessToken, groupId, channelId, Role).GetAwaiter().GetResult(), true); } } } diff --git a/src/Commands/Teams/GetTeamsPrimaryChannel.cs b/src/Commands/Teams/GetTeamsPrimaryChannel.cs index de24497a6..d0a7f2fe6 100644 --- a/src/Commands/Teams/GetTeamsPrimaryChannel.cs +++ b/src/Commands/Teams/GetTeamsPrimaryChannel.cs @@ -1,11 +1,7 @@ -using PnP.Framework.Graph; -using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Attributes; using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Base.PipeBinds; using PnP.PowerShell.Commands.Utilities; -using System; -using System.Collections.Generic; -using System.Linq; using System.Management.Automation; namespace PnP.PowerShell.Commands.Teams @@ -20,10 +16,10 @@ public class GetTeamsPrimaryChannel : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (groupId != null) { - WriteObject(TeamsUtility.GetPrimaryChannelAsync(AccessToken, HttpClient, groupId).GetAwaiter().GetResult()); + WriteObject(TeamsUtility.GetPrimaryChannelAsync(AccessToken, Connection, groupId).GetAwaiter().GetResult()); } else { throw new PSArgumentException("Team not found", nameof(Team)); diff --git a/src/Commands/Teams/GetTeamsTab.cs b/src/Commands/Teams/GetTeamsTab.cs index f330a7112..e48529137 100644 --- a/src/Commands/Teams/GetTeamsTab.cs +++ b/src/Commands/Teams/GetTeamsTab.cs @@ -23,19 +23,19 @@ public class GetTeamsTab : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (groupId != null) { - var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + var channelId = Channel.GetId(Connection, AccessToken, groupId); if (!string.IsNullOrEmpty(channelId)) { if (ParameterSpecified(nameof(Identity))) { - WriteObject(Identity.GetTab(this,HttpClient, AccessToken, groupId, channelId)); + WriteObject(Identity.GetTab(this,Connection, AccessToken, groupId, channelId)); } else { - WriteObject(TeamsUtility.GetTabsAsync(AccessToken, HttpClient, groupId, channelId).GetAwaiter().GetResult(), true); + WriteObject(TeamsUtility.GetTabsAsync(AccessToken, Connection, groupId, channelId).GetAwaiter().GetResult(), true); } } else diff --git a/src/Commands/Teams/GetTeamsTeam.cs b/src/Commands/Teams/GetTeamsTeam.cs index be84b8a8b..c493d2461 100644 --- a/src/Commands/Teams/GetTeamsTeam.cs +++ b/src/Commands/Teams/GetTeamsTeam.cs @@ -17,15 +17,15 @@ protected override void ExecuteCmdlet() { if (ParameterSpecified(nameof(Identity))) { - var groupId = Identity.GetGroupId(HttpClient, AccessToken); + var groupId = Identity.GetGroupId(Connection, AccessToken); if (groupId != null) { - WriteObject(TeamsUtility.GetTeamAsync(AccessToken, HttpClient, groupId).GetAwaiter().GetResult()); + WriteObject(TeamsUtility.GetTeamAsync(AccessToken, Connection, groupId).GetAwaiter().GetResult()); } } else { - WriteObject(TeamsUtility.GetTeamsAsync(AccessToken, HttpClient).GetAwaiter().GetResult(), true); + WriteObject(TeamsUtility.GetTeamsAsync(AccessToken, Connection).GetAwaiter().GetResult(), true); } } } diff --git a/src/Commands/Teams/GetTeamsUser.cs b/src/Commands/Teams/GetTeamsUser.cs index 64935c9a0..f7c01e095 100644 --- a/src/Commands/Teams/GetTeamsUser.cs +++ b/src/Commands/Teams/GetTeamsUser.cs @@ -25,22 +25,22 @@ public class GetTeamsUser : PnPGraphCmdlet public string Role; protected override void ExecuteCmdlet() { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (groupId != null) { try { if (ParameterSpecified(nameof(Channel))) { - var teamChannels = TeamsUtility.GetChannelsAsync(AccessToken, HttpClient, groupId).GetAwaiter().GetResult(); + var teamChannels = TeamsUtility.GetChannelsAsync(AccessToken, Connection, groupId).GetAwaiter().GetResult(); - var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + var channelId = Channel.GetId(Connection, AccessToken, groupId); var requestedChannel = teamChannels.FirstOrDefault(c => c.Id == channelId); if (!string.IsNullOrEmpty(channelId) && requestedChannel != null && requestedChannel.MembershipType.ToLower() == TeamChannelType.Private.ToString().ToLower()) { - WriteObject(TeamsUtility.GetUsersAsync(HttpClient, AccessToken, groupId, channelId, Role).GetAwaiter().GetResult(), true); + WriteObject(TeamsUtility.GetUsersAsync(Connection, AccessToken, groupId, channelId, Role).GetAwaiter().GetResult(), true); } else { @@ -49,7 +49,7 @@ protected override void ExecuteCmdlet() } else { - WriteObject(TeamsUtility.GetUsersAsync(HttpClient, AccessToken, groupId, Role).GetAwaiter().GetResult(), true); + WriteObject(TeamsUtility.GetUsersAsync(Connection, AccessToken, groupId, Role).GetAwaiter().GetResult(), true); } } catch (GraphException ex) diff --git a/src/Commands/Teams/NewTeamsApp.cs b/src/Commands/Teams/NewTeamsApp.cs index bbae1f1e9..2025ce6ca 100644 --- a/src/Commands/Teams/NewTeamsApp.cs +++ b/src/Commands/Teams/NewTeamsApp.cs @@ -27,7 +27,7 @@ protected override void ExecuteCmdlet() try { var bytes = System.IO.File.ReadAllBytes(Path); - TeamsUtility.AddAppAsync(HttpClient, AccessToken, bytes).GetAwaiter().GetResult(); + TeamsUtility.AddAppAsync(Connection, AccessToken, bytes).GetAwaiter().GetResult(); } catch (GraphException ex) { diff --git a/src/Commands/Teams/NewTeamsTeam.cs b/src/Commands/Teams/NewTeamsTeam.cs index f2930c651..d2c02879d 100644 --- a/src/Commands/Teams/NewTeamsTeam.cs +++ b/src/Commands/Teams/NewTeamsTeam.cs @@ -159,7 +159,7 @@ protected override void ExecuteCmdlet() } } - WriteObject(TeamsUtility.NewTeamAsync(AccessToken, HttpClient, GroupId, DisplayName, Description, Classification, MailNickName, (GroupVisibility)Enum.Parse(typeof(GroupVisibility), Visibility.ToString()), teamCI, Owners, Members, SensitivityLabels, Template, ResourceBehaviorOptions).GetAwaiter().GetResult()); + WriteObject(TeamsUtility.NewTeamAsync(AccessToken, Connection, GroupId, DisplayName, Description, Classification, MailNickName, (GroupVisibility)Enum.Parse(typeof(GroupVisibility), Visibility.ToString()), teamCI, Owners, Members, SensitivityLabels, Template, ResourceBehaviorOptions).GetAwaiter().GetResult()); } } } diff --git a/src/Commands/Teams/RemoveTeamsApp.cs b/src/Commands/Teams/RemoveTeamsApp.cs index 9dbff7d9f..1d842f4fb 100644 --- a/src/Commands/Teams/RemoveTeamsApp.cs +++ b/src/Commands/Teams/RemoveTeamsApp.cs @@ -20,14 +20,14 @@ public class RemoveTeamsApp : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var app = Identity.GetApp(HttpClient, AccessToken); + var app = Identity.GetApp(Connection, AccessToken); if (app == null) { throw new PSArgumentException("App not found"); } if (Force || ShouldContinue($"Do you want to remove {app.DisplayName}?", Properties.Resources.Confirm)) { - var response = TeamsUtility.DeleteAppAsync(HttpClient, AccessToken, app.Id).GetAwaiter().GetResult(); + var response = TeamsUtility.DeleteAppAsync(Connection, AccessToken, app.Id).GetAwaiter().GetResult(); if (!response.IsSuccessStatusCode) { if (GraphHelper.TryGetGraphException(response, out GraphException ex)) diff --git a/src/Commands/Teams/RemoveTeamsChannel.cs b/src/Commands/Teams/RemoveTeamsChannel.cs index 70a1fdf37..b12d78e22 100644 --- a/src/Commands/Teams/RemoveTeamsChannel.cs +++ b/src/Commands/Teams/RemoveTeamsChannel.cs @@ -27,13 +27,13 @@ protected override void ExecuteCmdlet() { if (Force || ShouldContinue("Removing the channel will also remove all the messages in the channel.", Properties.Resources.Confirm)) { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (groupId != null) { - var channel = Identity.GetChannel(HttpClient, AccessToken, groupId); + var channel = Identity.GetChannel(Connection, AccessToken, groupId); if (channel != null) { - var response = TeamsUtility.DeleteChannelAsync(AccessToken, HttpClient, groupId, channel.Id).GetAwaiter().GetResult(); + var response = TeamsUtility.DeleteChannelAsync(AccessToken, Connection, groupId, channel.Id).GetAwaiter().GetResult(); if (!response.IsSuccessStatusCode) { if (GraphHelper.TryGetGraphException(response, out GraphException ex)) diff --git a/src/Commands/Teams/RemoveTeamsChannelUser.cs b/src/Commands/Teams/RemoveTeamsChannelUser.cs index 31a35c3e4..1a46995ac 100644 --- a/src/Commands/Teams/RemoveTeamsChannelUser.cs +++ b/src/Commands/Teams/RemoveTeamsChannelUser.cs @@ -26,19 +26,19 @@ public class RemoveTeamsChannelUser : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (string.IsNullOrEmpty(groupId)) { throw new PSArgumentException("Group not found"); } - var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + var channelId = Channel.GetId(Connection, AccessToken, groupId); if (string.IsNullOrEmpty(channelId)) { throw new PSArgumentException("Channel not found in the specified team"); } - var memberId = Identity.GetIdAsync(HttpClient, AccessToken, groupId, channelId).GetAwaiter().GetResult(); + var memberId = Identity.GetIdAsync(Connection, AccessToken, groupId, channelId).GetAwaiter().GetResult(); if (string.IsNullOrEmpty(memberId)) { throw new PSArgumentException("User was not found in the specified Teams channel"); @@ -46,7 +46,7 @@ protected override void ExecuteCmdlet() if (Force || ShouldContinue("Remove specified member from the Microsoft Teams channel?", Resources.Confirm)) { - var response = TeamsUtility.DeleteChannelMemberAsync(HttpClient, AccessToken, groupId, channelId, memberId).GetAwaiter().GetResult(); + var response = TeamsUtility.DeleteChannelMemberAsync(Connection, AccessToken, groupId, channelId, memberId).GetAwaiter().GetResult(); if (!response.IsSuccessStatusCode) { diff --git a/src/Commands/Teams/RemoveTeamsTab.cs b/src/Commands/Teams/RemoveTeamsTab.cs index ba4430272..797d62ee9 100644 --- a/src/Commands/Teams/RemoveTeamsTab.cs +++ b/src/Commands/Teams/RemoveTeamsTab.cs @@ -27,18 +27,18 @@ public class RemoveTeamsTab : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (groupId != null) { - var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + var channelId = Channel.GetId(Connection, AccessToken, groupId); if (channelId != null) { - var tab = Identity.GetTab(this, HttpClient, AccessToken, groupId, channelId); + var tab = Identity.GetTab(this, Connection, AccessToken, groupId, channelId); if (tab != null) { if (Force || ShouldContinue("Removing the tab will remove the settings of this tab too.", Properties.Resources.Confirm)) { - var response = TeamsUtility.DeleteTabAsync(AccessToken, HttpClient, groupId, channelId, tab.Id).GetAwaiter().GetResult(); + var response = TeamsUtility.DeleteTabAsync(AccessToken, Connection, groupId, channelId, tab.Id).GetAwaiter().GetResult(); if (!response.IsSuccessStatusCode) { if (GraphHelper.TryGetGraphException(response, out GraphException ex)) diff --git a/src/Commands/Teams/RemoveTeamsTeam.cs b/src/Commands/Teams/RemoveTeamsTeam.cs index 784099ae1..148dbbb38 100644 --- a/src/Commands/Teams/RemoveTeamsTeam.cs +++ b/src/Commands/Teams/RemoveTeamsTeam.cs @@ -21,12 +21,12 @@ public class RemoveTeamsTeam : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Identity.GetGroupId(HttpClient, AccessToken); + var groupId = Identity.GetGroupId(Connection, AccessToken); if (groupId != null) { if (Force || ShouldContinue("Removing the team will remove all messages in all channels in the team.", Properties.Resources.Confirm)) { - var response = TeamsUtility.DeleteTeamAsync(AccessToken, HttpClient, groupId).GetAwaiter().GetResult(); + var response = TeamsUtility.DeleteTeamAsync(AccessToken, Connection, groupId).GetAwaiter().GetResult(); if (!response.IsSuccessStatusCode) { if (GraphHelper.TryGetGraphException(response, out GraphException ex)) diff --git a/src/Commands/Teams/RemoveTeamsUser.cs b/src/Commands/Teams/RemoveTeamsUser.cs index 5d7b83350..e36377277 100644 --- a/src/Commands/Teams/RemoveTeamsUser.cs +++ b/src/Commands/Teams/RemoveTeamsUser.cs @@ -25,14 +25,14 @@ public class RemoveTeamsUser : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (groupId != null) { try { if (Force || ShouldContinue($"Remove user with UPN {User}?", Properties.Resources.Confirm)) { - TeamsUtility.DeleteUserAsync(HttpClient, AccessToken, groupId, User, Role).GetAwaiter().GetResult(); + TeamsUtility.DeleteUserAsync(Connection, AccessToken, groupId, User, Role).GetAwaiter().GetResult(); } } catch (GraphException ex) diff --git a/src/Commands/Teams/SetTeamsChannel.cs b/src/Commands/Teams/SetTeamsChannel.cs index 2e08dc405..b2ccb6e03 100644 --- a/src/Commands/Teams/SetTeamsChannel.cs +++ b/src/Commands/Teams/SetTeamsChannel.cs @@ -28,10 +28,10 @@ public class SetTeamsChannel : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (groupId != null) { - var teamChannel = Identity.GetChannel(HttpClient, AccessToken, groupId); + var teamChannel = Identity.GetChannel(Connection, AccessToken, groupId); if (teamChannel != null) { if (ParameterSpecified(nameof(DisplayName)) && teamChannel.DisplayName != DisplayName) @@ -60,7 +60,7 @@ protected override void ExecuteCmdlet() teamChannel.MembershipType = null; try { - var updated = TeamsUtility.UpdateChannelAsync(HttpClient, AccessToken, groupId, teamChannel.Id, teamChannel).GetAwaiter().GetResult(); + var updated = TeamsUtility.UpdateChannelAsync(Connection, AccessToken, groupId, teamChannel.Id, teamChannel).GetAwaiter().GetResult(); WriteObject(updated); } catch (GraphException ex) diff --git a/src/Commands/Teams/SetTeamsChannelUser.cs b/src/Commands/Teams/SetTeamsChannelUser.cs index d985bc2e9..30d29a906 100644 --- a/src/Commands/Teams/SetTeamsChannelUser.cs +++ b/src/Commands/Teams/SetTeamsChannelUser.cs @@ -26,19 +26,19 @@ public class SetTeamsChannelUser : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (groupId == null) { throw new PSArgumentException("Group not found"); } - var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + var channelId = Channel.GetId(Connection, AccessToken, groupId); if (channelId == null) { throw new PSArgumentException("Channel not found"); } - var membershipId = Identity.GetIdAsync(HttpClient, AccessToken, groupId, channelId).GetAwaiter().GetResult(); + var membershipId = Identity.GetIdAsync(Connection, AccessToken, groupId, channelId).GetAwaiter().GetResult(); if (string.IsNullOrEmpty(membershipId)) { throw new PSArgumentException("User was not found in the specified Teams channel"); @@ -46,7 +46,7 @@ protected override void ExecuteCmdlet() try { - var updatedMember = TeamsUtility.UpdateChannelMemberAsync(HttpClient, AccessToken, groupId, channelId, membershipId, Role).GetAwaiter().GetResult(); + var updatedMember = TeamsUtility.UpdateChannelMemberAsync(Connection, AccessToken, groupId, channelId, membershipId, Role).GetAwaiter().GetResult(); WriteObject(updatedMember); } catch (GraphException ex) diff --git a/src/Commands/Teams/SetTeamsTab.cs b/src/Commands/Teams/SetTeamsTab.cs index f6e68fa18..5c29f81c1 100644 --- a/src/Commands/Teams/SetTeamsTab.cs +++ b/src/Commands/Teams/SetTeamsTab.cs @@ -24,20 +24,20 @@ public class SetTeamsTab : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (groupId != null) { - var channelId = Channel.GetId(HttpClient, AccessToken, groupId); + var channelId = Channel.GetId(Connection, AccessToken, groupId); if (channelId != null) { - var tab = Identity.GetTab(this,HttpClient, AccessToken, groupId, channelId); + var tab = Identity.GetTab(this,Connection, AccessToken, groupId, channelId); if (tab != null) { if (ParameterSpecified(nameof(DisplayName)) && tab.DisplayName != DisplayName) { tab.DisplayName = DisplayName; } - TeamsUtility.UpdateTabAsync(HttpClient, AccessToken, groupId, channelId, tab).GetAwaiter().GetResult(); + TeamsUtility.UpdateTabAsync(Connection, AccessToken, groupId, channelId, tab).GetAwaiter().GetResult(); } else { diff --git a/src/Commands/Teams/SetTeamsTeam.cs b/src/Commands/Teams/SetTeamsTeam.cs index 0d9254b78..52042c463 100644 --- a/src/Commands/Teams/SetTeamsTeam.cs +++ b/src/Commands/Teams/SetTeamsTeam.cs @@ -84,12 +84,12 @@ public class SetTeamsTeam : PnPGraphCmdlet public bool? AllowCreatePrivateChannels; protected override void ExecuteCmdlet() { - var groupId = Identity.GetGroupId(HttpClient, AccessToken); + var groupId = Identity.GetGroupId(Connection, AccessToken); if (groupId != null) { try { - var team = TeamsUtility.GetTeamAsync(AccessToken, HttpClient, groupId).GetAwaiter().GetResult(); + var team = TeamsUtility.GetTeamAsync(AccessToken, Connection, groupId).GetAwaiter().GetResult(); var updateGroup = false; var group = new Group(); if (team != null) @@ -125,7 +125,7 @@ protected override void ExecuteCmdlet() if(updateGroup) { - TeamsUtility.UpdateGroupAsync(HttpClient, AccessToken, groupId, group).GetAwaiter().GetResult(); + TeamsUtility.UpdateGroupAsync(Connection, AccessToken, groupId, group).GetAwaiter().GetResult(); } var teamCI = new TeamCreationInformation(); @@ -147,7 +147,7 @@ protected override void ExecuteCmdlet() teamCI.Classification = ParameterSpecified(nameof(Classification)) ? Classification : null; teamCI.AllowCreatePrivateChannels = ParameterSpecified(nameof(AllowCreatePrivateChannels)) ? AllowCreatePrivateChannels : null; - var updated = TeamsUtility.UpdateTeamAsync(HttpClient, AccessToken, groupId, teamCI.ToTeam(group.Visibility)).GetAwaiter().GetResult(); + var updated = TeamsUtility.UpdateTeamAsync(Connection, AccessToken, groupId, teamCI.ToTeam(group.Visibility)).GetAwaiter().GetResult(); WriteObject(updated); } } diff --git a/src/Commands/Teams/SetTeamsTeamArchivedState.cs b/src/Commands/Teams/SetTeamsTeamArchivedState.cs index a4d856a02..0266f1c3d 100644 --- a/src/Commands/Teams/SetTeamsTeamArchivedState.cs +++ b/src/Commands/Teams/SetTeamsTeamArchivedState.cs @@ -30,15 +30,15 @@ protected override void ExecuteCmdlet() { throw new PSArgumentException("You can only modify the read only state of a site when archiving a team"); } - var groupId = Identity.GetGroupId(HttpClient, AccessToken); + var groupId = Identity.GetGroupId(Connection, AccessToken); if (groupId != null) { - var team = Identity.GetTeam(HttpClient, AccessToken); + var team = Identity.GetTeam(Connection, AccessToken); if (Archived == team.IsArchived) { throw new PSInvalidOperationException($"Team {team.DisplayName} {(Archived ? "has already been" : "is not")} archived"); } - var response = TeamsUtility.SetTeamArchivedStateAsync(HttpClient, AccessToken, groupId, Archived, SetSiteReadOnlyForMembers).GetAwaiter().GetResult(); + var response = TeamsUtility.SetTeamArchivedStateAsync(Connection, AccessToken, groupId, Archived, SetSiteReadOnlyForMembers).GetAwaiter().GetResult(); if (!response.IsSuccessStatusCode) { if (GraphHelper.TryGetGraphException(response, out GraphException ex)) diff --git a/src/Commands/Teams/SetTeamsTeamPicture.cs b/src/Commands/Teams/SetTeamsTeamPicture.cs index 5d4c2b64e..e6337d08b 100644 --- a/src/Commands/Teams/SetTeamsTeamPicture.cs +++ b/src/Commands/Teams/SetTeamsTeamPicture.cs @@ -21,7 +21,7 @@ public class SetTeamsTeamPicture : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (groupId != null) { if (!System.IO.Path.IsPathRooted(Path)) @@ -52,7 +52,7 @@ protected override void ExecuteCmdlet() throw new PSArgumentException("File is not of a supported content type (jpg/png)"); } var byteArray = File.ReadAllBytes(Path); - TeamsUtility.SetTeamPictureAsync(HttpClient, AccessToken, groupId, byteArray, contentType).GetAwaiter().GetResult(); + TeamsUtility.SetTeamPictureAsync(Connection, AccessToken, groupId, byteArray, contentType).GetAwaiter().GetResult(); } else { diff --git a/src/Commands/Teams/SubmitTeamsChannelMessage.cs b/src/Commands/Teams/SubmitTeamsChannelMessage.cs index b2428bc56..cba8ec243 100644 --- a/src/Commands/Teams/SubmitTeamsChannelMessage.cs +++ b/src/Commands/Teams/SubmitTeamsChannelMessage.cs @@ -29,10 +29,10 @@ public class SubmitTeamsChannelMessage : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (groupId != null) { - var channel = Channel.GetChannel(HttpClient, AccessToken, groupId); + var channel = Channel.GetChannel(Connection, AccessToken, groupId); if (channel != null) { var channelMessage = new TeamChannelMessage(); @@ -40,7 +40,7 @@ protected override void ExecuteCmdlet() channelMessage.Body.Content = Message; channelMessage.Body.ContentType = ContentType == TeamChannelMessageContentType.Html ? "html" : "text"; - TeamsUtility.PostMessageAsync(HttpClient, AccessToken, groupId, channel.Id, channelMessage).GetAwaiter().GetResult(); + TeamsUtility.PostMessageAsync(Connection, AccessToken, groupId, channel.Id, channelMessage).GetAwaiter().GetResult(); } else { diff --git a/src/Commands/Teams/UpdateTeamsApp.cs b/src/Commands/Teams/UpdateTeamsApp.cs index 8ddc8db63..1f035e9ed 100644 --- a/src/Commands/Teams/UpdateTeamsApp.cs +++ b/src/Commands/Teams/UpdateTeamsApp.cs @@ -29,12 +29,12 @@ protected override void ExecuteCmdlet() if (System.IO.File.Exists(Path)) { - var app = Identity.GetApp(HttpClient, AccessToken); + var app = Identity.GetApp(Connection, AccessToken); if (app != null) { var bytes = System.IO.File.ReadAllBytes(Path); - var response = TeamsUtility.UpdateAppAsync(HttpClient, AccessToken, bytes, app.Id).GetAwaiter().GetResult(); + var response = TeamsUtility.UpdateAppAsync(Connection, AccessToken, bytes, app.Id).GetAwaiter().GetResult(); if (!response.IsSuccessStatusCode) { if (GraphHelper.TryGetGraphException(response, out GraphException ex)) diff --git a/src/Commands/Teams/UpdateTeamsUser.cs b/src/Commands/Teams/UpdateTeamsUser.cs index d1b2063e6..4d7a61eca 100644 --- a/src/Commands/Teams/UpdateTeamsUser.cs +++ b/src/Commands/Teams/UpdateTeamsUser.cs @@ -26,24 +26,24 @@ public class UpdateTeamsUser : PnPGraphCmdlet protected override void ExecuteCmdlet() { - var groupId = Team.GetGroupId(HttpClient, AccessToken); + var groupId = Team.GetGroupId(Connection, AccessToken); if (groupId != null) { try { if (Force || ShouldContinue($"Update role for user with UPN {User} ?", Properties.Resources.Confirm)) { - var teamsUser = TeamsUtility.GetUsersAsync(HttpClient, AccessToken, groupId, string.Empty).GetAwaiter().GetResult(); + var teamsUser = TeamsUtility.GetUsersAsync(Connection, AccessToken, groupId, string.Empty).GetAwaiter().GetResult(); var specifiedUser = teamsUser.Find(u => u.UserPrincipalName.ToLower() == User.ToLower()); if (specifiedUser != null) { // No easy way to get member Id for teams endpoint, need to rely on display name filter to fetch memberId of the specified user and then update - var teamUserWithDisplayName = TeamsUtility.GetTeamUsersWithDisplayNameAsync(HttpClient, AccessToken, groupId, specifiedUser.DisplayName).GetAwaiter().GetResult(); + var teamUserWithDisplayName = TeamsUtility.GetTeamUsersWithDisplayNameAsync(Connection, AccessToken, groupId, specifiedUser.DisplayName).GetAwaiter().GetResult(); var userToUpdate = teamUserWithDisplayName.Find(u => u.UserId == specifiedUser.Id); // Pass the member id of the user whose role we are changing - WriteObject(TeamsUtility.UpdateTeamUserRole(HttpClient, AccessToken, groupId, userToUpdate.Id, Role).GetAwaiter().GetResult()); + WriteObject(TeamsUtility.UpdateTeamUserRole(Connection, AccessToken, groupId, userToUpdate.Id, Role).GetAwaiter().GetResult()); } else { diff --git a/src/Commands/Utilities/Microsoft365GroupsUtility.cs b/src/Commands/Utilities/Microsoft365GroupsUtility.cs index 87c25e01e..c6969075b 100644 --- a/src/Commands/Utilities/Microsoft365GroupsUtility.cs +++ b/src/Commands/Utilities/Microsoft365GroupsUtility.cs @@ -12,10 +12,10 @@ namespace PnP.PowerShell.Commands.Utilities { internal static class Microsoft365GroupsUtility { - internal static async Task> GetGroupsAsync(HttpClient httpClient, string accessToken, bool includeSiteUrl, bool includeOwners) + internal static async Task> GetGroupsAsync(PnPConnection connection, string accessToken, bool includeSiteUrl, bool includeOwners) { var items = new List(); - var result = await GraphHelper.GetResultCollectionAsync(httpClient, "v1.0/groups?$filter=groupTypes/any(c:c+eq+'Unified')", accessToken); + var result = await GraphHelper.GetResultCollectionAsync(connection, "v1.0/groups?$filter=groupTypes/any(c:c+eq+'Unified')", accessToken); if (result != null && result.Any()) { items.AddRange(result); @@ -27,7 +27,7 @@ internal static async Task> GetGroupsAsync(HttpCl { foreach (var chunk in chunks) { - var ownerResults = await BatchUtility.GetObjectCollectionBatchedAsync(httpClient, accessToken, chunk.ToArray(), "/groups/{0}/owners"); + var ownerResults = await BatchUtility.GetObjectCollectionBatchedAsync(connection, accessToken, chunk.ToArray(), "/groups/{0}/owners"); foreach (var ownerResult in ownerResults) { items.First(i => i.Id.ToString() == ownerResult.Key).Owners = ownerResult.Value; @@ -39,8 +39,8 @@ internal static async Task> GetGroupsAsync(HttpCl { foreach (var chunk in chunks) { - var results = await BatchUtility.GetPropertyBatchedAsync(httpClient, accessToken, chunk.ToArray(), "/groups/{0}/sites/root", "webUrl"); - //var results = await GetSiteUrlBatchedAsync(httpClient, accessToken, chunk.ToArray()); + var results = await BatchUtility.GetPropertyBatchedAsync(connection, accessToken, chunk.ToArray(), "/groups/{0}/sites/root", "webUrl"); + //var results = await GetSiteUrlBatchedAsync(connection, accessToken, chunk.ToArray()); foreach (var batchResult in results) { items.First(i => i.Id.ToString() == batchResult.Key).SiteUrl = batchResult.Value; @@ -50,9 +50,9 @@ internal static async Task> GetGroupsAsync(HttpCl } return items; } - internal static async Task GetGroupAsync(HttpClient httpClient, Guid groupId, string accessToken, bool includeSiteUrl, bool includeOwners) + internal static async Task GetGroupAsync(PnPConnection connection, Guid groupId, string accessToken, bool includeSiteUrl, bool includeOwners) { - var group = await GraphHelper.GetAsync(httpClient, $"v1.0/groups/{groupId}", accessToken); + var group = await GraphHelper.GetAsync(connection, $"v1.0/groups/{groupId}", accessToken); if (includeSiteUrl) { bool wait = true; @@ -63,7 +63,7 @@ internal static async Task GetGroupAsync(HttpClient httpClien iterations++; try { - var siteUrlResult = await GraphHelper.GetAsync(httpClient, $"v1.0/groups/{group.Id}/sites/root?$select=webUrl", accessToken); + var siteUrlResult = await GraphHelper.GetAsync(connection, $"v1.0/groups/{group.Id}/sites/root?$select=webUrl", accessToken); if (!string.IsNullOrEmpty(siteUrlResult)) { wait = false; @@ -91,19 +91,19 @@ internal static async Task GetGroupAsync(HttpClient httpClien } if (includeOwners) { - group.Owners = await GetGroupMembersAsync("owners", httpClient, group.Id.Value, accessToken); + group.Owners = await GetGroupMembersAsync("owners", connection, group.Id.Value, accessToken); } return group; } - internal static async Task GetGroupAsync(HttpClient httpClient, string displayName, string accessToken, bool includeSiteUrl, bool includeOwners) + internal static async Task GetGroupAsync(PnPConnection connection, string displayName, string accessToken, bool includeSiteUrl, bool includeOwners) { - var results = await GraphHelper.GetAsync>(httpClient, $"v1.0/groups?$filter=groupTypes/any(c:c+eq+'Unified') and displayName eq '{displayName}' or mailNickName eq '{displayName}'", accessToken); + var results = await GraphHelper.GetAsync>(connection, $"v1.0/groups?$filter=groupTypes/any(c:c+eq+'Unified') and displayName eq '{displayName}' or mailNickName eq '{displayName}'", accessToken); if (results != null && results.Items.Any()) { var group = results.Items.First(); if (includeSiteUrl) { - var siteUrlResult = await GraphHelper.GetAsync(httpClient, $"v1.0/groups/{group.Id}/sites/root?$select=webUrl", accessToken); + var siteUrlResult = await GraphHelper.GetAsync(connection, $"v1.0/groups/{group.Id}/sites/root?$select=webUrl", accessToken); var resultElement = JsonSerializer.Deserialize(siteUrlResult); if (resultElement.TryGetProperty("webUrl", out JsonElement webUrlElement)) { @@ -112,21 +112,21 @@ internal static async Task GetGroupAsync(HttpClient httpClien } if (includeOwners) { - group.Owners = await GetGroupMembersAsync("owners", httpClient, group.Id.Value, accessToken); + group.Owners = await GetGroupMembersAsync("owners", connection, group.Id.Value, accessToken); } return group; } return null; } - internal static async Task GetDeletedGroupAsync(HttpClient httpClient, Guid groupId, string accessToken) + internal static async Task GetDeletedGroupAsync(PnPConnection connection, Guid groupId, string accessToken) { - return await GraphHelper.GetAsync(httpClient, $"v1.0/directory/deleteditems/microsoft.graph.group/{groupId}", accessToken); + return await GraphHelper.GetAsync(connection, $"v1.0/directory/deleteditems/microsoft.graph.group/{groupId}", accessToken); } - internal static async Task GetDeletedGroupAsync(HttpClient httpClient, string groupName, string accessToken) + internal static async Task GetDeletedGroupAsync(PnPConnection connection, string groupName, string accessToken) { - var results = await GraphHelper.GetAsync>(httpClient, $"v1.0/directory/deleteditems/microsoft.graph.group?$filter=displayName eq '{groupName}' or mailNickname eq '{groupName}'", accessToken); + var results = await GraphHelper.GetAsync>(connection, $"v1.0/directory/deleteditems/microsoft.graph.group?$filter=displayName eq '{groupName}' or mailNickname eq '{groupName}'", accessToken); if (results != null && results.Items.Any()) { return results.Items.First(); @@ -134,30 +134,30 @@ internal static async Task GetDeletedGroupAsync(HttpClient ht return null; } - internal static async Task> GetDeletedGroupsAsync(HttpClient httpClient, string accessToken) + internal static async Task> GetDeletedGroupsAsync(PnPConnection connection, string accessToken) { - var result = await GraphHelper.GetResultCollectionAsync(httpClient, "v1.0/directory/deleteditems/microsoft.graph.group", accessToken); + var result = await GraphHelper.GetResultCollectionAsync(connection, "v1.0/directory/deleteditems/microsoft.graph.group", accessToken); return result; } - internal static async Task RestoreDeletedGroupAsync(HttpClient httpClient, Guid groupId, string accessToken) + internal static async Task RestoreDeletedGroupAsync(PnPConnection connection, Guid groupId, string accessToken) { - return await GraphHelper.PostAsync(httpClient, $"v1.0/directory/deleteditems/microsoft.graph.group/{groupId}/restore", accessToken); + return await GraphHelper.PostAsync(connection, $"v1.0/directory/deleteditems/microsoft.graph.group/{groupId}/restore", accessToken); } - internal static async Task PermanentlyDeleteDeletedGroupAsync(HttpClient httpClient, Guid groupId, string accessToken) + internal static async Task PermanentlyDeleteDeletedGroupAsync(PnPConnection connection, Guid groupId, string accessToken) { - await GraphHelper.DeleteAsync(httpClient, $"v1.0/directory/deleteditems/microsoft.graph.group/{groupId}", accessToken); + await GraphHelper.DeleteAsync(connection, $"v1.0/directory/deleteditems/microsoft.graph.group/{groupId}", accessToken); } - internal static async Task AddOwnersAsync(HttpClient httpClient, Guid groupId, string[] users, string accessToken, bool removeExisting) + internal static async Task AddOwnersAsync(PnPConnection connection, Guid groupId, string[] users, string accessToken, bool removeExisting) { - await AddUsersToGroupAsync("owners", httpClient, groupId, users, accessToken, removeExisting); + await AddUsersToGroupAsync("owners", connection, groupId, users, accessToken, removeExisting); } - internal static async Task AddMembersAsync(HttpClient httpClient, Guid groupId, string[] users, string accessToken, bool removeExisting) + internal static async Task AddMembersAsync(PnPConnection connection, Guid groupId, string[] users, string accessToken, bool removeExisting) { - await AddUsersToGroupAsync("members", httpClient, groupId, users, accessToken, removeExisting); + await AddUsersToGroupAsync("members", connection, groupId, users, accessToken, removeExisting); } internal static string GetUserGraphUrlForUPN(string upn) @@ -170,11 +170,11 @@ internal static string GetUserGraphUrlForUPN(string upn) return $"users/{escapedUpn}"; } - private static async Task AddUsersToGroupAsync(string groupName, HttpClient httpClient, Guid groupId, string[] users, string accessToken, bool removeExisting) + private static async Task AddUsersToGroupAsync(string groupName, PnPConnection connection, Guid groupId, string[] users, string accessToken, bool removeExisting) { foreach (var user in users) { - var userIdResult = await GraphHelper.GetAsync(httpClient, $"v1.0/{GetUserGraphUrlForUPN(user)}?$select=Id", accessToken); + var userIdResult = await GraphHelper.GetAsync(connection, $"v1.0/{GetUserGraphUrlForUPN(user)}?$select=Id", accessToken); var resultElement = JsonSerializer.Deserialize(userIdResult); if (resultElement.TryGetProperty("id", out JsonElement idProperty)) { @@ -187,114 +187,114 @@ private static async Task AddUsersToGroupAsync(string groupName, HttpClient http var stringContent = new StringContent(JsonSerializer.Serialize(postData)); stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - await GraphHelper.PostAsync(httpClient, $"v1.0/groups/{groupId}/{groupName}/$ref", accessToken, stringContent); + await GraphHelper.PostAsync(connection, $"v1.0/groups/{groupId}/{groupName}/$ref", accessToken, stringContent); } } } - internal static async Task RemoveOwnersAsync(HttpClient httpClient, Guid groupId, string[] users, string accessToken) + internal static async Task RemoveOwnersAsync(PnPConnection connection, Guid groupId, string[] users, string accessToken) { - await RemoveUserFromGroupAsync("owners", httpClient, groupId, users, accessToken); + await RemoveUserFromGroupAsync("owners", connection, groupId, users, accessToken); } - internal static async Task RemoveMembersAsync(HttpClient httpClient, Guid groupId, string[] users, string accessToken) + internal static async Task RemoveMembersAsync(PnPConnection connection, Guid groupId, string[] users, string accessToken) { - await RemoveUserFromGroupAsync("members", httpClient, groupId, users, accessToken); + await RemoveUserFromGroupAsync("members", connection, groupId, users, accessToken); } - private static async Task RemoveUserFromGroupAsync(string groupName, HttpClient httpClient, Guid groupId, string[] users, string accessToken) + private static async Task RemoveUserFromGroupAsync(string groupName, PnPConnection connection, Guid groupId, string[] users, string accessToken) { foreach (var user in users) { - var resultString = await GraphHelper.GetAsync(httpClient, $"v1.0/{GetUserGraphUrlForUPN(user)}?$select=Id", accessToken); + var resultString = await GraphHelper.GetAsync(connection, $"v1.0/{GetUserGraphUrlForUPN(user)}?$select=Id", accessToken); var resultElement = JsonSerializer.Deserialize(resultString); if (resultElement.TryGetProperty("id", out JsonElement idElement)) { - await GraphHelper.DeleteAsync(httpClient, $"v1.0/groups/{groupId}/{groupName}/{idElement.GetString()}/$ref", accessToken); + await GraphHelper.DeleteAsync(connection, $"v1.0/groups/{groupId}/{groupName}/{idElement.GetString()}/$ref", accessToken); } } } - internal static async Task RemoveGroupAsync(HttpClient httpClient, Guid groupId, string accessToken) + internal static async Task RemoveGroupAsync(PnPConnection connection, Guid groupId, string accessToken) { - await GraphHelper.DeleteAsync(httpClient, $"v1.0/groups/{groupId}", accessToken); + await GraphHelper.DeleteAsync(connection, $"v1.0/groups/{groupId}", accessToken); } - internal static async Task> GetOwnersAsync(HttpClient httpClient, Guid groupId, string accessToken) + internal static async Task> GetOwnersAsync(PnPConnection connection, Guid groupId, string accessToken) { - return await GetGroupMembersAsync("owners", httpClient, groupId, accessToken); + return await GetGroupMembersAsync("owners", connection, groupId, accessToken); } - internal static async Task> GetMembersAsync(HttpClient httpClient, Guid groupId, string accessToken) + internal static async Task> GetMembersAsync(PnPConnection connection, Guid groupId, string accessToken) { - return await GetGroupMembersAsync("members", httpClient, groupId, accessToken); + return await GetGroupMembersAsync("members", connection, groupId, accessToken); } - private static async Task> GetGroupMembersAsync(string userType, HttpClient httpClient, Guid groupId, string accessToken) + private static async Task> GetGroupMembersAsync(string userType, PnPConnection connection, Guid groupId, string accessToken) { - var results = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/groups/{groupId}/{userType}?$select=*", accessToken); + var results = await GraphHelper.GetResultCollectionAsync(connection, $"v1.0/groups/{groupId}/{userType}?$select=*", accessToken); return results; } - internal static async Task ClearMembersAsync(HttpClient httpClient, Guid groupId, string accessToken) + internal static async Task ClearMembersAsync(PnPConnection connection, Guid groupId, string accessToken) { - var members = await GetMembersAsync(httpClient, groupId, accessToken); + var members = await GetMembersAsync(connection, groupId, accessToken); foreach (var member in members) { - await GraphHelper.DeleteAsync(httpClient, $"v1.0/groups/{groupId}/members/{member.Id}/$ref", accessToken); + await GraphHelper.DeleteAsync(connection, $"v1.0/groups/{groupId}/members/{member.Id}/$ref", accessToken); } } - internal static async Task ClearOwnersAsync(HttpClient httpClient, Guid groupId, string accessToken) + internal static async Task ClearOwnersAsync(PnPConnection connection, Guid groupId, string accessToken) { - var members = await GetOwnersAsync(httpClient, groupId, accessToken); + var members = await GetOwnersAsync(connection, groupId, accessToken); foreach (var member in members) { - await GraphHelper.DeleteAsync(httpClient, $"v1.0/groups/{groupId}/owners/{member.Id}/$ref", accessToken); + await GraphHelper.DeleteAsync(connection, $"v1.0/groups/{groupId}/owners/{member.Id}/$ref", accessToken); } } - internal static async Task UpdateOwnersAsync(HttpClient httpClient, Guid groupId, string accessToken, string[] owners) + internal static async Task UpdateOwnersAsync(PnPConnection connection, Guid groupId, string accessToken, string[] owners) { - var existingOwners = await GetOwnersAsync(httpClient, groupId, accessToken); + var existingOwners = await GetOwnersAsync(connection, groupId, accessToken); foreach (var owner in owners) { if (existingOwners.FirstOrDefault(o => o.UserPrincipalName == owner) == null) { - await AddOwnersAsync(httpClient, groupId, new string[] { owner }, accessToken, false); + await AddOwnersAsync(connection, groupId, new string[] { owner }, accessToken, false); } } foreach (var existingOwner in existingOwners) { if (!owners.Contains(existingOwner.UserPrincipalName)) { - await GraphHelper.DeleteAsync(httpClient, $"v1.0/groups/{groupId}/owners/{existingOwner.Id}/$ref", accessToken); + await GraphHelper.DeleteAsync(connection, $"v1.0/groups/{groupId}/owners/{existingOwner.Id}/$ref", accessToken); } } } - internal static async Task UpdateMembersAsync(HttpClient httpClient, Guid groupId, string accessToken, string[] members) + internal static async Task UpdateMembersAsync(PnPConnection connection, Guid groupId, string accessToken, string[] members) { - var existingMembers = await GetMembersAsync(httpClient, groupId, accessToken); + var existingMembers = await GetMembersAsync(connection, groupId, accessToken); foreach (var member in members) { if (existingMembers.FirstOrDefault(o => o.UserPrincipalName == member) == null) { - await AddMembersAsync(httpClient, groupId, new string[] { member }, accessToken, false); + await AddMembersAsync(connection, groupId, new string[] { member }, accessToken, false); } } foreach (var existingMember in existingMembers) { if (!members.Contains(existingMember.UserPrincipalName)) { - await GraphHelper.DeleteAsync(httpClient, $"v1.0/groups/{groupId}/members/{existingMember.Id}/$ref", accessToken); + await GraphHelper.DeleteAsync(connection, $"v1.0/groups/{groupId}/members/{existingMember.Id}/$ref", accessToken); } } } - internal static async Task> GetSiteUrlBatchedAsync(HttpClient httpClient, string accessToken, string[] groupIds) + internal static async Task> GetSiteUrlBatchedAsync(PnPConnection connection, string accessToken, string[] groupIds) { Dictionary returnValue = new Dictionary(); @@ -309,7 +309,7 @@ internal static async Task> GetSiteUrlBatchedAsync(Ht } var stringContent = new StringContent(JsonSerializer.Serialize(batch)); stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - var result = await GraphHelper.PostAsync(httpClient, "v1.0/$batch", stringContent, accessToken); + var result = await GraphHelper.PostAsync(connection, "v1.0/$batch", stringContent, accessToken); if (result.Responses != null && result.Responses.Any()) { foreach (var response in result.Responses) @@ -325,7 +325,7 @@ internal static async Task> GetSiteUrlBatchedAsync(Ht return returnValue; } - internal static async Task> GetUserIdsBatched(HttpClient httpClient, string accessToken, string[] userPrincipalNames) + internal static async Task> GetUserIdsBatched(PnPConnection connection, string accessToken, string[] userPrincipalNames) { Dictionary returnValue = new Dictionary(); @@ -340,7 +340,7 @@ internal static async Task> GetUserIdsBatched(HttpCli } var stringContent = new StringContent(JsonSerializer.Serialize(batch)); stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - var result = await GraphHelper.PostAsync(httpClient, "v1.0/$batch", stringContent, accessToken); + var result = await GraphHelper.PostAsync(connection, "v1.0/$batch", stringContent, accessToken); if (result.Responses != null && result.Responses.Any()) { foreach (var response in result.Responses) @@ -356,9 +356,9 @@ internal static async Task> GetUserIdsBatched(HttpCli return returnValue; } - internal static async Task GetUsersDataBindValueAsync(HttpClient httpClient, string accessToken, string[] users) + internal static async Task GetUsersDataBindValueAsync(PnPConnection connection, string accessToken, string[] users) { - var userids = await GetUserIdsBatched(httpClient, accessToken, users); + var userids = await GetUserIdsBatched(connection, accessToken, users); if (userids.Any()) { return userids.Select(u => $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/users/{u.Value}").ToArray(); @@ -366,16 +366,16 @@ internal static async Task GetUsersDataBindValueAsync(HttpClient httpC return null; } - internal static async Task CreateAsync(HttpClient httpClient, string accessToken, Microsoft365Group group, bool createTeam, string logoPath, string[] owners, string[] members, bool? hideFromAddressLists, bool? hideFromOutlookClients, List sensitivityLabels) + internal static async Task CreateAsync(PnPConnection connection, string accessToken, Microsoft365Group group, bool createTeam, string logoPath, string[] owners, string[] members, bool? hideFromAddressLists, bool? hideFromOutlookClients, List sensitivityLabels) { if (owners != null && owners.Length > 0) { - group.OwnersODataBind = await GetUsersDataBindValueAsync(httpClient, accessToken, owners); + group.OwnersODataBind = await GetUsersDataBindValueAsync(connection, accessToken, owners); } if (members != null && members.Length > 0) { - group.MembersODataBind = await GetUsersDataBindValueAsync(httpClient, accessToken, members); + group.MembersODataBind = await GetUsersDataBindValueAsync(connection, accessToken, members); } if (sensitivityLabels.Count > 0) @@ -395,27 +395,27 @@ internal static async Task CreateAsync(HttpClient httpClient, group.AssignedLabels = assignedLabels; } - var newGroup = await GraphHelper.PostAsync(httpClient, "v1.0/groups", group, accessToken); + var newGroup = await GraphHelper.PostAsync(connection, "v1.0/groups", group, accessToken); if (hideFromAddressLists.HasValue || hideFromOutlookClients.HasValue) { - await SetVisibilityAsync(httpClient, accessToken, newGroup.Id.Value, hideFromAddressLists, hideFromOutlookClients); + await SetVisibilityAsync(connection, accessToken, newGroup.Id.Value, hideFromAddressLists, hideFromOutlookClients); } if (!string.IsNullOrEmpty(logoPath)) { - await UploadLogoAsync(httpClient, accessToken, newGroup.Id.Value, logoPath); + await UploadLogoAsync(connection, accessToken, newGroup.Id.Value, logoPath); } if (createTeam) { - await CreateTeamAsync(httpClient, accessToken, newGroup.Id.Value); + await CreateTeamAsync(connection, accessToken, newGroup.Id.Value); } return newGroup; } - internal static async Task UploadLogoAsync(HttpClient httpClient, string accessToken, Guid groupId, string logoPath) + internal static async Task UploadLogoAsync(PnPConnection connection, string accessToken, Guid groupId, string logoPath) { var fileBytes = System.IO.File.ReadAllBytes(logoPath); @@ -448,7 +448,7 @@ internal static async Task UploadLogoAsync(HttpClient httpClient, string accessT var retryCount = 10; while (retryCount > 0) { - var responseMessage = await GraphHelper.PutAsync(httpClient, $"/v1.0/groups/{groupId}/photo/$value", accessToken, content); + var responseMessage = await GraphHelper.PutAsync(connection, $"/v1.0/groups/{groupId}/photo/$value", accessToken, content); if (responseMessage.IsSuccessStatusCode) { updated = true; @@ -470,7 +470,7 @@ internal static async Task UploadLogoAsync(HttpClient httpClient, string accessT } } - internal static async Task CreateTeamAsync(HttpClient httpClient, string accessToken, Guid groupId) + internal static async Task CreateTeamAsync(PnPConnection connection, string accessToken, Guid groupId) { var createTeamEndPoint = $"v1.0/groups/{groupId}/team"; bool wait = true; @@ -481,7 +481,7 @@ internal static async Task CreateTeamAsync(HttpClient httpClient, string accessT iterations++; try { - var teamId = await GraphHelper.PutAsync(httpClient, createTeamEndPoint, new { }, accessToken); + var teamId = await GraphHelper.PutAsync(connection, createTeamEndPoint, new { }, accessToken); if (teamId != null) { wait = false; @@ -502,17 +502,17 @@ internal static async Task CreateTeamAsync(HttpClient httpClient, string accessT } } - internal static async Task RenewAsync(HttpClient httpClient, string accessToken, Guid groupId) + internal static async Task RenewAsync(PnPConnection connection, string accessToken, Guid groupId) { - await GraphHelper.PostAsync(httpClient, $"v1.0/groups/{groupId}/renew", new { }, accessToken); + await GraphHelper.PostAsync(connection, $"v1.0/groups/{groupId}/renew", new { }, accessToken); } - internal static async Task UpdateAsync(HttpClient httpClient, string accessToken, Microsoft365Group group) + internal static async Task UpdateAsync(PnPConnection connection, string accessToken, Microsoft365Group group) { - return await GraphHelper.PatchAsync(httpClient, accessToken, $"v1.0/groups/{group.Id}", group); + return await GraphHelper.PatchAsync(connection, accessToken, $"v1.0/groups/{group.Id}", group); } - internal static async Task SetVisibilityAsync(HttpClient httpClient, string accessToken, Guid groupId, bool? hideFromAddressLists, bool? hideFromOutlookClients) + internal static async Task SetVisibilityAsync(PnPConnection connection, string accessToken, Guid groupId, bool? hideFromAddressLists, bool? hideFromOutlookClients) { var patchData = new { @@ -526,7 +526,7 @@ internal static async Task SetVisibilityAsync(HttpClient httpClient, string acce { try { - await GraphHelper.PatchAsync(httpClient, accessToken, $"v1.0/groups/{groupId}", patchData); + await GraphHelper.PatchAsync(connection, accessToken, $"v1.0/groups/{groupId}", patchData); retry = false; } @@ -543,71 +543,71 @@ internal static async Task SetVisibilityAsync(HttpClient httpClient, string acce } } - internal static async Task GetGroupSettingsAsync(HttpClient httpClient, string accessToken) + internal static async Task GetGroupSettingsAsync(PnPConnection connection, string accessToken) { - var result = await GraphHelper.GetAsync(httpClient, "v1.0/groupSettings", accessToken, propertyNameCaseInsensitive: true); + var result = await GraphHelper.GetAsync(connection, "v1.0/groupSettings", accessToken, propertyNameCaseInsensitive: true); return result; } - internal static async Task GetGroupSettingsAsync(HttpClient httpClient, string accessToken, string groupId) + internal static async Task GetGroupSettingsAsync(PnPConnection connection, string accessToken, string groupId) { - var result = await GraphHelper.GetAsync(httpClient, $"v1.0/groups/{groupId}/settings", accessToken, propertyNameCaseInsensitive: true); + var result = await GraphHelper.GetAsync(connection, $"v1.0/groups/{groupId}/settings", accessToken, propertyNameCaseInsensitive: true); return result; } - internal static async Task CreateGroupSetting(HttpClient httpClient, string accessToken, dynamic groupSettingObject) + internal static async Task CreateGroupSetting(PnPConnection connection, string accessToken, dynamic groupSettingObject) { var stringContent = new StringContent(JsonSerializer.Serialize(groupSettingObject)); stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - var result = await GraphHelper.PostAsync(httpClient, "v1.0/groupSettings", stringContent, accessToken, propertyNameCaseInsensitive: true); + var result = await GraphHelper.PostAsync(connection, "v1.0/groupSettings", stringContent, accessToken, propertyNameCaseInsensitive: true); return result; } - internal static async Task CreateGroupSetting(HttpClient httpClient, string accessToken, string groupId, dynamic groupSettingObject) + internal static async Task CreateGroupSetting(PnPConnection connection, string accessToken, string groupId, dynamic groupSettingObject) { var stringContent = new StringContent(JsonSerializer.Serialize(groupSettingObject)); stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - var result = await GraphHelper.PostAsync(httpClient, $"v1.0/groups/{groupId}/settings", stringContent, accessToken, propertyNameCaseInsensitive: true); + var result = await GraphHelper.PostAsync(connection, $"v1.0/groups/{groupId}/settings", stringContent, accessToken, propertyNameCaseInsensitive: true); return result; } - internal static async Task UpdateGroupSetting(HttpClient httpClient, string accessToken, string id, dynamic groupSettingObject) + internal static async Task UpdateGroupSetting(PnPConnection connection, string accessToken, string id, dynamic groupSettingObject) { var stringContent = new StringContent(JsonSerializer.Serialize(groupSettingObject)); stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - await GraphHelper.PatchAsync(httpClient, accessToken, stringContent, $"v1.0/groupSettings/{id}"); + await GraphHelper.PatchAsync(connection, accessToken, stringContent, $"v1.0/groupSettings/{id}"); } - internal static async Task UpdateGroupSetting(HttpClient httpClient, string accessToken, string id, string groupId, dynamic groupSettingObject) + internal static async Task UpdateGroupSetting(PnPConnection connection, string accessToken, string id, string groupId, dynamic groupSettingObject) { var stringContent = new StringContent(JsonSerializer.Serialize(groupSettingObject)); stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - await GraphHelper.PatchAsync(httpClient, accessToken, stringContent, $"v1.0/groups/{groupId}/settings/{id}"); + await GraphHelper.PatchAsync(connection, accessToken, stringContent, $"v1.0/groups/{groupId}/settings/{id}"); } - internal static async Task RemoveGroupSetting(HttpClient httpClient, string accessToken, string id) + internal static async Task RemoveGroupSetting(PnPConnection connection, string accessToken, string id) { - await GraphHelper.DeleteAsync(httpClient, $"v1.0/groupSettings/{id}", accessToken); + await GraphHelper.DeleteAsync(connection, $"v1.0/groupSettings/{id}", accessToken); } - internal static async Task RemoveGroupSetting(HttpClient httpClient, string accessToken, string id, string groupId) + internal static async Task RemoveGroupSetting(PnPConnection connection, string accessToken, string id, string groupId) { - await GraphHelper.DeleteAsync(httpClient, $"v1.0/groups/{groupId}/settings/{id}", accessToken); + await GraphHelper.DeleteAsync(connection, $"v1.0/groups/{groupId}/settings/{id}", accessToken); } - internal static async Task GetGroupTemplateSettingsAsync(HttpClient httpClient, string accessToken) + internal static async Task GetGroupTemplateSettingsAsync(PnPConnection connection, string accessToken) { - var result = await GraphHelper.GetAsync(httpClient, "v1.0/groupSettingTemplates", accessToken, propertyNameCaseInsensitive: true); + var result = await GraphHelper.GetAsync(connection, "v1.0/groupSettingTemplates", accessToken, propertyNameCaseInsensitive: true); return result; } - internal static async Task GetGroupTemplateSettingsAsync(HttpClient httpClient, string accessToken, string id) + internal static async Task GetGroupTemplateSettingsAsync(PnPConnection connection, string accessToken, string id) { - var result = await GraphHelper.GetAsync(httpClient, $"v1.0/groupSettingTemplates/{id}", accessToken, propertyNameCaseInsensitive: true); + var result = await GraphHelper.GetAsync(connection, $"v1.0/groupSettingTemplates/{id}", accessToken, propertyNameCaseInsensitive: true); return result; } - internal static async Task SetSensitivityLabelsAsync(HttpClient httpClient, string accessToken, Guid groupId, List assignedLabels) + internal static async Task SetSensitivityLabelsAsync(PnPConnection connection, string accessToken, Guid groupId, List assignedLabels) { var patchData = new { @@ -620,7 +620,7 @@ internal static async Task SetSensitivityLabelsAsync(HttpClient httpClient, stri { try { - await GraphHelper.PatchAsync(httpClient, accessToken, $"v1.0/groups/{groupId}", patchData); + await GraphHelper.PatchAsync(connection, accessToken, $"v1.0/groups/{groupId}", patchData); retry = false; } diff --git a/src/Commands/Utilities/PlannerUtility.cs b/src/Commands/Utilities/PlannerUtility.cs index 92b0c71dc..1f3e91a14 100644 --- a/src/Commands/Utilities/PlannerUtility.cs +++ b/src/Commands/Utilities/PlannerUtility.cs @@ -1,3 +1,4 @@ +using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Model.Graph; using PnP.PowerShell.Commands.Model.Planner; using PnP.PowerShell.Commands.Utilities.REST; @@ -12,19 +13,19 @@ namespace PnP.PowerShell.Commands.Utilities internal static class PlannerUtility { #region Plans - public static async Task> GetPlansAsync(HttpClient httpClient, string accessToken, string groupId, bool resolveDisplayNames) + public static async Task> GetPlansAsync(PnPConnection connection, string accessToken, string groupId, bool resolveDisplayNames) { var returnCollection = new List(); - var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/groups/{groupId}/planner/plans", accessToken); + var collection = await GraphHelper.GetResultCollectionAsync(connection, $"v1.0/groups/{groupId}/planner/plans", accessToken); if (collection != null && collection.Any()) { if (resolveDisplayNames) { foreach (var plan in collection) { - var fullIdentity = await ResolveIdentityAsync(httpClient, accessToken, plan.CreatedBy.User); + var fullIdentity = await ResolveIdentityAsync(connection, accessToken, plan.CreatedBy.User); plan.CreatedBy.User = fullIdentity; - var owner = await ResolveGroupName(httpClient, accessToken, plan.Owner); + var owner = await ResolveGroupName(connection, accessToken, plan.Owner); plan.Owner = owner; returnCollection.Add(plan); } @@ -37,33 +38,33 @@ public static async Task> GetPlansAsync(HttpClient http return returnCollection; } - public static async Task GetPlanAsync(HttpClient httpClient, string accessToken, string planId, bool resolveDisplayNames) + public static async Task GetPlanAsync(PnPConnection connection, string accessToken, string planId, bool resolveDisplayNames) { - var plan = await GraphHelper.GetAsync(httpClient, $"v1.0/planner/plans/{planId}", accessToken); + var plan = await GraphHelper.GetAsync(connection, $"v1.0/planner/plans/{planId}", accessToken); if (resolveDisplayNames) { - plan.CreatedBy.User = await ResolveIdentityAsync(httpClient, accessToken, plan.CreatedBy.User); + plan.CreatedBy.User = await ResolveIdentityAsync(connection, accessToken, plan.CreatedBy.User); } return plan; } - public static async Task CreatePlanAsync(HttpClient httpClient, string accessToken, string groupId, string title) + public static async Task CreatePlanAsync(PnPConnection connection, string accessToken, string groupId, string title) { var stringContent = new StringContent(JsonSerializer.Serialize(new { owner = groupId, title = title })); stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - return await GraphHelper.PostAsync(httpClient, "v1.0/planner/plans", stringContent, accessToken); + return await GraphHelper.PostAsync(connection, "v1.0/planner/plans", stringContent, accessToken); } - public static async Task UpdatePlanAsync(HttpClient httpClient, string accessToken, PlannerPlan plan, string title) + public static async Task UpdatePlanAsync(PnPConnection connection, string accessToken, PlannerPlan plan, string title) { var stringContent = new StringContent(JsonSerializer.Serialize(new { title })); stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - var responseMessage = await GraphHelper.PatchAsync(httpClient, accessToken, stringContent, $"v1.0/planner/plans/{plan.Id}", new Dictionary() { { "IF-MATCH", plan.ETag } }); + var responseMessage = await GraphHelper.PatchAsync(connection, accessToken, stringContent, $"v1.0/planner/plans/{plan.Id}", new Dictionary() { { "IF-MATCH", plan.ETag } }); while (responseMessage.StatusCode == System.Net.HttpStatusCode.PreconditionFailed) { // retrieve the plan again - plan = await GraphHelper.GetAsync(httpClient, $"v1.0/planner/plans/{plan.Id}", accessToken); - responseMessage = await GraphHelper.PatchAsync(httpClient, accessToken, stringContent, $"v1.0/planner/plans/{plan.Id}", new Dictionary() { { "IF-MATCH", plan.ETag } }); + plan = await GraphHelper.GetAsync(connection, $"v1.0/planner/plans/{plan.Id}", accessToken); + responseMessage = await GraphHelper.PatchAsync(connection, accessToken, stringContent, $"v1.0/planner/plans/{plan.Id}", new Dictionary() { { "IF-MATCH", plan.ETag } }); } if (responseMessage.IsSuccessStatusCode) { @@ -73,12 +74,12 @@ public static async Task UpdatePlanAsync(HttpClient httpClient, str return null; } - public static async Task DeletePlanAsync(HttpClient httpClient, string accessToken, string planId) + public static async Task DeletePlanAsync(PnPConnection connection, string accessToken, string planId) { - var plan = await GetPlanAsync(httpClient, accessToken, planId, false); + var plan = await GetPlanAsync(connection, accessToken, planId, false); if (plan != null) { - await GraphHelper.DeleteAsync(httpClient, $"v1.0/planner/plans/{planId}", accessToken, new Dictionary() { { "IF-MATCH", plan.ETag } }); + await GraphHelper.DeleteAsync(connection, $"v1.0/planner/plans/{planId}", accessToken, new Dictionary() { { "IF-MATCH", plan.ETag } }); } } @@ -86,23 +87,23 @@ public static async Task DeletePlanAsync(HttpClient httpClient, string accessTok #region Tasks - public static async Task> GetTasksAsync(HttpClient httpClient, string accessToken, string planId, bool resolveDisplayNames) + public static async Task> GetTasksAsync(PnPConnection connection, string accessToken, string planId, bool resolveDisplayNames) { var returnCollection = new List(); - var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/planner/plans/{planId}/tasks", accessToken); + var collection = await GraphHelper.GetResultCollectionAsync(connection, $"v1.0/planner/plans/{planId}/tasks", accessToken); if (collection != null && collection.Any()) { if (resolveDisplayNames) { foreach (var task in collection) { - var fullIdentity = await ResolveIdentityAsync(httpClient, accessToken, task.CreatedBy.User); + var fullIdentity = await ResolveIdentityAsync(connection, accessToken, task.CreatedBy.User); task.CreatedBy.User = fullIdentity; if (task.Assignments != null) { foreach (var assignment in task.Assignments) { - assignment.Value.AssignedBy.User = await ResolveIdentityAsync(httpClient, accessToken, assignment.Value.AssignedBy.User); + assignment.Value.AssignedBy.User = await ResolveIdentityAsync(connection, accessToken, assignment.Value.AssignedBy.User); } } returnCollection.Add(task); @@ -116,24 +117,24 @@ public static async Task> GetTasksAsync(HttpClient http return returnCollection; } - public static async Task GetTaskAsync(HttpClient httpClient, string accessToken, string taskId, bool resolveDisplayNames, bool includeDetails) + public static async Task GetTaskAsync(PnPConnection connection, string accessToken, string taskId, bool resolveDisplayNames, bool includeDetails) { - var task = await GraphHelper.GetAsync(httpClient, $"v1.0/planner/tasks/{taskId}", accessToken); + var task = await GraphHelper.GetAsync(connection, $"v1.0/planner/tasks/{taskId}", accessToken); if (resolveDisplayNames) { - task.CreatedBy.User = await ResolveIdentityAsync(httpClient, accessToken, task.CreatedBy.User); + task.CreatedBy.User = await ResolveIdentityAsync(connection, accessToken, task.CreatedBy.User); } if (includeDetails) { - var taskDetails = await GetTaskDetailsAsync(httpClient, accessToken, taskId, resolveDisplayNames); + var taskDetails = await GetTaskDetailsAsync(connection, accessToken, taskId, resolveDisplayNames); task.Details = taskDetails; } return task; } - public static async Task GetTaskDetailsAsync(HttpClient httpClient, string accessToken, string taskId, bool resolveDisplayNames) + public static async Task GetTaskDetailsAsync(PnPConnection connection, string accessToken, string taskId, bool resolveDisplayNames) { - var taskDetails = await GraphHelper.GetAsync(httpClient, $"v1.0/planner/tasks/{taskId}/details", accessToken); + var taskDetails = await GraphHelper.GetAsync(connection, $"v1.0/planner/tasks/{taskId}/details", accessToken); if (!resolveDisplayNames) return taskDetails; @@ -151,7 +152,7 @@ public static async Task GetTaskDetailsAsync(HttpClient http { newCheckListItem.LastModifiedBy = new IdentitySet { - User = await ResolveIdentityAsync(httpClient, accessToken, checklistItem.Value.LastModifiedBy.User) + User = await ResolveIdentityAsync(connection, accessToken, checklistItem.Value.LastModifiedBy.User) }; } newItems.Add(checklistItem.Key, newCheckListItem); @@ -161,32 +162,32 @@ public static async Task GetTaskDetailsAsync(HttpClient http return taskDetails; } - public static async Task AddTaskAsync(HttpClient httpClient, string accessToken, PlannerTask task) + public static async Task AddTaskAsync(PnPConnection connection, string accessToken, PlannerTask task) { - return await GraphHelper.PostAsync(httpClient, "v1.0/planner/tasks", task, accessToken); + return await GraphHelper.PostAsync(connection, "v1.0/planner/tasks", task, accessToken); } - public static async Task DeleteTaskAsync(HttpClient httpClient, string accessToken, string taskId) + public static async Task DeleteTaskAsync(PnPConnection connection, string accessToken, string taskId) { - var task = await GraphHelper.GetAsync(httpClient, $"v1.0/planner/tasks/{taskId}", accessToken); + var task = await GraphHelper.GetAsync(connection, $"v1.0/planner/tasks/{taskId}", accessToken); if (task != null) { - await GraphHelper.DeleteAsync(httpClient, $"v1.0/planner/tasks/{taskId}", accessToken, new Dictionary() { { "IF-MATCH", task.ETag } }); + await GraphHelper.DeleteAsync(connection, $"v1.0/planner/tasks/{taskId}", accessToken, new Dictionary() { { "IF-MATCH", task.ETag } }); } } - public static async Task UpdateTaskAsync(HttpClient httpClient, string accessToken, PlannerTask taskToUpdate, PlannerTask task) + public static async Task UpdateTaskAsync(PnPConnection connection, string accessToken, PlannerTask taskToUpdate, PlannerTask task) { - return await GraphHelper.PatchAsync(httpClient, accessToken, $"v1.0/planner/tasks/{taskToUpdate.Id}", task, new Dictionary { { "IF-MATCH", taskToUpdate.ETag } }); + return await GraphHelper.PatchAsync(connection, accessToken, $"v1.0/planner/tasks/{taskToUpdate.Id}", task, new Dictionary { { "IF-MATCH", taskToUpdate.ETag } }); } - public static async Task UpdateTaskDetailsAsync(HttpClient httpClient, string accessToken, PlannerTaskDetails taskToUpdate, string description) + public static async Task UpdateTaskDetailsAsync(PnPConnection connection, string accessToken, PlannerTaskDetails taskToUpdate, string description) { var body = new PlannerTaskDetails { Description = description, }; - await GraphHelper.PatchAsync(httpClient, accessToken, $"v1.0/planner/tasks/{taskToUpdate.Id}/details", body, new Dictionary { { "IF-MATCH", taskToUpdate.ETag } }); + await GraphHelper.PatchAsync(connection, accessToken, $"v1.0/planner/tasks/{taskToUpdate.Id}/details", body, new Dictionary { { "IF-MATCH", taskToUpdate.ETag } }); } #endregion @@ -199,11 +200,11 @@ public static async Task UpdateTaskDetailsAsync(HttpClient httpClient, string ac /// HttpClient instance to use to send out requests /// AccessToken to use to authenticate the request /// PlannerRoster - public static async Task CreateRosterAsync(HttpClient httpClient, string accessToken) + public static async Task CreateRosterAsync(PnPConnection connection, string accessToken) { var stringContent = new StringContent("{ \"@odata.type\": \"#microsoft.graph.plannerRoster\" }"); stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - return await GraphHelper.PostAsync(httpClient, "beta/planner/rosters", stringContent, accessToken); + return await GraphHelper.PostAsync(connection, "beta/planner/rosters", stringContent, accessToken); } /// @@ -213,9 +214,9 @@ public static async Task CreateRosterAsync(HttpClient httpClient, /// HttpClient instance to use to send out requests /// AccessToken to use to authenticate the request /// PlannerRoster - public static async Task GetRosterAsync(HttpClient httpClient, string accessToken, string rosterId) + public static async Task GetRosterAsync(PnPConnection connection, string accessToken, string rosterId) { - return await GraphHelper.GetAsync(httpClient, $"beta/planner/rosters/{rosterId}", accessToken); + return await GraphHelper.GetAsync(connection, $"beta/planner/rosters/{rosterId}", accessToken); } /// @@ -225,9 +226,9 @@ public static async Task GetRosterAsync(HttpClient httpClient, st /// HttpClient instance to use to send out requests /// AccessToken to use to authenticate the request /// HttpResponseMessage - public static async Task DeleteRosterAsync(HttpClient httpClient, string accessToken, string rosterId) + public static async Task DeleteRosterAsync(PnPConnection connection, string accessToken, string rosterId) { - return await GraphHelper.DeleteAsync(httpClient, $"beta/planner/rosters/{rosterId}", accessToken); + return await GraphHelper.DeleteAsync(connection, $"beta/planner/rosters/{rosterId}", accessToken); } /// @@ -238,11 +239,11 @@ public static async Task DeleteRosterAsync(HttpClient httpC /// HttpClient instance to use to send out requests /// AccessToken to use to authenticate the request /// PlannerRoster - public static async Task AddRosterMemberAsync(HttpClient httpClient, string accessToken, string rosterId, string userId) + public static async Task AddRosterMemberAsync(PnPConnection connection, string accessToken, string rosterId, string userId) { var stringContent = new StringContent("{ \"@odata.type\": \"#microsoft.graph.plannerRosterMember\", \"userId\": \"" + userId + "\" }"); stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - return await GraphHelper.PostAsync(httpClient, $"beta/planner/rosters/{rosterId}/members", stringContent, accessToken); + return await GraphHelper.PostAsync(connection, $"beta/planner/rosters/{rosterId}/members", stringContent, accessToken); } /// @@ -253,9 +254,9 @@ public static async Task AddRosterMemberAsync(HttpClient httpClie /// HttpClient instance to use to send out requests /// AccessToken to use to authenticate the request /// HttpResponseMessage - public static async Task RemoveRosterMemberAsync(HttpClient httpClient, string accessToken, string rosterId, string userId) + public static async Task RemoveRosterMemberAsync(PnPConnection connection, string accessToken, string rosterId, string userId) { - return await GraphHelper.DeleteAsync(httpClient, $"beta/planner/rosters/{rosterId}/members/{userId}", accessToken); + return await GraphHelper.DeleteAsync(connection, $"beta/planner/rosters/{rosterId}/members/{userId}", accessToken); } /// @@ -265,10 +266,10 @@ public static async Task RemoveRosterMemberAsync(HttpClient /// HttpClient instance to use to send out requests /// AccessToken to use to authenticate the request /// IEnumerable - public static async Task> GetRosterMembersAsync(HttpClient httpClient, string accessToken, string rosterId) + public static async Task> GetRosterMembersAsync(PnPConnection connection, string accessToken, string rosterId) { var returnCollection = new List(); - var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"beta/planner/rosters/{rosterId}/members", accessToken); + var collection = await GraphHelper.GetResultCollectionAsync(connection, $"beta/planner/rosters/{rosterId}/members", accessToken); if (collection != null && collection.Any()) { returnCollection = collection.ToList(); @@ -283,9 +284,9 @@ public static async Task> GetRosterMembersAsync /// HttpClient instance to use to send out requests /// AccessToken to use to authenticate the request /// PlannerRoster - public static async Task GetRosterPlansByUserAsync(HttpClient httpClient, string accessToken, string userId) + public static async Task GetRosterPlansByUserAsync(PnPConnection connection, string accessToken, string userId) { - return await GraphHelper.GetAsync(httpClient, $"beta/users/{userId}/planner/rosterPlans", accessToken); + return await GraphHelper.GetAsync(connection, $"beta/users/{userId}/planner/rosterPlans", accessToken); } /// @@ -295,9 +296,9 @@ public static async Task GetRosterPlansByUserAsync(HttpClient htt /// HttpClient instance to use to send out requests /// AccessToken to use to authenticate the request /// PlannerRoster - public static async Task GetRosterPlansByRosterAsync(HttpClient httpClient, string accessToken, string rosterId) + public static async Task GetRosterPlansByRosterAsync(PnPConnection connection, string accessToken, string rosterId) { - return await GraphHelper.GetAsync(httpClient, $"beta/planner/rosters/{rosterId}/plans", accessToken); + return await GraphHelper.GetAsync(connection, $"beta/planner/rosters/{rosterId}/plans", accessToken); } #endregion @@ -310,9 +311,9 @@ public static async Task GetRosterPlansByRosterAsync(HttpClient h /// HttpClient instance to use to send out requests /// AccessToken to use to authenticate the request /// PlannerTenantConfig - public static async Task GetPlannerConfigAsync(HttpClient httpClient, string accessToken) + public static async Task GetPlannerConfigAsync(PnPConnection connection, string accessToken) { - var result = await GraphHelper.GetAsync(httpClient, "https://tasks.office.com/taskAPI/tenantAdminSettings/Settings", accessToken); + var result = await GraphHelper.GetAsync(connection, "https://tasks.office.com/taskAPI/tenantAdminSettings/Settings", accessToken); return result; } @@ -322,7 +323,7 @@ public static async Task GetPlannerConfigAsync(HttpClient h /// HttpClient instance to use to send out requests /// AccessToken to use to authenticate the request /// PlannerTenantConfig - public static async Task SetPlannerConfigAsync(HttpClient httpClient, string accessToken, bool? isPlannerAllowed, bool? allowCalendarSharing, bool? allowTenantMoveWithDataLoss, bool? allowTenantMoveWithDataMigration, bool? allowRosterCreation, bool? allowPlannerMobilePushNotifications) + public static async Task SetPlannerConfigAsync(PnPConnection connection, string accessToken, bool? isPlannerAllowed, bool? allowCalendarSharing, bool? allowTenantMoveWithDataLoss, bool? allowTenantMoveWithDataMigration, bool? allowRosterCreation, bool? allowPlannerMobilePushNotifications) { var content = new PlannerTenantConfig { @@ -333,7 +334,7 @@ public static async Task SetPlannerConfigAsync(HttpClient h AllowRosterCreation = allowRosterCreation, AllowPlannerMobilePushNotifications = allowPlannerMobilePushNotifications }; - var result = await GraphHelper.PatchAsync(httpClient, accessToken, "https://tasks.office.com/taskAPI/tenantAdminSettings/Settings", content); + var result = await GraphHelper.PatchAsync(connection, accessToken, "https://tasks.office.com/taskAPI/tenantAdminSettings/Settings", content); return result; } @@ -344,9 +345,9 @@ public static async Task SetPlannerConfigAsync(HttpClient h /// HttpClient instance to use to send out requests /// AccessToken to use to authenticate the request /// PlannerUserPolicy - public static async Task GetPlannerUserPolicyAsync(HttpClient httpClient, string accessToken, string userId) + public static async Task GetPlannerUserPolicyAsync(PnPConnection connection, string accessToken, string userId) { - var result = await GraphHelper.GetAsync(httpClient, $"https://tasks.office.com/taskAPI/tenantAdminSettings/UserPolicy('{userId}')", accessToken); + var result = await GraphHelper.GetAsync(connection, $"https://tasks.office.com/taskAPI/tenantAdminSettings/UserPolicy('{userId}')", accessToken); return result; } @@ -357,19 +358,19 @@ public static async Task GetPlannerUserPolicyAsync(HttpClient /// HttpClient instance to use to send out requests /// AccessToken to use to authenticate the request /// PlannerUserPolicy - public static async Task SetPlannerUserPolicyAsync(HttpClient httpClient, string accessToken, string userId, bool? blockDeleteTasksNotCreatedBySelf) + public static async Task SetPlannerUserPolicyAsync(PnPConnection connection, string accessToken, string userId, bool? blockDeleteTasksNotCreatedBySelf) { var content = new PlannerUserPolicy { BlockDeleteTasksNotCreatedBySelf = blockDeleteTasksNotCreatedBySelf }; - var result = await GraphHelper.PutAsync(httpClient, $"https://tasks.office.com/taskAPI/tenantAdminSettings/UserPolicy('{userId}')", content, accessToken); + var result = await GraphHelper.PutAsync(connection, $"https://tasks.office.com/taskAPI/tenantAdminSettings/UserPolicy('{userId}')", content, accessToken); return result; } #endregion - private static async Task ResolveIdentityAsync(HttpClient httpClient, string accessToken, Identity identity) + private static async Task ResolveIdentityAsync(PnPConnection connection, string accessToken, Identity identity) { if (identity == null) { @@ -377,7 +378,7 @@ private static async Task ResolveIdentityAsync(HttpClient httpClient, } if (identity.DisplayName == null) { - return await GraphHelper.GetAsync(httpClient, $"v1.0/users/{identity.Id}", accessToken); + return await GraphHelper.GetAsync(connection, $"v1.0/users/{identity.Id}", accessToken); } else { @@ -385,9 +386,9 @@ private static async Task ResolveIdentityAsync(HttpClient httpClient, } } - private static async Task ResolveGroupName(HttpClient httpClient, string accessToken, string id) + private static async Task ResolveGroupName(PnPConnection connection, string accessToken, string id) { - var group = await GraphHelper.GetAsync(httpClient, $"v1.0/groups/{id}?$select=displayName", accessToken); + var group = await GraphHelper.GetAsync(connection, $"v1.0/groups/{id}?$select=displayName", accessToken); if (group != null) { return group.DisplayName; @@ -400,45 +401,45 @@ private static async Task ResolveGroupName(HttpClient httpClient, string #region Buckets - public static async Task> GetBucketsAsync(HttpClient httpClient, string accessToken, string planId) + public static async Task> GetBucketsAsync(PnPConnection connection, string accessToken, string planId) { - return await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/planner/plans/{planId}/buckets", accessToken); + return await GraphHelper.GetResultCollectionAsync(connection, $"v1.0/planner/plans/{planId}/buckets", accessToken); } - public static async Task CreateBucketAsync(HttpClient httpClient, string accessToken, string name, string planId) + public static async Task CreateBucketAsync(PnPConnection connection, string accessToken, string name, string planId) { var stringContent = new StringContent(JsonSerializer.Serialize(new { name = name, planId = planId, orderHint = " !" })); stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - return await GraphHelper.PostAsync(httpClient, $"v1.0/planner/buckets", stringContent, accessToken); + return await GraphHelper.PostAsync(connection, $"v1.0/planner/buckets", stringContent, accessToken); } - public static async System.Threading.Tasks.Task RemoveBucketAsync(HttpClient httpClient, string accessToken, string bucketId) + public static async System.Threading.Tasks.Task RemoveBucketAsync(PnPConnection connection, string accessToken, string bucketId) { - var bucket = GraphHelper.GetAsync(httpClient, $"v1.0/planner/buckets/{bucketId}", accessToken).GetAwaiter().GetResult(); + var bucket = GraphHelper.GetAsync(connection, $"v1.0/planner/buckets/{bucketId}", accessToken).GetAwaiter().GetResult(); if (bucket != null) { - await GraphHelper.DeleteAsync(httpClient, $"v1.0/planner/buckets/{bucketId}", accessToken, new Dictionary() { { "IF-MATCH", bucket.ETag } }); + await GraphHelper.DeleteAsync(connection, $"v1.0/planner/buckets/{bucketId}", accessToken, new Dictionary() { { "IF-MATCH", bucket.ETag } }); } } - public static async Task> GetBucketTasksAsync(HttpClient httpClient, string accessToken, string bucketId, bool resolveDisplayNames) + public static async Task> GetBucketTasksAsync(PnPConnection connection, string accessToken, string bucketId, bool resolveDisplayNames) { var returnCollection = new List(); - var collection = await GraphHelper.GetAsync>(httpClient, $"v1.0/planner/buckets/{bucketId}/tasks", accessToken); + var collection = await GraphHelper.GetAsync>(connection, $"v1.0/planner/buckets/{bucketId}/tasks", accessToken); if (collection != null && collection.Items.Any()) { if (resolveDisplayNames) { foreach (var task in collection.Items) { - var fullIdentity = await ResolveIdentityAsync(httpClient, accessToken, task.CreatedBy.User); + var fullIdentity = await ResolveIdentityAsync(connection, accessToken, task.CreatedBy.User); task.CreatedBy.User = fullIdentity; if (task.Assignments != null) { foreach (var assignment in task.Assignments) { - assignment.Value.AssignedBy.User = await ResolveIdentityAsync(httpClient, accessToken, assignment.Value.AssignedBy.User); + assignment.Value.AssignedBy.User = await ResolveIdentityAsync(connection, accessToken, assignment.Value.AssignedBy.User); } } returnCollection.Add(task); @@ -455,13 +456,13 @@ public static async Task> GetBucketTasksAsync(HttpClien { foreach (var task in collection.Items) { - var fullIdentity = await ResolveIdentityAsync(httpClient, accessToken, task.CreatedBy.User); + var fullIdentity = await ResolveIdentityAsync(connection, accessToken, task.CreatedBy.User); task.CreatedBy.User = fullIdentity; if (task.Assignments != null) { foreach (var assignment in task.Assignments) { - assignment.Value.AssignedBy.User = await ResolveIdentityAsync(httpClient, accessToken, assignment.Value.AssignedBy.User); + assignment.Value.AssignedBy.User = await ResolveIdentityAsync(connection, accessToken, assignment.Value.AssignedBy.User); } } returnCollection.Add(task); @@ -477,14 +478,14 @@ public static async Task> GetBucketTasksAsync(HttpClien return returnCollection; } - public static async Task UpdateBucketAsync(HttpClient httpClient, string accessToken, string name, string bucketId) + public static async Task UpdateBucketAsync(PnPConnection connection, string accessToken, string name, string bucketId) { - var bucket = await GraphHelper.GetAsync(httpClient, $"v1.0/planner/buckets/{bucketId}", accessToken); + var bucket = await GraphHelper.GetAsync(connection, $"v1.0/planner/buckets/{bucketId}", accessToken); if (bucket != null) { var stringContent = new StringContent(JsonSerializer.Serialize(new { name = name })); stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - return await GraphHelper.PatchAsync(httpClient, accessToken, $"v1.0/planner/buckets/{bucketId}", stringContent, new Dictionary() { { "IF-MATCH", bucket.ETag } }); + return await GraphHelper.PatchAsync(connection, accessToken, $"v1.0/planner/buckets/{bucketId}", stringContent, new Dictionary() { { "IF-MATCH", bucket.ETag } }); } return null; } diff --git a/src/Commands/Utilities/REST/GraphBatch.cs b/src/Commands/Utilities/REST/GraphBatch.cs index eb6324154..712fc2b4b 100644 --- a/src/Commands/Utilities/REST/GraphBatch.cs +++ b/src/Commands/Utilities/REST/GraphBatch.cs @@ -4,6 +4,7 @@ using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; +using PnP.PowerShell.Commands.Base; namespace PnP.PowerShell.Commands.Utilities.REST { @@ -20,7 +21,7 @@ internal static IEnumerable> Chunk(this IEnumerable source, } } - internal static async Task> GetPropertyBatchedAsync(HttpClient httpClient, string accessToken, string[] lookupData, string urlTemplate, string property) + internal static async Task> GetPropertyBatchedAsync(PnPConnection connection, string accessToken, string[] lookupData, string urlTemplate, string property) { Dictionary returnValue = new Dictionary(); @@ -36,7 +37,7 @@ internal static async Task> GetPropertyBatchedAsync(H } var stringContent = new StringContent(JsonSerializer.Serialize(batch)); stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - var result = await GraphHelper.PostAsync(httpClient, "v1.0/$batch", stringContent, accessToken); + var result = await GraphHelper.PostAsync(connection, "v1.0/$batch", stringContent, accessToken); if (result.Responses != null && result.Responses.Any()) { foreach (var response in result.Responses) @@ -52,7 +53,7 @@ internal static async Task> GetPropertyBatchedAsync(H return returnValue; } - internal static async Task>> GetObjectCollectionBatchedAsync(HttpClient httpClient, string accessToken, string[] lookupData, string urlTemplate) + internal static async Task>> GetObjectCollectionBatchedAsync(PnPConnection connection, string accessToken, string[] lookupData, string urlTemplate) { Dictionary> returnValue = new Dictionary>(); @@ -68,7 +69,7 @@ internal static async Task>> GetObjectCollecti } var stringContent = new StringContent(JsonSerializer.Serialize(batch)); stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - var result = await GraphHelper.PostAsync(httpClient, "v1.0/$batch", stringContent, accessToken); + var result = await GraphHelper.PostAsync(connection, "v1.0/$batch", stringContent, accessToken); if (result.Responses != null && result.Responses.Any()) { var options = new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase}; diff --git a/src/Commands/Utilities/REST/GraphHelper.cs b/src/Commands/Utilities/REST/GraphHelper.cs index 4a37d1a93..a7370f1ba 100644 --- a/src/Commands/Utilities/REST/GraphHelper.cs +++ b/src/Commands/Utilities/REST/GraphHelper.cs @@ -12,6 +12,9 @@ namespace PnP.PowerShell.Commands.Utilities.REST { + /// + /// Helper class that aids in making calls towards the Microsoft Graph API + /// internal static class GraphHelper { public static bool TryGetGraphException(HttpResponseMessage responseMessage, out GraphException exception) @@ -39,7 +42,7 @@ public static bool TryGetGraphException(HttpResponseMessage responseMessage, out } } - private static HttpRequestMessage GetMessage(string url, HttpMethod method, string accessToken, HttpContent content = null, IDictionary additionalHeaders = null) + private static HttpRequestMessage GetMessage(string url, HttpMethod method, PnPConnection connection, string accessToken, HttpContent content = null, IDictionary additionalHeaders = null) { if (url.StartsWith("/")) { @@ -48,7 +51,7 @@ private static HttpRequestMessage GetMessage(string url, HttpMethod method, stri var message = new HttpRequestMessage(); message.Method = method; - message.RequestUri = !url.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ? new Uri($"https://{PnPConnection.Current.GraphEndPoint}/{url}") : new Uri(url); + message.RequestUri = !url.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ? new Uri($"https://{connection.GraphEndPoint}/{url}") : new Uri(url); message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); if (additionalHeaders != null) { @@ -66,39 +69,39 @@ private static HttpRequestMessage GetMessage(string url, HttpMethod method, stri return message; } - public static async Task GetAsync(HttpClient httpClient, string url, string accessToken, IDictionary additionalHeaders = null) + public static async Task GetAsync(PnPConnection connection, string url, string accessToken, IDictionary additionalHeaders = null) { - var message = GetMessage(url, HttpMethod.Get, accessToken, null, additionalHeaders); - return await SendMessageAsync(httpClient, message, accessToken); + var message = GetMessage(url, HttpMethod.Get, connection, accessToken, null, additionalHeaders); + return await SendMessageAsync(connection, message, accessToken); } - public static async Task GetResponseAsync(HttpClient httpClient, string url, string accessToken) + public static async Task GetResponseAsync(PnPConnection connection, string url, string accessToken) { - var message = GetMessage(url, HttpMethod.Get, accessToken); - return await GetResponseMessageAsync(httpClient, message); + var message = GetMessage(url, HttpMethod.Get, connection, accessToken); + return await GetResponseMessageAsync(connection, message); } /// /// Queries the provided URL and looks for the NextLink in the results to fetch all the results /// /// Type of object to cast the response items to - /// HttpClient to use to make the request + /// Connection to use to make the request /// Url to query /// AccessToken to use for authorizing the request /// Policy indicating the CamlCase that should be applied when mapping results to typed objects /// Indicates if the response be mapped to the typed object ignoring different casing /// List with objects of type T returned by the request - public static async Task> GetResultCollectionAsync(HttpClient httpClient, string url, string accessToken, bool camlCasePolicy = true, bool propertyNameCaseInsensitive = false) + public static async Task> GetResultCollectionAsync(PnPConnection connection, string url, string accessToken, bool camlCasePolicy = true, bool propertyNameCaseInsensitive = false) { var results = new List(); - var request = await GetAsync>(httpClient, url, accessToken, camlCasePolicy, propertyNameCaseInsensitive); + var request = await GetAsync>(connection, url, accessToken, camlCasePolicy, propertyNameCaseInsensitive); if (request.Items.Any()) { results.AddRange(request.Items); while (!string.IsNullOrEmpty(request.NextLink)) { - request = await GetAsync>(httpClient, request.NextLink, accessToken, camlCasePolicy, propertyNameCaseInsensitive); + request = await GetAsync>(connection, request.NextLink, accessToken, camlCasePolicy, propertyNameCaseInsensitive); if (request.Items.Any()) { results.AddRange(request.Items); @@ -113,15 +116,15 @@ public static async Task> GetResultCollectionAsync(HttpClient /// Queries the provided URL and returns the results as typed objects. It does NOT follow NextLink pages, use GetResultCollectionAsync instead for that. /// /// Type of object to cast the response items to - /// HttpClient to use to make the request + /// Connection to use to make the request /// Url to query /// AccessToken to use for authorizing the request /// Policy indicating the CamlCase that should be applied when mapping results to typed objects /// Indicates if the response be mapped to the typed object ignoring different casing /// List with objects of type T returned by the request - public static async Task GetAsync(HttpClient httpClient, string url, string accessToken, bool camlCasePolicy = true, bool propertyNameCaseInsensitive = false) + public static async Task GetAsync(PnPConnection connection, string url, string accessToken, bool camlCasePolicy = true, bool propertyNameCaseInsensitive = false) { - var stringContent = await GetAsync(httpClient, url, accessToken); + var stringContent = await GetAsync(connection, url, accessToken); if (stringContent != null) { var options = new JsonSerializerOptions { IgnoreNullValues = true }; @@ -147,28 +150,28 @@ public static async Task GetAsync(HttpClient httpClient, string url, strin return default(T); } - public static async Task PostAsync(HttpClient httpClient, string url, string accessToken, HttpContent content, IDictionary additionalHeaders = null) + public static async Task PostAsync(PnPConnection connection, string url, string accessToken, HttpContent content, IDictionary additionalHeaders = null) { - var message = GetMessage(url, HttpMethod.Post, accessToken, content, additionalHeaders); - return await GetResponseMessageAsync(httpClient, message); + var message = GetMessage(url, HttpMethod.Post, connection, accessToken, content, additionalHeaders); + return await GetResponseMessageAsync(connection, message); } - public static async Task PutAsync(HttpClient httpClient, string url, string accessToken, HttpContent content, IDictionary additionalHeaders = null) + public static async Task PutAsync(PnPConnection connection, string url, string accessToken, HttpContent content, IDictionary additionalHeaders = null) { - var message = GetMessage(url, HttpMethod.Put, accessToken, content, additionalHeaders); - return await GetResponseMessageAsync(httpClient, message); + var message = GetMessage(url, HttpMethod.Put, connection, accessToken, content, additionalHeaders); + return await GetResponseMessageAsync(connection, message); } #region DELETE - public static async Task DeleteAsync(HttpClient httpClient, string url, string accessToken, IDictionary additionalHeaders = null) + public static async Task DeleteAsync(PnPConnection connection, string url, string accessToken, IDictionary additionalHeaders = null) { - var message = GetMessage(url, HttpMethod.Delete, accessToken, null, additionalHeaders); - return await GetResponseMessageAsync(httpClient, message); + var message = GetMessage(url, HttpMethod.Delete, connection, accessToken, null, additionalHeaders); + return await GetResponseMessageAsync(connection, message); } - public static async Task DeleteAsync(HttpClient httpClient, string url, string accessToken, bool camlCasePolicy = true, IDictionary additionalHeaders = null) + public static async Task DeleteAsync(PnPConnection connection, string url, string accessToken, bool camlCasePolicy = true, IDictionary additionalHeaders = null) { - var response = await DeleteAsync(httpClient, url, accessToken, additionalHeaders); + var response = await DeleteAsync(connection, url, accessToken, additionalHeaders); if (response.IsSuccessStatusCode) { var stringContent = await response.Content.ReadAsStringAsync(); @@ -195,7 +198,7 @@ public static async Task DeleteAsync(HttpClient httpClient, string url, st #endregion #region PATCH - public static async Task PatchAsync(HttpClient httpClient, string accessToken, string url, T content, IDictionary additionalHeaders = null, bool camlCasePolicy = true) + public static async Task PatchAsync(PnPConnection connection, string accessToken, string url, T content, IDictionary additionalHeaders = null, bool camlCasePolicy = true) { var serializerSettings = new JsonSerializerOptions() { IgnoreNullValues = true }; if (camlCasePolicy) @@ -205,11 +208,11 @@ public static async Task PatchAsync(HttpClient httpClient, string accessTo var requestContent = new StringContent(JsonSerializer.Serialize(content, serializerSettings)); requestContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); #if NETFRAMEWORK - var message = GetMessage(url, new HttpMethod("PATCH"), accessToken, requestContent, additionalHeaders); + var message = GetMessage(url, new HttpMethod("PATCH"), connection, accessToken, requestContent, additionalHeaders); #else - var message = GetMessage(url, HttpMethod.Patch, accessToken, requestContent, additionalHeaders); + var message = GetMessage(url, HttpMethod.Patch, connection, accessToken, requestContent, additionalHeaders); #endif - var returnValue = await SendMessageAsync(httpClient, message, accessToken); + var returnValue = await SendMessageAsync(connection, message, accessToken); if (!string.IsNullOrEmpty(returnValue)) { return JsonSerializer.Deserialize(returnValue); @@ -220,14 +223,14 @@ public static async Task PatchAsync(HttpClient httpClient, string accessTo } } - public static async Task PatchAsync(HttpClient httpClient, string accessToken, string url, HttpContent content, IDictionary additionalHeaders = null) + public static async Task PatchAsync(PnPConnection connection, string accessToken, string url, HttpContent content, IDictionary additionalHeaders = null) { #if NETFRAMEWORK - var message = GetMessage(url, new HttpMethod("PATCH"), accessToken, content, additionalHeaders); + var message = GetMessage(url, new HttpMethod("PATCH"), connection, accessToken, content, additionalHeaders); #else - var message = GetMessage(url, HttpMethod.Patch, accessToken, content, additionalHeaders); + var message = GetMessage(url, HttpMethod.Patch, connection, accessToken, content, additionalHeaders); #endif - var returnValue = await SendMessageAsync(httpClient, message, accessToken); + var returnValue = await SendMessageAsync(connection, message, accessToken); if (!string.IsNullOrEmpty(returnValue)) { return JsonSerializer.Deserialize(returnValue); @@ -238,27 +241,27 @@ public static async Task PatchAsync(HttpClient httpClient, string accessTo } } - public static async Task PatchAsync(HttpClient httpClient, string accessToken, HttpContent content, string url, IDictionary additionalHeaders = null) + public static async Task PatchAsync(PnPConnection connection, string accessToken, HttpContent content, string url, IDictionary additionalHeaders = null) { #if NETFRAMEWORK - var message = GetMessage(url, new HttpMethod("PATCH"), accessToken, content, additionalHeaders); + var message = GetMessage(url, new HttpMethod("PATCH"), connection, accessToken, content, additionalHeaders); #else - var message = GetMessage(url, HttpMethod.Patch, accessToken, content, additionalHeaders); + var message = GetMessage(url, HttpMethod.Patch, connection, accessToken, content, additionalHeaders); #endif - return await GetResponseMessageAsync(httpClient, message); + return await GetResponseMessageAsync(connection, message); } #endregion - public static async Task PostAsync(HttpClient httpClient, string url, HttpContent content, string accessToken, IDictionary additionalHeaders = null, bool propertyNameCaseInsensitive = false) + public static async Task PostAsync(PnPConnection connection, string url, HttpContent content, string accessToken, IDictionary additionalHeaders = null, bool propertyNameCaseInsensitive = false) { - return await PostInternalAsync(httpClient, url, accessToken, content, additionalHeaders, propertyNameCaseInsensitive); + return await PostInternalAsync(connection, url, accessToken, content, additionalHeaders, propertyNameCaseInsensitive); } - public static async Task PutAsync(HttpClient httpClient, string url, string accessToken, HttpContent content, IDictionary additionalHeaders = null) + public static async Task PutAsync(PnPConnection connection, string url, string accessToken, HttpContent content, IDictionary additionalHeaders = null) { - var message = GetMessage(url, HttpMethod.Put, accessToken, content, additionalHeaders); - var stringContent = await SendMessageAsync(httpClient, message, accessToken); + var message = GetMessage(url, HttpMethod.Put, connection, accessToken, content, additionalHeaders); + var stringContent = await SendMessageAsync(connection, message, accessToken); if (stringContent != null) { try @@ -273,23 +276,23 @@ public static async Task PutAsync(HttpClient httpClient, string url, strin return default; } - public static async Task PostAsync(HttpClient httpClient, string url, T content, string accessToken) + public static async Task PostAsync(PnPConnection connection, string url, T content, string accessToken) { var requestContent = new StringContent(JsonSerializer.Serialize(content, new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase })); requestContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - return await PostInternalAsync(httpClient, url, accessToken, requestContent); + return await PostInternalAsync(connection, url, accessToken, requestContent); } - public static async Task PostAsync(HttpClient httpClient, string url, string accessToken) + public static async Task PostAsync(PnPConnection connection, string url, string accessToken) { - return await PostInternalAsync(httpClient, url, accessToken, null); + return await PostInternalAsync(connection, url, accessToken, null); } - private static async Task PostInternalAsync(HttpClient httpClient, string url, string accessToken, HttpContent content, IDictionary additionalHeaders = null, bool propertyNameCaseInsensitive = false) + private static async Task PostInternalAsync(PnPConnection connection, string url, string accessToken, HttpContent content, IDictionary additionalHeaders = null, bool propertyNameCaseInsensitive = false) { - var message = GetMessage(url, HttpMethod.Post, accessToken, content, additionalHeaders); - var stringContent = await SendMessageAsync(httpClient, message, accessToken); + var message = GetMessage(url, HttpMethod.Post, connection, accessToken, content, additionalHeaders); + var stringContent = await SendMessageAsync(connection, message, accessToken); if (stringContent != null) { try @@ -304,12 +307,12 @@ private static async Task PostInternalAsync(HttpClient httpClient, string return default; } - public static async Task PutAsync(HttpClient httpClient, string url, T content, string accessToken) + public static async Task PutAsync(PnPConnection connection, string url, T content, string accessToken) { var requestContent = new StringContent(JsonSerializer.Serialize(content, new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase })); requestContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - var message = GetMessage(url, HttpMethod.Put, accessToken, requestContent); - var returnValue = await SendMessageAsync(httpClient, message, accessToken); + var message = GetMessage(url, HttpMethod.Put, connection, accessToken, requestContent); + var returnValue = await SendMessageAsync(connection, message, accessToken); if (!string.IsNullOrEmpty(returnValue)) { return JsonSerializer.Deserialize(returnValue, new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); @@ -320,22 +323,22 @@ public static async Task PutAsync(HttpClient httpClient, string url, T con } } - public static async Task DeleteAsync(HttpClient httpClient, string url, string accessToken) + public static async Task DeleteAsync(PnPConnection connection, string url, string accessToken) { - var message = GetMessage(url, HttpMethod.Delete, accessToken); - var response = await GetResponseMessageAsync(httpClient, message); + var message = GetMessage(url, HttpMethod.Delete, connection, accessToken); + var response = await GetResponseMessageAsync(connection, message); return response; } - private static async Task SendMessageAsync(HttpClient httpClient, HttpRequestMessage message, string accessToken) + private static async Task SendMessageAsync(PnPConnection connection, HttpRequestMessage message, string accessToken) { - var response = await httpClient.SendAsync(message); + var response = await connection.HttpClient.SendAsync(message); while (response.StatusCode == (HttpStatusCode)429) { // throttled var retryAfter = response.Headers.RetryAfter; await Task.Delay(retryAfter.Delta.Value.Seconds * 1000); - response = await httpClient.SendAsync(CloneMessage(message)); + response = await connection.HttpClient.SendAsync(CloneMessage(message)); } if (response.IsSuccessStatusCode) { @@ -350,15 +353,15 @@ private static async Task SendMessageAsync(HttpClient httpClient, HttpRe } } - public static async Task GetResponseMessageAsync(HttpClient httpClient, HttpRequestMessage message) + public static async Task GetResponseMessageAsync(PnPConnection connection, HttpRequestMessage message) { - var response = await httpClient.SendAsync(message); + var response = await connection.HttpClient.SendAsync(message); while (response.StatusCode == (HttpStatusCode)429) { // throttled var retryAfter = response.Headers.RetryAfter; await Task.Delay(retryAfter.Delta.Value.Seconds * 1000); - response = await httpClient.SendAsync(CloneMessage(message)); + response = await connection.HttpClient.SendAsync(CloneMessage(message)); } // Validate if the response was successful, if not throw an exception diff --git a/src/Commands/Utilities/ServiceHealthUtility.cs b/src/Commands/Utilities/ServiceHealthUtility.cs index 3f7f9612f..51537fc10 100644 --- a/src/Commands/Utilities/ServiceHealthUtility.cs +++ b/src/Commands/Utilities/ServiceHealthUtility.cs @@ -1,8 +1,7 @@ +using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Model.ServiceHealth; using PnP.PowerShell.Commands.Utilities.REST; using System.Collections.Generic; -using System.Linq; -using System.Net.Http; using System.Threading.Tasks; namespace PnP.PowerShell.Commands.Utilities @@ -14,12 +13,12 @@ internal static class ServiceHealthUtility /// /// Retrieves all Service Update Messages /// - /// HttpClient to use for retrieval of the data + /// Connection to use for retrieval of the data /// AccessToken to use for authentication of the request /// List with objects - public static async Task> GetServiceUpdateMessagesAsync(HttpClient httpClient, string accessToken) + public static async Task> GetServiceUpdateMessagesAsync(PnPConnection connection, string accessToken) { - var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/admin/serviceAnnouncement/messages", accessToken); + var collection = await GraphHelper.GetResultCollectionAsync(connection, $"v1.0/admin/serviceAnnouncement/messages", accessToken); return collection; } @@ -27,12 +26,12 @@ public static async Task> GetServiceUpdateMess /// Retrieves a specific Service Update Message /// /// Identifier of the service update message - /// HttpClient to use for retrieval of the data + /// Connection to use for retrieval of the data /// AccessToken to use for authentication of the request /// containing the requested information - public static async Task GetServiceUpdateMessageByIdAsync(string id, HttpClient httpClient, string accessToken) + public static async Task GetServiceUpdateMessageByIdAsync(string id, PnPConnection connection, string accessToken) { - var item = await GraphHelper.GetAsync(httpClient, $"v1.0/admin/serviceAnnouncement/messages/{id}", accessToken); + var item = await GraphHelper.GetAsync(connection, $"v1.0/admin/serviceAnnouncement/messages/{id}", accessToken); return item; } @@ -43,9 +42,9 @@ public static async Task GetServiceUpdateMessageByIdAsync( /// HttpClient to use for updating the data /// AccessToken to use for authentication of the request /// Boolean indicating whether the request succeeded - public static async Task SetServiceUpdateMessageAsReadByIdAsync(string id, HttpClient httpClient, string accessToken) + public static async Task SetServiceUpdateMessageAsReadByIdAsync(string id, PnPConnection connection, string accessToken) { - return await SetServiceUpdateMessageAsReadByIdAsync(new [] { id }, httpClient, accessToken); + return await SetServiceUpdateMessageAsReadByIdAsync(new [] { id }, connection, accessToken); } /// @@ -55,10 +54,10 @@ public static async Task SetServiceUpdateMessageAsReadByIdAsync(string id, /// HttpClient to use for updating the data /// AccessToken to use for authentication of the request /// Boolean indicating whether the request succeeded - public static async Task SetServiceUpdateMessageAsReadByIdAsync(string[] id, HttpClient httpClient, string accessToken) + public static async Task SetServiceUpdateMessageAsReadByIdAsync(string[] id, PnPConnection connection, string accessToken) { var postBody = new PnP.PowerShell.Commands.Model.ServiceHealth.ServiceUpdateMessageReadStatusBody { MessageIds = id }; - var item = await GraphHelper.PostAsync(httpClient, "v1.0/admin/serviceAnnouncement/messages/markRead", postBody, accessToken); + var item = await GraphHelper.PostAsync(connection, "v1.0/admin/serviceAnnouncement/messages/markRead", postBody, accessToken); return true; } @@ -69,9 +68,9 @@ public static async Task SetServiceUpdateMessageAsReadByIdAsync(string[] i /// HttpClient to use for updating the data /// AccessToken to use for authentication of the request /// Boolean indicating whether the request succeeded - public static async Task SetServiceUpdateMessageAsUnreadByIdAsync(string id, HttpClient httpClient, string accessToken) + public static async Task SetServiceUpdateMessageAsUnreadByIdAsync(string id, PnPConnection connection, string accessToken) { - return await SetServiceUpdateMessageAsUnreadByIdAsync(new [] { id }, httpClient, accessToken); + return await SetServiceUpdateMessageAsUnreadByIdAsync(new [] { id }, connection, accessToken); } /// @@ -81,10 +80,10 @@ public static async Task SetServiceUpdateMessageAsUnreadByIdAsync(string i /// HttpClient to use for updating the data /// AccessToken to use for authentication of the request /// Boolean indicating whether the request succeeded - public static async Task SetServiceUpdateMessageAsUnreadByIdAsync(string[] id, HttpClient httpClient, string accessToken) + public static async Task SetServiceUpdateMessageAsUnreadByIdAsync(string[] id, PnPConnection connection, string accessToken) { var postBody = new PnP.PowerShell.Commands.Model.ServiceHealth.ServiceUpdateMessageReadStatusBody { MessageIds = id }; - var item = await GraphHelper.PostAsync(httpClient, "v1.0/admin/serviceAnnouncement/messages/markUnread", postBody, accessToken); + var item = await GraphHelper.PostAsync(connection, "v1.0/admin/serviceAnnouncement/messages/markUnread", postBody, accessToken); return true; } @@ -95,9 +94,9 @@ public static async Task SetServiceUpdateMessageAsUnreadByIdAsync(string[] /// HttpClient to use for updating the data /// AccessToken to use for authentication of the request /// Boolean indicating whether the request succeeded - public static async Task SetServiceUpdateMessageAsArchivedByIdAsync(string id, HttpClient httpClient, string accessToken) + public static async Task SetServiceUpdateMessageAsArchivedByIdAsync(string id, PnPConnection connection, string accessToken) { - return await SetServiceUpdateMessageAsArchivedByIdAsync(new [] { id }, httpClient, accessToken); + return await SetServiceUpdateMessageAsArchivedByIdAsync(new [] { id }, connection, accessToken); } /// @@ -107,10 +106,10 @@ public static async Task SetServiceUpdateMessageAsArchivedByIdAsync(string /// HttpClient to use for updating the data /// AccessToken to use for authentication of the request /// Boolean indicating whether the request succeeded - public static async Task SetServiceUpdateMessageAsArchivedByIdAsync(string[] id, HttpClient httpClient, string accessToken) + public static async Task SetServiceUpdateMessageAsArchivedByIdAsync(string[] id, PnPConnection connection, string accessToken) { var postBody = new PnP.PowerShell.Commands.Model.ServiceHealth.ServiceUpdateMessageReadStatusBody { MessageIds = id }; - var item = await GraphHelper.PostAsync(httpClient, "v1.0/admin/serviceAnnouncement/messages/archive", postBody, accessToken); + var item = await GraphHelper.PostAsync(connection, "v1.0/admin/serviceAnnouncement/messages/archive", postBody, accessToken); return true; } @@ -121,9 +120,9 @@ public static async Task SetServiceUpdateMessageAsArchivedByIdAsync(string /// HttpClient to use for updating the data /// AccessToken to use for authentication of the request /// Boolean indicating whether the request succeeded - public static async Task SetServiceUpdateMessageAsUnarchivedByIdAsync(string id, HttpClient httpClient, string accessToken) + public static async Task SetServiceUpdateMessageAsUnarchivedByIdAsync(string id, PnPConnection connection, string accessToken) { - return await SetServiceUpdateMessageAsUnarchivedByIdAsync(new [] { id }, httpClient, accessToken); + return await SetServiceUpdateMessageAsUnarchivedByIdAsync(new [] { id }, connection, accessToken); } /// @@ -133,10 +132,10 @@ public static async Task SetServiceUpdateMessageAsUnarchivedByIdAsync(stri /// HttpClient to use for updating the data /// AccessToken to use for authentication of the request /// Boolean indicating whether the request succeeded - public static async Task SetServiceUpdateMessageAsUnarchivedByIdAsync(string[] id, HttpClient httpClient, string accessToken) + public static async Task SetServiceUpdateMessageAsUnarchivedByIdAsync(string[] id, PnPConnection connection, string accessToken) { var postBody = new PnP.PowerShell.Commands.Model.ServiceHealth.ServiceUpdateMessageReadStatusBody { MessageIds = id }; - var item = await GraphHelper.PostAsync(httpClient, "v1.0/admin/serviceAnnouncement/messages/unarchive", postBody, accessToken); + var item = await GraphHelper.PostAsync(connection, "v1.0/admin/serviceAnnouncement/messages/unarchive", postBody, accessToken); return true; } @@ -147,9 +146,9 @@ public static async Task SetServiceUpdateMessageAsUnarchivedByIdAsync(stri /// HttpClient to use for updating the data /// AccessToken to use for authentication of the request /// Boolean indicating whether the request succeeded - public static async Task SetServiceUpdateMessageAsFavoriteByIdAsync(string id, HttpClient httpClient, string accessToken) + public static async Task SetServiceUpdateMessageAsFavoriteByIdAsync(string id, PnPConnection connection, string accessToken) { - return await SetServiceUpdateMessageAsFavoriteByIdAsync(new [] { id }, httpClient, accessToken); + return await SetServiceUpdateMessageAsFavoriteByIdAsync(new [] { id }, connection, accessToken); } /// @@ -159,10 +158,10 @@ public static async Task SetServiceUpdateMessageAsFavoriteByIdAsync(string /// HttpClient to use for updating the data /// AccessToken to use for authentication of the request /// Boolean indicating whether the request succeeded - public static async Task SetServiceUpdateMessageAsFavoriteByIdAsync(string[] id, HttpClient httpClient, string accessToken) + public static async Task SetServiceUpdateMessageAsFavoriteByIdAsync(string[] id, PnPConnection connection, string accessToken) { var postBody = new PnP.PowerShell.Commands.Model.ServiceHealth.ServiceUpdateMessageReadStatusBody { MessageIds = id }; - var item = await GraphHelper.PostAsync(httpClient, "v1.0/admin/serviceAnnouncement/messages/favorite", postBody, accessToken); + var item = await GraphHelper.PostAsync(connection, "v1.0/admin/serviceAnnouncement/messages/favorite", postBody, accessToken); return true; } @@ -173,9 +172,9 @@ public static async Task SetServiceUpdateMessageAsFavoriteByIdAsync(string /// HttpClient to use for updating the data /// AccessToken to use for authentication of the request /// Boolean indicating whether the request succeeded - public static async Task SetServiceUpdateMessageAsNotfavoriteByIdAsync(string id, HttpClient httpClient, string accessToken) + public static async Task SetServiceUpdateMessageAsNotfavoriteByIdAsync(string id, PnPConnection connection, string accessToken) { - return await SetServiceUpdateMessageAsNotfavoriteByIdAsync(new [] { id }, httpClient, accessToken); + return await SetServiceUpdateMessageAsNotfavoriteByIdAsync(new [] { id }, connection, accessToken); } /// @@ -185,10 +184,10 @@ public static async Task SetServiceUpdateMessageAsNotfavoriteByIdAsync(str /// HttpClient to use for updating the data /// AccessToken to use for authentication of the request /// Boolean indicating whether the request succeeded - public static async Task SetServiceUpdateMessageAsNotfavoriteByIdAsync(string[] id, HttpClient httpClient, string accessToken) + public static async Task SetServiceUpdateMessageAsNotfavoriteByIdAsync(string[] id, PnPConnection connection, string accessToken) { var postBody = new PnP.PowerShell.Commands.Model.ServiceHealth.ServiceUpdateMessageReadStatusBody { MessageIds = id }; - var item = await GraphHelper.PostAsync(httpClient, "v1.0/admin/serviceAnnouncement/messages/unfavorite", postBody, accessToken); + var item = await GraphHelper.PostAsync(connection, "v1.0/admin/serviceAnnouncement/messages/unfavorite", postBody, accessToken); return true; } @@ -199,12 +198,12 @@ public static async Task SetServiceUpdateMessageAsNotfavoriteByIdAsync(str /// /// Retrieves all Service Health Issues /// - /// HttpClient to use for retrieval of the data + /// Connection to use for retrieval of the data /// AccessToken to use for authentication of the request /// List with objects - public static async Task> GetServiceHealthIssuesAsync(HttpClient httpClient, string accessToken) + public static async Task> GetServiceHealthIssuesAsync(PnPConnection connection, string accessToken) { - var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/admin/serviceAnnouncement/issues", accessToken); + var collection = await GraphHelper.GetResultCollectionAsync(connection, $"v1.0/admin/serviceAnnouncement/issues", accessToken); return collection; } @@ -212,12 +211,12 @@ public static async Task> GetServiceHealthIssues /// Retrieves a specific Service Health Issue /// /// Identifier of the service health issue - /// HttpClient to use for retrieval of the data + /// Connection to use for retrieval of the data /// AccessToken to use for authentication of the request /// containing the requested information - public static async Task GetServiceHealthIssueByIdAsync(string id, HttpClient httpClient, string accessToken) + public static async Task GetServiceHealthIssueByIdAsync(string id, PnPConnection connection, string accessToken) { - var item = await GraphHelper.GetAsync(httpClient, $"v1.0/admin/serviceAnnouncement/issues/{id}", accessToken); + var item = await GraphHelper.GetAsync(connection, $"v1.0/admin/serviceAnnouncement/issues/{id}", accessToken); return item; } @@ -228,12 +227,12 @@ public static async Task GetServiceHealthIssueByIdAsync(stri /// /// Retrieves the current health of all services /// - /// HttpClient to use for retrieval of the data + /// Connection to use for retrieval of the data /// AccessToken to use for authentication of the request /// List with objects - public static async Task> GetServiceCurrentHealthAsync(HttpClient httpClient, string accessToken) + public static async Task> GetServiceCurrentHealthAsync(PnPConnection connection, string accessToken) { - var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/admin/serviceAnnouncement/healthOverviews", accessToken); + var collection = await GraphHelper.GetResultCollectionAsync(connection, $"v1.0/admin/serviceAnnouncement/healthOverviews", accessToken); return collection; } @@ -241,12 +240,12 @@ public static async Task> GetServiceCurrentHea /// Retrieves the current health of a specific service /// /// Full name of the service, i.e. Microsoft Forms - /// HttpClient to use for retrieval of the data + /// Connection to use for retrieval of the data /// AccessToken to use for authentication of the request /// containing the requested information - public static async Task GetServiceCurrentHealthByIdAsync(string id, HttpClient httpClient, string accessToken) + public static async Task GetServiceCurrentHealthByIdAsync(string id, PnPConnection connection, string accessToken) { - var item = await GraphHelper.GetAsync(httpClient, $"v1.0/admin/serviceAnnouncement/healthOverviews/{id}", accessToken); + var item = await GraphHelper.GetAsync(connection, $"v1.0/admin/serviceAnnouncement/healthOverviews/{id}", accessToken); return item; } diff --git a/src/Commands/Utilities/SiteTemplates.cs b/src/Commands/Utilities/SiteTemplates.cs index e6159ec4f..091a7dab9 100644 --- a/src/Commands/Utilities/SiteTemplates.cs +++ b/src/Commands/Utilities/SiteTemplates.cs @@ -5,6 +5,7 @@ using Microsoft.Online.SharePoint.TenantAdministration; using PnP.PowerShell.Commands.Model.SharePoint; using System.Collections.Generic; +using PnP.PowerShell.Commands.Base; namespace PnP.PowerShell.Commands.Utilities { @@ -18,25 +19,25 @@ internal static class SiteTemplates /// /// Invokes the provided site script on the provided site /// - /// HttpClient that can be used to make HTTP requests + /// Connection that can be used to make HTTP requests /// Access Token that can be used to authorize HTTP requests /// The Site Script to invoke /// The URL of the SharePoint site to invoke the Site Script on /// HttpResponseMessage with the - public static async Task> InvokeSiteScript(HttpClient httpClient, string accessToken, TenantSiteScript script, string siteUrl) + public static async Task> InvokeSiteScript(PnPConnection connection, string accessToken, TenantSiteScript script, string siteUrl) { - return await InvokeSiteScript(httpClient, accessToken, script.Content, siteUrl); + return await InvokeSiteScript(connection, accessToken, script.Content, siteUrl); } /// /// Invokes the provided site script on the provided site /// - /// HttpClient that can be used to make HTTP requests + /// Connection that can be used to make HTTP requests /// Access Token that can be used to authorize HTTP requests /// The Site Script content to invoke /// The URL of the SharePoint site to invoke the Site Script on /// - public static async Task> InvokeSiteScript(HttpClient httpClient, string accessToken, string scriptContent, string siteUrl) + public static async Task> InvokeSiteScript(PnPConnection connection, string accessToken, string scriptContent, string siteUrl) { // Properly encode the contents of the provided site script var escapedScript = Regex.Replace(scriptContent.Replace("\\\"", "\\\\\\\""), "(?> I postBody.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); // Execute the request to apply the site script - var results = await GraphHelper.PostAsync>(httpClient, $"{siteUrl.TrimEnd('/')}/_api/Microsoft.Sharepoint.Utilities.WebTemplateExtensions.SiteScriptUtility.ExecuteTemplateScript()", postBody, accessToken, new Dictionary{{ "Accept", "application/json" }}); + var results = await GraphHelper.PostAsync>(connection, $"{siteUrl.TrimEnd('/')}/_api/Microsoft.Sharepoint.Utilities.WebTemplateExtensions.SiteScriptUtility.ExecuteTemplateScript()", postBody, accessToken, new Dictionary{{ "Accept", "application/json" }}); return results; } diff --git a/src/Commands/Utilities/TeamsUtility.cs b/src/Commands/Utilities/TeamsUtility.cs index eba4cc0ef..533166528 100644 --- a/src/Commands/Utilities/TeamsUtility.cs +++ b/src/Commands/Utilities/TeamsUtility.cs @@ -24,25 +24,25 @@ internal static class TeamsUtility private const int PageSize = 100; #region Team - public static async Task> GetGroupsWithTeamAsync(HttpClient httpClient, string accessToken) + public static async Task> GetGroupsWithTeamAsync(PnPConnection connection, string accessToken) { - var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"beta/groups?$filter=resourceProvisioningOptions/Any(x:x eq 'Team')&$select=Id,DisplayName,MailNickName,Description,Visibility&$top={PageSize}", accessToken); + var collection = await GraphHelper.GetResultCollectionAsync(connection, $"beta/groups?$filter=resourceProvisioningOptions/Any(x:x eq 'Team')&$select=Id,DisplayName,MailNickName,Description,Visibility&$top={PageSize}", accessToken); return collection.ToList(); } - public static async Task GetGroupWithTeamAsync(HttpClient httpClient, string accessToken, string mailNickname) + public static async Task GetGroupWithTeamAsync(PnPConnection connection, string accessToken, string mailNickname) { - return await GraphHelper.GetAsync(httpClient, $"beta/groups?$filter=(resourceProvisioningOptions/Any(x:x eq 'Team') and mailNickname eq '{mailNickname}')&$select=Id,DisplayName,MailNickName,Description,Visibility", accessToken); + return await GraphHelper.GetAsync(connection, $"beta/groups?$filter=(resourceProvisioningOptions/Any(x:x eq 'Team') and mailNickname eq '{mailNickname}')&$select=Id,DisplayName,MailNickName,Description,Visibility", accessToken); } - public static async Task> GetTeamsAsync(string accessToken, HttpClient httpClient) + public static async Task> GetTeamsAsync(string accessToken, PnPConnection connection) { List teams = new List(); - var groups = await GetGroupsWithTeamAsync(httpClient, accessToken); + var groups = await GetGroupsWithTeamAsync(connection, accessToken); foreach (var group in groups) { - Team team = await ParseTeamJsonAsync(accessToken, httpClient, group.Id); + Team team = await ParseTeamJsonAsync(accessToken, connection, group.Id); if (team != null) { @@ -55,12 +55,12 @@ public static async Task> GetTeamsAsync(string accessToken, HttpClien return teams; } - public static async Task GetTeamAsync(string accessToken, HttpClient httpClient, string groupId) + public static async Task GetTeamAsync(string accessToken, PnPConnection connection, string groupId) { // get the group - var group = await GraphHelper.GetAsync(httpClient, $"v1.0/groups/{groupId}?$select=Id,DisplayName,MailNickName,Description,Visibility", accessToken); + var group = await GraphHelper.GetAsync(connection, $"v1.0/groups/{groupId}?$select=Id,DisplayName,MailNickName,Description,Visibility", accessToken); - Team team = await ParseTeamJsonAsync(accessToken, httpClient, group.Id); + Team team = await ParseTeamJsonAsync(accessToken, connection, group.Id); if (team != null) { team.DisplayName = group.DisplayName; @@ -74,11 +74,11 @@ public static async Task GetTeamAsync(string accessToken, HttpClient httpC } } - public static async Task DeleteTeamAsync(string accessToken, HttpClient httpClient, string groupId) + public static async Task DeleteTeamAsync(string accessToken, PnPConnection connection, string groupId) { - return await GraphHelper.DeleteAsync(httpClient, $"v1.0/groups/{groupId}", accessToken); + return await GraphHelper.DeleteAsync(connection, $"v1.0/groups/{groupId}", accessToken); } - public static async Task CloneTeamAsync(string accessToken, HttpClient httpClient, string groupId, TeamCloneInformation teamClone) + public static async Task CloneTeamAsync(string accessToken, PnPConnection connection, string groupId, TeamCloneInformation teamClone) { StringContent content = new StringContent(JsonSerializer.Serialize( new { displayName = teamClone.DisplayName , classification = teamClone.Classification , @@ -88,14 +88,14 @@ public static async Task CloneTeamAsync(string accessToken, partsToClone = String.Join(",", teamClone.PartsToClone) })); content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - return await GraphHelper.PostAsync(httpClient, $"v1.0/teams/{groupId}/clone", accessToken, content); + return await GraphHelper.PostAsync(connection, $"v1.0/teams/{groupId}/clone", accessToken, content); } - private static async Task ParseTeamJsonAsync(string accessToken, HttpClient httpClient, string groupId) + private static async Task ParseTeamJsonAsync(string accessToken, PnPConnection connection, string groupId) { // Get Settings try { - var team = await GraphHelper.GetAsync(httpClient, $"v1.0/teams/{groupId}", accessToken, false, true); + var team = await GraphHelper.GetAsync(connection, $"v1.0/teams/{groupId}", accessToken, false, true); if (team != null) { team.GroupId = groupId; @@ -121,7 +121,7 @@ private static async Task ParseTeamJsonAsync(string accessToken, HttpClien } } - public static async Task NewTeamAsync(string accessToken, HttpClient httpClient, string groupId, string displayName, string description, string classification, string mailNickname, GroupVisibility visibility, TeamCreationInformation teamCI, string[] owners, string[] members, Guid[] sensitivityLabels, TeamsTemplateType templateType = TeamsTemplateType.None, TeamResourceBehaviorOptions?[] resourceBehaviorOptions = null) + public static async Task NewTeamAsync(string accessToken, PnPConnection connection, string groupId, string displayName, string description, string classification, string mailNickname, GroupVisibility visibility, TeamCreationInformation teamCI, string[] owners, string[] members, Guid[] sensitivityLabels, TeamsTemplateType templateType = TeamsTemplateType.None, TeamResourceBehaviorOptions?[] resourceBehaviorOptions = null) { Group group = null; Team returnTeam = null; @@ -129,7 +129,7 @@ public static async Task NewTeamAsync(string accessToken, HttpClient httpC // Create the Group if (string.IsNullOrEmpty(groupId)) { - group = await CreateGroupAsync(accessToken, httpClient, displayName, description, classification, mailNickname, visibility, owners, sensitivityLabels, templateType, resourceBehaviorOptions); + group = await CreateGroupAsync(accessToken, connection, displayName, description, classification, mailNickname, visibility, owners, sensitivityLabels, templateType, resourceBehaviorOptions); bool wait = true; int iterations = 0; while (wait) @@ -138,7 +138,7 @@ public static async Task NewTeamAsync(string accessToken, HttpClient httpC try { - var createdGroup = await GraphHelper.GetAsync(httpClient, $"v1.0/groups/{group.Id}", accessToken); + var createdGroup = await GraphHelper.GetAsync(connection, $"v1.0/groups/{group.Id}", accessToken); if (!string.IsNullOrEmpty(createdGroup.DisplayName)) { wait = false; @@ -159,7 +159,7 @@ public static async Task NewTeamAsync(string accessToken, HttpClient httpC } else { - group = await GraphHelper.GetAsync(httpClient, $"v1.0/groups/{groupId}", accessToken); + group = await GraphHelper.GetAsync(connection, $"v1.0/groups/{groupId}", accessToken); if (group == null) { throw new PSArgumentException($"Cannot find group with id {groupId}"); @@ -176,10 +176,10 @@ public static async Task NewTeamAsync(string accessToken, HttpClient httpC { try { - var teamSettings = await GraphHelper.PutAsync(httpClient, $"v1.0/groups/{group.Id}/team", team, accessToken); + var teamSettings = await GraphHelper.PutAsync(connection, $"v1.0/groups/{group.Id}/team", team, accessToken); if (teamSettings != null) { - returnTeam = await GetTeamAsync(accessToken, httpClient, group.Id); + returnTeam = await GetTeamAsync(accessToken, connection, group.Id); } retry = false; } @@ -219,7 +219,7 @@ public static async Task NewTeamAsync(string accessToken, HttpClient httpC var ownersAndMembers = BatchUtility.Chunk(teamOwnersAndMembers, 200); foreach (var chunk in ownersAndMembers) { - await GraphHelper.PostAsync(httpClient, $"v1.0/teams/{group.Id}/members/add", new { values = chunk.ToList() }, accessToken); + await GraphHelper.PostAsync(connection, $"v1.0/teams/{group.Id}/members/add", new { values = chunk.ToList() }, accessToken); } } @@ -237,14 +237,14 @@ internal static string GetUserGraphUrlForUPN(string upn) return $"users/{escapedUpn}"; } - private static async Task CreateGroupAsync(string accessToken, HttpClient httpClient, string displayName, string description, string classification, string mailNickname, GroupVisibility visibility, string[] owners, Guid[] sensitivityLabels, TeamsTemplateType templateType = TeamsTemplateType.None, TeamResourceBehaviorOptions?[] resourceBehaviorOptions = null) + private static async Task CreateGroupAsync(string accessToken, PnPConnection connection, string displayName, string description, string classification, string mailNickname, GroupVisibility visibility, string[] owners, Guid[] sensitivityLabels, TeamsTemplateType templateType = TeamsTemplateType.None, TeamResourceBehaviorOptions?[] resourceBehaviorOptions = null) { // When creating a group, we always need an owner, thus we'll try to define it from the passed in owners array string ownerId = null; if (owners != null && owners.Length > 0) { // Owner(s) have been provided, use the first owner as the initial owner. The other owners will be added later. - var user = await GraphHelper.GetAsync(httpClient, $"v1.0/{GetUserGraphUrlForUPN(owners[0])}?$select=Id", accessToken); + var user = await GraphHelper.GetAsync(connection, $"v1.0/{GetUserGraphUrlForUPN(owners[0])}?$select=Id", accessToken); if (user != null) { @@ -254,7 +254,7 @@ private static async Task CreateGroupAsync(string accessToken, HttpClient else { // Unable to find the owner by its user principal name, try looking for it on its email address - var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/users?$filter=mail eq '{owners[0]}'&$select=Id", accessToken); + var collection = await GraphHelper.GetResultCollectionAsync(connection, $"v1.0/users?$filter=mail eq '{owners[0]}'&$select=Id", accessToken); if (collection != null && collection.Any()) { // User found on its email address @@ -272,7 +272,7 @@ private static async Task CreateGroupAsync(string accessToken, HttpClient if (contextSettings.Type != Framework.Utilities.Context.ClientContextType.AzureADCertificate) { // A delegate context is available, make the user part of the delegate token the owner - var user = await GraphHelper.GetAsync(httpClient, "v1.0/me?$select=Id", accessToken); + var user = await GraphHelper.GetAsync(connection, "v1.0/me?$select=Id", accessToken); if (user != null) { @@ -289,7 +289,7 @@ private static async Task CreateGroupAsync(string accessToken, HttpClient Description = description, Classification = classification, MailEnabled = true, - MailNickname = mailNickname ?? await CreateAliasAsync(httpClient, accessToken), + MailNickname = mailNickname ?? await CreateAliasAsync(connection, accessToken), GroupTypes = new List() { "Unified" }, SecurityEnabled = false, Visibility = visibility == GroupVisibility.NotSpecified ? GroupVisibility.Private : visibility @@ -347,7 +347,7 @@ private static async Task CreateGroupAsync(string accessToken, HttpClient } try { - return await GraphHelper.PostAsync(httpClient, "v1.0/groups", group, accessToken); + return await GraphHelper.PostAsync(connection, "v1.0/groups", group, accessToken); } catch (GraphException ex) { @@ -362,7 +362,7 @@ private static async Task CreateGroupAsync(string accessToken, HttpClient } } - private static async Task CreateAliasAsync(HttpClient httpClient, string accessToken) + private static async Task CreateAliasAsync(PnPConnection connection, string accessToken) { var guid = Guid.NewGuid().ToString(); var teamName = string.Empty; @@ -370,7 +370,7 @@ private static async Task CreateAliasAsync(HttpClient httpClient, string do { var teamNameTemp = $"msteams_{guid.Substring(0, 8)}{guid.Substring(9, 4)}"; - var collection = await GraphHelper.GetAsync>(httpClient, $"v1.0/groups?$filter=groupTypes/any(c:c+eq+'Unified') and (mailNickname eq '{teamNameTemp}')", accessToken); + var collection = await GraphHelper.GetAsync>(connection, $"v1.0/groups?$filter=groupTypes/any(c:c+eq+'Unified') and (mailNickname eq '{teamNameTemp}')", accessToken); if (collection != null) { if (!collection.Items.Any()) teamName = teamNameTemp; @@ -380,44 +380,44 @@ private static async Task CreateAliasAsync(HttpClient httpClient, string return teamName; } - public static async Task UpdateTeamAsync(HttpClient httpClient, string accessToken, string groupId, Team team) + public static async Task UpdateTeamAsync(PnPConnection connection, string accessToken, string groupId, Team team) { - return await GraphHelper.PatchAsync(httpClient, accessToken, $"v1.0/teams/{groupId}", team); + return await GraphHelper.PatchAsync(connection, accessToken, $"v1.0/teams/{groupId}", team); } - public static async Task UpdateGroupAsync(HttpClient httpClient, string accessToken, string groupId, Group group) + public static async Task UpdateGroupAsync(PnPConnection connection, string accessToken, string groupId, Group group) { - return await GraphHelper.PatchAsync(httpClient, accessToken, $"v1.0/groups/{groupId}", group); + return await GraphHelper.PatchAsync(connection, accessToken, $"v1.0/groups/{groupId}", group); } - public static async Task SetTeamPictureAsync(HttpClient httpClient, string accessToken, string groupId, byte[] bytes, string contentType) + public static async Task SetTeamPictureAsync(PnPConnection connection, string accessToken, string groupId, byte[] bytes, string contentType) { var byteArrayContent = new ByteArrayContent(bytes); byteArrayContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType); - await GraphHelper.PutAsync(httpClient, $"v1.0/groups/{groupId}/photo/$value", accessToken, byteArrayContent); + await GraphHelper.PutAsync(connection, $"v1.0/groups/{groupId}/photo/$value", accessToken, byteArrayContent); } - public static async Task SetTeamArchivedStateAsync(HttpClient httpClient, string accessToken, string groupId, bool archived, bool? setSiteReadOnly) + public static async Task SetTeamArchivedStateAsync(PnPConnection connection, string accessToken, string groupId, bool archived, bool? setSiteReadOnly) { if (archived) { StringContent content = new StringContent(JsonSerializer.Serialize(setSiteReadOnly.HasValue ? new { shouldSetSpoSiteReadOnlyForMembers = setSiteReadOnly } : null)); content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - return await GraphHelper.PostAsync(httpClient, $"v1.0/teams/{groupId}/archive", accessToken, content); + return await GraphHelper.PostAsync(connection, $"v1.0/teams/{groupId}/archive", accessToken, content); } else { StringContent content = new StringContent(""); content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - return await GraphHelper.PostAsync(httpClient, $"v1.0/teams/{groupId}/unarchive", accessToken, content); + return await GraphHelper.PostAsync(connection, $"v1.0/teams/{groupId}/unarchive", accessToken, content); } } #endregion #region Users - public static async Task AddUserAsync(HttpClient httpClient, string accessToken, string groupId, string upn, string role) + public static async Task AddUserAsync(PnPConnection connection, string accessToken, string groupId, string upn, string role) { - var userIdResult = await GraphHelper.GetAsync(httpClient, $"v1.0/{GetUserGraphUrlForUPN(upn)}?$select=Id", accessToken); + var userIdResult = await GraphHelper.GetAsync(connection, $"v1.0/{GetUserGraphUrlForUPN(upn)}?$select=Id", accessToken); var resultElement = JsonSerializer.Deserialize(userIdResult); if (resultElement.TryGetProperty("id", out JsonElement idProperty)) { @@ -429,11 +429,11 @@ public static async Task AddUserAsync(HttpClient httpClient, string accessToken, var stringContent = new StringContent(JsonSerializer.Serialize(postData)); stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - await GraphHelper.PostAsync(httpClient, $"v1.0/groups/{groupId}/{role.ToLower()}s/$ref", accessToken, stringContent); + await GraphHelper.PostAsync(connection, $"v1.0/groups/{groupId}/{role.ToLower()}s/$ref", accessToken, stringContent); } } - public static async Task AddUsersAsync(HttpClient httpClient, string accessToken, string groupId, string[] upn, string role) + public static async Task AddUsersAsync(PnPConnection connection, string accessToken, string groupId, string[] upn, string role) { var teamChannelMember = new List(); if(upn != null && upn.Length > 0) @@ -447,13 +447,13 @@ public static async Task AddUsersAsync(HttpClient httpClient, string accessToken var chunks = BatchUtility.Chunk(teamChannelMember, 200); foreach (var chunk in chunks.ToList()) { - await GraphHelper.PostAsync(httpClient, $"v1.0/teams/{groupId}/members/add", new { values = chunk.ToList() }, accessToken); + await GraphHelper.PostAsync(connection, $"v1.0/teams/{groupId}/members/add", new { values = chunk.ToList() }, accessToken); } } } } - public static async Task> GetUsersAsync(HttpClient httpClient, string accessToken, string groupId, string role) + public static async Task> GetUsersAsync(PnPConnection connection, string accessToken, string groupId, string role) { var selectedRole = role != null ? role.ToLower() : null; var owners = new List(); @@ -461,7 +461,7 @@ public static async Task> GetUsersAsync(HttpClient httpClient, string var members = new List(); if (selectedRole != "guest") { - owners = (await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/groups/{groupId}/owners?$select=Id,displayName,userPrincipalName,userType", accessToken)).Select(t => new User() + owners = (await GraphHelper.GetResultCollectionAsync(connection, $"v1.0/groups/{groupId}/owners?$select=Id,displayName,userPrincipalName,userType", accessToken)).Select(t => new User() { Id = t.Id, DisplayName = t.DisplayName, @@ -471,7 +471,7 @@ public static async Task> GetUsersAsync(HttpClient httpClient, string } if (selectedRole != "owner") { - var users = (await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/groups/{groupId}/members?$select=Id,displayName,userPrincipalName,userType", accessToken)); + var users = (await GraphHelper.GetResultCollectionAsync(connection, $"v1.0/groups/{groupId}/members?$select=Id,displayName,userPrincipalName,userType", accessToken)); HashSet hashSet = new HashSet(owners.Select(u => u.Id)); foreach (var user in users) { @@ -510,12 +510,12 @@ public static async Task> GetUsersAsync(HttpClient httpClient, string return finalList; } - public static async Task> GetUsersAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string role) + public static async Task> GetUsersAsync(PnPConnection connection, string accessToken, string groupId, string channelId, string role) { List users = new List(); var selectedRole = role != null ? role.ToLower() : null; - var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/members", accessToken); + var collection = await GraphHelper.GetResultCollectionAsync(connection, $"v1.0/teams/{groupId}/channels/{channelId}/members", accessToken); if (collection != null && collection.Any()) { users.AddRange(collection.Select(m => new User() { DisplayName = m.DisplayName, Id = m.UserId, UserPrincipalName = m.Email, UserType = m.Roles.Count > 0 ? m.Roles[0].ToLower() : "" })); @@ -531,34 +531,34 @@ public static async Task> GetUsersAsync(HttpClient httpClient, } } - public static async Task DeleteUserAsync(HttpClient httpClient, string accessToken, string groupId, string upn, string role) + public static async Task DeleteUserAsync(PnPConnection connection, string accessToken, string groupId, string upn, string role) { - var user = await GraphHelper.GetAsync(httpClient, $"v1.0/{GetUserGraphUrlForUPN(upn)}?$select=Id", accessToken); + var user = await GraphHelper.GetAsync(connection, $"v1.0/{GetUserGraphUrlForUPN(upn)}?$select=Id", accessToken); if (user != null) { // check if the user is an owner - var owners = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/groups/{groupId}/owners?$select=Id", accessToken); + var owners = await GraphHelper.GetResultCollectionAsync(connection, $"v1.0/groups/{groupId}/owners?$select=Id", accessToken); if (owners.Any() && owners.FirstOrDefault(u => u.Id.Equals(user.Id, StringComparison.OrdinalIgnoreCase)) != null) { if (owners.Count() == 1) { throw new PSInvalidOperationException("Last owner cannot be removed"); } - await GraphHelper.DeleteAsync(httpClient, $"v1.0/groups/{groupId}/owners/{user.Id}/$ref", accessToken); + await GraphHelper.DeleteAsync(connection, $"v1.0/groups/{groupId}/owners/{user.Id}/$ref", accessToken); } if (!role.Equals("owner", StringComparison.OrdinalIgnoreCase)) { - await GraphHelper.DeleteAsync(httpClient, $"v1.0/groups/{groupId}/members/{user.Id}/$ref", accessToken); + await GraphHelper.DeleteAsync(connection, $"v1.0/groups/{groupId}/members/{user.Id}/$ref", accessToken); } } } - public static async Task> GetTeamUsersWithDisplayNameAsync(HttpClient httpClient, string accessToken, string groupId, string userDisplayName) + public static async Task> GetTeamUsersWithDisplayNameAsync(PnPConnection connection, string accessToken, string groupId, string userDisplayName) { // multiple users can have same display name, so using list var teamUserWithDisplayName = new List(); - teamUserWithDisplayName = (await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/teams/{groupId}/members?$filter=displayname eq '{userDisplayName}'", accessToken)).Select(t => new TeamUser() + teamUserWithDisplayName = (await GraphHelper.GetResultCollectionAsync(connection, $"v1.0/teams/{groupId}/members?$filter=displayname eq '{userDisplayName}'", accessToken)).Select(t => new TeamUser() { Id = t.Id, DisplayName = t.DisplayName, @@ -569,7 +569,7 @@ public static async Task> GetTeamUsersWithDisplayNameAsync(HttpCl return teamUserWithDisplayName; } - public static async Task UpdateTeamUserRole(HttpClient httpClient, string accessToken, string groupId, string teamMemberId, string role) + public static async Task UpdateTeamUserRole(PnPConnection connection, string accessToken, string groupId, string teamMemberId, string role) { var teamUser = new TeamUser { @@ -579,7 +579,7 @@ public static async Task UpdateTeamUserRole(HttpClient httpClient, str var updateUserEndpoint = $"v1.0/teams/{groupId}/members/{teamMemberId}"; - var result = await GraphHelper.PatchAsync(httpClient, accessToken, updateUserEndpoint, teamUser); + var result = await GraphHelper.PatchAsync(connection, accessToken, updateUserEndpoint, teamUser); return result; } @@ -587,22 +587,22 @@ public static async Task UpdateTeamUserRole(HttpClient httpClient, str #endregion #region Channel - public static async Task> GetChannelsAsync(string accessToken, HttpClient httpClient, string groupId) + public static async Task> GetChannelsAsync(string accessToken, PnPConnection connection, string groupId) { - var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/teams/{groupId}/channels", accessToken); + var collection = await GraphHelper.GetResultCollectionAsync(connection, $"v1.0/teams/{groupId}/channels", accessToken); return collection; } - public static async Task GetPrimaryChannelAsync(string accessToken, HttpClient httpClient, string groupId) + public static async Task GetPrimaryChannelAsync(string accessToken, PnPConnection connection, string groupId) { - var collection = await GraphHelper.GetAsync(httpClient, $"v1.0/teams/{groupId}/primaryChannel", accessToken); + var collection = await GraphHelper.GetAsync(connection, $"v1.0/teams/{groupId}/primaryChannel", accessToken); return collection; } - public static async Task DeleteChannelAsync(string accessToken, HttpClient httpClient, string groupId, string channelId) + public static async Task DeleteChannelAsync(string accessToken, PnPConnection connection, string groupId, string channelId) { - return await GraphHelper.DeleteAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}", accessToken); + return await GraphHelper.DeleteAsync(connection, $"v1.0/teams/{groupId}/channels/{channelId}", accessToken); } - public static async Task AddChannelAsync(string accessToken, HttpClient httpClient, string groupId, string displayName, string description, bool isPrivate, string ownerUPN, bool isFavoriteByDefault) + public static async Task AddChannelAsync(string accessToken, PnPConnection connection, string groupId, string displayName, string description, bool isPrivate, string ownerUPN, bool isFavoriteByDefault) { var channel = new TeamChannel() { @@ -616,32 +616,32 @@ public static async Task AddChannelAsync(string accessToken, HttpCl if (isPrivate) { channel.Type = "#Microsoft.Graph.channel"; - var user = await GraphHelper.GetAsync(httpClient, $"v1.0/{GetUserGraphUrlForUPN(ownerUPN)}", accessToken); + var user = await GraphHelper.GetAsync(connection, $"v1.0/{GetUserGraphUrlForUPN(ownerUPN)}", accessToken); channel.Members = new List(); channel.Members.Add(new TeamChannelMember() { Roles = new List { "owner" }, UserIdentifier = $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/users('{user.Id}')" }); - return await GraphHelper.PostAsync(httpClient, $"v1.0/teams/{groupId}/channels", channel, accessToken); + return await GraphHelper.PostAsync(connection, $"v1.0/teams/{groupId}/channels", channel, accessToken); } else { channel.IsFavoriteByDefault = isFavoriteByDefault; - return await GraphHelper.PostAsync(httpClient, $"v1.0/teams/{groupId}/channels", channel, accessToken); + return await GraphHelper.PostAsync(connection, $"v1.0/teams/{groupId}/channels", channel, accessToken); } } - public static async Task PostMessageAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, TeamChannelMessage message) + public static async Task PostMessageAsync(PnPConnection connection, string accessToken, string groupId, string channelId, TeamChannelMessage message) { - await GraphHelper.PostAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/messages", message, accessToken); + await GraphHelper.PostAsync(connection, $"v1.0/teams/{groupId}/channels/{channelId}/messages", message, accessToken); } - public static async Task GetMessageAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string messageId) + public static async Task GetMessageAsync(PnPConnection connection, string accessToken, string groupId, string channelId, string messageId) { - return await GraphHelper.GetAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/messages/{messageId}", accessToken); + return await GraphHelper.GetAsync(connection, $"v1.0/teams/{groupId}/channels/{channelId}/messages/{messageId}", accessToken); } - public static async Task> GetMessagesAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, bool includeDeleted = false) + public static async Task> GetMessagesAsync(PnPConnection connection, string accessToken, string groupId, string channelId, bool includeDeleted = false) { List messages = new List(); - var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/messages", accessToken); + var collection = await GraphHelper.GetResultCollectionAsync(connection, $"v1.0/teams/{groupId}/channels/{channelId}/messages", accessToken); messages.AddRange(collection); if (includeDeleted) @@ -657,9 +657,9 @@ public static async Task> GetMessagesAsync(HttpClient h /// /// List all the replies to a message in a channel of a team. /// - public static async Task> GetMessageRepliesAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string messageId, bool includeDeleted = false) + public static async Task> GetMessageRepliesAsync(PnPConnection connection, string accessToken, string groupId, string channelId, string messageId, bool includeDeleted = false) { - var replies = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/messages/{messageId}/replies", accessToken); + var replies = await GraphHelper.GetResultCollectionAsync(connection, $"v1.0/teams/{groupId}/channels/{channelId}/messages/{messageId}/replies", accessToken); return includeDeleted ? replies.ToList() : replies.Where(r => r.DeletedDateTime.HasValue).ToList(); } @@ -667,14 +667,14 @@ public static async Task> GetMessageRepliesAsync(H /// /// Get a specific reply of a message in a channel of a team. /// - public static async Task GetMessageReplyAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string messageId, string replyId) + public static async Task GetMessageReplyAsync(PnPConnection connection, string accessToken, string groupId, string channelId, string messageId, string replyId) { - return await GraphHelper.GetAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/messages/{messageId}/replies/{replyId}", accessToken); + return await GraphHelper.GetAsync(connection, $"v1.0/teams/{groupId}/channels/{channelId}/messages/{messageId}/replies/{replyId}", accessToken); } - public static async Task UpdateChannelAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, TeamChannel channel) + public static async Task UpdateChannelAsync(PnPConnection connection, string accessToken, string groupId, string channelId, TeamChannel channel) { - return await GraphHelper.PatchAsync(httpClient, accessToken, $"v1.0/teams/{groupId}/channels/{channelId}", channel); + return await GraphHelper.PatchAsync(connection, accessToken, $"v1.0/teams/{groupId}/channels/{channelId}", channel); } #endregion @@ -684,12 +684,12 @@ public static async Task UpdateChannelAsync(HttpClient httpClient, /// Get specific memberbership of user who has access to a certain Microsoft Teams channel. /// /// User channel membership. - public static async Task GetChannelMemberAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string membershipId) + public static async Task GetChannelMemberAsync(PnPConnection connection, string accessToken, string groupId, string channelId, string membershipId) { // Currently the Graph request to get a membership by id fails (v1.0/teams/{groupId}/channels/{channelId}/members/{membershipId}). // This is why the method below is used. - var memberships = await GetChannelMembersAsync(httpClient, accessToken, groupId, channelId); + var memberships = await GetChannelMembersAsync(connection, accessToken, groupId, channelId); return memberships.FirstOrDefault(m => membershipId.Equals(m.Id)); } @@ -697,9 +697,9 @@ public static async Task GetChannelMemberAsync(HttpClient htt /// Get list of all memberships of a certain Microsoft Teams channel. /// /// List of memberships. - public static async Task> GetChannelMembersAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string role = null) + public static async Task> GetChannelMembersAsync(PnPConnection connection, string accessToken, string groupId, string channelId, string role = null) { - var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/members", accessToken); + var collection = await GraphHelper.GetResultCollectionAsync(connection, $"v1.0/teams/{groupId}/channels/{channelId}/members", accessToken); if (!string.IsNullOrEmpty(role)) { @@ -715,7 +715,7 @@ public static async Task> GetChannelMembersAsync( /// /// User role, valid values: Owner, Member /// Added membership. - public static async Task AddChannelMemberAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string upn, string role) + public static async Task AddChannelMemberAsync(PnPConnection connection, string accessToken, string groupId, string channelId, string upn, string role) { var channelMember = new TeamChannelMember { @@ -726,23 +726,23 @@ public static async Task AddChannelMemberAsync(HttpClient htt if (role.Equals("owner", StringComparison.OrdinalIgnoreCase)) channelMember.Roles.Add("owner"); - return await GraphHelper.PostAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/members", channelMember, accessToken); + return await GraphHelper.PostAsync(connection, $"v1.0/teams/{groupId}/channels/{channelId}/members", channelMember, accessToken); } /// /// Remove specified member of a specified Microsoft Teams channel. /// /// True when removal succeeded, else false. - public static async Task DeleteChannelMemberAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string membershipId) + public static async Task DeleteChannelMemberAsync(PnPConnection connection, string accessToken, string groupId, string channelId, string membershipId) { - return await GraphHelper.DeleteAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/members/{membershipId}", accessToken); + return await GraphHelper.DeleteAsync(connection, $"v1.0/teams/{groupId}/channels/{channelId}/members/{membershipId}", accessToken); } /// /// Update the role of a specific member of a Microsoft Teams channel. /// /// Updated membership object. - public static async Task UpdateChannelMemberAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string membershipId, string role) + public static async Task UpdateChannelMemberAsync(PnPConnection connection, string accessToken, string groupId, string channelId, string membershipId, string role) { var channelMember = new TeamChannelMember(); @@ -750,41 +750,41 @@ public static async Task UpdateChannelMemberAsync(HttpClient if (role.Equals("owner", StringComparison.OrdinalIgnoreCase)) channelMember.Roles.Add("owner"); - return await GraphHelper.PatchAsync(httpClient, accessToken, $"v1.0/teams/{groupId}/channels/{channelId}/members/{membershipId}", channelMember); + return await GraphHelper.PatchAsync(connection, accessToken, $"v1.0/teams/{groupId}/channels/{channelId}/members/{membershipId}", channelMember); } - public static async Task GetChannelsFilesFolderAsync(HttpClient httpClient, string accessToken, string groupId, string channelId) + public static async Task GetChannelsFilesFolderAsync(PnPConnection connection, string accessToken, string groupId, string channelId) { - var collection = await GraphHelper.GetAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/filesFolder", accessToken); + var collection = await GraphHelper.GetAsync(connection, $"v1.0/teams/{groupId}/channels/{channelId}/filesFolder", accessToken); return collection; } #endregion #region Tabs - public static async Task> GetTabsAsync(string accessToken, HttpClient httpClient, string groupId, string channelId) + public static async Task> GetTabsAsync(string accessToken, PnPConnection connection, string groupId, string channelId) { - var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/tabs", accessToken); + var collection = await GraphHelper.GetResultCollectionAsync(connection, $"v1.0/teams/{groupId}/channels/{channelId}/tabs", accessToken); return collection; } - public static async Task GetTabAsync(string accessToken, HttpClient httpClient, string groupId, string channelId, string tabId) + public static async Task GetTabAsync(string accessToken, PnPConnection connection, string groupId, string channelId, string tabId) { - return await GraphHelper.GetAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/tabs/{tabId}?$expand=teamsApp", accessToken, propertyNameCaseInsensitive: true); + return await GraphHelper.GetAsync(connection, $"v1.0/teams/{groupId}/channels/{channelId}/tabs/{tabId}?$expand=teamsApp", accessToken, propertyNameCaseInsensitive: true); } - public static async Task DeleteTabAsync(string accessToken, HttpClient httpClient, string groupId, string channelId, string tabId) + public static async Task DeleteTabAsync(string accessToken, PnPConnection connection, string groupId, string channelId, string tabId) { - return await GraphHelper.DeleteAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/tabs/{tabId}", accessToken); + return await GraphHelper.DeleteAsync(connection, $"v1.0/teams/{groupId}/channels/{channelId}/tabs/{tabId}", accessToken); } - public static async Task UpdateTabAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, TeamTab tab) + public static async Task UpdateTabAsync(PnPConnection connection, string accessToken, string groupId, string channelId, TeamTab tab) { tab.Configuration = null; - await GraphHelper.PatchAsync(httpClient, accessToken, $"v1.0/teams/{groupId}/channels/{channelId}/tabs/{tab.Id}", tab); + await GraphHelper.PatchAsync(connection, accessToken, $"v1.0/teams/{groupId}/channels/{channelId}/tabs/{tab.Id}", tab); } - public static async Task AddTabAsync(HttpClient httpClient, string accessToken, string groupId, string channelId, string displayName, TeamTabType tabType, string teamsAppId, string entityId, string contentUrl, string removeUrl, string websiteUrl) + public static async Task AddTabAsync(PnPConnection connection, string accessToken, string groupId, string channelId, string displayName, TeamTabType tabType, string teamsAppId, string entityId, string contentUrl, string removeUrl, string websiteUrl) { TeamTab tab = new TeamTab(); switch (tabType) @@ -897,36 +897,36 @@ public static async Task AddTabAsync(HttpClient httpClient, string acce } tab.DisplayName = displayName; tab.TeamsAppOdataBind = $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/appCatalogs/teamsApps/{tab.TeamsAppId}"; - return await GraphHelper.PostAsync(httpClient, $"v1.0/teams/{groupId}/channels/{channelId}/tabs", tab, accessToken); + return await GraphHelper.PostAsync(connection, $"v1.0/teams/{groupId}/channels/{channelId}/tabs", tab, accessToken); } #endregion #region Apps - public static async Task> GetAppsAsync(string accessToken, HttpClient httpClient) + public static async Task> GetAppsAsync(string accessToken, PnPConnection connection) { - var collection = await GraphHelper.GetResultCollectionAsync(httpClient, $"v1.0/appCatalogs/teamsApps", accessToken); + var collection = await GraphHelper.GetResultCollectionAsync(connection, $"v1.0/appCatalogs/teamsApps", accessToken); return collection; } - public static async Task AddAppAsync(HttpClient httpClient, string accessToken, byte[] bytes) + public static async Task AddAppAsync(PnPConnection connection, string accessToken, byte[] bytes) { var byteArrayContent = new ByteArrayContent(bytes); byteArrayContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/zip"); - var response = await GraphHelper.PostAsync(httpClient, "v1.0/appCatalogs/teamsApps", accessToken, byteArrayContent); + var response = await GraphHelper.PostAsync(connection, "v1.0/appCatalogs/teamsApps", accessToken, byteArrayContent); var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); return JsonSerializer.Deserialize(content, new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); } - public static async Task UpdateAppAsync(HttpClient httpClient, string accessToken, byte[] bytes, string appId) + public static async Task UpdateAppAsync(PnPConnection connection, string accessToken, byte[] bytes, string appId) { var byteArrayContent = new ByteArrayContent(bytes); byteArrayContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/zip"); - return await GraphHelper.PutAsync(httpClient, $"v1.0/appCatalogs/teamsApps/{appId}", accessToken, byteArrayContent); + return await GraphHelper.PutAsync(connection, $"v1.0/appCatalogs/teamsApps/{appId}", accessToken, byteArrayContent); } - public static async Task DeleteAppAsync(HttpClient httpClient, string accessToken, string appId) + public static async Task DeleteAppAsync(PnPConnection connection, string accessToken, string appId) { - return await GraphHelper.DeleteAsync(httpClient, $"v1.0/appCatalogs/teamsApps/{appId}", accessToken); + return await GraphHelper.DeleteAsync(connection, $"v1.0/appCatalogs/teamsApps/{appId}", accessToken); } #endregion } diff --git a/src/Commands/Web/SetWebHeader.cs b/src/Commands/Web/SetWebHeader.cs index ce2c56915..27d40aa0c 100644 --- a/src/Commands/Web/SetWebHeader.cs +++ b/src/Commands/Web/SetWebHeader.cs @@ -46,7 +46,7 @@ protected override void ExecuteCmdlet() var stringContent = new StringContent("{\"relativeLogoUrl\":\"" + SiteLogoUrl + "\",\"type\":0,\"aspect\":1}"); stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); CurrentWeb.EnsureProperties(p => p.Url); - var result = GraphHelper.PostAsync(HttpClient, $"{CurrentWeb.Url.TrimEnd('/')}/_api/siteiconmanager/setsitelogo", AccessToken, stringContent).GetAwaiter().GetResult(); + var result = GraphHelper.PostAsync(Connection, $"{CurrentWeb.Url.TrimEnd('/')}/_api/siteiconmanager/setsitelogo", AccessToken, stringContent).GetAwaiter().GetResult(); WriteVerbose($"Response from setsitelogo request: {result.StatusCode}"); } @@ -108,7 +108,7 @@ protected override void ExecuteCmdlet() var stringContent = new StringContent("{" + string.Join(",", setSiteBackgroundImageInstructions) + ",\"type\":2,\"aspect\":0}"); stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); CurrentWeb.EnsureProperties(p => p.Url); - var result = GraphHelper.PostAsync(HttpClient, $"{CurrentWeb.Url.TrimEnd('/')}/_api/siteiconmanager/setsitelogo", AccessToken, stringContent).GetAwaiter().GetResult(); + var result = GraphHelper.PostAsync(Connection, $"{CurrentWeb.Url.TrimEnd('/')}/_api/siteiconmanager/setsitelogo", AccessToken, stringContent).GetAwaiter().GetResult(); WriteVerbose($"Response from setsitelogo request: {result.StatusCode}"); } } From 5abe303ad54c339d808c8a6e1d14b33daedca20a Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Wed, 15 Jun 2022 12:25:17 +0300 Subject: [PATCH 371/458] Update CHANGELOG.md Added contributors --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8db151c42..c40bfddeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -100,6 +100,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Contributors +- Emily Mancini [eemancini] - Jim Duncan [sparkitect] - Arleta Wanat [PowershellScripts] - Yuriy Samorodov [YuriySamorodov] From 467eef28f7f3d879dee0d4f291dbe5fb09c74b46 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 16 Jun 2022 03:42:28 +0000 Subject: [PATCH 372/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index eea70698f..fcbd0aa2a 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -a56428650313f3b7443ab54db645cedae2a3a7e2 \ No newline at end of file +3c7cef650c0fbec375f9de60ac891454647e54f5 \ No newline at end of file diff --git a/version.txt b/version.txt index 23f20f5fb..f545abbfb 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.49 \ No newline at end of file +1.10.50 \ No newline at end of file From 4f7010ae394f948fbe99b040a00b282552acbcf9 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 16 Jun 2022 14:48:28 +0300 Subject: [PATCH 373/458] Fix #1872 - issue with teams enumeration (#1988) --- src/Commands/Teams/GetTeamsChannel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Commands/Teams/GetTeamsChannel.cs b/src/Commands/Teams/GetTeamsChannel.cs index a9c99c312..d828c7d4c 100644 --- a/src/Commands/Teams/GetTeamsChannel.cs +++ b/src/Commands/Teams/GetTeamsChannel.cs @@ -28,7 +28,7 @@ protected override void ExecuteCmdlet() } else { - WriteObject(TeamsUtility.GetChannelsAsync(AccessToken, Connection, groupId).GetAwaiter().GetResult()); + WriteObject(TeamsUtility.GetChannelsAsync(AccessToken, Connection, groupId).GetAwaiter().GetResult(), true); } } else { From 08336320a675df6f39e06c88612cc97595adfe1d Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 16 Jun 2022 14:49:59 +0300 Subject: [PATCH 374/458] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c40bfddeb..7bf94ecdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -91,6 +91,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Add-PnPTeamsUser`, the parameter `-Channel` is now not required. [#1953](https://github.com/pnp/powershell/pull/1953) - Fixed `Get-PnPPlannerTask` throwing an object reference exception for completed tasks [#1956](https://github.com/pnp/powershell/issues/1956) - Fixed `Get-PnPUserOneDriveQuota` returning the maximum possible quota instead of the actual configured quota on a OneDrive for Business site [#1902](https://github.com/pnp/powershell/pull/1902) +- Fixed `Get-PnPTeamsChannel` not working correctly with PowerShell select. [#1988](https://github.com/pnp/powershell/pull/1988) ### Removed From 92445ea249c41ad32b4bbdfad296472e4fa0b543 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 16 Jun 2022 15:00:35 +0300 Subject: [PATCH 375/458] Fix #1971 - issue with site classification parameter ignored (#1989) --- src/Commands/Site/UpdateSiteClassification.cs | 56 +++++++++++++------ 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/src/Commands/Site/UpdateSiteClassification.cs b/src/Commands/Site/UpdateSiteClassification.cs index a49f79ad8..63ce0101c 100644 --- a/src/Commands/Site/UpdateSiteClassification.cs +++ b/src/Commands/Site/UpdateSiteClassification.cs @@ -31,39 +31,61 @@ protected override void ExecuteCmdlet() try { var changed = false; - var settings = PnP.Framework.Graph.SiteClassificationsUtility.GetSiteClassificationsSettings(AccessToken); - if (ParameterSpecified(nameof(Classifications))) + var siteClassificationSettings = PnP.Framework.Graph.SiteClassificationsUtility.GetSiteClassificationsSettings(AccessToken); + + if (ParameterSetName == ParameterSet_SETTINGS) { - if (settings.Classifications != Classifications) + if(siteClassificationSettings.Classifications != Settings.Classifications) + { + siteClassificationSettings.Classifications = Settings.Classifications; + changed = true; + } + if (siteClassificationSettings.DefaultClassification != Settings.DefaultClassification) + { + siteClassificationSettings.DefaultClassification = Settings.DefaultClassification; + changed = true; + } + if (siteClassificationSettings.UsageGuidelinesUrl != Settings.UsageGuidelinesUrl) { - settings.Classifications = Classifications; + siteClassificationSettings.UsageGuidelinesUrl = Settings.UsageGuidelinesUrl; changed = true; } } - if (ParameterSpecified(nameof(DefaultClassification))) + else { - if (settings.Classifications.Contains(DefaultClassification)) + if (ParameterSpecified(nameof(Classifications))) { - if (settings.DefaultClassification != DefaultClassification) + if (siteClassificationSettings.Classifications != Classifications) { - settings.DefaultClassification = DefaultClassification; + siteClassificationSettings.Classifications = Classifications; changed = true; } } - } - if (ParameterSpecified(nameof(UsageGuidelinesUrl))) - { - if (settings.UsageGuidelinesUrl != UsageGuidelinesUrl) + if (ParameterSpecified(nameof(DefaultClassification))) { - settings.UsageGuidelinesUrl = UsageGuidelinesUrl; - changed = true; + if (siteClassificationSettings.Classifications.Contains(DefaultClassification)) + { + if (siteClassificationSettings.DefaultClassification != DefaultClassification) + { + siteClassificationSettings.DefaultClassification = DefaultClassification; + changed = true; + } + } } - } + if (ParameterSpecified(nameof(UsageGuidelinesUrl))) + { + if (siteClassificationSettings.UsageGuidelinesUrl != UsageGuidelinesUrl) + { + siteClassificationSettings.UsageGuidelinesUrl = UsageGuidelinesUrl; + changed = true; + } + } + } if (changed) { - if (settings.Classifications.Contains(settings.DefaultClassification)) + if (siteClassificationSettings.Classifications.Contains(siteClassificationSettings.DefaultClassification)) { - PnP.Framework.Graph.SiteClassificationsUtility.UpdateSiteClassificationsSettings(AccessToken, settings); + PnP.Framework.Graph.SiteClassificationsUtility.UpdateSiteClassificationsSettings(AccessToken, siteClassificationSettings); } else { From c88f604ead151d0cf60ad99afdc229dfcb7b978d Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 16 Jun 2022 15:02:45 +0300 Subject: [PATCH 376/458] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7bf94ecdb..7ee79dbab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -92,6 +92,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Get-PnPPlannerTask` throwing an object reference exception for completed tasks [#1956](https://github.com/pnp/powershell/issues/1956) - Fixed `Get-PnPUserOneDriveQuota` returning the maximum possible quota instead of the actual configured quota on a OneDrive for Business site [#1902](https://github.com/pnp/powershell/pull/1902) - Fixed `Get-PnPTeamsChannel` not working correctly with PowerShell select. [#1988](https://github.com/pnp/powershell/pull/1988) +- Fixed `Update-PnPSiteClassification`, it was ignoring the `Settings` parameter. It will now be processed. [#1989](https://github.com/pnp/powershell/pull/1989) ### Removed From 6bbc6b793f0a0397799b2b71d1eeebe1fa1a0a2d Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Thu, 16 Jun 2022 14:20:21 +0200 Subject: [PATCH 377/458] Excluded the plus sign from getting decoded in `Get-PnPFile` (#1990) * Excluded the plus sign from getting decoded * Added PR reference Co-authored-by: = <=> --- CHANGELOG.md | 1 + src/Commands/Files/GetFile.cs | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ee79dbab..ec5feb56f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -91,6 +91,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Add-PnPTeamsUser`, the parameter `-Channel` is now not required. [#1953](https://github.com/pnp/powershell/pull/1953) - Fixed `Get-PnPPlannerTask` throwing an object reference exception for completed tasks [#1956](https://github.com/pnp/powershell/issues/1956) - Fixed `Get-PnPUserOneDriveQuota` returning the maximum possible quota instead of the actual configured quota on a OneDrive for Business site [#1902](https://github.com/pnp/powershell/pull/1902) +- Fixed `Get-PnPFile` throwing an exception when trying to download a file containing the plus character [#1990](https://github.com/pnp/powershell/pull/1990) - Fixed `Get-PnPTeamsChannel` not working correctly with PowerShell select. [#1988](https://github.com/pnp/powershell/pull/1988) - Fixed `Update-PnPSiteClassification`, it was ignoring the `Settings` parameter. It will now be processed. [#1989](https://github.com/pnp/powershell/pull/1989) diff --git a/src/Commands/Files/GetFile.cs b/src/Commands/Files/GetFile.cs index cce1d642b..5482badf6 100644 --- a/src/Commands/Files/GetFile.cs +++ b/src/Commands/Files/GetFile.cs @@ -2,7 +2,6 @@ using PnP.Core.Model.SharePoint; using PnP.Core.Services; using PnP.Framework.Utilities; -using System; using System.IO; using System.Management.Automation; using System.Threading.Tasks; @@ -68,8 +67,8 @@ protected override void ExecuteCmdlet() } } - // Remove URL decoding from the Url as that will not work - Url = Utilities.UrlUtilities.UrlDecode(Url); + // Remove URL decoding from the Url as that will not work. We will encode the + character specifically, because if that is part of the filename, it needs to stay and not be decoded. + Url = Utilities.UrlUtilities.UrlDecode(Url.Replace("+", "%2B")); var webUrl = CurrentWeb.EnsureProperty(w => w.ServerRelativeUrl); From 499772c325510619684dd0ca0e933aa0b61e4e1c Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 16 Jun 2022 15:23:56 +0300 Subject: [PATCH 378/458] Fix #1092 - issue with orphaned CTs (#1992) --- documentation/Add-PnPFieldToContentType.md | 14 ++++++++++++++ src/Commands/ContentTypes/AddFieldToContentType.cs | 5 ++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/documentation/Add-PnPFieldToContentType.md b/documentation/Add-PnPFieldToContentType.md index b576994cf..dc51b1949 100644 --- a/documentation/Add-PnPFieldToContentType.md +++ b/documentation/Add-PnPFieldToContentType.md @@ -102,6 +102,20 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -UpdateChildren +Specifies whether the field needs to be pushed to child content types. Default value is true. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: True +Accept pipeline input: False +Accept wildcard characters: False +``` + ## RELATED LINKS diff --git a/src/Commands/ContentTypes/AddFieldToContentType.cs b/src/Commands/ContentTypes/AddFieldToContentType.cs index 596b233fe..bcb140fda 100644 --- a/src/Commands/ContentTypes/AddFieldToContentType.cs +++ b/src/Commands/ContentTypes/AddFieldToContentType.cs @@ -22,6 +22,9 @@ public class AddFieldToContentType : PnPWebCmdlet [Parameter(Mandatory = false)] public SwitchParameter Hidden; + [Parameter(Mandatory = false)] + public bool UpdateChildren = true; + protected override void ExecuteCmdlet() { Field field = Field.Field; @@ -45,7 +48,7 @@ protected override void ExecuteCmdlet() var ct = ContentType.GetContentTypeOrWarn(this, CurrentWeb); if (ct != null) { - CurrentWeb.AddFieldToContentType(ct, field, Required, Hidden, true, true, false); + CurrentWeb.AddFieldToContentType(ct, field, Required, Hidden, UpdateChildren, true, false); } } } From e4b2d145951f67be112fbab48579d55b9b2dd133 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 16 Jun 2022 15:25:25 +0300 Subject: [PATCH 379/458] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec5feb56f..a6e97ec08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `-ExemptFromBlockDownloadOfNonViewableFiles` parameter to `Set-PnPList` cmdlet to configure access capabilites for unmanaged devices at list level. [#1973](https://github.com/pnp/powershell/pull/1973) - Added `-PercentComplete`, `-Priority`, `-StartDateTime`, `-DueDateTime` and `-Description` to `Add-PnPPlannerTask` [#1964](https://github.com/pnp/powershell/pull/1964) - Added `Set-PnPContentType` cmdlet to update the properties of the Content Types in a list or a web. [#1981](https://github.com/pnp/powershell/pull/1981) +- Added `-UpdateChildren` parameter to `Add-PnPFieldToContentType` cmdlet. This allows users to skip pushing the fields to child content types. [#1092](https://github.com/pnp/powershell/pull/1992) ### Changed From a0b711ba32e9567d21b2c66a207683b37fbea2d2 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 16 Jun 2022 16:27:27 +0300 Subject: [PATCH 380/458] Fix issue with Azure AD app creation (#1993) --- src/Commands/AzureAD/RegisterAzureADApp.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Commands/AzureAD/RegisterAzureADApp.cs b/src/Commands/AzureAD/RegisterAzureADApp.cs index c5ec2b1a3..602054a8b 100644 --- a/src/Commands/AzureAD/RegisterAzureADApp.cs +++ b/src/Commands/AzureAD/RegisterAzureADApp.cs @@ -592,12 +592,9 @@ private AzureADApp CreateApp(string loginEndPoint, HttpClient httpClient, string } }, requiredResourceAccess = scopesPayload - }; - - var requestContent = new StringContent(JsonSerializer.Serialize(payload)); - requestContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); + }; - var azureApp = RestHelper.PostAsync(httpClient, $"https://{AuthenticationManager.GetGraphEndPoint(AzureEnvironment)}/v1.0/applications", token, requestContent).GetAwaiter().GetResult(); + var azureApp = RestHelper.PostAsync(httpClient, $"https://{AuthenticationManager.GetGraphEndPoint(AzureEnvironment)}/v1.0/applications", token, payload).GetAwaiter().GetResult(); if (azureApp != null) { Host.UI.WriteLine(ConsoleColor.Yellow, Host.UI.RawUI.BackgroundColor, $"App {azureApp.DisplayName} with id {azureApp.AppId} created."); From 24f8563dfa83ad88c33648b8e11b419966106f26 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 16 Jun 2022 16:29:22 +0300 Subject: [PATCH 381/458] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6e97ec08..6fe64b485 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -95,6 +95,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Get-PnPFile` throwing an exception when trying to download a file containing the plus character [#1990](https://github.com/pnp/powershell/pull/1990) - Fixed `Get-PnPTeamsChannel` not working correctly with PowerShell select. [#1988](https://github.com/pnp/powershell/pull/1988) - Fixed `Update-PnPSiteClassification`, it was ignoring the `Settings` parameter. It will now be processed. [#1989](https://github.com/pnp/powershell/pull/1989) +- Fixed `Register-PnPAzureADApp` issue with app creation after the connection related changes. [#1993](https://github.com/pnp/powershell/pull/1993) ### Removed From bb7664c824ae1b2e79b8e6c2578e9d4574b328dc Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Thu, 16 Jun 2022 21:13:51 +0200 Subject: [PATCH 382/458] Update Test-PnPMicrosoft365GroupAliasIsUsed.md (#1995) --- documentation/Test-PnPMicrosoft365GroupAliasIsUsed.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Test-PnPMicrosoft365GroupAliasIsUsed.md b/documentation/Test-PnPMicrosoft365GroupAliasIsUsed.md index b2580843d..592a8aa73 100644 --- a/documentation/Test-PnPMicrosoft365GroupAliasIsUsed.md +++ b/documentation/Test-PnPMicrosoft365GroupAliasIsUsed.md @@ -10,7 +10,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Test-PnPMicrosoft365Gro # Test-PnPMicrosoft365GroupAliasIsUsed ## SYNOPSIS -Tests if a given alias is already used used +Tests if a given alias is already used. ## SYNTAX From 60c0f7481bd9444ad7f71cf23224bf0309a8d9f2 Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Thu, 16 Jun 2022 21:14:25 +0200 Subject: [PATCH 383/458] Update Set-PnPWikiPageContent.md (#1996) --- documentation/Set-PnPWikiPageContent.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/documentation/Set-PnPWikiPageContent.md b/documentation/Set-PnPWikiPageContent.md index 8949976cb..1671f6f47 100644 --- a/documentation/Set-PnPWikiPageContent.md +++ b/documentation/Set-PnPWikiPageContent.md @@ -27,9 +27,24 @@ Set-PnPWikiPageContent -Path -ServerRelativePageUrl ``` ## DESCRIPTION +This cmdlet updates the content of the specified wikipage to the value specified either in a string or a file. ## EXAMPLES +### EXAMPLE 1 +```powershell +Set-PnPWikiPageContent -ServerRelativePageUrl /sites/PnPWikiCollection/SitePages/OurWikiPage.aspx -Path .\sampleblog.html +``` +Sets the content of OurWikiPage to the content of sampleblog.html file. + +### EXAMPLE 2 +```powershell +$htmlContent = "
test
" +Set-PnPWikiPageContent -ServerRelativePageUrl /sites/PnPWikiCollection/SitePages/OurWikiPage.aspx -Content $htmlContent +``` + +Sets the content of OurWikiPage as "test". + ## PARAMETERS ### -Connection From cafcb4b8b327a7f0b7b52e9a802aa396be5dec4b Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 16 Jun 2022 22:28:50 +0300 Subject: [PATCH 384/458] Fix #1023 - issue with Get-PnPFileVersion filtering (#1997) --- src/Commands/Files/GetFileVersion.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Commands/Files/GetFileVersion.cs b/src/Commands/Files/GetFileVersion.cs index d210899ad..2fdff4045 100644 --- a/src/Commands/Files/GetFileVersion.cs +++ b/src/Commands/Files/GetFileVersion.cs @@ -39,7 +39,7 @@ protected override void ExecuteCmdlet() { var versions = file.Versions; ClientContext.ExecuteQueryRetry(); - WriteObject(versions); + WriteObject(versions, true); } } } From c7ed07e2d5a6e4c70f678be5e04739c0ffc62979 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 16 Jun 2022 22:30:56 +0300 Subject: [PATCH 385/458] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fe64b485..de759860d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,6 +96,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Get-PnPTeamsChannel` not working correctly with PowerShell select. [#1988](https://github.com/pnp/powershell/pull/1988) - Fixed `Update-PnPSiteClassification`, it was ignoring the `Settings` parameter. It will now be processed. [#1989](https://github.com/pnp/powershell/pull/1989) - Fixed `Register-PnPAzureADApp` issue with app creation after the connection related changes. [#1993](https://github.com/pnp/powershell/pull/1993) +- Fixed `Get-PnPFileVersion` not able to correctly use piping on the returned object. [#1997](https://github.com/pnp/powershell/pull/1997) ### Removed From e49a3e1bd731363ad93dfd75831892ddbb18212a Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Thu, 16 Jun 2022 21:40:25 +0200 Subject: [PATCH 386/458] Added optional `-IncludeAllLists` to `Get-PnPSiteScriptFromWeb` (#1987) * Added optional `-IncludeAllLists` to `Get-PnPSiteScriptFromWeb` which will include the JSON definition of all custom lists of the current site in the output * Adding to syntax * Added PR reference Co-authored-by: = <=> Co-authored-by: Gautam Sheth --- CHANGELOG.md | 1 + documentation/Get-PnPSiteScriptFromWeb.md | 75 +++++++++++-------- .../SiteDesigns/GetSiteScriptFromWeb.cs | 26 ++++++- 3 files changed, 67 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de759860d..2b1fdf5e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `-ExemptFromBlockDownloadOfNonViewableFiles` parameter to `Set-PnPList` cmdlet to configure access capabilites for unmanaged devices at list level. [#1973](https://github.com/pnp/powershell/pull/1973) - Added `-PercentComplete`, `-Priority`, `-StartDateTime`, `-DueDateTime` and `-Description` to `Add-PnPPlannerTask` [#1964](https://github.com/pnp/powershell/pull/1964) - Added `Set-PnPContentType` cmdlet to update the properties of the Content Types in a list or a web. [#1981](https://github.com/pnp/powershell/pull/1981) +- Added optional `-IncludeAllLists` to `Get-PnPSiteScriptFromWeb` which will include the JSON definition of all custom lists of the current site in the output [#1987](https://github.com/pnp/powershell/pull/1987) - Added `-UpdateChildren` parameter to `Add-PnPFieldToContentType` cmdlet. This allows users to skip pushing the fields to child content types. [#1092](https://github.com/pnp/powershell/pull/1992) ### Changed diff --git a/documentation/Get-PnPSiteScriptFromWeb.md b/documentation/Get-PnPSiteScriptFromWeb.md index 589cd4f8b..28d614f78 100644 --- a/documentation/Get-PnPSiteScriptFromWeb.md +++ b/documentation/Get-PnPSiteScriptFromWeb.md @@ -32,6 +32,13 @@ Get-PnPSiteScriptFromWeb [-Url ] [-Lists ] [-IncludeBranding] [-Connection ] [] ``` +### All lists +```powershell +Get-PnPSiteScriptFromWeb [-Url ] [-IncludeAllLists] [-IncludeBranding] [-IncludeLinksToExportedItems] + [-IncludeRegionalSettings] [-IncludeSiteExternalSharingCapability] [-IncludeTheme] + [-Connection ] [] +``` + ## DESCRIPTION This command allows a Site Script to be generated off of an existing site on your tenant. You need to provide at least one of the optional Include or Lists arguments. If you omit the URL, the Site Script will be created from the site to which you are connected. @@ -39,50 +46,43 @@ This command allows a Site Script to be generated off of an existing site on you ### EXAMPLE 1 ```powershell +Get-PnPSiteScriptFromWeb -IncludeAll +``` + +Returns the generated Site Script JSON containing all supported components from the currently connected to site + +### EXAMPLE 2 +```powershell Get-PnPSiteScriptFromWeb -Url "https://contoso.sharepoint.com/sites/teamsite" -IncludeAll ``` Returns the generated Site Script JSON containing all supported components from the site at the provided Url -### EXAMPLE 2 +### EXAMPLE 3 ```powershell Get-PnPSiteScriptFromWeb -Url "https://contoso.sharepoint.com/sites/teamsite" -IncludeAll -Lists "Shared Documents","Lists\MyList" ``` Returns the generated Site Script JSON containing all supported components from the site at the provided Url including the lists "Shared Documents" and "MyList" -### EXAMPLE 3 +### EXAMPLE 5 ```powershell Get-PnPSiteScriptFromWeb -Url "https://contoso.sharepoint.com/sites/teamsite" -IncludeBranding -IncludeLinksToExportedItems ``` Returns the generated Site Script JSON containing the branding and navigation links from the site at the provided Url -### EXAMPLE 4 +### EXAMPLE 6 ```powershell -Get-PnPSiteScriptFromWeb -IncludeAll +Get-PnPSiteScriptFromWeb -IncludeAllLists ``` -Returns the generated Site Script JSON containing all the components from the currently connected to site +Returns the generated Site Script JSON containing all lists from the currently connected to site ## PARAMETERS -### -Connection -Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. - -```yaml -Type: PnPConnection -Parameter Sets: (All) - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - ### -IncludeAll -If specified will include all supported components into the Site Script except for the lists and document libraries, these need to be explicitly be specified through -Lists +If specified will include all supported components into the Site Script including all self lists, branding, navigation links, regional settings, external sharing capability and theme. ```yaml Type: SwitchParameter @@ -100,7 +100,7 @@ If specified will include the branding of the site into the Site Script ```yaml Type: SwitchParameter -Parameter Sets: Specific components +Parameter Sets: Specific components, All lists Required: False Position: Named @@ -114,7 +114,7 @@ If specified will include navigation links into the Site Script ```yaml Type: SwitchParameter -Parameter Sets: Specific components +Parameter Sets: Specific components, All lists Required: False Position: Named @@ -128,7 +128,7 @@ If specified will include the regional settings into the Site Script ```yaml Type: SwitchParameter -Parameter Sets: Specific components +Parameter Sets: Specific components, All lists Required: False Position: Named @@ -142,7 +142,7 @@ If specified will include the external sharing configuration into the Site Scrip ```yaml Type: SwitchParameter -Parameter Sets: Specific components +Parameter Sets: Specific components, All lists Required: False Position: Named @@ -156,7 +156,21 @@ If specified will include the branding of the site into the Site Script ```yaml Type: SwitchParameter -Parameter Sets: Specific components +Parameter Sets: Specific components, All lists + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -IncludeAllLists +If specified, all lists that are not hidden, private, internal or catalogs will be included into the Site Script. It cannot be combined with the -Lists nor the -IncludeAll parameters as both will already include all lists. + +```yaml +Type: String[] +Parameter Sets: All lists Required: False Position: Named @@ -166,11 +180,11 @@ Accept wildcard characters: False ``` ### -Lists -Allows specifying one or more site relative URLs of lists that should be included into the Site Script, i.e. "Shared Documents","List\MyList" +Allows specifying one or more site relative URLs of lists that should be included into the Site Script, i.e. "Shared Documents","Lists\MyList" ```yaml Type: String[] -Parameter Sets: (All) +Parameter Sets: Basic components, All components, Specific components Required: False Position: Named @@ -193,13 +207,12 @@ Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` -### -WhatIf -Shows what would happen if the cmdlet runs. The cmdlet is not run. +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. ```yaml -Type: SwitchParameter +Type: PnPConnection Parameter Sets: (All) -Aliases: wi Required: False Position: Named diff --git a/src/Commands/SiteDesigns/GetSiteScriptFromWeb.cs b/src/Commands/SiteDesigns/GetSiteScriptFromWeb.cs index 2027a8fd1..6c5a5a58b 100644 --- a/src/Commands/SiteDesigns/GetSiteScriptFromWeb.cs +++ b/src/Commands/SiteDesigns/GetSiteScriptFromWeb.cs @@ -11,10 +11,12 @@ public class GetSiteScriptFromWeb : PnPAdminCmdlet { private const string ParameterSet_BASICCOMPONENTS = "Basic components"; private const string ParameterSet_ALLCOMPONENTS = "All components"; + private const string ParameterSet_ALLLISTS = "All lists"; private const string ParameterSet_SPECIFICCOMPONENTS = "Specific components"; [Parameter(ParameterSetName = ParameterSet_BASICCOMPONENTS)] [Parameter(ParameterSetName = ParameterSet_ALLCOMPONENTS)] + [Parameter(ParameterSetName = ParameterSet_ALLLISTS)] [Parameter(ParameterSetName = ParameterSet_SPECIFICCOMPONENTS)] [Parameter(Mandatory = false, ValueFromPipeline = true)] public string Url; @@ -28,18 +30,26 @@ public class GetSiteScriptFromWeb : PnPAdminCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_ALLCOMPONENTS)] public SwitchParameter IncludeAll; + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_ALLLISTS)] + public SwitchParameter IncludeAllLists; + + [Parameter(ParameterSetName = ParameterSet_ALLLISTS)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_SPECIFICCOMPONENTS)] public SwitchParameter IncludeBranding; - + + [Parameter(ParameterSetName = ParameterSet_ALLLISTS)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_SPECIFICCOMPONENTS)] public SwitchParameter IncludeLinksToExportedItems; - + + [Parameter(ParameterSetName = ParameterSet_ALLLISTS)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_SPECIFICCOMPONENTS)] public SwitchParameter IncludeRegionalSettings; - + + [Parameter(ParameterSetName = ParameterSet_ALLLISTS)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_SPECIFICCOMPONENTS)] public SwitchParameter IncludeSiteExternalSharingCapability; + [Parameter(ParameterSetName = ParameterSet_ALLLISTS)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_SPECIFICCOMPONENTS)] public SwitchParameter IncludeTheme; @@ -48,9 +58,17 @@ protected override void ExecuteCmdlet() // If no URL specified, we take the URL of the site that the current context is connected to if(!ParameterSpecified(nameof(Url))) { - Url = PnPConnection.Current.Url; + Url = Connection.Url; } + if(IncludeAllLists || IncludeAll) + { + SiteContext.Load(SiteContext.Web.Lists, lists => lists.Where(list => !list.Hidden && !list.IsCatalog && !list.IsSystemList && !list.IsPrivate && !list.IsApplicationList && !list.IsSiteAssetsLibrary && !list.IsEnterpriseGalleryLibrary).Include(list => list.RootFolder.ServerRelativeUrl)); + SiteContext.ExecuteQueryRetry(); + + Lists = SiteContext.Web.Lists.Select(l => System.Text.RegularExpressions.Regex.Replace(l.RootFolder.ServerRelativeUrl, @"\/(?:sites|teams)\/.*?\/", string.Empty)).ToArray(); + } + var tenantSiteScriptSerializationInfo = new TenantSiteScriptSerializationInfo { IncludeBranding = IncludeBranding || IncludeAll, From cc333ac894c1599b809158c0d0383c5826280ae2 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 16 Jun 2022 22:53:41 +0300 Subject: [PATCH 387/458] Feature #661 - Sharing Capability for New-PnPTenantSite (#1994) --- CHANGELOG.md | 1 + documentation/New-PnPTenantSite.md | 20 +++++++++-- src/Commands/Admin/NewTenantSite.cs | 54 ++++++++++++++++++++--------- 3 files changed, 56 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b1fdf5e2..35a984482 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `-ExemptFromBlockDownloadOfNonViewableFiles` parameter to `Set-PnPList` cmdlet to configure access capabilites for unmanaged devices at list level. [#1973](https://github.com/pnp/powershell/pull/1973) - Added `-PercentComplete`, `-Priority`, `-StartDateTime`, `-DueDateTime` and `-Description` to `Add-PnPPlannerTask` [#1964](https://github.com/pnp/powershell/pull/1964) - Added `Set-PnPContentType` cmdlet to update the properties of the Content Types in a list or a web. [#1981](https://github.com/pnp/powershell/pull/1981) +- Added `-SharingCapability` parameter to the `New-PnPTenantSite` cmdlet to update the Sharing capabilties of the newly provisioned classic site collection. [#1994](https://github.com/pnp/powershell/pull/1994) - Added optional `-IncludeAllLists` to `Get-PnPSiteScriptFromWeb` which will include the JSON definition of all custom lists of the current site in the output [#1987](https://github.com/pnp/powershell/pull/1987) - Added `-UpdateChildren` parameter to `Add-PnPFieldToContentType` cmdlet. This allows users to skip pushing the fields to child content types. [#1092](https://github.com/pnp/powershell/pull/1992) diff --git a/documentation/New-PnPTenantSite.md b/documentation/New-PnPTenantSite.md index dd25ecd75..764489b2b 100644 --- a/documentation/New-PnPTenantSite.md +++ b/documentation/New-PnPTenantSite.md @@ -22,7 +22,7 @@ Creates a new (classic) site collection for the current tenant ```powershell New-PnPTenantSite -Title -Url -Owner [-Lcid ] [-Template ] -TimeZone [-ResourceQuota ] [-ResourceQuotaWarningLevel ] [-StorageQuota ] - [-StorageQuotaWarningLevel ] [-RemoveDeletedSite] [-Wait] [-Connection ] + [-StorageQuotaWarningLevel ] [-RemoveDeletedSite] [-SharingCapability ] [-Wait] [-Connection ] [] ``` @@ -216,16 +216,32 @@ Accept wildcard characters: False ``` ### -Wait +Waits for the site collection to be fully provisioned. ```yaml Type: SwitchParameter -Parameter Sets: (All) +Parameter Sets: (All), (Wait) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -SharingCapability +Specifies what the sharing capabilities are for the site. Possible values: Disabled, ExternalUserSharingOnly, ExternalUserAndGuestSharing, ExistingExternalUserSharingOnly. It will only work if Wait parameter is specified. + +```yaml +Type: SwitchParameter +Parameter Sets: (Wait) Required: False Position: Named Default value: None Accept pipeline input: False Accept wildcard characters: False +Accepted values: Disabled, ExternalUserSharingOnly, ExternalUserAndGuestSharing, ExistingExternalUserSharingOnly ``` ## RELATED LINKS diff --git a/src/Commands/Admin/NewTenantSite.cs b/src/Commands/Admin/NewTenantSite.cs index a05719ebe..6d5f49054 100644 --- a/src/Commands/Admin/NewTenantSite.cs +++ b/src/Commands/Admin/NewTenantSite.cs @@ -1,4 +1,5 @@ -using Microsoft.SharePoint.Client; +using Microsoft.Online.SharePoint.TenantManagement; +using Microsoft.SharePoint.Client; using PnP.Framework; using PnP.Framework.Entities; @@ -12,46 +13,52 @@ namespace PnP.PowerShell.Commands [Cmdlet(VerbsCommon.New, "PnPTenantSite")] public class NewTenantSite : PnPAdminCmdlet { - [Parameter(Mandatory = true)] + private const string ParameterSetName_Wait = "By Wait"; + + [Parameter(Mandatory = true, ParameterSetName = ParameterAttribute.AllParameterSets)] public string Title; - [Parameter(Mandatory = true)] + [Parameter(Mandatory = true, ParameterSetName = ParameterAttribute.AllParameterSets)] public string Url; - [Parameter(Mandatory = true)] + [Parameter(Mandatory = true, ParameterSetName = ParameterAttribute.AllParameterSets)] public string Owner = string.Empty; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = false, ParameterSetName = ParameterAttribute.AllParameterSets)] public uint Lcid = 1033; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = false, ParameterSetName = ParameterAttribute.AllParameterSets)] public string Template = "STS#0"; - [Parameter(Mandatory = true)] + [Parameter(Mandatory = true, ParameterSetName = ParameterAttribute.AllParameterSets)] public int TimeZone; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = false, ParameterSetName = ParameterAttribute.AllParameterSets)] public double ResourceQuota = 0; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = false, ParameterSetName = ParameterAttribute.AllParameterSets)] public double ResourceQuotaWarningLevel = 0; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = false, ParameterSetName = ParameterAttribute.AllParameterSets)] public long StorageQuota = 100; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = false, ParameterSetName = ParameterAttribute.AllParameterSets)] public long StorageQuotaWarningLevel = 100; - [Parameter(Mandatory = false)] + [Parameter(Mandatory = false, ParameterSetName = ParameterAttribute.AllParameterSets)] public SwitchParameter RemoveDeletedSite; - - [Parameter(Mandatory = false)] + + [Parameter(Mandatory = false, ParameterSetName = ParameterAttribute.AllParameterSets)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSetName_Wait)] public SwitchParameter Wait; - [Obsolete("The Force parameter has been deprecated and is no longer required to be provided. It will be removed in a future version.")] - [Parameter(Mandatory = false)] + [Obsolete("The Force parameter has been deprecated and is no longer required to be provided. It will be removed in a future version.")] + [Parameter(Mandatory = false, ParameterSetName = ParameterAttribute.AllParameterSets)] public SwitchParameter Force; + [Parameter(Mandatory = false, ParameterSetName = ParameterSetName_Wait)] + public SharingCapabilities? SharingCapability; + protected override void ExecuteCmdlet() { if (!Url.ToLower().StartsWith("https://") && !Url.ToLower().StartsWith("http://")) @@ -61,9 +68,22 @@ protected override void ExecuteCmdlet() } Func timeoutFunction = TimeoutFunction; - Tenant.CreateSiteCollection(Url, Title, Owner, Template, (int)StorageQuota, + Guid newSiteId = Tenant.CreateSiteCollection(Url, Title, Owner, Template, (int)StorageQuota, (int)StorageQuotaWarningLevel, TimeZone, (int)ResourceQuota, (int)ResourceQuotaWarningLevel, Lcid, RemoveDeletedSite, Wait, Wait == true ? timeoutFunction : null); + + if (newSiteId != Guid.Empty && Wait && SharingCapability.HasValue) + { + var props = Tenant.GetSitePropertiesByUrl(Url, true); + Tenant.Context.Load(props); + Tenant.Context.ExecuteQueryRetry(); + + props.SharingCapability = SharingCapability.Value; + + var op = props.Update(); + ClientContext.Load(op, i => i.IsComplete, i => i.PollingInterval); + ClientContext.ExecuteQueryRetry(); + } } private bool TimeoutFunction(TenantOperationMessage message) From a5719fb90f1a5f9d376d56d12e97790ecfb9c419 Mon Sep 17 00:00:00 2001 From: Marc D Anderson Date: Thu, 16 Jun 2022 17:51:08 -0400 Subject: [PATCH 388/458] Fixed batching badge links --- documentation/Add-PnPListItem.md | 2 +- documentation/Invoke-PnPBatch.md | 2 +- documentation/New-PnPBatch.md | 2 +- documentation/Publish-PnPSyntexModel.md | 2 +- documentation/Remove-PnPListItem.md | 2 +- documentation/Request-PnPSyntexClassifyAndExtract.md | 2 +- documentation/Set-PnPListItem.md | 2 +- documentation/Unpublish-PnPSyntexModel.md | 2 +- pages/articles/batching.md | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/documentation/Add-PnPListItem.md b/documentation/Add-PnPListItem.md index 8a0dd28e3..f48215761 100644 --- a/documentation/Add-PnPListItem.md +++ b/documentation/Add-PnPListItem.md @@ -13,7 +13,7 @@ title: Add-PnPListItem Adds an item to the list and sets the creation time to the current date and time. The author is set to the current authenticated user executing the cmdlet. In order to set the author to a different user, please refer to Set-PnPListItem. -[![Maturity Model for Microsoft 365](/pages/images/batching/Batching.png)](../pages/articles/batching.md) +[![Supports Batching](../pages/images/batching/Batching.png)](../pages/articles/batching.md) ## SYNTAX diff --git a/documentation/Invoke-PnPBatch.md b/documentation/Invoke-PnPBatch.md index 00ee3868a..3fd8a2202 100644 --- a/documentation/Invoke-PnPBatch.md +++ b/documentation/Invoke-PnPBatch.md @@ -13,7 +13,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Invoke-PnPBatch.html Executes the batch -[![Maturity Model for Microsoft 365](/pages/images/batching/Batching.png)](../pages/articles/batching.md) +[![Supports Batching](../pages/images/batching/Batching.png)](../pages/articles/batching.md) ## SYNTAX diff --git a/documentation/New-PnPBatch.md b/documentation/New-PnPBatch.md index 1d2abe667..82437ac46 100644 --- a/documentation/New-PnPBatch.md +++ b/documentation/New-PnPBatch.md @@ -13,7 +13,7 @@ online version: https://pnp.github.io/powershell/cmdlets/New-PnPBatch.html Creates a new batch -[![Maturity Model for Microsoft 365](/pages/images/batching/Batching.png)](../pages/articles/batching.md) +[![Supports Batching](../pages/images/batching/Batching.png)](../pages/articles/batching.md) ## SYNTAX diff --git a/documentation/Publish-PnPSyntexModel.md b/documentation/Publish-PnPSyntexModel.md index 3809f4397..c50698ceb 100644 --- a/documentation/Publish-PnPSyntexModel.md +++ b/documentation/Publish-PnPSyntexModel.md @@ -15,7 +15,7 @@ Publishes a SharePoint Syntex models to a list. This cmdlet only works when you've connected to a SharePoint Syntex Content Center site. -[![Maturity Model for Microsoft 365](/pages/images/batching/Batching.png)](../pages/articles/batching.md) +[![Supports Batching](../pages/images/batching/Batching.png)](../pages/articles/batching.md) ## SYNTAX diff --git a/documentation/Remove-PnPListItem.md b/documentation/Remove-PnPListItem.md index 6023c3e65..876276c0f 100644 --- a/documentation/Remove-PnPListItem.md +++ b/documentation/Remove-PnPListItem.md @@ -13,7 +13,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Remove-PnPListItem.html Deletes an item from a list -[![Maturity Model for Microsoft 365](/pages/images/batching/Batching.png)](../pages/articles/batching.md) +[![Supports Batching](../pages/images/batching/Batching.png)](../pages/articles/batching.md) ## SYNTAX diff --git a/documentation/Request-PnPSyntexClassifyAndExtract.md b/documentation/Request-PnPSyntexClassifyAndExtract.md index a88ea1d44..55ffe659e 100644 --- a/documentation/Request-PnPSyntexClassifyAndExtract.md +++ b/documentation/Request-PnPSyntexClassifyAndExtract.md @@ -13,7 +13,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Get-PnPPage.html Requests for a file, folder or all files in a library to be classified and extracted via the published SharePoint Syntex models on the libraries hosting the files. -[![Maturity Model for Microsoft 365](/pages/images/batching/Batching.png)](../pages/articles/batching.md) +[![Supports Batching](../pages/images/batching/Batching.png)](../pages/articles/batching.md) ## SYNTAX diff --git a/documentation/Set-PnPListItem.md b/documentation/Set-PnPListItem.md index b510c80f6..026ce4372 100644 --- a/documentation/Set-PnPListItem.md +++ b/documentation/Set-PnPListItem.md @@ -13,7 +13,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Set-PnPListItem.html Updates a list item -[![Maturity Model for Microsoft 365](/pages/images/batching/Batching.png)](../pages/articles/batching.md) +[![Supports Batching](../pages/images/batching/Batching.png)](../pages/articles/batching.md) ## SYNTAX diff --git a/documentation/Unpublish-PnPSyntexModel.md b/documentation/Unpublish-PnPSyntexModel.md index bbc0ce1b6..71753ed40 100644 --- a/documentation/Unpublish-PnPSyntexModel.md +++ b/documentation/Unpublish-PnPSyntexModel.md @@ -15,7 +15,7 @@ Unpublishes a SharePoint Syntex model from a list. This cmdlet only works when you've connected to a SharePoint Syntex Content Center site. -[![Maturity Model for Microsoft 365](/pages/images/batching/Batching.png)](../pages/articles/batching.md) +[![Supports Batching](../pages/images/batching/Batching.png)](../pages/articles/batching.md) ## SYNTAX diff --git a/pages/articles/batching.md b/pages/articles/batching.md index ffabd60f6..80bc660b1 100644 --- a/pages/articles/batching.md +++ b/pages/articles/batching.md @@ -47,7 +47,7 @@ If during one of these chunks an exception occurs (for instance you are trying t The following cmdlets support batching. (This support began with the 1.7.63-nightly release.) All cmdlets which support batching display the badge, linking to this article. -![Maturity Model for Microsoft 365](/pages/images/batching/Batching.png) +![Supports Batching](../../pages/images/batching/Batching.png) * [`Add-PnPListItem`](/powershell/cmdlets/Add-PnPListItem.html) * [`Set-PnPListItem`](/powershell/cmdlets/Set-PnPListItem.html) From 7a1a9b760d247e43ae9a32a08709a44715e7e041 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Fri, 17 Jun 2022 03:44:05 +0000 Subject: [PATCH 389/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index fcbd0aa2a..6eddf96b7 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -3c7cef650c0fbec375f9de60ac891454647e54f5 \ No newline at end of file +abccc06f79d25bb7e5cf3fed96f03bc9dcc2b169 \ No newline at end of file diff --git a/version.txt b/version.txt index f545abbfb..03a3a5c96 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.50 \ No newline at end of file +1.10.51 \ No newline at end of file From 9750a4574dc88391fdf7c94d37930cecdfaf614a Mon Sep 17 00:00:00 2001 From: Ali Robertson Date: Fri, 17 Jun 2022 17:29:02 +1000 Subject: [PATCH 390/458] fix broken error messages (output contained variable name as string) --- src/Commands/Utilities/ListItemHelper.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Commands/Utilities/ListItemHelper.cs b/src/Commands/Utilities/ListItemHelper.cs index 6cdfbb02d..08c45bd40 100644 --- a/src/Commands/Utilities/ListItemHelper.cs +++ b/src/Commands/Utilities/ListItemHelper.cs @@ -135,7 +135,7 @@ public static void SetFieldValues(this ListItem item, Hashtable valuesToSet, Cmd } else { - cmdlet.WriteWarning($@"Unable to find the specified term.Skipping values for field ""{field.InternalName}"""); + cmdlet.WriteWarning("Unable to find the specified term. Skipping values for field " + $field.InternalName); } } @@ -159,7 +159,7 @@ public static void SetFieldValues(this ListItem item, Hashtable valuesToSet, Cmd } else { - cmdlet.WriteWarning($@"You are trying to set multiple values in a single value field. Skipping values for field ""{field.InternalName}"""); + cmdlet.WriteWarning("You are trying to set multiple values in a single value field. Skipping values for field " + $field.InternalName); } } else @@ -176,7 +176,7 @@ public static void SetFieldValues(this ListItem item, Hashtable valuesToSet, Cmd if (taxonomyItem == null) { updateTaxItemValue = false; - cmdlet.WriteWarning($@"Unable to find the specified term.Skipping values for field ""{field.InternalName}"""); + cmdlet.WriteWarning("Unable to find the specified term. Skipping values for field" + $field.InternalName); } } else From 3314c56a1179b88e8281e73ff085a5626e3e5144 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Fri, 17 Jun 2022 11:23:31 +0300 Subject: [PATCH 391/458] Feature: added better piping support for certain cmdlets --- src/Commands/Admin/GetSiteCollectionAppCatalogs.cs | 2 +- .../ContentTypes/AddContentTypesFromContentTypeHub.cs | 2 +- src/Commands/Files/FindFile.cs | 6 +++--- src/Commands/Lists/GetListPermissions.cs | 2 +- src/Commands/Search/GetSearchConfiguration.cs | 2 +- src/Commands/Search/GetSearchCrawlLog.cs | 4 ++-- src/Commands/Web/GetIndexedPropertyKeys.cs | 4 ++-- src/Commands/Webhooks/GetWebhookSubscriptions.cs | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Commands/Admin/GetSiteCollectionAppCatalogs.cs b/src/Commands/Admin/GetSiteCollectionAppCatalogs.cs index f168a2e1b..4bcf98b89 100644 --- a/src/Commands/Admin/GetSiteCollectionAppCatalogs.cs +++ b/src/Commands/Admin/GetSiteCollectionAppCatalogs.cs @@ -13,7 +13,7 @@ protected override void ExecuteCmdlet() ClientContext.Load(allowedSites); ClientContext.ExecuteQueryRetry(); - WriteObject(allowedSites); + WriteObject(allowedSites, true); } } } \ No newline at end of file diff --git a/src/Commands/ContentTypes/AddContentTypesFromContentTypeHub.cs b/src/Commands/ContentTypes/AddContentTypesFromContentTypeHub.cs index c15833796..239b8dc5f 100644 --- a/src/Commands/ContentTypes/AddContentTypesFromContentTypeHub.cs +++ b/src/Commands/ContentTypes/AddContentTypesFromContentTypeHub.cs @@ -34,7 +34,7 @@ protected override void ExecuteCmdlet() IsPassed = res.Value.IsPassed, Value = res.Value }; - WriteObject(result); + WriteObject(result, true); } } } \ No newline at end of file diff --git a/src/Commands/Files/FindFile.cs b/src/Commands/Files/FindFile.cs index ce2da4d06..da0921f30 100644 --- a/src/Commands/Files/FindFile.cs +++ b/src/Commands/Files/FindFile.cs @@ -29,7 +29,7 @@ protected override void ExecuteCmdlet() var list = List.GetList(CurrentWeb); if (list == null) throw new ArgumentException("The specified list was not found"); - WriteObject(list.FindFiles(Match)); + WriteObject(list.FindFiles(Match), true); break; } case "Folder": @@ -37,12 +37,12 @@ protected override void ExecuteCmdlet() var folder = Folder.GetFolder(CurrentWeb, false); if (folder == null) throw new ArgumentException("The specified folder was not found"); - WriteObject(folder.FindFiles(Match)); + WriteObject(folder.FindFiles(Match), true); break; } default: { - WriteObject(CurrentWeb.FindFiles(Match)); + WriteObject(CurrentWeb.FindFiles(Match), true); break; } } diff --git a/src/Commands/Lists/GetListPermissions.cs b/src/Commands/Lists/GetListPermissions.cs index f00a32a30..83d969535 100644 --- a/src/Commands/Lists/GetListPermissions.cs +++ b/src/Commands/Lists/GetListPermissions.cs @@ -19,7 +19,7 @@ public class GetListPermissions : PnPWebCmdlet protected override void ExecuteCmdlet() { var list = Identity.GetListOrThrow(nameof(List), PnPContext); - WriteObject(list.GetRoleDefinitions(PrincipalId).RequestedItems); + WriteObject(list.GetRoleDefinitions(PrincipalId).RequestedItems, true); } } } diff --git a/src/Commands/Search/GetSearchConfiguration.cs b/src/Commands/Search/GetSearchConfiguration.cs index afb9c25b9..f81ff661a 100644 --- a/src/Commands/Search/GetSearchConfiguration.cs +++ b/src/Commands/Search/GetSearchConfiguration.cs @@ -155,7 +155,7 @@ protected override void ExecuteCmdlet() var aliases = GetAliasesFromPid(doc, mp.Pid); mp.Aliases = aliases; } - WriteObject(mps); + WriteObject(mps, true); } } } diff --git a/src/Commands/Search/GetSearchCrawlLog.cs b/src/Commands/Search/GetSearchCrawlLog.cs index 7ce5e7a10..7522ec2e1 100644 --- a/src/Commands/Search/GetSearchCrawlLog.cs +++ b/src/Commands/Search/GetSearchCrawlLog.cs @@ -113,7 +113,7 @@ protected override void ExecuteCmdlet() entries.Add(ConvertToPSObject(dictionary)); } } - WriteObject(entries.Take(origLimit)); + WriteObject(entries.Take(origLimit), true); } else { @@ -135,7 +135,7 @@ protected override void ExecuteCmdlet() entries.Where(e => System.Net.WebUtility.UrlDecode(e.Url.ToString()).ToLower().Contains(":443/person")) .ToList(); } - WriteObject(entries.Take(origLimit).OrderByDescending(i => i.CrawlTime).ToList()); + WriteObject(entries.Take(origLimit).OrderByDescending(i => i.CrawlTime).ToList(), true); } } catch (Exception e) diff --git a/src/Commands/Web/GetIndexedPropertyKeys.cs b/src/Commands/Web/GetIndexedPropertyKeys.cs index 870a0cce9..973c5b1e9 100644 --- a/src/Commands/Web/GetIndexedPropertyKeys.cs +++ b/src/Commands/Web/GetIndexedPropertyKeys.cs @@ -20,13 +20,13 @@ protected override void ExecuteCmdlet() if (list != null) { var keys = list.GetIndexedPropertyBagKeys(); - WriteObject(keys); + WriteObject(keys, true); } } else { var keys = CurrentWeb.GetIndexedPropertyBagKeys(); - WriteObject(keys); + WriteObject(keys, true); } } } diff --git a/src/Commands/Webhooks/GetWebhookSubscriptions.cs b/src/Commands/Webhooks/GetWebhookSubscriptions.cs index 0efaabaa3..22aa579c5 100644 --- a/src/Commands/Webhooks/GetWebhookSubscriptions.cs +++ b/src/Commands/Webhooks/GetWebhookSubscriptions.cs @@ -30,7 +30,7 @@ protected override void ExecuteCmdlet() if (list != null) { // Get all the webhook subscriptions for the specified list - WriteObject(list.GetWebhookSubscriptions()); + WriteObject(list.GetWebhookSubscriptions(), true); } else { From d19efd20892fab8f5c24abed5c6fd8569a48a25b Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Sat, 18 Jun 2022 03:38:54 +0000 Subject: [PATCH 392/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 4e1947a29..92f134c36 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -69cfe6fca3a01a894166a853e0a85a82635f0861 \ No newline at end of file +6c36d79ec3fc13d36f331c38368db5c287201aa2 \ No newline at end of file diff --git a/version.txt b/version.txt index 03a3a5c96..ebb257382 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.51 \ No newline at end of file +1.10.52 \ No newline at end of file From 281ed4c8bd2836063ccda2c48b637fda2b0be9dd Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Sat, 18 Jun 2022 20:04:42 +0200 Subject: [PATCH 393/458] Update Set-PnPWebTheme.md --- documentation/Set-PnPWebTheme.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/documentation/Set-PnPWebTheme.md b/documentation/Set-PnPWebTheme.md index c54cfe0be..ff21cb44f 100644 --- a/documentation/Set-PnPWebTheme.md +++ b/documentation/Set-PnPWebTheme.md @@ -20,7 +20,7 @@ Set-PnPWebTheme [[-Theme] ] [-WebUrl ] ``` ## DESCRIPTION -Sets the theme of the current web. * Requires Tenant Administration Rights * +Sets the theme of the current web. * Requires SharePoint Online Administrator Rights * ## EXAMPLES @@ -38,6 +38,13 @@ Get-PnPTenantTheme -Name "MyTheme" | Set-PnPWebTheme Sets the theme named "MyTheme" to the current web +### EXAMPLE 3 +```powershell +Set-PnPWebTheme -Theme "MyCompanyTheme" -WebUrl https://contoso.sharepoint.com/sites/MyWeb +``` + +Sets the theme named "MyCompanyTheme" to MyWeb + ## PARAMETERS ### -Connection From 77eb70379d2da58ddd5885130f1baabf9a7ecb31 Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Sat, 18 Jun 2022 20:26:42 +0200 Subject: [PATCH 394/458] Update Set-PnPWebPermission.md --- documentation/Set-PnPWebPermission.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/documentation/Set-PnPWebPermission.md b/documentation/Set-PnPWebPermission.md index c868e0c47..fea04a8c8 100644 --- a/documentation/Set-PnPWebPermission.md +++ b/documentation/Set-PnPWebPermission.md @@ -7,7 +7,7 @@ external help file: PnP.PowerShell.dll-Help.xml online version: https://pnp.github.io/powershell/cmdlets/Set-PnPWebPermission.html --- -# Set-PnPWebPartProperty +# Set-PnPWebPermission ## SYNOPSIS Sets a web permissions @@ -15,7 +15,7 @@ Sets a web permissions ## SYNTAX ```powershell - Set-PnPWebPermission -Group [-Identity ] [-AddRole ] [-RemoveRole ] +Set-PnPWebPermission -Group [-Identity ] [-AddRole ] [-RemoveRole ] ``` ```powershell @@ -24,6 +24,7 @@ Set-PnPWebPermission -User [-Identity ] [-AddRole Date: Sat, 18 Jun 2022 20:48:42 +0200 Subject: [PATCH 395/458] Update Get-PnPTenantTheme.md --- documentation/Get-PnPTenantTheme.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/documentation/Get-PnPTenantTheme.md b/documentation/Get-PnPTenantTheme.md index a26d64a3a..1fb9be578 100644 --- a/documentation/Get-PnPTenantTheme.md +++ b/documentation/Get-PnPTenantTheme.md @@ -33,19 +33,26 @@ Returns all or a specific tenant theme. Get-PnPTenantTheme ``` -Returns all themes +Returns all themes. ### EXAMPLE 2 ```powershell Get-PnPTenantTheme -Name "MyCompanyTheme" ``` -Returns the specified theme +Returns the specified theme. + +### EXAMPLE 3 +```powershell +Get-PnPTenantTheme -Name "MyCompanyTheme" -AsJson +``` + +Returns the specified theme as json. ## PARAMETERS ### -AsJson -{{ Fill AsJson Description }} +The theme is returned in json format. ```yaml Type: SwitchParameter @@ -73,7 +80,7 @@ Accept wildcard characters: False ``` ### -Name -The name of the theme to retrieve +The name of the theme to retrieve. ```yaml Type: String From 27d82f199144fa68ad53179cc12e5566d36324f5 Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Sat, 18 Jun 2022 21:58:48 +0200 Subject: [PATCH 396/458] Update Set-PnPWeb.md --- documentation/Set-PnPWeb.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/documentation/Set-PnPWeb.md b/documentation/Set-PnPWeb.md index bcdade112..4aadd98fb 100644 --- a/documentation/Set-PnPWeb.md +++ b/documentation/Set-PnPWeb.md @@ -43,11 +43,18 @@ Hides the quick launch from being shown in the current web ### EXAMPLE 3 ```powershell +Set-PnPWeb -HeaderEmphasis Strong -HeaderLayout Compact +``` +Sets the header style in the current web. + +### EXAMPLE 4 +```powershell Set-PnPWeb -NoCrawl:$true ``` Prevents the current web from being returned in search results + ## PARAMETERS ### -AlternateCssUrl @@ -135,6 +142,7 @@ Accept wildcard characters: False ``` ### -HeaderEmphasis +Defines the tone of color used for the bar shown at the top of the site with the site name and logo ```yaml Type: SPVariantThemeType @@ -149,6 +157,7 @@ Accept wildcard characters: False ``` ### -HeaderLayout +Defines the layout type of the site header ```yaml Type: HeaderLayoutType @@ -191,7 +200,7 @@ Accept wildcard characters: False ``` ### -MembersCanShare -Indicates if members of this site can share the site and individual sites with others ($true) or only owners can do this ($false) +Indicates if members of this site can share the site and its content with others ($true) or only owners can do this ($false) ```yaml Type: SwitchParameter @@ -275,7 +284,7 @@ Accept wildcard characters: False ``` ### -HideTitleInHeader -Toggle the title visiblity in the header. +Toggle the title visibility in the header. Set -HideTitleInHeader:$false to show the header From f57227703754e0be2554683cd59255d229e65083 Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Sat, 18 Jun 2022 22:10:43 +0200 Subject: [PATCH 397/458] Update Set-PnPWebHeader.md --- documentation/Set-PnPWebHeader.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/documentation/Set-PnPWebHeader.md b/documentation/Set-PnPWebHeader.md index b07b03d43..17606025f 100644 --- a/documentation/Set-PnPWebHeader.md +++ b/documentation/Set-PnPWebHeader.md @@ -49,7 +49,7 @@ Sets the site title and logo to be displayed in the middle of the screen ## PARAMETERS ### -LogoAlignment -Allows configuring the site title and logo to be shown on the left (default), in the middle or at the right. +Allows configuring the site title and logo to be shown on the left (default), in the middle or on the right. ```yaml Type: LogoAlignment @@ -120,7 +120,7 @@ Accept wildcard characters: False ``` ### -HeaderEmphasis -Allows defining the tone of color used for the bar at shown at the top of the site under the site name and logo +Defines the tone of color used for the bar at shown at the top of the site under the site name and logo ```yaml Type: SPVariantThemeType @@ -135,7 +135,7 @@ Accept wildcard characters: False ``` ### -HeaderLayout -Allows defining how the header of the site should be layed out +Defines how the header of the site should be layed out ```yaml Type: HeaderLayoutType @@ -164,7 +164,7 @@ Accept wildcard characters: False ``` ### -HideTitleInHeader -Toggle the title visiblity in the header. +Toggle the title visibility in the header. Set -HideTitleInHeader:$false to show the header From 08314688a2d5fdc713b53ac2b5df26a9b440e6a1 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Sun, 19 Jun 2022 03:50:48 +0000 Subject: [PATCH 398/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 92f134c36..248cba875 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -6c36d79ec3fc13d36f331c38368db5c287201aa2 \ No newline at end of file +121c69280db556259e046b6d5f42174c00701a5e \ No newline at end of file diff --git a/version.txt b/version.txt index ebb257382..59541cb33 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.52 \ No newline at end of file +1.10.53 \ No newline at end of file From bd71464c2770c7b0aea862015908135ead6aec73 Mon Sep 17 00:00:00 2001 From: = <=> Date: Sun, 19 Jun 2022 12:58:38 +0200 Subject: [PATCH 399/458] Adjusting to supporting -Connection in near all cmdlets --- documentation/Get-PnPTenantId.md | 29 +++++++++++++++++-- .../Admin/AddSiteCollectionAppCatalog.cs | 2 +- src/Commands/Admin/GetSiteUserInvitations.cs | 2 +- src/Commands/Admin/GetTenantId.cs | 23 +++++++++++---- .../Admin/RemoveSiteUserInvitations.cs | 2 +- src/Commands/Admin/RepairSite.cs | 9 ++---- src/Commands/Admin/TestSite.cs | 10 ++----- .../Apps/GetAzureADAppSitePermission.cs | 4 +-- .../Apps/GrantAzureADAppSitePermission.cs | 2 +- .../Apps/RevokeAzureADAppSitePermission.cs | 2 +- .../Apps/SetAzureADAppSitePermission.cs | 2 +- src/Commands/AzureAD/AddAzureADGroupMember.cs | 4 +-- src/Commands/Base/GetAccessToken.cs | 2 +- src/Commands/Base/GetDiagnostics.cs | 4 --- .../Base/GetPowerShellTelemetryEnabled.cs | 2 +- src/Commands/Base/PipeBinds/PagePipeBind.cs | 11 ++----- src/Commands/Base/PipeBinds/SitePipeBind.cs | 2 +- .../Base/PipeBinds/SyntexModelPipeBind.cs | 12 ++++---- src/Commands/Base/RequestAccessToken.cs | 13 ++++----- src/Commands/InvokeAction/InvokeWebAction.cs | 12 ++++---- src/Commands/Lists/CopyList.cs | 4 +-- .../NewMicrosoft365Group.cs | 2 +- .../SetMicrosoft365Group.cs | 2 +- .../GetStructuralNavigationCacheSiteState.cs | 2 +- .../GetStructuralNavigationCacheWebState.cs | 2 +- .../SetStructuralNavigationCacheSiteState.cs | 2 +- .../SetStructuralNavigationCacheWebState.cs | 2 +- src/Commands/Pages/AddPageSection.cs | 3 +- src/Commands/Pages/AddPageTextPart.cs | 2 +- src/Commands/Pages/AddPageWebPart.cs | 2 +- .../Pages/GetAvailablePageComponents.cs | 3 +- src/Commands/Pages/GetPage.cs | 2 +- src/Commands/Pages/GetPageComponent.cs | 2 +- src/Commands/Pages/MovePageComponent.cs | 4 ++- src/Commands/Pages/RemovePage.cs | 2 +- src/Commands/Pages/RemovePageComponent.cs | 2 +- src/Commands/Pages/SetPage.cs | 5 +--- src/Commands/Pages/SetPageTextPart.cs | 2 +- src/Commands/Pages/SetPageWebPart.cs | 4 +-- src/Commands/Principals/ExportUserInfo.cs | 2 +- src/Commands/Principals/GetSiteGroup.cs | 2 +- src/Commands/Principals/NewSiteGroup.cs | 2 +- src/Commands/Principals/RemoveSiteGroup.cs | 2 +- src/Commands/Principals/RemoveUserInfo.cs | 2 +- src/Commands/Principals/SetSiteGroup.cs | 2 +- .../Provisioning/Site/GetSiteTemplate.cs | 4 +-- .../Provisioning/Site/InvokeSiteTemplate.cs | 4 +-- .../Provisioning/Tenant/GetTenantTemplate.cs | 7 ++--- .../Tenant/InvokeTenantTemplate.cs | 6 ++-- src/Commands/Provisioning/TokenRetrieval.cs | 7 ++--- src/Commands/Search/GetSearchCrawlLog.cs | 10 +++---- src/Commands/Site/AddSiteClassification.cs | 4 +-- src/Commands/Site/GetSite.cs | 1 - .../SiteDesigns/AddSiteDesignFromWeb.cs | 2 +- src/Commands/Syntex/GetSyntexModel.cs | 4 +-- .../Syntex/GetSyntexModelPublication.cs | 4 +-- src/Commands/Syntex/PublishSyntexModel.cs | 8 ++--- .../Syntex/RequestSyntexClassifyAndExtract.cs | 2 +- src/Commands/Syntex/UnPublishSyntexModel.cs | 8 ++--- src/Commands/Teams/NewTeamsTeam.cs | 2 +- .../GetSubscribeSharePointNewsDigest.cs | 2 +- .../UserProfiles/NewUPABulkImportJob.cs | 2 +- .../SetSubscribeSharePointNewsDigest.cs | 2 +- ...intUserProfilesFromAzureActiveDirectory.cs | 2 +- .../Utilities/Microsoft365GroupsUtility.cs | 4 +-- src/Commands/Utilities/TeamsUtility.cs | 20 ++++++------- src/Commands/Web/InvokeWebAction.cs | 2 +- 67 files changed, 159 insertions(+), 158 deletions(-) diff --git a/documentation/Get-PnPTenantId.md b/documentation/Get-PnPTenantId.md index da49df866..0fbfca515 100644 --- a/documentation/Get-PnPTenantId.md +++ b/documentation/Get-PnPTenantId.md @@ -14,10 +14,20 @@ Returns the Tenant ID ## SYNTAX +### By TenantUrl ```powershell -Get-PnPTenantId [-TenantUrl ] [] +Get-PnPTenantId -TenantUrl [] ``` +Allows passing in a tenant url such as https://contoso.sharepoint.com to retrieve the tenant Id of + +### By connection +```powershell +Get-PnPTenantId [-Connection ] [] +``` + +Allows returning the tenantId of the tenant currently connected to in the current context or through the provided connection + ## DESCRIPTION ## EXAMPLES @@ -51,7 +61,20 @@ Accept pipeline input: False Accept wildcard characters: False ``` -## RELATED LINKS +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/src/Commands/Admin/AddSiteCollectionAppCatalog.cs b/src/Commands/Admin/AddSiteCollectionAppCatalog.cs index 7feb22ce4..f15fd9143 100644 --- a/src/Commands/Admin/AddSiteCollectionAppCatalog.cs +++ b/src/Commands/Admin/AddSiteCollectionAppCatalog.cs @@ -29,7 +29,7 @@ protected override void ExecuteCmdlet() } else { - url = PnPConnection.Current.Url; + url = Connection.Url; } Tenant.GetSiteByUrl(url).RootWeb.TenantAppCatalog.SiteCollectionAppCatalogsSites.Add(url); diff --git a/src/Commands/Admin/GetSiteUserInvitations.cs b/src/Commands/Admin/GetSiteUserInvitations.cs index c8f6709e0..cdc637d0c 100644 --- a/src/Commands/Admin/GetSiteUserInvitations.cs +++ b/src/Commands/Admin/GetSiteUserInvitations.cs @@ -18,7 +18,7 @@ public class GetSiteUserInvitations : PnPAdminCmdlet protected override void ExecuteCmdlet() { - var url = PnPConnection.Current.Url; + var url = Connection.Url; if(ParameterSpecified(nameof(Site))) { url = Site.Url; diff --git a/src/Commands/Admin/GetTenantId.cs b/src/Commands/Admin/GetTenantId.cs index e554f906d..d6db3890d 100644 --- a/src/Commands/Admin/GetTenantId.cs +++ b/src/Commands/Admin/GetTenantId.cs @@ -1,5 +1,4 @@ using Microsoft.SharePoint.Client; - using PnP.PowerShell.Commands.Base; using System; using System.Management.Automation; @@ -8,19 +7,31 @@ namespace PnP.PowerShell.Commands.Admin { - [Cmdlet(VerbsCommon.Get, "PnPTenantId")] + [Cmdlet(VerbsCommon.Get, "PnPTenantId", DefaultParameterSetName = ParameterSet_FROMCONNECTION)] public class GetTenantId : BasePSCmdlet { - [Parameter(Mandatory = false)] + private const string ParameterSet_BYURL = "By URL"; + private const string ParameterSet_FROMCONNECTION = "From connection"; + + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_BYURL)] public string TenantUrl; + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FROMCONNECTION, HelpMessage = "Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection.")] + public PnPConnection Connection = null; + protected override void ProcessRecord() { + // If a specific connection has been provided, use that, otherwise use the current connection + if(Connection == null) + { + Connection = PnPConnection.Current; + } + try { - if (string.IsNullOrEmpty(TenantUrl) && PnPConnection.Current != null) + if (string.IsNullOrEmpty(TenantUrl) && Connection != null) { - WriteObject(TenantExtensions.GetTenantIdByUrl(PnPConnection.Current.Url)); + WriteObject(TenantExtensions.GetTenantIdByUrl(Connection.Url)); } else if (!string.IsNullOrEmpty(TenantUrl)) { @@ -28,7 +39,7 @@ protected override void ProcessRecord() } else { - throw new InvalidOperationException("Either a connection needs to be made by Connect-PnPOnline or TenantUrl needs to be specified"); + throw new InvalidOperationException($"Either a connection needs to be made by Connect-PnPOnline, a connection needs to be provided through -{nameof(Connection)} or -{nameof(TenantUrl)} needs to be specified"); } } catch (Exception ex) diff --git a/src/Commands/Admin/RemoveSiteUserInvitations.cs b/src/Commands/Admin/RemoveSiteUserInvitations.cs index e2eb608c5..f81d02e62 100644 --- a/src/Commands/Admin/RemoveSiteUserInvitations.cs +++ b/src/Commands/Admin/RemoveSiteUserInvitations.cs @@ -21,7 +21,7 @@ public class RemoveSiteUserInvitations : PnPAdminCmdlet protected override void ExecuteCmdlet() { - var url = PnPConnection.Current.Url; + var url = Connection.Url; if (ParameterSpecified(nameof(Site))) { url = Site.Url; diff --git a/src/Commands/Admin/RepairSite.cs b/src/Commands/Admin/RepairSite.cs index fe10578e4..43547c7f9 100644 --- a/src/Commands/Admin/RepairSite.cs +++ b/src/Commands/Admin/RepairSite.cs @@ -1,14 +1,9 @@ -using Microsoft.Online.SharePoint.TenantAdministration; -using Microsoft.SharePoint.Client; +using Microsoft.SharePoint.Client; using PnP.PowerShell.Commands.Base; using System.Management.Automation; -using PnP.Framework.Sites; using PnP.PowerShell.Commands.Base.PipeBinds; using System; -using PnP.PowerShell.Commands.Model; -using System.Linq; -using System.Text.Json; using System.Text; using System.Collections.Generic; @@ -28,7 +23,7 @@ public class RepairSite : PnPAdminCmdlet protected override void ExecuteCmdlet() { - var siteUrl = PnPConnection.Current.Url; + var siteUrl = Connection.Url; if (ParameterSpecified(nameof(Identity))) { siteUrl = Identity.Url; diff --git a/src/Commands/Admin/TestSite.cs b/src/Commands/Admin/TestSite.cs index 2f5778c81..bd53d0bff 100644 --- a/src/Commands/Admin/TestSite.cs +++ b/src/Commands/Admin/TestSite.cs @@ -1,14 +1,8 @@ -using Microsoft.Online.SharePoint.TenantAdministration; -using Microsoft.SharePoint.Client; - +using Microsoft.SharePoint.Client; using PnP.PowerShell.Commands.Base; using System.Management.Automation; -using PnP.Framework.Sites; using PnP.PowerShell.Commands.Base.PipeBinds; using System; -using PnP.PowerShell.Commands.Model; -using System.Linq; -using System.Text.Json; using System.Text; using System.Collections.Generic; @@ -28,7 +22,7 @@ public class TestSite : PnPAdminCmdlet protected override void ExecuteCmdlet() { - var siteUrl = PnPConnection.Current.Url; + var siteUrl = Connection.Url; if (ParameterSpecified(nameof(Identity))) { siteUrl = Identity.Url; diff --git a/src/Commands/Apps/GetAzureADAppSitePermission.cs b/src/Commands/Apps/GetAzureADAppSitePermission.cs index 6ae9cee02..ef62d4c16 100644 --- a/src/Commands/Apps/GetAzureADAppSitePermission.cs +++ b/src/Commands/Apps/GetAzureADAppSitePermission.cs @@ -47,7 +47,7 @@ protected override void ExecuteCmdlet() if (!ParameterSpecified(nameof(PermissionId))) { // all permissions - var results = GraphHelper.GetResultCollectionAsync(Connection, $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/sites/{siteId}/permissions", AccessToken).GetAwaiter().GetResult(); + var results = GraphHelper.GetResultCollectionAsync(Connection, $"https://{Connection.GraphEndPoint}/v1.0/sites/{siteId}/permissions", AccessToken).GetAwaiter().GetResult(); if (results.Any()) { var convertedResults = results.Select(i => i.Convert()); @@ -64,7 +64,7 @@ protected override void ExecuteCmdlet() } else { - var results = GraphHelper.GetAsync(Connection, $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/sites/{siteId}/permissions/{PermissionId}", AccessToken).GetAwaiter().GetResult(); + var results = GraphHelper.GetAsync(Connection, $"https://{Connection.GraphEndPoint}/v1.0/sites/{siteId}/permissions/{PermissionId}", AccessToken).GetAwaiter().GetResult(); WriteObject(results.Convert()); } } diff --git a/src/Commands/Apps/GrantAzureADAppSitePermission.cs b/src/Commands/Apps/GrantAzureADAppSitePermission.cs index 9537d69af..d14554613 100644 --- a/src/Commands/Apps/GrantAzureADAppSitePermission.cs +++ b/src/Commands/Apps/GrantAzureADAppSitePermission.cs @@ -56,7 +56,7 @@ protected override void ExecuteCmdlet() } }; - var results = PnP.PowerShell.Commands.Utilities.REST.RestHelper.PostAsync(Connection.HttpClient, $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/sites/{siteId}/permissions", AccessToken, payload).GetAwaiter().GetResult(); + var results = PnP.PowerShell.Commands.Utilities.REST.RestHelper.PostAsync(Connection.HttpClient, $"https://{Connection.GraphEndPoint}/v1.0/sites/{siteId}/permissions", AccessToken, payload).GetAwaiter().GetResult(); WriteObject(results.Convert()); } } diff --git a/src/Commands/Apps/RevokeAzureADAppSitePermission.cs b/src/Commands/Apps/RevokeAzureADAppSitePermission.cs index 90a71a2df..45a4c7241 100644 --- a/src/Commands/Apps/RevokeAzureADAppSitePermission.cs +++ b/src/Commands/Apps/RevokeAzureADAppSitePermission.cs @@ -37,7 +37,7 @@ protected override void ExecuteCmdlet() { if (Force || ShouldContinue("Are you sure you want to revoke the permissions?", string.Empty)) { - var results = PnP.PowerShell.Commands.Utilities.REST.RestHelper.DeleteAsync(Connection.HttpClient, $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/sites/{siteId}/permissions/{PermissionId}", AccessToken).GetAwaiter().GetResult(); + var results = PnP.PowerShell.Commands.Utilities.REST.RestHelper.DeleteAsync(Connection.HttpClient, $"https://{Connection.GraphEndPoint}/v1.0/sites/{siteId}/permissions/{PermissionId}", AccessToken).GetAwaiter().GetResult(); } } } diff --git a/src/Commands/Apps/SetAzureADAppSitePermission.cs b/src/Commands/Apps/SetAzureADAppSitePermission.cs index 20738b65b..83f074a64 100644 --- a/src/Commands/Apps/SetAzureADAppSitePermission.cs +++ b/src/Commands/Apps/SetAzureADAppSitePermission.cs @@ -44,7 +44,7 @@ protected override void ExecuteCmdlet() roles = Permissions.Select(p => p.ToLower()).ToArray() }; - var results = PnP.PowerShell.Commands.Utilities.REST.RestHelper.PatchAsync(Connection.HttpClient, $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/sites/{siteId}/permissions/{PermissionId}", AccessToken, payload).GetAwaiter().GetResult(); + var results = PnP.PowerShell.Commands.Utilities.REST.RestHelper.PatchAsync(Connection.HttpClient, $"https://{Connection.GraphEndPoint}/v1.0/sites/{siteId}/permissions/{PermissionId}", AccessToken, payload).GetAwaiter().GetResult(); WriteObject(results.Convert()); } } diff --git a/src/Commands/AzureAD/AddAzureADGroupMember.cs b/src/Commands/AzureAD/AddAzureADGroupMember.cs index be81fcb8c..6e7858384 100644 --- a/src/Commands/AzureAD/AddAzureADGroupMember.cs +++ b/src/Commands/AzureAD/AddAzureADGroupMember.cs @@ -22,9 +22,9 @@ public class AddAzureADGroupMember : PnPGraphCmdlet protected override void ExecuteCmdlet() { - if (PnPConnection.Current.ClientId == PnPConnection.PnPManagementShellClientId) + if (Connection.ClientId == PnPConnection.PnPManagementShellClientId) { - PnPConnection.Current.Scopes = new[] { "Group.ReadWrite.All" }; + Connection.Scopes = new[] { "Group.ReadWrite.All" }; } AzureADGroup group = null; diff --git a/src/Commands/Base/GetAccessToken.cs b/src/Commands/Base/GetAccessToken.cs index c26d349b2..119e66f85 100644 --- a/src/Commands/Base/GetAccessToken.cs +++ b/src/Commands/Base/GetAccessToken.cs @@ -46,7 +46,7 @@ protected override void ExecuteCmdlet() accessTokenValue = AccessToken; break; case ResourceTypeName.SharePoint: - accessTokenValue = TokenHandler.GetAccessToken(null, PnPConnection.Current?.Context?.Url?.TrimEnd('/') + "/.default", Connection); + accessTokenValue = TokenHandler.GetAccessToken(null, Connection?.Context?.Url?.TrimEnd('/') + "/.default", Connection); break; case ResourceTypeName.ARM: accessTokenValue = TokenHandler.GetAccessToken(null, "https://management.azure.com/.default", Connection); diff --git a/src/Commands/Base/GetDiagnostics.cs b/src/Commands/Base/GetDiagnostics.cs index 9a806fbe9..65b9a3184 100644 --- a/src/Commands/Base/GetDiagnostics.cs +++ b/src/Commands/Base/GetDiagnostics.cs @@ -1,11 +1,7 @@ -using PnP.PowerShell.Commands.Enums; using PnP.PowerShell.Commands.Model; using PnP.PowerShell.Commands.Utilities; - using System; using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Management.Automation; diff --git a/src/Commands/Base/GetPowerShellTelemetryEnabled.cs b/src/Commands/Base/GetPowerShellTelemetryEnabled.cs index 9e2845dde..0f2a2fb02 100644 --- a/src/Commands/Base/GetPowerShellTelemetryEnabled.cs +++ b/src/Commands/Base/GetPowerShellTelemetryEnabled.cs @@ -8,7 +8,7 @@ public class GetPowerShellTelemetryEnabled : PnPSharePointCmdlet { protected override void ProcessRecord() { - WriteObject(PnPConnection.Current.ApplicationInsights != null); + WriteObject(Connection.ApplicationInsights != null); } } } \ No newline at end of file diff --git a/src/Commands/Base/PipeBinds/PagePipeBind.cs b/src/Commands/Base/PipeBinds/PagePipeBind.cs index b381983cf..2e809f6d7 100644 --- a/src/Commands/Base/PipeBinds/PagePipeBind.cs +++ b/src/Commands/Base/PipeBinds/PagePipeBind.cs @@ -1,12 +1,7 @@ -using Microsoft.SharePoint.Client; -using PnP.Core.Model.SharePoint; -using PnP.Core.Services; +using PnP.Core.Model.SharePoint; using PnP.PowerShell.Commands.Pages; using System; -using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace PnP.PowerShell.Commands.Base.PipeBinds { @@ -33,9 +28,9 @@ public PagePipeBind(string name) public override string ToString() => Name; - internal IPage GetPage() + internal IPage GetPage(PnPConnection connection) { - var ctx = PnPConnection.Current.PnPContext; + var ctx = connection.PnPContext; if (_page != null) { return _page; diff --git a/src/Commands/Base/PipeBinds/SitePipeBind.cs b/src/Commands/Base/PipeBinds/SitePipeBind.cs index 45da4e576..2058881c8 100644 --- a/src/Commands/Base/PipeBinds/SitePipeBind.cs +++ b/src/Commands/Base/PipeBinds/SitePipeBind.cs @@ -70,7 +70,7 @@ public Guid GetSiteIdThroughGraph(PnPConnection connection, string accesstoken) { var uri = new Uri(_url); - var result = Utilities.REST.RestHelper.GetAsync(connection.HttpClient, $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/sites/{uri.Host}:{uri.LocalPath}", accesstoken).GetAwaiter().GetResult(); + var result = Utilities.REST.RestHelper.GetAsync(connection.HttpClient, $"https://{connection.GraphEndPoint}/v1.0/sites/{uri.Host}:{uri.LocalPath}", accesstoken).GetAwaiter().GetResult(); if (!string.IsNullOrEmpty(result)) { var resultElement = JsonSerializer.Deserialize(result); diff --git a/src/Commands/Base/PipeBinds/SyntexModelPipeBind.cs b/src/Commands/Base/PipeBinds/SyntexModelPipeBind.cs index 135490cde..4fb418425 100644 --- a/src/Commands/Base/PipeBinds/SyntexModelPipeBind.cs +++ b/src/Commands/Base/PipeBinds/SyntexModelPipeBind.cs @@ -34,7 +34,7 @@ public SyntexModelPipeBind(int id) public override string ToString() => Name; - internal ISyntexModel GetSyntexModel() + internal ISyntexModel GetSyntexModel(PnPConnection connection) { if (syntexModel != null) { @@ -42,14 +42,14 @@ internal ISyntexModel GetSyntexModel() } else if (syntexModelId > 0) { - var ctx = PnPConnection.Current.PnPContext; + var ctx = connection.PnPContext; var syntexContentCenter = ctx.Web.AsSyntexContentCenter(); var models = syntexContentCenter.GetSyntexModels(); return models.FirstOrDefault(p => p.Id == syntexModelId); } else if (!string.IsNullOrEmpty(syntexModelName)) { - var ctx = PnPConnection.Current.PnPContext; + var ctx = connection.PnPContext; var syntexContentCenter = ctx.Web.AsSyntexContentCenter(); var models = syntexContentCenter.GetSyntexModels(); return models.FirstOrDefault(p => p.Name.Equals(syntexModelName, StringComparison.InvariantCultureIgnoreCase)); @@ -60,7 +60,7 @@ internal ISyntexModel GetSyntexModel() } } - internal ISyntexModel GetSyntexModel(PnPBatch batch) + internal ISyntexModel GetSyntexModel(PnPBatch batch, PnPConnection connection) { if (syntexModel != null) { @@ -74,7 +74,7 @@ internal ISyntexModel GetSyntexModel(PnPBatch batch) return batchedSyntexModel; } - var ctx = PnPConnection.Current.PnPContext; + var ctx = connection.PnPContext; var syntexContentCenter = ctx.Web.AsSyntexContentCenter(); var models = syntexContentCenter.GetSyntexModels(); var syntexModel = models.FirstOrDefault(p => p.Id == syntexModelId); @@ -89,7 +89,7 @@ internal ISyntexModel GetSyntexModel(PnPBatch batch) return batchedSyntexModel; } - var ctx = PnPConnection.Current.PnPContext; + var ctx = connection.PnPContext; var syntexContentCenter = ctx.Web.AsSyntexContentCenter(); var models = syntexContentCenter.GetSyntexModels(); var syntexModel = models.FirstOrDefault(p => p.Name.Equals(syntexModelName, StringComparison.InvariantCultureIgnoreCase)); diff --git a/src/Commands/Base/RequestAccessToken.cs b/src/Commands/Base/RequestAccessToken.cs index b52b1baf8..59e2a97d6 100644 --- a/src/Commands/Base/RequestAccessToken.cs +++ b/src/Commands/Base/RequestAccessToken.cs @@ -3,7 +3,6 @@ using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Management.Automation; -using System.Net.Http; using System.Security; using Microsoft.SharePoint.Client; @@ -12,7 +11,6 @@ namespace PnP.PowerShell.Commands.Base [Cmdlet(VerbsLifecycle.Request, "PnPAccessToken")] public class RequestAccessToken : PnPConnectedCmdlet { - [Parameter(Mandatory = false)] public string ClientId = PnPConnection.PnPManagementShellClientId; // defaults to PnPManagementShell @@ -36,12 +34,11 @@ public class RequestAccessToken : PnPConnectedCmdlet protected override void ProcessRecord() { - Uri tenantUri = null; - if (string.IsNullOrEmpty(TenantUrl) && PnPConnection.Current != null) + if (string.IsNullOrEmpty(TenantUrl) && Connection != null) { - var uri = new Uri(PnPConnection.Current.Url); + var uri = new Uri(Connection.Url); var uriParts = uri.Host.Split('.'); if (uriParts[0].ToLower().EndsWith("-admin")) { @@ -73,9 +70,9 @@ protected override void ProcessRecord() username = Credentials.UserName; authManager = new AuthenticationManager(ClientId, username, password, azureEnvironment: AzureEnvironment); } - else if (PnPConnection.Current != null) + else if (Connection != null) { - authManager = PnPConnection.Current.Context.GetContextSettings().AuthenticationManager; + authManager = Connection.Context.GetContextSettings().AuthenticationManager; } else { @@ -92,7 +89,7 @@ protected override void ProcessRecord() } else { - token = authManager.GetAccessTokenAsync(PnPConnection.Current.Url).GetAwaiter().GetResult(); + token = authManager.GetAccessTokenAsync(Connection.Url).GetAwaiter().GetResult(); } } diff --git a/src/Commands/InvokeAction/InvokeWebAction.cs b/src/Commands/InvokeAction/InvokeWebAction.cs index 945c1d7e4..6b35f1813 100644 --- a/src/Commands/InvokeAction/InvokeWebAction.cs +++ b/src/Commands/InvokeAction/InvokeWebAction.cs @@ -82,14 +82,14 @@ public InvokeWebAction(Cmdlet cmdlet, Web web, string listName, InvokeActionPara _isListNameSpecified = true; } - public InvokeWebActionResult StartProcessAction() + public InvokeWebActionResult StartProcessAction(PnPConnection connection) { _totalExecutionTimeStopWatch = Stopwatch.StartNew(); _result = new InvokeWebActionResult(); _result.StartDate = DateTime.Now; - ClientContext previousContext = PnPConnection.Current.Context; + ClientContext previousContext = connection.Context; UpdatePropertiesToLoad(); @@ -102,12 +102,12 @@ public InvokeWebActionResult StartProcessAction() if (!_skipCounting) CountItems(webs); - ProcessAction(webs); + ProcessAction(webs, connection); UpdateResult(); //Reset context to where the user were before. - PnPConnection.Current.Context = previousContext; + connection.Context = previousContext; return _result; } @@ -195,7 +195,7 @@ private void CountItems(List webs) } } - private void ProcessAction(List webs) + private void ProcessAction(List webs, PnPConnection connection) { bool processAction; int webCount = webs.Count; @@ -205,7 +205,7 @@ private void ProcessAction(List webs) //Update current connection context to the web that is beeing process //So commands like Get-PnPList returns the correct list for the current web beeing proccess - PnPConnection.Current.Context = (ClientContext) currentWeb.Context; + connection.Context = (ClientContext) currentWeb.Context; currentWeb.LoadProperties(_webActions.Properties); diff --git a/src/Commands/Lists/CopyList.cs b/src/Commands/Lists/CopyList.cs index 17a64b6db..270ab5929 100644 --- a/src/Commands/Lists/CopyList.cs +++ b/src/Commands/Lists/CopyList.cs @@ -56,7 +56,7 @@ protected override void ExecuteCmdlet() } // Define the full URL to the list to copy - var hostUri = new Uri(PnPConnection.Current.Url); + var hostUri = new Uri(Connection.Url); SourceListUrl = $"{hostUri.Scheme}://{hostUri.Authority}{list.RootFolder.ServerRelativeUrl}"; } @@ -85,7 +85,7 @@ protected override void ExecuteCmdlet() // Check if we need to set the destination to the current site if(ParameterSetName == ParameterSet_TOCURRENTSITEBYPIPE || ParameterSetName == ParameterSet_TOCURRENTSITEBYURL) { - DestinationWebUrl = PnPConnection.Current.Url; + DestinationWebUrl = Connection.Url; } if(ParameterSpecified(nameof(WhatIf))) diff --git a/src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs b/src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs index 45891aa1a..b120f4984 100644 --- a/src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs +++ b/src/Commands/Microsoft365Groups/NewMicrosoft365Group.cs @@ -107,7 +107,7 @@ protected override void ExecuteCmdlet() } var Labels = new List(); - var contextSettings = PnPConnection.Current.Context.GetContextSettings(); + var contextSettings = Connection.Context.GetContextSettings(); if (SensitivityLabels != null && SensitivityLabels.Length > 0) { if (contextSettings.Type != Framework.Utilities.Context.ClientContextType.AzureADCertificate) diff --git a/src/Commands/Microsoft365Groups/SetMicrosoft365Group.cs b/src/Commands/Microsoft365Groups/SetMicrosoft365Group.cs index 98fc01456..63d48f047 100644 --- a/src/Commands/Microsoft365Groups/SetMicrosoft365Group.cs +++ b/src/Commands/Microsoft365Groups/SetMicrosoft365Group.cs @@ -120,7 +120,7 @@ protected override void ExecuteCmdlet() var assignedLabels = new List(); if (SensitivityLabels != null && SensitivityLabels.Length > 0) { - var contextSettings = PnPConnection.Current.Context.GetContextSettings(); + var contextSettings = Connection.Context.GetContextSettings(); if (contextSettings.Type != Framework.Utilities.Context.ClientContextType.AzureADCertificate) { foreach (var label in SensitivityLabels) diff --git a/src/Commands/Navigation/GetStructuralNavigationCacheSiteState.cs b/src/Commands/Navigation/GetStructuralNavigationCacheSiteState.cs index 946000c22..b85e82ae5 100644 --- a/src/Commands/Navigation/GetStructuralNavigationCacheSiteState.cs +++ b/src/Commands/Navigation/GetStructuralNavigationCacheSiteState.cs @@ -13,7 +13,7 @@ public class GetStructuralNavigationCacheSiteState : PnPAdminCmdlet protected override void ExecuteCmdlet() { - var url = PnPConnection.Current.Url; + var url = Connection.Url; if (ParameterSpecified(nameof(SiteUrl))) { url = SiteUrl; diff --git a/src/Commands/Navigation/GetStructuralNavigationCacheWebState.cs b/src/Commands/Navigation/GetStructuralNavigationCacheWebState.cs index 8d630b582..7c09313bc 100644 --- a/src/Commands/Navigation/GetStructuralNavigationCacheWebState.cs +++ b/src/Commands/Navigation/GetStructuralNavigationCacheWebState.cs @@ -13,7 +13,7 @@ public class GetStructuralNavigationCacheWebState : PnPAdminCmdlet protected override void ExecuteCmdlet() { - var url = PnPConnection.Current.Url; + var url = Connection.Url; if (ParameterSpecified(nameof(WebUrl))) { url = WebUrl; diff --git a/src/Commands/Navigation/SetStructuralNavigationCacheSiteState.cs b/src/Commands/Navigation/SetStructuralNavigationCacheSiteState.cs index 24a42f9a4..7488f81bf 100644 --- a/src/Commands/Navigation/SetStructuralNavigationCacheSiteState.cs +++ b/src/Commands/Navigation/SetStructuralNavigationCacheSiteState.cs @@ -16,7 +16,7 @@ public class SetStructuralNavigationCacheSiteState : PnPAdminCmdlet protected override void ExecuteCmdlet() { - var url = PnPConnection.Current.Url; + var url = Connection.Url; if (ParameterSpecified(nameof(SiteUrl))) { url = SiteUrl; diff --git a/src/Commands/Navigation/SetStructuralNavigationCacheWebState.cs b/src/Commands/Navigation/SetStructuralNavigationCacheWebState.cs index 76c7da552..14ffd5906 100644 --- a/src/Commands/Navigation/SetStructuralNavigationCacheWebState.cs +++ b/src/Commands/Navigation/SetStructuralNavigationCacheWebState.cs @@ -16,7 +16,7 @@ public class SetStructuralNavigationCacheWebState : PnPAdminCmdlet protected override void ExecuteCmdlet() { - var url = PnPConnection.Current.Url; + var url = Connection.Url; if (ParameterSpecified(nameof(WebUrl))) { url = WebUrl; diff --git a/src/Commands/Pages/AddPageSection.cs b/src/Commands/Pages/AddPageSection.cs index 387ee9347..7be050ce9 100644 --- a/src/Commands/Pages/AddPageSection.cs +++ b/src/Commands/Pages/AddPageSection.cs @@ -22,7 +22,7 @@ public class AddPageSection : PnPWebCmdlet protected override void ExecuteCmdlet() { - var page = Page?.GetPage(); + var page = Page?.GetPage(Connection); if (page != null) { @@ -34,7 +34,6 @@ protected override void ExecuteCmdlet() // If the client side page object cannot be found throw new Exception($"Page {Page} cannot be found."); } - } } } \ No newline at end of file diff --git a/src/Commands/Pages/AddPageTextPart.cs b/src/Commands/Pages/AddPageTextPart.cs index f6efccd81..f603a4b38 100644 --- a/src/Commands/Pages/AddPageTextPart.cs +++ b/src/Commands/Pages/AddPageTextPart.cs @@ -40,7 +40,7 @@ protected override void ExecuteCmdlet() throw new Exception("Column value should be at least 1 or higher"); } - var clientSidePage = Page.GetPage(); + var clientSidePage = Page.GetPage(Connection); if (clientSidePage == null) // If the client side page object cannot be found diff --git a/src/Commands/Pages/AddPageWebPart.cs b/src/Commands/Pages/AddPageWebPart.cs index ac11d120d..c407b45d9 100644 --- a/src/Commands/Pages/AddPageWebPart.cs +++ b/src/Commands/Pages/AddPageWebPart.cs @@ -58,7 +58,7 @@ protected override void ExecuteCmdlet() throw new Exception("Column value should be at least 1 or higher"); } - var clientSidePage = Page.GetPage(); + var clientSidePage = Page.GetPage(Connection); // If the client side page object cannot be found if (clientSidePage == null) { diff --git a/src/Commands/Pages/GetAvailablePageComponents.cs b/src/Commands/Pages/GetAvailablePageComponents.cs index 4ed52deeb..9c71cee22 100644 --- a/src/Commands/Pages/GetAvailablePageComponents.cs +++ b/src/Commands/Pages/GetAvailablePageComponents.cs @@ -1,6 +1,5 @@  using PnP.PowerShell.Commands.Base.PipeBinds; -using System; using System.Linq; using System.Management.Automation; @@ -17,7 +16,7 @@ public class GetAvailablePageComponents : PnPWebCmdlet protected override void ExecuteCmdlet() { - var clientSidePage = Page.GetPage(); + var clientSidePage = Page.GetPage(Connection); if (clientSidePage == null) throw new PSArgumentException($"Page '{Page}' does not exist", "List"); diff --git a/src/Commands/Pages/GetPage.cs b/src/Commands/Pages/GetPage.cs index 7008026c9..145262ebb 100644 --- a/src/Commands/Pages/GetPage.cs +++ b/src/Commands/Pages/GetPage.cs @@ -14,7 +14,7 @@ public class GetPage : PnPWebCmdlet protected override void ExecuteCmdlet() { - var clientSidePage = Identity.GetPage(); + var clientSidePage = Identity.GetPage(Connection); if (clientSidePage == null) throw new Exception($"Page '{Identity?.Name}' does not exist"); diff --git a/src/Commands/Pages/GetPageComponent.cs b/src/Commands/Pages/GetPageComponent.cs index 4366b70cb..2780bdf29 100644 --- a/src/Commands/Pages/GetPageComponent.cs +++ b/src/Commands/Pages/GetPageComponent.cs @@ -23,7 +23,7 @@ public class GetPageComponent : PnPWebCmdlet protected override void ExecuteCmdlet() { - var clientSidePage = Page.GetPage(); + var clientSidePage = Page.GetPage(Connection); if (clientSidePage == null) throw new Exception($"Page '{Page?.Name}' does not exist"); diff --git a/src/Commands/Pages/MovePageComponent.cs b/src/Commands/Pages/MovePageComponent.cs index 8caa44e79..7e39a745b 100644 --- a/src/Commands/Pages/MovePageComponent.cs +++ b/src/Commands/Pages/MovePageComponent.cs @@ -37,10 +37,12 @@ public class MovePageComponent : PnPWebCmdlet protected override void ExecuteCmdlet() { - var clientSidePage = Page.GetPage(); + var clientSidePage = Page.GetPage(Connection); if (clientSidePage == null) + { throw new Exception($"Page '{Page?.Name}' does not exist"); + } var control = clientSidePage.Controls.FirstOrDefault(c => c.InstanceId == InstanceId); if (control != null) diff --git a/src/Commands/Pages/RemovePage.cs b/src/Commands/Pages/RemovePage.cs index 2b54f2b47..3cbb759ae 100644 --- a/src/Commands/Pages/RemovePage.cs +++ b/src/Commands/Pages/RemovePage.cs @@ -30,7 +30,7 @@ protected override void ExecuteCmdlet() { if (Force || ShouldContinue(Resources.RemoveClientSidePage, Resources.Confirm)) { - var clientSidePage = Identity.GetPage(); + var clientSidePage = Identity.GetPage(Connection); if (clientSidePage == null) throw new Exception($"Page '{Identity?.Name}' does not exist"); diff --git a/src/Commands/Pages/RemovePageComponent.cs b/src/Commands/Pages/RemovePageComponent.cs index 8d2ae876f..15abf2276 100644 --- a/src/Commands/Pages/RemovePageComponent.cs +++ b/src/Commands/Pages/RemovePageComponent.cs @@ -23,7 +23,7 @@ public class RemovePageComponent : PnPWebCmdlet protected override void ExecuteCmdlet() { - var clientSidePage = Page.GetPage(); + var clientSidePage = Page.GetPage(Connection); if (clientSidePage == null) throw new Exception($"Page '{Page?.Name}' does not exist"); diff --git a/src/Commands/Pages/SetPage.cs b/src/Commands/Pages/SetPage.cs index ecf5b204a..5842b650b 100644 --- a/src/Commands/Pages/SetPage.cs +++ b/src/Commands/Pages/SetPage.cs @@ -2,15 +2,12 @@ using PnP.PowerShell.Commands.Base.PipeBinds; using System; using System.Management.Automation; -using Microsoft.SharePoint.Client; -using PnP.PowerShell.Commands.Attributes; using System.Collections.Generic; namespace PnP.PowerShell.Commands.Pages { [Cmdlet(VerbsCommon.Set, "PnPPage")] [Alias("Set-PnPClientSidePage")] - public class SetPage : PnPWebCmdlet, IDynamicParameters { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] @@ -76,7 +73,7 @@ public object GetDynamicParameters() protected override void ExecuteCmdlet() { - var clientSidePage = Identity?.GetPage(); + var clientSidePage = Identity?.GetPage(Connection); if (clientSidePage == null) { diff --git a/src/Commands/Pages/SetPageTextPart.cs b/src/Commands/Pages/SetPageTextPart.cs index 95eab1d08..1995a8606 100644 --- a/src/Commands/Pages/SetPageTextPart.cs +++ b/src/Commands/Pages/SetPageTextPart.cs @@ -23,7 +23,7 @@ public class SetClientSideText : PnPWebCmdlet protected override void ExecuteCmdlet() { - var clientSidePage = Page.GetPage(); + var clientSidePage = Page.GetPage(Connection); if (clientSidePage == null) throw new Exception($"Page '{Page?.Name}' does not exist"); diff --git a/src/Commands/Pages/SetPageWebPart.cs b/src/Commands/Pages/SetPageWebPart.cs index a6d342d5f..5e621347f 100644 --- a/src/Commands/Pages/SetPageWebPart.cs +++ b/src/Commands/Pages/SetPageWebPart.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Management.Automation; using PnP.Core.Model.SharePoint; @@ -27,7 +26,7 @@ public class SetClientSideWebPart : PnPWebCmdlet protected override void ExecuteCmdlet() { - var clientSidePage = Page.GetPage(); + var clientSidePage = Page.GetPage(Connection); if (clientSidePage == null) throw new Exception($"Page '{Page?.Name}' does not exist"); @@ -57,7 +56,6 @@ protected override void ExecuteCmdlet() { clientSidePage.Save(); } - } else { diff --git a/src/Commands/Principals/ExportUserInfo.cs b/src/Commands/Principals/ExportUserInfo.cs index 0c2f08ce8..2b5129dec 100644 --- a/src/Commands/Principals/ExportUserInfo.cs +++ b/src/Commands/Principals/ExportUserInfo.cs @@ -18,7 +18,7 @@ public class ExportUserInfo : PnPAdminCmdlet protected override void ExecuteCmdlet() { - var siteUrl = PnPConnection.Current.Url; + var siteUrl = Connection.Url; if(ParameterSpecified(Site)) { siteUrl = Site; diff --git a/src/Commands/Principals/GetSiteGroup.cs b/src/Commands/Principals/GetSiteGroup.cs index 264f0ceb6..ed87588a1 100644 --- a/src/Commands/Principals/GetSiteGroup.cs +++ b/src/Commands/Principals/GetSiteGroup.cs @@ -19,7 +19,7 @@ public class GetSiteGroup : PnPAdminCmdlet protected override void ExecuteCmdlet() { - var url = PnPConnection.Current.Url; + var url = Connection.Url; if (ParameterSpecified(nameof(Site))) { url = Site.Url; diff --git a/src/Commands/Principals/NewSiteGroup.cs b/src/Commands/Principals/NewSiteGroup.cs index 471ea19b3..0d7940200 100644 --- a/src/Commands/Principals/NewSiteGroup.cs +++ b/src/Commands/Principals/NewSiteGroup.cs @@ -24,7 +24,7 @@ public class NewSiteGroup : PnPAdminCmdlet protected override void ExecuteCmdlet() { - var url = PnPConnection.Current.Url; + var url = Connection.Url; if (ParameterSpecified(nameof(Site))) { url = Site.Url; diff --git a/src/Commands/Principals/RemoveSiteGroup.cs b/src/Commands/Principals/RemoveSiteGroup.cs index 7a91f6b37..e27280741 100644 --- a/src/Commands/Principals/RemoveSiteGroup.cs +++ b/src/Commands/Principals/RemoveSiteGroup.cs @@ -18,7 +18,7 @@ public class RemoveSiteGroup : PnPAdminCmdlet protected override void ExecuteCmdlet() { - var url = PnPConnection.Current.Url; + var url = Connection.Url; if (ParameterSpecified(nameof(Site))) { url = Site.Url; diff --git a/src/Commands/Principals/RemoveUserInfo.cs b/src/Commands/Principals/RemoveUserInfo.cs index 8ecfd7f26..64c8d5464 100644 --- a/src/Commands/Principals/RemoveUserInfo.cs +++ b/src/Commands/Principals/RemoveUserInfo.cs @@ -21,7 +21,7 @@ public class RemoveUserInfo : PnPAdminCmdlet protected override void ExecuteCmdlet() { - var siteUrl = PnPConnection.Current.Url; + var siteUrl = Connection.Url; if (ParameterSpecified(Site)) { siteUrl = Site; diff --git a/src/Commands/Principals/SetSiteGroup.cs b/src/Commands/Principals/SetSiteGroup.cs index 234965526..34966a4f1 100644 --- a/src/Commands/Principals/SetSiteGroup.cs +++ b/src/Commands/Principals/SetSiteGroup.cs @@ -31,7 +31,7 @@ public class SetSiteGroup : PnPAdminCmdlet public string[] PermissionLevelsToRemove; protected override void ExecuteCmdlet() { - var url = PnPConnection.Current.Url; + var url = Connection.Url; if (ParameterSpecified(nameof(Site))) { url = Site.Url; diff --git a/src/Commands/Provisioning/Site/GetSiteTemplate.cs b/src/Commands/Provisioning/Site/GetSiteTemplate.cs index 4dab89414..573d73a3f 100644 --- a/src/Commands/Provisioning/Site/GetSiteTemplate.cs +++ b/src/Commands/Provisioning/Site/GetSiteTemplate.cs @@ -365,8 +365,8 @@ private void ExtractTemplate(XMLPnPSchemaVersion schema, string path, string pac ProvisioningTemplate template = null; using (var provisioningContext = new PnPProvisioningContext(async (resource, scope) => { - return await TokenRetrieval.GetAccessTokenAsync(resource, scope); - }, azureEnvironment: PnPConnection.Current.AzureEnvironment)) + return await TokenRetrieval.GetAccessTokenAsync(resource, scope, Connection); + }, azureEnvironment: Connection.AzureEnvironment)) { template = CurrentWeb.GetProvisioningTemplate(creationInformation); } diff --git a/src/Commands/Provisioning/Site/InvokeSiteTemplate.cs b/src/Commands/Provisioning/Site/InvokeSiteTemplate.cs index 3d20a840b..15ef7f363 100644 --- a/src/Commands/Provisioning/Site/InvokeSiteTemplate.cs +++ b/src/Commands/Provisioning/Site/InvokeSiteTemplate.cs @@ -321,8 +321,8 @@ protected override void ExecuteCmdlet() using (var provisioningContext = new PnPProvisioningContext(async (resource, scope) => { - return await TokenRetrieval.GetAccessTokenAsync(resource, scope); - }, azureEnvironment: PnPConnection.Current.AzureEnvironment)) + return await TokenRetrieval.GetAccessTokenAsync(resource, scope, Connection); + }, azureEnvironment: Connection.AzureEnvironment)) { CurrentWeb.ApplyProvisioningTemplate(provisioningTemplate, applyingInformation); } diff --git a/src/Commands/Provisioning/Tenant/GetTenantTemplate.cs b/src/Commands/Provisioning/Tenant/GetTenantTemplate.cs index 0e542410c..0495636dc 100644 --- a/src/Commands/Provisioning/Tenant/GetTenantTemplate.cs +++ b/src/Commands/Provisioning/Tenant/GetTenantTemplate.cs @@ -5,14 +5,12 @@ using PnP.Framework.Provisioning.Connectors; using PnP.Framework.Provisioning.Model; using PnP.Framework.Provisioning.ObjectHandlers; - using PnP.Framework.Provisioning.Providers.Xml; using File = System.IO.File; using Resources = PnP.PowerShell.Commands.Properties.Resources; using PnP.PowerShell.Commands.Base.PipeBinds; using PnP.Framework.Provisioning.Model.Configuration; using PnP.PowerShell.Commands.Base; -using System.Threading.Tasks; namespace PnP.PowerShell.Commands.Provisioning.Site { @@ -170,12 +168,11 @@ private ProvisioningHierarchy ExtractTemplate(ExtractConfiguration configuration }; using (var provisioningContext = new PnPProvisioningContext(async (resource, scope) => { - return await TokenRetrieval.GetAccessTokenAsync(resource, scope); - }, azureEnvironment: PnPConnection.Current.AzureEnvironment)) + return await TokenRetrieval.GetAccessTokenAsync(resource, scope, Connection); + }, azureEnvironment: Connection.AzureEnvironment)) { return Tenant.GetTenantTemplate(configuration); } } } - } \ No newline at end of file diff --git a/src/Commands/Provisioning/Tenant/InvokeTenantTemplate.cs b/src/Commands/Provisioning/Tenant/InvokeTenantTemplate.cs index ef5009a4c..dd3fce0bd 100644 --- a/src/Commands/Provisioning/Tenant/InvokeTenantTemplate.cs +++ b/src/Commands/Provisioning/Tenant/InvokeTenantTemplate.cs @@ -294,14 +294,14 @@ protected override void ExecuteCmdlet() } catch { - throw new PSInvalidOperationException($"Your template contains artifacts that require an access token for https://{PnPConnection.Current.GraphEndPoint}. Please provide consent to the PnP Management Shell application first by executing: Register-PnPManagementShellAccess"); + throw new PSInvalidOperationException($"Your template contains artifacts that require an access token for https://{Connection.GraphEndPoint}. Please provide consent to the PnP Management Shell application first by executing: Register-PnPManagementShellAccess"); } } using (var provisioningContext = new PnPProvisioningContext(async (resource, scope) => { - return await TokenRetrieval.GetAccessTokenAsync(resource, scope); - }, azureEnvironment: PnPConnection.Current.AzureEnvironment)) + return await TokenRetrieval.GetAccessTokenAsync(resource, scope, Connection); + }, azureEnvironment: Connection.AzureEnvironment)) { if (!string.IsNullOrEmpty(SequenceId)) { diff --git a/src/Commands/Provisioning/TokenRetrieval.cs b/src/Commands/Provisioning/TokenRetrieval.cs index 9f27c7f0d..c93dd1556 100644 --- a/src/Commands/Provisioning/TokenRetrieval.cs +++ b/src/Commands/Provisioning/TokenRetrieval.cs @@ -2,22 +2,21 @@ using PnP.PowerShell.Commands.Base; using Microsoft.SharePoint.Client; using System.Threading.Tasks; -using System.Management.Automation; namespace PnP.PowerShell.Commands.Provisioning { public static class TokenRetrieval { - public async static Task GetAccessTokenAsync(string resource, string scope) + public async static Task GetAccessTokenAsync(string resource, string scope, PnPConnection connection) { if (resource.ToLower().StartsWith("https://")) { var uri = new Uri(resource); resource = uri.Authority; } - if (PnPConnection.Current?.Context != null) + if (connection?.Context != null) { - var settings = PnPConnection.Current.Context.GetContextSettings(); + var settings = connection.Context.GetContextSettings(); var authManager = settings.AuthenticationManager; if (authManager != null) { diff --git a/src/Commands/Search/GetSearchCrawlLog.cs b/src/Commands/Search/GetSearchCrawlLog.cs index 7ce5e7a10..26675f878 100644 --- a/src/Commands/Search/GetSearchCrawlLog.cs +++ b/src/Commands/Search/GetSearchCrawlLog.cs @@ -84,14 +84,14 @@ protected override void ExecuteCmdlet() string postFilter = string.Empty; if (string.IsNullOrWhiteSpace(Filter) && ContentSource == ContentSource.Sites) { - Filter = $"https://{GetHostName()}.sharepoint.{PnP.Framework.AuthenticationManager.GetSharePointDomainSuffix(PnPConnection.Current.AzureEnvironment)}"; + Filter = $"https://{GetHostName()}.sharepoint.{PnP.Framework.AuthenticationManager.GetSharePointDomainSuffix(Connection.AzureEnvironment)}"; } int origLimit = RowLimit; if (ContentSource == ContentSource.UserProfiles) { postFilter = Filter; - Filter = $"https://{GetHostName()}-my.sharepoint.{PnP.Framework.AuthenticationManager.GetSharePointDomainSuffix(PnPConnection.Current.AzureEnvironment)}"; + Filter = $"https://{GetHostName()}-my.sharepoint.{PnP.Framework.AuthenticationManager.GetSharePointDomainSuffix(Connection.AzureEnvironment)}"; RowLimit = MaxRows; } @@ -148,13 +148,13 @@ protected override void ExecuteCmdlet() private string GetHostName() { - return new Uri(ClientContext.Url).Host.Replace("-admin", "").Replace("-public", "").Replace("-my", "").Replace($".sharepoint.{PnP.Framework.AuthenticationManager.GetSharePointDomainSuffix(PnPConnection.Current.AzureEnvironment)}", ""); + return new Uri(ClientContext.Url).Host.Replace("-admin", "").Replace("-public", "").Replace("-my", "").Replace($".sharepoint.{PnP.Framework.AuthenticationManager.GetSharePointDomainSuffix(Connection.AzureEnvironment)}", ""); } private int GetContentSourceIdForSites(DocumentCrawlLog crawlLog) { var hostName = GetHostName(); - var spContent = crawlLog.GetCrawledUrls(false, 10, $"https://{hostName}.sharepoint.{PnP.Framework.AuthenticationManager.GetSharePointDomainSuffix(PnPConnection.Current.AzureEnvironment)}/sites", true, -1, (int)LogLevel.All, -1, DateTime.Now.AddDays(-100), DateTime.Now.AddDays(1)); + var spContent = crawlLog.GetCrawledUrls(false, 10, $"https://{hostName}.sharepoint.{PnP.Framework.AuthenticationManager.GetSharePointDomainSuffix(Connection.AzureEnvironment)}/sites", true, -1, (int)LogLevel.All, -1, DateTime.Now.AddDays(-100), DateTime.Now.AddDays(1)); ClientContext.ExecuteQueryRetry(); if (spContent.Value.Rows.Count > 0) return (int)spContent.Value.Rows.First()["ContentSourceID"]; return -1; @@ -163,7 +163,7 @@ private int GetContentSourceIdForSites(DocumentCrawlLog crawlLog) private int GetContentSourceIdForUserProfiles(DocumentCrawlLog crawlLog) { var hostName = GetHostName(); - var peopleContent = crawlLog.GetCrawledUrls(false, 100, $"sps3s://{hostName}-my.sharepoint.{PnP.Framework.AuthenticationManager.GetSharePointDomainSuffix(PnPConnection.Current.AzureEnvironment)}", true, -1, (int)LogLevel.All, -1, DateTime.Now.AddDays(-100), DateTime.Now.AddDays(1)); + var peopleContent = crawlLog.GetCrawledUrls(false, 100, $"sps3s://{hostName}-my.sharepoint.{PnP.Framework.AuthenticationManager.GetSharePointDomainSuffix(Connection.AzureEnvironment)}", true, -1, (int)LogLevel.All, -1, DateTime.Now.AddDays(-100), DateTime.Now.AddDays(1)); ClientContext.ExecuteQueryRetry(); if (peopleContent.Value.Rows.Count > 0) return (int)peopleContent.Value.Rows.First()["ContentSourceID"]; return -1; diff --git a/src/Commands/Site/AddSiteClassification.cs b/src/Commands/Site/AddSiteClassification.cs index 78972b3d5..e556a1a6a 100644 --- a/src/Commands/Site/AddSiteClassification.cs +++ b/src/Commands/Site/AddSiteClassification.cs @@ -17,9 +17,9 @@ public class AddSiteClassification : PnPGraphCmdlet protected override void ExecuteCmdlet() { - if (PnPConnection.Current.ClientId == PnPConnection.PnPManagementShellClientId) + if (Connection.ClientId == PnPConnection.PnPManagementShellClientId) { - PnPConnection.Current.Scopes = new[] { "Directory.ReadWrite.All" }; + Connection.Scopes = new[] { "Directory.ReadWrite.All" }; } try diff --git a/src/Commands/Site/GetSite.cs b/src/Commands/Site/GetSite.cs index 38fa8e503..db85ec7c4 100644 --- a/src/Commands/Site/GetSite.cs +++ b/src/Commands/Site/GetSite.cs @@ -3,7 +3,6 @@ using System.Management.Automation; using Microsoft.SharePoint.Client; - namespace PnP.PowerShell.Commands.Site { [Cmdlet(VerbsCommon.Get, "PnPSite")] diff --git a/src/Commands/SiteDesigns/AddSiteDesignFromWeb.cs b/src/Commands/SiteDesigns/AddSiteDesignFromWeb.cs index 183aace0f..1e3ce9442 100644 --- a/src/Commands/SiteDesigns/AddSiteDesignFromWeb.cs +++ b/src/Commands/SiteDesigns/AddSiteDesignFromWeb.cs @@ -98,7 +98,7 @@ protected override void ExecuteCmdlet() // If no URL specified, we take the URL of the site that the current context is connected to if(!ParameterSpecified(nameof(Url))) { - Url = PnPConnection.Current.Url; + Url = Connection.Url; } // Generate site script diff --git a/src/Commands/Syntex/GetSyntexModel.cs b/src/Commands/Syntex/GetSyntexModel.cs index 37cbdd17b..d562cc855 100644 --- a/src/Commands/Syntex/GetSyntexModel.cs +++ b/src/Commands/Syntex/GetSyntexModel.cs @@ -16,13 +16,13 @@ public class GetSyntexModel : PnPWebCmdlet protected override void ExecuteCmdlet() { - var ctx = PnPConnection.Current.PnPContext; + var ctx = Connection.PnPContext; if (ctx.Web.IsSyntexContentCenter()) { if (ParameterSpecified(nameof(Identity)) && Identity != null) { - WriteObject(Identity.GetSyntexModel()); + WriteObject(Identity.GetSyntexModel(Connection)); } else { diff --git a/src/Commands/Syntex/GetSyntexModelPublication.cs b/src/Commands/Syntex/GetSyntexModelPublication.cs index a21ef813b..e54a1c947 100644 --- a/src/Commands/Syntex/GetSyntexModelPublication.cs +++ b/src/Commands/Syntex/GetSyntexModelPublication.cs @@ -15,12 +15,12 @@ public class GetSyntexModelPublication : PnPWebCmdlet protected override void ExecuteCmdlet() { - var ctx = PnPConnection.Current.PnPContext; + var ctx = Connection.PnPContext; if (ctx.Web.IsSyntexContentCenter()) { // Get the model we're publishing - ISyntexModel model = Model.GetSyntexModel(); + ISyntexModel model = Model.GetSyntexModel(Connection); if (model == null) { diff --git a/src/Commands/Syntex/PublishSyntexModel.cs b/src/Commands/Syntex/PublishSyntexModel.cs index f06368dc0..2cc04efeb 100644 --- a/src/Commands/Syntex/PublishSyntexModel.cs +++ b/src/Commands/Syntex/PublishSyntexModel.cs @@ -42,14 +42,14 @@ public class PublishSyntexModel : PnPWebCmdlet protected override void ExecuteCmdlet() { - var ctx = PnPConnection.Current.PnPContext; + var ctx = Connection.PnPContext; if (ctx.Web.IsSyntexContentCenter()) { if (ParameterSpecified(nameof(Batch))) { // Get the model we're publishing - ISyntexModel modelToPublish = Model.GetSyntexModel(Batch); + ISyntexModel modelToPublish = Model.GetSyntexModel(Batch, Connection); if (modelToPublish == null) { @@ -67,7 +67,7 @@ protected override void ExecuteCmdlet() else { // Get the model we're publishing - ISyntexModel modelToPublish = Model.GetSyntexModel(); + ISyntexModel modelToPublish = Model.GetSyntexModel(Connection); if (modelToPublish == null) { @@ -76,7 +76,7 @@ protected override void ExecuteCmdlet() // resolve the list IList listToPublishModelTo = null; - using (var listContext = PnPConnection.Current.CloneContext(ListWebUrl)) + using (var listContext = Connection.CloneContext(ListWebUrl)) { var pnpContext = PnPCoreSdk.Instance.GetPnPContext(listContext); listToPublishModelTo = List.GetList(pnpContext); diff --git a/src/Commands/Syntex/RequestSyntexClassifyAndExtract.cs b/src/Commands/Syntex/RequestSyntexClassifyAndExtract.cs index 3dc57339d..793d3f649 100644 --- a/src/Commands/Syntex/RequestSyntexClassifyAndExtract.cs +++ b/src/Commands/Syntex/RequestSyntexClassifyAndExtract.cs @@ -41,7 +41,7 @@ public class RequestSyntexClassifyAndExtract : PnPWebCmdlet protected override void ExecuteCmdlet() { var serverRelativeUrl = string.Empty; - var ctx = PnPConnection.Current.PnPContext; + var ctx = Connection.PnPContext; if (ParameterSpecified(nameof(List))) { diff --git a/src/Commands/Syntex/UnPublishSyntexModel.cs b/src/Commands/Syntex/UnPublishSyntexModel.cs index 0f2aed5d1..0bec5e5e9 100644 --- a/src/Commands/Syntex/UnPublishSyntexModel.cs +++ b/src/Commands/Syntex/UnPublishSyntexModel.cs @@ -39,14 +39,14 @@ public class UnPublishSyntexModel : PnPWebCmdlet protected override void ExecuteCmdlet() { - var ctx = PnPConnection.Current.PnPContext; + var ctx = Connection.PnPContext; if (ctx.Web.IsSyntexContentCenter()) { if (ParameterSpecified(nameof(Batch))) { // Get the model we're publishing - ISyntexModel modelToPublish = Model.GetSyntexModel(Batch); + ISyntexModel modelToPublish = Model.GetSyntexModel(Batch, Connection); if (modelToPublish == null) { @@ -63,7 +63,7 @@ protected override void ExecuteCmdlet() else { // Get the model we're publishing - ISyntexModel modelToUnPublish = Model.GetSyntexModel(); + ISyntexModel modelToUnPublish = Model.GetSyntexModel(Connection); if (modelToUnPublish == null) { @@ -72,7 +72,7 @@ protected override void ExecuteCmdlet() // resolve the list IList listToUnPublishModelFrom = null; - using (var listContext = PnPConnection.Current.CloneContext(ListWebUrl)) + using (var listContext = Connection.CloneContext(ListWebUrl)) { var pnpContext = PnPCoreSdk.Instance.GetPnPContext(listContext); listToUnPublishModelFrom = List.GetList(pnpContext); diff --git a/src/Commands/Teams/NewTeamsTeam.cs b/src/Commands/Teams/NewTeamsTeam.cs index d2c02879d..f29bd4e2f 100644 --- a/src/Commands/Teams/NewTeamsTeam.cs +++ b/src/Commands/Teams/NewTeamsTeam.cs @@ -149,7 +149,7 @@ protected override void ExecuteCmdlet() } #pragma warning restore 612, 618 - var contextSettings = PnPConnection.Current.Context.GetContextSettings(); + var contextSettings = Connection.Context.GetContextSettings(); if (contextSettings.Type == Framework.Utilities.Context.ClientContextType.AzureADCertificate) { if (SensitivityLabels != null && SensitivityLabels.Length > 0) diff --git a/src/Commands/UserProfiles/GetSubscribeSharePointNewsDigest.cs b/src/Commands/UserProfiles/GetSubscribeSharePointNewsDigest.cs index 4e11a5923..4f1ee8af6 100644 --- a/src/Commands/UserProfiles/GetSubscribeSharePointNewsDigest.cs +++ b/src/Commands/UserProfiles/GetSubscribeSharePointNewsDigest.cs @@ -31,7 +31,7 @@ protected override void ExecuteCmdlet() WriteVerbose($"Connecting to OneDrive for Business site at {properties.PersonalUrl}"); - var oneDriveContext = PnPConnection.Current.CloneContext(properties.PersonalUrl); + var oneDriveContext = Connection.CloneContext(properties.PersonalUrl); WriteVerbose("Retrieving notificationSubscriptionHiddenList list"); diff --git a/src/Commands/UserProfiles/NewUPABulkImportJob.cs b/src/Commands/UserProfiles/NewUPABulkImportJob.cs index 71a6d3135..50931cb91 100644 --- a/src/Commands/UserProfiles/NewUPABulkImportJob.cs +++ b/src/Commands/UserProfiles/NewUPABulkImportJob.cs @@ -58,7 +58,7 @@ protected override void ExecuteCmdlet() throw new InvalidEnumArgumentException(@"Path cannot be empty"); } - var webCtx = ClientContext.Clone(PnPConnection.Current.Url); + var webCtx = ClientContext.Clone(Connection.Url); var web = webCtx.Web; var webServerRelativeUrl = web.EnsureProperty(w => w.ServerRelativeUrl); if (!Folder.ToLower().StartsWith(webServerRelativeUrl)) diff --git a/src/Commands/UserProfiles/SetSubscribeSharePointNewsDigest.cs b/src/Commands/UserProfiles/SetSubscribeSharePointNewsDigest.cs index a63fca066..d894f8d21 100644 --- a/src/Commands/UserProfiles/SetSubscribeSharePointNewsDigest.cs +++ b/src/Commands/UserProfiles/SetSubscribeSharePointNewsDigest.cs @@ -34,7 +34,7 @@ protected override void ExecuteCmdlet() WriteVerbose($"Connecting to OneDrive for Business site at {properties.PersonalUrl}"); - var oneDriveContext = PnPConnection.Current.CloneContext(properties.PersonalUrl); + var oneDriveContext = Connection.CloneContext(properties.PersonalUrl); WriteVerbose("Retrieving notificationSubscriptionHiddenList list"); diff --git a/src/Commands/UserProfiles/SyncSharePointUserProfilesFromAzureActiveDirectory.cs b/src/Commands/UserProfiles/SyncSharePointUserProfilesFromAzureActiveDirectory.cs index 2b6daf6b1..d8cdf6808 100644 --- a/src/Commands/UserProfiles/SyncSharePointUserProfilesFromAzureActiveDirectory.cs +++ b/src/Commands/UserProfiles/SyncSharePointUserProfilesFromAzureActiveDirectory.cs @@ -87,7 +87,7 @@ protected override void ExecuteCmdlet() } // Create a ClientContext connecting to the site specified through the Connect-PnPOnline cmdlet instead of the current potential Admin ClientContext. - var nonAdminClientContext = ClientContext.Clone(PnPConnection.Current.Url); + var nonAdminClientContext = ClientContext.Clone(Connection.Url); // Perform the mapping and execute the sync operation WriteVerbose($"Creating mapping file{(WhatIf.ToBool() ? " and" : ",")} uploading it to SharePoint Online to folder '{Folder}'{(WhatIf.ToBool() ? "" : " and executing sync job")}"); diff --git a/src/Commands/Utilities/Microsoft365GroupsUtility.cs b/src/Commands/Utilities/Microsoft365GroupsUtility.cs index c6969075b..a3b1c5231 100644 --- a/src/Commands/Utilities/Microsoft365GroupsUtility.cs +++ b/src/Commands/Utilities/Microsoft365GroupsUtility.cs @@ -181,7 +181,7 @@ private static async Task AddUsersToGroupAsync(string groupName, PnPConnection c var postData = new Dictionary() { { - "@odata.id", $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/users/{idProperty.GetString()}" + "@odata.id", $"https://{connection.GraphEndPoint}/v1.0/users/{idProperty.GetString()}" } }; var stringContent = new StringContent(JsonSerializer.Serialize(postData)); @@ -361,7 +361,7 @@ internal static async Task GetUsersDataBindValueAsync(PnPConnection co var userids = await GetUserIdsBatched(connection, accessToken, users); if (userids.Any()) { - return userids.Select(u => $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/users/{u.Value}").ToArray(); + return userids.Select(u => $"https://{connection.GraphEndPoint}/v1.0/users/{u.Value}").ToArray(); } return null; } diff --git a/src/Commands/Utilities/TeamsUtility.cs b/src/Commands/Utilities/TeamsUtility.cs index 533166528..f8c8c35b3 100644 --- a/src/Commands/Utilities/TeamsUtility.cs +++ b/src/Commands/Utilities/TeamsUtility.cs @@ -202,7 +202,7 @@ public static async Task NewTeamAsync(string accessToken, PnPConnection co { foreach (var owner in owners) { - teamOwnersAndMembers.Add(new TeamChannelMember { Roles = new List { "owner" }, UserIdentifier = $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/users('{owner}')" }); + teamOwnersAndMembers.Add(new TeamChannelMember { Roles = new List { "owner" }, UserIdentifier = $"https://{connection.GraphEndPoint}/v1.0/users('{owner}')" }); } } @@ -210,7 +210,7 @@ public static async Task NewTeamAsync(string accessToken, PnPConnection co { foreach (var member in members) { - teamOwnersAndMembers.Add(new TeamChannelMember { Roles = new List(), UserIdentifier = $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/users('{member}')" }); + teamOwnersAndMembers.Add(new TeamChannelMember { Roles = new List(), UserIdentifier = $"https://{connection.GraphEndPoint}/v1.0/users('{member}')" }); } } @@ -266,7 +266,7 @@ private static async Task CreateGroupAsync(string accessToken, PnPConnect // Check if by now we've identified a user Id to become the owner if (!string.IsNullOrEmpty(ownerId)) { - var contextSettings = PnPConnection.Current.Context.GetContextSettings(); + var contextSettings = connection.Context.GetContextSettings(); // Still no owner identified, see if we can make the current user executing this cmdlet the owner if (contextSettings.Type != Framework.Utilities.Context.ClientContextType.AzureADCertificate) @@ -298,8 +298,8 @@ private static async Task CreateGroupAsync(string accessToken, PnPConnect // Check if we managed to define an owner for the group. If not, we'll revert to not providing an owner, which will mean that the app principal will become the owner of the Group if (!string.IsNullOrEmpty(ownerId)) { - group.Owners = new List() { $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/users/{ownerId}" }; - group.Members = new List() { $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/users/{ownerId}" }; + group.Owners = new List() { $"https://{connection.GraphEndPoint}/v1.0/users/{ownerId}" }; + group.Members = new List() { $"https://{connection.GraphEndPoint}/v1.0/users/{ownerId}" }; } if (resourceBehaviorOptions != null && resourceBehaviorOptions.Length > 0) @@ -423,7 +423,7 @@ public static async Task AddUserAsync(PnPConnection connection, string accessTok { var postData = new Dictionary() { { - "@odata.id", $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/users/{idProperty.GetString()}" + "@odata.id", $"https://{connection.GraphEndPoint}/v1.0/users/{idProperty.GetString()}" } }; var stringContent = new StringContent(JsonSerializer.Serialize(postData)); @@ -440,7 +440,7 @@ public static async Task AddUsersAsync(PnPConnection connection, string accessTo { foreach (var user in upn) { - teamChannelMember.Add(new TeamChannelMember() { Roles = new List { role }, UserIdentifier = $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/users('{user}')" }); + teamChannelMember.Add(new TeamChannelMember() { Roles = new List { role }, UserIdentifier = $"https://{connection.GraphEndPoint}/v1.0/users('{user}')" }); } if (teamChannelMember.Count > 0) { @@ -618,7 +618,7 @@ public static async Task AddChannelAsync(string accessToken, PnPCon channel.Type = "#Microsoft.Graph.channel"; var user = await GraphHelper.GetAsync(connection, $"v1.0/{GetUserGraphUrlForUPN(ownerUPN)}", accessToken); channel.Members = new List(); - channel.Members.Add(new TeamChannelMember() { Roles = new List { "owner" }, UserIdentifier = $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/users('{user.Id}')" }); + channel.Members.Add(new TeamChannelMember() { Roles = new List { "owner" }, UserIdentifier = $"https://{connection.GraphEndPoint}/v1.0/users('{user.Id}')" }); return await GraphHelper.PostAsync(connection, $"v1.0/teams/{groupId}/channels", channel, accessToken); } else @@ -719,7 +719,7 @@ public static async Task AddChannelMemberAsync(PnPConnection { var channelMember = new TeamChannelMember { - UserIdentifier = $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/users('{upn}')", + UserIdentifier = $"https://{connection.GraphEndPoint}/v1.0/users('{upn}')", }; // The role for the user. Must be owner or empty. @@ -896,7 +896,7 @@ public static async Task AddTabAsync(PnPConnection connection, string a } } tab.DisplayName = displayName; - tab.TeamsAppOdataBind = $"https://{PnPConnection.Current.GraphEndPoint}/v1.0/appCatalogs/teamsApps/{tab.TeamsAppId}"; + tab.TeamsAppOdataBind = $"https://{connection.GraphEndPoint}/v1.0/appCatalogs/teamsApps/{tab.TeamsAppId}"; return await GraphHelper.PostAsync(connection, $"v1.0/teams/{groupId}/channels/{channelId}/tabs", tab, accessToken); } #endregion diff --git a/src/Commands/Web/InvokeWebAction.cs b/src/Commands/Web/InvokeWebAction.cs index 84bbce9ff..ec9ade66b 100644 --- a/src/Commands/Web/InvokeWebAction.cs +++ b/src/Commands/Web/InvokeWebAction.cs @@ -115,7 +115,7 @@ protected override void ExecuteCmdlet() invokeAction = new InvokeAction.InvokeWebAction(this, CurrentWeb, ListName, webActions, listActions, listItemActions, SkipCounting.ToBool()); } - InvokeWebActionResult result = invokeAction.StartProcessAction(); + InvokeWebActionResult result = invokeAction.StartProcessAction(Connection); if (!DisableStatisticsOutput) { From c2e3d1a9ff651b949fbe5c4f5940fd90e281bee3 Mon Sep 17 00:00:00 2001 From: = <=> Date: Sun, 19 Jun 2022 13:06:31 +0200 Subject: [PATCH 400/458] Trying to fix build error --- documentation/Get-PnPTenantId.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/documentation/Get-PnPTenantId.md b/documentation/Get-PnPTenantId.md index 0fbfca515..78f9349c3 100644 --- a/documentation/Get-PnPTenantId.md +++ b/documentation/Get-PnPTenantId.md @@ -19,15 +19,11 @@ Returns the Tenant ID Get-PnPTenantId -TenantUrl [] ``` -Allows passing in a tenant url such as https://contoso.sharepoint.com to retrieve the tenant Id of - ### By connection ```powershell Get-PnPTenantId [-Connection ] [] ``` -Allows returning the tenantId of the tenant currently connected to in the current context or through the provided connection - ## DESCRIPTION ## EXAMPLES @@ -37,14 +33,14 @@ Allows returning the tenantId of the tenant currently connected to in the curren Get-PnPTenantId ``` -Returns the current Tenant Id. A valid connection with Connect-PnPOnline is required. +Returns the current Tenant Id. A valid connection with Connect-PnPOnline is required either as a current connection or by providing it using the -Connection parameter. ### EXAMPLE 2 ```powershell Get-PnPTenantId -TenantUrl "https://contoso.sharepoint.com" ``` -Returns the Tenant ID for the specified tenant. Can be executed without a connecting first with Connect-PnPOnline +Returns the Tenant ID for the specified tenant. Can be executed without an active PnP Connection. ## PARAMETERS From 5dbca6b65fa85d7b79fa85c929d1aaf675f7d164 Mon Sep 17 00:00:00 2001 From: = <=> Date: Sun, 19 Jun 2022 13:08:33 +0200 Subject: [PATCH 401/458] Adding PR reference --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ee79dbab..c3209d354 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,7 +39,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added capability to Debug the module in Visual Studio. [#1880](https://github.com/pnp/powershell/pull/1880) - Added `Set-PnPTeamsChannelUser` cmdlet to update the role of user in a private channel. [#1865](https://github.com/pnp/powershell/pull/1865) - Added `Restart-PnPFlowRun` which allows for a failed Power Automate flow run to be retried [#1915](https://github.com/pnp/powershell/pull/1915) -- Added optional `-Connection` parameter to `Get-PnPConnection`, `Get-PnPContext` and `Set-PnPContext` which allows for using any of these for a specific connection [#1919](https://github.com/pnp/powershell/pull/1919) , [#1958](https://github.com/pnp/powershell/pull/1958) +- Added optional `-Connection` parameter to `Get-PnPConnection`, `Get-PnPContext` and `Set-PnPContext` which allows for using any of these for a specific connection [#1919](https://github.com/pnp/powershell/pull/1919) - Added `-IncludeDeprecated` parameter to `Get-PnPTerm` cmdlet to fetch deprecated terms if specified [#1903](https://github.com/pnp/powershell/pull/1903) - Added `-IncludeContentType` parameter, which if specified will retrieve content type information of the list items. [#1921](https://github.com/pnp/powershell/pull/1921) - Added optional `-ValidateConnection` to `Connect-PnPOnline` which will check if the site you are connecting to exists and if not, will throw an exception [#1924](https://github.com/pnp/powershell/pull/1924) @@ -68,7 +68,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Changed `Connect-PnPOnline -Interactive` and `Connect-PnPOnline -DeviceLogin` to no longer suppress errors which should allow for certificate logins to be used. [#1933](https://github.com/pnp/powershell/pull/1933) - `Set-PnPTeamsChannel` now uses the Graph v1 endpoint, previously it used the beta endpoint. [#1938](https://github.com/pnp/powershell/pull/1938) - Service Health cmdlets have been improved and are now consistent with other cmdlets to handle pagination [#1938](https://github.com/pnp/powershell/pull/1938) -- Changed that every cmdlet now supports passing in a specific connection using `-Connection`. If omitted, the default connection will be used. [#1949](https://github.com/pnp/powershell/pull/1949) +- Changed that almost every cmdlet now supports passing in a specific connection using `-Connection`. If omitted, the default connection will be used. [#1949](https://github.com/pnp/powershell/pull/1949), [#2011](https://github.com/pnp/powershell/pull/2011), [#1958](https://github.com/pnp/powershell/pull/1958) ### Fixed From ecfac00b4de320fcc32c6807ded1e447563e5732 Mon Sep 17 00:00:00 2001 From: Ali Robertson Date: Sun, 19 Jun 2022 22:29:45 +1000 Subject: [PATCH 402/458] Fix bug #2000 broken error messages (output contained variable name as string) --- src/Commands/Utilities/ListItemHelper.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Commands/Utilities/ListItemHelper.cs b/src/Commands/Utilities/ListItemHelper.cs index 08c45bd40..44ab0ed90 100644 --- a/src/Commands/Utilities/ListItemHelper.cs +++ b/src/Commands/Utilities/ListItemHelper.cs @@ -135,7 +135,7 @@ public static void SetFieldValues(this ListItem item, Hashtable valuesToSet, Cmd } else { - cmdlet.WriteWarning("Unable to find the specified term. Skipping values for field " + $field.InternalName); + cmdlet.WriteWarning("Unable to find the specified term. Skipping values for field " + field.InternalName); } } @@ -159,7 +159,7 @@ public static void SetFieldValues(this ListItem item, Hashtable valuesToSet, Cmd } else { - cmdlet.WriteWarning("You are trying to set multiple values in a single value field. Skipping values for field " + $field.InternalName); + cmdlet.WriteWarning("You are trying to set multiple values in a single value field. Skipping values for field " + field.InternalName); } } else @@ -176,7 +176,7 @@ public static void SetFieldValues(this ListItem item, Hashtable valuesToSet, Cmd if (taxonomyItem == null) { updateTaxItemValue = false; - cmdlet.WriteWarning("Unable to find the specified term. Skipping values for field" + $field.InternalName); + cmdlet.WriteWarning("Unable to find the specified term. Skipping values for field" + field.InternalName); } } else From 3bc4194acb588c96dd6828270f4b81046780ca29 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Mon, 20 Jun 2022 03:40:56 +0000 Subject: [PATCH 403/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 248cba875..6a3cd6261 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -121c69280db556259e046b6d5f42174c00701a5e \ No newline at end of file +acc95071a0ab56d900ef474b97b10c7d8018577e \ No newline at end of file diff --git a/version.txt b/version.txt index 59541cb33..da9681e4d 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.53 \ No newline at end of file +1.10.54 \ No newline at end of file From d603d4e01a344696e5ae4e165db7fdf141c1881f Mon Sep 17 00:00:00 2001 From: Leif Frederiksen Date: Mon, 20 Jun 2022 12:17:50 +0200 Subject: [PATCH 404/458] Update Receive-PnPCopyMoveJobStatus.md -Job parameter should be $job - not $result --- documentation/Receive-PnPCopyMoveJobStatus.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Receive-PnPCopyMoveJobStatus.md b/documentation/Receive-PnPCopyMoveJobStatus.md index b7217e7d2..f108a1c2f 100644 --- a/documentation/Receive-PnPCopyMoveJobStatus.md +++ b/documentation/Receive-PnPCopyMoveJobStatus.md @@ -24,7 +24,7 @@ This cmdlets outputs the results of a pending/finished copy or move job. ### Example 1 ```powershell $job = Copy-PnPFile -SourceUrl "Shared Documents/company.docx" -TargetUrl "SubSite2/Shared Documents" -NoWait -$jobStatus = Receive-PnPCopyMoveJobStatus -Job $result +$jobStatus = Receive-PnPCopyMoveJobStatus -Job $job if($jobStatus.JobState == 0) { Write-Host "Job finished" From 21df04cbd744efcea6bc085f40acf5242e8cc833 Mon Sep 17 00:00:00 2001 From: = <=> Date: Mon, 20 Jun 2022 14:41:23 +0200 Subject: [PATCH 405/458] Adding to contributors --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e9a85786..a54366f33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -108,6 +108,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Contributors +- Leif Frederiksen [Leif Frederiksen] - Emily Mancini [eemancini] - Jim Duncan [sparkitect] - Arleta Wanat [PowershellScripts] From 08a74406e52f446192184224e0e0822e4c4f153d Mon Sep 17 00:00:00 2001 From: = <=> Date: Mon, 20 Jun 2022 14:42:17 +0200 Subject: [PATCH 406/458] Fixing GitHub username --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a54366f33..6aa8eb940 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -108,7 +108,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Contributors -- Leif Frederiksen [Leif Frederiksen] +- Leif Frederiksen [Leif-Frederiksen] - Emily Mancini [eemancini] - Jim Duncan [sparkitect] - Arleta Wanat [PowershellScripts] From 2c04fcf956ed02aadb57ad936d3ff3b1eb1d7b44 Mon Sep 17 00:00:00 2001 From: = <=> Date: Mon, 20 Jun 2022 15:02:09 +0200 Subject: [PATCH 407/458] Code cleanup + addiiton to changelog --- CHANGELOG.md | 2 ++ src/Commands/Utilities/ListItemHelper.cs | 11 +++-------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35a984482..4d062c294 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -99,6 +99,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Update-PnPSiteClassification`, it was ignoring the `Settings` parameter. It will now be processed. [#1989](https://github.com/pnp/powershell/pull/1989) - Fixed `Register-PnPAzureADApp` issue with app creation after the connection related changes. [#1993](https://github.com/pnp/powershell/pull/1993) - Fixed `Get-PnPFileVersion` not able to correctly use piping on the returned object. [#1997](https://github.com/pnp/powershell/pull/1997) +- Fixed `Add-PnPListItem` not showing field name when it has an improper value assigned to it [#2002](https://github.com/pnp/powershell/pull/202) ### Removed @@ -108,6 +109,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Contributors +- Ali Robertson [alirobe] - Emily Mancini [eemancini] - Jim Duncan [sparkitect] - Arleta Wanat [PowershellScripts] diff --git a/src/Commands/Utilities/ListItemHelper.cs b/src/Commands/Utilities/ListItemHelper.cs index 44ab0ed90..19a02f67d 100644 --- a/src/Commands/Utilities/ListItemHelper.cs +++ b/src/Commands/Utilities/ListItemHelper.cs @@ -9,8 +9,6 @@ using System.Globalization; using System.Linq; using System.Management.Automation; -using System.Text; -using System.Threading.Tasks; namespace PnP.PowerShell.Commands.Utilities { @@ -37,8 +35,6 @@ public FieldUpdateValue(string key, object value, string fieldTypeString) public static void SetFieldValues(this ListItem item, Hashtable valuesToSet, Cmdlet cmdlet) { - // xxx: return early if hashtable is empty to save getting fields? - var itemValues = new List(); var context = item.Context as ClientContext; @@ -135,7 +131,7 @@ public static void SetFieldValues(this ListItem item, Hashtable valuesToSet, Cmd } else { - cmdlet.WriteWarning("Unable to find the specified term. Skipping values for field " + field.InternalName); + cmdlet.WriteWarning("Unable to find the specified term. Skipping values for field '" + field.InternalName + "'."); } } @@ -159,7 +155,7 @@ public static void SetFieldValues(this ListItem item, Hashtable valuesToSet, Cmd } else { - cmdlet.WriteWarning("You are trying to set multiple values in a single value field. Skipping values for field " + field.InternalName); + cmdlet.WriteWarning("You are trying to set multiple values in a single value field. Skipping values for field '" + field.InternalName + "'."); } } else @@ -176,7 +172,7 @@ public static void SetFieldValues(this ListItem item, Hashtable valuesToSet, Cmd if (taxonomyItem == null) { updateTaxItemValue = false; - cmdlet.WriteWarning("Unable to find the specified term. Skipping values for field" + field.InternalName); + cmdlet.WriteWarning("Unable to find the specified term. Skipping values for field '" + field.InternalName + "'."); } } else @@ -304,7 +300,6 @@ public static void SetFieldValues(this ListItem item, Hashtable valuesToSet, Cmd } } } - } public static Dictionary GetFieldValues(PnP.Core.Model.SharePoint.IList list, PnP.Core.Model.SharePoint.IListItem existingItem, Hashtable valuesToSet, ClientContext clientContext, PnPBatch batch) From c671e39cfac894e7132bc041c4274a57093872ef Mon Sep 17 00:00:00 2001 From: Marc D Anderson Date: Mon, 20 Jun 2022 09:53:40 -0400 Subject: [PATCH 408/458] Final (?) fix for batching badges --- documentation/Add-PnPListItem.md | 2 +- documentation/Invoke-PnPBatch.md | 2 +- documentation/New-PnPBatch.md | 2 +- documentation/Publish-PnPSyntexModel.md | 2 +- documentation/Remove-PnPListItem.md | 2 +- documentation/Request-PnPSyntexClassifyAndExtract.md | 2 +- documentation/Set-PnPListItem.md | 2 +- documentation/Unpublish-PnPSyntexModel.md | 2 +- pages/articles/batching.md | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/documentation/Add-PnPListItem.md b/documentation/Add-PnPListItem.md index f48215761..1846cfb70 100644 --- a/documentation/Add-PnPListItem.md +++ b/documentation/Add-PnPListItem.md @@ -13,7 +13,7 @@ title: Add-PnPListItem Adds an item to the list and sets the creation time to the current date and time. The author is set to the current authenticated user executing the cmdlet. In order to set the author to a different user, please refer to Set-PnPListItem. -[![Supports Batching](../pages/images/batching/Batching.png)](../pages/articles/batching.md) +![Supports Batching](../images/batching/Batching.png)] ## SYNTAX diff --git a/documentation/Invoke-PnPBatch.md b/documentation/Invoke-PnPBatch.md index 3fd8a2202..0bcae5a86 100644 --- a/documentation/Invoke-PnPBatch.md +++ b/documentation/Invoke-PnPBatch.md @@ -13,7 +13,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Invoke-PnPBatch.html Executes the batch -[![Supports Batching](../pages/images/batching/Batching.png)](../pages/articles/batching.md) +![Supports Batching](../images/batching/Batching.png)] ## SYNTAX diff --git a/documentation/New-PnPBatch.md b/documentation/New-PnPBatch.md index 82437ac46..9bb39e834 100644 --- a/documentation/New-PnPBatch.md +++ b/documentation/New-PnPBatch.md @@ -13,7 +13,7 @@ online version: https://pnp.github.io/powershell/cmdlets/New-PnPBatch.html Creates a new batch -[![Supports Batching](../pages/images/batching/Batching.png)](../pages/articles/batching.md) +![Supports Batching](../images/batching/Batching.png)] ## SYNTAX diff --git a/documentation/Publish-PnPSyntexModel.md b/documentation/Publish-PnPSyntexModel.md index c50698ceb..084af95d7 100644 --- a/documentation/Publish-PnPSyntexModel.md +++ b/documentation/Publish-PnPSyntexModel.md @@ -15,7 +15,7 @@ Publishes a SharePoint Syntex models to a list. This cmdlet only works when you've connected to a SharePoint Syntex Content Center site. -[![Supports Batching](../pages/images/batching/Batching.png)](../pages/articles/batching.md) +![Supports Batching](../images/batching/Batching.png)] ## SYNTAX diff --git a/documentation/Remove-PnPListItem.md b/documentation/Remove-PnPListItem.md index 876276c0f..abe56c040 100644 --- a/documentation/Remove-PnPListItem.md +++ b/documentation/Remove-PnPListItem.md @@ -13,7 +13,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Remove-PnPListItem.html Deletes an item from a list -[![Supports Batching](../pages/images/batching/Batching.png)](../pages/articles/batching.md) +![Supports Batching](../images/batching/Batching.png)] ## SYNTAX diff --git a/documentation/Request-PnPSyntexClassifyAndExtract.md b/documentation/Request-PnPSyntexClassifyAndExtract.md index 55ffe659e..05cfb755a 100644 --- a/documentation/Request-PnPSyntexClassifyAndExtract.md +++ b/documentation/Request-PnPSyntexClassifyAndExtract.md @@ -13,7 +13,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Get-PnPPage.html Requests for a file, folder or all files in a library to be classified and extracted via the published SharePoint Syntex models on the libraries hosting the files. -[![Supports Batching](../pages/images/batching/Batching.png)](../pages/articles/batching.md) +![Supports Batching](../images/batching/Batching.png)] ## SYNTAX diff --git a/documentation/Set-PnPListItem.md b/documentation/Set-PnPListItem.md index 026ce4372..b8f5c4808 100644 --- a/documentation/Set-PnPListItem.md +++ b/documentation/Set-PnPListItem.md @@ -13,7 +13,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Set-PnPListItem.html Updates a list item -[![Supports Batching](../pages/images/batching/Batching.png)](../pages/articles/batching.md) +![Supports Batching](../images/batching/Batching.png)] ## SYNTAX diff --git a/documentation/Unpublish-PnPSyntexModel.md b/documentation/Unpublish-PnPSyntexModel.md index 71753ed40..26d158843 100644 --- a/documentation/Unpublish-PnPSyntexModel.md +++ b/documentation/Unpublish-PnPSyntexModel.md @@ -15,7 +15,7 @@ Unpublishes a SharePoint Syntex model from a list. This cmdlet only works when you've connected to a SharePoint Syntex Content Center site. -[![Supports Batching](../pages/images/batching/Batching.png)](../pages/articles/batching.md) +![Supports Batching](../images/batching/Batching.png)] ## SYNTAX diff --git a/pages/articles/batching.md b/pages/articles/batching.md index 80bc660b1..ad70e2543 100644 --- a/pages/articles/batching.md +++ b/pages/articles/batching.md @@ -47,7 +47,7 @@ If during one of these chunks an exception occurs (for instance you are trying t The following cmdlets support batching. (This support began with the 1.7.63-nightly release.) All cmdlets which support batching display the badge, linking to this article. -![Supports Batching](../../pages/images/batching/Batching.png) +![Supports Batching](../images/batching/Batching.png) * [`Add-PnPListItem`](/powershell/cmdlets/Add-PnPListItem.html) * [`Set-PnPListItem`](/powershell/cmdlets/Set-PnPListItem.html) From 1d922e060b61d03d06a7b6f0d78e6e9cbb521b9d Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 21 Jun 2022 03:49:46 +0000 Subject: [PATCH 409/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index 6eddf96b7..c77997add 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -abccc06f79d25bb7e5cf3fed96f03bc9dcc2b169 \ No newline at end of file +651c209b6a5d0354db49b62ef931a50f1af7e5fa \ No newline at end of file diff --git a/version.txt b/version.txt index da9681e4d..0dbec8426 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.54 \ No newline at end of file +1.10.55 \ No newline at end of file From 3bc443a865563215c88092c7b2081b47e4a4c59a Mon Sep 17 00:00:00 2001 From: Leif Frederiksen Date: Tue, 21 Jun 2022 07:12:04 +0200 Subject: [PATCH 410/458] Update Copy-PnPFile.md $job should be passed as value for -Job parameter. --- documentation/Copy-PnPFile.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Copy-PnPFile.md b/documentation/Copy-PnPFile.md index 9335df11f..f30de1356 100644 --- a/documentation/Copy-PnPFile.md +++ b/documentation/Copy-PnPFile.md @@ -100,7 +100,7 @@ Copies a file named company.docx in the library named Documents in SubSite1 to t ### EXAMPLE 11 ```powershell $job = Copy-PnPFile -SourceUrl "Shared Documents/company.docx" -TargetUrl "SubSite2/Shared Documents" -NoWait -$jobStatus = Receive-PnPCopyMoveJobStatus -Job $result +$jobStatus = Receive-PnPCopyMoveJobStatus -Job $job if($jobStatus.JobState == 0) { Write-Host "Job finished" From d71469732c674f630b3830734f804d7b4773bd53 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 22 Jun 2022 03:47:11 +0000 Subject: [PATCH 411/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index c77997add..947bdd264 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -651c209b6a5d0354db49b62ef931a50f1af7e5fa \ No newline at end of file +77ff1f6c9923692329abc5a95a22999ad3c80022 \ No newline at end of file diff --git a/version.txt b/version.txt index 0dbec8426..1a7d444ed 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.55 \ No newline at end of file +1.10.56 \ No newline at end of file From aca0cb39d4324891238114ff0b15cbdbcbcf3853 Mon Sep 17 00:00:00 2001 From: Marc D Anderson Date: Wed, 22 Jun 2022 08:53:14 -0400 Subject: [PATCH 412/458] Removed dangling bracket ] --- documentation/Add-PnPListItem.md | 2 +- documentation/Invoke-PnPBatch.md | 2 +- documentation/New-PnPBatch.md | 2 +- documentation/Publish-PnPSyntexModel.md | 2 +- documentation/Remove-PnPListItem.md | 2 +- documentation/Request-PnPSyntexClassifyAndExtract.md | 2 +- documentation/Set-PnPListItem.md | 2 +- documentation/Unpublish-PnPSyntexModel.md | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/documentation/Add-PnPListItem.md b/documentation/Add-PnPListItem.md index 1846cfb70..74cd38c9c 100644 --- a/documentation/Add-PnPListItem.md +++ b/documentation/Add-PnPListItem.md @@ -13,7 +13,7 @@ title: Add-PnPListItem Adds an item to the list and sets the creation time to the current date and time. The author is set to the current authenticated user executing the cmdlet. In order to set the author to a different user, please refer to Set-PnPListItem. -![Supports Batching](../images/batching/Batching.png)] +![Supports Batching](../images/batching/Batching.png) ## SYNTAX diff --git a/documentation/Invoke-PnPBatch.md b/documentation/Invoke-PnPBatch.md index 0bcae5a86..b4bab3f0c 100644 --- a/documentation/Invoke-PnPBatch.md +++ b/documentation/Invoke-PnPBatch.md @@ -13,7 +13,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Invoke-PnPBatch.html Executes the batch -![Supports Batching](../images/batching/Batching.png)] +![Supports Batching](../images/batching/Batching.png) ## SYNTAX diff --git a/documentation/New-PnPBatch.md b/documentation/New-PnPBatch.md index 9bb39e834..8e9a615e7 100644 --- a/documentation/New-PnPBatch.md +++ b/documentation/New-PnPBatch.md @@ -13,7 +13,7 @@ online version: https://pnp.github.io/powershell/cmdlets/New-PnPBatch.html Creates a new batch -![Supports Batching](../images/batching/Batching.png)] +![Supports Batching](../images/batching/Batching.png) ## SYNTAX diff --git a/documentation/Publish-PnPSyntexModel.md b/documentation/Publish-PnPSyntexModel.md index 084af95d7..44badf4f8 100644 --- a/documentation/Publish-PnPSyntexModel.md +++ b/documentation/Publish-PnPSyntexModel.md @@ -15,7 +15,7 @@ Publishes a SharePoint Syntex models to a list. This cmdlet only works when you've connected to a SharePoint Syntex Content Center site. -![Supports Batching](../images/batching/Batching.png)] +![Supports Batching](../images/batching/Batching.png) ## SYNTAX diff --git a/documentation/Remove-PnPListItem.md b/documentation/Remove-PnPListItem.md index abe56c040..a7bc2d828 100644 --- a/documentation/Remove-PnPListItem.md +++ b/documentation/Remove-PnPListItem.md @@ -13,7 +13,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Remove-PnPListItem.html Deletes an item from a list -![Supports Batching](../images/batching/Batching.png)] +![Supports Batching](../images/batching/Batching.png) ## SYNTAX diff --git a/documentation/Request-PnPSyntexClassifyAndExtract.md b/documentation/Request-PnPSyntexClassifyAndExtract.md index 05cfb755a..ed18080d1 100644 --- a/documentation/Request-PnPSyntexClassifyAndExtract.md +++ b/documentation/Request-PnPSyntexClassifyAndExtract.md @@ -13,7 +13,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Get-PnPPage.html Requests for a file, folder or all files in a library to be classified and extracted via the published SharePoint Syntex models on the libraries hosting the files. -![Supports Batching](../images/batching/Batching.png)] +![Supports Batching](../images/batching/Batching.png) ## SYNTAX diff --git a/documentation/Set-PnPListItem.md b/documentation/Set-PnPListItem.md index b8f5c4808..f0a113ea9 100644 --- a/documentation/Set-PnPListItem.md +++ b/documentation/Set-PnPListItem.md @@ -13,7 +13,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Set-PnPListItem.html Updates a list item -![Supports Batching](../images/batching/Batching.png)] +![Supports Batching](../images/batching/Batching.png) ## SYNTAX diff --git a/documentation/Unpublish-PnPSyntexModel.md b/documentation/Unpublish-PnPSyntexModel.md index 26d158843..ea4467a64 100644 --- a/documentation/Unpublish-PnPSyntexModel.md +++ b/documentation/Unpublish-PnPSyntexModel.md @@ -15,7 +15,7 @@ Unpublishes a SharePoint Syntex model from a list. This cmdlet only works when you've connected to a SharePoint Syntex Content Center site. -![Supports Batching](../images/batching/Batching.png)] +![Supports Batching](../images/batching/Batching.png) ## SYNTAX From 0b2f9ef30e5346629c25568d2c2c8bd6d8aedc62 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Thu, 23 Jun 2022 03:51:22 +0000 Subject: [PATCH 413/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 6a3cd6261..601da27fc 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -acc95071a0ab56d900ef474b97b10c7d8018577e \ No newline at end of file +139bacff5f6bcf321ce32a70a6912ba8bada2c4d \ No newline at end of file diff --git a/version.txt b/version.txt index 1a7d444ed..f5f4bdb9c 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.56 \ No newline at end of file +1.10.57 \ No newline at end of file From d8cb5d1810f375232a91a921022b8cebcf8475ef Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Thu, 23 Jun 2022 12:41:26 +0200 Subject: [PATCH 414/458] Added `Get-PnPSensitivityLabel` (#2023) * Added Get-PnPSensitivityLabel * Added PR reference * Adding documentation to model Co-authored-by: = <=> --- CHANGELOG.md | 1 + documentation/Get-PnPSensitivityLabel.md | 94 +++++++++++++++++++ src/Commands/Base/BasePSCmdlet.cs | 8 +- src/Commands/Model/AzureAD/User.cs | 2 + src/Commands/Model/Graph/GraphException.cs | 5 +- .../Purview/InformationProtectionLabel.cs | 59 ++++++++++++ src/Commands/Purview/GetSensitivityLabel.cs | 57 +++++++++++ src/Commands/Utilities/REST/GraphHelper.cs | 2 + 8 files changed, 224 insertions(+), 4 deletions(-) create mode 100644 documentation/Get-PnPSensitivityLabel.md create mode 100644 src/Commands/Model/Graph/Purview/InformationProtectionLabel.cs create mode 100644 src/Commands/Purview/GetSensitivityLabel.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index b423e2d2e..5e2fee0df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `-SharingCapability` parameter to the `New-PnPTenantSite` cmdlet to update the Sharing capabilties of the newly provisioned classic site collection. [#1994](https://github.com/pnp/powershell/pull/1994) - Added optional `-IncludeAllLists` to `Get-PnPSiteScriptFromWeb` which will include the JSON definition of all custom lists of the current site in the output [#1987](https://github.com/pnp/powershell/pull/1987) - Added `-UpdateChildren` parameter to `Add-PnPFieldToContentType` cmdlet. This allows users to skip pushing the fields to child content types. [#1092](https://github.com/pnp/powershell/pull/1992) +- Added `Get-PnPSensitivityLabel` cmdlet to retrieve Microsoft Purview sensitivity labels available on the tenant [#2023](https://github.com/pnp/powershell/pull/2023) ### Changed diff --git a/documentation/Get-PnPSensitivityLabel.md b/documentation/Get-PnPSensitivityLabel.md new file mode 100644 index 000000000..735862a60 --- /dev/null +++ b/documentation/Get-PnPSensitivityLabel.md @@ -0,0 +1,94 @@ +--- +Module Name: PnP.PowerShell +schema: 2.0.0 +applicable: SharePoint Online +online version: https://pnp.github.io/powershell/cmdlets/Get-PnPSensitivityLabel.html +external help file: PnP.PowerShell.dll-Help.xml +title: Get-PnPSensitivityLabel +--- + +# Get-PnPSensitivityLabel + +## SYNOPSIS +Gets the Microsoft Purview sensitivity labels that are available within the tenant + +## SYNTAX + +```powershell +Get-PnPSensitivityLabel [-Identity ] [-User ] [-Connection ] [] +``` + +## DESCRIPTION +This cmdlet allows retrieval of the available Microsoft Purview sensitivity labels in the currently connected tenant. You can retrieve all the labels, a specific label or all the labels available to a specific user. + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Get-PnPSensitivityLabel +``` + +Returns all the Microsoft Purview sensitivitiy labels that exist on the tenant + +### EXAMPLE 2 +```powershell +Get-PnPSensitivityLabel -User johndoe@tenant.onmicrosoft.com +``` + +Returns all Microsoft Purview sensitivitiy labels which are available to the provided user + +### EXAMPLE 3 +```powershell +Get-PnPSensitivityLabel -Identity 47e66706-8627-4979-89f1-fa7afeba2884 +``` + +Returns a specific Microsoft Purview sensitivitiy label by its id + +## PARAMETERS + +### -Identity +The Id of the Microsoft Purview sensitivity label to retrieve + +```yaml +Type: Guid +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -User +The UPN, Id or instance of an Azure AD user for which you would like to retrieve the Microsoft Purview sensitivity labels available to this user + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) +[Microsoft Graph documentation](https://docs.microsoft.com/graph/api/informationprotectionpolicy-list-labels) \ No newline at end of file diff --git a/src/Commands/Base/BasePSCmdlet.cs b/src/Commands/Base/BasePSCmdlet.cs index f16d0927c..72de479a7 100644 --- a/src/Commands/Base/BasePSCmdlet.cs +++ b/src/Commands/Base/BasePSCmdlet.cs @@ -52,6 +52,8 @@ protected override void ProcessRecord() } catch (PnP.PowerShell.Commands.Model.Graph.GraphException gex) { + var errorMessage = gex.Error.Message; + if (gex.Error.Code == "Authorization_RequestDenied") { if (!string.IsNullOrEmpty(gex.AccessToken)) @@ -59,7 +61,11 @@ protected override void ProcessRecord() TokenHandler.ValidateTokenForPermissions(GetType(), gex.AccessToken); } } - throw new PSInvalidOperationException(gex.Error.Message); + if(string.IsNullOrWhiteSpace(errorMessage) && gex.HttpResponse != null && gex.HttpResponse.StatusCode == System.Net.HttpStatusCode.Forbidden) + { + errorMessage = "Access denied. Check for the required permissions."; + } + throw new PSInvalidOperationException(errorMessage); } } diff --git a/src/Commands/Model/AzureAD/User.cs b/src/Commands/Model/AzureAD/User.cs index 501020f68..3148bc298 100644 --- a/src/Commands/Model/AzureAD/User.cs +++ b/src/Commands/Model/AzureAD/User.cs @@ -80,6 +80,8 @@ public class User /// PnP PowerShell Azure Active Directory User object internal static User CreateFrom(PnP.Framework.Graph.Model.User entity) { + if(entity == null) return null; + var user = new User { UserPrincipalName = entity.UserPrincipalName, diff --git a/src/Commands/Model/Graph/GraphException.cs b/src/Commands/Model/Graph/GraphException.cs index b62fb8cf7..22a7bb682 100644 --- a/src/Commands/Model/Graph/GraphException.cs +++ b/src/Commands/Model/Graph/GraphException.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Net; -using System.Text; namespace PnP.PowerShell.Commands.Model.Graph { @@ -10,6 +8,8 @@ public class GraphException : Exception public GraphError Error { get; set; } public string AccessToken { get; set; } + + public System.Net.Http.HttpResponseMessage HttpResponse { get; set; } } public class GraphError @@ -23,6 +23,5 @@ public class GraphError public Dictionary AdditionalData { get; set; } public string ThrowSite { get; set; } - } } diff --git a/src/Commands/Model/Graph/Purview/InformationProtectionLabel.cs b/src/Commands/Model/Graph/Purview/InformationProtectionLabel.cs new file mode 100644 index 000000000..f4a680093 --- /dev/null +++ b/src/Commands/Model/Graph/Purview/InformationProtectionLabel.cs @@ -0,0 +1,59 @@ +using System.Text.Json.Serialization; + +namespace PnP.PowerShell.Commands.Model.Graph.Purview +{ + /// + /// Describes the information protection label that details how to properly apply a sensitivity label to information. The informationProtectionLabel resource describes the configuration of sensitivity labels that apply to a user or tenant. + /// + /// + public class InformationProtectionLabel + { + /// + /// The label ID is a globally unique identifier (GUID) + /// + [JsonPropertyName("id")] + public string Id { get; set; } + + /// + /// The plaintext name of the label. + /// + [JsonPropertyName("name")] + public string Name { get; set; } + + /// + /// The admin-defined description for the label. + /// + [JsonPropertyName("description")] + public string Description { get; set; } + + /// + /// The color that the UI should display for the label, if configured. + /// + [JsonPropertyName("color")] + public string Color { get; set; } + + /// + /// The sensitivity value of the label, where lower is less sensitive. + /// + [JsonPropertyName("sensitivity")] + public int Sensitivity { get; set; } + + /// + /// The tooltip that should be displayed for the label in a UI. + /// + [JsonPropertyName("tooltip")] + public string Tooltip { get; set; } + + /// + /// Indicates whether the label is active or not. Active labels should be hidden or disabled in UI. + /// + [JsonPropertyName("isActive")] + public bool? IsActive { get; set; } + + /// + /// The parent label associated with a child label. Null if label has no parent. + /// + [JsonPropertyName("parent")] + public object Parent { get; set; } + } +} \ No newline at end of file diff --git a/src/Commands/Purview/GetSensitivityLabel.cs b/src/Commands/Purview/GetSensitivityLabel.cs new file mode 100644 index 000000000..1558b3551 --- /dev/null +++ b/src/Commands/Purview/GetSensitivityLabel.cs @@ -0,0 +1,57 @@ +using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Utilities.REST; +using System; +using System.Collections.Generic; +using System.Management.Automation; + +namespace PnP.PowerShell.Commands.PowerPlatform.PowerAutomate +{ + [Cmdlet(VerbsCommon.Get, "PnPSensitivityLabel")] + [RequiredMinimalApiPermissions("InformationProtectionPolicy.Read.All")] + [OutputType(typeof(IEnumerable))] + [OutputType(typeof(Model.Graph.Purview.InformationProtectionLabel))] + public class GetSensitivityLabel : PnPGraphCmdlet + { + [Parameter(Mandatory = false)] + public AzureADUserPipeBind User; + + [Parameter(Mandatory = false)] + public Guid Identity; + + protected override void ExecuteCmdlet() + { + string url; + if (ParameterSpecified(nameof(User))) + { + var user = User.GetUser(AccessToken); + + if(user == null) + { + WriteWarning("Provided user not found"); + return; + } + + url = $"/beta/users/{user.UserPrincipalName}/informationProtection/policy/labels"; + } + else + { + url = "/beta/informationProtection/policy/labels"; + } + + if (ParameterSpecified(nameof(Identity))) + { + url += $"/{Identity}"; + + var labels = GraphHelper.GetAsync(Connection, url, AccessToken).GetAwaiter().GetResult(); + WriteObject(labels, false); + } + else + { + var labels = GraphHelper.GetResultCollectionAsync(Connection, url, AccessToken).GetAwaiter().GetResult(); + WriteObject(labels, true); + } + } + } +} \ No newline at end of file diff --git a/src/Commands/Utilities/REST/GraphHelper.cs b/src/Commands/Utilities/REST/GraphHelper.cs index a7370f1ba..9d129ae27 100644 --- a/src/Commands/Utilities/REST/GraphHelper.cs +++ b/src/Commands/Utilities/REST/GraphHelper.cs @@ -349,6 +349,8 @@ private static async Task SendMessageAsync(PnPConnection connection, Htt var errorContent = await response.Content.ReadAsStringAsync(); var exception = JsonSerializer.Deserialize(errorContent, new JsonSerializerOptions() { IgnoreNullValues = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); exception.AccessToken = accessToken; + exception.HttpResponse = response; + throw exception; } } From c9dc50edef0ae3ea032d5f7a262d7ca072db4fb3 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Thu, 23 Jun 2022 12:49:26 +0200 Subject: [PATCH 415/458] Update CHANGELOG.md Adding PR entry for https://github.com/pnp/pnpframework/pull/686 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e2fee0df..401958134 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,6 +73,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - `Set-PnPTeamsChannel` now uses the Graph v1 endpoint, previously it used the beta endpoint. [#1938](https://github.com/pnp/powershell/pull/1938) - Service Health cmdlets have been improved and are now consistent with other cmdlets to handle pagination [#1938](https://github.com/pnp/powershell/pull/1938) - Changed that almost every cmdlet now supports passing in a specific connection using `-Connection`. If omitted, the default connection will be used. [#1949](https://github.com/pnp/powershell/pull/1949), [#2011](https://github.com/pnp/powershell/pull/2011), [#1958](https://github.com/pnp/powershell/pull/1958) +- Changed connecting with `Connect-PnPOnline -Credentials` now throwing a clear exception when making a typo in the hostname instead of getting stuck [#686](https://github.com/pnp/pnpframework/pull/686) ### Fixed From f1da0ac83f354d42d482de38605373952170a753 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Thu, 23 Jun 2022 13:55:25 +0200 Subject: [PATCH 416/458] Added `-SensitivityLabel` to `Set-PnPSite` (#2024) * Added optional `-SensitivityLabel` to `Set-PnPSite` which allows for a Microsoft Purview sensitivitylabel to be set * Adding PR reference Co-authored-by: = <=> Co-authored-by: Gautam Sheth --- CHANGELOG.md | 2 ++ documentation/Set-PnPSite.md | 25 +++++++++++++++++++------ src/Commands/Site/SetSite.cs | 10 ++++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 401958134..d6f7ff80c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Set-PnPContentType` cmdlet to update the properties of the Content Types in a list or a web. [#1981](https://github.com/pnp/powershell/pull/1981) - Added `-SharingCapability` parameter to the `New-PnPTenantSite` cmdlet to update the Sharing capabilties of the newly provisioned classic site collection. [#1994](https://github.com/pnp/powershell/pull/1994) - Added optional `-IncludeAllLists` to `Get-PnPSiteScriptFromWeb` which will include the JSON definition of all custom lists of the current site in the output [#1987](https://github.com/pnp/powershell/pull/1987) +- Added `-UpdateChildren` parameter to `Add-PnPFieldToContentType` cmdlet. This allows users to skip pushing the fields to child content types. [#1992](https://github.com/pnp/powershell/pull/1992) +- Added optional `-SensitivityLabel` to `Set-PnPSite` which allows for a Microsoft Purview sensitivitylabel to be set [#2024](https://github.com/pnp/powershell/pull/2024) - Added `-UpdateChildren` parameter to `Add-PnPFieldToContentType` cmdlet. This allows users to skip pushing the fields to child content types. [#1092](https://github.com/pnp/powershell/pull/1992) - Added `Get-PnPSensitivityLabel` cmdlet to retrieve Microsoft Purview sensitivity labels available on the tenant [#2023](https://github.com/pnp/powershell/pull/2023) diff --git a/documentation/Set-PnPSite.md b/documentation/Set-PnPSite.md index 3f70c37d2..82d9065c2 100644 --- a/documentation/Set-PnPSite.md +++ b/documentation/Set-PnPSite.md @@ -24,14 +24,13 @@ Set-PnPSite [-Identity ] [-Classification ] [-DisableFlows] [-Lo [-DisableCompanyWideSharingLinks ] [-DisableSharingForNonOwners] [-LocaleId ] [-RestrictedToGeo ] [-SocialBarOnSitePagesDisabled] [-AnonymousLinkExpirationInDays ] [-OverrideTenantAnonymousLinkExpirationPolicy] - [-MediaTranscription ] + [-MediaTranscription ] [-SensitivityLabel ] [-Connection ] [] ``` ### Set Lock State ```powershell -Set-PnPSite [-Identity ] [-Classification ] [-DisableFlows] [-LockState ] - [-Wait] [-Connection ] [] +Set-PnPSite [-Identity ] [-LockState ] [-Wait] [-Connection ] [] ``` ## DESCRIPTION @@ -43,14 +42,14 @@ Set-PnPSite [-Identity ] [-Classification ] [-DisableFlows] [-Lo Set-PnPSite -Classification "HBI" ``` -Sets the current site classification to HBI +Sets the current site classification tag to HBI ### EXAMPLE 2 ```powershell Set-PnPSite -Classification $null ``` -Unsets the current site classification +Unsets the current site classification tag ### EXAMPLE 3 ```powershell @@ -111,7 +110,21 @@ Accept wildcard characters: False ``` ### -Classification -The classification to set +The classification tag to set. This is the old classification/labeling method. Set it to $null to remove the classification entirely. + +```yaml +Type: String +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -SensitivityLabel +The Microsoft Purview sensitivity label to set. This is the new classification/labeling method. ```yaml Type: String diff --git a/src/Commands/Site/SetSite.cs b/src/Commands/Site/SetSite.cs index 74fb53d54..85cbda9fe 100644 --- a/src/Commands/Site/SetSite.cs +++ b/src/Commands/Site/SetSite.cs @@ -22,6 +22,7 @@ public class SetSite : PnPSharePointCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_PROPERTIES)] public string Classification; + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_PROPERTIES)] public SwitchParameter? DisableFlows; @@ -93,6 +94,9 @@ public class SetSite : PnPSharePointCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_PROPERTIES)] public MediaTranscriptionPolicyType? MediaTranscription { get; set; } + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_PROPERTIES)] + public Guid? SensitivityLabel; + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_LOCKSTATE)] public SwitchParameter Wait; @@ -117,6 +121,12 @@ protected override void ExecuteCmdlet() context.ExecuteQueryRetry(); } + if(ParameterSpecified(nameof(SensitivityLabel)) && SensitivityLabel.HasValue) + { + site.SensitivityLabel = SensitivityLabel.Value; + context.ExecuteQueryRetry(); + } + if (ParameterSpecified(nameof(LogoFilePath))) { if (!System.IO.Path.IsPathRooted(LogoFilePath)) From 9b179b053424af6116ba97a84ea291452e1bcc35 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 23 Jun 2022 22:33:12 +0200 Subject: [PATCH 417/458] Fixed Az function article (#2030) * Fixed Az function article * Fixed bullet points --- pages/articles/azurefunctions.md | 75 ++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/pages/articles/azurefunctions.md b/pages/articles/azurefunctions.md index 4c294f2fa..220dbaa14 100644 --- a/pages/articles/azurefunctions.md +++ b/pages/articles/azurefunctions.md @@ -7,32 +7,35 @@ In this article we will setup an Azure Function to use PnP PowerShell ## Create the function app -As the UI in https://portal.azure.com changes every now and then, but the principles stay the same, follow the following steps: +As the UI in changes every now and then, but the principles stay the same, follow the following steps: 1. Create a new Function App 1. Choose runtime stack `PowerShell Core` and version `7.0` - - ![](./../images/azurefunctions/createfunctionappbasics.png) + + ![Create function app basics](./../images/azurefunctions/createfunctionappbasics.png) 1. Select `Windows` as the operating system or else you will not be able to perform the following steps from your browser. - ![](./../images/azurefunctions/createfunctionapphosting.png) + ![Create function app hosting](./../images/azurefunctions/createfunctionapphosting.png) ## Make PnP PowerShell available to all functions in the app 1. Navigate to `App files` which is located the left side menu of the function app under the `Functions` header. -1. In the dropdown presented, select `requirements.psd1`. You'll notice that the function app wants to provide the Azure cmdlets. If you do not need those, keep the `Az` entry presented commented out. -1. Add a new entry or replace the whole contents of the file with: - - ### Specific stable version +2. In the dropdown presented, select `requirements.psd1`. You'll notice that the function app wants to provide the Azure cmdlets. If you do not need those, keep the `Az` entry presented commented out. +3. Add a new entry or replace the whole contents of the file with: + +### Specific stable version + ```powershell @{ 'PnP.PowerShell' = '1.9.0' } ``` + The version that will be installed will be the specified specific build, which is generally recommended. You build and test your Azure Function against this specific PnP PowerShell version. Future releases may work differently and cause issues, therefore it is generally recommended to specify a specific version here. - - ### Latest stable version + +### Latest stable version + If, for some reason, you would like to ensure it is always using the latest available PnP PowerShell version, you can also specify a wildcard in the version: ```powershell @@ -40,21 +43,24 @@ As the UI in https://portal.azure.com changes every now and then, but the princi 'PnP.PowerShell' = '1.*' } ``` + This will then automatically download any minor version of the major 1 release when available. Note that wildcards will always take the latest stable version and not the nightly build/prerelease versions. - - ### Specific prerelease version + +### Specific prerelease version + If you wish to use a specific prerelease/nightly build version, go to the [overview of available versions](https://www.powershellgallery.com/packages/PnP.PowerShell) and literally copy/paste the version in the definition: + ```powershell @{ - 'PnP.PowerShell' = '1.9.46-nightly' + 'PnP.PowerShell' = '1.10.58-nightly' } ``` ![Adding PnP PowerShell to the requirements.psd1 file in an Azure Function](./../images/azurefunctions/addpnpposhtoappfilerequirements.png) -1. Save the `requirements.psd1` file +1. Save the `requirements.psd1` file -1. If you decide to keep the Az cmdlets commented out, save and edit the `profile.psd` file. Mark out the following block in the file as follows, if not already done: +2. If you decide to keep the Az cmdlets commented out, save and edit the `profile.psd` file. Mark out the following block in the file as follows, if not already done: ```powershell # if ($env:MSI_SECRET) { @@ -67,15 +73,15 @@ As the UI in https://portal.azure.com changes every now and then, but the princi ![Disable the Az commands in profile](../images/azurefunctions/disableazinprofile.png) - ## Decide how you want to authenticate in your PowerShell Function ### By using Credentials #### Create your credentials -1. Navigate to `Configuration` under `Settings` and create a new Application Setting. -1. Enter `tenant_user` and enter the username you want to authenticate with as the user -1. Enter `tenant_pwd` and enter the password you want to use for that user + +1. Navigate to `Configuration` under `Settings` and create a new Application Setting. +2. Enter `tenant_user` and enter the username you want to authenticate with as the user +3. Enter `tenant_pwd` and enter the password you want to use for that user #### Create the function @@ -139,13 +145,13 @@ Make a note of the clientid shown and proceed with the steps in the following se Once you have an Azure Active Directory application set up and the public key certificate uploaded to its registration, proceed with configuring the Azure Function to make use of the private key of this certificate pair: 1. In your function app, navigate to `TLS/SSL Settings` and switch to the `Private Key Certificates (.pfx)` section. -1. Click `Upload Certificate` and select the "MyDemoApp.pfx" file that has been created for you. Enter the password you used in the script above. -1 After the certificate has been uploaded, copy the thumbprint value shown. -1 Navigate to `Configuration` and add a new Application Setting -1. Call the setting `WEBSITE_LOAD_CERTIFICATES` and set the thumbprint as a value. To make all the certificates you uploaded available use `*` as the value. See https://docs.microsoft.com/azure/app-service/configure-ssl-certificate-in-code for more information. -1. Save the settings +2. Click `Upload Certificate` and select the "MyDemoApp.pfx" file that has been created for you. Enter the password you used in the script above. +3. After the certificate has been uploaded, copy the thumbprint value shown. +4. Navigate to `Configuration` and add a new Application Setting +5. Call the setting `WEBSITE_LOAD_CERTIFICATES` and set the thumbprint as a value. To make all the certificates you uploaded available use `*` as the value. See for more information. +6. Save the settings -#### Create the Azure Function +#### Create the Azure Function for certificate authentication Create a new function and replace the function code with the following example: @@ -197,40 +203,45 @@ Next step is to assign permissions to this managed identity so it is authorized ```powershell Install-Module Az ``` - + 1. Connect to the Azure instance where your Azure Function runs and of which you want to use the Microsoft Graph through PnP PowerShell ```powershell Connect-AzAccount -Tenant .onmicrosoft.com ``` - + 1. Retrieve the Azure AD Service Principal instance for the Microsoft Graph. It should always be AppId 00000003-0000-0000-c000-000000000000. ```powershell $graphServicePrincipal = Get-AzADServicePrincipal -SearchString "Microsoft Graph" | Select-Object -First 1 ``` - + 1. Using the following PowerShell cmdlet you can list all the possible Microsoft Graph permissions you can give your Azure Function through the Managed Identity. This list will be long. Notice that we are specifically querying for application permissions. Delegate permissions cannot be utilized using a Managed Identity. ```powershell $graphServicePrincipal.AppRole | Where-Object { $_.AllowedMemberType -eq "Application" } ``` - + 1. Pick a permission which you would like to grant your Azure Function to have towards the Microsoft Graph, i.e. `Group.Read.All`. ```powershell $appRole = $graphServicePrincipal.AppRole | Where-Object { $_.AllowedMemberType -eq "Application" -and $_.Value -eq "Group.Read.All" } ``` - + 1. Now assign this permission to the Azure Active Directory app registration that has been created automatically by enabling the managed identity on the Azure Function in the steps above: ```powershell $managedIdentityId = "" - Add-AzADAppPermission -ObjectId $managedIdentityId -ApiId $graphServicePrincipal.AppId -PermissionId $appRole.Id -Type 'Role' + + $body = "{'principalId':'$($managedIdentityId)','resourceId':'$($graphServicePrincipal.Id)','appRoleId':'$($appRole.Id)'}" + + $accessTokenResource = Get-AzAccessToken -ResourceTypeName MSGraph + + Invoke-WebRequest "https://graph.microsoft.com/v1.0/servicePrincipals/$($managedIdentityId)/appRoleAssignments" -Headers @{"Authorization" = "Bearer $($accessTokenResource.Token)"} -ContentType "application/json" -Body $body -Method Post ``` -#### Create the Azure Function +#### Create the Azure Function for managed identity authentication Create a new function and replace the function code with the following example: From e87c1315435cbf16af06f8b836407f1970dbd27a Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Fri, 24 Jun 2022 03:53:07 +0000 Subject: [PATCH 418/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index 947bdd264..18b686312 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -77ff1f6c9923692329abc5a95a22999ad3c80022 \ No newline at end of file +dbccee2bda5ee53ce3c65ed2021c2d0b256cd026 \ No newline at end of file diff --git a/version.txt b/version.txt index f5f4bdb9c..03cecf2a8 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.57 \ No newline at end of file +1.10.58 \ No newline at end of file From f93b4be987c8f192ec2fc65d0ff1f71d3490fb06 Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Fri, 24 Jun 2022 17:49:04 +0200 Subject: [PATCH 419/458] Update Set-PnPTermGroup.md --- documentation/Set-PnPTermGroup.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/documentation/Set-PnPTermGroup.md b/documentation/Set-PnPTermGroup.md index eeb074442..5767e28bc 100644 --- a/documentation/Set-PnPTermGroup.md +++ b/documentation/Set-PnPTermGroup.md @@ -94,6 +94,22 @@ Accept pipeline input: False Accept wildcard characters: False ``` + +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + + ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) From a2c0939c9d8e8078c510c0c2092731b760bd4854 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 25 Jun 2022 03:46:09 +0000 Subject: [PATCH 420/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 601da27fc..583d75f77 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -139bacff5f6bcf321ce32a70a6912ba8bada2c4d \ No newline at end of file +b30df81300e111bc306ecbef63af08800c64173b \ No newline at end of file diff --git a/version.txt b/version.txt index 03cecf2a8..16f242598 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.58 \ No newline at end of file +1.10.59 \ No newline at end of file From 815c2db8bac8a43338396b9123082f109a9bdbf9 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Sun, 26 Jun 2022 03:48:47 +0000 Subject: [PATCH 421/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 583d75f77..859955000 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -b30df81300e111bc306ecbef63af08800c64173b \ No newline at end of file +a62f9b9efecc8d7e148294738be13f90ed19f745 \ No newline at end of file diff --git a/version.txt b/version.txt index 16f242598..5baa2f9c6 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.59 \ No newline at end of file +1.10.60 \ No newline at end of file From f202fd6d22eaaac6de434fa9420c687a292cb224 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Mon, 27 Jun 2022 04:02:07 +0000 Subject: [PATCH 422/458] Nightly publish to PowerShell Gallery --- pnpframework_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnpframework_hash.txt b/pnpframework_hash.txt index 859955000..92c5a4307 100644 --- a/pnpframework_hash.txt +++ b/pnpframework_hash.txt @@ -1 +1 @@ -a62f9b9efecc8d7e148294738be13f90ed19f745 \ No newline at end of file +afa22c7252259bae48a8efd376da7d969463f43e \ No newline at end of file diff --git a/version.txt b/version.txt index 5baa2f9c6..ac7415629 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.60 \ No newline at end of file +1.10.61 \ No newline at end of file From 649bca1123a9a4ba27ad98e60dedbe723dcf518b Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Mon, 27 Jun 2022 09:22:18 +0200 Subject: [PATCH 423/458] Fix for switching from App Only session to Interactive session when using the same ClientId (#2035) * Fix * Updating with PR Reference Co-authored-by: = <=> --- CHANGELOG.md | 3 ++- src/Commands/Base/ConnectOnline.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6f7ff80c..627842e29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -103,7 +103,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Update-PnPSiteClassification`, it was ignoring the `Settings` parameter. It will now be processed. [#1989](https://github.com/pnp/powershell/pull/1989) - Fixed `Register-PnPAzureADApp` issue with app creation after the connection related changes. [#1993](https://github.com/pnp/powershell/pull/1993) - Fixed `Get-PnPFileVersion` not able to correctly use piping on the returned object. [#1997](https://github.com/pnp/powershell/pull/1997) -- Fixed `Add-PnPListItem` not showing field name when it has an improper value assigned to it [#2002](https://github.com/pnp/powershell/pull/202) +- Fixed `Add-PnPListItem` not showing field name when it has an improper value assigned to it [#2002](https://github.com/pnp/powershell/pull/2002) +- Fixed connecting using `Connect-PnPOnline -Interactive -ClientId` not working well when already having an App-Only connection using the same ClientId [#2035](https://github.com/pnp/powershell/pull/2035) ### Removed diff --git a/src/Commands/Base/ConnectOnline.cs b/src/Commands/Base/ConnectOnline.cs index 9ea862d15..dffcc527e 100644 --- a/src/Commands/Base/ConnectOnline.cs +++ b/src/Commands/Base/ConnectOnline.cs @@ -573,7 +573,7 @@ private PnPConnection ConnectInteractive() { ClientId = PnPConnection.PnPManagementShellClientId; } - if (PnPConnection.Current?.ClientId == ClientId) + if (PnPConnection.Current?.ClientId == ClientId && PnPConnection.Current?.ConnectionMethod == ConnectionMethod.Credentials) { if (IsSameOrAdminHost(new Uri(Url), new Uri(PnPConnection.Current.Url))) { From 0d94b5a209360a03f900892b9c1c17a5b8c4b5bc Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Mon, 27 Jun 2022 09:27:54 +0200 Subject: [PATCH 424/458] Modifications to `Get-PnPHubSiteChild` (#2033) * -Identity is now optional, clear error message gets shown when requesting this for a site that's not a hub site * Added PR reference Co-authored-by: = <=> Co-authored-by: Gautam Sheth --- CHANGELOG.md | 3 +++ documentation/Get-PnPHubSiteChild.md | 29 +++++++++++++++++---------- src/Commands/Admin/GetHubSiteChild.cs | 26 ++++++++++++++++++------ 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 627842e29..bd80affc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Service Health cmdlets have been improved and are now consistent with other cmdlets to handle pagination [#1938](https://github.com/pnp/powershell/pull/1938) - Changed that almost every cmdlet now supports passing in a specific connection using `-Connection`. If omitted, the default connection will be used. [#1949](https://github.com/pnp/powershell/pull/1949), [#2011](https://github.com/pnp/powershell/pull/2011), [#1958](https://github.com/pnp/powershell/pull/1958) - Changed connecting with `Connect-PnPOnline -Credentials` now throwing a clear exception when making a typo in the hostname instead of getting stuck [#686](https://github.com/pnp/pnpframework/pull/686) +- Changed `Get-PnPHubSiteChild` to have its `-Identity` parameter become optional. If not provided, the currently connected to site will be used. [#2033](https://github.com/pnp/powershell/pull/2033) ### Fixed @@ -103,6 +104,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Update-PnPSiteClassification`, it was ignoring the `Settings` parameter. It will now be processed. [#1989](https://github.com/pnp/powershell/pull/1989) - Fixed `Register-PnPAzureADApp` issue with app creation after the connection related changes. [#1993](https://github.com/pnp/powershell/pull/1993) - Fixed `Get-PnPFileVersion` not able to correctly use piping on the returned object. [#1997](https://github.com/pnp/powershell/pull/1997) +- Fixed `Add-PnPListItem` not showing field name when it has an improper value assigned to it [#2002](https://github.com/pnp/powershell/pull/202) +- Fixed `Get-PnPHubSiteChild` throwing an exception when passing in a URL that is actually not a hub site [#2033](https://github.com/pnp/powershell/pull/2033) - Fixed `Add-PnPListItem` not showing field name when it has an improper value assigned to it [#2002](https://github.com/pnp/powershell/pull/2002) - Fixed connecting using `Connect-PnPOnline -Interactive -ClientId` not working well when already having an App-Only connection using the same ClientId [#2035](https://github.com/pnp/powershell/pull/2035) diff --git a/documentation/Get-PnPHubSiteChild.md b/documentation/Get-PnPHubSiteChild.md index 7f67b2de8..3a6858a6f 100644 --- a/documentation/Get-PnPHubSiteChild.md +++ b/documentation/Get-PnPHubSiteChild.md @@ -20,7 +20,7 @@ Retrieves all sites associated to a specific hub site ## SYNTAX ```powershell -Get-PnPHubSiteChild -Identity [-Connection ] [] +Get-PnPHubSiteChild [-Identity ] [-Connection ] [] ``` ## DESCRIPTION @@ -30,12 +30,19 @@ Retrieves all sites associated to a specific hub site ### EXAMPLE 1 ```powershell +Get-PnPHubSiteChild +``` + +Returns the sites which are associated to the currently connected to hub site + +### EXAMPLE 2 +```powershell Get-PnPHubSiteChild -Identity "https://contoso.sharepoint.com/sites/myhubsite" ``` Returns the sites which are associated with the provided hub site as their hub site -### EXAMPLE 2 +### EXAMPLE 3 ```powershell Get-PnPHubSite | Get-PnPHubSiteChild ``` @@ -44,31 +51,31 @@ Returns all sites that are associated to a hub site ## PARAMETERS -### -Connection -Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. +### -Identity +The URL, Id or instance of the hubsite for which to receive the sites refering to it. If not provided, the currently connected to site will be used. ```yaml -Type: PnPConnection +Type: HubSitePipeBind Parameter Sets: (All) Required: False Position: Named Default value: None -Accept pipeline input: False +Accept pipeline input: True (ByValue) Accept wildcard characters: False ``` -### -Identity -The URL, Id or instance of the hubsite for which to receive the sites refering to it +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. ```yaml -Type: HubSitePipeBind +Type: PnPConnection Parameter Sets: (All) -Required: True +Required: False Position: Named Default value: None -Accept pipeline input: True (ByValue) +Accept pipeline input: False Accept wildcard characters: False ``` diff --git a/src/Commands/Admin/GetHubSiteChild.cs b/src/Commands/Admin/GetHubSiteChild.cs index d8c5b700e..44a403f36 100644 --- a/src/Commands/Admin/GetHubSiteChild.cs +++ b/src/Commands/Admin/GetHubSiteChild.cs @@ -11,7 +11,7 @@ namespace PnP.PowerShell.Commands.Admin [Cmdlet(VerbsCommon.Get, "PnPHubSiteChild")] public class GetHubSiteChild : PnPAdminCmdlet { - [Parameter(ValueFromPipeline = true, Mandatory = true)] + [Parameter(ValueFromPipeline = true, Mandatory = false)] public HubSitePipeBind Identity; protected override void ExecuteCmdlet() @@ -19,16 +19,30 @@ protected override void ExecuteCmdlet() HubSiteProperties hubSiteProperties; try { - hubSiteProperties = Identity.GetHubSite(Tenant); + if (ParameterSpecified(nameof(Identity))) + { + hubSiteProperties = Identity.GetHubSite(Tenant); + } + else + { + hubSiteProperties = Tenant.GetHubSitePropertiesByUrl(Connection.Url); + } hubSiteProperties.EnsureProperty(h => h.ID); } - catch (ServerException ex) + catch (ServerObjectNullReferenceException) { - if (ex.ServerErrorTypeName.Equals("System.IO.FileNotFoundException")) + if (ParameterSpecified(nameof(Identity))) { - throw new ArgumentException(Resources.SiteNotFound, nameof(Identity)); + throw new ArgumentException($"Unable to retrieve hub child sites of site provided through -{nameof(Identity)}. This could be caused by the site not being a hub site.", nameof(Identity)); } - throw; + else + { + throw new PSInvalidOperationException($"Unable to retrieve hub child sites of the current site {Connection.Url}. This could be caused by this site not being a hub site."); + } + } + catch (ServerException e) when (e.ServerErrorTypeName.Equals("System.IO.FileNotFoundException")) + { + throw new ArgumentException(Resources.SiteNotFound, nameof(Identity)); } // Get the ID of the hubsite for which we need to find child sites From 5b1b8d6c45cf9af7a2d8036a52f41306999a1489 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Mon, 27 Jun 2022 09:49:37 +0200 Subject: [PATCH 425/458] Bugfix for closing consent dialog exception (#2037) * Removing lines to resolve the issue where PowerShell throws an exception when closing the permission consent dialog * Adding changelog entry Co-authored-by: = <=> Co-authored-by: Gautam Sheth --- CHANGELOG.md | 1 + src/Commands/Utilities/BrowserHelper.cs | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd80affc7..db2d8738e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -105,6 +105,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Register-PnPAzureADApp` issue with app creation after the connection related changes. [#1993](https://github.com/pnp/powershell/pull/1993) - Fixed `Get-PnPFileVersion` not able to correctly use piping on the returned object. [#1997](https://github.com/pnp/powershell/pull/1997) - Fixed `Add-PnPListItem` not showing field name when it has an improper value assigned to it [#2002](https://github.com/pnp/powershell/pull/202) +- Fixed the browser consent dialog throwing an exception when trying to close it [#2037](https://github.com/pnp/powershell/pull/2037) - Fixed `Get-PnPHubSiteChild` throwing an exception when passing in a URL that is actually not a hub site [#2033](https://github.com/pnp/powershell/pull/2033) - Fixed `Add-PnPListItem` not showing field name when it has an improper value assigned to it [#2002](https://github.com/pnp/powershell/pull/2002) - Fixed connecting using `Connect-PnPOnline -Interactive -ClientId` not working well when already having an App-Only connection using the same ClientId [#2035](https://github.com/pnp/powershell/pull/2035) diff --git a/src/Commands/Utilities/BrowserHelper.cs b/src/Commands/Utilities/BrowserHelper.cs index e493eb3c0..816af621f 100644 --- a/src/Commands/Utilities/BrowserHelper.cs +++ b/src/Commands/Utilities/BrowserHelper.cs @@ -183,11 +183,6 @@ internal static bool GetWebBrowserPopup(string siteUrl, string title, (string ur { var form = new System.Windows.Forms.Form(); - cancellationTokenSource?.Token.Register(() => - { - form.Invoke((System.Windows.Forms.MethodInvoker)(() => form.Close())); - }); - var browser = new System.Windows.Forms.WebBrowser { ScriptErrorsSuppressed = scriptErrorsSuppressed, From f1518d1c61a0cfcb3fc22e89940d189bbd10af05 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 28 Jun 2022 03:55:43 +0000 Subject: [PATCH 426/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index 18b686312..962c1800c 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -dbccee2bda5ee53ce3c65ed2021c2d0b256cd026 \ No newline at end of file +420871f1514593bca01a2a2a82f82dead58d2235 \ No newline at end of file diff --git a/version.txt b/version.txt index ac7415629..92542a378 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.61 \ No newline at end of file +1.10.62 \ No newline at end of file From 662cb276f9eaf8f013a0943bdae4fc8110d84647 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 28 Jun 2022 08:56:48 +0200 Subject: [PATCH 427/458] Optimizations (#2040) Co-authored-by: = <=> --- .../UserProfiles/NewUPABulkImportJob.cs | 66 ++++++++++++++----- 1 file changed, 51 insertions(+), 15 deletions(-) diff --git a/src/Commands/UserProfiles/NewUPABulkImportJob.cs b/src/Commands/UserProfiles/NewUPABulkImportJob.cs index 50931cb91..dc1524650 100644 --- a/src/Commands/UserProfiles/NewUPABulkImportJob.cs +++ b/src/Commands/UserProfiles/NewUPABulkImportJob.cs @@ -21,27 +21,31 @@ public class NewUPABulkImportJob : PnPAdminCmdlet [Parameter(Mandatory = true, Position = 0, ParameterSetName = ParameterSet_UPLOADFILE)] public string Folder; - [Parameter(Mandatory = true, Position = 1, ParameterSetName = ParameterSet_UPLOADFILE)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_UPLOADFILE)] public string Path = string.Empty; [Parameter(Mandatory = true, ParameterSetName = ParameterSet_URL)] public string Url = string.Empty; - [Parameter(Mandatory = true, Position = 2, ParameterSetName = ParameterSet_UPLOADFILE)] - [Parameter(Mandatory = true, Position = 1, ParameterSetName = ParameterSet_URL)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_UPLOADFILE)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_URL)] public Hashtable UserProfilePropertyMapping; - [Parameter(Mandatory = true, Position = 3, ParameterSetName = ParameterSet_UPLOADFILE)] - [Parameter(Mandatory = true, Position = 2, ParameterSetName = ParameterSet_URL)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_UPLOADFILE)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_URL)] public string IdProperty; - [Parameter(Mandatory = false, Position = 4, ParameterSetName = ParameterSet_UPLOADFILE)] - [Parameter(Mandatory = false, Position = 3, ParameterSetName = ParameterSet_URL)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_UPLOADFILE)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_URL)] public ImportProfilePropertiesUserIdType IdType = ImportProfilePropertiesUserIdType.Email; - [Parameter(Mandatory = false, Position = 4, ParameterSetName = ParameterSet_UPLOADFILE)] - [Parameter(Mandatory = false, Position = 3, ParameterSetName = ParameterSet_URL)] - public SwitchParameter Wait; + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_UPLOADFILE)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_URL)] + public SwitchParameter Wait; + + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_UPLOADFILE)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_URL)] + public SwitchParameter WhatIf; protected override void ExecuteCmdlet() { @@ -58,6 +62,8 @@ protected override void ExecuteCmdlet() throw new InvalidEnumArgumentException(@"Path cannot be empty"); } + WriteVerbose($"Going to use mapping file to upload from {Path}"); + var webCtx = ClientContext.Clone(Connection.Url); var web = webCtx.Web; var webServerRelativeUrl = web.EnsureProperty(w => w.ServerRelativeUrl); @@ -72,23 +78,53 @@ protected override void ExecuteCmdlet() var folder = web.GetFolderByServerRelativeUrl(Folder); var fileName = System.IO.Path.GetFileName(Path); - File file = folder.UploadFile(fileName, Path, true); - Url = new Uri(webCtx.Url).GetLeftPart(UriPartial.Authority) + file.ServerRelativeUrl; + + File file = null; + if(!ParameterSpecified(nameof(WhatIf))) + { + WriteVerbose($"Uploading file from {Path} to {fileName}"); + file = folder.UploadFile(fileName, Path, true); + } + else + { + WriteVerbose($"Skipping uploading file from {Path} to {fileName} due to {nameof(WhatIf)} parameter being specified"); + } + + Url = new Uri(webCtx.Url).GetLeftPart(UriPartial.Authority) + file?.ServerRelativeUrl; break; case ParameterSet_URL: if (string.IsNullOrWhiteSpace(Url)) { throw new InvalidEnumArgumentException(@"Url cannot be empty"); } + WriteVerbose($"Will instruct SharePoint Online to use mapping file located at {Url}"); break; } var o365 = new Office365Tenant(ClientContext); var propDictionary = UserProfilePropertyMapping.Cast().ToDictionary(kvp => (string)kvp.Key, kvp => (string)kvp.Value); - var id = o365.QueueImportProfileProperties(IdType, IdProperty, propDictionary, Url); - ClientContext.ExecuteQueryRetry(); - var job = o365.GetImportProfilePropertyJob(id.Value); + Guid? jobId; + if (!ParameterSpecified(nameof(WhatIf))) + { + WriteVerbose($"Instructing SharePoint Online to queue user profile file located at {Url}"); + jobId = o365.QueueImportProfileProperties(IdType, IdProperty, propDictionary, Url)?.Value; + ClientContext.ExecuteQueryRetry(); + } + else + { + WriteVerbose($"Skipping instructing SharePoint Online to queue user profile file located at {Url} due to {nameof(WhatIf)} parameter being specified"); + return; + } + + // For some reason it sometimes does not always properly return the JobId while the job did start. Show this in the output. + if(jobId == Guid.Empty) + { + WriteWarning("The execution of the synchronization job did not return a job Id but seems to have started successfully. Use Get-PnPUPABulkImportStatus to check for the current status."); + return; + } + + var job = o365.GetImportProfilePropertyJob(jobId.Value); ClientContext.Load(job); ClientContext.ExecuteQueryRetry(); From 6639e37d12672962deb269d16248108f6d06d26a Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Tue, 28 Jun 2022 09:58:57 +0300 Subject: [PATCH 428/458] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index db2d8738e..9b5ba0742 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -190,6 +190,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Changed `Get-PnPTenantDeletedSite -Identity` no longer returning an unknown exception when no site collection with the provided Url exists in the tenant recycle bin but instead returning no output to align with other cmdlets [#1596](https://github.com/pnp/powershell/pull/1596) - Changed `Connect-PnPOnline -UseWebLogin` to no longer suppress errors which should allow for certificate logins to be used [#1706](https://github.com/pnp/powershell/issues/1706) - The cmdlet `New-PnPTeamsTeam` no longer supports adding members or owners through their e-mail addresses, if they differ from their UPNs. The User Principal Names must be used instead [#1241](https://github.com/pnp/powershell/pull/1241) +- Improved `New-PnPUPABulkImportJob` by optimizing it and also adding support for `WhatIf` paramater. [#2040](https://github.com/pnp/powershell/pull/2040) ### Fixed From a0cfa133977f71010129817fafe6355a70939e9a Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 28 Jun 2022 09:00:12 +0200 Subject: [PATCH 429/458] Update README.md Raised cmdlet amount --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1d3665730..383ab5523 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # PnP PowerShell -**PnP PowerShell** is a .NET Core 3.1 / .NET Framework 4.6.2 based PowerShell Module providing over 600 cmdlets that work with Microsoft 365 environments such as SharePoint Online, Microsoft Teams, Microsoft Project, Security & Compliance, Azure Active Directory, and more. +**PnP PowerShell** is a .NET Core 3.1 / .NET Framework 4.6.2 based PowerShell Module providing nearly 650 cmdlets that work with Microsoft 365 environments such as SharePoint Online, Microsoft Teams, Microsoft Project, Security & Compliance, Azure Active Directory, and more. Last version | Last nightly version -------------|--------------------- From 05d0133cac0d2f51da544ebc0b524936d178f372 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Tue, 28 Jun 2022 09:02:02 +0200 Subject: [PATCH 430/458] Added Microsoft 365 Group Endpoint cmdlets (#2038) * Added cmdlets * Added PR reference * Changing warning to exception Co-authored-by: = <=> --- CHANGELOG.md | 3 + .../Get-PnPMicrosoft365GroupEndpoint.md | 98 +++++++++++++++++++ documentation/Get-PnPMicrosoft365GroupTeam.md | 98 +++++++++++++++++++ ...Get-PnPMicrosoft365GroupYammerCommunity.md | 98 +++++++++++++++++++ .../PipeBinds/Microsoft365GroupPipeBind.cs | 7 +- .../GetMicrosoft365GroupEndpoint.cs | 51 ++++++++++ .../GetMicrosoft365GroupTeam.cs | 55 +++++++++++ .../GetMicrosoft365GroupYammerCommunity.cs | 55 +++++++++++ .../Model/AzureAD/AzureADGroupEndPoint.cs | 32 ++++++ 9 files changed, 491 insertions(+), 6 deletions(-) create mode 100644 documentation/Get-PnPMicrosoft365GroupEndpoint.md create mode 100644 documentation/Get-PnPMicrosoft365GroupTeam.md create mode 100644 documentation/Get-PnPMicrosoft365GroupYammerCommunity.md create mode 100644 src/Commands/Microsoft365Groups/GetMicrosoft365GroupEndpoint.cs create mode 100644 src/Commands/Microsoft365Groups/GetMicrosoft365GroupTeam.cs create mode 100644 src/Commands/Microsoft365Groups/GetMicrosoft365GroupYammerCommunity.cs create mode 100644 src/Commands/Model/AzureAD/AzureADGroupEndPoint.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b5ba0742..c34329d07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added optional `-SensitivityLabel` to `Set-PnPSite` which allows for a Microsoft Purview sensitivitylabel to be set [#2024](https://github.com/pnp/powershell/pull/2024) - Added `-UpdateChildren` parameter to `Add-PnPFieldToContentType` cmdlet. This allows users to skip pushing the fields to child content types. [#1092](https://github.com/pnp/powershell/pull/1992) - Added `Get-PnPSensitivityLabel` cmdlet to retrieve Microsoft Purview sensitivity labels available on the tenant [#2023](https://github.com/pnp/powershell/pull/2023) +- Added `Get-Microsoft365GroupYammerCommunity` cmdlet to retrieve details on the Yammer Community connected to a Microsoft 365 Group [#2038](https://github.com/pnp/powershell/pull/2038) +- Added `Get-Microsoft365GroupTeam` cmdlet to retrieve details on the Microsoft Teams team connected to a Microsoft 365 Group [#2038](https://github.com/pnp/powershell/pull/2038) +- Added `Get-Microsoft365GroupEndpoints` cmdlet to retrieve details on all endpoints connected to a Microsoft 365 Group [#2038](https://github.com/pnp/powershell/pull/2038) ### Changed diff --git a/documentation/Get-PnPMicrosoft365GroupEndpoint.md b/documentation/Get-PnPMicrosoft365GroupEndpoint.md new file mode 100644 index 000000000..024593051 --- /dev/null +++ b/documentation/Get-PnPMicrosoft365GroupEndpoint.md @@ -0,0 +1,98 @@ +--- +Module Name: PnP.PowerShell +schema: 2.0.0 +applicable: SharePoint Online +online version: https://pnp.github.io/powershell/cmdlets/Get-PnPMicrosoft365GroupEndpoint.html +external help file: PnP.PowerShell.dll-Help.xml +title: Get-PnPMicrosoft365GroupEndpoint +--- + +# Get-PnPMicrosoft365GroupEndpoint + +## SYNOPSIS + +**Required Permissions** + + * Microsoft Graph API : Group.Read.All + +Returns the endpoints behind a particular Microsoft 365 Group + +## SYNTAX + +```powershell +Get-PnPMicrosoft365GroupEndpoint -Identity [-Connection] [-Verbose] [] +``` + +## DESCRIPTION +This cmdlet allows retrieval of details on the endpoints connected to a Microsoft 365 Group + +## EXAMPLES + +### EXAMPLE 2 +```powershell +Get-PnPMicrosoft365GroupEndpoint +``` + +Retrieves the endpoints behind the Microsoft 365 Group of the currently connected to site + +### EXAMPLE 2 +```powershell +Get-PnPMicrosoft365GroupEndpoint -Identity "IT Team" +``` + +Retrieves the endpoints behind the Microsoft 365 Group named "IT Team" + +### EXAMPLE 3 +```powershell +Get-PnPMicrosoft365GroupEndpoint -Identity e6212531-7f09-4c3b-bc2e-12cae26fb409 +``` + +Retrieves the endpoints behind the Microsoft 365 Group with the provided Id + +## PARAMETERS + +### -Identity +The Identity of the Microsoft 365 Group + +```yaml +Type: Microsoft365GroupPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Verbose +When provided, additional debug statements will be shown while executing the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/documentation/Get-PnPMicrosoft365GroupTeam.md b/documentation/Get-PnPMicrosoft365GroupTeam.md new file mode 100644 index 000000000..d77e92380 --- /dev/null +++ b/documentation/Get-PnPMicrosoft365GroupTeam.md @@ -0,0 +1,98 @@ +--- +Module Name: PnP.PowerShell +schema: 2.0.0 +applicable: SharePoint Online +online version: https://pnp.github.io/powershell/cmdlets/Get-PnPMicrosoft365GroupTeam.html +external help file: PnP.PowerShell.dll-Help.xml +title: Get-PnPMicrosoft365GroupTeam +--- + +# Get-PnPMicrosoft365GroupTeam + +## SYNOPSIS + +**Required Permissions** + + * Microsoft Graph API : Group.Read.All + +Returns the Microsoft Teams team behind a particular Microsoft 365 Group + +## SYNTAX + +```powershell +Get-PnPMicrosoft365GroupTeam -Identity [-Connection] [-Verbose] [] +``` + +## DESCRIPTION +This cmdlet allows retrieval of details on the Microsoft Teams team connected to a Microsoft 365 Group + +## EXAMPLES + +### EXAMPLE 2 +```powershell +Get-PnPMicrosoft365GroupTeam +``` + +Retrieves the Microsoft Teams team details behind the Microsoft 365 Group of the currently connected to site + +### EXAMPLE 2 +```powershell +Get-PnPMicrosoft365GroupTeam -Identity "IT Team" +``` + +Retrieves the Microsoft Teams team details behind the Microsoft 365 Group named "IT Team" + +### EXAMPLE 3 +```powershell +Get-PnPMicrosoft365GroupTeam -Identity e6212531-7f09-4c3b-bc2e-12cae26fb409 +``` + +Retrieves the Microsoft Teams team details behind the Microsoft 365 Group with the provided Id + +## PARAMETERS + +### -Identity +The Identity of the Microsoft 365 Group + +```yaml +Type: Microsoft365GroupPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Verbose +When provided, additional debug statements will be shown while executing the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/documentation/Get-PnPMicrosoft365GroupYammerCommunity.md b/documentation/Get-PnPMicrosoft365GroupYammerCommunity.md new file mode 100644 index 000000000..963d86ee1 --- /dev/null +++ b/documentation/Get-PnPMicrosoft365GroupYammerCommunity.md @@ -0,0 +1,98 @@ +--- +Module Name: PnP.PowerShell +schema: 2.0.0 +applicable: SharePoint Online +online version: https://pnp.github.io/powershell/cmdlets/Get-PnPMicrosoft365GroupYammerCommunity.html +external help file: PnP.PowerShell.dll-Help.xml +title: Get-PnPMicrosoft365GroupYammerCommunity +--- + +# Get-PnPMicrosoft365GroupYammerCommunity + +## SYNOPSIS + +**Required Permissions** + + * Microsoft Graph API : Group.Read.All + +Returns the Yammer Community behind a particular Microsoft 365 Group + +## SYNTAX + +```powershell +Get-PnPMicrosoft365GroupYammerCommunity -Identity [-Connection] [-Verbose] [] +``` + +## DESCRIPTION +This cmdlet allows retrieval of details on the Yammer Community connected to a Microsoft 365 Group + +## EXAMPLES + +### EXAMPLE 2 +```powershell +Get-PnPMicrosoft365GroupYammerCommunity +``` + +Retrieves the Yammer Community details behind the Microsoft 365 Group of the currently connected to site + +### EXAMPLE 2 +```powershell +Get-PnPMicrosoft365GroupYammerCommunity -Identity "IT Community" +``` + +Retrieves the Yammer Community details behind the Microsoft 365 Group named "IT Community" + +### EXAMPLE 3 +```powershell +Get-PnPMicrosoft365GroupYammerCommunity -Identity e6212531-7f09-4c3b-bc2e-12cae26fb409 +``` + +Retrieves the Yammer Community details behind the Microsoft 365 Group with the provided Id + +## PARAMETERS + +### -Identity +The Identity of the Microsoft 365 Group + +```yaml +Type: Microsoft365GroupPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Verbose +When provided, additional debug statements will be shown while executing the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/src/Commands/Base/PipeBinds/Microsoft365GroupPipeBind.cs b/src/Commands/Base/PipeBinds/Microsoft365GroupPipeBind.cs index 9f6997d52..164269ec4 100644 --- a/src/Commands/Base/PipeBinds/Microsoft365GroupPipeBind.cs +++ b/src/Commands/Base/PipeBinds/Microsoft365GroupPipeBind.cs @@ -1,11 +1,7 @@ -using PnP.Framework.Entities; -using PnP.Framework.Graph; -using PnP.PowerShell.Commands.Model; +using PnP.PowerShell.Commands.Model; using PnP.PowerShell.Commands.Utilities; using System; -using System.Linq; using System.Management.Automation; -using System.Net.Http; namespace PnP.PowerShell.Commands.Base.PipeBinds { @@ -85,7 +81,6 @@ public Guid GetGroupId(PnPConnection connection, string accessToken) } } throw new PSInvalidOperationException("Group not found"); - //return Guid.Empty; } public Microsoft365Group GetDeletedGroup(PnPConnection connection, string accessToken) diff --git a/src/Commands/Microsoft365Groups/GetMicrosoft365GroupEndpoint.cs b/src/Commands/Microsoft365Groups/GetMicrosoft365GroupEndpoint.cs new file mode 100644 index 000000000..52380b267 --- /dev/null +++ b/src/Commands/Microsoft365Groups/GetMicrosoft365GroupEndpoint.cs @@ -0,0 +1,51 @@ +using Microsoft.SharePoint.Client; +using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Utilities.REST; +using System; +using System.Linq; +using System.Management.Automation; + +namespace PnP.PowerShell.Commands.Microsoft365Groups +{ + [Cmdlet(VerbsCommon.Get, "PnPMicrosoft365GroupEndpoint")] + [RequiredMinimalApiPermissions("Group.Read.All")] + public class GetMicrosoft365GroupEndpoint : PnPGraphCmdlet + { + [Parameter(Mandatory = false, ValueFromPipeline = true, Position = 0)] + public Microsoft365GroupPipeBind Identity; + + protected override void ExecuteCmdlet() + { + Guid groupId; + if (ParameterSpecified(nameof(Identity))) + { + WriteVerbose($"Defining Microsoft 365 Group based on {nameof(Identity)} parameter"); + groupId = Identity.GetGroupId(Connection, AccessToken); + } + else + { + WriteVerbose($"Validating if the current site at {Connection.Url} has a Microsoft 365 Group behind it"); + ClientContext.Load(ClientContext.Site, s => s.GroupId); + ClientContext.ExecuteQueryRetry(); + + groupId = ClientContext.Site.GroupId; + + if(groupId == Guid.Empty) + { + throw new PSArgumentException("Current site is not backed by a Microsoft 365 Group", nameof(Identity)); + } + else + { + WriteVerbose($"Current site at {Connection.Url} is backed by the Microsoft 365 Group with Id {groupId}"); + } + } + + WriteVerbose($"Requesting endpoints of Microsoft 365 Group with Id {groupId}"); + var endpoints = GraphHelper.GetResultCollectionAsync(Connection, $"/beta/groups/{groupId}/endpoints", AccessToken).GetAwaiter().GetResult(); + WriteVerbose($"{endpoints.Count()} endpoint(s) found in total"); + WriteObject(endpoints, true); + } + } +} \ No newline at end of file diff --git a/src/Commands/Microsoft365Groups/GetMicrosoft365GroupTeam.cs b/src/Commands/Microsoft365Groups/GetMicrosoft365GroupTeam.cs new file mode 100644 index 000000000..1fd2843df --- /dev/null +++ b/src/Commands/Microsoft365Groups/GetMicrosoft365GroupTeam.cs @@ -0,0 +1,55 @@ +using Microsoft.SharePoint.Client; +using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Utilities.REST; +using System; +using System.Linq; +using System.Management.Automation; + +namespace PnP.PowerShell.Commands.Microsoft365Groups +{ + [Cmdlet(VerbsCommon.Get, "PnPMicrosoft365GroupTeam")] + [RequiredMinimalApiPermissions("Group.Read.All")] + public class GetMicrosoft365GroupTeam : PnPGraphCmdlet + { + [Parameter(Mandatory = false, ValueFromPipeline = true, Position = 0)] + public Microsoft365GroupPipeBind Identity; + + protected override void ExecuteCmdlet() + { + Guid groupId; + if (ParameterSpecified(nameof(Identity))) + { + WriteVerbose($"Defining Microsoft 365 Group based on {nameof(Identity)} parameter"); + groupId = Identity.GetGroupId(Connection, AccessToken); + } + else + { + WriteVerbose($"Validating if the current site at {Connection.Url} has a Microsoft 365 Group behind it"); + ClientContext.Load(ClientContext.Site, s => s.GroupId); + ClientContext.ExecuteQueryRetry(); + + groupId = ClientContext.Site.GroupId; + + if(groupId == Guid.Empty) + { + throw new PSArgumentException("Current site is not backed by a Microsoft 365 Group", nameof(Identity)); + } + else + { + WriteVerbose($"Current site at {Connection.Url} is backed by the Microsoft 365 Group with Id {groupId}"); + } + } + + WriteVerbose($"Requesting endpoints of Microsoft 365 Group with Id {groupId}"); + var endpoints = GraphHelper.GetResultCollectionAsync(Connection, $"/beta/groups/{groupId}/endpoints", AccessToken).GetAwaiter().GetResult(); + WriteVerbose($"{endpoints.Count()} endpoint(s) found in total"); + + var yammerEndpoint = endpoints.Where(e => e.ProviderName.Equals("Microsoft Teams", StringComparison.InvariantCultureIgnoreCase)); + WriteVerbose($"{yammerEndpoint.Count()} Teams endpoint(s) found"); + + WriteObject(yammerEndpoint, true); + } + } +} \ No newline at end of file diff --git a/src/Commands/Microsoft365Groups/GetMicrosoft365GroupYammerCommunity.cs b/src/Commands/Microsoft365Groups/GetMicrosoft365GroupYammerCommunity.cs new file mode 100644 index 000000000..d5eb3b9a4 --- /dev/null +++ b/src/Commands/Microsoft365Groups/GetMicrosoft365GroupYammerCommunity.cs @@ -0,0 +1,55 @@ +using Microsoft.SharePoint.Client; +using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Utilities.REST; +using System; +using System.Linq; +using System.Management.Automation; + +namespace PnP.PowerShell.Commands.Microsoft365Groups +{ + [Cmdlet(VerbsCommon.Get, "PnPMicrosoft365GroupYammerCommunity")] + [RequiredMinimalApiPermissions("Group.Read.All")] + public class GetMicrosoft365GroupYammerCommunity : PnPGraphCmdlet + { + [Parameter(Mandatory = false, ValueFromPipeline = true, Position = 0)] + public Microsoft365GroupPipeBind Identity; + + protected override void ExecuteCmdlet() + { + Guid groupId; + if (ParameterSpecified(nameof(Identity))) + { + WriteVerbose($"Defining Microsoft 365 Group based on {nameof(Identity)} parameter"); + groupId = Identity.GetGroupId(Connection, AccessToken); + } + else + { + WriteVerbose($"Validating if the current site at {Connection.Url} has a Microsoft 365 Group behind it"); + ClientContext.Load(ClientContext.Site, s => s.GroupId); + ClientContext.ExecuteQueryRetry(); + + groupId = ClientContext.Site.GroupId; + + if(groupId == Guid.Empty) + { + throw new PSArgumentException("Current site is not backed by a Microsoft 365 Group", nameof(Identity)); + } + else + { + WriteVerbose($"Current site at {Connection.Url} is backed by the Microsoft 365 Group with Id {groupId}"); + } + } + + WriteVerbose($"Requesting endpoints of Microsoft 365 Group with Id {groupId}"); + var endpoints = GraphHelper.GetResultCollectionAsync(Connection, $"/beta/groups/{groupId}/endpoints", AccessToken).GetAwaiter().GetResult(); + WriteVerbose($"{endpoints.Count()} endpoint(s) found in total"); + + var yammerEndpoint = endpoints.Where(e => e.ProviderName.Equals("Yammer", StringComparison.InvariantCultureIgnoreCase)); + WriteVerbose($"{yammerEndpoint.Count()} Yammer endpoint(s) found"); + + WriteObject(yammerEndpoint, true); + } + } +} \ No newline at end of file diff --git a/src/Commands/Model/AzureAD/AzureADGroupEndPoint.cs b/src/Commands/Model/AzureAD/AzureADGroupEndPoint.cs new file mode 100644 index 000000000..56e85c7b8 --- /dev/null +++ b/src/Commands/Model/AzureAD/AzureADGroupEndPoint.cs @@ -0,0 +1,32 @@ +using System; +using System.Text.Json.Serialization; + +namespace PnP.PowerShell.Commands.Model.AzureAD +{ + /// + /// Definition of a Microsoft 365 Group Endpoint such as Yammer or Teams + /// + public class AzureADGroupEndPoint + { + [JsonPropertyName("id")] + public Guid? Id { get; set; } + + [JsonPropertyName("deletedDateTime")] + public DateTime? DeletedDateTime { get; set; } + + [JsonPropertyName("capability")] + public string Capability { get; set; } + + [JsonPropertyName("providerId")] + public string ProviderId { get; set; } + + [JsonPropertyName("providerName")] + public string ProviderName { get; set; } + + [JsonPropertyName("uri")] + public string Uri { get; set; } + + [JsonPropertyName("providerResourceId")] + public string ProviderResourceId { get; set; } + } +} \ No newline at end of file From 0c9f0656eea783e64bc793088bb32c40a40b7d02 Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Tue, 28 Jun 2022 13:29:45 +0200 Subject: [PATCH 431/458] Update Set-PnPTemporarilyDisableAppBar.md (#2047) --- documentation/Set-PnPTemporarilyDisableAppBar.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/documentation/Set-PnPTemporarilyDisableAppBar.md b/documentation/Set-PnPTemporarilyDisableAppBar.md index d83f63862..cdaf0a4d9 100644 --- a/documentation/Set-PnPTemporarilyDisableAppBar.md +++ b/documentation/Set-PnPTemporarilyDisableAppBar.md @@ -7,7 +7,7 @@ external help file: PnP.PowerShell.dll-Help.xml online version: https://pnp.github.io/powershell/cmdlets/Set-PnPTemporarilyDisableAppBar.html --- -# Set-PnPHomeSite +# Set-PnPTemporarilyDisableAppBar ## SYNOPSIS @@ -20,7 +20,7 @@ Allows the SharePoint Online App Bar to be disabled. It may take some time for t ## SYNTAX ```powershell -Set-PnPTemporarilyDisableAppBar [] +Set-PnPTemporarilyDisableAppBar -Enabled [] ``` ## DESCRIPTION @@ -44,7 +44,7 @@ Shows the SharePoint Online App Bar. ## PARAMETERS ### -Enable -The url of the site to set as the home site +Specifies whether to show or hide SharePoint Online App Bar. ```yaml Type: Boolean From 833a7cb6bcd30fcc00f26382fd72768fb11d184f Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Tue, 28 Jun 2022 13:30:17 +0200 Subject: [PATCH 432/458] Removed WhatIf and Confirm parameter (#2046) The parameters are not available --- documentation/Set-PnPTenantAppCatalogUrl.md | 37 +++------------------ 1 file changed, 4 insertions(+), 33 deletions(-) diff --git a/documentation/Set-PnPTenantAppCatalogUrl.md b/documentation/Set-PnPTenantAppCatalogUrl.md index a030ce100..78f5eb915 100644 --- a/documentation/Set-PnPTenantAppCatalogUrl.md +++ b/documentation/Set-PnPTenantAppCatalogUrl.md @@ -25,6 +25,7 @@ Set-PnPTenantAppCatalogUrl -Url [-Connection ] ``` ## DESCRIPTION +This cmdlet sets the tenant scoped app catalog to the specified url. ## EXAMPLES @@ -37,35 +38,6 @@ Sets the tenant scoped app catalog to the provided site collection url ## PARAMETERS -### -Confirm -Prompts you for confirmation before running the cmdlet. - -```yaml -Type: SwitchParameter -Parameter Sets: (All) -Aliases: cf - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - -### -Connection -Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. - -```yaml -Type: PnPConnection -Parameter Sets: (All) - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - ### -Url The url of the site to set as the tenant scoped app catalog @@ -80,13 +52,12 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -WhatIf -Shows what would happen if the cmdlet runs. The cmdlet is not run. +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. ```yaml -Type: SwitchParameter +Type: PnPConnection Parameter Sets: (All) -Aliases: wi Required: False Position: Named From 1574186cf35aa5178dd45d2c6aa59724fcccbde5 Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Tue, 28 Jun 2022 13:30:49 +0200 Subject: [PATCH 433/458] Update Set-PnPTermSet.md (#2045) --- documentation/Set-PnPTermSet.md | 37 +++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/documentation/Set-PnPTermSet.md b/documentation/Set-PnPTermSet.md index 3953c25fc..53193df25 100644 --- a/documentation/Set-PnPTermSet.md +++ b/documentation/Set-PnPTermSet.md @@ -9,7 +9,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Set-PnPTermSet.html # Set-PnPTermSet ## SYNOPSIS -Updates an existing TermSet +Updates an existing term set. ## SYNTAX @@ -24,7 +24,7 @@ Set-PnPTermSet -Identity [-TermGroup] Date: Tue, 28 Jun 2022 13:59:25 +0200 Subject: [PATCH 434/458] Update Set-PnPTeamsTeam.md --- documentation/Set-PnPTeamsTeam.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Set-PnPTeamsTeam.md b/documentation/Set-PnPTeamsTeam.md index 287cbbbd5..d2714395a 100644 --- a/documentation/Set-PnPTeamsTeam.md +++ b/documentation/Set-PnPTeamsTeam.md @@ -61,7 +61,7 @@ Updates the team 'My Team' to disallow Team @mentions, allow Channel @mentions a Set-PnPTeamsTeam -Identity "My Team" -GiphyContentRating Moderate ``` -Updates the team 'My Team' to make the have a Moderate level of sensitivity for giphy usage. +Updates the team 'My Team' to have a Moderate level of sensitivity for giphy usage. ## PARAMETERS From 54fc15589250953342c09fd10f61dcba6f7c60d9 Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Tue, 28 Jun 2022 14:14:15 +0200 Subject: [PATCH 435/458] Update Set-PnPSubscribeSharePointNewsDigest.md --- documentation/Set-PnPSubscribeSharePointNewsDigest.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Set-PnPSubscribeSharePointNewsDigest.md b/documentation/Set-PnPSubscribeSharePointNewsDigest.md index 0f3d1ebb5..35b7157ac 100644 --- a/documentation/Set-PnPSubscribeSharePointNewsDigest.md +++ b/documentation/Set-PnPSubscribeSharePointNewsDigest.md @@ -44,7 +44,7 @@ Enables the user user@domain.com for receiving the SharePoint News Digest e-mail Set-PnPSubscribeSharePointNewsDigest -Account 'user@domain.com' -Enabled:$false ``` -Stops the user user@domain.com for receiving the SharePoint News Digest e-mails. +Stops the user user@domain.com from receiving the SharePoint News Digest e-mails. ## PARAMETERS From 872e84a73ed20ca108e749d5ff745cdac6f12772 Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Tue, 28 Jun 2022 15:33:12 +0200 Subject: [PATCH 436/458] Update Set-PnPStructuralNavigationCacheWebState.md --- .../Set-PnPStructuralNavigationCacheWebState.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/documentation/Set-PnPStructuralNavigationCacheWebState.md b/documentation/Set-PnPStructuralNavigationCacheWebState.md index 0496fa296..eb6c16e85 100644 --- a/documentation/Set-PnPStructuralNavigationCacheWebState.md +++ b/documentation/Set-PnPStructuralNavigationCacheWebState.md @@ -19,7 +19,7 @@ Set-PnPStructuralNavigationCacheWebState -IsEnabled [-WebUrl ] ``` ## DESCRIPTION -The Set-PnPStructuralNavigationCacheWebtate cmdlet can be used to enable or disable caching for a webs in a site collection. If the WebUrl parameter has not been specified the currently connected to site will be used. +The Set-PnPStructuralNavigationCacheWebState cmdlet can be used to enable or disable caching for a webs in a site collection. If the WebUrl parameter has not been specified the currently connected to site will be used. ## EXAMPLES @@ -30,22 +30,23 @@ Set-PnPStructuralNavigationCacheWebState -IsEnabled $true -WebUrl "https://conto This example enables caching for the web https://contoso.sharepoint.com/sites/product/electronics. -### Example 1 +### Example 2 ```powershell -Set-PnPStructuralNavigationCacheSiteState -IsEnabled $false -SiteUrl "https://contoso.sharepoint.com/sites/product/electronics" +Set-PnPStructuralNavigationCacheWebState -IsEnabled $false -WebUrl "https://contoso.sharepoint.com/sites/product/electronics" ``` -This example disabled caching for all webs in the web https://contoso.sharepoint.com/sites/product/electronics. +This example disables caching for the web https://contoso.sharepoint.com/sites/product/electronics. ## PARAMETERS ### -IsEnabled -$true to enable caching, $false to disable caching.. +$true to enable caching, $false to disable caching. ```yaml Type: Boolean Parameter Sets: (All) Aliases: + Required: True Position: Named Default value: None @@ -53,13 +54,14 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -SiteUrl +### -WebUrl Specifies the absolute URL for the web that needs its caching state set. ```yaml Type: String Parameter Sets: (All) Aliases: + Required: False Position: Named Default value: None From 7a061bb89419e102aa752d5a6994cbd9058d5ca2 Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Tue, 28 Jun 2022 17:02:15 +0200 Subject: [PATCH 437/458] Update Set-PnPStructuralNavigationCacheSiteState.md --- ...t-PnPStructuralNavigationCacheSiteState.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/documentation/Set-PnPStructuralNavigationCacheSiteState.md b/documentation/Set-PnPStructuralNavigationCacheSiteState.md index 2d6a2c0e3..eaa4eb80d 100644 --- a/documentation/Set-PnPStructuralNavigationCacheSiteState.md +++ b/documentation/Set-PnPStructuralNavigationCacheSiteState.md @@ -15,7 +15,7 @@ Enable or disable caching for all webs in a site collection. ## SYNTAX ``` -Set-PnPStructuralNavigationCacheSiteState -IsEnabled [-SiteUrl ] +Set-PnPStructuralNavigationCacheSiteState -IsEnabled [-SiteUrl ] [-Connection ] ``` ## DESCRIPTION @@ -35,7 +35,7 @@ This example enables caching for all webs in the site collection https://contoso Set-PnPStructuralNavigationCacheSiteState -IsEnabled $false -SiteUrl "https://contoso.sharepoint.com/sites/product/" ``` -This example disabled caching for all webs in the site collection https://contoso.sharepoint.com/sites/product/. +This example disables caching for all webs in the site collection https://contoso.sharepoint.com/sites/product/. ## PARAMETERS @@ -67,6 +67,21 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + + ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) From 6688dbcd7a548806d8aff38b337bd2d75f3cb955 Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 29 Jun 2022 00:50:14 +0200 Subject: [PATCH 438/458] Fixes #2029 --- CHANGELOG.md | 3 +- src/Commands/Admin/GetTenantTheme.cs | 5 --- src/Commands/Base/PnPAdminCmdlet.cs | 59 ++++++++++++++++++++-------- 3 files changed, 45 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c34329d07..95edf4c3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -93,7 +93,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Get-PnPTeamsTeam`, the cmdlet now also returns additional properties like `WebUrl, CreatedDateTime, InternalId`. [#1825](https://github.com/pnp/powershell/pull/1825) - Fixed `Set-PnPListPermission`, it will now throw error if the list does not exist. [#1891](https://github.com/pnp/powershell/pull/1891) - Fixed `Invoke-PnPSPRestMethod` invalid parsing for SharePoint number columns. [#1877](https://github.com/pnp/powershell/pull/1879) -- Fix issue with `Add/Set-PnPListItem` not throwing correct exception for invalid taxonomy values. [#1870](https://github.com/pnp/powershell/pull/1870) +- Fixed issue with `Add/Set-PnPListItem` not throwing correct exception for invalid taxonomy values. [#1870](https://github.com/pnp/powershell/pull/1870) - Fixed `Sync-PnPSharePointUserProfilesFromAzureActiveDirectory` throwing an "Object reference not set to an instance of an object" exception when providing an empty users collection or incorrect user mapping [#1896](https://github.com/pnp/powershell/pull/1896) - Fixed `Connect-PnPOnline -ReturnConnection` also setting the current connection instead of just the returned connection [#1919](https://github.com/pnp/powershell/pull/1919) - Fixed `Disconnect-PnPOnline -Connection` also disconnecting other connections next to the provided connection [#1919](https://github.com/pnp/powershell/pull/1919) @@ -112,6 +112,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Get-PnPHubSiteChild` throwing an exception when passing in a URL that is actually not a hub site [#2033](https://github.com/pnp/powershell/pull/2033) - Fixed `Add-PnPListItem` not showing field name when it has an improper value assigned to it [#2002](https://github.com/pnp/powershell/pull/2002) - Fixed connecting using `Connect-PnPOnline -Interactive -ClientId` not working well when already having an App-Only connection using the same ClientId [#2035](https://github.com/pnp/powershell/pull/2035) +- Fixed cmdlets inheriting from PnPAdminCmdlet not working well on vanity domain SharePoint Online tenants ### Removed diff --git a/src/Commands/Admin/GetTenantTheme.cs b/src/Commands/Admin/GetTenantTheme.cs index 4551b3372..cdb52cf6b 100644 --- a/src/Commands/Admin/GetTenantTheme.cs +++ b/src/Commands/Admin/GetTenantTheme.cs @@ -1,11 +1,7 @@ using Microsoft.Online.SharePoint.TenantAdministration; using Microsoft.SharePoint.Client; - using PnP.PowerShell.Commands.Base; using System.Management.Automation; -using PnP.Framework.Sites; -using PnP.PowerShell.Commands.Base.PipeBinds; -using System; using System.Linq; using PnP.PowerShell.Commands.Model; using System.Text.Json; @@ -51,7 +47,6 @@ protected override void ExecuteCmdlet() WriteObject(themes.Select(t => new SPOTheme(t.Name, t.Palette, t.IsInverted)), true); } } - } } } \ No newline at end of file diff --git a/src/Commands/Base/PnPAdminCmdlet.cs b/src/Commands/Base/PnPAdminCmdlet.cs index e510fc08c..26b41a4ff 100644 --- a/src/Commands/Base/PnPAdminCmdlet.cs +++ b/src/Commands/Base/PnPAdminCmdlet.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Management.Automation; +using System.Net; using Microsoft.Online.SharePoint.TenantAdministration; using Microsoft.SharePoint.Client; using PnP.PowerShell.Commands.Enums; @@ -11,10 +12,11 @@ namespace PnP.PowerShell.Commands.Base /// Base cmdlet for cmdlets that require running on against the admin site collection /// public abstract class PnPAdminCmdlet : PnPSharePointCmdlet - { + { private Tenant _tenant; - private Uri _baseUri; - + /// + /// Tenant instance + /// public Tenant Tenant { get @@ -26,7 +28,11 @@ public Tenant Tenant return _tenant; } } - + + private Uri _baseUri; + /// + /// The root sitecollection URL of the SharePoint Online tenant + /// public Uri BaseUri => _baseUri; /// @@ -60,9 +66,10 @@ protected override void BeginProcessing() Connection.CacheContext(); - if (Connection.TenantAdminUrl != null && - (Connection.ConnectionType == ConnectionType.O365)) + string tenantAdminUrl; + if (Connection.TenantAdminUrl != null && Connection.ConnectionType == ConnectionType.O365) { + // An explicit SharePoint Online Admin Center URL has been provided in the connect, use it var uri = new Uri(Connection.Url); var uriParts = uri.Host.Split('.'); if (uriParts[0].ToLower().EndsWith("-admin")) @@ -72,32 +79,52 @@ protected override void BeginProcessing() else { _baseUri = new Uri($"{uri.Scheme}://{uri.Authority}"); - } - IsDeviceLogin(Connection.TenantAdminUrl); - Connection.CloneContext(Connection.TenantAdminUrl); + } + + tenantAdminUrl = Connection.TenantAdminUrl; } else { + // No explicit SharePoint Online Admin Center URL has been provided in the connect, try to guess it using the default -admin.sharepoint. syntax Uri uri = new Uri(ClientContext.Url); var uriParts = uri.Host.Split('.'); - if (!uriParts[0].EndsWith("-admin") && - Connection.ConnectionType == ConnectionType.O365) - { + + if (!uriParts[0].EndsWith("-admin") && Connection.ConnectionType == ConnectionType.O365) + { + // The current connection has not been made to the SharePoint Online Admin Center, try to predict the admin center URL _baseUri = new Uri($"{uri.Scheme}://{uri.Authority}"); // Remove -my postfix from the tenant name, if present, to allow elevation to the admin context even when being connected to the MySite var tenantName = uriParts[0].EndsWith("-my") ? uriParts[0].Remove(uriParts[0].Length - 3, 3) : uriParts[0]; - var adminUrl = $"https://{tenantName}-admin.{string.Join(".", uriParts.Skip(1))}"; - IsDeviceLogin(adminUrl); - Connection.Context = - Connection.CloneContext(adminUrl); + tenantAdminUrl = $"https://{tenantName}-admin.{string.Join(".", uriParts.Skip(1))}"; } else { + // The current connection has been made to the SharePoint Online Admin Center URL already, we can use it as is _baseUri = new Uri($"{uri.Scheme}://{uriParts[0].ToLower().Replace("-admin", "")}{(uriParts.Length > 1 ? $".{string.Join(".", uriParts.Skip(1))}" : string.Empty)}{(!uri.IsDefaultPort ? ":" + uri.Port : "")}"); + return; } } + + // Check if a connection has been made using DeviceLogin, in this case we cannot clone the context to the admin URL and will throw an exception + IsDeviceLogin(Connection.TenantAdminUrl); + + // Set up a temporary context to the SharePoint Online Admin Center URL to allow this cmdlet to execute + WriteVerbose($"Connecting to the SharePoint Online Admin Center at '{Connection.TenantAdminUrl}' to run this cmdlet"); + try + { + Connection.Context = Connection.CloneContext(Connection.TenantAdminUrl); + } + catch(WebException e) when (e.Status == WebExceptionStatus.NameResolutionFailure) + { + throw new PSInvalidOperationException($"The hostname '{Connection.TenantAdminUrl}' which you have passed in your Connect-PnPOnline -TenantAdminUrl is invalid. Please connect again using the proper hostname.", e); + } + catch(Exception e) + { + throw new PSInvalidOperationException($"Unable to connect to the SharePoint Online Admin Center at '{Connection.TenantAdminUrl}' to run this cmdlet. Please ensure you pass in the correct Admin Center URL using Connect-PnPOnline -TenantAdminUrl and you have access to it. Error message: {e.Message}.", e); + } + WriteVerbose($"Connected to the SharePoint Online Admin Center at '{Connection.TenantAdminUrl}' to run this cmdlet"); } /// From 80eb2b3118a4844406a9cdcd768ac499b386693f Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 29 Jun 2022 00:52:30 +0200 Subject: [PATCH 439/458] Adding PR reference --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95edf4c3d..b5a58969b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -112,7 +112,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Get-PnPHubSiteChild` throwing an exception when passing in a URL that is actually not a hub site [#2033](https://github.com/pnp/powershell/pull/2033) - Fixed `Add-PnPListItem` not showing field name when it has an improper value assigned to it [#2002](https://github.com/pnp/powershell/pull/2002) - Fixed connecting using `Connect-PnPOnline -Interactive -ClientId` not working well when already having an App-Only connection using the same ClientId [#2035](https://github.com/pnp/powershell/pull/2035) -- Fixed cmdlets inheriting from PnPAdminCmdlet not working well on vanity domain SharePoint Online tenants +- Fixed cmdlets inheriting from PnPAdminCmdlet not working well on vanity domain SharePoint Online tenants [#2052](https://github.com/pnp/powershell/pull/2052) ### Removed From e95455470a6f46fe9fd79674af06fcf0ee36c0a0 Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 29 Jun 2022 00:58:39 +0200 Subject: [PATCH 440/458] Changing to tenantAdminUrl --- src/Commands/Base/PnPAdminCmdlet.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Commands/Base/PnPAdminCmdlet.cs b/src/Commands/Base/PnPAdminCmdlet.cs index 26b41a4ff..68551f0ba 100644 --- a/src/Commands/Base/PnPAdminCmdlet.cs +++ b/src/Commands/Base/PnPAdminCmdlet.cs @@ -108,23 +108,23 @@ protected override void BeginProcessing() } // Check if a connection has been made using DeviceLogin, in this case we cannot clone the context to the admin URL and will throw an exception - IsDeviceLogin(Connection.TenantAdminUrl); + IsDeviceLogin(tenantAdminUrl); // Set up a temporary context to the SharePoint Online Admin Center URL to allow this cmdlet to execute - WriteVerbose($"Connecting to the SharePoint Online Admin Center at '{Connection.TenantAdminUrl}' to run this cmdlet"); + WriteVerbose($"Connecting to the SharePoint Online Admin Center at '{tenantAdminUrl}' to run this cmdlet"); try { - Connection.Context = Connection.CloneContext(Connection.TenantAdminUrl); + Connection.Context = Connection.CloneContext(tenantAdminUrl); } catch(WebException e) when (e.Status == WebExceptionStatus.NameResolutionFailure) { - throw new PSInvalidOperationException($"The hostname '{Connection.TenantAdminUrl}' which you have passed in your Connect-PnPOnline -TenantAdminUrl is invalid. Please connect again using the proper hostname.", e); + throw new PSInvalidOperationException($"The hostname '{tenantAdminUrl}' which you have passed in your Connect-PnPOnline -TenantAdminUrl is invalid. Please connect again using the proper hostname.", e); } catch(Exception e) { throw new PSInvalidOperationException($"Unable to connect to the SharePoint Online Admin Center at '{Connection.TenantAdminUrl}' to run this cmdlet. Please ensure you pass in the correct Admin Center URL using Connect-PnPOnline -TenantAdminUrl and you have access to it. Error message: {e.Message}.", e); } - WriteVerbose($"Connected to the SharePoint Online Admin Center at '{Connection.TenantAdminUrl}' to run this cmdlet"); + WriteVerbose($"Connected to the SharePoint Online Admin Center at '{tenantAdminUrl}' to run this cmdlet"); } /// From db026312b641bdda5f4907839b45febac6f4d9c1 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 29 Jun 2022 01:04:38 +0200 Subject: [PATCH 441/458] Update PnPAdminCmdlet.cs Fixing forgotten to update parameter reference --- src/Commands/Base/PnPAdminCmdlet.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Commands/Base/PnPAdminCmdlet.cs b/src/Commands/Base/PnPAdminCmdlet.cs index 68551f0ba..fb0850645 100644 --- a/src/Commands/Base/PnPAdminCmdlet.cs +++ b/src/Commands/Base/PnPAdminCmdlet.cs @@ -122,7 +122,7 @@ protected override void BeginProcessing() } catch(Exception e) { - throw new PSInvalidOperationException($"Unable to connect to the SharePoint Online Admin Center at '{Connection.TenantAdminUrl}' to run this cmdlet. Please ensure you pass in the correct Admin Center URL using Connect-PnPOnline -TenantAdminUrl and you have access to it. Error message: {e.Message}.", e); + throw new PSInvalidOperationException($"Unable to connect to the SharePoint Online Admin Center at '{tenantAdminUrl}' to run this cmdlet. Please ensure you pass in the correct Admin Center URL using Connect-PnPOnline -TenantAdminUrl and you have access to it. Error message: {e.Message}.", e); } WriteVerbose($"Connected to the SharePoint Online Admin Center at '{tenantAdminUrl}' to run this cmdlet"); } @@ -138,4 +138,4 @@ protected override void EndProcessing() Connection.Context = SiteContext; } } -} \ No newline at end of file +} From 1fcb2294f7beaa38c039daf10ac48b72d8591504 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 29 Jun 2022 01:43:08 +0200 Subject: [PATCH 442/458] Update Connect-PnPOnline.md Documentation update --- documentation/Connect-PnPOnline.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/documentation/Connect-PnPOnline.md b/documentation/Connect-PnPOnline.md index 361dab93b..1e8c63db6 100644 --- a/documentation/Connect-PnPOnline.md +++ b/documentation/Connect-PnPOnline.md @@ -204,7 +204,7 @@ Using this method PnP PowerShell will not acquire tokens dynamically and if the Using this parameter you can provide your own access token. Notice that it is recommend to use one of the other connection methods as this will limits the offered functionality on PnP PowerShell. For instance if the token expires (typically after 1 hour) will not be able to acquire a new valid token, which the other connection methods do allow. -You are fully responsible for providing your own valid token, for the correct audience, with the correct permissions scopes. +You are responsible for providing your own valid access token when using this parameter, for the correct audience, with the correct permissions scopes. ```yaml Type: String @@ -292,7 +292,7 @@ Accept wildcard characters: False ``` ### -ClientSecret -The client secret to use. +The client secret to use. When using this, technically an Azure Access Control Service (ACS) authentication will take place. This effectively means only cmdlets that are connecting to SharePoint Online will work. Cmdlets using Microsoft Graph or any other API behind the scenes will not work. ```yaml Type: String @@ -442,7 +442,7 @@ Accept wildcard characters: False ``` ### -ReturnConnection -Returns the connection for use with the -Connection parameter on cmdlets. +Returns the connection for use with the -Connection parameter on cmdlets. It will not touch the current connection which can be established by omitting this parameter. ```yaml Type: SwitchParameter @@ -457,8 +457,7 @@ Accept wildcard characters: False ``` ### -Tenant -The Azure AD Tenant name,e.g. -mycompany.onmicrosoft.com +The Azure Active Directory tenant name, e.g. mycompany.onmicrosoft.com or mycompany.com if you have added custom domains to your tenant ```yaml Type: String @@ -648,4 +647,4 @@ Accept wildcard characters: False ## RELATED LINKS -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) From c8c9f86bc6ea0c71d376570cb5807b190d4d648e Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 29 Jun 2022 04:04:48 +0000 Subject: [PATCH 443/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index 962c1800c..5027c4dde 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -420871f1514593bca01a2a2a82f82dead58d2235 \ No newline at end of file +c7d60731a6ba5e5ebda99d2da7ca3fee8a8e6683 \ No newline at end of file diff --git a/version.txt b/version.txt index 92542a378..073ff00b0 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.62 \ No newline at end of file +1.10.63 \ No newline at end of file From d0cbcc37ab9424b9cd033b18eada9c11024075eb Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 29 Jun 2022 15:54:34 +0200 Subject: [PATCH 444/458] Fixes `Copy-PnPList` throwing unauthorized exception when used with a non SPO admin user (#2054) * Fixes #1927 * Adding PR reference Co-authored-by: = <=> --- CHANGELOG.md | 1 + src/Commands/Base/PnPWebCmdlet.cs | 1 - src/Commands/Lists/CopyList.cs | 33 ++++++++----------- .../Model/SharePoint/SiteScriptFromList.cs | 13 ++++++++ src/Commands/Utilities/REST/RestHelper.cs | 5 +-- src/Commands/Utilities/REST/RestResult.cs | 17 ++++++++++ 6 files changed, 48 insertions(+), 22 deletions(-) create mode 100644 src/Commands/Model/SharePoint/SiteScriptFromList.cs create mode 100644 src/Commands/Utilities/REST/RestResult.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index b5a58969b..2c669be6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -113,6 +113,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Add-PnPListItem` not showing field name when it has an improper value assigned to it [#2002](https://github.com/pnp/powershell/pull/2002) - Fixed connecting using `Connect-PnPOnline -Interactive -ClientId` not working well when already having an App-Only connection using the same ClientId [#2035](https://github.com/pnp/powershell/pull/2035) - Fixed cmdlets inheriting from PnPAdminCmdlet not working well on vanity domain SharePoint Online tenants [#2052](https://github.com/pnp/powershell/pull/2052) +- Fixed `Copy-PnPList` throwing an unauthorized exception when using it with a non SharePoint Online administrator user [#2054](https://github.com/pnp/powershell/pull/2054) ### Removed diff --git a/src/Commands/Base/PnPWebCmdlet.cs b/src/Commands/Base/PnPWebCmdlet.cs index 876639543..439c6c139 100644 --- a/src/Commands/Base/PnPWebCmdlet.cs +++ b/src/Commands/Base/PnPWebCmdlet.cs @@ -3,7 +3,6 @@ using System.Management.Automation; using Microsoft.SharePoint.Client; - namespace PnP.PowerShell.Commands { public abstract class PnPWebCmdlet : PnPSharePointCmdlet diff --git a/src/Commands/Lists/CopyList.cs b/src/Commands/Lists/CopyList.cs index 270ab5929..29c51cb7e 100644 --- a/src/Commands/Lists/CopyList.cs +++ b/src/Commands/Lists/CopyList.cs @@ -1,11 +1,11 @@ using System.Management.Automation; using Microsoft.SharePoint.Client; -using PnP.PowerShell.Commands.Base; using PnP.PowerShell.Commands.Base.PipeBinds; -using Microsoft.Online.SharePoint.TenantAdministration; using System; using System.Text.RegularExpressions; using System.Linq; +using PnP.PowerShell.Commands.Utilities.REST; +using PnP.PowerShell.Commands.Model.SharePoint; namespace PnP.PowerShell.Commands.Lists { @@ -62,18 +62,10 @@ protected override void ExecuteCmdlet() // Generate a site script from the list that needs to be copied WriteVerbose($"Generating script from list at {SourceListUrl}"); - var scriptRequest = Tenant.GetSiteScriptFromList(ClientContext, SourceListUrl); - try - { - ClientContext.ExecuteQueryRetry(); - } - catch (Microsoft.SharePoint.Client.ServerException e) when (e.ServerErrorTypeName == "System.IO.FileNotFoundException") - { - throw new PSArgumentException($"List provided through {nameof(SourceListUrl)} could not be found", nameof(SourceListUrl)); - } + var generatedScript = RestHelper.PostAsync>(Connection.HttpClient, $"{Connection.Url}/_api/Microsoft.Sharepoint.Utilities.WebTemplateExtensions.SiteScriptUtility.GetSiteScriptFromList()", ClientContext, new { listUrl = SourceListUrl}).GetAwaiter().GetResult(); // Take the site script of the list to copy - var script = scriptRequest.Value; + var script = generatedScript.Content; if (ParameterSpecified(nameof(Title)) && !string.IsNullOrWhiteSpace(Title)) { @@ -96,31 +88,34 @@ protected override void ExecuteCmdlet() // Execute site script on destination site so the list will be created WriteVerbose($"Executing site script to site at {DestinationWebUrl}"); - var actionResults = PnP.PowerShell.Commands.Utilities.SiteTemplates.InvokeSiteScript(Connection, AccessToken, script, DestinationWebUrl).GetAwaiter().GetResult().Items.ToArray(); + var actionResults = RestHelper.PostAsync>(Connection.HttpClient, $"{Connection.Url}/_api/Microsoft.Sharepoint.Utilities.WebTemplateExtensions.SiteScriptUtility.ExecuteTemplateScript()", ClientContext, new { script = script}).GetAwaiter().GetResult(); // Ensure site script actions have been executed - if(actionResults.Length == 0) + if(actionResults.Items.Count() == 0) { throw new PSInvalidOperationException($"List copy failed. No site script actions have been executed."); } // Display the results of each action in verbose - foreach(var actionResult in actionResults) + foreach(var actionResult in actionResults.Items) { WriteVerbose($"Action {actionResult.Title} {(actionResult.ErrorCode != 0 ? $"failed: {actionResult.OutcomeText}" : "succeeded")}"); } // Ensure the list creation succeeded - if(actionResults[0].ErrorCode != 0) + if(actionResults.Items.ElementAt(0).ErrorCode != 0) { - throw new PSInvalidOperationException($"List copy failed with error {actionResults[0].OutcomeText}"); + throw new PSInvalidOperationException($"List copy failed with error {actionResults.Items.ElementAt(0).OutcomeText}"); } - // Create a ClientContext to the web where the list has been created + //Create a ClientContext to the web where the list has been created var destinationContext = ClientContext.Clone(DestinationWebUrl); // Retrieve the newly created list - var createdList = destinationContext.Web.Lists.GetById(Guid.Parse(actionResults[0].TargetId)); + var newListId = actionResults.Items.ElementAt(0).TargetId; + + WriteVerbose($"Retrieving newly created list hosted in {DestinationWebUrl} with ID {newListId}"); + var createdList = destinationContext.Web.Lists.GetById(Guid.Parse(newListId)); destinationContext.Load(createdList, l => l.Id, l => l.BaseTemplate, l => l.OnQuickLaunch, l => l.DefaultViewUrl, l => l.Title, l => l.Hidden, l => l.ContentTypesEnabled, l => l.RootFolder.ServerRelativeUrl); destinationContext.ExecuteQuery(); diff --git a/src/Commands/Model/SharePoint/SiteScriptFromList.cs b/src/Commands/Model/SharePoint/SiteScriptFromList.cs new file mode 100644 index 000000000..34befd3fc --- /dev/null +++ b/src/Commands/Model/SharePoint/SiteScriptFromList.cs @@ -0,0 +1,13 @@ +namespace PnP.PowerShell.Commands.Model.SharePoint +{ + /// + /// Contains the information regarding a site script which was generated from an existing list + /// + public class SiteScriptFromList + { + /// + /// The site script + /// + public string GetSiteScriptFromList { get; set; } + } +} \ No newline at end of file diff --git a/src/Commands/Utilities/REST/RestHelper.cs b/src/Commands/Utilities/REST/RestHelper.cs index 15ce5e661..b769086fc 100644 --- a/src/Commands/Utilities/REST/RestHelper.cs +++ b/src/Commands/Utilities/REST/RestHelper.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Net; using System.Net.Http; +using System.Net.Http.Headers; using System.Text; using System.Text.Json; using System.Threading; @@ -223,7 +224,7 @@ public static async Task PostAsync(HttpClient httpClient, string url, st HttpRequestMessage message = null; if (payload != null) { - var content = new StringContent(JsonSerializer.Serialize(payload, new JsonSerializerOptions() { IgnoreNullValues = true })); + var content = new StringContent(JsonSerializer.Serialize(payload, new JsonSerializerOptions { IgnoreNullValues = true })); content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); message = GetMessage(url, HttpMethod.Post, accessToken, accept, content); } @@ -591,7 +592,7 @@ private static HttpRequestMessage GetMessage(string url, HttpMethod method, Clie var message = new HttpRequestMessage(); message.Method = method; message.RequestUri = new Uri(url); - message.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue(accept)); + message.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse(accept)); PnP.Framework.Http.PnPHttpClient.AuthenticateRequestAsync(message, clientContext).GetAwaiter().GetResult(); if (method == HttpMethod.Post || method == HttpMethod.Put || method.Method == "PATCH") { diff --git a/src/Commands/Utilities/REST/RestResult.cs b/src/Commands/Utilities/REST/RestResult.cs new file mode 100644 index 000000000..2389d6f40 --- /dev/null +++ b/src/Commands/Utilities/REST/RestResult.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization; + +namespace PnP.PowerShell.Commands.Utilities.REST +{ + /// + /// Contains a single result + /// + /// Model type to map the content to + public class RestResult + { + /// + /// The content contained in the results + /// + [JsonPropertyName("value")] + public T Content { get; set; } + } +} From 3bf52796d621cb7d76b3f37c6c3e225a8a386ab3 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 29 Jun 2022 15:54:58 +0200 Subject: [PATCH 445/458] Fixing messed up paramsets in `Get-PnPAccessToken` (#2053) * Fixing messed up paramsets * Moving order of examples Co-authored-by: = <=> --- documentation/Get-PnPAccessToken.md | 39 +++++++++++++++++++++-------- src/Commands/Base/GetAccessToken.cs | 17 ++++++------- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/documentation/Get-PnPAccessToken.md b/documentation/Get-PnPAccessToken.md index d8cccd59e..22ea4f6f8 100644 --- a/documentation/Get-PnPAccessToken.md +++ b/documentation/Get-PnPAccessToken.md @@ -16,7 +16,7 @@ If a Resource Type Name or Resource URL is specified, it will fetch the access t ## SYNTAX ```powershell -Get-PnPAccessToken [-ResourceTypeName] [-ResourceUrl] [-Decoded] [] +Get-PnPAccessToken [-ResourceTypeName] [-ResourceUrl] [-Decoded] [-Connection ] [] ``` ## DESCRIPTION @@ -33,19 +33,26 @@ Gets the OAuth 2.0 Access Token to consume the Microsoft Graph API ### EXAMPLE 2 ```powershell +Get-PnPAccessToken -Decoded +``` + +Gets the OAuth 2.0 Access Token to consume the Microsoft Graph API and shows the token with its content decoded + +### EXAMPLE 3 +```powershell Get-PnPAccessToken -ResourceTypeName SharePoint ``` Gets the OAuth 2.0 Access Token to consume the SharePoint APIs and perform CSOM operations. -### EXAMPLE 3 +### EXAMPLE 4 ```powershell Get-PnPAccessToken -ResourceTypeName ARM ``` Gets the OAuth 2.0 Access Token to consume the Azure Resource Manager APIs and perform related operations. In PnP, you can use them in cmdlets related to Flow and PowerPlatform etc. -### EXAMPLE 4 +### EXAMPLE 5 ```powershell Get-PnPAccessToken -ResourceUrl "https://management.azure.com/.default" ``` @@ -55,12 +62,11 @@ Gets the OAuth 2.0 Access Token to consume the SharePoint APIs and perform CSOM ## PARAMETERS ### -ResourceTypeName -Specify the Resource Type for which you want the access token. -If not specified, it will by default return Microsoft Graph access token. +Specify the Resource Type for which you want the access token. If not specified, it will by default return a Microsoft Graph access token. ```yaml Type: ResourceTypeName -Parameter Sets: Resource Type Name +Parameter Sets: Resource Type Name, Resource Type Name (decoded) Accepted values: Graph, SharePoint, ARM Required: False @@ -71,12 +77,11 @@ Accept wildcard characters: False ``` ### -ResourceUrl -Specify the Resource URL for which you want the access token. -If not specified, it will by default return Microsoft Graph access token. +Specify the Resource URL for which you want the access token, i.e. https://graph.microsoft.com/.default. If not specified, it will by default return a Microsoft Graph access token. ```yaml Type: String -Parameter Sets: Resource Url +Parameter Sets: Resource Url, Resource Url (decoded) Required: False Position: Named @@ -90,6 +95,20 @@ Returns the details from the access token in a decoded manner ```yaml Type: SwitchParameter +Parameter Sets: Default (decoded), Resource Type Name (decoded), Resource Url (decoded) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection Parameter Sets: (All) Required: False @@ -101,4 +120,4 @@ Accept wildcard characters: False ## RELATED LINKS -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/src/Commands/Base/GetAccessToken.cs b/src/Commands/Base/GetAccessToken.cs index 119e66f85..4130d1fd2 100644 --- a/src/Commands/Base/GetAccessToken.cs +++ b/src/Commands/Base/GetAccessToken.cs @@ -1,5 +1,4 @@ using System.Management.Automation; - using PnP.PowerShell.Commands.Attributes; using PnP.PowerShell.Commands.Enums; @@ -14,23 +13,21 @@ public class GetPnPAccessToken : PnPGraphCmdlet private const string DefaultParam = "Default"; private const string ResourceTypeParam = "Resource Type Name"; private const string ResourceUrlParam = "Resource Url"; - private const string DefaultParam_Decoded = "Default (decoded)"; private const string ResourceTypeParam_Decoded = "Resource Type Name (decoded)"; private const string ResourceUrlParam_Decoded = "Resource Url (decoded)"; - [Parameter(Mandatory = false, ParameterSetName = ResourceTypeParam)] - [Parameter(Mandatory = false, ParameterSetName = ResourceTypeParam_Decoded)] + [Parameter(Mandatory = true, ParameterSetName = ResourceTypeParam)] + [Parameter(Mandatory = true, ParameterSetName = ResourceTypeParam_Decoded)] public ResourceTypeName ResourceTypeName = ResourceTypeName.Graph; - [Parameter(Mandatory = false, ParameterSetName = ResourceUrlParam)] - [Parameter(Mandatory = false, ParameterSetName = ResourceUrlParam_Decoded)] + [Parameter(Mandatory = true, ParameterSetName = ResourceUrlParam)] + [Parameter(Mandatory = true, ParameterSetName = ResourceUrlParam_Decoded)] public string ResourceUrl; - [Parameter(ParameterSetName = DefaultParam_Decoded)] - [Parameter(ParameterSetName = ResourceTypeParam_Decoded)] - [Parameter(ParameterSetName = ResourceUrlParam_Decoded)] - [Parameter(Mandatory = false)] + [Parameter(Mandatory = true, ParameterSetName = DefaultParam_Decoded)] + [Parameter(Mandatory = true, ParameterSetName = ResourceTypeParam_Decoded)] + [Parameter(Mandatory = true, ParameterSetName = ResourceUrlParam_Decoded)] public SwitchParameter Decoded; protected override void ExecuteCmdlet() { From 9e6f99117d7ae23c4f8613247e47e214968120fd Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Wed, 29 Jun 2022 16:15:21 +0200 Subject: [PATCH 446/458] Changes to `Get-PnPSiteCollectionAppCatalogs` (#2044) * Fixes #2041 * Added additional CurrentSite parameter Co-authored-by: = <=> Co-authored-by: Gautam Sheth --- CHANGELOG.md | 4 + .../Get-PnPSiteCollectionAppCatalog.md | 104 ++++++++++++++++++ .../Get-PnPSiteCollectionAppCatalogs.md | 38 ------- .../Admin/GetSiteCollectionAppCatalog.cs | 90 +++++++++++++++ .../Admin/GetSiteCollectionAppCatalogs.cs | 19 ---- src/Commands/Admin/GetTenantSite.cs | 1 - .../SharePoint/SiteCollectionAppCatalog.cs | 26 +++++ 7 files changed, 224 insertions(+), 58 deletions(-) create mode 100644 documentation/Get-PnPSiteCollectionAppCatalog.md delete mode 100644 documentation/Get-PnPSiteCollectionAppCatalogs.md create mode 100644 src/Commands/Admin/GetSiteCollectionAppCatalog.cs delete mode 100644 src/Commands/Admin/GetSiteCollectionAppCatalogs.cs create mode 100644 src/Commands/Model/SharePoint/SiteCollectionAppCatalog.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c669be6f..5c6a8cbc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Get-Microsoft365GroupYammerCommunity` cmdlet to retrieve details on the Yammer Community connected to a Microsoft 365 Group [#2038](https://github.com/pnp/powershell/pull/2038) - Added `Get-Microsoft365GroupTeam` cmdlet to retrieve details on the Microsoft Teams team connected to a Microsoft 365 Group [#2038](https://github.com/pnp/powershell/pull/2038) - Added `Get-Microsoft365GroupEndpoints` cmdlet to retrieve details on all endpoints connected to a Microsoft 365 Group [#2038](https://github.com/pnp/powershell/pull/2038) +- Added `-ExcludeDeletedSites` optional parameter to `Get-PnPSiteCollectionAppCatalogs` which allows for site collections with App Catalogs that are in the recycle bin to be exluded from the results [#2044](https://github.com/pnp/powershell/pull/2044) +- Added `-CurrentSite` optional parameter to `Get-PnPSiteCollectionAppCatalogs` which allows for checking if the currently connected to site has a site collection App Catalogs provisioned on it [#2044](https://github.com/pnp/powershell/pull/2044) ### Changed @@ -80,6 +82,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Changed that almost every cmdlet now supports passing in a specific connection using `-Connection`. If omitted, the default connection will be used. [#1949](https://github.com/pnp/powershell/pull/1949), [#2011](https://github.com/pnp/powershell/pull/2011), [#1958](https://github.com/pnp/powershell/pull/1958) - Changed connecting with `Connect-PnPOnline -Credentials` now throwing a clear exception when making a typo in the hostname instead of getting stuck [#686](https://github.com/pnp/pnpframework/pull/686) - Changed `Get-PnPHubSiteChild` to have its `-Identity` parameter become optional. If not provided, the currently connected to site will be used. [#2033](https://github.com/pnp/powershell/pull/2033) +- Changed `Get-PnPSiteCollectionAppCatalogs` (plural) to `Get-PnPSiteCollectionAppCatalog` (singular) to follow the naming convention [#2044](https://github.com/pnp/powershell/pull/2044) ### Fixed @@ -112,6 +115,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Get-PnPHubSiteChild` throwing an exception when passing in a URL that is actually not a hub site [#2033](https://github.com/pnp/powershell/pull/2033) - Fixed `Add-PnPListItem` not showing field name when it has an improper value assigned to it [#2002](https://github.com/pnp/powershell/pull/2002) - Fixed connecting using `Connect-PnPOnline -Interactive -ClientId` not working well when already having an App-Only connection using the same ClientId [#2035](https://github.com/pnp/powershell/pull/2035) +- Fixed `Get-PnPSiteCollectionAppCatalog` not returning updated site collection URLs if they had been renamed [#2044](https://github.com/pnp/powershell/pull/2044) - Fixed cmdlets inheriting from PnPAdminCmdlet not working well on vanity domain SharePoint Online tenants [#2052](https://github.com/pnp/powershell/pull/2052) - Fixed `Copy-PnPList` throwing an unauthorized exception when using it with a non SharePoint Online administrator user [#2054](https://github.com/pnp/powershell/pull/2054) diff --git a/documentation/Get-PnPSiteCollectionAppCatalog.md b/documentation/Get-PnPSiteCollectionAppCatalog.md new file mode 100644 index 000000000..6e83220b6 --- /dev/null +++ b/documentation/Get-PnPSiteCollectionAppCatalog.md @@ -0,0 +1,104 @@ +--- +Module Name: PnP.PowerShell +title: Get-PnPSiteCollectionAppCatalog +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Get-PnPSiteCollectionAppCatalog.html +--- + +# Get-PnPSiteCollectionAppCatalog + +## SYNOPSIS +Returns all site collection scoped app catalogs that exist on the tenant + +## SYNTAX + +```powershell +Get-PnPSiteCollectionAppCatalog [-CurrentSite ] [-ExcludeDeletedSites ] [-Connection ] [-Verbose] [] +``` + +## DESCRIPTION +Returns all the site collection scoped app catalogs that exist on the tenant + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Get-PnPSiteCollectionAppCatalog +``` +Will return all the site collection app catalogs that exist on the tenant, including those that may be in the tenant recycle bin + +### EXAMPLE 2 +```powershell +Get-PnPSiteCollectionAppCatalog -CurrentSite +``` +Will return the site collection app catalog for the currently connected to site, if it has one. Otherwise it will yield no result. + +### EXAMPLE 2 +```powershell +Get-PnPSiteCollectionAppCatalog -ExcludeDeletedSites +``` +Will return all the site collection app catalogs that exist on the tenant excluding the site collections having App Catalogs that are in the tenant recycle bin + +## PARAMETERS + +### -CurrentSite +When provided, it will check if the currently connected to site has a site collection App Catalog and will return information on it. If the current site holds no site collection App Catalog, an empty response will be returned. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ExcludeDeletedSites +When provided, all site collections having site collection App Catalogs but residing in the tenant recycle bin, will be excluded + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Verbose +When provided, additional debug statements will be shown while executing the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/documentation/Get-PnPSiteCollectionAppCatalogs.md b/documentation/Get-PnPSiteCollectionAppCatalogs.md deleted file mode 100644 index 0b011f4af..000000000 --- a/documentation/Get-PnPSiteCollectionAppCatalogs.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -Module Name: PnP.PowerShell -title: Get-PnPSiteCollectionAppCatalogs -schema: 2.0.0 -applicable: SharePoint Online -external help file: PnP.PowerShell.dll-Help.xml -online version: https://pnp.github.io/powershell/cmdlets/Get-PnPSiteCollectionAppCatalogs.html ---- - -# Get-PnPSiteCollectionAppCatalogs - -## SYNOPSIS -Returns site collection scoped app catalogs - -## SYNTAX - -```powershell -Get-PnPSiteCollectionAppCatalogs - [] -``` - -## DESCRIPTION -Returns site collection scoped app catalogs - -## EXAMPLES - -### EXAMPLE 1 -```powershell -Get-PnPSiteCollectionAppCatalogs -``` -Will return the site collection app catalogs - -## PARAMETERS - -## RELATED LINKS - -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - diff --git a/src/Commands/Admin/GetSiteCollectionAppCatalog.cs b/src/Commands/Admin/GetSiteCollectionAppCatalog.cs new file mode 100644 index 000000000..ede1383d2 --- /dev/null +++ b/src/Commands/Admin/GetSiteCollectionAppCatalog.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation; +using Microsoft.SharePoint.Client; +using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Model.SharePoint; + +namespace PnP.PowerShell.Commands +{ + [Cmdlet(VerbsCommon.Get, "PnPSiteCollectionAppCatalog")] + [Alias("Get-PnPSiteCollectionAppCatalogs")] + [WriteAliasWarning("Please use 'Get-PnPSiteCollectionAppCatalog' (singular). The alias 'Get-PnPSiteCollectionAppCatalogs' (plural) will be removed in a future release.")] + [OutputType(typeof(IEnumerable))] + public class GetSiteCollectionAppCatalog : PnPAdminCmdlet + { + [Parameter(Mandatory = false)] + public SwitchParameter ExcludeDeletedSites; + + [Parameter(Mandatory = false)] + public SwitchParameter CurrentSite; + + protected override void ExecuteCmdlet() + { + WriteVerbose("Retrieving all site collection App Catalogs from SharePoint Online"); + + var appCatalogsCsom = ClientContext.Web.TenantAppCatalog.SiteCollectionAppCatalogsSites; + ClientContext.Load(appCatalogsCsom); + ClientContext.ExecuteQueryRetry(); + + var appCatalogsLocalModel = appCatalogsCsom.Select(ac => + new SiteCollectionAppCatalog { + AbsoluteUrl = ac.AbsoluteUrl, + ErrorMessage = ac.ErrorMessage, + SiteID = ac.SiteID + } + ).ToList(); + + WriteVerbose($"{appCatalogsLocalModel.Count} site collection App Catalog{(appCatalogsLocalModel.Count != 1 ? "s have" : " has")} been retrieved"); + + if(CurrentSite.ToBool()) + { + SiteContext.Site.EnsureProperties(s => s.Id); + + WriteVerbose($"Filtering down to only the current site at {Connection.Url} with ID {SiteContext.Site.Id}"); + var currentSite = appCatalogsLocalModel.FirstOrDefault(a => a.SiteID.HasValue && a.SiteID.Value == SiteContext.Site.Id); + + appCatalogsLocalModel.Clear(); + + if(currentSite == null) + { + WriteVerbose($"Current site at {Connection.Url} with ID {SiteContext.Site.Id} does not have a site collection App Catalog on it"); + return; + } + + appCatalogsLocalModel.Add(currentSite); + } + + var results = new List(appCatalogsLocalModel.Count); + foreach (var appCatalogLocalModel in appCatalogsLocalModel) + { + if (appCatalogLocalModel.SiteID.HasValue) + { + try + { + WriteVerbose($"Validating site collection App Catalog at {appCatalogLocalModel.AbsoluteUrl}"); + + appCatalogLocalModel.AbsoluteUrl = Tenant.GetSitePropertiesById(appCatalogLocalModel.SiteID.Value, false).Url; + results.Add(appCatalogLocalModel); + } + catch (Microsoft.SharePoint.Client.ServerException e) when (e.ServerErrorTypeName.Equals("Microsoft.Online.SharePoint.Common.SpoNoSiteException", StringComparison.InvariantCultureIgnoreCase)) + { + if(!ExcludeDeletedSites.ToBool()) + { + WriteVerbose($"Site collection App Catalog at {appCatalogLocalModel.AbsoluteUrl} regards a site that has been deleted"); + results.Add(appCatalogLocalModel); + } + else + { + WriteVerbose($"Site collection App Catalog at {appCatalogLocalModel.AbsoluteUrl} regards a site that has been deleted. Since the {nameof(ExcludeDeletedSites)} flag has been provided, it will not be included in the results."); + } + } + } + } + + WriteObject(results, true); + } + } +} \ No newline at end of file diff --git a/src/Commands/Admin/GetSiteCollectionAppCatalogs.cs b/src/Commands/Admin/GetSiteCollectionAppCatalogs.cs deleted file mode 100644 index 4bcf98b89..000000000 --- a/src/Commands/Admin/GetSiteCollectionAppCatalogs.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Management.Automation; -using Microsoft.SharePoint.Client; - - -namespace PnP.PowerShell.Commands -{ - [Cmdlet(VerbsCommon.Get, "PnPSiteCollectionAppCatalogs")] - public class GetSiteCollectionAppCatalogs : PnPWebCmdlet - { - protected override void ExecuteCmdlet() - { - var allowedSites = this.CurrentWeb.TenantAppCatalog.SiteCollectionAppCatalogsSites; - ClientContext.Load(allowedSites); - ClientContext.ExecuteQueryRetry(); - - WriteObject(allowedSites, true); - } - } -} \ No newline at end of file diff --git a/src/Commands/Admin/GetTenantSite.cs b/src/Commands/Admin/GetTenantSite.cs index 668cef586..56b6f8401 100644 --- a/src/Commands/Admin/GetTenantSite.cs +++ b/src/Commands/Admin/GetTenantSite.cs @@ -4,7 +4,6 @@ using Microsoft.Online.SharePoint.TenantAdministration; using Microsoft.SharePoint.Client; using PnP.PowerShell.Commands.Base; -using PnP.PowerShell.Commands.Enums; using System.Collections.Generic; using Microsoft.Online.SharePoint.TenantManagement; using PnP.PowerShell.Commands.Base.PipeBinds; diff --git a/src/Commands/Model/SharePoint/SiteCollectionAppCatalog.cs b/src/Commands/Model/SharePoint/SiteCollectionAppCatalog.cs new file mode 100644 index 000000000..2f572f01a --- /dev/null +++ b/src/Commands/Model/SharePoint/SiteCollectionAppCatalog.cs @@ -0,0 +1,26 @@ +using System; + +namespace PnP.PowerShell.Commands.Model.SharePoint +{ + /// + /// All properties regarding a site collection scoped App Catalog + /// + public class SiteCollectionAppCatalog + { + /// + /// The full Url to the location of the App Catalog in the tenant + /// + public string AbsoluteUrl { get; set; } + + /// + /// Informational message regarding the provisioning of the App Catalog + /// + public string ErrorMessage { get; set; } + + /// + /// Unique identifier of the site on which this App Catalog is located + /// + public Guid? SiteID { get; set; } + + } +} \ No newline at end of file From adffcf7edb7c4e62dfff3d36a37b599e25d139c2 Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Thu, 30 Jun 2022 03:53:43 +0000 Subject: [PATCH 447/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index 5027c4dde..73a48531c 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -c7d60731a6ba5e5ebda99d2da7ca3fee8a8e6683 \ No newline at end of file +08c93e315fadde8e8beeed1c0bbbfb34b9727f00 \ No newline at end of file diff --git a/version.txt b/version.txt index 073ff00b0..b99ce00ea 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.63 \ No newline at end of file +1.10.64 \ No newline at end of file From c72fb2afb6c3da3b65218921f209f6a88b7821a1 Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Thu, 30 Jun 2022 08:53:46 +0200 Subject: [PATCH 448/458] Set-PnPTenant parameter description (#2056) * Update Set-PnPTenant.md * Parameter descriptions * Update Set-PnPTenant.md --- documentation/Set-PnPTenant.md | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/documentation/Set-PnPTenant.md b/documentation/Set-PnPTenant.md index 24f5ea370..9fbeeccf5 100644 --- a/documentation/Set-PnPTenant.md +++ b/documentation/Set-PnPTenant.md @@ -107,6 +107,7 @@ Accept wildcard characters: False ``` ### -AllowEditing +Prevents users from editing Office files in the browser and copying and pasting Office file contents out of the browser window. ```yaml Type: Boolean @@ -120,6 +121,7 @@ Accept wildcard characters: False ``` ### -ApplyAppEnforcedRestrictionsToAdHocRecipients +When the feature is enabled, all guest users are subject to conditional access policy. By default guest users who are accessing SharePoint Online files with pass code are exempt from the conditional access policy. ```yaml Type: Boolean @@ -170,6 +172,7 @@ Accept wildcard characters: False ``` ### -CommentsOnSitePagesDisabled +Disables or enables the commenting functionality on all site pages in the tenant. ```yaml Type: Boolean @@ -183,6 +186,7 @@ Accept wildcard characters: False ``` ### -ConditionalAccessPolicy +Blocks or limits access to SharePoint and OneDrive content from unmanaged devices. ```yaml Type: SPOConditionalAccessPolicyType @@ -211,6 +215,7 @@ Accept wildcard characters: False ``` ### -DefaultLinkPermission +Sets the default permission of the link in the sharing dialog box in OneDrive for Business and SharePoint Online. This applies to anonymous access, internal and direct links. ```yaml Type: SharingPermissionType @@ -230,9 +235,8 @@ Lets administrators choose what type of link appears is selected in the "Get a l For additional information about how to change the default link type, see Change the default link type when users get links for sharing. Note: -Setting this value to "none" will default "get a link" to the most permissive link available (that is, if anonymous links are enabled, the default link will be anonymous access; if they are disabled then the default link will be internal. +Setting this value to "none" will default "get a link" to the most permissive link available. If anonymous links are enabled, the default link will be anonymous access; if they are disabled, then the default link will be internal. -The values are: None Direct Internal AnonymousAccess ```yaml Type: SharingLinkType @@ -332,6 +336,7 @@ Accept wildcard characters: False ``` ### -EmailAttestationReAuthDays +Sets the number of days for email attestation re-authentication. Value can be from 1 to 365 days. ```yaml Type: Int32 @@ -408,6 +413,7 @@ Accept wildcard characters: False ``` ### -FileAnonymousLinkType +Sets whether anonymous access links can allow recipients to only view or view and edit. The value can be set separately for folders and separately for files. ```yaml Type: AnonymousLinkType @@ -422,6 +428,7 @@ Accept wildcard characters: False ``` ### -FilePickerExternalImageSearchEnabled +Sets whether webparts that support inserting images, like for example Image or Hero webpart, the Web search (Powered by Bing) should allow choosing external images. The default is enabled. ```yaml Type: Boolean @@ -435,6 +442,7 @@ Accept wildcard characters: False ``` ### -FolderAnonymousLinkType +Sets whether anonymous access links can allow recipients to only view or view and edit. The value can be set separately for folders and separately for files. ```yaml Type: AnonymousLinkType @@ -449,7 +457,7 @@ Accept wildcard characters: False ``` ### -HideDefaultThemes -Defines if the default themes are visible or hidden +Defines if the default themes are visible or hidden. ```yaml Type: Boolean @@ -500,6 +508,7 @@ Accept wildcard characters: False ``` ### -IPAddressWACTokenLifetime +Allows to set the session timeout. If you are a tenant administrator and you begin IP address enforcement for OneDrive for Business in Office 365, this enforcement automatically activates a tenant parameter IPAddressWACTokenLifetime. The default value is 15 minutes, when IP Address Enforcement is True. ```yaml Type: Int32 @@ -582,6 +591,7 @@ Accept wildcard characters: False ``` ### -NotificationsInOneDriveForBusinessEnabled +Enables or disables notifications in OneDrive for Business. ```yaml Type: Boolean @@ -595,6 +605,7 @@ Accept wildcard characters: False ``` ### -NotificationsInSharePointEnabled +Enables or disables notifications in SharePoint. ```yaml Type: Boolean @@ -764,6 +775,7 @@ Accept wildcard characters: False ``` ### -OwnerAnonymousNotification +Specifies whether an email notification should be sent to the OneDrive for Business owners when an anonymous links are created or changed. ```yaml Type: Boolean @@ -777,6 +789,7 @@ Accept wildcard characters: False ``` ### -PreventExternalUsersFromResharing +Prevents external users from resharing files, folders, and sites that they do not own. ```yaml Type: Boolean @@ -1030,6 +1043,7 @@ Accept wildcard characters: False ### -ShowPeoplePickerSuggestionsForGuestUsers + ```yaml Type: Boolean Parameter Sets: (All) @@ -1069,6 +1083,7 @@ Accept wildcard characters: False ``` ### -SocialBarOnSitePagesDisabled +Disables or enables the Social Bar which appears on all modern SharePoint pages with the exception of the home page of a site. It gives users the ability to like a page, see the number of views, likes, and comments on a page, and see the people who have liked a page. ```yaml Type: Boolean @@ -1082,7 +1097,7 @@ Accept wildcard characters: False ``` ### -SpecialCharactersStateInFileFolderNames -{{ Fill SpecialCharactersStateInFileFolderNames Description }} +Permits the use of special characters in file and folder names in SharePoint Online and OneDrive for Business document libraries. The only two characters that can be managed at this time are the **#** and **%** characters. ```yaml Type: SpecialCharactersState @@ -1153,6 +1168,7 @@ Accept wildcard characters: False ``` ### -UserVoiceForFeedbackEnabled +Enables or disables the User Voice Feedback button shown at the bottom of all modern SharePoint Online pages. The "Feedback" link allows the end user to fill out a feedback form inside SharePoint Online which then creates an entry in the public SharePoint UserVoice topic. ```yaml Type: Boolean @@ -1208,7 +1224,7 @@ Accept wildcard characters: False ``` ### -InformationBarriersSuspension -Allows suspension of the information barriers future in a Microsoft 365 tenant. Setting this to $true will disable information barriers, setting this to $falsde will enable information barriers. For more information, see https://docs.microsoft.com/sharepoint/information-barriers. +Allows suspension of the information barriers future in a Microsoft 365 tenant. Setting this to $true will disable information barriers, setting this to $false will enable information barriers. For more information, see https://docs.microsoft.com/sharepoint/information-barriers. ```yaml Type: Boolean @@ -1306,7 +1322,7 @@ Accept wildcard characters: False ``` ### -EnableModernListTemplateIds -Guids of out of the box modern liststemplates to show when creating a new list +Guids of out of the box modern list templates to show when creating a new list ```yaml Type: Guid[] @@ -1335,4 +1351,4 @@ Accept wildcard characters: False ## RELATED LINKS -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) From b3838f55adfeec2f114e5cf3d88faafb7ea39502 Mon Sep 17 00:00:00 2001 From: = <=> Date: Thu, 30 Jun 2022 09:07:25 +0200 Subject: [PATCH 449/458] Removed mentioning limitation --- documentation/Copy-PnPFolder.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/Copy-PnPFolder.md b/documentation/Copy-PnPFolder.md index 0eb6f9c50..7655a0004 100644 --- a/documentation/Copy-PnPFolder.md +++ b/documentation/Copy-PnPFolder.md @@ -21,7 +21,7 @@ Copy-PnPFolder [-SourceUrl] [-TargetUrl] [-Overwrite] [-Force] ## DESCRIPTION -Copies a folder or file to a different location. This location can be within the same document library, same site, same site collection or even to another site collection on the same tenant. Currently there is a 200MB file size limit for the file or folder to be copied. Notice that if copying between sites or to a subsite you cannot specify a target filename, only a folder name. +Copies a folder or file to a different location. This location can be within the same document library, same site, same site collection or even to another site collection on the same tenant. Notice that if copying between sites or to a subsite you cannot specify a target filename, only a folder name. Copying files and folders is bound to some restrictions. You can find more on it here: https://docs.microsoft.com/office365/servicedescriptions/sharepoint-online-service-description/sharepoint-online-limits#moving-and-copying-across-sites From ea951bb5de47ab7700371b741f9efa4363c9133a Mon Sep 17 00:00:00 2001 From: Koen Zomers Date: Thu, 30 Jun 2022 12:19:45 +0200 Subject: [PATCH 450/458] Changes around Classification and Sensitivity Labels (#2036) * Renamed Get-PnPSensitivityLabel to Get-PnPAvailableSensitivityLabel to better suit its goal * Renaming file to match name change * Updating namespace to reflect actual location * Aligned Get-PnPSiteClassification to match Get-PnPAvailableSensitivityLabel * Work in progress * Work in progress * Work in progress * Work done * Added PR references * Setting and removing the label on the site as well for Microsoft 365 Groups so its directly visible * Adding changelog entry for Remove-PnPSiteSensitivityLabel Co-authored-by: = <=> --- CHANGELOG.md | 10 ++ ... => Add-PnPAvailableSiteClassification.md} | 20 ++-- ...md => Get-PnPAvailableSensitivityLabel.md} | 18 ++-- .../Get-PnPAvailableSiteClassification.md | 56 +++++++++++ documentation/Get-PnPSiteClassification.md | 42 -------- documentation/Get-PnPSiteSensitivityLabel.md | 52 ++++++++++ ... Remove-PnPAvailableSiteClassification.md} | 20 ++-- documentation/Set-PnPSiteClassification.md | 56 +++++++++++ documentation/Set-PnPSiteSensitivityLabel.md | 98 +++++++++++++++++++ ... Update-PnPAvailableSiteClassification.md} | 22 ++--- src/Commands/Base/PnPSharePointCmdlet.cs | 92 +++++++++-------- ...s => RemoveAvailableSiteClassification.cs} | 8 +- .../Model/SharePoint/SensitivityLabel.cs | 20 ++++ ...bel.cs => GetAvailableSensitivityLabel.cs} | 16 ++- .../Purview/GetSiteSensitivityLabel.cs | 22 +++++ .../Purview/RemoveSiteSensitivityLabel.cs | 45 +++++++++ src/Commands/Purview/SetSiteClassification.cs | 18 ++++ .../Purview/SetSiteSensitivityLabel.cs | 71 ++++++++++++++ ...n.cs => AddAvailableSiteClassification.cs} | 5 +- ...n.cs => GetAvailableSiteClassification.cs} | 9 +- ...s => UpdateAvailableSiteClassification.cs} | 19 ++-- src/Commands/Utilities/REST/GraphHelper.cs | 1 + src/Commands/Utilities/REST/RestHelper.cs | 1 - ...AddPnPAvailableSiteClassificationTests.cs} | 2 +- ...GetPnPAvailableSiteClassificationTests.cs} | 2 +- ...ovePnPAvailableSiteClassificationTests.cs} | 2 +- ...atePnPAvailableSiteClassificationTests.cs} | 2 +- 27 files changed, 574 insertions(+), 155 deletions(-) rename documentation/{Add-PnPSiteClassification.md => Add-PnPAvailableSiteClassification.md} (55%) rename documentation/{Get-PnPSensitivityLabel.md => Get-PnPAvailableSensitivityLabel.md} (70%) create mode 100644 documentation/Get-PnPAvailableSiteClassification.md delete mode 100644 documentation/Get-PnPSiteClassification.md create mode 100644 documentation/Get-PnPSiteSensitivityLabel.md rename documentation/{Remove-PnPSiteClassification.md => Remove-PnPAvailableSiteClassification.md} (57%) create mode 100644 documentation/Set-PnPSiteClassification.md create mode 100644 documentation/Set-PnPSiteSensitivityLabel.md rename documentation/{Update-PnPSiteClassification.md => Update-PnPAvailableSiteClassification.md} (71%) rename src/Commands/Graph/{RemoveSiteClassification.cs => RemoveAvailableSiteClassification.cs} (89%) create mode 100644 src/Commands/Model/SharePoint/SensitivityLabel.cs rename src/Commands/Purview/{GetSensitivityLabel.cs => GetAvailableSensitivityLabel.cs} (76%) create mode 100644 src/Commands/Purview/GetSiteSensitivityLabel.cs create mode 100644 src/Commands/Purview/RemoveSiteSensitivityLabel.cs create mode 100644 src/Commands/Purview/SetSiteClassification.cs create mode 100644 src/Commands/Purview/SetSiteSensitivityLabel.cs rename src/Commands/Site/{AddSiteClassification.cs => AddAvailableSiteClassification.cs} (87%) rename src/Commands/Site/{GetSiteClassification.cs => GetAvailableSiteClassification.cs} (71%) rename src/Commands/Site/{UpdateSiteClassification.cs => UpdateAvailableSiteClassification.cs} (82%) rename src/Tests/Graph/{AddPnPSiteClassificationTests.cs => AddPnPAvailableSiteClassificationTests.cs} (97%) rename src/Tests/Graph/{GetPnPSiteClassificationTests.cs => GetPnPAvailableSiteClassificationTests.cs} (97%) rename src/Tests/Graph/{RemovePnPSiteClassificationTests.cs => RemovePnPAvailableSiteClassificationTests.cs} (97%) rename src/Tests/Graph/{UpdatePnPSiteClassificationTests.cs => UpdatePnPAvailableSiteClassificationTests.cs} (97%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c6a8cbc7..268de9c84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `-UpdateChildren` parameter to `Add-PnPFieldToContentType` cmdlet. This allows users to skip pushing the fields to child content types. [#1992](https://github.com/pnp/powershell/pull/1992) - Added optional `-SensitivityLabel` to `Set-PnPSite` which allows for a Microsoft Purview sensitivitylabel to be set [#2024](https://github.com/pnp/powershell/pull/2024) - Added `-UpdateChildren` parameter to `Add-PnPFieldToContentType` cmdlet. This allows users to skip pushing the fields to child content types. [#1092](https://github.com/pnp/powershell/pull/1992) +- Added `Get-PnPAvailableSensitivityLabel` cmdlet to retrieve Microsoft Purview sensitivity labels available on the tenant [#2023](https://github.com/pnp/powershell/pull/2023) +- Added `Get-PnPSiteSensitivityLabel` cmdlet to retrieve the Microsoft Purview sensitivity label set on the current site [#2036](https://github.com/pnp/powershell/pull/2036) +- Added `Set-PnPSiteClassification` cmdlet which allows setting a classic site classification on the current site [#2036](https://github.com/pnp/powershell/pull/2036) +- Added `Set-PnPSiteSensitivityLabel` cmdlet which allows setting a Microsoft Purview sensitivity label on the current site [#2036](https://github.com/pnp/powershell/pull/2036) +- Added `Remove-PnPSiteSensitivityLabel` cmdlet which allows removing the Microsoft Purview sensitivity label from the current site [#2036](https://github.com/pnp/powershell/pull/2036) - Added `Get-PnPSensitivityLabel` cmdlet to retrieve Microsoft Purview sensitivity labels available on the tenant [#2023](https://github.com/pnp/powershell/pull/2023) - Added `Get-Microsoft365GroupYammerCommunity` cmdlet to retrieve details on the Yammer Community connected to a Microsoft 365 Group [#2038](https://github.com/pnp/powershell/pull/2038) - Added `Get-Microsoft365GroupTeam` cmdlet to retrieve details on the Microsoft Teams team connected to a Microsoft 365 Group [#2038](https://github.com/pnp/powershell/pull/2038) @@ -81,6 +86,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Service Health cmdlets have been improved and are now consistent with other cmdlets to handle pagination [#1938](https://github.com/pnp/powershell/pull/1938) - Changed that almost every cmdlet now supports passing in a specific connection using `-Connection`. If omitted, the default connection will be used. [#1949](https://github.com/pnp/powershell/pull/1949), [#2011](https://github.com/pnp/powershell/pull/2011), [#1958](https://github.com/pnp/powershell/pull/1958) - Changed connecting with `Connect-PnPOnline -Credentials` now throwing a clear exception when making a typo in the hostname instead of getting stuck [#686](https://github.com/pnp/pnpframework/pull/686) +- Renamed `Get-PnPSiteClassification` to `Get-PnPAvailableSiteClassification` to fall in line with `Get-PnPAvailableSensitivityLabel`. Old name will stay as an alias for backwards compatibility for now, but will be removed in a future version. [#2036](https://github.com/pnp/powershell/pull/2036) +- Renamed `Add-PnPSiteClassification` to `Add-PnPAvailableSiteClassification` to fall in line with `Get-PnPAvailableSensitivityLabel`. Old name will stay as an alias for backwards compatibility for now, but will be removed in a future version. [#2036](https://github.com/pnp/powershell/pull/2036) +- Renamed `Update-PnPSiteClassification` to `Update-PnPAvailableSiteClassification` to fall in line with `Get-PnPAvailableSensitivityLabel`. Old name will stay as an alias for backwards compatibility for now, but will be removed in a future version. [#2036](https://github.com/pnp/powershell/pull/2036) +- Renamed `Remove-PnPSiteClassification` to `Remove-PnPAvailableSiteClassification` to fall in line with `Get-PnPAvailableSensitivityLabel`. Old name will stay as an alias for backwards compatibility for now, but will be removed in a future version. [#2036](https://github.com/pnp/powershell/pull/2036) - Changed `Get-PnPHubSiteChild` to have its `-Identity` parameter become optional. If not provided, the currently connected to site will be used. [#2033](https://github.com/pnp/powershell/pull/2033) - Changed `Get-PnPSiteCollectionAppCatalogs` (plural) to `Get-PnPSiteCollectionAppCatalog` (singular) to follow the naming convention [#2044](https://github.com/pnp/powershell/pull/2044) @@ -111,6 +120,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Fixed `Register-PnPAzureADApp` issue with app creation after the connection related changes. [#1993](https://github.com/pnp/powershell/pull/1993) - Fixed `Get-PnPFileVersion` not able to correctly use piping on the returned object. [#1997](https://github.com/pnp/powershell/pull/1997) - Fixed `Add-PnPListItem` not showing field name when it has an improper value assigned to it [#2002](https://github.com/pnp/powershell/pull/202) +- Fixed `Update-PnPSiteClassification` not allowing the `-UsageGuidelinesUrl` to be set without also setting `-DefaultClassification` [#2036](https://github.com/pnp/powershell/pull/2036) - Fixed the browser consent dialog throwing an exception when trying to close it [#2037](https://github.com/pnp/powershell/pull/2037) - Fixed `Get-PnPHubSiteChild` throwing an exception when passing in a URL that is actually not a hub site [#2033](https://github.com/pnp/powershell/pull/2033) - Fixed `Add-PnPListItem` not showing field name when it has an improper value assigned to it [#2002](https://github.com/pnp/powershell/pull/2002) diff --git a/documentation/Add-PnPSiteClassification.md b/documentation/Add-PnPAvailableSiteClassification.md similarity index 55% rename from documentation/Add-PnPSiteClassification.md rename to documentation/Add-PnPAvailableSiteClassification.md index 959e30bc5..68dc4869a 100644 --- a/documentation/Add-PnPSiteClassification.md +++ b/documentation/Add-PnPAvailableSiteClassification.md @@ -2,12 +2,12 @@ Module Name: PnP.PowerShell schema: 2.0.0 applicable: SharePoint Online -online version: https://pnp.github.io/powershell/cmdlets/Add-PnPSiteClassification.html +online version: https://pnp.github.io/powershell/cmdlets/Add-PnPAvailableSiteClassification.html external help file: PnP.PowerShell.dll-Help.xml -title: Add-PnPSiteClassification +title: Add-PnPAvailableSiteClassification --- -# Add-PnPSiteClassification +# Add-PnPAvailableSiteClassification ## SYNOPSIS @@ -15,12 +15,12 @@ title: Add-PnPSiteClassification * Microsoft Graph API: Directory.ReadWrite.All -Adds one or more site classification values to the list of possible values +Adds one or more classic site classification values to the list of possible values ## SYNTAX ```powershell -Add-PnPSiteClassification -Classifications [] +Add-PnPAvailableSiteClassification -Classifications [] ``` ## DESCRIPTION @@ -29,17 +29,17 @@ Add-PnPSiteClassification -Classifications ] [-User ] [-Connection ] [] +Get-PnPAvailableSensitivityLabel [-Identity ] [-User ] [-Connection ] [] ``` ## DESCRIPTION -This cmdlet allows retrieval of the available Microsoft Purview sensitivity labels in the currently connected tenant. You can retrieve all the labels, a specific label or all the labels available to a specific user. +This cmdlet allows retrieval of the available Microsoft Purview sensitivity labels in the currently connected tenant. You can retrieve all the labels, a specific label or all the labels available to a specific user. When connected with a delegate token, it will return the Microsoft Purview sensitivity labels for the user you logged on with. When connecting with an application token, it will return all available Microsoft Purview sensitivity labels on the tenant. + +For retrieval of the available classic Site Classification, use [Get-PnPAvailableSiteClassification](Get-PnPAvailableSiteClassification.html) instead. ## EXAMPLES ### EXAMPLE 1 ```powershell -Get-PnPSensitivityLabel +Get-PnPAvailableSensitivityLabel ``` Returns all the Microsoft Purview sensitivitiy labels that exist on the tenant ### EXAMPLE 2 ```powershell -Get-PnPSensitivityLabel -User johndoe@tenant.onmicrosoft.com +Get-PnPAvailableSensitivityLabel -User johndoe@tenant.onmicrosoft.com ``` Returns all Microsoft Purview sensitivitiy labels which are available to the provided user ### EXAMPLE 3 ```powershell -Get-PnPSensitivityLabel -Identity 47e66706-8627-4979-89f1-fa7afeba2884 +Get-PnPAvailableSensitivityLabel -Identity 47e66706-8627-4979-89f1-fa7afeba2884 ``` Returns a specific Microsoft Purview sensitivitiy label by its id diff --git a/documentation/Get-PnPAvailableSiteClassification.md b/documentation/Get-PnPAvailableSiteClassification.md new file mode 100644 index 000000000..de8495aa5 --- /dev/null +++ b/documentation/Get-PnPAvailableSiteClassification.md @@ -0,0 +1,56 @@ +--- +Module Name: PnP.PowerShell +title: Get-PnPAvailableSiteClassification +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Get-PnPAvailableSiteClassification.html +--- + +# Get-PnPAvailableSiteClassification + +## SYNOPSIS + +**Required Permissions** + + * Microsoft Graph API : One of Directory.Read.All, Directory.ReadWrite.All + +Returns the available classic Site Classifications for the tenant + +## SYNTAX + +```powershell +Get-PnPAvailableSiteClassification [-Connection ] [] +``` + +## DESCRIPTION +This cmdlet allows for retrieving the configuration of the classic site classifications configured within the tenant. For the new Microsoft Purview sensitivity labels, use [Get-PnPAvailableSensitivityLabel](Get-PnPAvailableSensitivityLabel.html) instead. + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Get-PnPAvailableSiteClassification +``` + +Returns the currently set site classifications for the tenant. + +## PARAMETERS + +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/documentation/Get-PnPSiteClassification.md b/documentation/Get-PnPSiteClassification.md deleted file mode 100644 index e088192dd..000000000 --- a/documentation/Get-PnPSiteClassification.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -Module Name: PnP.PowerShell -title: Get-PnPSiteClassification -schema: 2.0.0 -applicable: SharePoint Online -external help file: PnP.PowerShell.dll-Help.xml -online version: https://pnp.github.io/powershell/cmdlets/Get-PnPSiteClassification.html ---- - -# Get-PnPSiteClassification - -## SYNOPSIS - -**Required Permissions** - - * Microsoft Graph API : One of Directory.Read.All, Directory.ReadWrite.All - -Returns the defined Site Classifications for the tenant - -## SYNTAX - -```powershell -Get-PnPSiteClassification [] -``` - -## DESCRIPTION - -## EXAMPLES - -### EXAMPLE 1 -```powershell -Get-PnPSiteClassification -``` - -Returns the currently set site classifications for the tenant. - -## PARAMETERS - -## RELATED LINKS - -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - diff --git a/documentation/Get-PnPSiteSensitivityLabel.md b/documentation/Get-PnPSiteSensitivityLabel.md new file mode 100644 index 000000000..649e3ed18 --- /dev/null +++ b/documentation/Get-PnPSiteSensitivityLabel.md @@ -0,0 +1,52 @@ +--- +Module Name: PnP.PowerShell +schema: 2.0.0 +applicable: SharePoint Online +online version: https://pnp.github.io/powershell/cmdlets/Get-PnPSiteSensitivityLabel.html +external help file: PnP.PowerShell.dll-Help.xml +title: Get-PnPSiteSensitivityLabel +--- + +# Get-PnPSiteSensitivityLabel + +## SYNOPSIS +Gets the Microsoft Purview sensitivity label that is set on the connected to site + +## SYNTAX + +```powershell +Get-PnPSiteSensitivityLabel [-Connection ] [] +``` + +## DESCRIPTION +This cmdlet allows retrieval of the currently assigned Microsoft Purview sensitivity label to the currently connectected to site. + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Get-PnPSiteSensitivityLabel +``` + +Returns the Microsoft Purview sensitivitiy label set on the currently connected to site + +## PARAMETERS + +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) +[Microsoft Docs documentation](https://docs.microsoft.com/sharepoint/dev/solution-guidance/modern-experience-site-classification#programmatically-read-the-classification-of-a-site) \ No newline at end of file diff --git a/documentation/Remove-PnPSiteClassification.md b/documentation/Remove-PnPAvailableSiteClassification.md similarity index 57% rename from documentation/Remove-PnPSiteClassification.md rename to documentation/Remove-PnPAvailableSiteClassification.md index de6693004..3315dbcf8 100644 --- a/documentation/Remove-PnPSiteClassification.md +++ b/documentation/Remove-PnPAvailableSiteClassification.md @@ -1,13 +1,13 @@ --- Module Name: PnP.PowerShell -title: Remove-PnPSiteClassification +title: Remove-PnPAvailableSiteClassification schema: 2.0.0 applicable: SharePoint Online external help file: PnP.PowerShell.dll-Help.xml -online version: https://pnp.github.io/powershell/cmdlets/Remove-PnPSiteClassification.html +online version: https://pnp.github.io/powershell/cmdlets/Remove-PnPAvailableSiteClassification.html --- -# Remove-PnPSiteClassification +# Remove-PnPAvailableSiteClassification ## SYNOPSIS @@ -15,13 +15,12 @@ online version: https://pnp.github.io/powershell/cmdlets/Remove-PnPSiteClassific * Microsoft Graph API: Directory.ReadWrite.All -Removes one or more existing site classification values from the list of available values +Removes one or more existing classic site classification values from the list of available values on the tenant ## SYNTAX ```powershell -Remove-PnPSiteClassification -Classifications - [] +Remove-PnPAvailableSiteClassification -Classifications [] ``` ## DESCRIPTION @@ -30,14 +29,14 @@ Remove-PnPSiteClassification -Classifications [-Connection ] [] +``` + +## DESCRIPTION +This cmdlet allows for setting a classic site classification on the currently connected to site. If the site has a Microsoft 365 Group behind it, the classification will be placed on the Microsoft 365 Group and will require either Directory.Read.All or Directory.ReadWrite.All application permissions on Microsoft Graph. If it does not have a Microsoft 365 Group behind it, it will set the site classification on the SharePoint Online site and will not require Microsoft Graph permissions. Use [Get-PnPAvailableSiteClassification](Get-PnPAvailableSiteClassification.html) to get an overview of the available site classifications on the tenant. For the new Microsoft Purview sensitivity labels, use [Set-PnPSiteSensitivityLabel](Set-PnPSiteSensitivityLabel.html) instead. + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Set-PnPSiteClassification -Identity "LBI" +``` + +Sets the "LBI" site classification on the current site + +## PARAMETERS + +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/documentation/Set-PnPSiteSensitivityLabel.md b/documentation/Set-PnPSiteSensitivityLabel.md new file mode 100644 index 000000000..ab61cf817 --- /dev/null +++ b/documentation/Set-PnPSiteSensitivityLabel.md @@ -0,0 +1,98 @@ +--- +Module Name: PnP.PowerShell +title: Set-PnPSiteSensitivityLabel +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Set-PnPSiteSensitivityLabel.html +--- + +# Set-PnPSiteSensitivityLabel + +## SYNOPSIS + +**Required Permissions** + + * Microsoft Graph API : Delegate token of Group.ReadWrite.All, Directory.ReadWrite.All (see description below) + +Allows placing a Microsoft Purview sensitivity label on the current site + +## SYNTAX + +```powershell +Set-PnPSiteSensitivityLabel -Identity [-Connection ] [-Verbose] [] +``` + +## DESCRIPTION +This cmdlet allows for setting a Microsoft Purview sensitivity label on the currently connected to site. If the site has a Microsoft 365 Group behind it, the label will be placed on the Microsoft 365 Group and will require either Group.ReadWrite.All or Directory.ReadWrite.All delegate permissions on Microsoft Graph. This currently cannot be done using App Only permissions due to a limitation in Microsoft Graph. If it does not have a Microsoft 365 Group behind it, it will set the label on the SharePoint Online site and will not require Microsoft Graph permissions and will work with both delegate as well as app only logins. + +It may take up to a few minutes for a change to the sensitity label to become visible in SharePoint Online and Azure Active Directory. + +Use [Get-PnPAvailableSensitivityLabel](Get-PnPAvailableSensitivityLabel.html) to get an overview of the available Microsoft Purview sensitivity labels on the tenant. + +For the classic classification labels, use [Set-PnPSiteClassification](Set-PnPSiteClassification.html) instead. + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Set-PnPSiteSensitivityLabel -Identity "Top Secret" +``` + +Sets the Microsoft Purview sensitivity label with the name "Top Secret" on the current site + +### EXAMPLE 2 +```powershell +Set-PnPSiteSensitivityLabel -Identity a1888df2-84c2-4379-8d53-7091dd630ca7 +``` + +Sets the Microsoft Purview sensitivity label with the Id a1888df2-84c2-4379-8d53-7091dd630ca7 on the current site + +## PARAMETERS + +### -Identity +Id or name of the Microsoft Purview sensitivity label to apply + +```yaml +Type: String +Parameter Sets: (All) + +Required: True +Position: Named +Default value: True +Accept pipeline input: True +Accept wildcard characters: False +``` + +### -Connection +Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Verbose +When provided, additional debug statements will be shown while going through the execution of this cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) +[Microsoft Graph documentation](https://docs.microsoft.com/graph/api/group-update?view=graph-rest-beta&tabs=http#example-2-apply-sensitivity-label-to-a-microsoft-365-group) \ No newline at end of file diff --git a/documentation/Update-PnPSiteClassification.md b/documentation/Update-PnPAvailableSiteClassification.md similarity index 71% rename from documentation/Update-PnPSiteClassification.md rename to documentation/Update-PnPAvailableSiteClassification.md index e2417dbfe..9b3bf0855 100644 --- a/documentation/Update-PnPSiteClassification.md +++ b/documentation/Update-PnPAvailableSiteClassification.md @@ -1,13 +1,13 @@ --- Module Name: PnP.PowerShell -title: Update-PnPSiteClassification +title: Update-PnPAvailableSiteClassification schema: 2.0.0 applicable: SharePoint Online external help file: PnP.PowerShell.dll-Help.xml -online version: https://pnp.github.io/powershell/cmdlets/Update-PnPSiteClassification.html +online version: https://pnp.github.io/powershell/cmdlets/Update-PnPAvailableSiteClassification.html --- -# Update-PnPSiteClassification +# Update-PnPAvailableSiteClassification ## SYNOPSIS @@ -15,43 +15,44 @@ online version: https://pnp.github.io/powershell/cmdlets/Update-PnPSiteClassific * Microsoft Graph API: Directory.ReadWrite.All -Updates Site Classifications for the tenant +Updates available classic Site Classifications for the tenant ## SYNTAX ### Settings ```powershell -Update-PnPSiteClassification -Settings +Update-PnPAvailableSiteClassification -Settings [] ``` ### Specific ```powershell -Update-PnPSiteClassification [-Classifications ] +Update-PnPAvailableSiteClassification [-Classifications ] [-DefaultClassification ] [-UsageGuidelinesUrl ] [] ``` ## DESCRIPTION +This cmdlet allows for updating the configuration of the classic site classifications configured within the tenant. ## EXAMPLES ### EXAMPLE 1 ```powershell -Update-PnPSiteClassification -Classifications "HBI","Top Secret" +Update-PnPAvailableSiteClassification -Classifications "HBI","Top Secret" ``` Replaces the existing values of the site classification settings ### EXAMPLE 2 ```powershell -Update-PnPSiteClassification -DefaultClassification "LBI" +Update-PnPAvailableSiteClassification -DefaultClassification "LBI" ``` Sets the default classification value to "LBI". This value needs to be present in the list of classification values. ### EXAMPLE 3 ```powershell -Update-PnPSiteClassification -UsageGuidelinesUrl https://aka.ms/m365pnp +Update-PnPAvailableSiteClassification -UsageGuidelinesUrl https://aka.ms/m365pnp ``` sets the usage guideliness URL to the specified URL @@ -116,5 +117,4 @@ Accept wildcard characters: False ## RELATED LINKS -[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/src/Commands/Base/PnPSharePointCmdlet.cs b/src/Commands/Base/PnPSharePointCmdlet.cs index 6f7050bd6..b29b4130b 100644 --- a/src/Commands/Base/PnPSharePointCmdlet.cs +++ b/src/Commands/Base/PnPSharePointCmdlet.cs @@ -32,6 +32,55 @@ public abstract class PnPSharePointCmdlet : PnPConnectedCmdlet /// public HttpClient HttpClient => PnP.Framework.Http.PnPHttpClient.Instance.GetHttpClient(ClientContext); + /// + /// The current Bearer access token for SharePoitn Online + /// + protected string AccessToken + { + get + { + if (Connection != null) + { + if (Connection.Context != null) + { + var settings = Microsoft.SharePoint.Client.InternalClientContextExtensions.GetContextSettings(Connection.Context); + if (settings != null) + { + var authManager = settings.AuthenticationManager; + if (authManager != null) + { + return authManager.GetAccessTokenAsync(Connection.Context.Url).GetAwaiter().GetResult(); + } + } + } + } + return null; + } + } + + /// + /// The current Bearer access token for Microsoft Graph + /// + public string GraphAccessToken + { + get + { + if (Connection?.ConnectionMethod == ConnectionMethod.ManagedIdentity) + { + return TokenHandler.GetManagedIdentityTokenAsync(this, HttpClient, $"https://{Connection.GraphEndPoint}/").GetAwaiter().GetResult(); + } + else + { + if (Connection?.Context != null) + { + return TokenHandler.GetAccessToken(GetType(), $"https://{Connection.GraphEndPoint}/.default", Connection); + } + } + + return null; + } + } + protected override void BeginProcessing() { // Call the base but instruct it not to check if there's an active connection as we will do that in this method already @@ -98,49 +147,6 @@ protected override void EndProcessing() base.EndProcessing(); } - protected string AccessToken - { - get - { - if (Connection != null) - { - if (Connection.Context != null) - { - var settings = Microsoft.SharePoint.Client.InternalClientContextExtensions.GetContextSettings(Connection.Context); - if (settings != null) - { - var authManager = settings.AuthenticationManager; - if (authManager != null) - { - return authManager.GetAccessTokenAsync(Connection.Context.Url).GetAwaiter().GetResult(); - } - } - } - } - return null; - } - } - - public string GraphAccessToken - { - get - { - if (Connection?.ConnectionMethod == ConnectionMethod.ManagedIdentity) - { - return TokenHandler.GetManagedIdentityTokenAsync(this, HttpClient, $"https://{Connection.GraphEndPoint}/").GetAwaiter().GetResult(); - } - else - { - if (Connection?.Context != null) - { - return TokenHandler.GetAccessToken(GetType(), $"https://{Connection.GraphEndPoint}/.default", Connection); - } - } - - return null; - } - } - protected void PollOperation(SpoOperation spoOperation) { while (true) diff --git a/src/Commands/Graph/RemoveSiteClassification.cs b/src/Commands/Graph/RemoveAvailableSiteClassification.cs similarity index 89% rename from src/Commands/Graph/RemoveSiteClassification.cs rename to src/Commands/Graph/RemoveAvailableSiteClassification.cs index cb4a25965..7916d9601 100644 --- a/src/Commands/Graph/RemoveSiteClassification.cs +++ b/src/Commands/Graph/RemoveAvailableSiteClassification.cs @@ -1,5 +1,4 @@ - -using PnP.PowerShell.Commands.Attributes; +using PnP.PowerShell.Commands.Attributes; using PnP.PowerShell.Commands.Base; using System; using System.Collections.Generic; @@ -8,12 +7,13 @@ namespace PnP.PowerShell.Commands.Graph { - [Cmdlet(VerbsCommon.Remove, "PnPSiteClassification")] + [Cmdlet(VerbsCommon.Remove, "PnPAvailableSiteClassification")] [RequiredMinimalApiPermissions("Directory.ReadWrite.All")] + [Alias("Remove-PnPSiteClassitication")] + [WriteAliasWarning("Please use 'Remove-PnPAvailableSiteClassification'. The alias 'Remove-PnPSiteClassification' will be removed in a future release.")] [OutputType(typeof(void))] public class RemoveSiteClassification : PnPGraphCmdlet { - [Parameter(Mandatory = true)] public List Classifications; diff --git a/src/Commands/Model/SharePoint/SensitivityLabel.cs b/src/Commands/Model/SharePoint/SensitivityLabel.cs new file mode 100644 index 000000000..d73278e6a --- /dev/null +++ b/src/Commands/Model/SharePoint/SensitivityLabel.cs @@ -0,0 +1,20 @@ +using System; + +namespace PnP.PowerShell.Commands.Model.SharePoint +{ + /// + /// Describes the information returned in a sensitivitylabel of a site + /// + public class SensitivityLabel + { + /// + /// Unique identifier of the sensitivity label + /// + public Guid? Id { get; set; } + + /// + /// The name of the sensitivity label + /// + public string DisplayName { get; set; } + } +} \ No newline at end of file diff --git a/src/Commands/Purview/GetSensitivityLabel.cs b/src/Commands/Purview/GetAvailableSensitivityLabel.cs similarity index 76% rename from src/Commands/Purview/GetSensitivityLabel.cs rename to src/Commands/Purview/GetAvailableSensitivityLabel.cs index 1558b3551..ac1d73a0a 100644 --- a/src/Commands/Purview/GetSensitivityLabel.cs +++ b/src/Commands/Purview/GetAvailableSensitivityLabel.cs @@ -6,13 +6,12 @@ using System.Collections.Generic; using System.Management.Automation; -namespace PnP.PowerShell.Commands.PowerPlatform.PowerAutomate +namespace PnP.PowerShell.Commands.Purview { - [Cmdlet(VerbsCommon.Get, "PnPSensitivityLabel")] - [RequiredMinimalApiPermissions("InformationProtectionPolicy.Read.All")] + [Cmdlet(VerbsCommon.Get, "PnPAvailableSensitivityLabel")] [OutputType(typeof(IEnumerable))] [OutputType(typeof(Model.Graph.Purview.InformationProtectionLabel))] - public class GetSensitivityLabel : PnPGraphCmdlet + public class GetAvailableSensitivityLabel : PnPGraphCmdlet { [Parameter(Mandatory = false)] public AzureADUserPipeBind User; @@ -37,7 +36,14 @@ protected override void ExecuteCmdlet() } else { - url = "/beta/informationProtection/policy/labels"; + if(Connection.ConnectionMethod == Model.ConnectionMethod.AzureADAppOnly) + { + url = "/beta/informationProtection/policy/labels"; + } + else + { + url = "/beta/me/informationProtection/policy/labels"; + } } if (ParameterSpecified(nameof(Identity))) diff --git a/src/Commands/Purview/GetSiteSensitivityLabel.cs b/src/Commands/Purview/GetSiteSensitivityLabel.cs new file mode 100644 index 000000000..bb6494689 --- /dev/null +++ b/src/Commands/Purview/GetSiteSensitivityLabel.cs @@ -0,0 +1,22 @@ +using Microsoft.SharePoint.Client; +using System; +using System.Management.Automation; + +namespace PnP.PowerShell.Commands.Purview +{ + [Cmdlet(VerbsCommon.Get, "PnPSiteSensitivityLabel")] + [OutputType(typeof(PnP.PowerShell.Commands.Model.SharePoint.SensitivityLabel))] + public class GetSiteSensitivityLabel : PnPSharePointCmdlet + { + protected override void ExecuteCmdlet() + { + ClientContext.Load(ClientContext.Site, s => s.SensitivityLabelInfo); + ClientContext.ExecuteQueryRetry(); + + WriteObject(new PnP.PowerShell.Commands.Model.SharePoint.SensitivityLabel { + Id = Guid.TryParse(ClientContext.Site.SensitivityLabelInfo.Id, out Guid labelGuid) ? (Guid?) labelGuid : null, + DisplayName = ClientContext.Site.SensitivityLabelInfo.DisplayName + }, false); + } + } +} \ No newline at end of file diff --git a/src/Commands/Purview/RemoveSiteSensitivityLabel.cs b/src/Commands/Purview/RemoveSiteSensitivityLabel.cs new file mode 100644 index 000000000..964c684c8 --- /dev/null +++ b/src/Commands/Purview/RemoveSiteSensitivityLabel.cs @@ -0,0 +1,45 @@ +using Microsoft.SharePoint.Client; +using PnP.PowerShell.Commands.Utilities.REST; +using System; +using System.Management.Automation; +using System.Net.Http; +using System.Text.Json; + +namespace PnP.PowerShell.Commands.Purview +{ + [Cmdlet(VerbsCommon.Remove, "PnPSiteSensitivityLabel")] + [OutputType(typeof(void))] + public class RemoveSiteSensitivityLabel : PnPSharePointCmdlet + { + protected override void ExecuteCmdlet() + { + WriteVerbose($"Verifying if the current site at {Connection.Url} is backed by a Microsoft 365 Group"); + ClientContext.Load(ClientContext.Site, s => s.GroupId); + ClientContext.ExecuteQueryRetry(); + + if(ClientContext.Site.GroupId != Guid.Empty) + { + // Site is Microsoft 365 Group backed + WriteVerbose($"Current site at {Connection.Url} is backed by Microsoft 365 Group with Id {ClientContext.Site.GroupId}, going to try removing the label from the group"); + if(Connection.ConnectionMethod == Model.ConnectionMethod.AzureADAppOnly) + { + WriteWarning("Current connection is not using a delegate token and is backed by a Microsoft 365 Group, removing the Microsoft Purview sensitivity label will likely not work. Check the documentation."); + } + + WriteVerbose($"Trying to remove the Microsoft Purview sensitivity label from the Microsoft 365 Group with Id {ClientContext.Site.GroupId} behind the current site {Connection.Url}"); + var stringContent = new StringContent(JsonSerializer.Serialize(new { assignedLabels = new [] { new { labelId = "" }}})); + stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); + GraphHelper.PatchAsync(Connection, GraphAccessToken, stringContent, $"beta/groups/{ClientContext.Site.GroupId}").GetAwaiter().GetResult();; + } + else + { + // Site does not have a Microsoft 365 Group behind it + WriteVerbose($"Current site at {Connection.Url} is not backed by a Microsoft 365 Group"); + } + + WriteVerbose($"Trying to remove the Microsoft Purview sensitivity label from the current site {Connection.Url}"); + ClientContext.Site.SensitivityLabelId = null; + ClientContext.ExecuteQueryRetry(); + } + } +} \ No newline at end of file diff --git a/src/Commands/Purview/SetSiteClassification.cs b/src/Commands/Purview/SetSiteClassification.cs new file mode 100644 index 000000000..e9b00f7dd --- /dev/null +++ b/src/Commands/Purview/SetSiteClassification.cs @@ -0,0 +1,18 @@ +using Microsoft.SharePoint.Client; +using System.Management.Automation; + +namespace PnP.PowerShell.Commands.Purview +{ + [Cmdlet(VerbsCommon.Set, "PnPSiteClassification")] + [OutputType(typeof(void))] + public class SetSiteClassification : PnPSharePointCmdlet + { + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] + public string Identity; + + protected override void ExecuteCmdlet() + { + ClientContext.Site.SetSiteClassification(Identity, GraphAccessToken); + } + } +} \ No newline at end of file diff --git a/src/Commands/Purview/SetSiteSensitivityLabel.cs b/src/Commands/Purview/SetSiteSensitivityLabel.cs new file mode 100644 index 000000000..706ac300d --- /dev/null +++ b/src/Commands/Purview/SetSiteSensitivityLabel.cs @@ -0,0 +1,71 @@ +using Microsoft.SharePoint.Client; +using PnP.PowerShell.Commands.Utilities.REST; +using System; +using System.Linq; +using System.Management.Automation; +using System.Net.Http; +using System.Text.Json; + +namespace PnP.PowerShell.Commands.Purview +{ + [Cmdlet(VerbsCommon.Set, "PnPSiteSensitivityLabel")] + [OutputType(typeof(void))] + public class SetSiteSensitivityLabel : PnPSharePointCmdlet + { + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] + public string Identity; + + protected override void ExecuteCmdlet() + { + string sensitivityLabelId; + if(!Guid.TryParse(Identity, out Guid sensitivityLabelGuid)) + { + // Look up the sensitivity label Guid with the provided name + WriteVerbose($"Passed in label '{Identity}' is a name, going to try to lookup its Id"); + + var label = GraphHelper.GetResultCollectionAsync(Connection, $"/beta/{(Connection.ConnectionMethod == Model.ConnectionMethod.AzureADAppOnly ? "" : "me/")}informationProtection/policy/labels?$filter=name eq '{Identity}'", GraphAccessToken).GetAwaiter().GetResult(); + if(label == null || label.Count() == 0) + { + throw new PSArgumentException($"No Microsoft Purview sensitivity label with the provided name '{Identity}' could be found", nameof(Identity)); + } + + sensitivityLabelId = label.ElementAt(0).Id; + WriteVerbose($"Microsoft Purview label with name '{Identity}' successfully resolved to Id '{sensitivityLabelId}'"); + } + else + { + // Sensitivity label has been passed in by its Id, we can use it as provided + WriteVerbose($"Passed in label '{Identity} is a Guid, going to use it as is"); + sensitivityLabelId = sensitivityLabelGuid.ToString(); + } + + WriteVerbose($"Verifying if the current site at {Connection.Url} is backed by a Microsoft 365 Group"); + ClientContext.Load(ClientContext.Site, s => s.GroupId); + ClientContext.ExecuteQueryRetry(); + + if(ClientContext.Site.GroupId != Guid.Empty) + { + // Site is Microsoft 365 Group backed + WriteVerbose($"Current site at {Connection.Url} is backed by Microsoft 365 Group with Id {ClientContext.Site.GroupId}, going to try setting the label on the group"); + if(Connection.ConnectionMethod == Model.ConnectionMethod.AzureADAppOnly) + { + WriteWarning("Current connection is not using a delegate token and is backed by a Microsoft 365 Group, setting the Microsoft Purview sensitivity label will likely not work. Check the documentation."); + } + + WriteVerbose($"Trying to set the Microsoft 365 Group with Id {ClientContext.Site.GroupId} behind the current site {Connection.Url} to Microsoft Purview sensitivity label with Id {sensitivityLabelId}"); + var stringContent = new StringContent(JsonSerializer.Serialize(new { assignedLabels = new [] { new { labelId = sensitivityLabelId }}})); + stringContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); + GraphHelper.PatchAsync(Connection, GraphAccessToken, stringContent, $"beta/groups/{ClientContext.Site.GroupId}").GetAwaiter().GetResult();; + } + else + { + // Site does not have a Microsoft 365 Group behind it + WriteVerbose($"Current site at {Connection.Url} is not backed by a Microsoft 365 Group"); + } + + WriteVerbose($"Trying to set the current site {Connection.Url} to Microsoft Purview sensitivity label with Id {sensitivityLabelId}"); + ClientContext.Site.SensitivityLabelId = sensitivityLabelId; + ClientContext.ExecuteQueryRetry(); + } + } +} \ No newline at end of file diff --git a/src/Commands/Site/AddSiteClassification.cs b/src/Commands/Site/AddAvailableSiteClassification.cs similarity index 87% rename from src/Commands/Site/AddSiteClassification.cs rename to src/Commands/Site/AddAvailableSiteClassification.cs index e556a1a6a..02c38c652 100644 --- a/src/Commands/Site/AddSiteClassification.cs +++ b/src/Commands/Site/AddAvailableSiteClassification.cs @@ -7,11 +7,12 @@ namespace PnP.PowerShell.Commands.Site { - [Cmdlet(VerbsCommon.Add, "PnPSiteClassification")] + [Cmdlet(VerbsCommon.Add, "PnPAvailableSiteClassification")] [RequiredMinimalApiPermissions("Group.ReadWrite.All")] + [Alias("Add-PnPSiteClassification")] + [WriteAliasWarning("Please use 'Add-PnPAvailableSiteClassification'. The alias 'Add-PnPSiteClassification' will be removed in a future release.")] public class AddSiteClassification : PnPGraphCmdlet { - [Parameter(Mandatory = true)] public List Classifications; diff --git a/src/Commands/Site/GetSiteClassification.cs b/src/Commands/Site/GetAvailableSiteClassification.cs similarity index 71% rename from src/Commands/Site/GetSiteClassification.cs rename to src/Commands/Site/GetAvailableSiteClassification.cs index 378f8b678..0ca6e8c62 100644 --- a/src/Commands/Site/GetSiteClassification.cs +++ b/src/Commands/Site/GetAvailableSiteClassification.cs @@ -1,4 +1,4 @@ - +using PnP.Framework.Graph.Model; using PnP.PowerShell.Commands.Attributes; using PnP.PowerShell.Commands.Base; using System; @@ -6,9 +6,12 @@ namespace PnP.PowerShell.Commands.Site { - [Cmdlet(VerbsCommon.Get, "PnPSiteClassification")] + [Cmdlet(VerbsCommon.Get, "PnPAvailableSiteClassification")] [RequiredMinimalApiPermissions("Directory.Read.All")] - public class GetSiteClassification : PnPGraphCmdlet + [OutputType(typeof(SiteClassificationsSettings))] + [Alias("Get-PnPSiteClassification")] + [WriteAliasWarning("Please use 'Get-PnPAvailableSiteClassification'. The alias 'Get-PnPSiteClassification' will be removed in a future release.")] + public class GetAvailableSiteClassification : PnPGraphCmdlet { protected override void ExecuteCmdlet() { diff --git a/src/Commands/Site/UpdateSiteClassification.cs b/src/Commands/Site/UpdateAvailableSiteClassification.cs similarity index 82% rename from src/Commands/Site/UpdateSiteClassification.cs rename to src/Commands/Site/UpdateAvailableSiteClassification.cs index 63ce0101c..a8c65a077 100644 --- a/src/Commands/Site/UpdateSiteClassification.cs +++ b/src/Commands/Site/UpdateAvailableSiteClassification.cs @@ -7,9 +7,11 @@ namespace PnP.PowerShell.Commands.Site { - [Cmdlet(VerbsData.Update, "PnPSiteClassification")] + [Cmdlet(VerbsData.Update, "PnPAvailableSiteClassification")] [RequiredMinimalApiPermissions("Directory.ReadWrite.All")] - public class UpdateSiteClassification : PnPGraphCmdlet + [Alias("Update-SiteClassification")] + [WriteAliasWarning("Please use 'Update-PnPAvailableSiteClassification'. The alias 'Update-PnPSiteClassification' will be removed in a future release.")] + public class UpdateAvailableSiteClassification : PnPGraphCmdlet { const string ParameterSet_SETTINGS = "Settings"; const string ParameterSet_SPECIFIC = "Specific"; @@ -71,6 +73,10 @@ protected override void ExecuteCmdlet() changed = true; } } + else + { + WriteError(new ErrorRecord(new InvalidOperationException("You are trying to set the default classification to a value that is not available in the list of possible values. Use Get-PnPAvailableSiteClassification see which site classifications you can use."), "SITECLASSIFICATION_DEFAULTVALUE_INVALID", ErrorCategory.InvalidArgument, null)); + } } if (ParameterSpecified(nameof(UsageGuidelinesUrl))) { @@ -83,14 +89,7 @@ protected override void ExecuteCmdlet() } if (changed) { - if (siteClassificationSettings.Classifications.Contains(siteClassificationSettings.DefaultClassification)) - { - PnP.Framework.Graph.SiteClassificationsUtility.UpdateSiteClassificationsSettings(AccessToken, siteClassificationSettings); - } - else - { - WriteError(new ErrorRecord(new InvalidOperationException("You are trying to set the default classification to a value that is not available in the list of possible values."), "SITECLASSIFICATION_DEFAULTVALUE_INVALID", ErrorCategory.InvalidArgument, null)); - } + PnP.Framework.Graph.SiteClassificationsUtility.UpdateSiteClassificationsSettings(AccessToken, siteClassificationSettings); } } catch (ApplicationException ex) diff --git a/src/Commands/Utilities/REST/GraphHelper.cs b/src/Commands/Utilities/REST/GraphHelper.cs index 9d129ae27..7cd9f7538 100644 --- a/src/Commands/Utilities/REST/GraphHelper.cs +++ b/src/Commands/Utilities/REST/GraphHelper.cs @@ -51,6 +51,7 @@ private static HttpRequestMessage GetMessage(string url, HttpMethod method, PnPC var message = new HttpRequestMessage(); message.Method = method; + message.Headers.TryAddWithoutValidation("Accept", "application/json"); message.RequestUri = !url.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ? new Uri($"https://{connection.GraphEndPoint}/{url}") : new Uri(url); message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); if (additionalHeaders != null) diff --git a/src/Commands/Utilities/REST/RestHelper.cs b/src/Commands/Utilities/REST/RestHelper.cs index b769086fc..36dbe7a1f 100644 --- a/src/Commands/Utilities/REST/RestHelper.cs +++ b/src/Commands/Utilities/REST/RestHelper.cs @@ -6,7 +6,6 @@ using System.Net.Http.Headers; using System.Text; using System.Text.Json; -using System.Threading; using System.Threading.Tasks; using Microsoft.SharePoint.Client; diff --git a/src/Tests/Graph/AddPnPSiteClassificationTests.cs b/src/Tests/Graph/AddPnPAvailableSiteClassificationTests.cs similarity index 97% rename from src/Tests/Graph/AddPnPSiteClassificationTests.cs rename to src/Tests/Graph/AddPnPAvailableSiteClassificationTests.cs index de49566db..bd210e70c 100644 --- a/src/Tests/Graph/AddPnPSiteClassificationTests.cs +++ b/src/Tests/Graph/AddPnPAvailableSiteClassificationTests.cs @@ -5,7 +5,7 @@ namespace PnP.PowerShell.Tests.Graph { [TestClass] - public class AddSiteClassificationTests + public class AddAvailableSiteClassificationTests { #region Test Setup/CleanUp [ClassInitialize] diff --git a/src/Tests/Graph/GetPnPSiteClassificationTests.cs b/src/Tests/Graph/GetPnPAvailableSiteClassificationTests.cs similarity index 97% rename from src/Tests/Graph/GetPnPSiteClassificationTests.cs rename to src/Tests/Graph/GetPnPAvailableSiteClassificationTests.cs index f565e62ec..0e7c83a58 100644 --- a/src/Tests/Graph/GetPnPSiteClassificationTests.cs +++ b/src/Tests/Graph/GetPnPAvailableSiteClassificationTests.cs @@ -5,7 +5,7 @@ namespace PnP.PowerShell.Tests.Graph { [TestClass] - public class GetSiteClassificationTests + public class GetAvailableSiteClassificationTests { #region Test Setup/CleanUp [ClassInitialize] diff --git a/src/Tests/Graph/RemovePnPSiteClassificationTests.cs b/src/Tests/Graph/RemovePnPAvailableSiteClassificationTests.cs similarity index 97% rename from src/Tests/Graph/RemovePnPSiteClassificationTests.cs rename to src/Tests/Graph/RemovePnPAvailableSiteClassificationTests.cs index df69732a5..73fad478e 100644 --- a/src/Tests/Graph/RemovePnPSiteClassificationTests.cs +++ b/src/Tests/Graph/RemovePnPAvailableSiteClassificationTests.cs @@ -5,7 +5,7 @@ namespace PnP.PowerShell.Tests.Graph { [TestClass] - public class RemoveSiteClassificationTests + public class RemoveAvailableSiteClassificationTests { #region Test Setup/CleanUp [ClassInitialize] diff --git a/src/Tests/Graph/UpdatePnPSiteClassificationTests.cs b/src/Tests/Graph/UpdatePnPAvailableSiteClassificationTests.cs similarity index 97% rename from src/Tests/Graph/UpdatePnPSiteClassificationTests.cs rename to src/Tests/Graph/UpdatePnPAvailableSiteClassificationTests.cs index 92c592842..2cf328e12 100644 --- a/src/Tests/Graph/UpdatePnPSiteClassificationTests.cs +++ b/src/Tests/Graph/UpdatePnPAvailableSiteClassificationTests.cs @@ -5,7 +5,7 @@ namespace PnP.PowerShell.Tests.Graph { [TestClass] - public class UpdateSiteClassificationTests + public class UpdateAvailableSiteClassificationTests { #region Test Setup/CleanUp [ClassInitialize] From c11ede8fcbf2ffacc67600da206fa0a13c81260f Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 30 Jun 2022 23:16:34 +0300 Subject: [PATCH 451/458] Fix #449 - issue with certificate generation in non-windows OS (#2061) * Fixed Az function article * Fixed bullet points * Potentially fix #449 - issue with certificate generation * Bump deps --- src/Commands/PnP.PowerShell.csproj | 20 ++++++++++---------- src/Commands/Utilities/CertificateHelper.cs | 7 +++---- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/Commands/PnP.PowerShell.csproj b/src/Commands/PnP.PowerShell.csproj index 621fcd9e4..284feee62 100644 --- a/src/Commands/PnP.PowerShell.csproj +++ b/src/Commands/PnP.PowerShell.csproj @@ -50,26 +50,26 @@ - - + + - - + + - - + + - - + + - + - + diff --git a/src/Commands/Utilities/CertificateHelper.cs b/src/Commands/Utilities/CertificateHelper.cs index e2ca55f9e..2fe1a7b06 100644 --- a/src/Commands/Utilities/CertificateHelper.cs +++ b/src/Commands/Utilities/CertificateHelper.cs @@ -309,15 +309,14 @@ internal static X509Certificate2 CreateSelfSignedCertificate(string commonName, string distinguishedNameString = string.Join("; ", x500Values); X500DistinguishedName distinguishedName = new X500DistinguishedName(distinguishedNameString); - - using (RSA rsa = MakeExportable(new RSACng(2048))) - { + + using (RSA rsa = Platform.IsWindows ? MakeExportable(new RSACng(2048)) : RSA.Create(2048)) + { var request = new CertificateRequest(distinguishedName, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); request.CertificateExtensions.Add( new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false)); - request.CertificateExtensions.Add( new X509EnhancedKeyUsageExtension( new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false)); From b63f451a83379b990cfabc9ee156b4f9e27eab10 Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Thu, 30 Jun 2022 22:18:44 +0200 Subject: [PATCH 452/458] Update Set-PnPSiteScriptPackage.md (#2062) --- documentation/Set-PnPSiteScriptPackage.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/documentation/Set-PnPSiteScriptPackage.md b/documentation/Set-PnPSiteScriptPackage.md index c51a86079..bd5123880 100644 --- a/documentation/Set-PnPSiteScriptPackage.md +++ b/documentation/Set-PnPSiteScriptPackage.md @@ -33,16 +33,9 @@ Set-PnPSiteScriptPackage -Identity [-Title ] Set-PnPSiteScriptPackage -Identity f1d55d9b-b116-4f54-bc00-164a51e7e47f -Title "My Site Script" ``` -Updates an existing Site Script PAckage and changes the title. - -### EXAMPLE 2 -```powershell -$script = Get-PnPSiteScriptPackage -Identity f1d55d9b-b116-4f54-bc00-164a51e7e47f -Set-PnPSiteScriptPackage -Identity $script -Title "My Site Script" -``` - Updates an existing Site Script Package and changes the title. + ## PARAMETERS ### -Connection From ff909694f98521b55845ff939224364e28b17b48 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Thu, 30 Jun 2022 23:23:08 +0300 Subject: [PATCH 453/458] Feature #2055 - additional props for set-pnptenant (#2063) * Fixed Az function article * Fixed bullet points * #2055 - issue with external user props in Set-PnPTenant --- CHANGELOG.md | 1 + documentation/Set-PnPTenant.md | 29 +++++++++++++++++++++++++++++ src/Commands/Admin/SetTenant.cs | 17 +++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 268de9c84..bffc46ac8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - Added `Get-Microsoft365GroupEndpoints` cmdlet to retrieve details on all endpoints connected to a Microsoft 365 Group [#2038](https://github.com/pnp/powershell/pull/2038) - Added `-ExcludeDeletedSites` optional parameter to `Get-PnPSiteCollectionAppCatalogs` which allows for site collections with App Catalogs that are in the recycle bin to be exluded from the results [#2044](https://github.com/pnp/powershell/pull/2044) - Added `-CurrentSite` optional parameter to `Get-PnPSiteCollectionAppCatalogs` which allows for checking if the currently connected to site has a site collection App Catalogs provisioned on it [#2044](https://github.com/pnp/powershell/pull/2044) +- Added `ExternalUserExpirationRequired` and `ExternalUserExpireInDays` parameters to `Set-PnPTenant` cmdlet to handle expiration policy for External users. ### Changed diff --git a/documentation/Set-PnPTenant.md b/documentation/Set-PnPTenant.md index 9fbeeccf5..1de492803 100644 --- a/documentation/Set-PnPTenant.md +++ b/documentation/Set-PnPTenant.md @@ -51,6 +51,7 @@ Set-PnPTenant [-SpecialCharactersStateInFileFolderNames [-EnableAutoNewsDigest ] [-CommentsOnListItemsDisabled ] [-CommentsOnFilesDisabled ] [-DisableBackToClassic ] [-InformationBarriersSuspension ] [-AllowFilesWithKeepLabelToBeDeletedODB ] [-AllowFilesWithKeepLabelToBeDeletedSPO ] + [-ExternalUserExpirationRequired ] [-ExternalUserExpireInDays ] [-Force] [-Connection ] [] ``` @@ -1335,6 +1336,34 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -ExternalUserExpirationRequired +When set to true, it will set enable expiration date for external users. + +```yaml +Type: Boolean +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -ExternalUserExpireInDays +When a value is set, it means that the access of the external user will expire in those many number of days. + +```yaml +Type: Int +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Force If provided, no confirmation will be requested and the action will be performed diff --git a/src/Commands/Admin/SetTenant.cs b/src/Commands/Admin/SetTenant.cs index f0d098bcb..2e0b131bd 100644 --- a/src/Commands/Admin/SetTenant.cs +++ b/src/Commands/Admin/SetTenant.cs @@ -246,6 +246,11 @@ public class SetTenant : PnPAdminCmdlet public Guid[] EnableModernListTemplateIds; [Parameter(Mandatory = false)] + public bool? ExternalUserExpirationRequired; + + [Parameter(Mandatory = false)] + public int? ExternalUserExpireInDays; + public SwitchParameter Force; protected override void ExecuteCmdlet() @@ -901,6 +906,18 @@ protected override void ExecuteCmdlet() modified = true; } + if (ExternalUserExpirationRequired.HasValue) + { + Tenant.ExternalUserExpirationRequired = ExternalUserExpirationRequired.Value; + modified = true; + } + + if (ExternalUserExpireInDays.HasValue) + { + Tenant.ExternalUserExpireInDays = ExternalUserExpireInDays.Value; + modified = true; + } + if (modified) { ClientContext.ExecuteQueryRetry(); From 81d5603216258e64fbf5a7b2c25750c9faffbdea Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Fri, 1 Jul 2022 04:07:16 +0000 Subject: [PATCH 454/458] Nightly publish to PowerShell Gallery --- pnppowershell_hash.txt | 2 +- version.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pnppowershell_hash.txt b/pnppowershell_hash.txt index 73a48531c..e805721b6 100644 --- a/pnppowershell_hash.txt +++ b/pnppowershell_hash.txt @@ -1 +1 @@ -08c93e315fadde8e8beeed1c0bbbfb34b9727f00 \ No newline at end of file +6d5809964279233883eecb342a4bc0adbaa79107 \ No newline at end of file diff --git a/version.txt b/version.txt index b99ce00ea..9a0af7d1b 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.64 \ No newline at end of file +1.10.65 \ No newline at end of file From 718d79a82fafc8e5c1cb30e76360ca5448ace2be Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Fri, 1 Jul 2022 11:30:20 +0300 Subject: [PATCH 455/458] Feature #2055 : added missing props for get-pnptenant cmdlet (#2067) * Fixed Az function article * Fixed bullet points * #2055 - issue with external user props in Set-PnPTenant * #2055 - retrieve additional props in get-pnptenant --- src/Commands/Model/SPOTenant.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Commands/Model/SPOTenant.cs b/src/Commands/Model/SPOTenant.cs index 61b16743d..30f95f045 100644 --- a/src/Commands/Model/SPOTenant.cs +++ b/src/Commands/Model/SPOTenant.cs @@ -38,6 +38,8 @@ public SPOTenant(Tenant tenant, ClientContext clientContext) this.disabledWebPartIds = tenant.DisabledWebPartIds; this.stopNew2013Workflows = tenant.StopNew2013Workflows; this.viewInFileExplorerEnabled = tenant.ViewInFileExplorerEnabled; + this.externalUserExpirationRequired = tenant.ExternalUserExpirationRequired; + this.externalUserExpireInDays = tenant.ExternalUserExpireInDays; try { @@ -546,6 +548,10 @@ public SPOTenant(Tenant tenant, ClientContext clientContext) public bool IsFluidEnabled => isFluidEnabled; public bool DisablePersonalListCreation => disablePersonalListCreation; + public bool ExternalUserExpirationRequired => externalUserExpirationRequired; + + public int ExternalUserExpireInDays => externalUserExpireInDays; + public Guid[] DisabledModernListTemplateIds => disabledModernListTemplateIds; private bool hideDefaultThemes; @@ -700,5 +706,9 @@ public SPOTenant(Tenant tenant, ClientContext clientContext) private Guid[] disabledModernListTemplateIds; + private bool externalUserExpirationRequired; + + private int externalUserExpireInDays; + } } From 84c706a49250e5f804d844e233a00ee12afdd3a0 Mon Sep 17 00:00:00 2001 From: Arleta Wanat <42035526+PowershellScripts@users.noreply.github.com> Date: Fri, 1 Jul 2022 15:30:44 +0200 Subject: [PATCH 456/458] Update Set-PnPSiteScript.md (#2069) --- documentation/Set-PnPSiteScript.md | 53 +++++++++--------------------- 1 file changed, 16 insertions(+), 37 deletions(-) diff --git a/documentation/Set-PnPSiteScript.md b/documentation/Set-PnPSiteScript.md index 13ce91908..d02032d22 100644 --- a/documentation/Set-PnPSiteScript.md +++ b/documentation/Set-PnPSiteScript.md @@ -15,7 +15,7 @@ online version: https://pnp.github.io/powershell/cmdlets/Set-PnPSiteScript.html * SharePoint: Access to the SharePoint Tenant Administration site -Updates an existing Site Script on the current tenant. +Updates an existing site script on the current tenant. ## SYNTAX @@ -25,6 +25,7 @@ Set-PnPSiteScript -Identity [-Title ] [-Descr ``` ## DESCRIPTION +This cmdlet updates an existing site script. ## EXAMPLES @@ -33,7 +34,7 @@ Set-PnPSiteScript -Identity [-Title ] [-Descr Set-PnPSiteScript -Identity f1d55d9b-b116-4f54-bc00-164a51e7e47f -Title "My Site Script" ``` -Updates an existing Site Script and changes the title. +Updates an existing site script and changes the title. ### EXAMPLE 2 ```powershell @@ -41,24 +42,17 @@ $script = Get-PnPSiteScript -Identity f1d55d9b-b116-4f54-bc00-164a51e7e47f Set-PnPSiteScript -Identity $script -Title "My Site Script" ``` -Updates an existing Site Script and changes the title. +Updates an existing site script and changes the title. -## PARAMETERS - -### -Confirm -Prompts you for confirmation before running the cmdlet. +### EXAMPLE 3 +```powershell +$content = Get-PnPSiteScriptFromWeb -Url https://contoso.sharepoint.com/sites/SampleSite -IncludeAll +Set-PnPSiteScript -Identity f1d55d9b-b116-4f54-bc00-164a51e7e47f -Content $content +``` -```yaml -Type: SwitchParameter -Parameter Sets: (All) -Aliases: cf +Updates an existing site script and its components. -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` +## PARAMETERS ### -Connection Optional connection to be used by the cmdlet. Retrieve the value for this parameter by either specifying -ReturnConnection on Connect-PnPOnline or by executing Get-PnPConnection. @@ -75,7 +69,7 @@ Accept wildcard characters: False ``` ### -Content -A JSON string containing the site script +A JSON string containing the site script. ```yaml Type: String @@ -89,7 +83,7 @@ Accept wildcard characters: False ``` ### -Description -The description of the site script +The description of the site script. ```yaml Type: String @@ -103,7 +97,7 @@ Accept wildcard characters: False ``` ### -Identity -The guid or an object representing the site script +The guid or an object representing the site script. ```yaml Type: TenantSiteScriptPipeBind @@ -117,7 +111,7 @@ Accept wildcard characters: False ``` ### -Title -The title of the site script +The title of the site script. ```yaml Type: String @@ -131,7 +125,7 @@ Accept wildcard characters: False ``` ### -Version -Specifies the version of the site script +Specifies the version of the site script. ```yaml Type: Int32 @@ -144,21 +138,6 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -WhatIf -Shows what would happen if the cmdlet runs. The cmdlet is not run. - -```yaml -Type: SwitchParameter -Parameter Sets: (All) -Aliases: wi - -Required: False -Position: Named -Default value: None -Accept pipeline input: False -Accept wildcard characters: False -``` - ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) From d31405231f62381d507e5fa94dbb7302ac1db374 Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Fri, 1 Jul 2022 15:53:13 +0200 Subject: [PATCH 457/458] Version 1.11.0 --- CHANGELOG.md | 4 ++++ version.txt | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bffc46ac8..901c558b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Added +## [1.11.0] + +### Added + - Added `-Wait` and `-Verbose` optional paramarers to `New-PnPUPABulkImportJob` [#1752](https://github.com/pnp/powershell/pull/1752) - Added `Add-PnPTeamsChannelUser` which allows members and owners to be added to private channels in Teams [#1735](https://github.com/pnp/powershell/pull/1735) - Added `Channel` parameter to `Add-PnPTeamsUser` cmdlet which if specified will allow owners and members to be added to private channels in a Teams Team. [#1772](https://github.com/pnp/powershell/pull/1772) diff --git a/version.txt b/version.txt index 9a0af7d1b..169f19b49 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.10.65 \ No newline at end of file +1.11.0 \ No newline at end of file From b54690194f59b694268f69e793920ea3825dabc2 Mon Sep 17 00:00:00 2001 From: Erwin van Hunen Date: Fri, 1 Jul 2022 15:55:25 +0200 Subject: [PATCH 458/458] Version 1.11.0 --- src/Commands/PnP.PowerShell.csproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Commands/PnP.PowerShell.csproj b/src/Commands/PnP.PowerShell.csproj index 284feee62..cbcfbad91 100644 --- a/src/Commands/PnP.PowerShell.csproj +++ b/src/Commands/PnP.PowerShell.csproj @@ -51,25 +51,25 @@ - + - + - + - + - + - +