Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PreviousNode parameter to Add-PnPNavigationNode #2940

Merged
merged 4 commits into from
Apr 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
- Added `DisableDocumentLibraryDefaultLabeling`, `DisableListSync`, `IsEnableAppAuthPopUpEnabled`, `ExpireVersionsAfterDays`, `MajorVersionLimit` and `EnableAutoExpirationVersionTrim`, `OneDriveLoopSharingCapability`, `OneDriveLoopDefaultSharingLinkScope`, `OneDriveLoopDefaultSharingLinkRole`, `CoreLoopSharingCapability`, `CoreLoopDefaultSharingLinkScope`, `CoreLoopDefaultSharingLinkRole` , `DisableVivaConnectionsAnalytics` , `CoreDefaultLinkToExistingAccess`, `HideSyncButtonOnTeamSite` , `CoreBlockGuestsAsSiteAdmin`, `IsWBFluidEnabled`, `IsCollabMeetingNotesFluidEnabled`, `AllowAnonymousMeetingParticipantsToAccessWhiteboards`, `IBImplicitGroupBased`, `ShowOpenInDesktopOptionForSyncedFiles` and `ShowPeoplePickerGroupSuggestionsForIB` parameters to the `Set-PnPTenant` cmdlet. [#2979](https://github.com/pnp/powershell/pull/2979)
- Added `-OutFile` to `Invoke-PnPGraphMethod` which allows for the response to be written to a file [#2971](https://github.com/pnp/powershell/pull/2971)
- Added `-OutStream` to `Invoke-PnPGraphMethod` which allows for the response to be written to a memory stream [#2976](https://github.com/pnp/powershell/pull/2976)
- Added `-PreviousNode` to `Add-PnPNavigationNode` which allows for adding a navigation node after a specific node [#2940](https://github.com/pnp/powershell/pull/2940)

### Fixed

- Fixed issue with `Grant-PnPAzureADAppSitePermission` cmdlet where users are not able to set selected site in the `Sites.Selected` permission. [#2983](https://github.com/pnp/powershell/pull/2983)
- Fixed issue with `Get-PnPList` cmdlet not working with site-relative URL as identity. [#3005](https://github.com/pnp/powershell/pull/3005)
- Fixed issue with `Add-PnPNavigationNode` cmdlet where the target audience would not correctly be set when creating a node as a child of a parent node [#2940](https://github.com/pnp/powershell/pull/2940)

### Contributors

- [reusto]
- [dhiabedoui]
- Koen Zomers [koenzomers]

Expand Down
36 changes: 32 additions & 4 deletions documentation/Add-PnPNavigationNode.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,16 @@ Adds an item to a navigation element

## SYNTAX

### Default

```powershell
Add-PnPNavigationNode -Location <NavigationType> -Title <String> [-Url <String>] [-Parent <NavigationNodePipeBind>] [-First] [-External] [-AudienceIds <Guid[]>] [-Connection <PnPConnection>]
```

### Provide PreviousNode

```powershell
Add-PnPNavigationNode -Location <NavigationType> -Title <String> [-Url <String>] [-Parent <Int32>] [-First] [-External] [-AudienceIds <Guid[]>] [-Connection <PnPConnection>]
Add-PnPNavigationNode -Location <NavigationType> -Title <String> -PreviousNode <NavigationNodePipeBind> [-Url <String>] [-Parent <NavigationNodePipeBind>] [-External] [-AudienceIds <Guid[]>] [-Connection <PnPConnection>]
```

## DESCRIPTION
Expand Down Expand Up @@ -65,6 +73,12 @@ Add-PnPNavigationNode -Title "Label" -Location "TopNavigationBar" -Url "http://l

Adds a navigation node to the top navigation bar. The navigation node will be created as a label.

### EXAMPLE 7
```powershell
Add-PnPNavigationNode -Title "Wiki" -Location "QuickLaunch" -Url "wiki/" -PreviousNode 2012
```
Adds a navigation node to the quicklaunch. The navigation node will have the title "Wiki" and will link to the Wiki library on the selected Web after the node with the ID 2012.

## PARAMETERS

### -Connection
Expand Down Expand Up @@ -100,7 +114,7 @@ Add the new menu item to beginning of the collection

```yaml
Type: SwitchParameter
Parameter Sets: (All)
Parameter Sets: Default

Required: False
Position: Named
Expand All @@ -125,10 +139,10 @@ Accept wildcard characters: False
```

### -Parent
The key of the parent. Leave empty to add to the top level
The parent navigation node. Leave empty to add to the top level

```yaml
Type: Int32
Type: NavigationNodePipeBind
Parameter Sets: (All)

Required: False
Expand All @@ -138,6 +152,20 @@ Accept pipeline input: False
Accept wildcard characters: False
```

### -PreviousNode
Specifies the navigation node after which the new navigation node will appear in the navigation node collection.

```yaml
Type: NavigationNodePipeBind
Parameter Sets: Add node after another node

Required: True
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```

### -Title
The title of the node to add

Expand Down
23 changes: 21 additions & 2 deletions src/Commands/Base/PipeBinds/NavigationNodePipeBind.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.SharePoint.Client;
using PnP.PowerShell.Commands.Branding;
using System;
using System.Collections.Generic;
using System.Linq;
Expand All @@ -10,6 +11,7 @@ namespace PnP.PowerShell.Commands.Base.PipeBinds
public class NavigationNodePipeBind
{
private int _id;
private NavigationNode _node;

public NavigationNodePipeBind(int id)
{
Expand All @@ -18,9 +20,26 @@ public NavigationNodePipeBind(int id)

public NavigationNodePipeBind(NavigationNode node)
{
_id = node.Id;
_node = node;
}

public int Id => _id;
internal NavigationNode GetNavigationNode(Web web)
{
NavigationNode node = null;
if (_node != null)
{
node = _node;
} else {
node = web.Navigation.GetNodeById(_id);
}

if (node != null)
{
web.Context.Load(node);
web.Context.ExecuteQueryRetry();
}

return node;
}
}
}
136 changes: 77 additions & 59 deletions src/Commands/Navigation/AddNavigationNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,54 @@
using System.Management.Automation;
using Microsoft.SharePoint.Client;
using PnP.Framework.Enums;
using PnP.PowerShell.Commands.Base.PipeBinds;

namespace PnP.PowerShell.Commands.Branding
{
[Cmdlet(VerbsCommon.Add, "PnPNavigationNode")]
[Cmdlet(VerbsCommon.Add, "PnPNavigationNode", DefaultParameterSetName = ParameterSet_Default)]
[OutputType(typeof(NavigationNode))]
public class AddNavigationNode : PnPWebCmdlet
{
private const string ParameterSet_Default = "Default";
private const string ParameterSet_PreviousNode = "Add node after another node";

[Parameter(ParameterSetName = ParameterSet_Default)]
[Parameter(ParameterSetName = ParameterSet_PreviousNode)]
[Parameter(Mandatory = true)]
public NavigationType Location;

[Parameter(ParameterSetName = ParameterSet_Default)]
[Parameter(ParameterSetName = ParameterSet_PreviousNode)]
[Parameter(Mandatory = true)]
public string Title;

[Parameter(ParameterSetName = ParameterSet_Default)]
[Parameter(ParameterSetName = ParameterSet_PreviousNode)]
[Parameter(Mandatory = false)]
public string Url;

[Parameter(ParameterSetName = ParameterSet_Default)]
[Parameter(ParameterSetName = ParameterSet_PreviousNode)]
[Parameter(Mandatory = false)]
public int? Parent;
public NavigationNodePipeBind Parent;

[Parameter(ParameterSetName = ParameterSet_Default)]
[Parameter(Mandatory = false)]
public SwitchParameter First;

[Parameter(ParameterSetName = ParameterSet_Default)]
[Parameter(ParameterSetName = ParameterSet_PreviousNode)]
[Parameter(Mandatory = false)]
public SwitchParameter External;

[Parameter(ParameterSetName = ParameterSet_Default)]
[Parameter(ParameterSetName = ParameterSet_PreviousNode)]
[Parameter(Mandatory = false)]
public List<Guid> AudienceIds;

[Parameter(Mandatory = true, ParameterSetName = ParameterSet_PreviousNode)]
public NavigationNodePipeBind PreviousNode;

protected override void ExecuteCmdlet()
{
if (Url == null)
Expand All @@ -39,72 +59,70 @@ protected override void ExecuteCmdlet()
ClientContext.ExecuteQueryRetry();
Url = CurrentWeb.Url;
}
if (Parent.HasValue)

var navigationNodeCreationInformation = new NavigationNodeCreationInformation {
Title = Title,
Url = Url,
IsExternal = External.IsPresent,
};

if (ParameterSpecified(nameof(PreviousNode)))
{
var parentNode = CurrentWeb.Navigation.GetNodeById(Parent.Value);
ClientContext.Load(parentNode);
ClientContext.ExecuteQueryRetry();
var addedNode = parentNode.Children.Add(new NavigationNodeCreationInformation()
{
Title = Title,
Url = Url,
IsExternal = External.IsPresent,
AsLastNode = !First.IsPresent
});
ClientContext.Load(addedNode);
ClientContext.ExecuteQueryRetry();
WriteObject(addedNode);
navigationNodeCreationInformation.PreviousNode = PreviousNode.GetNavigationNode(CurrentWeb);
} else
{
navigationNodeCreationInformation.AsLastNode = !First.IsPresent;
}

NavigationNodeCollection nodeCollection = null;
if (ParameterSpecified(nameof(Parent)))
{
var parentNode = Parent.GetNavigationNode(CurrentWeb);
nodeCollection = parentNode.Children;
CurrentWeb.Context.Load(nodeCollection);
CurrentWeb.Context.ExecuteQueryRetry();
}
else if (Location == NavigationType.SearchNav)
{
nodeCollection = CurrentWeb.LoadSearchNavigation();
}
else if (Location == NavigationType.Footer)
{
nodeCollection = CurrentWeb.LoadFooterNavigation();
}
else
{
NavigationNodeCollection nodeCollection = null;
if (Location == NavigationType.SearchNav)
{
nodeCollection = CurrentWeb.LoadSearchNavigation();
}
else if (Location == NavigationType.Footer)
{
nodeCollection = CurrentWeb.LoadFooterNavigation();
}
else
{
nodeCollection = Location == NavigationType.QuickLaunch ? CurrentWeb.Navigation.QuickLaunch : CurrentWeb.Navigation.TopNavigationBar;
ClientContext.Load(nodeCollection);
}
if (nodeCollection != null)
nodeCollection = Location == NavigationType.QuickLaunch ? CurrentWeb.Navigation.QuickLaunch : CurrentWeb.Navigation.TopNavigationBar;
ClientContext.Load(nodeCollection);
}

if (nodeCollection != null)
{
var addedNode = nodeCollection.Add(navigationNodeCreationInformation);

if (ParameterSpecified(nameof(AudienceIds)))
{
var addedNode = nodeCollection.Add(new NavigationNodeCreationInformation
{
Title = Title,
Url = Url,
IsExternal = External.IsPresent,
AsLastNode = !First.IsPresent
});

if (ParameterSpecified(nameof(AudienceIds)))
{
addedNode.AudienceIds = AudienceIds;
addedNode.Update();
}

ClientContext.Load(addedNode);
ClientContext.ExecuteQueryRetry();

if (Location == NavigationType.QuickLaunch)
{
// Retrieve the menu definition and save it back again. This step is needed to enforce some properties of the menu to be shown, such as the audience targeting.
CurrentWeb.EnsureProperties(w => w.Url);
var menuState = Utilities.REST.RestHelper.GetAsync(Connection.HttpClient, $"{CurrentWeb.Url}/_api/navigation/MenuState", ClientContext, "application/json;odata=nometadata").GetAwaiter().GetResult();
Utilities.REST.RestHelper.PostAsync(Connection.HttpClient, $"{CurrentWeb.Url}/_api/navigation/SaveMenuState", ClientContext, @"{ ""menuState"": " + menuState + "}", "application/json", "application/json;odata=nometadata").GetAwaiter().GetResult();
}

WriteObject(addedNode);
addedNode.AudienceIds = AudienceIds;
addedNode.Update();
}
else

ClientContext.Load(addedNode);
ClientContext.ExecuteQueryRetry();

if (Location == NavigationType.QuickLaunch)
{
throw new Exception("Navigation Node Collection is null");
// Retrieve the menu definition and save it back again. This step is needed to enforce some properties of the menu to be shown, such as the audience targeting.
CurrentWeb.EnsureProperties(w => w.Url);
var menuState = Utilities.REST.RestHelper.GetAsync(Connection.HttpClient, $"{CurrentWeb.Url}/_api/navigation/MenuState", ClientContext, "application/json;odata=nometadata").GetAwaiter().GetResult();
Utilities.REST.RestHelper.PostAsync(Connection.HttpClient, $"{CurrentWeb.Url}/_api/navigation/SaveMenuState", ClientContext, @"{ ""menuState"": " + menuState + "}", "application/json", "application/json;odata=nometadata").GetAwaiter().GetResult();
}

WriteObject(addedNode);
}
else
{
throw new Exception("Unable to define Navigation Node collection to add the node to");
}
}
}
}
2 changes: 1 addition & 1 deletion src/Commands/Navigation/RemoveNavigationNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ protected override void ExecuteCmdlet()
{
if (ParameterSetName == ParameterSet_BYID)
{
var node = CurrentWeb.Navigation.GetNodeById(Identity.Id);
var node = Identity.GetNavigationNode(CurrentWeb);
node.DeleteObject();
ClientContext.ExecuteQueryRetry();
}
Expand Down