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

Commit 2cea22f

Browse files
committed
Merge
2 parents bf1d134 + a4ed0c8 commit 2cea22f

4 files changed

+305
-121
lines changed
+160
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
using Microsoft.SharePoint.Client;
2+
using Microsoft.SharePoint.Client.Utilities;
3+
using OfficeDevPnP.Core.Framework.Provisioning.Connectors;
4+
using OfficeDevPnP.Core.Framework.Provisioning.Model;
5+
using OfficeDevPnP.Core.Framework.Provisioning.Providers;
6+
using OfficeDevPnP.Core.Framework.Provisioning.Providers.Xml;
7+
using SharePointPnP.PowerShell.Commands.Provisioning;
8+
using System;
9+
using System.IO;
10+
using System.Linq;
11+
using System.Management.Automation;
12+
using System.Net;
13+
using PnPFileLevel = OfficeDevPnP.Core.Framework.Provisioning.Model.FileLevel;
14+
using SPFile = Microsoft.SharePoint.Client.File;
15+
16+
namespace SharePointPnP.PowerShell.Commands
17+
{
18+
/// <summary>
19+
/// Base class for commands related to adding file to template
20+
/// </summary>
21+
public class BaseFileProvisioningCmdlet : PnPWebCmdlet
22+
{
23+
protected const string PSNAME_LOCAL_SOURCE = "LocalSourceFile";
24+
protected const string PSNAME_REMOTE_SOURCE = "RemoteSourceFile";
25+
26+
[Parameter(Mandatory = true, Position = 0, HelpMessage = "Filename of the .PNP Open XML provisioning template to read from, optionally including full path.")]
27+
public string Path;
28+
29+
[Parameter(Mandatory = false, Position = 3, HelpMessage = "The target Container for the file to add to the in-memory template, optional argument.")]
30+
public string Container;
31+
32+
[Parameter(Mandatory = false, Position = 4, HelpMessage = "The level of the files to add. Defaults to Published")]
33+
public PnPFileLevel FileLevel = PnPFileLevel.Published;
34+
35+
[Parameter(Mandatory = false, Position = 5, HelpMessage = "Set to overwrite in site, Defaults to true")]
36+
public SwitchParameter FileOverwrite = true;
37+
38+
[Parameter(Mandatory = false, Position = 6, HelpMessage = "Allows you to specify ITemplateProviderExtension to execute while loading the template.")]
39+
public ITemplateProviderExtension[] TemplateProviderExtensions;
40+
41+
protected ProvisioningTemplate LoadTemplate()
42+
{
43+
if (!System.IO.Path.IsPathRooted(Path))
44+
{
45+
Path = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Path);
46+
}
47+
// Load the template
48+
var template = ReadProvisioningTemplate
49+
.LoadProvisioningTemplateFromFile(Path,
50+
TemplateProviderExtensions);
51+
52+
if (template == null)
53+
{
54+
throw new ApplicationException("Invalid template file!");
55+
}
56+
57+
return template;
58+
}
59+
60+
/// <summary>
61+
/// Add a file to the template
62+
/// </summary>
63+
/// <param name="template">The provisioning template to add the file to</param>
64+
/// <param name="fs">Stream to read the file content</param>
65+
/// <param name="folder">target folder in the provisioning template</param>
66+
/// <param name="fileName">Name of the file</param>
67+
/// <param name="container">Container path within the template (pnp file) or related to the xml templage</param>
68+
protected void AddFileToTemplate(ProvisioningTemplate template, Stream fs, string folder, string fileName, string container)
69+
{
70+
var source = !string.IsNullOrEmpty(container) ? (container + "/" + fileName) : fileName;
71+
72+
template.Connector.SaveFileStream(fileName, container, fs);
73+
74+
if (template.Connector is ICommitableFileConnector)
75+
{
76+
((ICommitableFileConnector)template.Connector).Commit();
77+
}
78+
79+
var existing = template.Files.FirstOrDefault(f => f.Src == $"{container}/{fileName}" && f.Folder == folder);
80+
81+
if (existing != null)
82+
template.Files.Remove(existing);
83+
84+
var newFile = new OfficeDevPnP.Core.Framework.Provisioning.Model.File
85+
{
86+
Src = source,
87+
Folder = folder,
88+
Level = FileLevel,
89+
Overwrite = FileOverwrite,
90+
};
91+
92+
template.Files.Add(newFile);
93+
94+
// Determine the output file name and path
95+
var outFileName = System.IO.Path.GetFileName(Path);
96+
var outPath = new FileInfo(Path).DirectoryName;
97+
98+
var fileSystemConnector = new FileSystemConnector(outPath, "");
99+
var formatter = XMLPnPSchemaFormatter.LatestFormatter;
100+
var extension = new FileInfo(Path).Extension.ToLowerInvariant();
101+
if (extension == ".pnp")
102+
{
103+
var provider = new XMLOpenXMLTemplateProvider(template.Connector as OpenXMLConnector);
104+
var templateFileName = outFileName.Substring(0, outFileName.LastIndexOf(".", StringComparison.Ordinal)) + ".xml";
105+
106+
provider.SaveAs(template, templateFileName, formatter, TemplateProviderExtensions);
107+
}
108+
else
109+
{
110+
XMLTemplateProvider provider = new XMLFileSystemTemplateProvider(Path, "");
111+
provider.SaveAs(template, Path, formatter, TemplateProviderExtensions);
112+
}
113+
}
114+
115+
/// <summary>
116+
/// Adds a remote file to a template
117+
/// </summary>
118+
/// <param name="template">Template to add the file to</param>
119+
/// <param name="file">The SharePoint file to retrieve and add</param>
120+
protected void AddSPFileToTemplate(ProvisioningTemplate template, SPFile file)
121+
{
122+
file.EnsureProperties(f => f.Name, f => f.ServerRelativeUrl);
123+
var folderRelativeUrl = file.ServerRelativeUrl.Substring(0, file.ServerRelativeUrl.Length - file.Name.Length - 1);
124+
var folderWebRelativeUrl = HttpUtility.UrlKeyValueDecode(folderRelativeUrl.Substring(SelectedWeb.ServerRelativeUrl.TrimEnd('/').Length + 1));
125+
if (ClientContext.HasPendingRequest) ClientContext.ExecuteQuery();
126+
try
127+
{
128+
using (var fi = SPFile.OpenBinaryDirect(ClientContext, file.ServerRelativeUrl))
129+
using (var ms = new MemoryStream())
130+
{
131+
// We are using a temporary memory stream because the file connector is seeking in the stream
132+
// and the stream provided by OpenBinaryDirect does not allow it
133+
fi.Stream.CopyTo(ms);
134+
ms.Position = 0;
135+
AddFileToTemplate(template, ms, folderWebRelativeUrl, file.Name, folderWebRelativeUrl);
136+
}
137+
}
138+
catch (WebException exc)
139+
{
140+
WriteWarning($"Can't add file from url {file.ServerRelativeUrl} : {exc}");
141+
}
142+
}
143+
144+
/// <summary>
145+
/// Adds a local file to a template
146+
/// </summary>
147+
/// <param name="template">Template to add the file to</param>
148+
/// <param name="file">Full path to a local file</param>
149+
/// <param name="folder">Destination folder of the added file</param>
150+
protected void AddLocalFileToTemplate(ProvisioningTemplate template, string file, string folder)
151+
{
152+
var fileName = System.IO.Path.GetFileName(file);
153+
var container = !string.IsNullOrEmpty(Container) ? Container : folder.Replace("\\", "/");
154+
using (var fs = System.IO.File.OpenRead(file))
155+
{
156+
AddFileToTemplate(template, fs, folder.Replace("\\", "/"), fileName, container);
157+
}
158+
}
159+
}
160+
}

