Skip to content

Commit e65c62e

Browse files
committed
Added Stream option
1 parent 5767acf commit e65c62e

File tree

3 files changed

+105
-17
lines changed

3 files changed

+105
-17
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
6868
- Added `Get-PnPBrandCenterFontPackage` to retrieve the available font packages from the various Brand Centers [#4830](https://github.com/pnp/powershell/pull/4830)
6969
- Added `Use-PnPBrandCenterFontPackage` to apply the specified font package from the Brand Center to the current site [#4830](https://github.com/pnp/powershell/pull/4830)
7070
- Added `Add-PnPBrandCenterFont` to upload a font to the tenant Brand Center [#4830](https://github.com/pnp/powershell/pull/4830)
71+
- Added `-Stream` parameter to `Invoke-PnPSiteTemplate` which allows an in memory .pnp site template to be applied to a site
7172

7273
### Changed
7374

documentation/Invoke-PnPSiteTemplate.md

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Applies a site template to a web
1616

1717
### Path
1818
```powershell
19-
Invoke-PnPSiteTemplate [-Path] <String> [-TemplateId <String>] [-ResourceFolder <String>]
19+
Invoke-PnPSiteTemplate -Path <String> [-TemplateId <String>] [-ResourceFolder <String>]
2020
[-OverwriteSystemPropertyBagValues] [-IgnoreDuplicateDataRowErrors] [-ProvisionContentTypesToSubWebs]
2121
[-ProvisionFieldsToSubWebs] [-ClearNavigation] [-Parameters <Hashtable>] [-Handlers <Handlers>]
2222
[-ExcludeHandlers <Handlers>] [-ExtensibilityHandlers <ExtensibilityHandler[]>]
@@ -26,11 +26,21 @@ Invoke-PnPSiteTemplate [-Path] <String> [-TemplateId <String>] [-ResourceFolder
2626

2727
### Instance
2828
```powershell
29-
Invoke-PnPSiteTemplate [-TemplateId <String>] [-ResourceFolder <String>]
29+
Invoke-PnPSiteTemplate -InputInstance <SiteTemplate> [-TemplateId <String>] [-ResourceFolder <String>]
3030
[-OverwriteSystemPropertyBagValues] [-IgnoreDuplicateDataRowErrors] [-ProvisionContentTypesToSubWebs]
3131
[-ProvisionFieldsToSubWebs] [-ClearNavigation] [-Parameters <Hashtable>] [-Handlers <Handlers>]
3232
[-ExcludeHandlers <Handlers>] [-ExtensibilityHandlers <ExtensibilityHandler[]>]
33-
[-TemplateProviderExtensions <ITemplateProviderExtension[]>] [-InputInstance <SiteTemplate>]
33+
[-TemplateProviderExtensions <ITemplateProviderExtension[]>]
34+
[-Connection <PnPConnection>]
35+
```
36+
37+
### Stream
38+
```powershell
39+
Invoke-PnPSiteTemplate -Stream <Stream> [-TemplateId <String>] [-ResourceFolder <String>]
40+
[-OverwriteSystemPropertyBagValues] [-IgnoreDuplicateDataRowErrors] [-ProvisionContentTypesToSubWebs]
41+
[-ProvisionFieldsToSubWebs] [-ClearNavigation] [-Parameters <Hashtable>] [-Handlers <Handlers>]
42+
[-ExcludeHandlers <Handlers>] [-ExtensibilityHandlers <ExtensibilityHandler[]>]
43+
[-TemplateProviderExtensions <ITemplateProviderExtension[]>]
3444
[-Connection <PnPConnection>]
3545
```
3646

@@ -107,6 +117,14 @@ Invoke-PnPSiteTemplate -Path .\template.xml -TemplateId "MyTemplate"
107117

108118
Applies the SiteTemplate with the ID "MyTemplate" located in the template definition file template.xml.
109119

120+
### EXAMPLE 10
121+
```powershell
122+
$stream = Get-PnPFile -Url https://tenant.sharepoint.com/sites/TemplateGallery/Shared%20Documents/ProjectSite.pnp -AsMemoryStream
123+
Invoke-PnPSiteTemplate -Stream $stream
124+
```
125+
126+
Downloads the ProjectSite.pnp template from the TemplateGallery document library and applies it to the currently connected to site.
127+
110128
## PARAMETERS
111129

112130
### -ClearNavigation
@@ -293,6 +311,20 @@ Accept pipeline input: False
293311
Accept wildcard characters: False
294312
```
295313
314+
### -Stream
315+
Allows a stream to be passed in. This is useful when you want to apply a template that is stored in a stream, for example when you download it from SharePoint Online so you can keep it in memory and don't (temporarily) need to store it anywhere. It only supports .pnp files, not .xml files. It also supports having additional files stored in the .pnp file.
316+
317+
```yaml
318+
Type: Stream
319+
Parameter Sets: Stream
320+
321+
Required: True
322+
Position: Named
323+
Default value: None
324+
Accept pipeline input: False
325+
Accept wildcard characters: False
326+
```
327+
296328
### -TemplateId
297329
ID of the template to use from the xml file containing the provisioning template. If not specified and multiple SiteTemplate elements exist, the last one will be used.
298330

src/Commands/Provisioning/Site/InvokeSiteTemplate.cs

Lines changed: 69 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ public class InvokeSiteTemplate : PnPWebCmdlet
6262

6363
[Parameter(Mandatory = false, ParameterSetName = "Instance")]
6464
public ProvisioningTemplate InputInstance;
65+
66+
[Parameter(Mandatory = false, ParameterSetName = "Stream")]
67+
public MemoryStream Stream { get; set; }
6568

6669
protected override void ExecuteCmdlet()
6770
{
@@ -173,29 +176,75 @@ protected override void ExecuteCmdlet()
173176
}
174177
else
175178
{
176-
provisioningTemplate = InputInstance;
177-
178-
if (ResourceFolder != null)
179+
if (InputInstance == null && Stream != null)
179180
{
180-
var fileSystemConnector = new FileSystemConnector(ResourceFolder, "");
181-
provisioningTemplate.Connector = fileSystemConnector;
181+
LogDebug("Determining if template from provided stream is a .pnp package file");
182+
Stream.Position = 0;
183+
184+
var isOpenOfficeFile = FileUtilities.IsOpenOfficeFile(Stream);
185+
if (isOpenOfficeFile)
186+
{
187+
LogDebug("Package is a .pnp package file, loading template from provided stream");
188+
var openXmlConnector = new OpenXMLConnector(Stream);
189+
var provider = new XMLOpenXMLTemplateProvider(openXmlConnector);
190+
191+
var templates = ProvisioningHelper.LoadSiteTemplatesFromStream(Stream, TemplateProviderExtensions, LogError);
192+
193+
LogDebug($"Package stream contains {templates.Count} template{(templates.Count != 1 ? "s" : "")}");
194+
195+
if (ParameterSpecified(nameof(TemplateId)))
196+
{
197+
// If we have the -TemplateId parameter, we look for the template with that ID in the package stream
198+
LogDebug($"Looking for template with ID {TemplateId} in package stream");
199+
provisioningTemplate = templates.FirstOrDefault(t => t.Id == TemplateId);
200+
}
201+
else
202+
{
203+
// If we don't have the -TemplateId parameter, we use the last template in the package stream
204+
LogDebug($"No template ID specified, using the last template in package stream");
205+
provisioningTemplate = templates.LastOrDefault();
206+
}
207+
if (provisioningTemplate == null)
208+
{
209+
// If we don't have a template, raise an error and exit
210+
LogError("Unable to find template in provided stream. Please check the template ID or the content of the stream.");
211+
return;
212+
}
213+
provisioningTemplate.Connector = provider.Connector;
214+
}
215+
else
216+
{
217+
// XML template files have not been implemented to be used from a stream, only .pnp files
218+
throw new NotSupportedException("Only .pnp package files are supported in a stream");
219+
}
182220
}
183221
else
184222
{
185-
if (Path != null)
223+
provisioningTemplate = InputInstance;
224+
225+
if (ResourceFolder != null)
186226
{
187-
if (!System.IO.Path.IsPathRooted(Path))
188-
{
189-
Path = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Path);
190-
}
227+
var fileSystemConnector = new FileSystemConnector(ResourceFolder, "");
228+
provisioningTemplate.Connector = fileSystemConnector;
191229
}
192230
else
193231
{
194-
Path = SessionState.Path.CurrentFileSystemLocation.Path;
232+
if (Path != null)
233+
{
234+
if (!System.IO.Path.IsPathRooted(Path))
235+
{
236+
Path = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Path);
237+
}
238+
239+
var fileInfo = new FileInfo(Path);
240+
fileConnector = new FileSystemConnector(System.IO.Path.IsPathRooted(fileInfo.FullName) ? fileInfo.FullName : fileInfo.DirectoryName, "");
241+
provisioningTemplate.Connector = fileConnector;
242+
}
243+
else
244+
{
245+
Path = SessionState.Path.CurrentFileSystemLocation.Path;
246+
}
195247
}
196-
var fileInfo = new FileInfo(Path);
197-
fileConnector = new FileSystemConnector(System.IO.Path.IsPathRooted(fileInfo.FullName) ? fileInfo.FullName : fileInfo.DirectoryName, "");
198-
provisioningTemplate.Connector = fileConnector;
199248
}
200249
}
201250

@@ -327,6 +376,12 @@ protected override void ExecuteCmdlet()
327376
}
328377

329378
WriteProgress(new ProgressRecord(0, $"Applying template to {CurrentWeb.Url}", " ") { RecordType = ProgressRecordType.Completed });
379+
380+
if(Stream != null)
381+
{
382+
// Reset the stream position to 0 so it can be used again if needed
383+
Stream.Position = 0;
384+
}
330385
}
331386
}
332387
}

0 commit comments

Comments
 (0)