Skip to content

Commit b92e59e

Browse files
committed
Tests and their bugfixes
1 parent 07533f9 commit b92e59e

File tree

12 files changed

+455
-174
lines changed

12 files changed

+455
-174
lines changed

bin/Assets.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ class Assets extends \Tatter\Assets\Config\Assets
2020
public $extensions = ['css', 'js'];
2121

2222
// Location of asset files in the filesystem
23-
public $fileBase = FCPATH . 'assets/';
23+
public $fileBase = FCPATH . 'assets/';
2424

2525
// Location of asset URLs
26-
public $webBase = 'https://example.com/assets/';
26+
public $webBase = 'https://example.com/assets/';
2727

2828
// Starting directory for manifest publication
29-
public $publishRoot = ROOTPATH;
29+
public $publishBase = ROOTPATH;
3030

3131
// Additional paths to load per route
3232
// Relative to fileBase, no leading/trailing slashes

src/Config/Assets.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ class Assets extends BaseConfig
1111
public $extensions = ['css', 'js'];
1212

1313
// Location of asset files in the filesystem
14-
public $fileBase = FCPATH . 'assets/';
14+
public $fileBase = FCPATH . 'assets/';
1515

1616
// Location of asset URLs
17-
public $webBase = 'assets/';
17+
public $webBase = 'assets/';
1818

1919
// Starting directory for manifest publication
20-
public $publishRoot = ROOTPATH;
20+
public $publishBase = ROOTPATH;
2121

2222
// Additional paths to load per route
2323
// Relative to fileBase, no leading/trailing slashes

src/Exceptions/ManifestsException.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,23 @@
55

66
class ManifestsException extends FrameworkException implements ExceptionInterface
77
{
8-
public static function forInvalidFileFormat(string $path)
8+
public static function forInvalidFileFormat(string $path, string $reason)
99
{
10-
return new static(lang('Manifests.invalidFileFormat', [$path]));
10+
return new static(lang('Manifests.invalidFileFormat', [$path, $reason]));
1111
}
1212

13-
public static function forFieldMissingFromFile(string $field, string $path)
13+
public static function forMissingField(string $field)
1414
{
15-
return new static(lang('Manifests.fieldMissingFromFile', [$field, $path]));
15+
return new static(lang('Manifests.missingField', [$field]));
16+
}
17+
18+
public static function forCannotCreateDirectory(string $path)
19+
{
20+
return new static(lang('Manifests.cannotCreateDirectory', [$path]));
21+
}
22+
23+
public static function forDirectoryNotWritable(string $path)
24+
{
25+
return new static(lang('Manifests.directoryNotWritable', [$path]));
1626
}
1727
}

src/Language/en/Manifests.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<?php
22

33
return [
4-
'invalidFileFormat' => 'Unsupported file format: "{0}"',
5-
'fieldMissingFromFile' => 'Invalid manfiest, the {0} field is missing: {1}',
4+
'invalidFileFormat' => '{0} is not a supported file format: {1}',
5+
'missingField' => 'Invalid manifest, the {0} field is required',
66
'cannotCreateIndexFile' => 'Unable to create the index file: {0}',
7-
'cannotCreateDirectory' => 'Unable to create the directroy: {0}',
7+
'cannotCreateDirectory' => 'Unable to create the directory: {0}',
88
'directoryNotWritable' => 'The destination is not writable: {0}',
99
];

src/Libraries/Manifests.php

Lines changed: 104 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<?php namespace Tatter\Assets\Libraries;
22

3-
use CodeIgniter\Config\BaseConfig;
43
use CodeIgniter\Files\File;
54
use CodeIgniter\Files\Exceptions\FileException;
65
use 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

Comments
 (0)