Commands/Provisioning/AddFileToProvisioningTemplate.cs

+17-121
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,7 @@
11
using Microsoft.SharePoint.Client;
2-
using Microsoft.SharePoint.Client.Utilities;
3-
using OfficeDevPnP.Core.Framework.Provisioning.Connectors;
4-
using OfficeDevPnP.Core.Framework.Provisioning.Model;
5-
using OfficeDevPnP.Core.Framework.Provisioning.Providers;
6-
using OfficeDevPnP.Core.Framework.Provisioning.Providers.Xml;
72
using SharePointPnP.PowerShell.CmdletHelpAttributes;
83
using System;
9-
using System.IO;
10-
using System.Linq;
114
using System.Management.Automation;
12-
using System.Net;
13-
using PnPFileLevel = OfficeDevPnP.Core.Framework.Provisioning.Model.FileLevel;
14-
using SPFile = Microsoft.SharePoint.Client.File;
155

166
namespace SharePointPnP.PowerShell.Commands.Provisioning
177
{
@@ -38,78 +28,38 @@ namespace SharePointPnP.PowerShell.Commands.Provisioning
3828
Code = @"PS:> Add-PnPFileToProvisioningTemplate -Path template.pnp -SourceUrl $urlOfFile",
3929
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.",
4030
SortOrder = 4)]
41-
public class AddFileToProvisioningTemplate : PnPWebCmdlet
31+
public class AddFileToProvisioningTemplate : BaseFileProvisioningCmdlet
4232
{
43-
[Parameter(Mandatory = true, Position = 0, HelpMessage = "Filename of the .PNP Open XML provisioning template to read from, optionally including full path.")]
44-
public string Path;
33+
/*
34+
* Path, FileLevel, FileOverwrite and TemplateProviderExtensions fields are in the base class
35+
* */
4536

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

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.")]
40+
[Parameter(Mandatory = true, Position = 1, ParameterSetName = PSNAME_REMOTE_SOURCE, HelpMessage = "The file to add to the in-memory template, specifying its url in the current connected Web.")]
5041
public string SourceUrl;
5142

52-
[Parameter(Mandatory = true, Position = 2, ParameterSetName = "LocalSourceFile", HelpMessage = "The target Folder for the file to add to the in-memory template.")]
43+
[Parameter(Mandatory = true, Position = 2, ParameterSetName = PSNAME_LOCAL_SOURCE, HelpMessage = "The target Folder for the file to add to the in-memory template.")]
5344
public string Folder;
5445

55-
[Parameter(Mandatory = false, Position = 3, HelpMessage = "The target Container for the file to add to the in-memory template, optional argument.")]
56-
public string Container;
57-
58-
[Parameter(Mandatory = false, Position = 4, HelpMessage = "The level of the files to add. Defaults to Published")]
59-
public PnPFileLevel FileLevel = PnPFileLevel.Published;
60-
61-
[Parameter(Mandatory = false, Position = 5, HelpMessage = "Set to overwrite in site, Defaults to true")]
62-
public SwitchParameter FileOverwrite = true;
63-
64-
[Parameter(Mandatory = false, Position = 4, HelpMessage = "Allows you to specify ITemplateProviderExtension to execute while loading the template.")]
65-
public ITemplateProviderExtension[] TemplateProviderExtensions;
66-
6746
protected override void ProcessRecord()
6847
{
69-
if (!System.IO.Path.IsPathRooted(Path))
70-
{
71-
Path = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Path);
72-
}
73-
// Load the template
74-
var template = ReadProvisioningTemplate
75-
.LoadProvisioningTemplateFromFile(Path,
76-
TemplateProviderExtensions);
77-
78-
if (template == null)
79-
{
80-
throw new ApplicationException("Invalid template file!");
81-
}
82-
if (this.ParameterSetName == "RemoteSourceFile")
48+
var template = LoadTemplate();
49+
if (this.ParameterSetName == PSNAME_REMOTE_SOURCE)
8350
{
8451
SelectedWeb.EnsureProperty(w => w.ServerRelativeUrl);
8552
var sourceUri = new Uri(SourceUrl, UriKind.RelativeOrAbsolute);
53+
54+
// Get the server relative url of the file, whatever the input url is (absolute, server relative or web relative form)
8655
var serverRelativeUrl =
87-
sourceUri.IsAbsoluteUri ? sourceUri.AbsolutePath :
88-
SourceUrl.StartsWith("/", StringComparison.Ordinal) ? SourceUrl :
89-
SelectedWeb.ServerRelativeUrl.TrimEnd('/') + "/" + SourceUrl;
56+
sourceUri.IsAbsoluteUri ? sourceUri.AbsolutePath : // The url is absolute, extract the absolute path (http://server/sites/web/folder/file)
57+
SourceUrl.StartsWith("/", StringComparison.Ordinal) ? SourceUrl : // The url is server relative. Take it as is (/sites/web/folder/file)
58+
SelectedWeb.ServerRelativeUrl.TrimEnd('/') + "/" + SourceUrl; // The url is web relative, prepend by the web url (folder/file)
9059

9160
var file = SelectedWeb.GetFileByServerRelativeUrl(serverRelativeUrl);
9261

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
98-
{
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-
}
108-
}
109-
catch (WebException exc)
110-
{
111-
WriteWarning($"Can't add file from url {serverRelativeUrl} : {exc}");
112-
}
62+
AddSPFileToTemplate(template, file);
11363
}
11464
else
11565
{
@@ -119,63 +69,9 @@ protected override void ProcessRecord()
11969
}
12070

12171
// Load the file and add it to the .PNP file
122-
using (var fs = System.IO.File.OpenRead(Source))
123-
{
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);
129-
}
130-
}
131-
}
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);
72+
Folder = Folder.Replace("\\", "/");
15073

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);
74+
AddLocalFileToTemplate(template, Source, Folder);
17975
}
18076
}
18177
}

0 commit comments

Comments
 (0)