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 ;
24using OfficeDevPnP . Core . Framework . Provisioning . Model ;
35using OfficeDevPnP . Core . Framework . Provisioning . Providers ;
46using OfficeDevPnP . Core . Framework . Provisioning . Providers . Xml ;
57using SharePointPnP . PowerShell . CmdletHelpAttributes ;
68using System ;
7- using System . Collections . Generic ;
89using System . IO ;
910using System . Linq ;
1011using 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
1416namespace 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