Chris@5
|
1 <?php
|
Chris@5
|
2
|
Chris@5
|
3 namespace Drupal\migrate_plus\Plugin\migrate\process;
|
Chris@5
|
4
|
Chris@5
|
5 use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
|
Chris@5
|
6 use Drupal\migrate\MigrateExecutableInterface;
|
Chris@5
|
7 use Drupal\migrate\Row;
|
Chris@5
|
8
|
Chris@5
|
9 /**
|
Chris@5
|
10 * String replacements on a source dom.
|
Chris@5
|
11 *
|
Chris@5
|
12 * Analogous to str_replace process plugin, but based on a \DOMDocument instead
|
Chris@5
|
13 * of a string.
|
Chris@5
|
14 * Meant to be used after dom process plugin.
|
Chris@5
|
15 *
|
Chris@5
|
16 * Available configuration keys:
|
Chris@5
|
17 * - mode: What to modify. Possible values:
|
Chris@5
|
18 * - attribute: One element attribute.
|
Chris@5
|
19 * - expression: XPath query expression that will produce the \DOMNodeList to
|
Chris@5
|
20 * walk.
|
Chris@5
|
21 * - attribute_options: A map of options related to the attribute mode. Required
|
Chris@5
|
22 * when mode is attribute. The keys can be:
|
Chris@5
|
23 * - name: Name of the attribute to match and modify.
|
Chris@5
|
24 * - search: pattern to match.
|
Chris@5
|
25 * - replace: value to replace the searched pattern with.
|
Chris@5
|
26 * - regex: Use regular expression replacement.
|
Chris@5
|
27 * - case_insensitive: Case insensitive search. Only valid when regex is false.
|
Chris@5
|
28 *
|
Chris@5
|
29 * Examples:
|
Chris@5
|
30 *
|
Chris@5
|
31 * @code
|
Chris@5
|
32 * process:
|
Chris@5
|
33 * 'body/value':
|
Chris@5
|
34 * -
|
Chris@5
|
35 * plugin: dom
|
Chris@5
|
36 * method: import
|
Chris@5
|
37 * source: 'body/0/value'
|
Chris@5
|
38 * -
|
Chris@5
|
39 * plugin: dom_str_replace
|
Chris@5
|
40 * mode: attribute
|
Chris@5
|
41 * expression: '//a'
|
Chris@5
|
42 * attribute_options:
|
Chris@5
|
43 * name: href
|
Chris@5
|
44 * search: 'foo'
|
Chris@5
|
45 * replace: 'bar'
|
Chris@5
|
46 * -
|
Chris@5
|
47 * plugin: dom_str_replace
|
Chris@5
|
48 * mode: attribute
|
Chris@5
|
49 * expression: '//a'
|
Chris@5
|
50 * attribute_options:
|
Chris@5
|
51 * name: href
|
Chris@5
|
52 * regex: true
|
Chris@5
|
53 * search: '/foo/'
|
Chris@5
|
54 * replace: 'bar'
|
Chris@5
|
55 * -
|
Chris@5
|
56 * plugin: dom
|
Chris@5
|
57 * method: export
|
Chris@5
|
58 * @endcode
|
Chris@5
|
59 *
|
Chris@5
|
60 * @MigrateProcessPlugin(
|
Chris@5
|
61 * id = "dom_str_replace"
|
Chris@5
|
62 * )
|
Chris@5
|
63 */
|
Chris@5
|
64 class DomStrReplace extends DomProcessBase {
|
Chris@5
|
65
|
Chris@5
|
66 /**
|
Chris@5
|
67 * {@inheritdoc}
|
Chris@5
|
68 */
|
Chris@5
|
69 public function __construct(array $configuration, $plugin_id, $plugin_definition) {
|
Chris@5
|
70 parent::__construct($configuration, $plugin_id, $plugin_definition);
|
Chris@5
|
71 $this->configuration += [
|
Chris@5
|
72 'case_insensitive' => FALSE,
|
Chris@5
|
73 'regex' => FALSE,
|
Chris@5
|
74 ];
|
Chris@5
|
75 $options_validation = [
|
Chris@5
|
76 'expression' => NULL,
|
Chris@5
|
77 'mode' => ['attribute'],
|
Chris@5
|
78 // @todo Move out once another mode is supported.
|
Chris@5
|
79 // @see https://www.drupal.org/project/migrate_plus/issues/3042833
|
Chris@5
|
80 'attribute_options' => NULL,
|
Chris@5
|
81 'search' => NULL,
|
Chris@5
|
82 'replace' => NULL,
|
Chris@5
|
83 ];
|
Chris@5
|
84 foreach ($options_validation as $option_name => $possible_values) {
|
Chris@5
|
85 if (empty($this->configuration[$option_name])) {
|
Chris@5
|
86 throw new InvalidPluginDefinitionException(
|
Chris@5
|
87 $this->getPluginId(),
|
Chris@5
|
88 "Configuration option '$option_name' is required."
|
Chris@5
|
89 );
|
Chris@5
|
90 }
|
Chris@5
|
91 if (!is_null($possible_values) && !in_array($this->configuration[$option_name], $possible_values)) {
|
Chris@5
|
92 throw new InvalidPluginDefinitionException(
|
Chris@5
|
93 $this->getPluginId(),
|
Chris@5
|
94 sprintf(
|
Chris@5
|
95 'Configuration option "%s" only accepts the following values: %s.',
|
Chris@5
|
96 $option_name,
|
Chris@5
|
97 implode(', ', $possible_values)
|
Chris@5
|
98 )
|
Chris@5
|
99 );
|
Chris@5
|
100 }
|
Chris@5
|
101 }
|
Chris@5
|
102 }
|
Chris@5
|
103
|
Chris@5
|
104 /**
|
Chris@5
|
105 * {@inheritdoc}
|
Chris@5
|
106 */
|
Chris@5
|
107 public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
Chris@5
|
108 $this->init($value, $destination_property);
|
Chris@5
|
109
|
Chris@5
|
110 foreach ($this->xpath->query($this->configuration['expression']) as $html_node) {
|
Chris@5
|
111 $subject = $this->getSubject($html_node);
|
Chris@5
|
112 if (empty($subject)) {
|
Chris@5
|
113 // Could not find subject, skip processing.
|
Chris@5
|
114 continue;
|
Chris@5
|
115 }
|
Chris@5
|
116 $search = $this->getSearch();
|
Chris@5
|
117 $replace = $this->getReplace();
|
Chris@5
|
118 $this->doReplace($html_node, $search, $replace, $subject);
|
Chris@5
|
119 }
|
Chris@5
|
120
|
Chris@5
|
121 return $this->document;
|
Chris@5
|
122 }
|
Chris@5
|
123
|
Chris@5
|
124 /**
|
Chris@5
|
125 * Retrieves the right subject string.
|
Chris@5
|
126 *
|
Chris@5
|
127 * @param \DOMElement $node
|
Chris@5
|
128 * The current element from iteration.
|
Chris@5
|
129 *
|
Chris@5
|
130 * @return string
|
Chris@5
|
131 * The string to use a subject on search.
|
Chris@5
|
132 */
|
Chris@5
|
133 protected function getSubject(\DOMElement $node) {
|
Chris@5
|
134 switch ($this->configuration['mode']) {
|
Chris@5
|
135 case 'attribute':
|
Chris@5
|
136 return $node->getAttribute($this->configuration['attribute_options']['name']);
|
Chris@5
|
137 }
|
Chris@5
|
138 }
|
Chris@5
|
139
|
Chris@5
|
140 /**
|
Chris@5
|
141 * Retrieves the right search string based on configuration.
|
Chris@5
|
142 *
|
Chris@5
|
143 * @return string
|
Chris@5
|
144 * The value to be searched.
|
Chris@5
|
145 */
|
Chris@5
|
146 protected function getSearch() {
|
Chris@5
|
147 switch ($this->configuration['mode']) {
|
Chris@5
|
148 case 'attribute':
|
Chris@5
|
149 return $this->configuration['search'];
|
Chris@5
|
150 }
|
Chris@5
|
151 }
|
Chris@5
|
152
|
Chris@5
|
153 /**
|
Chris@5
|
154 * Retrieves the right replace string based on configuration.
|
Chris@5
|
155 *
|
Chris@5
|
156 * @return string
|
Chris@5
|
157 * The value to use for replacement.
|
Chris@5
|
158 */
|
Chris@5
|
159 protected function getReplace() {
|
Chris@5
|
160 switch ($this->configuration['mode']) {
|
Chris@5
|
161 case 'attribute':
|
Chris@5
|
162 return $this->configuration['replace'];
|
Chris@5
|
163 }
|
Chris@5
|
164 }
|
Chris@5
|
165
|
Chris@5
|
166 /**
|
Chris@5
|
167 * Retrieves the right replace string based on configuration.
|
Chris@5
|
168 *
|
Chris@5
|
169 * @param \DOMElement $html_node
|
Chris@5
|
170 * The current element from iteration.
|
Chris@5
|
171 * @param string $search
|
Chris@5
|
172 * The search string or pattern.
|
Chris@5
|
173 * @param string $replace
|
Chris@5
|
174 * The replacement string.
|
Chris@5
|
175 * @param string $subject
|
Chris@5
|
176 * The string on which to perform the substitution.
|
Chris@5
|
177 */
|
Chris@5
|
178 protected function doReplace(\DOMElement $html_node, $search, $replace, $subject) {
|
Chris@5
|
179 if ($this->configuration['regex']) {
|
Chris@5
|
180 $function = 'preg_replace';
|
Chris@5
|
181 }
|
Chris@5
|
182 elseif ($this->configuration['case_insensitive']) {
|
Chris@5
|
183 $function = 'str_ireplace';
|
Chris@5
|
184 }
|
Chris@5
|
185 else {
|
Chris@5
|
186 $function = "str_replace";
|
Chris@5
|
187 }
|
Chris@5
|
188 $new_subject = $function($search, $replace, $subject);
|
Chris@5
|
189 $this->postReplace($html_node, $new_subject);
|
Chris@5
|
190 }
|
Chris@5
|
191
|
Chris@5
|
192 /**
|
Chris@5
|
193 * Performs post-replace actions.
|
Chris@5
|
194 *
|
Chris@5
|
195 * @param \DOMElement $html_node
|
Chris@5
|
196 * The current element from iteration.
|
Chris@5
|
197 * @param string $new_subject
|
Chris@5
|
198 * The new value to use.
|
Chris@5
|
199 */
|
Chris@5
|
200 protected function postReplace(\DOMElement $html_node, $new_subject) {
|
Chris@5
|
201 switch ($this->configuration['mode']) {
|
Chris@5
|
202 case 'attribute':
|
Chris@5
|
203 $html_node->setAttribute($this->configuration['attribute_options']['name'], $new_subject);
|
Chris@5
|
204 }
|
Chris@5
|
205 }
|
Chris@5
|
206
|
Chris@5
|
207 }
|