From b539017901e6a71f1d06b9a3f7d03a1be087f81e Mon Sep 17 00:00:00 2001 From: Nate Ferrell Date: Tue, 16 Jul 2019 20:00:11 -0500 Subject: [PATCH 1/2] ***NO_CI*** Pushing local branch up to GH --- PSGSuite/Public/Drive/Get-GSDriveRevision.ps1 | 207 ++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 PSGSuite/Public/Drive/Get-GSDriveRevision.ps1 diff --git a/PSGSuite/Public/Drive/Get-GSDriveRevision.ps1 b/PSGSuite/Public/Drive/Get-GSDriveRevision.ps1 new file mode 100644 index 00000000..0c823274 --- /dev/null +++ b/PSGSuite/Public/Drive/Get-GSDriveRevision.ps1 @@ -0,0 +1,207 @@ +function Get-GSDriveRevision { + <# + .SYNOPSIS + Gets information about a Drive file's revisions + + .DESCRIPTION + Gets information about a Drive file's revisions + + .PARAMETER FileId + The unique Id of the file to get revisions of + + .PARAMETER RevisionId + The unique Id of the revision to get. If excluded, gets the list of revisions for the file + + .PARAMETER User + The email or unique Id of the owner of the Drive file + + Defaults to the AdminEmail user + + .PARAMETER Fields + The specific fields to returned + + .EXAMPLE + Get-GSDriveFile -FileId '1rhsAYTOB_vrpvfwImPmWy0TcVa2sgmQa_9u976' + + Gets the information for the file + + .EXAMPLE + Get-GSDriveFile -FileId '1rhsAYTOB_vrpvfwImPmWy0TcVa2sgmQa_9u976' -OutFilePath (Get-Location).Path + + Gets the information for the file and saves the file in the current working directory + #> + [OutputType('Google.Apis.Drive.v3.Data.Revision')] + [cmdletbinding(DefaultParameterSetName = "List")] + Param + ( + [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] + [Alias('Id')] + [String] + $FileId, + [parameter(Mandatory = $false,Position = 1,ParameterSetName = "Get")] + [String[]] + $RevisionId, + [parameter(Mandatory = $false,ParameterSetName = "Get")] + [Alias('SaveFileTo')] + [String] + $OutFilePath, + [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] + [Alias('Owner','PrimaryEmail','UserKey','Mail')] + [string] + $User = $Script:PSGSuite.AdminEmail, + [parameter(Mandatory = $false,ParameterSetName = "Fields")] + [String[]] + $Fields, + [parameter(Mandatory = $false,ParameterSetName = "Get")] + [Switch] + $Force, + [parameter(Mandatory = $false,ParameterSetName = "List")] + [Alias('MaxResults')] + [ValidateRange(1,1000)] + [Int] + $PageSize = 1000, + [parameter(Mandatory = $false,ParameterSetName = "List")] + [Alias('First')] + [Int] + $Limit = 0 + ) + Process { + if ($User -ceq 'me') { + $User = $Script:PSGSuite.AdminEmail + } + elseif ($User -notlike "*@*.*") { + $User = "$($User)@$($Script:PSGSuite.Domain)" + } + $serviceParams = @{ + Scope = 'https://www.googleapis.com/auth/drive' + ServiceType = 'Google.Apis.Drive.v3.DriveService' + User = $User + } + $service = New-GoogleService @serviceParams + switch ($PSCmdlet.ParameterSetName) { + Get { + try { + foreach ($revision in $RevisionId) { + $request = $service.Revisions.Get($FileId,$revision) + if ($Fields) { + $request.Fields = $($Fields -join ",") + } + $baseVerbose = "Getting" + if ($Fields) { + $baseVerbose += " Fields [$($Fields -join ",")] of" + } + $baseVerbose += " Drive File Revision Id '$revision' of File Id '$FileId' for User '$User'" + Write-Verbose $baseVerbose + $res = $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru | Add-Member -MemberType NoteProperty -Name 'FileId' -Value $FileId -PassThru + if ($OutFilePath) { + if (Test-Path $OutFilePath) { + $outFilePathItem = Get-Item $OutFilePath -ErrorAction SilentlyContinue + if ($outFilePathItem.PSIsContainer) { + $resPath = $outFilePathItem.FullName + $cleanedName = Get-SafeFileName "$($res.Name).$($res.FileExtension)" + $filePath = Join-Path $resPath $cleanedName + } + elseif ($Force) { + $endings = [System.Collections.Generic.List[string]]@('.bak') + 1..100 | ForEach-Object {$endings.Add(".bak$_")} + foreach ($end in $endings) { + $backupPath = "$($outFilePathItem.FullName)$end" + if (-not (Test-Path $backupPath)) { + break + } + else { + $backupPath = $null + } + } + Write-Warning "Renaming '$($outFilePathItem.Name)' to '$($outFilePathItem.Name).bak' in case replacement download fails." + Rename-Item $outFilePathItem.FullName -NewName $backupPath -Force + $filePath = $OutFilePath + } + else { + throw "File already exists at path '$($OutFilePath)'. Please specify -Force to overwrite any files with the same name if they exist." + } + } + else { + $filePath = $OutFilePath + } + Write-Verbose "Saving file to path '$filePath'" + $stream = [System.IO.File]::Create($filePath) + $request.Download($stream) + $stream.Close() + $res | Add-Member -MemberType NoteProperty -Name OutFilePath -Value $filePath -Force + if ($backupPath) { + Write-Verbose "File has been downloaded successfully! Removing the backup file at path: $backupPath" + Remove-Item $backupPath -Recurse -Force -Confirm:$false + } + } + $res + } + } + catch { + $err = $_ + if ($backupPath) { + if (Test-Path $outFilePathItem.FullName) { + Remove-Item $outFilePathItem.FullName -Recurse -Force + } + Rename-Item $backupPath -NewName $outFilePathItem.FullName -Force + } + if ($ErrorActionPreference -eq 'Stop') { + $PSCmdlet.ThrowTerminatingError($err) + } + else { + Write-Error $err + } + } + } + List { + try { + $request = $service.Revisions.List($FileId) + if ($Limit -gt 0 -and $PageSize -gt $Limit) { + Write-Verbose ("Reducing PageSize from {0} to {1} to meet limit with first page" -f $PageSize,$Limit) + $PageSize = $Limit + } + $request.PageSize = $PageSize + if ($Fields) { + $request.Fields = "$($Fields -join ",")" + } + $baseVerbose = "Getting" + if ($Fields) { + $baseVerbose += " Fields [$($Fields -join ",")] of" + } + $baseVerbose += " all Drive File Revisions of File Id '$FileId' for User '$User'" + Write-Verbose $baseVerbose + [int]$i = 1 + $overLimit = $false + do { + $result = $request.Execute() + $result.Revisions | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru | Add-Member -MemberType NoteProperty -Name 'FileId' -Value $FileId -PassThru + if ($result.NextPageToken) { + $request.PageToken = $result.NextPageToken + } + [int]$retrieved = ($i + $result.Revisions.Count) - 1 + Write-Verbose "Retrieved $retrieved Revisions..." + if ($Limit -gt 0 -and $retrieved -eq $Limit) { + Write-Verbose "Limit reached: $Limit" + $overLimit = $true + } + elseif ($Limit -gt 0 -and ($retrieved + $PageSize) -gt $Limit) { + $newPS = $Limit - $retrieved + Write-Verbose ("Reducing PageSize from {0} to {1} to meet limit with next page" -f $PageSize,$newPS) + $request.PageSize = $newPS + } + [int]$i = $i + $result.Revisions.Count + } + until ($overLimit -or !$result.NextPageToken) + } + catch { + if ($ErrorActionPreference -eq 'Stop') { + $PSCmdlet.ThrowTerminatingError($_) + } + else { + Write-Error $_ + } + } + } + } + } +} From 59344b50c29ecd77f7974a1ee7679c8c030a50d4 Mon Sep 17 00:00:00 2001 From: Nate Ferrell Date: Wed, 17 Jul 2019 00:29:48 -0500 Subject: [PATCH 2/2] fixes for #193, #210, #209, #204 ## 2.30.0 * [Issue #193](https://github.com/scrthq/PSGSuite/issues/193) * Added: Drive Revision functions: * Get-GSDriveRevision * Remove-GSDriveRevision * Update-GSDriveRevision * [Issue #210](https://github.com/scrthq/PSGSuite/issues/210) * Fixed: Update-GSUser was not accepting User ID's as the User parameter * [Issue #209](https://github.com/scrthq/PSGSuite/issues/209) * Added: Support for inline image downloading with Get-GSGmailMessage where the image is not included on the Attachments property of the parsed message object. * Fixed: Get-GSGmailMessage will now automatically set the Format to Raw if either ParseMessage or SaveAttachmentsTo is passed, as ParseMessage is a requirement in order to be able to access the message attachments as needed. * [Issue #204](https://github.com/scrthq/PSGSuite/issues/204) * Added: Recurse parameter to Get-GSDriveFileList to allow recursively listing all files and subfolders underneath the result set. Confirmed setting the Limit parameter also works as expected with Recurse included, stopping is the original limit is reached. * Added: Get-GSDriveFolderSize function to return the calculated total size of the files in the specified folder(s). * Miscellaneous * Added: Rfc822MsgId parameter to Get-GSGmailMessageList to easily build a query looking for a specific RFS 822 Message ID. * Added: Pipeline support for *-GSDrivePermission functions to enable piping Drive Files into them to manage permissions without looping manually. --- CHANGELOG.md | 20 +++ PSGSuite/PSGSuite.psd1 | 2 +- .../Public/Drive/Add-GSDrivePermission.ps1 | 11 +- PSGSuite/Public/Drive/Get-GSDriveFileList.ps1 | 49 +++++++- .../Public/Drive/Get-GSDriveFolderSize.ps1 | 63 ++++++++++ .../Public/Drive/Get-GSDrivePermission.ps1 | 9 +- PSGSuite/Public/Drive/Get-GSDriveRevision.ps1 | 16 ++- .../Public/Drive/Remove-GSDrivePermission.ps1 | 9 +- .../Public/Drive/Remove-GSDriveRevision.ps1 | 78 ++++++++++++ .../Public/Drive/Update-GSDriveRevision.ps1 | 114 ++++++++++++++++++ PSGSuite/Public/Gmail/Get-GSGmailMessage.ps1 | 17 ++- .../Public/Gmail/Get-GSGmailMessageList.ps1 | 15 ++- PSGSuite/Public/Users/Update-GSUser.ps1 | 7 +- README.md | 29 +++-- psake.ps1 | 2 +- 15 files changed, 387 insertions(+), 54 deletions(-) create mode 100644 PSGSuite/Public/Drive/Get-GSDriveFolderSize.ps1 create mode 100644 PSGSuite/Public/Drive/Remove-GSDriveRevision.ps1 create mode 100644 PSGSuite/Public/Drive/Update-GSDriveRevision.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index a07018e0..159d5ee6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ * [PSGSuite - ChangeLog](#PSGSuite---ChangeLog) + * [2.30.0](#2300) * [2.29.0](#2290) * [2.28.2](#2282) * [2.28.1](#2281) @@ -91,6 +92,25 @@ # PSGSuite - ChangeLog +## 2.30.0 + +* [Issue #193](https://github.com/scrthq/PSGSuite/issues/193) + * Added: Drive Revision functions: + * `Get-GSDriveRevision` + * `Remove-GSDriveRevision` + * `Update-GSDriveRevision` +* [Issue #210](https://github.com/scrthq/PSGSuite/issues/210) + * Fixed: `Update-GSUser` was not accepting User ID's as the User parameter +* [Issue #209](https://github.com/scrthq/PSGSuite/issues/209) + * Added: Support for inline image downloading with `Get-GSGmailMessage` where the image is not included on the Attachments property of the parsed message object. + * Fixed: `Get-GSGmailMessage` will now automatically set the `Format` to `Raw` if either `ParseMessage` or `SaveAttachmentsTo` is passed, as `ParseMessage` is a requirement in order to be able to access the message attachments as needed. +* [Issue #204](https://github.com/scrthq/PSGSuite/issues/204) + * Added: `Recurse` parameter to `Get-GSDriveFileList` to allow recursively listing all files and subfolders underneath the result set. Confirmed setting the `Limit` parameter also works as expected with `Recurse` included, stopping is the original limit is reached. + * Added: `Get-GSDriveFolderSize` function to return the calculated total size of the files in the specified folder(s). +* Miscellaneous + * Added: `Rfc822MsgId` parameter to `Get-GSGmailMessageList` to easily build a query looking for a specific RFS 822 Message ID. + * Added: Pipeline support for `*-GSDrivePermission` functions to enable piping Drive Files into them to manage permissions without looping manually. + ## 2.29.0 * [Issue #201](https://github.com/scrthq/PSGSuite/issues/201) diff --git a/PSGSuite/PSGSuite.psd1 b/PSGSuite/PSGSuite.psd1 index 658e4da6..e520fce4 100644 --- a/PSGSuite/PSGSuite.psd1 +++ b/PSGSuite/PSGSuite.psd1 @@ -12,7 +12,7 @@ RootModule = 'PSGSuite.psm1' # Version number of this module. - ModuleVersion = '2.29.0' + ModuleVersion = '2.30.0' # ID used to uniquely identify this module GUID = '9d751152-e83e-40bb-a6db-4c329092aaec' diff --git a/PSGSuite/Public/Drive/Add-GSDrivePermission.ps1 b/PSGSuite/Public/Drive/Add-GSDrivePermission.ps1 index 227e3d63..7589152d 100644 --- a/PSGSuite/Public/Drive/Add-GSDrivePermission.ps1 +++ b/PSGSuite/Public/Drive/Add-GSDrivePermission.ps1 @@ -78,11 +78,8 @@ function Add-GSDrivePermission { [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High",DefaultParameterSetName = "Email")] Param ( - [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] - [Alias('Owner','PrimaryEmail','UserKey','Mail')] - [string] - $User = $Script:PSGSuite.AdminEmail, - [parameter(Mandatory = $true)] + [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] + [Alias('Id')] [String] $FileId, [parameter(Mandatory = $true)] @@ -93,6 +90,10 @@ function Add-GSDrivePermission { [ValidateSet("User","Group","Domain","Anyone")] [String] $Type, + [parameter(Mandatory = $false,Position = 1,ValueFromPipelineByPropertyName = $true)] + [Alias('Owner','PrimaryEmail','UserKey','Mail')] + [string] + $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $false,ParameterSetName = "Email")] [String] $EmailAddress, diff --git a/PSGSuite/Public/Drive/Get-GSDriveFileList.ps1 b/PSGSuite/Public/Drive/Get-GSDriveFileList.ps1 index a4bebda4..e3842ebc 100644 --- a/PSGSuite/Public/Drive/Get-GSDriveFileList.ps1 +++ b/PSGSuite/Public/Drive/Get-GSDriveFileList.ps1 @@ -22,6 +22,9 @@ function Get-GSDriveFileList { .PARAMETER ParentFolderId ID of parent folder to search to add to the filter + .PARAMETER Recurse + If True, recurses through subfolders found underneath primary search results + .PARAMETER IncludeTeamDriveItems Whether Team Drive items should be included in results. (Default: false) @@ -68,6 +71,9 @@ function Get-GSDriveFileList { $ParentFolderId, [parameter(Mandatory = $false)] [Switch] + $Recurse, + [parameter(Mandatory = $false)] + [Switch] $IncludeTeamDriveItems, [parameter(Mandatory = $false)] [String[]] @@ -137,7 +143,7 @@ function Get-GSDriveFileList { if ($Fields) { $request.Fields = "$($Fields -join ",")" } - foreach ($key in $PSBoundParameters.Keys | Where-Object {$_ -notin @('Fields','PageSize')}) { + foreach ($key in $PSBoundParameters.Keys | Where-Object { $_ -notin @('Fields','PageSize') }) { switch ($key) { Filter { $FilterFmt = ($PSBoundParameters[$key] -join " and ") -replace " -eq ","=" -replace " -like ",":" -replace " -match ",":" -replace " -contains ",":" -creplace "'True'","True" -creplace "'False'","False" -replace " -in "," in " -replace " -le ",'<=' -replace " -ge ",">=" -replace " -gt ",'>' -replace " -lt ",'<' -replace " -ne ","!=" -replace " -and "," and " -replace " -or "," or " -replace " -not "," not " @@ -164,20 +170,51 @@ function Get-GSDriveFileList { Write-Verbose $baseVerbose [int]$i = 1 $overLimit = $false + $originalLimit = $Limit do { $result = $request.Execute() $result.Files | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru + [int]$retrieved = ($i + $result.Files.Count) - 1 + if ($Recurse -and ($Limit -eq 0 -or $retrieved -lt $Limit)) { + Write-Verbose "Starting recursive search..." + if ($Limit -gt 0) { + $Limit = $Limit - $result.Files.Count + Write-Verbose "[Prerecursion] Limit reduced to $Limit to account for $($result.Files.Count) files found in main search" + } + foreach ($subFolder in ($result.Files | Where-Object { $_.MimeType -eq 'application/vnd.google-apps.folder' } | Sort-Object Name)) { + Write-Verbose "Getting recursive file list of files under subfolder $($subFolder.Name) [$($subFolder.Id)]" + $params = @{ + PageSize = $PageSize + User = $User + ParentFolderId = $subfolder.Id + IncludeTeamDriveItems = $IncludeTeamDriveItems + Recurse = $true + Fields = $Fields + Limit = $Limit + Verbose = $false + } + Get-GSDriveFileList @params -OutVariable sub + Write-Verbose "Found $($sub.Count) files in subfolder $($subfolder.Name) [$($subfolder.Id)]" + [int]$retrieved += $sub.Count + if ($originalLimit -gt 0) { + $Limit = $originalLimit - $retrieved + Write-Verbose "[Postrecursion] Limit reduced to $Limit" + if ($retrieved -ge $originalLimit) { + break + } + } + } + } if ($result.NextPageToken) { $request.PageToken = $result.NextPageToken } - [int]$retrieved = ($i + $result.Files.Count) - 1 Write-Verbose "Retrieved $retrieved Files..." - if ($Limit -gt 0 -and $retrieved -eq $Limit) { - Write-Verbose "Limit reached: $Limit" + if ($originalLimit -gt 0 -and $retrieved -ge $originalLimit) { + Write-Verbose "Limit reached: $originalLimit" $overLimit = $true } - elseif ($Limit -gt 0 -and ($retrieved + $PageSize) -gt $Limit) { - $newPS = $Limit - $retrieved + elseif ($originalLimit -gt 0 -and ($retrieved + $PageSize) -gt $originalLimit) { + $newPS = $originalLimit - $retrieved Write-Verbose ("Reducing PageSize from {0} to {1} to meet limit with next page" -f $PageSize,$newPS) $request.PageSize = $newPS } diff --git a/PSGSuite/Public/Drive/Get-GSDriveFolderSize.ps1 b/PSGSuite/Public/Drive/Get-GSDriveFolderSize.ps1 new file mode 100644 index 00000000..2b4af038 --- /dev/null +++ b/PSGSuite/Public/Drive/Get-GSDriveFolderSize.ps1 @@ -0,0 +1,63 @@ +function Get-GSDriveFolderSize { + <# + .SYNOPSIS + Gets the size of the files with the specified ParentFolderId in Drive. + + .DESCRIPTION + Gets the size of the files with the specified ParentFolderId in Drive. + + .PARAMETER ParentFolderId + ID of parent folder to search to add to the filter + + .PARAMETER Recurse + If True, recurses through subfolders found underneath primary search results + + .PARAMETER Depth + Internal use only. Used to track how deep in the subfolder structure the command is currently searching when used with -Verbose + + .EXAMPLE + Get-GSDriveFolderSize -ParentFolderId $id1,$id2 -Recurse + #> + + [CmdletBinding()] + Param ( + [parameter(Mandatory,Position = 0,ValueFromPipelineByPropertyName)] + [Alias('Id')] + [String[]] + $ParentFolderId, + [parameter()] + [Switch] + $Recurse, + [parameter()] + [Int] + $Depth = 0 + ) + Begin { + $final = @{ + TotalSize = 0 + } + } + Process { + foreach ($id in $ParentFolderId) { + $files = Get-GSDriveFileList -ParentFolderId $id -IncludeTeamDriveItems -Fields "files(name, id, size, mimeType)" -Verbose:$false + $folderTotal = ($files.Size | Measure-Object -Sum).Sum + if ($folderTotal){ + Write-Verbose ("Total file size in bytes in folder ID '$id': {0}" -f $folderTotal) + $final.TotalSize += $folderTotal + } + if ($Recurse -and ($subfolders = $files | Where-Object {$_.MimeType -eq 'application/vnd.google-apps.folder'})) { + $newDepth = $Depth + 1 + Write-Verbose "[Depth: $Depth > $newDepth] Recursively searching subfolder Ids: [ $($subfolders.Id -join ", ") ]" + $subFolderTotal = Get-GSDriveFolderSize -ParentFolderId $subfolders.Id -Recurse -Depth $newDepth + if ($subFolderTotal) { + $final.TotalSize += $subFolderTotal.TotalSize + } + } + } + } + End { + $final['TotalSizeInMB'] = $final['TotalSize'] / 1MB + $final['TotalSizeInGB'] = $final['TotalSize'] / 1GB + [PSCustomObject]$final + } +} diff --git a/PSGSuite/Public/Drive/Get-GSDrivePermission.ps1 b/PSGSuite/Public/Drive/Get-GSDrivePermission.ps1 index 718e49e3..0e77f6f7 100644 --- a/PSGSuite/Public/Drive/Get-GSDrivePermission.ps1 +++ b/PSGSuite/Public/Drive/Get-GSDrivePermission.ps1 @@ -32,13 +32,14 @@ function Get-GSDrivePermission { [cmdletbinding(DefaultParameterSetName = "List")] Param ( - [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] + [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] + [Alias('Id')] + [String] + $FileId, + [parameter(Mandatory = $false,Position = 1,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, - [parameter(Mandatory = $true)] - [String] - $FileId, [parameter(Mandatory = $false,ParameterSetName = "Get")] [String[]] $PermissionId, diff --git a/PSGSuite/Public/Drive/Get-GSDriveRevision.ps1 b/PSGSuite/Public/Drive/Get-GSDriveRevision.ps1 index 0c823274..8b64b210 100644 --- a/PSGSuite/Public/Drive/Get-GSDriveRevision.ps1 +++ b/PSGSuite/Public/Drive/Get-GSDriveRevision.ps1 @@ -21,14 +21,14 @@ function Get-GSDriveRevision { The specific fields to returned .EXAMPLE - Get-GSDriveFile -FileId '1rhsAYTOB_vrpvfwImPmWy0TcVa2sgmQa_9u976' + Get-GSDriveFile -FileId $fileId | Get-GSDriveRevision - Gets the information for the file + Gets the list of revisions for the file .EXAMPLE - Get-GSDriveFile -FileId '1rhsAYTOB_vrpvfwImPmWy0TcVa2sgmQa_9u976' -OutFilePath (Get-Location).Path + Get-GSDriveRevision -FileId $fileId -Limit 1 - Gets the information for the file and saves the file in the current working directory + Gets the most recent revision for the file #> [OutputType('Google.Apis.Drive.v3.Data.Revision')] [cmdletbinding(DefaultParameterSetName = "List")] @@ -49,9 +49,9 @@ function Get-GSDriveRevision { [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, - [parameter(Mandatory = $false,ParameterSetName = "Fields")] + [parameter(Mandatory = $false)] [String[]] - $Fields, + $Fields = '*', [parameter(Mandatory = $false,ParameterSetName = "Get")] [Switch] $Force, @@ -83,12 +83,10 @@ function Get-GSDriveRevision { try { foreach ($revision in $RevisionId) { $request = $service.Revisions.Get($FileId,$revision) - if ($Fields) { - $request.Fields = $($Fields -join ",") - } $baseVerbose = "Getting" if ($Fields) { $baseVerbose += " Fields [$($Fields -join ",")] of" + $request.Fields = $($Fields -join ",") } $baseVerbose += " Drive File Revision Id '$revision' of File Id '$FileId' for User '$User'" Write-Verbose $baseVerbose diff --git a/PSGSuite/Public/Drive/Remove-GSDrivePermission.ps1 b/PSGSuite/Public/Drive/Remove-GSDrivePermission.ps1 index dc697412..8e88a8d7 100644 --- a/PSGSuite/Public/Drive/Remove-GSDrivePermission.ps1 +++ b/PSGSuite/Public/Drive/Remove-GSDrivePermission.ps1 @@ -30,14 +30,15 @@ function Remove-GSDrivePermission { [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] Param ( - [parameter(Mandatory = $false,Position = 0,ValueFromPipelineByPropertyName = $true)] + [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] + [Alias('Id')] + [String] + $FileId, + [parameter(Mandatory = $false,Position = 1,ValueFromPipelineByPropertyName = $true)] [Alias('Owner','PrimaryEmail','UserKey','Mail')] [string] $User = $Script:PSGSuite.AdminEmail, [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] - [String] - $FileId, - [parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] [Alias('Id')] [String] $PermissionId diff --git a/PSGSuite/Public/Drive/Remove-GSDriveRevision.ps1 b/PSGSuite/Public/Drive/Remove-GSDriveRevision.ps1 new file mode 100644 index 00000000..a757da5f --- /dev/null +++ b/PSGSuite/Public/Drive/Remove-GSDriveRevision.ps1 @@ -0,0 +1,78 @@ +function Remove-GSDriveRevision { + <# + .SYNOPSIS + Permanently deletes a file version. + + .DESCRIPTION + Permanently deletes a file version. + + You can only delete revisions for files with binary content in Google Drive, like images or videos. Revisions for other files, like Google Docs or Sheets, and the last remaining file version can't be deleted. + + .PARAMETER FileId + The unique Id of the file to remove revisions from + + .PARAMETER RevisionId + The unique Id of the revision to remove + + .PARAMETER User + The email or unique Id of the owner of the Drive file + + Defaults to the AdminEmail user + + .EXAMPLE + Get-GSDriveRevision -FileId $fileId -Limit 1 | Remove-GSDriveRevision + + Removes the oldest revision for the file + + .EXAMPLE + Get-GSDriveRevision -FileId $fileId | Select-Object -Last 1 | Remove-GSDriveRevision + + Removes the newest revision for the file + #> + [cmdletbinding(SupportsShouldProcess = $true,ConfirmImpact = "High")] + Param + ( + [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] + [String] + $FileId, + [parameter(Mandatory = $true,Position = 1,ValueFromPipelineByPropertyName = $true)] + [Alias('Id')] + [String[]] + $RevisionId, + [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] + [Alias('Owner','PrimaryEmail','UserKey','Mail')] + [string] + $User = $Script:PSGSuite.AdminEmail + ) + Process { + if ($User -ceq 'me') { + $User = $Script:PSGSuite.AdminEmail + } + elseif ($User -notlike "*@*.*") { + $User = "$($User)@$($Script:PSGSuite.Domain)" + } + $serviceParams = @{ + Scope = 'https://www.googleapis.com/auth/drive' + ServiceType = 'Google.Apis.Drive.v3.DriveService' + User = $User + } + $service = New-GoogleService @serviceParams + foreach ($id in $RevisionId) { + try { + if ($PSCmdlet.ShouldProcess("Removing Drive Revision Id '$id' from FileId '$FileID' for user '$User'")) { + $request = $service.Revisions.Delete($FileId,$id) + $request.Execute() + Write-Verbose "Successfully removed Drive Revision Id '$id' from FileId '$FileID' for user '$User'" + } + } + catch { + if ($ErrorActionPreference -eq 'Stop') { + $PSCmdlet.ThrowTerminatingError($_) + } + else { + Write-Error $_ + } + } + } + } +} diff --git a/PSGSuite/Public/Drive/Update-GSDriveRevision.ps1 b/PSGSuite/Public/Drive/Update-GSDriveRevision.ps1 new file mode 100644 index 00000000..02eaa6a6 --- /dev/null +++ b/PSGSuite/Public/Drive/Update-GSDriveRevision.ps1 @@ -0,0 +1,114 @@ +function Update-GSDriveRevision { + <# + .SYNOPSIS + Updates a revision with patch semantics + + .DESCRIPTION + Updates a revision with patch semantics + + .PARAMETER FileId + The unique Id of the file to update revisions for + + .PARAMETER RevisionId + The unique Id of the revision to update + + .PARAMETER KeepForever + Whether to keep this revision forever, even if it is no longer the head revision. If not set, the revision will be automatically purged 30 days after newer content is uploaded. This can be set on a maximum of 200 revisions for a file. + + This field is only applicable to files with binary content in Drive. + + .PARAMETER PublishAuto + Whether subsequent revisions will be automatically republished. This is only applicable to Google Docs. + + .PARAMETER Published + Whether this revision is published. This is only applicable to Google Docs. + + .PARAMETER PublishedOutsideDomain + Whether this revision is published outside the domain. This is only applicable to Google Docs. + + .PARAMETER Fields + The specific fields to returned + + .PARAMETER User + The email or unique Id of the owner of the Drive file + + Defaults to the AdminEmail user + + .EXAMPLE + Get-GSDriveRevision -FileId $fileId -Limit 1 | Update-GSDriveRevision -KeepForever + + Sets 'KeepForever' for the oldest revision of the file to 'True' + + .EXAMPLE + Get-GSDriveRevision -FileId $fileId | Select-Object -Last 1 | Update-GSDriveRevision -KeepForever + + Sets 'KeepForever' for the newest revision of the file to 'True' + #> + [OutputType('Google.Apis.Drive.v3.Data.Revision')] + [cmdletbinding()] + Param + ( + [parameter(Mandatory = $true,Position = 0,ValueFromPipelineByPropertyName = $true)] + [String] + $FileId, + [parameter(Mandatory = $true,Position = 1,ValueFromPipelineByPropertyName = $true)] + [Alias('Id')] + [String[]] + $RevisionId, + [parameter(Mandatory = $false)] + [Switch] + $KeepForever, + [parameter(Mandatory = $false)] + [Switch] + $PublishAuto, + [parameter(Mandatory = $false)] + [Switch] + $Published, + [parameter(Mandatory = $false)] + [Switch] + $PublishedOutsideDomain, + [parameter(Mandatory = $false)] + [String[]] + $Fields = '*', + [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] + [Alias('Owner','PrimaryEmail','UserKey','Mail')] + [string] + $User = $Script:PSGSuite.AdminEmail + ) + Process { + if ($User -ceq 'me') { + $User = $Script:PSGSuite.AdminEmail + } + elseif ($User -notlike "*@*.*") { + $User = "$($User)@$($Script:PSGSuite.Domain)" + } + $serviceParams = @{ + Scope = 'https://www.googleapis.com/auth/drive' + ServiceType = 'Google.Apis.Drive.v3.DriveService' + User = $User + } + $service = New-GoogleService @serviceParams + $body = New-Object 'Google.Apis.Drive.v3.Data.Revision' + foreach ($key in $PSBoundParameters.Keys | Where-Object {$_ -in $body.PSObject.Properties.Name}) { + $body.$key = $PSBoundParameters[$key] + } + foreach ($id in $RevisionId) { + try { + $request = $service.Revisions.Update($body,$FileId,$id) + if ($Fields) { + $request.Fields = $($Fields -join ",") + } + Write-Verbose "Updating Drive File Revision Id '$revision' of File Id '$FileId' for User '$User'" + $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru | Add-Member -MemberType NoteProperty -Name 'FileId' -Value $FileId -PassThru + } + catch { + if ($ErrorActionPreference -eq 'Stop') { + $PSCmdlet.ThrowTerminatingError($_) + } + else { + Write-Error $_ + } + } + } + } +} diff --git a/PSGSuite/Public/Gmail/Get-GSGmailMessage.ps1 b/PSGSuite/Public/Gmail/Get-GSGmailMessage.ps1 index 73289a9a..24c10781 100644 --- a/PSGSuite/Public/Gmail/Get-GSGmailMessage.ps1 +++ b/PSGSuite/Public/Gmail/Get-GSGmailMessage.ps1 @@ -68,7 +68,7 @@ function Get-GSGmailMessage { elseif ($User -notlike "*@*.*") { $User = "$($User)@$($Script:PSGSuite.Domain)" } - if ($ParseMessage) { + if ($ParseMessage -or $SaveAttachmentsTo) { $Format = "Raw" } $serviceParams = @{ @@ -92,11 +92,22 @@ function Get-GSGmailMessage { } Write-Verbose "Getting Message Id '$mId' for user '$User'" $result = $request.Execute() | Add-Member -MemberType NoteProperty -Name 'User' -Value $User -PassThru - if ($ParseMessage) { + if ($ParseMessage -or $SaveAttachmentsTo) { $parsed = Read-MimeMessage -String $(Convert-Base64 -From WebSafeBase64String -To NormalString -String $result.Raw) | Select-Object @{N = 'User';E = {$User}},@{N = "Id";E = {$result.Id}},@{N = "ThreadId";E = {$result.ThreadId}},@{N = "LabelIds";E = {$result.LabelIds}},@{N = "Snippet";E = {$result.Snippet}},@{N = "HistoryId";E = {$result.HistoryId}},@{N = "InternalDate";E = {$result.InternalDate}},@{N = "InternalDateConverted";E = {Convert-EpochToDate -EpochString $result.internalDate}},@{N = "SizeEstimate";E = {$result.SizeEstimate}},* if ($SaveAttachmentsTo) { $resPath = Resolve-Path $SaveAttachmentsTo - $attachments = $parsed.Attachments + $attachments = New-Object System.Collections.Generic.List[object] + if ($parsed.Attachments | Where-Object {$_.FileName}) { + $parsed.Attachments | Where-Object {$_.FileName} | ForEach-Object { + $attachments.Add($_) + } + } + if ($parsed.BodyParts | Where-Object {$_.FileName}) { + $parsed.BodyParts | Where-Object {$_.FileName} | ForEach-Object { + $attachments.Add($_) + } + } + $attachments = $attachments | Sort-Object {"$($_.FileName)_$($_.ContentId)"} -Unique foreach ($att in $attachments) { $cleanedName = Get-SafeFileName $att.FileName $fileName = Join-Path $resPath $cleanedName diff --git a/PSGSuite/Public/Gmail/Get-GSGmailMessageList.ps1 b/PSGSuite/Public/Gmail/Get-GSGmailMessageList.ps1 index 71e33c7a..512092d5 100644 --- a/PSGSuite/Public/Gmail/Get-GSGmailMessageList.ps1 +++ b/PSGSuite/Public/Gmail/Get-GSGmailMessageList.ps1 @@ -37,17 +37,21 @@ function Get-GSGmailMessageList { Gets the list of messages sent directly to the user after 2017/12/25 excluding chats #> [OutputType('Google.Apis.Gmail.v1.Data.Message')] - [cmdletbinding()] + [cmdletbinding(DefaultParameterSetName = "Filter")] Param ( [parameter(Mandatory = $false,ValueFromPipelineByPropertyName = $true)] [Alias("PrimaryEmail","UserKey","Mail")] [String] $User = $Script:PSGSuite.AdminEmail, - [parameter(Mandatory = $false)] + [parameter(Mandatory = $false,ParameterSetName = "Filter")] [Alias('Query')] [String[]] $Filter, + [parameter(Mandatory = $false,ParameterSetName = "Rfc822MsgId")] + [Alias('MessageId','MsgId')] + [String] + $Rfc822MsgId, [parameter(Mandatory = $false)] [Alias('LabelId')] [String[]] @@ -93,6 +97,9 @@ function Get-GSGmailMessageList { $request = $service.Users.Messages.List($U) foreach ($key in $PSBoundParameters.Keys) { switch ($key) { + Rfc822MsgId { + $request.Q = "rfc822msgid:$($Rfc822MsgId.Trim())" + } Filter { $request.Q = $($Filter -join " ") } @@ -111,8 +118,8 @@ function Get-GSGmailMessageList { $PageSize = $Limit } $request.MaxResults = $PageSize - if ($Filter) { - Write-Verbose "Getting all Messages matching filter '$Filter' for user '$U'" + if ($request.Q) { + Write-Verbose "Getting all Messages matching filter '$($request.Q)' for user '$U'" } else { Write-Verbose "Getting all Messages for user '$U'" diff --git a/PSGSuite/Public/Users/Update-GSUser.ps1 b/PSGSuite/Public/Users/Update-GSUser.ps1 index 88e66d3c..92141468 100644 --- a/PSGSuite/Public/Users/Update-GSUser.ps1 +++ b/PSGSuite/Public/Users/Update-GSUser.ps1 @@ -221,12 +221,7 @@ function Update-GSUser { Process { foreach ($U in $User) { try { - if ($U -ceq 'me') { - $U = $Script:PSGSuite.AdminEmail - } - elseif ($U -notlike "*@*.*") { - $U = "$($U)@$($Script:PSGSuite.Domain)" - } + Resolve-Email ([ref]$U) if ($PSCmdlet.ShouldProcess("Updating user '$U'")) { Write-Verbose "Updating user '$U'" $userObj = Get-GSUser $U -Verbose:$false diff --git a/README.md b/README.md index b625952b..0b2cd02a 100644 --- a/README.md +++ b/README.md @@ -158,14 +158,21 @@ All other functions are either intact or have an alias included to support backw [Full CHANGELOG here](https://github.com/scrthq/PSGSuite/blob/master/CHANGELOG.md) -#### 2.29.0 - -* [Issue #201](https://github.com/scrthq/PSGSuite/issues/201) - * Fixed: Fields parameter on remaining `*-GSDriveFile` functions -* [Issue #197](https://github.com/scrthq/PSGSuite/issues/197) - * Updated: All remaining `*-TeamDrive` functions now use the new Drives namespace. All previous functions names have been converted to aliases to maintain backwards compatibility. - * Added: `Hide-GSDrive` - * Added: `Show-GSDrive` -* [Issue #184](https://github.com/scrthq/PSGSuite/issues/184) - * Added: `EnableCollaborativeInbox` parameter to `Update-GSGroupSettings` - * Added: `WhoCanDiscoverGroup` parameter to `Update-GSGroupSettings`. +#### 2.30.0 + +* [Issue #193](https://github.com/scrthq/PSGSuite/issues/193) + * Added: Drive Revision functions: + * `Get-GSDriveRevision` + * `Remove-GSDriveRevision` + * `Update-GSDriveRevision` +* [Issue #210](https://github.com/scrthq/PSGSuite/issues/210) + * Fixed: `Update-GSUser` was not accepting User ID's as the User parameter +* [Issue #209](https://github.com/scrthq/PSGSuite/issues/209) + * Added: Support for inline image downloading with `Get-GSGmailMessage` where the image is not included on the Attachments property of the parsed message object. + * Fixed: `Get-GSGmailMessage` will now automatically set the `Format` to `Raw` if either `ParseMessage` or `SaveAttachmentsTo` is passed, as `ParseMessage` is a requirement in order to be able to access the message attachments as needed. +* [Issue #204](https://github.com/scrthq/PSGSuite/issues/204) + * Added: `Recurse` parameter to `Get-GSDriveFileList` to allow recursively listing all files and subfolders underneath the result set. Confirmed setting the `Limit` parameter also works as expected with `Recurse` included, stopping is the original limit is reached. + * Added: `Get-GSDriveFolderSize` function to return the calculated total size of the files in the specified folder(s). +* Miscellaneous + * Added: `Rfc822MsgId` parameter to `Get-GSGmailMessageList` to easily build a query looking for a specific RFS 822 Message ID. + * Added: Pipeline support for `*-GSDrivePermission` functions to enable piping Drive Files into them to manage permissions without looping manually. diff --git a/psake.ps1 b/psake.ps1 index 5f27dd42..282128ee 100644 --- a/psake.ps1 +++ b/psake.ps1 @@ -484,7 +484,7 @@ $deployScriptBlock = { if ($ENV:BHBuildSystem -eq 'VSTS' -and -not [String]::IsNullOrEmpty($env:TwitterAccessSecret) -and -not [String]::IsNullOrEmpty($env:TwitterAccessToken) -and -not [String]::IsNullOrEmpty($env:TwitterConsumerKey) -and -not [String]::IsNullOrEmpty($env:TwitterConsumerSecret)) { " Publishing tweet about new release..." $manifest = Import-PowerShellDataFile -Path (Join-Path $outputModVerDir "$($env:BHProjectName).psd1") - $text = "#$($env:BHProjectName) v$($versionToDeploy) is now available on the #PSGallery! https://www.powershellgallery.com/packages/$($env:BHProjectName) #PowerShell" + $text = "#$($env:BHProjectName) v$($versionToDeploy) is now available on the #PSGallery! https://www.powershellgallery.com/packages/$($env:BHProjectName)/$($versionToDeploy) #PowerShell" $manifest.PrivateData.PSData.Tags | Foreach-Object { $text += " #$($_)" }