This repository was archived by the owner on Jan 19, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 648
Expand file tree
/
Copy pathAddFileToProvisioningTemplate.cs
More file actions
280 lines (243 loc) · 13.5 KB
/
AddFileToProvisioningTemplate.cs
File metadata and controls
280 lines (243 loc) · 13.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
using Microsoft.SharePoint.Client;
using Microsoft.SharePoint.Client.Utilities;
using OfficeDevPnP.Core.Framework.Provisioning.Connectors;
using OfficeDevPnP.Core.Framework.Provisioning.Model;
using OfficeDevPnP.Core.Framework.Provisioning.Providers;
using OfficeDevPnP.Core.Framework.Provisioning.Providers.Xml;
using SharePointPnP.PowerShell.CmdletHelpAttributes;
using SharePointPnP.PowerShell.Commands.Utilities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Management.Automation;
using System.Net;
using PnPFileLevel = OfficeDevPnP.Core.Framework.Provisioning.Model.FileLevel;
using SPFile = Microsoft.SharePoint.Client.File;
namespace SharePointPnP.PowerShell.Commands.Provisioning.Site
{
[Cmdlet(VerbsCommon.Add, "PnPFileToProvisioningTemplate")]
[CmdletHelp("Adds a file to a PnP Provisioning Template",
Category = CmdletHelpCategory.Provisioning)]
[CmdletExample(
Code = @"PS:> Add-PnPFileToProvisioningTemplate -Path template.pnp -Source $sourceFilePath -Folder $targetFolder",
Remarks = "Adds a file to a PnP Site Template",
SortOrder = 1)]
[CmdletExample(
Code = @"PS:> Add-PnPFileToProvisioningTemplate -Path template.xml -Source $sourceFilePath -Folder $targetFolder",
Remarks = "Adds a file reference to a PnP Site XML Template",
SortOrder = 2)]
[CmdletExample(
Code = @"PS:> Add-PnPFileToProvisioningTemplate -Path template.pnp -Source ""./myfile.png"" -Folder ""folderinsite"" -FileLevel Published -FileOverwrite:$false",
Remarks = "Adds a file to a PnP Site Template, specifies the level as Published and defines to not overwrite the file if it exists in the site.",
SortOrder = 3)]
[CmdletExample(
Code = @"PS:> Add-PnPFileToProvisioningTemplate -Path template.pnp -Source $sourceFilePath -Folder $targetFolder -Container $container",
Remarks = "Adds a file to a PnP Site Template with a custom container for the file",
SortOrder = 4)]
[CmdletExample(
Code = @"PS:> Add-PnPFileToProvisioningTemplate -Path template.pnp -SourceUrl ""Shared%20Documents/ProjectStatus.docs""",
Remarks = "Adds a file to a PnP Provisioning Template retrieved from the currently connected site. The url can be server relative or web relative. If specifying a server relative url has to start with the current site url.",
SortOrder = 5)]
[CmdletExample(
Code = @"PS:> Add-PnPFileToProvisioningTemplate -Path template.pnp -SourceUrl ""SitePages/Home.aspx"" -ExtractWebParts",
Remarks = "Adds a file to a PnP Provisioning Template retrieved from the currently connected site. If the file is a classic page, also extract its webparts. The url can be server relative or web relative. If specifying a server relative url has to start with the current site url.",
SortOrder = 6)]
public class AddFileToProvisioningTemplate : PnPWebCmdlet
{
const string parameterSet_LOCALFILE = "Local File";
const string parameterSet_REMOTEFILE = "Remote File";
[Parameter(Mandatory = true, Position = 0, HelpMessage = "Filename of the .PNP Open XML site template to read from, optionally including full path.")]
public string Path;
[Parameter(Mandatory = true, Position = 1, ParameterSetName = parameterSet_LOCALFILE, HelpMessage = "The file to add to the in-memory template, optionally including full path.")]
public string Source;
[Parameter(Mandatory = true, Position = 1, ParameterSetName = parameterSet_REMOTEFILE, HelpMessage = "The file to add to the in-memory template, specifying its url in the current connected Web.")]
public string SourceUrl;
[Parameter(Mandatory = true, Position = 2, ParameterSetName = parameterSet_LOCALFILE, HelpMessage = "The target Folder for the file to add to the in-memory template.")]
public string Folder;
[Parameter(Mandatory = false, Position = 3, HelpMessage = "The target Container for the file to add to the in-memory template, optional argument.")]
public string Container;
[Parameter(Mandatory = false, Position = 4, HelpMessage = "The level of the files to add. Defaults to Published")]
public PnPFileLevel FileLevel = PnPFileLevel.Published;
[Parameter(Mandatory = false, Position = 5, HelpMessage = "Set to overwrite in site, Defaults to true")]
public SwitchParameter FileOverwrite = true;
[Parameter(Mandatory = false, Position = 6, ParameterSetName = parameterSet_REMOTEFILE, HelpMessage = "Include webparts if the file is a page")]
public SwitchParameter ExtractWebParts = true;
[Parameter(Mandatory = false, Position = 4, HelpMessage = "Allows you to specify ITemplateProviderExtension to execute while loading the template.")]
public ITemplateProviderExtension[] TemplateProviderExtensions;
protected override void ProcessRecord()
{
if (!System.IO.Path.IsPathRooted(Path))
{
Path = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Path);
}
// Load the template
var template = ReadProvisioningTemplate
.LoadProvisioningTemplateFromFile(Path,
TemplateProviderExtensions, (e) =>
{
WriteError(new ErrorRecord(e, "TEMPLATENOTVALID", ErrorCategory.SyntaxError, null));
});
if (template == null)
{
throw new ApplicationException("Invalid template file!");
}
// Add a file from the connected Web
if (this.ParameterSetName == parameterSet_REMOTEFILE)
{
if (ExtractWebParts)
{
ClientContext.Load(SelectedWeb, web => web.Url, web => web.Id, web => web.ServerRelativeUrl);
ClientContext.Load(((ClientContext)SelectedWeb.Context).Site, site => site.Id, site => site.ServerRelativeUrl, site => site.Url);
ClientContext.Load(SelectedWeb.Lists, lists => lists.Include(l => l.Title, l => l.RootFolder.ServerRelativeUrl, l => l.Id));
}
ClientContext.ExecuteQuery();
var sourceUri = new Uri(SourceUrl, UriKind.RelativeOrAbsolute);
var serverRelativeUrl =
sourceUri.IsAbsoluteUri ? sourceUri.AbsolutePath :
SourceUrl.StartsWith("/", StringComparison.Ordinal) ? SourceUrl :
SelectedWeb.ServerRelativeUrl.TrimEnd('/') + "/" + SourceUrl;
var file = SelectedWeb.GetFileByServerRelativeUrl(serverRelativeUrl);
AddSPFileToTemplate(template, file);
}
// Add a file from the file system
else
{
if (!System.IO.Path.IsPathRooted(Source))
{
Source = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Source);
}
// Load the file and add it to the .PNP file
using (var fs = System.IO.File.OpenRead(Source))
{
Folder = Folder.Replace('\\', '/');
var fileName = Source.IndexOf(System.IO.Path.DirectorySeparatorChar) > 0
? Source.Substring(Source.LastIndexOf(System.IO.Path.DirectorySeparatorChar) + 1)
: Source;
var container = !string.IsNullOrEmpty(Container) ? Container : string.Empty;
AddFileToTemplate(template, fs, Folder, fileName, container);
}
}
}
private void AddSPFileToTemplate(ProvisioningTemplate template, SPFile file)
{
if (template == null) throw new ArgumentNullException(nameof(template));
if (file == null) throw new ArgumentNullException(nameof(file));
file.EnsureProperties(f => f.Name, f => f.ServerRelativeUrl);
var serverRelativeUrl = file.ServerRelativeUrl;
var fileName = file.Name;
var folderRelativeUrl = serverRelativeUrl.Substring(0, serverRelativeUrl.Length - fileName.Length - 1);
var folderWebRelativeUrl = HttpUtility.UrlKeyValueDecode(folderRelativeUrl.Substring(SelectedWeb.ServerRelativeUrl.TrimEnd('/').Length + 1));
try
{
#if SP2013 || SP2016
var fi = SelectedWeb.GetFileByServerRelativeUrl(serverRelativeUrl);
#else
var fi = SelectedWeb.GetFileByServerRelativePath(ResourcePath.FromDecodedUrl(serverRelativeUrl));
#endif
IEnumerable<WebPart> webParts = null;
if (ExtractWebParts)
{
webParts = ExtractSPFileWebParts(file).ToArray();
}
var fileStream = fi.OpenBinaryStream();
ClientContext.ExecuteQueryRetry();
using (var ms = fileStream.Value)
{
AddFileToTemplate(template, ms, folderWebRelativeUrl, fileName, folderWebRelativeUrl, webParts);
}
}
catch (WebException exc)
{
WriteWarning($"Can't add file from url {serverRelativeUrl} : {exc}");
}
}
private IEnumerable<WebPart> ExtractSPFileWebParts(SPFile file)
{
if (file == null) throw new ArgumentNullException(nameof(file));
if (string.Compare(System.IO.Path.GetExtension(file.Name), ".aspx", true) == 0)
{
foreach (var spwp in SelectedWeb.GetWebParts(file.ServerRelativeUrl))
{
spwp.EnsureProperties(wp => wp.WebPart, wp => wp.ZoneId);
yield return new WebPart
{
Contents = Tokenize(SelectedWeb.GetWebPartXml(spwp.Id, file.ServerRelativeUrl)),
Order = (uint)spwp.WebPart.ZoneIndex,
Title = spwp.WebPart.Title,
Zone = spwp.ZoneId
};
}
}
}
private string Tokenize(string input)
{
if (string.IsNullOrEmpty(input)) return input;
foreach (var list in SelectedWeb.Lists)
{
var webRelativeUrl = list.GetWebRelativeUrl();
if (!webRelativeUrl.StartsWith("_catalogs", StringComparison.Ordinal))
{
input = input
.ReplaceCaseInsensitive(list.Id.ToString("D"), "{listid:" + list.Title + "}")
.ReplaceCaseInsensitive(webRelativeUrl, "{listurl:" + list.Title + "}");
}
}
return input.ReplaceCaseInsensitive(SelectedWeb.Url, "{site}")
.ReplaceCaseInsensitive(SelectedWeb.ServerRelativeUrl, "{site}")
.ReplaceCaseInsensitive(SelectedWeb.Id.ToString(), "{siteid}")
.ReplaceCaseInsensitive(((ClientContext)SelectedWeb.Context).Site.ServerRelativeUrl, "{sitecollection}")
.ReplaceCaseInsensitive(((ClientContext)SelectedWeb.Context).Site.Id.ToString(), "{sitecollectionid}")
.ReplaceCaseInsensitive(((ClientContext)SelectedWeb.Context).Site.Url, "{sitecollection}");
}
private void AddFileToTemplate(
ProvisioningTemplate template,
Stream fs,
string folder,
string fileName,
string container,
IEnumerable<WebPart> webParts = null
)
{
if (template == null) throw new ArgumentNullException(nameof(template));
if (fs == null) throw new ArgumentNullException(nameof(fs));
var source = !string.IsNullOrEmpty(container) ? (container + "/" + fileName) : fileName;
template.Connector.SaveFileStream(fileName, container, fs);
if (template.Connector is ICommitableFileConnector)
{
((ICommitableFileConnector)template.Connector).Commit();
}
var existing = template.Files.FirstOrDefault(f =>
f.Src == $"{container}/{fileName}"
&& f.Folder == folder);
if (existing != null)
template.Files.Remove(existing);
var newFile = new OfficeDevPnP.Core.Framework.Provisioning.Model.File
{
Src = source,
Folder = folder,
Level = FileLevel,
Overwrite = FileOverwrite,
};
if (webParts != null) newFile.WebParts.AddRange(webParts);
template.Files.Add(newFile);
// Determine the output file name and path
var outFileName = System.IO.Path.GetFileName(Path);
var outPath = new FileInfo(Path).DirectoryName;
var fileSystemConnector = new FileSystemConnector(outPath, "");
var formatter = XMLPnPSchemaFormatter.LatestFormatter;
var extension = new FileInfo(Path).Extension.ToLowerInvariant();
if (extension == ".pnp")
{
var provider = new XMLOpenXMLTemplateProvider(template.Connector as OpenXMLConnector);
var templateFileName = outFileName.Substring(0, outFileName.LastIndexOf(".", StringComparison.Ordinal)) + ".xml";
provider.SaveAs(template, templateFileName, formatter, TemplateProviderExtensions);
}
else
{
XMLTemplateProvider provider = new XMLFileSystemTemplateProvider(Path, "");
provider.SaveAs(template, Path, formatter, TemplateProviderExtensions);
}
}
}
}