5
5
using OfficeDevPnP . Core . Framework . Provisioning . Providers ;
6
6
using OfficeDevPnP . Core . Framework . Provisioning . Providers . Xml ;
7
7
using SharePointPnP . PowerShell . Commands . Provisioning ;
8
+ using SharePointPnP . PowerShell . Commands . Utilities ;
8
9
using System ;
10
+ using System . Collections . Generic ;
9
11
using System . IO ;
10
12
using System . Linq ;
11
13
using System . Management . Automation ;
12
- using System . Net ;
14
+ using System . Text . RegularExpressions ;
13
15
using PnPFileLevel = OfficeDevPnP . Core . Framework . Provisioning . Model . FileLevel ;
14
16
using SPFile = Microsoft . SharePoint . Client . File ;
15
17
@@ -35,9 +37,29 @@ public class BaseFileProvisioningCmdlet : PnPWebCmdlet
35
37
[ Parameter ( Mandatory = false , Position = 5 , HelpMessage = "Set to overwrite in site, Defaults to true" ) ]
36
38
public SwitchParameter FileOverwrite = true ;
37
39
38
- [ Parameter ( Mandatory = false , Position = 6 , HelpMessage = "Allows you to specify ITemplateProviderExtension to execute while loading the template." ) ]
40
+ [ Parameter ( Mandatory = false , Position = 6 , ParameterSetName = PSNAME_REMOTE_SOURCE , HelpMessage = "Include webparts when the file is a page" ) ]
41
+ public SwitchParameter ExtractWebParts = true ;
42
+
43
+ [ Parameter ( Mandatory = false , Position = 7 , HelpMessage = "Allows you to specify ITemplateProviderExtension to execute while loading the template." ) ]
39
44
public ITemplateProviderExtension [ ] TemplateProviderExtensions ;
40
45
46
+ protected readonly ProgressRecord _progressEnumeration = new ProgressRecord ( 0 , "Activity" , "Status" ) { Activity = "Enumerating folder" } ;
47
+ protected readonly ProgressRecord _progressFilesEnumeration = new ProgressRecord ( 1 , "Activity" , "Status" ) { Activity = "Extracting files" } ;
48
+ protected readonly ProgressRecord _progressFileProcessing = new ProgressRecord ( 2 , "Activity" , "Status" ) { Activity = "Extracting file" } ;
49
+
50
+ protected override void ProcessRecord ( )
51
+ {
52
+ base . ProcessRecord ( ) ;
53
+ var ctx = ( ClientContext ) SelectedWeb . Context ;
54
+ ctx . Load ( SelectedWeb , web => web . Id , web => web . ServerRelativeUrl , web => web . Url ) ;
55
+ if ( ExtractWebParts )
56
+ {
57
+ ctx . Load ( ctx . Site , site => site . Id , site => site . ServerRelativeUrl , site => site . Url ) ;
58
+ ctx . Load ( SelectedWeb . Lists , lists => lists . Include ( l => l . Title , l => l . RootFolder . ServerRelativeUrl , l => l . Id ) ) ;
59
+ }
60
+ ctx . ExecuteQueryRetry ( ) ;
61
+ }
62
+
41
63
protected ProvisioningTemplate LoadTemplate ( )
42
64
{
43
65
if ( ! System . IO . Path . IsPathRooted ( Path ) )
@@ -65,8 +87,20 @@ protected ProvisioningTemplate LoadTemplate()
65
87
/// <param name="folder">target folder in the provisioning template</param>
66
88
/// <param name="fileName">Name of the file</param>
67
89
/// <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 )
90
+ /// <param name="webParts">WebParts to include</param>
91
+ protected void AddFileToTemplate (
92
+ ProvisioningTemplate template ,
93
+ Stream fs ,
94
+ string folder ,
95
+ string fileName ,
96
+ string container ,
97
+ IEnumerable < WebPart > webParts = null
98
+ )
69
99
{
100
+ if ( template == null ) throw new ArgumentNullException ( nameof ( template ) ) ;
101
+ if ( fs == null ) throw new ArgumentNullException ( nameof ( fs ) ) ;
102
+ if ( fileName == null ) throw new ArgumentNullException ( nameof ( fileName ) ) ;
103
+
70
104
var source = ! string . IsNullOrEmpty ( container ) ? ( container + "/" + fileName ) : fileName ;
71
105
72
106
template . Connector . SaveFileStream ( fileName , container , fs ) ;
@@ -86,9 +120,11 @@ protected void AddFileToTemplate(ProvisioningTemplate template, Stream fs, strin
86
120
Src = source ,
87
121
Folder = folder ,
88
122
Level = FileLevel ,
89
- Overwrite = FileOverwrite ,
123
+ Overwrite = FileOverwrite
90
124
} ;
91
125
126
+ if ( webParts != null ) newFile . WebParts . AddRange ( webParts ) ;
127
+
92
128
template . Files . Add ( newFile ) ;
93
129
94
130
// Determine the output file name and path
@@ -119,25 +155,72 @@ protected void AddFileToTemplate(ProvisioningTemplate template, Stream fs, strin
119
155
/// <param name="file">The SharePoint file to retrieve and add</param>
120
156
protected void AddSPFileToTemplate ( ProvisioningTemplate template , SPFile file )
121
157
{
158
+ if ( template == null ) throw new ArgumentNullException ( nameof ( template ) ) ;
159
+ if ( file == null ) throw new ArgumentNullException ( nameof ( file ) ) ;
160
+
122
161
file . EnsureProperties ( f => f . Name , f => f . ServerRelativeUrl ) ;
162
+
163
+ _progressFileProcessing . StatusDescription = $ "Extracting file { file . ServerRelativeUrl } ";
123
164
var folderRelativeUrl = file . ServerRelativeUrl . Substring ( 0 , file . ServerRelativeUrl . Length - file . Name . Length - 1 ) ;
124
165
var folderWebRelativeUrl = HttpUtility . UrlKeyValueDecode ( folderRelativeUrl . Substring ( SelectedWeb . ServerRelativeUrl . TrimEnd ( '/' ) . Length + 1 ) ) ;
125
166
if ( ClientContext . HasPendingRequest ) ClientContext . ExecuteQuery ( ) ;
126
167
try
127
168
{
169
+ IEnumerable < WebPart > webParts = null ;
170
+ if ( ExtractWebParts )
171
+ {
172
+ webParts = ExtractSPFileWebParts ( file ) . ToArray ( ) ;
173
+ _progressFileProcessing . PercentComplete = 25 ;
174
+ _progressFileProcessing . StatusDescription = $ "Extracting webpart from { file . ServerRelativeUrl } ";
175
+ WriteProgress ( _progressFileProcessing ) ;
176
+ }
177
+
128
178
using ( var fi = SPFile . OpenBinaryDirect ( ClientContext , file . ServerRelativeUrl ) )
129
179
using ( var ms = new MemoryStream ( ) )
130
180
{
181
+ _progressFileProcessing . PercentComplete = 50 ;
182
+ _progressFileProcessing . StatusDescription = $ "Reading file { file . ServerRelativeUrl } ";
183
+ WriteProgress ( _progressFileProcessing ) ;
131
184
// We are using a temporary memory stream because the file connector is seeking in the stream
132
185
// and the stream provided by OpenBinaryDirect does not allow it
133
186
fi . Stream . CopyTo ( ms ) ;
134
187
ms . Position = 0 ;
135
- AddFileToTemplate ( template , ms , folderWebRelativeUrl , file . Name , folderWebRelativeUrl ) ;
188
+ AddFileToTemplate ( template , ms , folderWebRelativeUrl , file . Name , folderWebRelativeUrl , webParts ) ;
189
+ _progressFileProcessing . PercentComplete = 100 ;
190
+ _progressFileProcessing . StatusDescription = $ "Adding file { file . ServerRelativeUrl } to template";
191
+ _progressFileProcessing . RecordType = ProgressRecordType . Completed ;
192
+ WriteProgress ( _progressFileProcessing ) ;
136
193
}
137
194
}
138
- catch ( WebException exc )
195
+ catch ( Exception exc )
196
+ {
197
+ WriteWarning ( $ "Error trying to add file { file . ServerRelativeUrl } : { exc . Message } ") ;
198
+ }
199
+ }
200
+
201
+ private IEnumerable < WebPart > ExtractSPFileWebParts ( SPFile file )
202
+ {
203
+ if ( file == null ) throw new ArgumentNullException ( nameof ( file ) ) ;
204
+
205
+ if ( string . Compare ( System . IO . Path . GetExtension ( file . Name ) , ".aspx" , true ) == 0 )
139
206
{
140
- WriteWarning ( $ "Can't add file from url { file . ServerRelativeUrl } : { exc } ") ;
207
+ foreach ( var spwp in SelectedWeb . GetWebParts ( file . ServerRelativeUrl ) )
208
+ {
209
+ spwp . EnsureProperties ( wp => wp . WebPart
210
+ #if ! SP2016 // Missing ZoneId property in SP2016 version of the CSOM Library
211
+ , wp => wp . ZoneId
212
+ #endif
213
+ ) ;
214
+ yield return new WebPart
215
+ {
216
+ Contents = Tokenize ( SelectedWeb . GetWebPartXml ( spwp . Id , file . ServerRelativeUrl ) ) ,
217
+ Order = ( uint ) spwp . WebPart . ZoneIndex ,
218
+ Title = spwp . WebPart . Title ,
219
+ #if ! SP2016 // Missing ZoneId property in SP2016 version of the CSOM Library
220
+ Zone = spwp . ZoneId
221
+ #endif
222
+ } ;
223
+ }
141
224
}
142
225
}
143
226
@@ -149,12 +232,49 @@ protected void AddSPFileToTemplate(ProvisioningTemplate template, SPFile file)
149
232
/// <param name="folder">Destination folder of the added file</param>
150
233
protected void AddLocalFileToTemplate ( ProvisioningTemplate template , string file , string folder )
151
234
{
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 ) )
235
+ if ( template == null ) throw new ArgumentNullException ( nameof ( template ) ) ;
236
+ if ( file == null ) throw new ArgumentNullException ( nameof ( file ) ) ;
237
+ if ( folder == null ) throw new ArgumentNullException ( nameof ( folder ) ) ;
238
+
239
+ _progressFileProcessing . Activity = $ "Extracting file { file } ";
240
+ _progressFileProcessing . StatusDescription = "Adding file {file}" ;
241
+ _progressFileProcessing . PercentComplete = 0 ;
242
+ WriteProgress ( _progressFileProcessing ) ;
243
+
244
+ try
245
+ {
246
+ var fileName = System . IO . Path . GetFileName ( file ) ;
247
+ var container = ! string . IsNullOrEmpty ( Container ) ? Container : folder . Replace ( "\\ " , "/" ) ;
248
+
249
+ using ( var fs = System . IO . File . OpenRead ( file ) )
250
+ {
251
+ AddFileToTemplate ( template , fs , folder . Replace ( "\\ " , "/" ) , fileName , container ) ;
252
+ }
253
+ }
254
+ catch ( Exception exc )
255
+ {
256
+ WriteWarning ( $ "Error trying to add file { file } : { exc . Message } ") ;
257
+ }
258
+ _progressFileProcessing . RecordType = ProgressRecordType . Completed ;
259
+ WriteProgress ( _progressFileProcessing ) ;
260
+ }
261
+
262
+ private string Tokenize ( string input )
263
+ {
264
+ if ( string . IsNullOrEmpty ( input ) ) return input ;
265
+
266
+ foreach ( var list in SelectedWeb . Lists )
155
267
{
156
- AddFileToTemplate ( template , fs , folder . Replace ( "\\ " , "/" ) , fileName , container ) ;
268
+ input = input
269
+ . ReplaceCaseInsensitive ( list . Id . ToString ( "D" ) , "{listid:" + Regex . Escape ( list . Title ) + "}" )
270
+ . ReplaceCaseInsensitive ( list . GetWebRelativeUrl ( ) , "{listurl:" + Regex . Escape ( list . Title ) + "}" ) ;
157
271
}
272
+ return input . ReplaceCaseInsensitive ( SelectedWeb . Url , "{site}" )
273
+ . ReplaceCaseInsensitive ( SelectedWeb . ServerRelativeUrl , "{site}" )
274
+ . ReplaceCaseInsensitive ( SelectedWeb . Id . ToString ( ) , "{siteid}" )
275
+ . ReplaceCaseInsensitive ( ( ( ClientContext ) SelectedWeb . Context ) . Site . ServerRelativeUrl , "{sitecollection}" )
276
+ . ReplaceCaseInsensitive ( ( ( ClientContext ) SelectedWeb . Context ) . Site . Id . ToString ( ) , "{sitecollectionid}" )
277
+ . ReplaceCaseInsensitive ( ( ( ClientContext ) SelectedWeb . Context ) . Site . Url , "{sitecollection}" ) ;
158
278
}
159
279
}
160
280
}
0 commit comments