Skip to content
This repository was archived by the owner on Jan 19, 2021. It is now read-only.

Commit bf1d134

Browse files
committed
Feature: Add-PnPFileToProvisionningTemplate allow add a file from an url
1 parent 02ee1b3 commit bf1d134

1 file changed

Lines changed: 107 additions & 50 deletions

File tree

Lines changed: 107 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
using OfficeDevPnP.Core.Framework.Provisioning.Connectors;
1+
using Microsoft.SharePoint.Client;
2+
using Microsoft.SharePoint.Client.Utilities;
3+
using OfficeDevPnP.Core.Framework.Provisioning.Connectors;
24
using OfficeDevPnP.Core.Framework.Provisioning.Model;
35
using OfficeDevPnP.Core.Framework.Provisioning.Providers;
46
using OfficeDevPnP.Core.Framework.Provisioning.Providers.Xml;
57
using SharePointPnP.PowerShell.CmdletHelpAttributes;
68
using System;
7-
using System.Collections.Generic;
89
using System.IO;
910
using System.Linq;
1011
using System.Management.Automation;
11-
using System.Text;
12-
using System.Threading.Tasks;
12+
using System.Net;
13+
using PnPFileLevel = OfficeDevPnP.Core.Framework.Provisioning.Model.FileLevel;
14+
using SPFile = Microsoft.SharePoint.Client.File;
1315

