Skip to content

Commit dbab849

Browse files
authored
Merge pull request #13 from cybtachyon/feature/svg-support
SVG & Arbitrary File Support
2 parents 8d045a2 + 28c5b93 commit dbab849

File tree

4 files changed

+276
-2
lines changed

4 files changed

+276
-2
lines changed

patternkit.services.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ services:
1414
arguments: ['@cache.discovery', '@config.factory', '@file_system', '@plugin.manager.library.pattern', '@lock', '@module_handler', '@app.root', '@theme.manager']
1515
tags:
1616
- { name: needs_destruction }
17+
patternkit.library.discovery.parser.file:
18+
class: Drupal\patternkit\PatternLibraryParser\FilePatternLibraryParser
19+
arguments: ['@serialization.json', '@app.root', '@module_handler', '@theme.manager']
1720
patternkit.library.discovery.parser.json:
1821
class: Drupal\patternkit\PatternLibraryParser\JSONPatternLibraryParser
1922
arguments: ['@serialization.json', '@app.root', '@module_handler', '@theme.manager']

src/PatternLibraryCollector.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -448,8 +448,15 @@ protected function getLibraryMetadata(): array {
448448
$plugin = $this->libraryPluginManager->createInstance($plugin_id);
449449
}
450450
catch (PluginNotFoundException $exception) {
451-
\Drupal::logger('patternkit')->error('Error loading pattern libraries: @message', ['@message' => $exception->getMessage()]);
452-
continue;
451+
// Allow plugin fallbacks of type 'base_plugin.override_plugin'.
452+
$plugin_id = strstr($plugin_id, '.', TRUE);
453+
try {
454+
$plugin = $this->libraryPluginManager->createInstance($plugin_id);
455+
}
456+
catch (PluginNotFoundException $exception) {
457+
\Drupal::logger('patternkit')->error('Error loading pattern libraries: @message', ['@message' => $exception->getMessage()]);
458+
continue;
459+
}
453460
}
454461
/** @var \Drupal\patternkit\Pattern $pattern */
455462
foreach ($plugin->getMetadata($extension, $metadata[$library_name], $info['data']) as $pattern_path => $pattern) {
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
<?php
2+
3+
namespace Drupal\patternkit\PatternLibraryParser;
4+
5+
use Drupal\Component\Serialization\SerializationInterface;
6+
use Drupal\Core\Asset\Exception\InvalidLibraryFileException;
7+
use Drupal\Core\Extension\ModuleHandlerInterface;
8+
use Drupal\Core\Theme\ThemeManagerInterface;
9+
use Drupal\patternkit\Pattern;
10+
use Drupal\patternkit\PatternEditorConfig;
11+
use Drupal\patternkit\PatternLibraryJSONParserTrait;
12+
use Drupal\patternkit\PatternLibraryParserBase;
13+
14+
/**
15+
* Parses a File pattern library collection into usable metadata.
16+
*/
17+
class FilePatternLibraryParser extends PatternLibraryParserBase {
18+
use PatternLibraryJSONParserTrait;
19+
20+
/**
21+
* FilePatternLibraryParser constructor.
22+
*
23+
* @param \Drupal\Component\Serialization\SerializationInterface $serializer
24+
* Serializes and de-serializes data.
25+
* @param string $root
26+
* The application root path.
27+
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
28+
* Allows modules to alter library parsing.
29+
* @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
30+
* Allows themes to alter library parsing.
31+
*/
32+
public function __construct(
33+
SerializationInterface $serializer,
34+
$root,
35+
ModuleHandlerInterface $module_handler,
36+
ThemeManagerInterface $theme_manager) {
37+
38+
$this->serializer = $serializer;
39+
parent::__construct($root, $module_handler, $theme_manager);
40+
}
41+
42+
/**
43+
* Fetches all assets for a pattern.
44+
*
45+
* {@inheritDoc}
46+
*/
47+
public function fetchPatternAssets(Pattern $pattern,
48+
PatternEditorConfig $config): Pattern {
49+
// @todo Add support for File lib attachments such as JS and images.
50+
$pattern->attachments = array();
51+
return $pattern;
52+
}
53+
54+
/**
55+
* Parses a given library file and allows modules and themes to alter it.
56+
*
57+
* This method sets the parsed information onto the library property.
58+
*
59+
* Library information is parsed from *.libraries.yml files; see
60+
* editor.libraries.yml for an example. Each entry starts with a machine name
61+
* and defines the following elements:
62+
* - patterns: A list of pattern libraries and subtypes to include. Each
63+
* subtype is keyed by the subtype path.
64+
* @code
65+
* patterns:
66+
* path/atoms: {type: File, category: atoms}
67+
* path/molecules: {type: File, category: molecules}
68+
* path/organisms: {}
69+
* @endcode
70+
* - dependencies: A list of libraries this library depends on.
71+
* - version: The library version. The string "VERSION" can be used to mean
72+
* the current Drupal core version.
73+
* - header: By default, JavaScript files are included in the footer. If the
74+
* script must be included in the header (along with all its dependencies),
75+
* set this to true. Defaults to false.
76+
* - minified: If the file is already minified, set this to true to avoid
77+
* minifying it again. Defaults to false.
78+
* - remote: If the library is a third-party script, this provides the
79+
* repository URL for reference.
80+
* - license: If the remote property is set, the license information is
81+
* required. It has 3 properties:
82+
* - name: The human-readable name of the license.
83+
* - url: The URL of the license file/information for the version of the
84+
* library used.
85+
* - gpl-compatible: A Boolean for whether this library is GPL compatible.
86+
*
87+
* See https://www.drupal.org/node/2274843#define-library for more
88+
* information.
89+
*
90+
* @param array $library
91+
* The data of the library that was registered.
92+
* @param string $path
93+
* The relative path to the extension.
94+
*
95+
* @return array
96+
* An array of parsed library data.
97+
*
98+
* @throws \Drupal\Core\Asset\Exception\InvalidLibraryFileException
99+
* Thrown when a parser exception was thrown.
100+
*/
101+
public function parsePatternLibraryInfo(array $library, $path): array {
102+
if (!file_exists($path)) {
103+
throw new InvalidLibraryFileException("Path $path does not exist.");
104+
}
105+
$metadata = [];
106+
// @todo Grab the extension from the plugin.
107+
$type = strtok($library['plugin'], '.');
108+
if ($type !== 'file') {
109+
return [];
110+
}
111+
$ext = strtok('.');
112+
foreach (self::discoverComponents($path, [$ext]) as $name => $data) {
113+
if (empty($data[$ext]) || !file_exists($data[$ext])) {
114+
continue;
115+
}
116+
// If the component has a json file, create the pattern from it.
117+
$category = $library['category'] ?? 'default';
118+
$library_defaults = [
119+
'$schema' => NULL,
120+
'category' => $category,
121+
'title' => $name,
122+
'type' => 'object',
123+
'format' => 'grid',
124+
'license' => $library['license'] ?? [],
125+
'name' => $name,
126+
'properties' => (object) [],
127+
'required' => [],
128+
'version' => $library['version'] ?? '',
129+
];
130+
// Create the pattern from defaults.
131+
// @todo Have this cleverly infer defaults from the template.
132+
$pattern = $this->createPattern($name, $library_defaults);
133+
$pattern->filename = trim(substr($data[$ext], strlen($path)), '/\\');
134+
$pattern->path = substr($pattern->filename, 0, -strlen('.' . $ext));
135+
$pattern->template = file_get_contents($data[$ext]);
136+
// URL is redundant for the File based components, so we use it to
137+
// store namespace.
138+
$pattern->url = $library['name'];
139+
// @todo add default of library version fallback to extension version.
140+
$pattern->version = $pattern->version ?? 'VERSION';
141+
$metadata['@' . $library['name'] . '/' . $pattern->path] = $pattern;
142+
}
143+
144+
foreach ($metadata as $pattern_type => $pattern) {
145+
// Replace any $ref links with relative paths.
146+
if (!isset($pattern->schema['properties'])) {
147+
continue;
148+
}
149+
$pattern->schema['properties'] = static::schemaDereference(
150+
$pattern->schema['properties'],
151+
$metadata
152+
);
153+
$metadata[$pattern_type] = $pattern;
154+
}
155+
return $metadata;
156+
}
157+
158+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
namespace Drupal\patternkit\Plugin\PatternLibrary;
4+
5+
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
6+
use Drupal\patternkit\Pattern;
7+
use Drupal\patternkit\PatternEditorConfig;
8+
use Drupal\patternkit\PatternLibraryPluginDefault;
9+
use Drupal\patternkit\PatternLibraryPluginInterface;
10+
use Symfony\Component\DependencyInjection\ContainerInterface;
11+
12+
/**
13+
* Provides mechanisms for parsing and rendering a File Library of patterns.
14+
*
15+
* @PatternLibrary(
16+
* id = "file",
17+
* )
18+
*/
19+
class PatternLibraryFile extends PatternLibraryPluginDefault implements ContainerFactoryPluginInterface {
20+
21+
/**
22+
* Twig environment service.
23+
*
24+
* @var \Drupal\Core\Template\TwigEnvironment
25+
*/
26+
protected $twig;
27+
28+
/**
29+
* Twig file loader.
30+
*
31+
* @var \Twig\Loader\FilesystemLoader
32+
*/
33+
protected $twigLoader;
34+
35+
/**
36+
* File pattern library parser service.
37+
*
38+
* @var \Drupal\patternkit\PatternLibraryParser\FilePatternLibraryParser
39+
*/
40+
protected $fileParser;
41+
42+
/**
43+
* Creates a new File Pattern Library using the given container.
44+
*
45+
* {@inheritDoc}
46+
*/
47+
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): PatternLibraryPluginInterface {
48+
$root = $container->get('app.root');
49+
/** @var \Drupal\patternkit\PatternLibraryParserInterface $file_parser */
50+
$file_parser = $container->get('patternkit.library.discovery.parser.file');
51+
/** @var \Drupal\Core\Config\ConfigFactoryInterface $config_factory */
52+
$config_factory = $container->get('config.factory');
53+
return new static($root, $file_parser, $config_factory, $configuration, $plugin_id, $plugin_definition);
54+
}
55+
56+
/**
57+
* Returns the editor for the generic file plugin.
58+
*
59+
* @param \Drupal\patternkit\Pattern|NULL $pattern
60+
* @param \Drupal\patternkit\PatternEditorConfig|NULL $config
61+
*
62+
* @return mixed|void
63+
*/
64+
public function getEditor(Pattern $pattern = NULL,
65+
PatternEditorConfig $config = NULL) {
66+
// TODO: Implement getEditor() method.
67+
return '';
68+
}
69+
70+
/**
71+
* Overrides the JSON Library render method.
72+
*
73+
* {@inheritdoc}
74+
*
75+
* @throws \Throwable
76+
*
77+
* @todo Return render arrays for File only.
78+
*/
79+
public function render(array $assets): array {
80+
$elements = [];
81+
foreach ($assets as $pattern) {
82+
if (empty($pattern->filename)) {
83+
return [];
84+
}
85+
// @todo Allow filename to cache the template contents based on settings.
86+
$template = $pattern->filename;
87+
// Add the namespace, if provided.
88+
if (!empty($pattern->url)) {
89+
$template = '@' . $pattern->url . '#/' . $template;
90+
}
91+
$namespace = '';
92+
$file = $template;
93+
// If a namespace is provided, break it up.
94+
if (strpos($template, '@') === 0) {
95+
[$namespace, $file] = explode('#', $template);
96+
}
97+
$bare = basename($file);
98+
/** @var \Drupal\Core\Template\TwigEnvironment $twig */
99+
$twig = \Drupal::service('twig');
100+
$template = $twig->load("$namespace/$pattern->filename");
101+
$elements[] = $template->render($pattern->config ?? []);
102+
}
103+
return $elements;
104+
}
105+
106+
}

0 commit comments

Comments
 (0)