11<?php namespace Tatter \Assets \Libraries ;
22
3- use CodeIgniter \Config \BaseConfig ;
43use CodeIgniter \Files \File ;
54use CodeIgniter \Files \Exceptions \FileException ;
65use CodeIgniter \Files \Exceptions \FileNotFoundException ;
@@ -26,6 +25,9 @@ public function __construct($config = null)
2625 {
2726 // Save the configuration
2827 $ this ->config = $ config ;
28+
29+ // Load the helper
30+ helper ('filesystem ' );
2931 }
3032
3133 // Scan all namespaces for manifest files
@@ -42,15 +44,14 @@ public function locate(): array
4244 $ files = $ locator ->listFiles ('Manifests ' );
4345
4446 // Filter by .json extension
45- return array_unique (preg_grep ("/ .+\.json$/ i " , $ files ));
47+ return array_unique (preg_grep ("# .+\.json$# i " , $ files ));
4648 }
4749
4850 // Publish assets from a single manifest
49- public function publish ($ path ): bool
51+ public function publish (object $ manifest ): bool
5052 {
51- // Verify the manifest
52- $ manifest = $ this ->manifestFromFile ($ path );
53- if ($ manifest === null )
53+ // Validate the manifest
54+ if (! $ this ->validate ($ manifest ))
5455 {
5556 return false ;
5657 }
@@ -73,7 +74,7 @@ public function publish($path): bool
7374 }
7475
7576 // Read in and verify a manifest from a file
76- protected function manifestFromFile ( $ file ): ?object
77+ public function parse ( string $ file ): ?object
7778 {
7879 // Make sure the file is valid and accessible
7980 if (! is_file ($ file ))
@@ -95,48 +96,78 @@ protected function manifestFromFile($file): ?object
9596 $ manifest = json_decode ($ manifest );
9697 if ($ manifest === NULL )
9798 {
98- $ errornum = json_last_error ();
99-
10099 if ($ this ->config ->silent )
101100 {
102- $ error = ' JSON Error # ' . $ errornum . ' . ' . lang ('Manifests.invalidFileFormat ' , [$ file ]);
101+ $ error = lang ('Manifests.invalidFileFormat ' , [$ file, json_last_error_msg () ]);
103102 log_message ('warning ' , $ error );
104103 $ this ->messages [] = [$ error , 'red ' ];
105104 return null ;
106105 }
107106
108- throw ManifestsException::forInvalidFileFormat ($ file );
107+ throw ManifestsException::forInvalidFileFormat ($ file , json_last_error_msg ());
108+ }
109+
110+ // Validate the manifest
111+ if (! $ this ->validate ($ manifest ))
112+ {
113+ return null ;
109114 }
110115
111- // Verify necessary fields
116+ return $ manifest ;
117+ }
118+
119+ // Validate the required fields
120+ public function validate (object $ manifest ): bool
121+ {
122+ // Check for the necessary fields
112123 foreach (['source ' , 'destination ' , 'resources ' ] as $ field )
113124 {
114125 if (empty ($ manifest ->{$ field }))
115126 {
116127 if ($ this ->config ->silent )
117128 {
118- $ error = lang ('Manifests.fieldMissingFromFile ' , [$ field, $ file ]);
129+ $ error = lang ('Manifests.missingField ' , [$ field ]);
119130 log_message ('warning ' , $ error );
120131 $ this ->messages [] = [$ error , 'red ' ];
121- return null ;
132+ return false ;
122133 }
123-
124- throw ManifestsException::forFieldMissingFromFile ($ field, $ file );
134+
135+ throw ManifestsException::forMissingField ($ field );
125136 }
126137 }
127138
128- return $ manifest ;
139+ // Check each resource for a source
140+ foreach ($ manifest ->resources as $ resource )
141+ {
142+ if (empty ($ resource ->source ))
143+ {
144+ if ($ this ->config ->silent )
145+ {
146+ $ error = lang ('Manifests.missingField ' , ['resource->source ' ]);
147+ log_message ('warning ' , $ error );
148+ $ this ->messages [] = [$ error , 'red ' ];
149+ return false ;
150+ }
151+
152+ throw ManifestsException::forMissingField ('resource->source ' );
153+ }
154+ }
155+
156+ return true ;
129157 }
130158
131- // Verify or create a destination folder and all folders up to it
159+ // Verify or create a destination folder within fileBase ( and all folders up to it)
132160 protected function secureDestination ($ path ): bool
133161 {
134162 $ directory = rtrim ($ this ->config ->fileBase , '/ ' );
135-
163+
164+ // Make sure $path is relative and has no trailing slash
165+ $ path = trim (str_replace ($ directory , '' , $ path ), '/ ' );
166+
136167 $ segments = explode ('/ ' , $ path );
137168 if ($ segments [0 ] != '' )
138169 {
139- $ segments = array_unshift ($ segments , '' );
170+ array_unshift ($ segments , '' );
140171 }
141172
142173 // Secure each directory up to the destination
@@ -167,22 +198,32 @@ protected function ensureDirectory($directory): bool
167198 mkdir ($ directory , 0644 , true );
168199 }
169200
170- // Make sure its a directory
201+ // Make sure there's a directory there now
171202 if (! is_dir ($ directory ))
172203 {
173- $ error = lang ('Manifests.cannotCreateDirectory ' , [$ directory ]);
174- log_message ('warning ' , $ error );
175- $ this ->messages [] = [$ error , 'red ' ];
176- return false ;
204+ if ($ this ->config ->silent )
205+ {
206+ $ error = lang ('Manifests.cannotCreateDirectory ' , [$ directory ]);
207+ log_message ('warning ' , $ error );
208+ $ this ->messages [] = [$ error , 'red ' ];
209+ return false ;
210+ }
211+
212+ throw ManifestsException::forCannotCreateDirectory ($ directory );
177213 }
178214
179215 // Make sure it is writable
180216 if (! is_writable ($ directory ))
181217 {
182- $ error = lang ('Manifests.directoryNotWritable ' , [$ directory ]);
183- log_message ('warning ' , $ error );
184- $ this ->messages [] = [$ error , 'red ' ];
185- return false ;
218+ if ($ this ->config ->silent )
219+ {
220+ $ error = lang ('Manifests.directoryNotWritable ' , [$ directory ]);
221+ log_message ('warning ' , $ error );
222+ $ this ->messages [] = [$ error , 'red ' ];
223+ return false ;
224+ }
225+
226+ throw ManifestsException::forDirectoryNotWritable ($ directory );
186227 }
187228
188229 return true ;
@@ -200,7 +241,7 @@ protected function addIndexToDirectory($directory): bool
200241 }
201242
202243 // Directory should be writable but just in case...
203- if (! is_writable ($ file ))
244+ if (! is_writable (dirname ( $ file) ))
204245 {
205246 $ error = lang ('Manifests.directoryNotWritable ' , [$ directory ]);
206247 log_message ('warning ' , $ error );
@@ -209,7 +250,7 @@ protected function addIndexToDirectory($directory): bool
209250 }
210251
211252 // Do it
212- if (file_put_contents ($ file , $ this ->getIndexHtml ) === false )
253+ if (file_put_contents ($ file , $ this ->getIndexHtml () ) === false )
213254 {
214255 $ error = lang ('Manifests.cannotCreateIndexFile ' , [$ file ]);
215256 log_message ('warning ' , $ error );
@@ -221,7 +262,7 @@ protected function addIndexToDirectory($directory): bool
221262 }
222263
223264 // Generate content for index.html
224- protected function getIndexHtml (): string
265+ public function getIndexHtml (): string
225266 {
226267 return '<!DOCTYPE html>
227268<html>
@@ -237,18 +278,18 @@ protected function getIndexHtml(): string
237278 ' ;
238279 }
239280
240- // Expand resource paths relative to the configured publish root and the manifest property
241- protected function expandPaths (&$ resource , &$ manifest )
281+ // Expand resource paths relative to their configured bases and the manifest property
282+ public function expandPaths (&$ resource , &$ manifest )
242283 {
243284 $ resource ->source =
244- rtrim ($ this ->config ->publishRoot , '/ ' ) . '/ ' .
285+ rtrim ($ this ->config ->publishBase , '/ ' ) . '/ ' .
245286 trim ($ manifest ->source , '/ ' ) . '/ ' .
246- ltrim ($ resource ->source , '/ ' );
287+ trim ($ resource ->source , '/ ' );
247288
248289 $ resource ->destination =
249- rtrim ($ this ->config ->publishRoot , '/ ' ) . '/ ' .
290+ rtrim ($ this ->config ->fileBase , '/ ' ) . '/ ' .
250291 trim ($ manifest ->destination , '/ ' ) . '/ ' .
251- ltrim ($ resource ->destination ?? '' , '/ ' );
292+ trim ($ resource ->destination ?? '' , '/ ' ) . ' / ' ;
252293 }
253294
254295 // Parse a resource and copy it to the determined destination
@@ -275,9 +316,13 @@ protected function publishResource($resource): bool
275316 throw FileNotFoundException::forFileNotFound ($ resource ->source );
276317 }
277318
278- return is_dir ($ resource ->source ) ?
279- $ this ->publishResourceDirectory ($ resource ) :
280- $ this ->publishFile ($ resource ->source , $ resource ->destination );
319+ if (is_dir ($ resource ->source ))
320+ {
321+ $ resource ->source = rtrim ($ resource ->source , '/ ' ) . '/ ' ;
322+ return $ this ->publishResourceDirectory ($ resource );
323+ }
324+
325+ return $ this ->publishFile ($ resource ->source , $ resource ->destination );
281326 }
282327
283328 // Scan a directory and apply filters, publishing each file
@@ -304,35 +349,37 @@ protected function publishResourceDirectory($resource): bool
304349 }
305350
306351 // Recursive, not flatten
307- elseif (! empty ($ resource ->recursive ))
352+ if (! empty ($ resource ->recursive ))
308353 {
309354 $ items = directory_map ($ resource ->source );
310- return $ this ->publishDirectoryRecursive ($ items , $ resource ->source , $ resource ->destination );
355+ return $ this ->publishDirectory ($ items , $ resource ->source , $ resource ->destination , $ resource -> filter ?? null );
311356 }
312357
313- // Publish every file back to the destination
314- foreach ($ files as $ source => $ destination )
315- {
316- $ result = $ this ->publishFile ($ source , $ destination );
317- }
318-
319- return $ result ;
358+ // Single directory
359+ $ items = directory_map ($ resource ->source );
360+ $ items = array_filter ($ items , 'is_string ' );
361+ return $ this ->publishDirectory ($ items , $ resource ->source , $ resource ->destination , $ resource ->filter ?? null );
320362 }
321363
322364 // Recursive-safe directory publish
323- protected function publishDirectoryRecursive (array $ items , string $ source , string $ destination ): bool
365+ protected function publishDirectory (array $ items , string $ source , string $ destination, $ filter = null ): bool
324366 {
325367 $ result = true ;
326-
368+
327369 foreach ($ items as $ dir => $ item )
328370 {
329371 // Directory
330372 if (is_array ($ item ))
331373 {
332- $ result = $ result && $ this ->publishDirectoryRecursive ($ item , $ source . $ dir , $ destination . $ dir );
374+ $ result = $ result && $ this ->publishDirectory ($ item , $ source . $ dir , $ destination . $ dir, $ filter );
333375 }
334- // File
335- else
376+ // File, no filter
377+ elseif (empty ($ filter ))
378+ {
379+ $ result = $ result && $ this ->publishFile ($ source . $ item , $ destination );
380+ }
381+ // File passes filter
382+ elseif (preg_match ($ filter , $ item ))
336383 {
337384 $ result = $ result && $ this ->publishFile ($ source . $ item , $ destination );
338385 }
@@ -348,8 +395,8 @@ protected function publishFile(string $source, string $destination): bool
348395 {
349396 return false ;
350397 }
351-
352- if (copy ($ source , $ destination ))
398+
399+ if (copy ($ source , $ destination . basename ( $ source ) ))
353400 {
354401 return true ;
355402 }
0 commit comments