Skip to content

Commit

Permalink
Merge pull request #1606 from gautamdsheth/feature/603
Browse files Browse the repository at this point in the history
Feature #603 - added cmdlet to rename site URL
  • Loading branch information
KoenZomers authored Feb 8, 2022
2 parents 993ac52 + 36bea2d commit 8658f97
Show file tree
Hide file tree
Showing 4 changed files with 384 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
- Added `Publish\Unpublish-PnPContentType` to allow for content types to be published or unpublished on hub sites [#1597](https://github.com/pnp/powershell/pull/1597)
- Added `Get-PnPContentTypePublishingStatus` to get te current publication state of a content type in the content type hub site [#1597](https://github.com/pnp/powershell/pull/1597)
- Added ability to pipe the output of `Get-PnPTenantDeletedSite` to either `Restore-PnPTenantDeletedSite` or `Remove-PnPTenantDeletedSite` [#1596](https://github.com/pnp/powershell/pull/1596)
- Added `Rename-PnPTenantSite` to rename a SharePoint Online site URL [#1606](https://github.com/pnp/powershell/pull/1606)

### Changed

Expand Down
153 changes: 153 additions & 0 deletions documentation/Rename-PnPTenantSite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
---
Module Name: PnP.PowerShell
title: Rename-PnPTenantSite
schema: 2.0.0
applicable: SharePoint Online
external help file: PnP.PowerShell.dll-Help.xml
online version: https://pnp.github.io/powershell/cmdlets/Rename-PnPTenantSite.html
---

# Rename-PnPTenantSite

## SYNOPSIS
This command starts a rename of a site on a SharePoint Online site. You can change the URL, and optionally the site title along with changing the URL.

This will not work for Multi-geo environments.

## SYNTAX

```powershell
Rename-PnPTenantSite [[-Identity] <SPOSitePipeBind>] [[-NewSiteUrl] <String>] [[-NewSiteTitle] <string>]
[[-SuppressMarketplaceAppCheck] [<SwitchParameter>]] [[-SuppressWorkflow2013Check] [<SwitchParameter>]] [-Connection <PnPConnection>] [<CommonParameters>]
```

## DESCRIPTION

## EXAMPLES

### EXAMPLE 1
```powershell
$currentSiteUrl = "https://<tenant>.sharepoint.com/site/samplesite"
$updatedSiteUrl = "https://<tenant>.sharepoint.com/site/renamed"
Rename-PnPTenantSite -Identity $currentSiteUrl -NewSiteUrl $updatedSiteUrl
```

Starts the rename of the SharePoint Online site with name "samplesite" to "renamed" without modifying the title.

## 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
Specifies the full URL of the SharePoint Online site collection that needs to be renamed.
```yaml
Type: SPOSitePipeBind
Parameter Sets: (All)

Required: False
Position: Named
Default value: None
Accept pipeline input: True
Accept wildcard characters: False
```
### -NewSiteUrl
Specifies the full URL of the SharePoint Online site collection to which it needs to be renamed.
```yaml
Type: String
Parameter Sets: (All)

Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -NewSiteTitle
Specifies the new title of of the SharePoint Site.
```yaml
Type: String
Parameter Sets: (All)

Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -SuppressMarketplaceAppCheck
Suppress checking compatibility of marketplace SharePoint Add-ins deployed to the associated site.
```yaml
Type: SwitchParameter
Parameter Sets: (All)

Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -SuppressWorkflow2013Check
Suppress checking compatibility of SharePoint 2013 Workflows deployed to the associated site.
```yaml
Type: SwitchParameter
Parameter Sets: (All)

Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -SuppressBcsCheck
Suppress checking compatibility of BCS connections deployed to the associated site.
```yaml
Type: SwitchParameter
Parameter Sets: (All)

Required: False
Position: Named
Default value: None
Accept pipeline input: True
Accept wildcard characters: False
```
### -Wait
Wait till the renaming of the new site collection is successfull. If not specified, a job will be created which you can use to check for its status.
```yaml
Type: SwitchParameter
Parameter Sets: (All)

Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
## RELATED LINKS
[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp)
155 changes: 155 additions & 0 deletions src/Commands/Admin/RenameTenantSite.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
using Microsoft.SharePoint.Client;
using PnP.Framework.Http;
using PnP.PowerShell.Commands.Base;
using PnP.PowerShell.Commands.Base.PipeBinds;
using PnP.PowerShell.Commands.Model;
using PnP.PowerShell.Commands.Utilities;
using System;
using System.Collections.Generic;
using System.Management.Automation;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;

namespace PnP.PowerShell.Commands.Admin
{
[Cmdlet(VerbsCommon.Rename, "PnPTenantSite")]
public class RenameTenantSite : PnPAdminCmdlet
{
[Parameter(Mandatory = true)]
[ValidateNotNullOrEmpty]
public SPOSitePipeBind Identity { get; set; }

[Parameter(Mandatory = true)]
[ValidateNotNullOrEmpty]
public string NewSiteUrl { get; set; }

[Parameter(Mandatory = false)]
[ValidateNotNullOrEmpty]
public string NewSiteTitle { get; set; }

[Parameter(Mandatory = false)]
public SwitchParameter SuppressMarketplaceAppCheck { get; set; }

[Parameter(Mandatory = false)]
public SwitchParameter SuppressWorkflow2013Check { get; set; }

[Parameter(Mandatory = false)]
public SwitchParameter SuppressBcsCheck { get; set; }

[Parameter(Mandatory = false)]
public SwitchParameter Wait { get; set; }

protected override void ExecuteCmdlet()
{
ClientContext.ExecuteQueryRetry(); // fixes issue where ServerLibraryVersion is not available.

int optionsBitMask = 0;
if (SuppressMarketplaceAppCheck.IsPresent)
{
optionsBitMask |= 8;
}
if (SuppressWorkflow2013Check.IsPresent)
{
optionsBitMask |= 16;
}
if (SuppressBcsCheck.IsPresent)
{
optionsBitMask |= 128;
}

var body = new
{
SourceSiteUrl = Identity.Url,
TargetSiteUrl = NewSiteUrl,
TargetSiteTitle = NewSiteTitle ?? null,
Option = optionsBitMask,
Reserve = string.Empty,
OperationId = Guid.Empty
};

var tenantUrl = UrlUtilities.GetTenantAdministrationUrl(ClientContext.Url);

var results = Utilities.REST.RestHelper.PostAsync<SPOSiteRenameJob>(HttpClient, $"{tenantUrl.TrimEnd('/')}/_api/SiteRenameJobs?api-version=1.4.7", ClientContext, body, false).GetAwaiter().GetResult();
if (!Wait.IsPresent)
{
if (results != null)
{
WriteObject(results);
}
}
else
{
bool wait = true;
var iterations = 0;

var method = new HttpMethod("GET");

var httpClient = PnPHttpClient.Instance.GetHttpClient(ClientContext);

var requestUrl = $"{tenantUrl.TrimEnd('/')}/_api/SiteRenameJobs/GetJobsBySiteUrl(url='{Identity.Url}')?api-version=1.4.7";

while (wait)
{
iterations++;
try
{
using (HttpRequestMessage request = new HttpRequestMessage(method, requestUrl))
{
request.Headers.Add("accept", "application/json;odata=nometadata");
request.Headers.Add("X-AttemptNumber", iterations.ToString());
PnPHttpClient.AuthenticateRequestAsync(request, ClientContext).GetAwaiter().GetResult();

HttpResponseMessage response = httpClient.SendAsync(request, new System.Threading.CancellationToken()).Result;

if (response.IsSuccessStatusCode)
{
var responseString = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
if (responseString != null)
{
var jsonElement = JsonSerializer.Deserialize<JsonElement>(responseString);

if (jsonElement.TryGetProperty("value", out JsonElement valueProperty))
{
var siteRenameResults = JsonSerializer.Deserialize<List<SPOSiteRenameJob>>(valueProperty.ToString());

if (siteRenameResults != null && siteRenameResults.Count > 0)
{
var siteRenameResponse = siteRenameResults[0];
if (!string.IsNullOrEmpty(siteRenameResponse.ErrorDescription))
{
wait = false;
throw new PSInvalidOperationException(siteRenameResponse.ErrorDescription);
}
if (siteRenameResponse.JobState == "Success")
{
wait = false;
WriteObject(siteRenameResponse);
}
else
{
Task.Delay(TimeSpan.FromSeconds(30)).GetAwaiter().GetResult();
}
}
}
}
}
}
}
catch (Exception)
{
if (iterations * 30 >= 300)
{
wait = false;
throw;
}
else
{
Task.Delay(TimeSpan.FromSeconds(30)).GetAwaiter().GetResult();
}
}
}
}
}
}
}
75 changes: 75 additions & 0 deletions src/Commands/Model/SPOSiteRename.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System;

namespace PnP.PowerShell.Commands.Model
{
/// <summary>
/// Contains information about an ongoing SharePoint Online site collection rename
/// </summary>
public class SPOSiteRenameJob
{
/// <summary>
/// State the rename process is in
/// </summary>
public string JobState { get; set; }

/// <summary>
/// Id of the site that is being renamed
/// </summary>
public Guid? SiteId { get; set; }

/// <summary>
/// Unique identifier of the rename job
/// </summary>
public Guid? JobId { get; set; }

/// <summary>
/// Unknown
/// </summary>
public Guid? ParentId { get; set; }

/// <summary>
/// Person or process having initiated the rename
/// </summary>
public string TriggeredBy { get; set; }

/// <summary>
/// Error code, if any
/// </summary>
public int? ErrorCode { get; set; }

/// <summary>
/// Error description, if any
/// </summary>
public string ErrorDescription { get; set; }

/// <summary>
/// Url of the site collection before the rename
/// </summary>
public string SourceSiteUrl { get; set; }

/// <summary>
/// Url of the site collection after the rename
/// </summary>
public string TargetSiteUrl { get; set; }

/// <summary>
/// Unknown
/// </summary>
public object TargetSiteTitle { get; set; }

/// <summary>
/// Unknown
/// </summary>
public int? Option { get; set; }

/// <summary>
/// Unknown
/// </summary>
public object Reserve { get; set; }

/// <summary>
/// Unknown
/// </summary>
public object SkipGestures { get; set; }
}
}

0 comments on commit 8658f97

Please sign in to comment.