From 88b073fc01fadc07e9ed4a226197e936696f3ffd Mon Sep 17 00:00:00 2001 From: Michal Romiszewski <23618894+mromiszewski@users.noreply.github.com> Date: Wed, 3 May 2023 20:42:24 +0200 Subject: [PATCH 1/2] Add list item version history cmdlets --- .../Base/PipeBinds/ListItemVersionPipeBind.cs | 25 ++++++ src/Commands/Lists/GetListItemVersion.cs | 40 +++++++++ src/Commands/Lists/RemoveListItemVersion.cs | 66 ++++++++++++++ src/Commands/Lists/RestoreListItemVersion.cs | 85 +++++++++++++++++++ src/Commands/Properties/Resources.Designer.cs | 17 +++- src/Commands/Properties/Resources.resx | 3 + 6 files changed, 232 insertions(+), 4 deletions(-) create mode 100644 src/Commands/Base/PipeBinds/ListItemVersionPipeBind.cs create mode 100644 src/Commands/Lists/GetListItemVersion.cs create mode 100644 src/Commands/Lists/RemoveListItemVersion.cs create mode 100644 src/Commands/Lists/RestoreListItemVersion.cs diff --git a/src/Commands/Base/PipeBinds/ListItemVersionPipeBind.cs b/src/Commands/Base/PipeBinds/ListItemVersionPipeBind.cs new file mode 100644 index 000000000..317147d03 --- /dev/null +++ b/src/Commands/Base/PipeBinds/ListItemVersionPipeBind.cs @@ -0,0 +1,25 @@ +namespace PnP.PowerShell.Commands.Base.PipeBinds; + +public class ListItemVersionPipeBind +{ + public ListItemVersionPipeBind(string versionLabel) + { + if (int.TryParse(versionLabel, out var id)) + { + Id = id; + } + else + { + VersionLabel = versionLabel; + } + } + + public ListItemVersionPipeBind(int id) + { + Id = id; + } + + public int Id { get; } = -1; + + public string VersionLabel { get; } +} \ No newline at end of file diff --git a/src/Commands/Lists/GetListItemVersion.cs b/src/Commands/Lists/GetListItemVersion.cs new file mode 100644 index 000000000..afb4e1315 --- /dev/null +++ b/src/Commands/Lists/GetListItemVersion.cs @@ -0,0 +1,40 @@ +using System.Linq; +using System.Management.Automation; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Extensions; + +namespace PnP.PowerShell.Commands.Lists +{ + [Cmdlet(VerbsCommon.Get, "PnPListItemVersion")] + public class GetListItemVersion : PnPWebCmdlet + { + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] + public ListPipeBind List; + [Parameter(Mandatory = true, ValueFromPipeline = true)] + public ListItemPipeBind Identity; + + protected override void ExecuteCmdlet() + { + var list = List.GetList(CurrentWeb); + + if (list is null) + { + throw new PSArgumentException($"Cannot find the list provided through -{nameof(List)}", nameof(List)); + } + + var item = Identity.GetListItem(list); + + if (item is null) + { + throw new PSArgumentException($"Cannot find the list item provided through -{nameof(Identity)}", nameof(Identity)); + } + + item.LoadProperties(i => i.Versions); + + if (item.Versions is not null) + { + WriteObject(item.Versions.ToList(), true); + } + } + } +} \ No newline at end of file diff --git a/src/Commands/Lists/RemoveListItemVersion.cs b/src/Commands/Lists/RemoveListItemVersion.cs new file mode 100644 index 000000000..5822e649a --- /dev/null +++ b/src/Commands/Lists/RemoveListItemVersion.cs @@ -0,0 +1,66 @@ +using System.Linq; +using System.Management.Automation; +using Microsoft.SharePoint.Client; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Extensions; +using Resources = PnP.PowerShell.Commands.Properties.Resources; + +namespace PnP.PowerShell.Commands.Lists +{ + [Cmdlet(VerbsCommon.Remove, "PnPListItemVersion")] + public class RemoveListItemVersion : PnPWebCmdlet + { + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] + public ListPipeBind List; + [Parameter(Mandatory = true, ValueFromPipeline = true)] + public ListItemPipeBind Identity; + [Parameter(Mandatory = true, ValueFromPipeline = true)] + public ListItemVersionPipeBind Version; + [Parameter(Mandatory = false)] + public SwitchParameter Force; + + protected override void ExecuteCmdlet() + { + var list = List.GetList(CurrentWeb); + + if (list is null) + { + throw new PSArgumentException($"Cannot find the list provided through -{nameof(List)}", nameof(List)); + } + + var item = Identity.GetListItem(list); + + if (item is null) + { + throw new PSArgumentException($"Cannot find the list item provided through -{nameof(Identity)}", nameof(Identity)); + } + + item.LoadProperties(i => i.Versions); + + ListItemVersion version = null; + if (!string.IsNullOrEmpty(Version.VersionLabel)) + { + version = item.Versions.FirstOrDefault(v => v.VersionLabel == Version.VersionLabel); + } + else if (Version.Id != -1) + { + version = item.Versions.FirstOrDefault(v => v.VersionId == Version.Id); + } + + if (version is null) + { + throw new PSArgumentException($"Cannot find the list item version provided through -{nameof(Version)}", nameof(Version)); + } + + if(Force || ShouldContinue(string.Format(Resources.Delete0, version.VersionLabel), Resources.Confirm)) + { + WriteVerbose($"Trying to remove version {Version.VersionLabel}"); + + version.DeleteObject(); + ClientContext.ExecuteQueryRetry(); + + WriteVerbose($"Removed version {Version.VersionLabel} of list item {item.Id} in list {list.Title}"); + } + } + } +} \ No newline at end of file diff --git a/src/Commands/Lists/RestoreListItemVersion.cs b/src/Commands/Lists/RestoreListItemVersion.cs new file mode 100644 index 000000000..8806b3a34 --- /dev/null +++ b/src/Commands/Lists/RestoreListItemVersion.cs @@ -0,0 +1,85 @@ +using System.Linq; +using System.Management.Automation; +using Microsoft.SharePoint.Client; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Extensions; +using Resources = PnP.PowerShell.Commands.Properties.Resources; + +namespace PnP.PowerShell.Commands.Lists +{ + [Cmdlet(VerbsData.Restore, "PnPListItemVersion")] + public class RestoreListItemVersion : PnPWebCmdlet + { + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] + public ListPipeBind List; + [Parameter(Mandatory = true, ValueFromPipeline = true)] + public ListItemPipeBind Identity; + [Parameter(Mandatory = true, ValueFromPipeline = true)] + public ListItemVersionPipeBind Version; + [Parameter(Mandatory = false)] + public SwitchParameter Force; + + private static readonly FieldType[] UnsupportedFieldTypes = + { + FieldType.Attachments, + FieldType.Computed + }; + + protected override void ExecuteCmdlet() + { + var list = List.GetList(CurrentWeb); + + if (list is null) + { + throw new PSArgumentException($"Cannot find the list provided through -{nameof(List)}", nameof(List)); + } + + var item = Identity.GetListItem(list); + + if (item is null) + { + throw new PSArgumentException($"Cannot find the list item provided through -{nameof(Identity)}", nameof(Identity)); + } + + item.LoadProperties(i => i.Versions); + + ListItemVersion version = null; + if (!string.IsNullOrEmpty(Version.VersionLabel)) + { + version = item.Versions.FirstOrDefault(v => v.VersionLabel == Version.VersionLabel); + } + else if (Version.Id != -1) + { + version = item.Versions.FirstOrDefault(v => v.VersionId == Version.Id); + } + + if (version is null) + { + throw new PSArgumentException($"Cannot find the list item version provided through -{nameof(Version)}", nameof(Version)); + } + + if (Force || ShouldContinue(string.Format(Resources.Restore, version.VersionLabel), Resources.Confirm)) + { + WriteVerbose($"Trying to restore to version with label '{version.VersionLabel}'"); + + var fields = ClientContext.LoadQuery(list.Fields.Include(f => f.InternalName, + f => f.Title, f => f.Hidden, f => f.ReadOnlyField, f => f.FieldTypeKind)); + ClientContext.ExecuteQueryRetry(); + + foreach (var fieldValue in version.FieldValues) + { + var field = fields.FirstOrDefault(f => f.InternalName == fieldValue.Key || f.Title == fieldValue.Key); + if (field is { ReadOnlyField: false, Hidden: false } && !UnsupportedFieldTypes.Contains(field.FieldTypeKind)) + { + item[field.InternalName] = fieldValue.Value; + } + } + + item.Update(); + ClientContext.ExecuteQueryRetry(); + + WriteVerbose($"Restored version {version.VersionLabel} of list item {item.Id} in list {list.Title}"); + } + } + } +} \ No newline at end of file diff --git a/src/Commands/Properties/Resources.Designer.cs b/src/Commands/Properties/Resources.Designer.cs index e1e3af75c..a75ce14e8 100644 --- a/src/Commands/Properties/Resources.Designer.cs +++ b/src/Commands/Properties/Resources.Designer.cs @@ -357,7 +357,7 @@ internal static string NoApiAccessToken { return ResourceManager.GetString("NoApiAccessToken", resourceCulture); } } - + /// /// Looks up a localized string similar to There is currently no connection yet. Use Connect-PnPOnline to connect.. /// @@ -384,7 +384,7 @@ 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.. /// @@ -393,13 +393,13 @@ internal static string NoDefaultSharePointConnection { 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.. /// @@ -724,6 +724,15 @@ internal static string ResetTenantRecycleBinItem { } } + /// + /// Looks up a localized string similar to Restore '{0}'?. + /// + internal static string Restore { + get { + return ResourceManager.GetString("Restore", resourceCulture); + } + } + /// /// Looks up a localized string similar to Restore {0} items from recycle bin to their original locations?. /// diff --git a/src/Commands/Properties/Resources.resx b/src/Commands/Properties/Resources.resx index 84fd486e3..6f08f2f15 100644 --- a/src/Commands/Properties/Resources.resx +++ b/src/Commands/Properties/Resources.resx @@ -366,4 +366,7 @@ Restore {0} items from recycle bin to their original locations? + + Restore '{0}'? + \ No newline at end of file From f3509e4682f1ddd96fcefca54fb7f7ac544c44ce Mon Sep 17 00:00:00 2001 From: Michal Romiszewski <23618894+mromiszewski@users.noreply.github.com> Date: Wed, 3 May 2023 20:42:42 +0200 Subject: [PATCH 2/2] Add list item version history cmdlets documentation --- documentation/Get-PnPListItemVersion.md | 93 ++++++++++++++ documentation/Remove-PnPListItemVersion.md | 128 ++++++++++++++++++++ documentation/Restore-PnPListItemVersion.md | 128 ++++++++++++++++++++ 3 files changed, 349 insertions(+) create mode 100644 documentation/Get-PnPListItemVersion.md create mode 100644 documentation/Remove-PnPListItemVersion.md create mode 100644 documentation/Restore-PnPListItemVersion.md diff --git a/documentation/Get-PnPListItemVersion.md b/documentation/Get-PnPListItemVersion.md new file mode 100644 index 000000000..35ff71b1c --- /dev/null +++ b/documentation/Get-PnPListItemVersion.md @@ -0,0 +1,93 @@ +--- +Module Name: PnP.PowerShell +title: Get-PnPListItemVersion +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Get-PnPListItemVersion.html +--- + +# Get-PnPListItemVersion + +## SYNOPSIS +Retrieves the previous versions of a list item. + +## SYNTAX + +```powershell +Get-PnPListItemVersion -List -Identity [-Connection ] +``` + +## DESCRIPTION +This cmdlet retrieves the version history of a list item. + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Get-PnPListItemVersion -List "Demo List" -Identity 1 +``` + +Retrieves the list item version history. + +## PARAMETERS + +### -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: True (ByValue) +Accept wildcard characters: False +``` + +### -Version +The ID or label of the version. + +```yaml +Type: ListItemPipeBind +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 +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) \ No newline at end of file diff --git a/documentation/Remove-PnPListItemVersion.md b/documentation/Remove-PnPListItemVersion.md new file mode 100644 index 000000000..62922f05d --- /dev/null +++ b/documentation/Remove-PnPListItemVersion.md @@ -0,0 +1,128 @@ +--- +Module Name: PnP.PowerShell +title: Remove-PnPListItemVersion +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Remove-PnPListItemVersion.html +--- + +# Remove-PnPListItemVersion + +## SYNOPSIS +Removes a specific list item version. + +## SYNTAX + +```powershell +Remove-PnPListItemVersion -List -Identity -Version [-Force] [-Verbose] [-Connection ] +``` + +## DESCRIPTION +This cmdlet removes a specific list item version. + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Remove-PnPListItemVersion -List "Demo List" -Identity 1 -Version 512 +``` + +Removes the list item version with Id 512. + +### EXAMPLE 2 +```powershell +Remove-PnPListItemVersion -List "Demo List" -Identity 1 -Version "1.0" +``` + +Removes the list item version with version label "1.0". + +## PARAMETERS + +### -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: True (ByValue) +Accept wildcard characters: False +``` + +### -Version +The ID or label of the version. + +```yaml +Type: ListItemPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Force +If provided, no confirmation will be requested and the action will be performed. + +```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 executing the cmdlet. + +```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) \ No newline at end of file diff --git a/documentation/Restore-PnPListItemVersion.md b/documentation/Restore-PnPListItemVersion.md new file mode 100644 index 000000000..b8d60c5d7 --- /dev/null +++ b/documentation/Restore-PnPListItemVersion.md @@ -0,0 +1,128 @@ +--- +Module Name: PnP.PowerShell +title: Restore-PnPListItemVersion +schema: 2.0.0 +applicable: SharePoint Online +external help file: PnP.PowerShell.dll-Help.xml +online version: https://pnp.github.io/powershell/cmdlets/Restore-PnPListItemVersion.html +--- + +# Restore-PnPListItemVersion + +## SYNOPSIS +Restores a specific list item version. + +## SYNTAX + +```powershell +Restore-PnPListItemVersion -List -Identity -Version [-Force] [-Verbose] [-Connection ] +``` + +## DESCRIPTION +This cmdlet restores a specific list item version. + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Restore-PnPListItemVersion -List "Demo List" -Identity 1 -Version 512 +``` + +Restores the list item version with Id 512. + +### EXAMPLE 2 +```powershell +Restore-PnPListItemVersion -List "Demo List" -Identity 1 -Version "1.0" +``` + +Restores the list item version with version label "1.0". + +## PARAMETERS + +### -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: True (ByValue) +Accept wildcard characters: False +``` + +### -Version +The ID or label of the version. + +```yaml +Type: ListItemPipeBind +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -Force +If provided, no confirmation will be requested and the action will be performed. + +```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 executing the cmdlet. + +```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) \ No newline at end of file