1416
namespace SharePointPnP.PowerShell.Commands.Provisioning
1517
{
@@ -32,23 +34,29 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning
3234
Code = @"PS:> Add-PnPFileToProvisioningTemplate -Path template.pnp -Source $sourceFilePath -Folder $targetFolder -Container $container",
3335
Remarks = "Adds a file to a PnP Provisioning Template with a custom container for the file",
3436
SortOrder = 4)]
35-
36-
public class AddFileToProvisioningTemplate : PSCmdlet
37+
[CmdletExample(
38+
Code = @"PS:> Add-PnPFileToProvisioningTemplate -Path template.pnp -SourceUrl $urlOfFile",
39+
Remarks = "Adds a file to a PnP Provisioning Template retrieved from the currently connected web. The url can be either full, server relative or Web relative url.",
40+
SortOrder = 4)]
41+
public class AddFileToProvisioningTemplate : PnPWebCmdlet
3742
{
3843
[Parameter(Mandatory = true, Position = 0, HelpMessage = "Filename of the .PNP Open XML provisioning template to read from, optionally including full path.")]
3944
public string Path;
4045

41-
[Parameter(Mandatory = true, Position = 1, HelpMessage = "The file to add to the in-memory template, optionally including full path.")]
46+
[Parameter(Mandatory = true, Position = 1, ParameterSetName = "LocalSourceFile", HelpMessage = "The file to add to the in-memory template, optionally including full path.")]
4247
public string Source;
4348

44-
[Parameter(Mandatory = true, Position = 2, HelpMessage = "The target Folder for the file to add to the in-memory template.")]
49+
[Parameter(Mandatory = true, Position = 1, ParameterSetName = "RemoteSourceFile", HelpMessage = "The file to add to the in-memory template, specifying its url in the current connected Web.")]
50+
public string SourceUrl;
51+
52+
[Parameter(Mandatory = true, Position = 2, ParameterSetName = "LocalSourceFile", HelpMessage = "The target Folder for the file to add to the in-memory template.")]
4553
public string Folder;
4654

4755
[Parameter(Mandatory = false, Position = 3, HelpMessage = "The target Container for the file to add to the in-memory template, optional argument.")]
4856
public string Container;
4957

5058
[Parameter(Mandatory = false, Position = 4, HelpMessage = "The level of the files to add. Defaults to Published")]
51-
public FileLevel FileLevel = FileLevel.Published;
59+
public PnPFileLevel FileLevel = PnPFileLevel.Published;
5260

5361
[Parameter(Mandatory = false, Position = 5, HelpMessage = "Set to overwrite in site, Defaults to true")]
5462
public SwitchParameter FileOverwrite = true;
@@ -62,10 +70,6 @@ protected override void ProcessRecord()
6270
{
6371
Path = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Path);
6472
}
65-
if(!System.IO.Path.IsPathRooted(Source))
66-
{
67-
Source = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Source);
68-
}
6973
// Load the template
7074
var template = ReadProvisioningTemplate
7175
.LoadProvisioningTemplateFromFile(Path,
@@ -75,51 +79,104 @@ protected override void ProcessRecord()
7579
{
7680
throw new ApplicationException("Invalid template file!");
7781
}
78-
79-
// Load the file and add it to the .PNP file
80-
using (var fs = new FileStream(Source, FileMode.Open, FileAccess.Read, FileShare.Read))
82+
if (this.ParameterSetName == "RemoteSourceFile")
8183
{
82-
Folder = Folder.Replace("\\", "/");
83-
84-
var fileName = Source.IndexOf("\\") > 0 ? Source.Substring(Source.LastIndexOf("\\") + 1) : Source;
85-
var container = !string.IsNullOrEmpty(Container) ? Container : string.Empty;
86-
var source = !string.IsNullOrEmpty(container) ? (container + "/" + fileName) : fileName;
87-
88-
template.Connector.SaveFileStream(fileName, container, fs);
89-
90-
if (template.Connector is ICommitableFileConnector)
84+
SelectedWeb.EnsureProperty(w => w.ServerRelativeUrl);
85+
var sourceUri = new Uri(SourceUrl, UriKind.RelativeOrAbsolute);
86+
var serverRelativeUrl =
87+
sourceUri.IsAbsoluteUri ? sourceUri.AbsolutePath :
88+
SourceUrl.StartsWith("/", StringComparison.Ordinal) ? SourceUrl :
89+
SelectedWeb.ServerRelativeUrl.TrimEnd('/') + "/" + SourceUrl;
90+
91+
var file = SelectedWeb.GetFileByServerRelativeUrl(serverRelativeUrl);
92+
93+
var fileName = file.EnsureProperty(f => f.Name);
94+
var folderRelativeUrl = serverRelativeUrl.Substring(0, serverRelativeUrl.Length - fileName.Length - 1);
95+
var folderWebRelativeUrl = HttpUtility.UrlKeyValueDecode(folderRelativeUrl.Substring(SelectedWeb.ServerRelativeUrl.TrimEnd('/').Length + 1));
96+
if (ClientContext.HasPendingRequest) ClientContext.ExecuteQuery();
97+
try
9198
{
92-
((ICommitableFileConnector)template.Connector).Commit();
99+
using (var fi = SPFile.OpenBinaryDirect(ClientContext, serverRelativeUrl))
100+
using (var ms = new MemoryStream())
101+
{
102+
// We are using a temporary memory stream because the file connector is seeking in the stream
103+
// and the stream provided by OpenBinaryDirect does not allow it
104+
fi.Stream.CopyTo(ms);
105+
ms.Position = 0;
106+
AddFileToTemplate(template, ms, folderWebRelativeUrl, fileName, folderWebRelativeUrl);
107+
}
93108
}
94-
95-
template.Files.Add(new OfficeDevPnP.Core.Framework.Provisioning.Model.File
109+
catch (WebException exc)
96110
{
97-
Src = source,
98-
Folder = Folder,
99-
Level = FileLevel,
100-
Overwrite = FileOverwrite,
101-
});
102-
103-
// Determine the output file name and path
104-
var outFileName = System.IO.Path.GetFileName(Path);
105-
var outPath = new FileInfo(Path).DirectoryName;
106-
107-
var fileSystemConnector = new FileSystemConnector(outPath, "");
108-
var formatter = XMLPnPSchemaFormatter.LatestFormatter;
109-
var extension = new FileInfo(Path).Extension.ToLowerInvariant();
110-
if (extension == ".pnp")
111+
WriteWarning($"Can't add file from url {serverRelativeUrl} : {exc}");
112+
}
113+
}
114+
else
115+
{
116+
if (!System.IO.Path.IsPathRooted(Source))
111117
{
112-
var provider = new XMLOpenXMLTemplateProvider(template.Connector as OpenXMLConnector);
113-
var templateFileName = outFileName.Substring(0, outFileName.LastIndexOf(".", StringComparison.Ordinal)) + ".xml";
114-
115-
provider.SaveAs(template, templateFileName, formatter, TemplateProviderExtensions);
118+
Source = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Source);
116119
}
117-
else
120+
121+
// Load the file and add it to the .PNP file
122+
using (var fs = System.IO.File.OpenRead(Source))
118123
{
119-
XMLTemplateProvider provider = new XMLFileSystemTemplateProvider(Path, "");
120-
provider.SaveAs(template, Path, formatter, TemplateProviderExtensions);
124+
Folder = Folder.Replace("\\", "/");
125+
126+
var fileName = Source.IndexOf("\\", StringComparison.Ordinal) > 0 ? Source.Substring(Source.LastIndexOf("\\") + 1) : Source;
127+
var container = !string.IsNullOrEmpty(Container) ? Container : string.Empty;
128+
AddFileToTemplate(template, fs, Folder, fileName, container);
121129
}
122130
}
123131
}
132+
133+
private void AddFileToTemplate(ProvisioningTemplate template, Stream fs, string folder, string fileName, string container)
134+
{
135+
var source = !string.IsNullOrEmpty(container) ? (container + "/" + fileName) : fileName;
136+
137+
template.Connector.SaveFileStream(fileName, container, fs);
138+
139+
if (template.Connector is ICommitableFileConnector)
140+
{
141+
((ICommitableFileConnector)template.Connector).Commit();
142+
}
143+
144+
var existing = template.Files.FirstOrDefault(f =>
145+
f.Src == $"{container}/{fileName}"
146+
&& f.Folder == folder);
147+
148+
if (existing != null)
149+
template.Files.Remove(existing);
150+
151+
var newFile = new OfficeDevPnP.Core.Framework.Provisioning.Model.File
152+
{
153+
Src = source,
154+
Folder = folder,
155+
Level = FileLevel,
156+
Overwrite = FileOverwrite,
157+
};
158+
159+
template.Files.Add(newFile);
160+
161+
// Determine the output file name and path
162+
var outFileName = System.IO.Path.GetFileName(Path);
163+
var outPath = new FileInfo(Path).DirectoryName;
164+
165+
var fileSystemConnector = new FileSystemConnector(outPath, "");
166+
var formatter = XMLPnPSchemaFormatter.LatestFormatter;
167+
var extension = new FileInfo(Path).Extension.ToLowerInvariant();
168+
if (extension == ".pnp")
169+
{
170+
var provider = new XMLOpenXMLTemplateProvider(template.Connector as OpenXMLConnector);
171+
var templateFileName = outFileName.Substring(0, outFileName.LastIndexOf(".", StringComparison.Ordinal)) + ".xml";
172+
173+
provider.SaveAs(template, templateFileName, formatter, TemplateProviderExtensions);
174+
}
175+
else
176+
{
177+
XMLTemplateProvider provider = new XMLFileSystemTemplateProvider(Path, "");
178+
provider.SaveAs(template, Path, formatter, TemplateProviderExtensions);
179+
}
180+
}
124181
}
125-
}
182+
}

0 commit comments

Comments
 (0)