From cce8d39a57951aa87973f8a1e53961e7b295f95b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Oct 2025 11:27:54 +0000 Subject: [PATCH 1/4] Initial plan From ef19f07fd34c753197fbbfcce21b2415ed8a2911 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Oct 2025 11:39:38 +0000 Subject: [PATCH 2/4] Add SharePoint Rules cmdlets implementation Co-authored-by: KoenZomers <5940670+KoenZomers@users.noreply.github.com> --- documentation/Add-PnPListRule.md | 208 +++++++++++++++++ documentation/Get-PnPListRule.md | 94 ++++++++ documentation/Remove-PnPListRule.md | 101 +++++++++ documentation/Set-PnPListRule.md | 222 +++++++++++++++++++ src/Commands/Base/PipeBinds/RulePipeBind.cs | 42 ++++ src/Commands/Model/SharePoint/Rule.cs | 71 ++++++ src/Commands/Model/SharePoint/RuleAction.cs | 41 ++++ src/Commands/Model/SharePoint/RuleTrigger.cs | 28 +++ src/Commands/Rules/AddListRule.cs | 109 +++++++++ src/Commands/Rules/GetListRule.cs | 106 +++++++++ src/Commands/Rules/RemoveListRule.cs | 75 +++++++ src/Commands/Rules/SetListRule.cs | 137 ++++++++++++ src/Tests/Rules/AddPnPListRuleTests.cs | 79 +++++++ src/Tests/Rules/GetPnPListRuleTests.cs | 73 ++++++ src/Tests/Rules/RemovePnPListRuleTests.cs | 73 ++++++ src/Tests/Rules/SetPnPListRuleTests.cs | 73 ++++++ 16 files changed, 1532 insertions(+) create mode 100644 documentation/Add-PnPListRule.md create mode 100644 documentation/Get-PnPListRule.md create mode 100644 documentation/Remove-PnPListRule.md create mode 100644 documentation/Set-PnPListRule.md create mode 100644 src/Commands/Base/PipeBinds/RulePipeBind.cs create mode 100644 src/Commands/Model/SharePoint/Rule.cs create mode 100644 src/Commands/Model/SharePoint/RuleAction.cs create mode 100644 src/Commands/Model/SharePoint/RuleTrigger.cs create mode 100644 src/Commands/Rules/AddListRule.cs create mode 100644 src/Commands/Rules/GetListRule.cs create mode 100644 src/Commands/Rules/RemoveListRule.cs create mode 100644 src/Commands/Rules/SetListRule.cs create mode 100644 src/Tests/Rules/AddPnPListRuleTests.cs create mode 100644 src/Tests/Rules/GetPnPListRuleTests.cs create mode 100644 src/Tests/Rules/RemovePnPListRuleTests.cs create mode 100644 src/Tests/Rules/SetPnPListRuleTests.cs diff --git a/documentation/Add-PnPListRule.md b/documentation/Add-PnPListRule.md new file mode 100644 index 000000000..fae7ec4fc --- /dev/null +++ b/documentation/Add-PnPListRule.md @@ -0,0 +1,208 @@ +--- +Module Name: PnP.PowerShell +schema: 2.0.0 +applicable: SharePoint Online +online version: https://pnp.github.io/powershell/cmdlets/Add-PnPListRule.html +external help file: PnP.PowerShell.dll-Help.xml +title: Add-PnPListRule +--- + +# Add-PnPListRule + +## SYNOPSIS +Adds a new SharePoint list or library rule. + +## SYNTAX + +```powershell +Add-PnPListRule -List -Title -TriggerEventType -ActionType + [-Description ] [-EmailRecipients ] [-EmailSubject ] [-EmailBody ] + [-Condition ] [-Enabled] [-Connection ] +``` + +## DESCRIPTION +Adds a new rule to a SharePoint list or library. SharePoint Rules are the replacement for SharePoint Alerts which are being retired. Rules can trigger actions like sending email notifications when items are created, modified, or deleted. + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Add-PnPListRule -List "Demo List" -Title "Notify on new items" -TriggerEventType "create" -ActionType "sendEmail" -EmailRecipients "user@contoso.com" +``` + +Creates a rule that sends an email to the specified recipient when a new item is created in the "Demo List". + +### EXAMPLE 2 +```powershell +Add-PnPListRule -List "Documents" -Title "Document modified alert" -TriggerEventType "update" -ActionType "sendEmail" -EmailRecipients "team@contoso.com" -EmailSubject "Document Updated" -EmailBody "A document has been modified in the library" +``` + +Creates a rule that sends a custom email when a document is modified in the "Documents" library. + +### EXAMPLE 3 +```powershell +Add-PnPListRule -List "Tasks" -Title "Task deleted notification" -TriggerEventType "delete" -ActionType "sendEmail" -EmailRecipients "manager@contoso.com","admin@contoso.com" -Description "Notify managers when tasks are deleted" +``` + +Creates a rule that notifies multiple recipients when a task is deleted, with a description for the rule. + +## PARAMETERS + +### -ActionType +The type of action to perform when the rule is triggered (e.g., "sendEmail"). + +```yaml +Type: String +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Condition +Optional condition that must be met for the rule to trigger. + +```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. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Description +Optional description for the rule. + +```yaml +Type: String +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -EmailBody +The body content for email notifications. + +```yaml +Type: String +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -EmailRecipients +The email addresses to send notifications to. + +```yaml +Type: String[] +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -EmailSubject +The subject line for email notifications. + +```yaml +Type: String +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Enabled +Whether the rule is enabled. Enabled by default. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: True +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 +``` + +### -Title +The title/name of the rule. + +```yaml +Type: String +Parameter Sets: (All) + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -TriggerEventType +The type of event that triggers the rule (e.g., "create", "update", "delete"). + +```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) +[SharePoint Alerts Retirement](https://support.microsoft.com/en-us/office/sharepoint-alerts-retirement-813a90c7-3ff1-47a9-8a2f-152f48b2486f) diff --git a/documentation/Get-PnPListRule.md b/documentation/Get-PnPListRule.md new file mode 100644 index 000000000..161c289d7 --- /dev/null +++ b/documentation/Get-PnPListRule.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-PnPListRule.html +external help file: PnP.PowerShell.dll-Help.xml +title: Get-PnPListRule +--- + +# Get-PnPListRule + +## SYNOPSIS +Retrieves SharePoint list or library rules. + +## SYNTAX + +```powershell +Get-PnPListRule -List [-Identity ] [-Connection ] +``` + +## DESCRIPTION +Retrieves all rules or a specific rule from a SharePoint list or library. SharePoint Rules are the replacement for SharePoint Alerts which are being retired. + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Get-PnPListRule -List "Demo List" +``` + +Returns all rules configured on the "Demo List". + +### EXAMPLE 2 +```powershell +Get-PnPListRule -List "Demo List" -Identity "12345678-1234-1234-1234-123456789012" +``` + +Returns the rule with the specified ID from the "Demo List". + +### EXAMPLE 3 +```powershell +Get-PnPListRule -List "Demo List" -Identity "My Rule" +``` + +Returns rules with the title "My Rule" from the "Demo List". + +## 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 or Title of the rule to retrieve. If not specified, all rules for the list will be returned. + +```yaml +Type: RulePipeBind +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 +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) +[SharePoint Alerts Retirement](https://support.microsoft.com/en-us/office/sharepoint-alerts-retirement-813a90c7-3ff1-47a9-8a2f-152f48b2486f) diff --git a/documentation/Remove-PnPListRule.md b/documentation/Remove-PnPListRule.md new file mode 100644 index 000000000..f26ba5775 --- /dev/null +++ b/documentation/Remove-PnPListRule.md @@ -0,0 +1,101 @@ +--- +Module Name: PnP.PowerShell +schema: 2.0.0 +applicable: SharePoint Online +online version: https://pnp.github.io/powershell/cmdlets/Remove-PnPListRule.html +external help file: PnP.PowerShell.dll-Help.xml +title: Remove-PnPListRule +--- + +# Remove-PnPListRule + +## SYNOPSIS +Removes a SharePoint list or library rule. + +## SYNTAX + +```powershell +Remove-PnPListRule -List -Identity [-Force] [-Connection ] +``` + +## DESCRIPTION +Removes a rule from a SharePoint list or library. + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Remove-PnPListRule -List "Demo List" -Identity "12345678-1234-1234-1234-123456789012" +``` + +Removes the rule with the specified ID from the "Demo List". A confirmation prompt will be displayed. + +### EXAMPLE 2 +```powershell +Remove-PnPListRule -List "Documents" -Identity "My Rule" -Force +``` + +Removes the rule with title "My Rule" from the "Documents" library without prompting 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 +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Force +If specified, the rule will be removed without prompting for confirmation. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Identity +The ID or Title of the rule to remove. + +```yaml +Type: RulePipeBind +Parameter Sets: (All) + +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 +``` + +## RELATED LINKS + +[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) +[SharePoint Alerts Retirement](https://support.microsoft.com/en-us/office/sharepoint-alerts-retirement-813a90c7-3ff1-47a9-8a2f-152f48b2486f) diff --git a/documentation/Set-PnPListRule.md b/documentation/Set-PnPListRule.md new file mode 100644 index 000000000..b49f5c06b --- /dev/null +++ b/documentation/Set-PnPListRule.md @@ -0,0 +1,222 @@ +--- +Module Name: PnP.PowerShell +schema: 2.0.0 +applicable: SharePoint Online +online version: https://pnp.github.io/powershell/cmdlets/Set-PnPListRule.html +external help file: PnP.PowerShell.dll-Help.xml +title: Set-PnPListRule +--- + +# Set-PnPListRule + +## SYNOPSIS +Updates an existing SharePoint list or library rule. + +## SYNTAX + +```powershell +Set-PnPListRule -List -Identity [-Title ] [-Description ] + [-TriggerEventType ] [-ActionType ] [-EmailRecipients ] [-EmailSubject ] + [-EmailBody ] [-Condition ] [-Enabled ] [-Connection ] +``` + +## DESCRIPTION +Updates an existing rule in a SharePoint list or library. Only the specified parameters will be updated. + +## EXAMPLES + +### EXAMPLE 1 +```powershell +Set-PnPListRule -List "Demo List" -Identity "12345678-1234-1234-1234-123456789012" -Title "Updated Rule Title" +``` + +Updates the title of the specified rule. + +### EXAMPLE 2 +```powershell +Set-PnPListRule -List "Documents" -Identity "My Rule" -Enabled $false +``` + +Disables the rule with title "My Rule". + +### EXAMPLE 3 +```powershell +Set-PnPListRule -List "Tasks" -Identity "12345678-1234-1234-1234-123456789012" -EmailRecipients "newuser@contoso.com","admin@contoso.com" -EmailSubject "New Subject" +``` + +Updates the email recipients and subject for the specified rule. + +## PARAMETERS + +### -ActionType +The type of action to perform when the rule is triggered. + +```yaml +Type: String +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Condition +Optional condition that must be met for the rule to trigger. + +```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. + +```yaml +Type: PnPConnection +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Description +The description for the rule. + +```yaml +Type: String +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -EmailBody +The body content for email notifications. + +```yaml +Type: String +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -EmailRecipients +The email addresses to send notifications to. + +```yaml +Type: String[] +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -EmailSubject +The subject line for email notifications. + +```yaml +Type: String +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Enabled +Whether the rule is enabled or disabled. + +```yaml +Type: Boolean +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Identity +The ID or Title of the rule to update. + +```yaml +Type: RulePipeBind +Parameter Sets: (All) + +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 +``` + +### -Title +The title/name of the rule. + +```yaml +Type: String +Parameter Sets: (All) + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -TriggerEventType +The type of event that triggers the rule. + +```yaml +Type: String +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) +[SharePoint Alerts Retirement](https://support.microsoft.com/en-us/office/sharepoint-alerts-retirement-813a90c7-3ff1-47a9-8a2f-152f48b2486f) diff --git a/src/Commands/Base/PipeBinds/RulePipeBind.cs b/src/Commands/Base/PipeBinds/RulePipeBind.cs new file mode 100644 index 000000000..644139064 --- /dev/null +++ b/src/Commands/Base/PipeBinds/RulePipeBind.cs @@ -0,0 +1,42 @@ +using System; +using PnP.PowerShell.Commands.Model.SharePoint; + +namespace PnP.PowerShell.Commands.Base.PipeBinds +{ + public sealed class RulePipeBind + { + private readonly Guid _id; + private readonly string _title; + + public RulePipeBind(Guid guid) + { + _id = guid; + } + + public RulePipeBind(string input) + { + if (Guid.TryParse(input, out Guid guid)) + { + _id = guid; + } + else + { + _title = input; + } + } + + public RulePipeBind(Rule rule) + { + _id = rule.RuleId; + _title = rule.Title; + } + + public Guid Id => _id; + public string Title => _title; + + public RulePipeBind() + { + _id = Guid.Empty; + } + } +} diff --git a/src/Commands/Model/SharePoint/Rule.cs b/src/Commands/Model/SharePoint/Rule.cs new file mode 100644 index 000000000..a1bb021b1 --- /dev/null +++ b/src/Commands/Model/SharePoint/Rule.cs @@ -0,0 +1,71 @@ +using System; +using System.Text.Json.Serialization; + +namespace PnP.PowerShell.Commands.Model.SharePoint +{ + /// + /// Represents a SharePoint list or library rule + /// + public class Rule + { + /// + /// The unique identifier of the rule + /// + [JsonPropertyName("ruleId")] + public Guid RuleId { get; set; } + + /// + /// The title/name of the rule + /// + [JsonPropertyName("title")] + public string Title { get; set; } + + /// + /// The description of the rule + /// + [JsonPropertyName("description")] + public string Description { get; set; } + + /// + /// Whether the rule is enabled or disabled + /// + [JsonPropertyName("isEnabled")] + public bool IsEnabled { get; set; } + + /// + /// The trigger conditions for the rule + /// + [JsonPropertyName("triggerCondition")] + public RuleTrigger TriggerCondition { get; set; } + + /// + /// The actions to execute when the rule is triggered + /// + [JsonPropertyName("actionParameters")] + public RuleAction ActionParameters { get; set; } + + /// + /// The creation date of the rule + /// + [JsonPropertyName("createdDate")] + public DateTime? CreatedDate { get; set; } + + /// + /// The last modified date of the rule + /// + [JsonPropertyName("lastModifiedDate")] + public DateTime? LastModifiedDate { get; set; } + + /// + /// The user who created the rule + /// + [JsonPropertyName("createdBy")] + public string CreatedBy { get; set; } + + /// + /// The user who last modified the rule + /// + [JsonPropertyName("modifiedBy")] + public string ModifiedBy { get; set; } + } +} diff --git a/src/Commands/Model/SharePoint/RuleAction.cs b/src/Commands/Model/SharePoint/RuleAction.cs new file mode 100644 index 000000000..14769db11 --- /dev/null +++ b/src/Commands/Model/SharePoint/RuleAction.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace PnP.PowerShell.Commands.Model.SharePoint +{ + /// + /// Represents the actions to execute when a SharePoint rule is triggered + /// + public class RuleAction + { + /// + /// The type of action to perform (e.g., "sendEmail", "createAlert") + /// + [JsonPropertyName("actionType")] + public string ActionType { get; set; } + + /// + /// The email addresses to send notifications to + /// + [JsonPropertyName("emailRecipients")] + public List EmailRecipients { get; set; } + + /// + /// The subject line for email notifications + /// + [JsonPropertyName("emailSubject")] + public string EmailSubject { get; set; } + + /// + /// The body content for email notifications + /// + [JsonPropertyName("emailBody")] + public string EmailBody { get; set; } + + /// + /// Additional parameters for the action + /// + [JsonPropertyName("parameters")] + public Dictionary Parameters { get; set; } + } +} diff --git a/src/Commands/Model/SharePoint/RuleTrigger.cs b/src/Commands/Model/SharePoint/RuleTrigger.cs new file mode 100644 index 000000000..83b398bf9 --- /dev/null +++ b/src/Commands/Model/SharePoint/RuleTrigger.cs @@ -0,0 +1,28 @@ +using System.Text.Json.Serialization; + +namespace PnP.PowerShell.Commands.Model.SharePoint +{ + /// + /// Represents the trigger conditions for a SharePoint rule + /// + public class RuleTrigger + { + /// + /// The type of event that triggers the rule (e.g., "create", "update", "delete") + /// + [JsonPropertyName("eventType")] + public string EventType { get; set; } + + /// + /// Optional conditions that must be met for the rule to trigger + /// + [JsonPropertyName("condition")] + public string Condition { get; set; } + + /// + /// Specifies which fields to monitor for changes + /// + [JsonPropertyName("fieldValues")] + public object FieldValues { get; set; } + } +} diff --git a/src/Commands/Rules/AddListRule.cs b/src/Commands/Rules/AddListRule.cs new file mode 100644 index 000000000..b17b0e4b5 --- /dev/null +++ b/src/Commands/Rules/AddListRule.cs @@ -0,0 +1,109 @@ +using Microsoft.SharePoint.Client; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Base.Completers; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Model.SharePoint; +using PnP.PowerShell.Commands.Utilities.REST; +using System; +using System.Collections.Generic; +using System.Management.Automation; +using System.Text.Json; + +namespace PnP.PowerShell.Commands.Rules +{ + [Cmdlet(VerbsCommon.Add, "PnPListRule")] + [OutputType(typeof(Rule))] + public class AddListRule : PnPWebCmdlet + { + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] + [ArgumentCompleter(typeof(ListNameCompleter))] + public ListPipeBind List { get; set; } + + [Parameter(Mandatory = true)] + public string Title { get; set; } + + [Parameter(Mandatory = false)] + public string Description { get; set; } + + [Parameter(Mandatory = true)] + public string TriggerEventType { get; set; } + + [Parameter(Mandatory = true)] + public string ActionType { get; set; } + + [Parameter(Mandatory = false)] + public string[] EmailRecipients { get; set; } + + [Parameter(Mandatory = false)] + public string EmailSubject { get; set; } + + [Parameter(Mandatory = false)] + public string EmailBody { get; set; } + + [Parameter(Mandatory = false)] + public string Condition { get; set; } + + [Parameter(Mandatory = false)] + public SwitchParameter Enabled = true; + + protected override void ExecuteCmdlet() + { + var list = List?.GetList(CurrentWeb); + if (list == null) + { + throw new PSArgumentException("Unable to retrieve the specified list", nameof(List)); + } + + list.EnsureProperty(l => l.Id); + + try + { + // Build the rule object + var rule = new + { + title = Title, + description = Description ?? string.Empty, + isEnabled = Enabled.ToBool(), + triggerCondition = new + { + eventType = TriggerEventType, + condition = Condition ?? string.Empty + }, + actionParameters = new + { + actionType = ActionType, + emailRecipients = EmailRecipients ?? Array.Empty(), + emailSubject = EmailSubject ?? string.Empty, + emailBody = EmailBody ?? string.Empty + } + }; + + var jsonContent = JsonSerializer.Serialize(rule, new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }); + + // Call the CreateRuleEx endpoint + var endpoint = $"web/lists(guid'{list.Id}')/CreateRuleEx"; + var response = RestHelper.ExecutePostRequest(ClientContext, endpoint, jsonContent); + var responseContent = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + + // Parse and return the created rule + var createdRule = JsonSerializer.Deserialize(responseContent, new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }); + + WriteObject(createdRule); + } + catch (Exception ex) + { + WriteError(new ErrorRecord( + new Exception($"Failed to create rule: {ex.Message}", ex), + "FailedToCreateRule", + ErrorCategory.WriteError, + list)); + } + } + } +} diff --git a/src/Commands/Rules/GetListRule.cs b/src/Commands/Rules/GetListRule.cs new file mode 100644 index 000000000..0689f6f6f --- /dev/null +++ b/src/Commands/Rules/GetListRule.cs @@ -0,0 +1,106 @@ +using Microsoft.SharePoint.Client; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Base.Completers; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Model.SharePoint; +using PnP.PowerShell.Commands.Utilities.REST; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation; +using System.Text.Json; + +namespace PnP.PowerShell.Commands.Rules +{ + [Cmdlet(VerbsCommon.Get, "PnPListRule")] + [OutputType(typeof(Rule))] + public class GetListRule : PnPWebCmdlet + { + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] + [ArgumentCompleter(typeof(ListNameCompleter))] + public ListPipeBind List { get; set; } + + [Parameter(Mandatory = false)] + public RulePipeBind Identity { get; set; } + + protected override void ExecuteCmdlet() + { + var list = List?.GetList(CurrentWeb); + if (list == null) + { + throw new PSArgumentException("Unable to retrieve the specified list", nameof(List)); + } + + list.EnsureProperty(l => l.Id); + + try + { + // Call the GetAllRules endpoint + var endpoint = $"web/lists(guid'{list.Id}')/GetAllRules"; + var response = RestHelper.ExecutePostRequest(ClientContext, endpoint, "{}"); + var responseContent = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + + // Parse the response + var jsonDoc = JsonDocument.Parse(responseContent); + var rules = new List(); + + if (jsonDoc.RootElement.TryGetProperty("value", out var valueElement)) + { + foreach (var ruleElement in valueElement.EnumerateArray()) + { + var rule = JsonSerializer.Deserialize(ruleElement.GetRawText(), new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }); + rules.Add(rule); + } + } + else if (jsonDoc.RootElement.TryGetProperty("d", out var dElement) && dElement.TryGetProperty("GetAllRules", out var rulesElement)) + { + foreach (var ruleElement in rulesElement.EnumerateArray()) + { + var rule = JsonSerializer.Deserialize(ruleElement.GetRawText(), new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }); + rules.Add(rule); + } + } + + // Filter by Identity if specified + if (Identity != null) + { + if (Identity.Id != Guid.Empty) + { + var rule = rules.FirstOrDefault(r => r.RuleId == Identity.Id); + if (rule != null) + { + WriteObject(rule); + } + else + { + WriteWarning($"Rule with ID '{Identity.Id}' not found"); + } + } + else if (!string.IsNullOrEmpty(Identity.Title)) + { + var matchingRules = rules.Where(r => r.Title.Equals(Identity.Title, StringComparison.OrdinalIgnoreCase)).ToList(); + WriteObject(matchingRules, true); + } + } + else + { + WriteObject(rules, true); + } + } + catch (Exception ex) + { + WriteError(new ErrorRecord( + new Exception($"Failed to retrieve rules: {ex.Message}", ex), + "FailedToRetrieveRules", + ErrorCategory.ReadError, + list)); + } + } + } +} diff --git a/src/Commands/Rules/RemoveListRule.cs b/src/Commands/Rules/RemoveListRule.cs new file mode 100644 index 000000000..beb4fed11 --- /dev/null +++ b/src/Commands/Rules/RemoveListRule.cs @@ -0,0 +1,75 @@ +using Microsoft.SharePoint.Client; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Base.Completers; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Utilities.REST; +using System; +using System.Management.Automation; +using System.Text.Json; + +namespace PnP.PowerShell.Commands.Rules +{ + [Cmdlet(VerbsCommon.Remove, "PnPListRule")] + [OutputType(typeof(void))] + public class RemoveListRule : PnPWebCmdlet + { + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] + [ArgumentCompleter(typeof(ListNameCompleter))] + public ListPipeBind List { get; set; } + + [Parameter(Mandatory = true)] + public RulePipeBind Identity { get; set; } + + [Parameter(Mandatory = false)] + public SwitchParameter Force { get; set; } + + protected override void ExecuteCmdlet() + { + var list = List?.GetList(CurrentWeb); + if (list == null) + { + throw new PSArgumentException("Unable to retrieve the specified list", nameof(List)); + } + + list.EnsureProperty(l => l.Id); + + if (Identity == null || Identity.Id == Guid.Empty) + { + throw new PSArgumentException("Identity must be specified with a valid Rule ID", nameof(Identity)); + } + + if (!Force && !ShouldContinue($"Remove rule '{Identity.Id}' from list '{list.Title}'?", Properties.Resources.Confirm)) + { + return; + } + + try + { + // Build the request body + var deleteData = new + { + ruleId = Identity.Id + }; + + var jsonContent = JsonSerializer.Serialize(deleteData, new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }); + + // Call the DeleteRule endpoint + var endpoint = $"web/lists(guid'{list.Id}')/DeleteRule"; + RestHelper.ExecutePostRequest(ClientContext, endpoint, jsonContent); + + WriteVerbose($"Rule '{Identity.Id}' successfully removed from list '{list.Title}'"); + } + catch (Exception ex) + { + WriteError(new ErrorRecord( + new Exception($"Failed to remove rule: {ex.Message}", ex), + "FailedToRemoveRule", + ErrorCategory.WriteError, + list)); + } + } + } +} diff --git a/src/Commands/Rules/SetListRule.cs b/src/Commands/Rules/SetListRule.cs new file mode 100644 index 000000000..7bbca0d25 --- /dev/null +++ b/src/Commands/Rules/SetListRule.cs @@ -0,0 +1,137 @@ +using Microsoft.SharePoint.Client; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Base.Completers; +using PnP.PowerShell.Commands.Base.PipeBinds; +using PnP.PowerShell.Commands.Model.SharePoint; +using PnP.PowerShell.Commands.Utilities.REST; +using System; +using System.Management.Automation; +using System.Text.Json; + +namespace PnP.PowerShell.Commands.Rules +{ + [Cmdlet(VerbsCommon.Set, "PnPListRule")] + [OutputType(typeof(Rule))] + public class SetListRule : PnPWebCmdlet + { + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] + [ArgumentCompleter(typeof(ListNameCompleter))] + public ListPipeBind List { get; set; } + + [Parameter(Mandatory = true)] + public RulePipeBind Identity { get; set; } + + [Parameter(Mandatory = false)] + public string Title { get; set; } + + [Parameter(Mandatory = false)] + public string Description { get; set; } + + [Parameter(Mandatory = false)] + public string TriggerEventType { get; set; } + + [Parameter(Mandatory = false)] + public string ActionType { get; set; } + + [Parameter(Mandatory = false)] + public string[] EmailRecipients { get; set; } + + [Parameter(Mandatory = false)] + public string EmailSubject { get; set; } + + [Parameter(Mandatory = false)] + public string EmailBody { get; set; } + + [Parameter(Mandatory = false)] + public string Condition { get; set; } + + [Parameter(Mandatory = false)] + public bool? Enabled { get; set; } + + protected override void ExecuteCmdlet() + { + var list = List?.GetList(CurrentWeb); + if (list == null) + { + throw new PSArgumentException("Unable to retrieve the specified list", nameof(List)); + } + + list.EnsureProperty(l => l.Id); + + if (Identity == null || Identity.Id == Guid.Empty) + { + throw new PSArgumentException("Identity must be specified with a valid Rule ID", nameof(Identity)); + } + + try + { + // Build the update object with only specified parameters + var updateData = new System.Collections.Generic.Dictionary(); + + if (ParameterSpecified(nameof(Title))) + updateData["title"] = Title; + + if (ParameterSpecified(nameof(Description))) + updateData["description"] = Description; + + if (ParameterSpecified(nameof(Enabled))) + updateData["isEnabled"] = Enabled.Value; + + if (ParameterSpecified(nameof(TriggerEventType)) || ParameterSpecified(nameof(Condition))) + { + var triggerCondition = new System.Collections.Generic.Dictionary(); + if (ParameterSpecified(nameof(TriggerEventType))) + triggerCondition["eventType"] = TriggerEventType; + if (ParameterSpecified(nameof(Condition))) + triggerCondition["condition"] = Condition; + + updateData["triggerCondition"] = triggerCondition; + } + + if (ParameterSpecified(nameof(ActionType)) || ParameterSpecified(nameof(EmailRecipients)) || + ParameterSpecified(nameof(EmailSubject)) || ParameterSpecified(nameof(EmailBody))) + { + var actionParameters = new System.Collections.Generic.Dictionary(); + if (ParameterSpecified(nameof(ActionType))) + actionParameters["actionType"] = ActionType; + if (ParameterSpecified(nameof(EmailRecipients))) + actionParameters["emailRecipients"] = EmailRecipients; + if (ParameterSpecified(nameof(EmailSubject))) + actionParameters["emailSubject"] = EmailSubject; + if (ParameterSpecified(nameof(EmailBody))) + actionParameters["emailBody"] = EmailBody; + + updateData["actionParameters"] = actionParameters; + } + + updateData["ruleId"] = Identity.Id; + + var jsonContent = JsonSerializer.Serialize(updateData, new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }); + + // Call the UpdateRule endpoint + var endpoint = $"web/lists(guid'{list.Id}')/UpdateRule"; + var response = RestHelper.ExecutePostRequest(ClientContext, endpoint, jsonContent); + var responseContent = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + + // Parse and return the updated rule + var updatedRule = JsonSerializer.Deserialize(responseContent, new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }); + + WriteObject(updatedRule); + } + catch (Exception ex) + { + WriteError(new ErrorRecord( + new Exception($"Failed to update rule: {ex.Message}", ex), + "FailedToUpdateRule", + ErrorCategory.WriteError, + list)); + } + } + } +} diff --git a/src/Tests/Rules/AddPnPListRuleTests.cs b/src/Tests/Rules/AddPnPListRuleTests.cs new file mode 100644 index 000000000..ca3a95bac --- /dev/null +++ b/src/Tests/Rules/AddPnPListRuleTests.cs @@ -0,0 +1,79 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Management.Automation.Runspaces; + +namespace PnP.PowerShell.Tests.Rules +{ + [TestClass] + public class AddListRuleTests + { + #region Test Setup/CleanUp + [ClassInitialize] + public static void Initialize(TestContext testContext) + { + // This runs on class level once before all tests run + } + + [ClassCleanup] + public static void Cleanup(TestContext testContext) + { + // This runs on class level once + } + + [TestInitialize] + public void Initialize() + { + using (var scope = new PSTestScope()) + { + // Example + // scope.ExecuteCommand("cmdlet", new CommandParameter("param1", prop)); + } + } + + [TestCleanup] + public void Cleanup() + { + using (var scope = new PSTestScope()) + { + try + { + // Do Test Cleanup - Note, this runs PER test + } + catch (Exception) + { + // Describe Exception + } + } + } + #endregion + + #region Scaffolded Cmdlet Tests + //TODO: This is a scaffold of the cmdlet - complete the unit test + //[TestMethod] + public void AddPnPListRuleTest() + { + using (var scope = new PSTestScope(true)) + { + // Complete writing cmd parameters + + // From Cmdlet Help: The ID, Title or Url of the list. + var list = ""; + // From Cmdlet Help: The title/name of the rule. + var title = ""; + // From Cmdlet Help: The type of event that triggers the rule. + var triggerEventType = ""; + // From Cmdlet Help: The type of action to perform when the rule is triggered. + var actionType = ""; + + var results = scope.ExecuteCommand("Add-PnPListRule", + new CommandParameter("List", list), + new CommandParameter("Title", title), + new CommandParameter("TriggerEventType", triggerEventType), + new CommandParameter("ActionType", actionType)); + + Assert.IsNotNull(results); + } + } + #endregion + } +} diff --git a/src/Tests/Rules/GetPnPListRuleTests.cs b/src/Tests/Rules/GetPnPListRuleTests.cs new file mode 100644 index 000000000..e6f6d95fe --- /dev/null +++ b/src/Tests/Rules/GetPnPListRuleTests.cs @@ -0,0 +1,73 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Management.Automation.Runspaces; + +namespace PnP.PowerShell.Tests.Rules +{ + [TestClass] + public class GetListRuleTests + { + #region Test Setup/CleanUp + [ClassInitialize] + public static void Initialize(TestContext testContext) + { + // This runs on class level once before all tests run + } + + [ClassCleanup] + public static void Cleanup(TestContext testContext) + { + // This runs on class level once + } + + [TestInitialize] + public void Initialize() + { + using (var scope = new PSTestScope()) + { + // Example + // scope.ExecuteCommand("cmdlet", new CommandParameter("param1", prop)); + } + } + + [TestCleanup] + public void Cleanup() + { + using (var scope = new PSTestScope()) + { + try + { + // Do Test Cleanup - Note, this runs PER test + } + catch (Exception) + { + // Describe Exception + } + } + } + #endregion + + #region Scaffolded Cmdlet Tests + //TODO: This is a scaffold of the cmdlet - complete the unit test + //[TestMethod] + public void GetPnPListRuleTest() + { + using (var scope = new PSTestScope(true)) + { + // Complete writing cmd parameters + + // From Cmdlet Help: The ID, Title or Url of the list. + var list = ""; + // From Cmdlet Help: The ID or Title of the rule to retrieve. + var identity = ""; + + var results = scope.ExecuteCommand("Get-PnPListRule", + new CommandParameter("List", list), + new CommandParameter("Identity", identity)); + + Assert.IsNotNull(results); + } + } + #endregion + } +} diff --git a/src/Tests/Rules/RemovePnPListRuleTests.cs b/src/Tests/Rules/RemovePnPListRuleTests.cs new file mode 100644 index 000000000..44c58ae3b --- /dev/null +++ b/src/Tests/Rules/RemovePnPListRuleTests.cs @@ -0,0 +1,73 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Management.Automation.Runspaces; + +namespace PnP.PowerShell.Tests.Rules +{ + [TestClass] + public class RemoveListRuleTests + { + #region Test Setup/CleanUp + [ClassInitialize] + public static void Initialize(TestContext testContext) + { + // This runs on class level once before all tests run + } + + [ClassCleanup] + public static void Cleanup(TestContext testContext) + { + // This runs on class level once + } + + [TestInitialize] + public void Initialize() + { + using (var scope = new PSTestScope()) + { + // Example + // scope.ExecuteCommand("cmdlet", new CommandParameter("param1", prop)); + } + } + + [TestCleanup] + public void Cleanup() + { + using (var scope = new PSTestScope()) + { + try + { + // Do Test Cleanup - Note, this runs PER test + } + catch (Exception) + { + // Describe Exception + } + } + } + #endregion + + #region Scaffolded Cmdlet Tests + //TODO: This is a scaffold of the cmdlet - complete the unit test + //[TestMethod] + public void RemovePnPListRuleTest() + { + using (var scope = new PSTestScope(true)) + { + // Complete writing cmd parameters + + // From Cmdlet Help: The ID, Title or Url of the list. + var list = ""; + // From Cmdlet Help: The ID or Title of the rule to remove. + var identity = ""; + + var results = scope.ExecuteCommand("Remove-PnPListRule", + new CommandParameter("List", list), + new CommandParameter("Identity", identity)); + + Assert.IsNotNull(results); + } + } + #endregion + } +} diff --git a/src/Tests/Rules/SetPnPListRuleTests.cs b/src/Tests/Rules/SetPnPListRuleTests.cs new file mode 100644 index 000000000..6b062b266 --- /dev/null +++ b/src/Tests/Rules/SetPnPListRuleTests.cs @@ -0,0 +1,73 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Management.Automation.Runspaces; + +namespace PnP.PowerShell.Tests.Rules +{ + [TestClass] + public class SetListRuleTests + { + #region Test Setup/CleanUp + [ClassInitialize] + public static void Initialize(TestContext testContext) + { + // This runs on class level once before all tests run + } + + [ClassCleanup] + public static void Cleanup(TestContext testContext) + { + // This runs on class level once + } + + [TestInitialize] + public void Initialize() + { + using (var scope = new PSTestScope()) + { + // Example + // scope.ExecuteCommand("cmdlet", new CommandParameter("param1", prop)); + } + } + + [TestCleanup] + public void Cleanup() + { + using (var scope = new PSTestScope()) + { + try + { + // Do Test Cleanup - Note, this runs PER test + } + catch (Exception) + { + // Describe Exception + } + } + } + #endregion + + #region Scaffolded Cmdlet Tests + //TODO: This is a scaffold of the cmdlet - complete the unit test + //[TestMethod] + public void SetPnPListRuleTest() + { + using (var scope = new PSTestScope(true)) + { + // Complete writing cmd parameters + + // From Cmdlet Help: The ID, Title or Url of the list. + var list = ""; + // From Cmdlet Help: The ID or Title of the rule to update. + var identity = ""; + + var results = scope.ExecuteCommand("Set-PnPListRule", + new CommandParameter("List", list), + new CommandParameter("Identity", identity)); + + Assert.IsNotNull(results); + } + } + #endregion + } +} From 37b3577beb8f7c0e5d2253e08782f14c73f97ff1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Oct 2025 11:47:44 +0000 Subject: [PATCH 3/4] Add changelog entry for SharePoint Rules cmdlets Co-authored-by: KoenZomers <5940670+KoenZomers@users.noreply.github.com> --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b20d9c10..eb2fff8ac 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 `-KnowledgeAgentEnabled` and `-KnowledgeAgentSelectedSitesList` parameter to `Set-PnPTenant` cmdlets to support knowledge agents. - Added `-Force` parameter to `Remove-PnPTerm` cmdlet to remove terms without confirmation. - Added `Import-PnPFlow` cmdlet to import Power Automate in the tenant. [#4854](https://github.com/pnp/powershell/pull/4854) +- Added `Get-PnPListRule`, `Add-PnPListRule`, `Set-PnPListRule` and `Remove-PnPListRule` cmdlets to manage SharePoint Rules as a replacement for the retiring SharePoint Alerts feature [#5123](https://github.com/pnp/powershell/pull/5123) ### Changed - Improved `Get-PnPTerm` cmdlet to show a better error message. [#4933](https://github.com/pnp/powershell/pull/4933) From 723becbdc107d0e6c763aa656032f03fb02d1fb1 Mon Sep 17 00:00:00 2001 From: KoenZomers Date: Fri, 17 Oct 2025 14:54:56 +0200 Subject: [PATCH 4/4] Fixed some things. Still not entirely working. --- .../{RulePipeBind.cs => ListRulePipeBind.cs} | 22 ++--- .../Model/SharePoint/{Rule.cs => ListRule.cs} | 8 +- .../{RuleAction.cs => ListRuleAction.cs} | 2 +- .../{RuleTrigger.cs => ListRuleTrigger.cs} | 2 +- src/Commands/Rules/AddListRule.cs | 80 ++++++++++++++----- src/Commands/Rules/GetListRule.cs | 10 +-- src/Commands/Rules/RemoveListRule.cs | 6 +- src/Commands/Rules/SetListRule.cs | 6 +- 8 files changed, 91 insertions(+), 45 deletions(-) rename src/Commands/Base/PipeBinds/{RulePipeBind.cs => ListRulePipeBind.cs} (52%) rename src/Commands/Model/SharePoint/{Rule.cs => ListRule.cs} (90%) rename src/Commands/Model/SharePoint/{RuleAction.cs => ListRuleAction.cs} (97%) rename src/Commands/Model/SharePoint/{RuleTrigger.cs => ListRuleTrigger.cs} (96%) diff --git a/src/Commands/Base/PipeBinds/RulePipeBind.cs b/src/Commands/Base/PipeBinds/ListRulePipeBind.cs similarity index 52% rename from src/Commands/Base/PipeBinds/RulePipeBind.cs rename to src/Commands/Base/PipeBinds/ListRulePipeBind.cs index 644139064..c8adbaaa4 100644 --- a/src/Commands/Base/PipeBinds/RulePipeBind.cs +++ b/src/Commands/Base/PipeBinds/ListRulePipeBind.cs @@ -3,17 +3,25 @@ namespace PnP.PowerShell.Commands.Base.PipeBinds { - public sealed class RulePipeBind + public sealed class ListRulePipeBind { + public ListRule ListRuleInstance { get; private set; } private readonly Guid _id; private readonly string _title; - public RulePipeBind(Guid guid) + public ListRulePipeBind(Guid guid) { _id = guid; } - public RulePipeBind(string input) + public ListRulePipeBind(ListRule listRule) + { + ListRuleInstance = listRule ?? throw new ArgumentNullException(nameof(listRule)); + _id = listRule.RuleId; + _title = listRule.Title; + } + + public ListRulePipeBind(string input) { if (Guid.TryParse(input, out Guid guid)) { @@ -25,16 +33,10 @@ public RulePipeBind(string input) } } - public RulePipeBind(Rule rule) - { - _id = rule.RuleId; - _title = rule.Title; - } - public Guid Id => _id; public string Title => _title; - public RulePipeBind() + public ListRulePipeBind() { _id = Guid.Empty; } diff --git a/src/Commands/Model/SharePoint/Rule.cs b/src/Commands/Model/SharePoint/ListRule.cs similarity index 90% rename from src/Commands/Model/SharePoint/Rule.cs rename to src/Commands/Model/SharePoint/ListRule.cs index a1bb021b1..00cd14050 100644 --- a/src/Commands/Model/SharePoint/Rule.cs +++ b/src/Commands/Model/SharePoint/ListRule.cs @@ -6,12 +6,12 @@ namespace PnP.PowerShell.Commands.Model.SharePoint /// /// Represents a SharePoint list or library rule /// - public class Rule + public class ListRule { /// /// The unique identifier of the rule /// - [JsonPropertyName("ruleId")] + [JsonPropertyName("ID")] public Guid RuleId { get; set; } /// @@ -36,13 +36,13 @@ public class Rule /// The trigger conditions for the rule /// [JsonPropertyName("triggerCondition")] - public RuleTrigger TriggerCondition { get; set; } + public ListRuleTrigger TriggerCondition { get; set; } /// /// The actions to execute when the rule is triggered /// [JsonPropertyName("actionParameters")] - public RuleAction ActionParameters { get; set; } + public ListRuleAction ActionParameters { get; set; } /// /// The creation date of the rule diff --git a/src/Commands/Model/SharePoint/RuleAction.cs b/src/Commands/Model/SharePoint/ListRuleAction.cs similarity index 97% rename from src/Commands/Model/SharePoint/RuleAction.cs rename to src/Commands/Model/SharePoint/ListRuleAction.cs index 14769db11..e41e7c893 100644 --- a/src/Commands/Model/SharePoint/RuleAction.cs +++ b/src/Commands/Model/SharePoint/ListRuleAction.cs @@ -6,7 +6,7 @@ namespace PnP.PowerShell.Commands.Model.SharePoint /// /// Represents the actions to execute when a SharePoint rule is triggered /// - public class RuleAction + public class ListRuleAction { /// /// The type of action to perform (e.g., "sendEmail", "createAlert") diff --git a/src/Commands/Model/SharePoint/RuleTrigger.cs b/src/Commands/Model/SharePoint/ListRuleTrigger.cs similarity index 96% rename from src/Commands/Model/SharePoint/RuleTrigger.cs rename to src/Commands/Model/SharePoint/ListRuleTrigger.cs index 83b398bf9..805ff6110 100644 --- a/src/Commands/Model/SharePoint/RuleTrigger.cs +++ b/src/Commands/Model/SharePoint/ListRuleTrigger.cs @@ -5,7 +5,7 @@ namespace PnP.PowerShell.Commands.Model.SharePoint /// /// Represents the trigger conditions for a SharePoint rule /// - public class RuleTrigger + public class ListRuleTrigger { /// /// The type of event that triggers the rule (e.g., "create", "update", "delete") diff --git a/src/Commands/Rules/AddListRule.cs b/src/Commands/Rules/AddListRule.cs index b17b0e4b5..43e895985 100644 --- a/src/Commands/Rules/AddListRule.cs +++ b/src/Commands/Rules/AddListRule.cs @@ -6,13 +6,14 @@ using PnP.PowerShell.Commands.Utilities.REST; using System; using System.Collections.Generic; +using System.Linq; using System.Management.Automation; using System.Text.Json; namespace PnP.PowerShell.Commands.Rules { [Cmdlet(VerbsCommon.Add, "PnPListRule")] - [OutputType(typeof(Rule))] + [OutputType(typeof(ListRule))] public class AddListRule : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] @@ -22,9 +23,6 @@ public class AddListRule : PnPWebCmdlet [Parameter(Mandatory = true)] public string Title { get; set; } - [Parameter(Mandatory = false)] - public string Description { get; set; } - [Parameter(Mandatory = true)] public string TriggerEventType { get; set; } @@ -58,23 +56,69 @@ protected override void ExecuteCmdlet() try { - // Build the rule object + // Build notification receivers in the expected format + var notificationReceivers = new List(); + if (EmailRecipients != null) + { + foreach (var email in EmailRecipients) + { + notificationReceivers.Add(new + { + name = email, // Using email as name for now - could be enhanced to parse display names + email = email, + userId = $"i:0#.f|membership|{email}" // Standard claims format for email users + }); + } + } + + var actionParams = new List(); + + // Add notification receivers if any + if (notificationReceivers.Count > 0) + { + actionParams.Add(new + { + Key = "NotificationReceivers", + Value = System.Text.Json.JsonSerializer.Serialize(notificationReceivers), + ValueType = "String" + }); + } + + // Add custom message if email body is provided + if (!string.IsNullOrEmpty(EmailBody)) + { + actionParams.Add(new + { + Key = "CustomMessage", + Value = $"'{EmailBody}'", // Wrapped in quotes as shown in sample + ValueType = "String" + }); + } + + // Add email subject if provided + if (!string.IsNullOrEmpty(EmailSubject)) + { + actionParams.Add(new + { + Key = "EmailSubject", + Value = EmailSubject, + ValueType = "String" + }); + } + + // Build the rule object to match the expected API format var rule = new { title = Title, - description = Description ?? string.Empty, - isEnabled = Enabled.ToBool(), - triggerCondition = new - { - eventType = TriggerEventType, - condition = Condition ?? string.Empty - }, - actionParameters = new + condition = Condition ?? "true", // Default to "true" as shown in sample + triggerType = int.TryParse(TriggerEventType, out int triggerTypeValue) ? triggerTypeValue : 0, + action = new { - actionType = ActionType, - emailRecipients = EmailRecipients ?? Array.Empty(), - emailSubject = EmailSubject ?? string.Empty, - emailBody = EmailBody ?? string.Empty + ActionType = int.TryParse(ActionType, out int actionTypeValue) ? actionTypeValue : 0, + ActionParams = new + { + results = actionParams.ToArray() + } } }; @@ -89,7 +133,7 @@ protected override void ExecuteCmdlet() var responseContent = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); // Parse and return the created rule - var createdRule = JsonSerializer.Deserialize(responseContent, new JsonSerializerOptions + var createdRule = JsonSerializer.Deserialize(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); diff --git a/src/Commands/Rules/GetListRule.cs b/src/Commands/Rules/GetListRule.cs index 0689f6f6f..f0b277dd7 100644 --- a/src/Commands/Rules/GetListRule.cs +++ b/src/Commands/Rules/GetListRule.cs @@ -13,7 +13,7 @@ namespace PnP.PowerShell.Commands.Rules { [Cmdlet(VerbsCommon.Get, "PnPListRule")] - [OutputType(typeof(Rule))] + [OutputType(typeof(ListRule))] public class GetListRule : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] @@ -21,7 +21,7 @@ public class GetListRule : PnPWebCmdlet public ListPipeBind List { get; set; } [Parameter(Mandatory = false)] - public RulePipeBind Identity { get; set; } + public ListRulePipeBind Identity { get; set; } protected override void ExecuteCmdlet() { @@ -42,13 +42,13 @@ protected override void ExecuteCmdlet() // Parse the response var jsonDoc = JsonDocument.Parse(responseContent); - var rules = new List(); + var rules = new List(); if (jsonDoc.RootElement.TryGetProperty("value", out var valueElement)) { foreach (var ruleElement in valueElement.EnumerateArray()) { - var rule = JsonSerializer.Deserialize(ruleElement.GetRawText(), new JsonSerializerOptions + var rule = JsonSerializer.Deserialize(ruleElement.GetRawText(), new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); @@ -59,7 +59,7 @@ protected override void ExecuteCmdlet() { foreach (var ruleElement in rulesElement.EnumerateArray()) { - var rule = JsonSerializer.Deserialize(ruleElement.GetRawText(), new JsonSerializerOptions + var rule = JsonSerializer.Deserialize(ruleElement.GetRawText(), new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); diff --git a/src/Commands/Rules/RemoveListRule.cs b/src/Commands/Rules/RemoveListRule.cs index beb4fed11..139abf41d 100644 --- a/src/Commands/Rules/RemoveListRule.cs +++ b/src/Commands/Rules/RemoveListRule.cs @@ -13,12 +13,12 @@ namespace PnP.PowerShell.Commands.Rules [OutputType(typeof(void))] public class RemoveListRule : PnPWebCmdlet { - [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] + [Parameter(Mandatory = false, ValueFromPipeline = false, Position = 1)] [ArgumentCompleter(typeof(ListNameCompleter))] public ListPipeBind List { get; set; } - [Parameter(Mandatory = true)] - public RulePipeBind Identity { get; set; } + [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] + public ListRulePipeBind Identity { get; set; } [Parameter(Mandatory = false)] public SwitchParameter Force { get; set; } diff --git a/src/Commands/Rules/SetListRule.cs b/src/Commands/Rules/SetListRule.cs index 7bbca0d25..bf4ecabe2 100644 --- a/src/Commands/Rules/SetListRule.cs +++ b/src/Commands/Rules/SetListRule.cs @@ -11,7 +11,7 @@ namespace PnP.PowerShell.Commands.Rules { [Cmdlet(VerbsCommon.Set, "PnPListRule")] - [OutputType(typeof(Rule))] + [OutputType(typeof(ListRule))] public class SetListRule : PnPWebCmdlet { [Parameter(Mandatory = true, ValueFromPipeline = true, Position = 0)] @@ -19,7 +19,7 @@ public class SetListRule : PnPWebCmdlet public ListPipeBind List { get; set; } [Parameter(Mandatory = true)] - public RulePipeBind Identity { get; set; } + public ListRulePipeBind Identity { get; set; } [Parameter(Mandatory = false)] public string Title { get; set; } @@ -117,7 +117,7 @@ protected override void ExecuteCmdlet() var responseContent = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); // Parse and return the updated rule - var updatedRule = JsonSerializer.Deserialize(responseContent, new JsonSerializerOptions + var updatedRule = JsonSerializer.Deserialize(responseContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });