@@ -65,6 +65,7 @@ public function publish($path): bool
6565 $ result = true ;
6666 foreach ($ manifest ->resources as $ resource )
6767 {
68+ $ this ->expandPaths ($ resource , $ manifest );
6869 $ result = $ result && $ this ->publishResource ($ resource );
6970 }
7071
@@ -81,8 +82,11 @@ protected function manifestFromFile($path): ?object
8182 {
8283 if ($ this ->config ->silent )
8384 {
84- log_message ('warning ' , lang ('Files.fileNotFound ' , [$ path ]));
85- return ;
85+ $ error = lang ('Files.fileNotFound ' , [$ path ]);
86+ log_message ('warning ' , $ error );
87+ $ this ->messages [] = [$ error , 'red ' ];
88+
89+ return null ;
8690 }
8791
8892 throw FileNotFoundException::forFileNotFound ($ path );
@@ -98,9 +102,9 @@ protected function manifestFromFile($path): ?object
98102 if ($ this ->config ->silent )
99103 {
100104 $ error = 'JSON Error # ' . $ errornum . '. ' . lang ('Manifests.invalidFileFormat ' , [$ path ]);
101- log_message ('warning ' , $ error )) ;
105+ log_message ('warning ' , $ error );
102106 $ this ->messages [] = [$ error , 'red ' ];
103- return ;
107+ return null ;
104108 }
105109
106110 throw ManifestsException::forInvalidFileFormat ($ path );
@@ -114,9 +118,9 @@ protected function manifestFromFile($path): ?object
114118 if ($ this ->config ->silent )
115119 {
116120 $ error = lang ('Manifests.fieldMissingFromFile ' , [$ field , $ path ]);
117- log_message ('warning ' , $ error )) ;
121+ log_message ('warning ' , $ error );
118122 $ this ->messages [] = [$ error , 'red ' ];
119- return ;
123+ return null ;
120124 }
121125
122126 throw ManifestsException::forFieldMissingFromFile ($ field , $ path );
@@ -158,6 +162,7 @@ protected function secureDestination($path): bool
158162
159163 // Make sure a directory exists and is writable
160164 protected function ensureDirectory ($ directory ): bool
165+ {
161166 // Check for existence
162167 if (! file_exists ($ directory ))
163168 {
@@ -168,7 +173,7 @@ protected function ensureDirectory($directory): bool
168173 if (! is_dir ($ directory ))
169174 {
170175 $ error = lang ('Manifests.cannotCreateDirectory ' , [$ directory ]);
171- log_message ('warning ' , $ error )) ;
176+ log_message ('warning ' , $ error );
172177 $ this ->messages [] = [$ error , 'red ' ];
173178 return false ;
174179 }
@@ -177,7 +182,7 @@ protected function ensureDirectory($directory): bool
177182 if (! is_writable ($ directory ))
178183 {
179184 $ error = lang ('Manifests.directoryNotWritable ' , [$ directory ]);
180- log_message ('warning ' , $ error )) ;
185+ log_message ('warning ' , $ error );
181186 $ this ->messages [] = [$ error , 'red ' ];
182187 return false ;
183188 }
@@ -197,11 +202,11 @@ protected function addIndexToDirectory($directory): bool
197202 return true ;
198203 }
199204
200- // Directory should be writable but jsut in case...
205+ // Directory should be writable but just in case...
201206 if (! $ file ->isWritable )
202207 {
203208 $ error = lang ('Manifests.directoryNotWritable ' , [$ directory ]);
204- log_message ('warning ' , $ error )) ;
209+ log_message ('warning ' , $ error );
205210 $ this ->messages [] = [$ error , 'red ' ];
206211 return false ;
207212 }
@@ -211,7 +216,7 @@ protected function addIndexToDirectory($directory): bool
211216 if (! $ file ->fwrite ($ this ->getIndexHtml ))
212217 {
213218 $ error = lang ('Manifests.cannotCreateIndexFile ' , [$ path ]);
214- log_message ('warning ' , $ error )) ;
219+ log_message ('warning ' , $ error );
215220 $ this ->messages [] = [$ error , 'red ' ];
216221 return false ;
217222 }
@@ -235,9 +240,23 @@ protected function getIndexHtml(): string
235240</html>
236241 ' ;
237242 }
243+
244+ // Expand resource paths relative to the configured publish root and the manifest property
245+ protected function expandPaths (&$ resource , &$ manifest )
246+ {
247+ $ resource ->source =
248+ rtrim ($ this ->config ->publishRoot , '/ ' ) . '/ ' .
249+ trim ($ manifest ->source , '/ ' ) . '/ ' .
250+ ltrim ($ resource ->source , '/ ' );
251+
252+ $ resource ->destination =
253+ rtrim ($ this ->config ->publishRoot , '/ ' ) . '/ ' .
254+ trim ($ manifest ->destination , '/ ' ) . '/ ' .
255+ ltrim ($ resource ->destination ?? '' , '/ ' );
256+ }
238257
239258 // Parse a resource and copy it to the determined destination
240- protected function publishResource ($ resource ): ? object
259+ protected function publishResource ($ resource ): bool
241260 {
242261 // Validate the source
243262 if (! isset ($ resource ->source ))
@@ -247,7 +266,108 @@ protected function publishResource($resource): ?object
247266
248267 // Make sure the source exists
249268 $ file = new File ($ resource ->source );
250- //WIP
269+ if (! $ file ->getRealPath ())
270+ {
271+ if ($ this ->config ->silent )
272+ {
273+ $ error = lang ('Files.fileNotFound ' , [$ path ]);
274+ log_message ('warning ' , $ error );
275+ $ this ->messages [] = [$ error , 'red ' ];
276+
277+ return false ;
278+ }
279+
280+ throw FileNotFoundException::forFileNotFound ($ path );
281+ }
282+
283+ return $ file ->isDir () ?
284+ $ this ->publishResourceDirectory ($ resource ) :
285+ $ this ->publishFile ($ resource ->source , $ resource ->destination );
286+ }
287+
288+ // Scan a directory and apply filters, publishing each file
289+ protected function publishResourceDirectory ($ resource ): bool
290+ {
291+ $ result = true ;
292+
293+ // Recursive, flatten
294+ if (! empty ($ resource ->recursive ) && ! empty ($ resource ->flatten ))
295+ {
296+ $ files = get_filenames ($ resource ->source , true );
297+ if (! empty ($ resource ->filter ))
298+ {
299+ $ files = preg_grep ($ resource ->filter , $ files );
300+ }
301+
302+ // Publish every file back to the destination
303+ foreach ($ files as $ file )
304+ {
305+ $ result = $ this ->publishFile ($ file , $ resource ->destination );
306+ }
307+
308+ return $ result ;
309+ }
310+
311+ // Recursive, not flatten
312+ elseif (! empty ($ resource ->recursive ))
313+ {
314+ $ items = directory_map ($ resource ->source );
315+ return $ this ->publishDirectoryRecursive ($ items , $ resource ->source , $ resource ->destination );
316+ }
317+
318+ // Publish every file back to the destination
319+ foreach ($ files as $ source => $ destination )
320+ {
321+ $ result = $ this ->publishFile ($ source , $ destination );
322+ }
323+
324+ return $ result ;
325+ }
326+
327+ // Recursive-safe directory publish
328+ protected function publishDirectoryRecursive (array $ items , string $ source , string $ destination ): bool
329+ {
330+ $ result = true ;
331+
332+ foreach ($ items as $ dir => $ item )
333+ {
334+ // Directory
335+ if (is_array ($ item ))
336+ {
337+ $ result = $ result && $ this ->publishDirectoryRecursive ($ item , $ source . $ dir , $ destination . $ dir );
338+ }
339+ // File
340+ else
341+ {
342+ $ result = $ result && $ this ->publishFile ($ source . $ item , $ destination );
343+ }
344+ }
345+
346+ return $ result ;
347+ }
251348
349+ // Copy a file into place, creating and securing missing directories
350+ protected function publishFile (string $ source , string $ destination ): bool
351+ {
352+ if (! $ this ->secureDestination ($ destination ))
353+ {
354+ return false ;
355+ }
356+
357+ if (copy ($ source , $ destination ))
358+ {
359+ return true ;
360+ }
361+
362+ if ($ this ->config ->silent )
363+ {
364+ $ error = lang ('Files.cannotMove ' , [$ source , $ destination , -1 ]);
365+ log_message ('warning ' , $ error );
366+ $ this ->messages [] = [$ error , 'red ' ];
367+
368+ return false ;
369+ }
370+
371+ throw FileException::forUnableToMove ($ source , $ destination , -1 );
252372 }
253373}
0 commit comments