PluginProbe
Site Reviews / trunk
Site Reviews vtrunk
8.3.1 8.3.0 8.2.2 8.2.1 8.2.0 8.1.0 8.0.13 8.0.12 8.0.11 trunk 1.2.2 2.17.1 3.5.4 4.7.0 5.25.1 6.11.8 7.0.10 7.0.11 7.0.12 7.0.13 7.0.14 7.0.15 7.0.16 7.0.17 7.0.18 All 54 releases
site-reviews / plugin / Shortcodes / Shortcode.php

Shortcode.php in Site Reviews trunk, at plugin/Shortcodes/Shortcode.php

280 lines 9.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace GeminiLabs\SiteReviews\Shortcodes;
4
5 use GeminiLabs\SiteReviews\Contracts\ShortcodeContract;
6 use GeminiLabs\SiteReviews\Database\ShortcodeOptionManager;
7 use GeminiLabs\SiteReviews\Defaults\DefaultsAbstract;
8 use GeminiLabs\SiteReviews\Helper;
9 use GeminiLabs\SiteReviews\Helpers\Arr;
10 use GeminiLabs\SiteReviews\Helpers\Cast;
11 use GeminiLabs\SiteReviews\Helpers\Str;
12 use GeminiLabs\SiteReviews\Modules\Html\Builder;
13 use GeminiLabs\SiteReviews\Modules\Multilingual;
14 use GeminiLabs\SiteReviews\Modules\Sanitizer;
15 use GeminiLabs\SiteReviews\Modules\Style;
16
17 abstract class Shortcode implements ShortcodeContract
18 {
19 public array $args;
20 public string $debug;
21 public string $description;
22 public string $from;
23 public string $name;
24 public string $tag;
25
26 public function __construct()
27 {
28 $this->args = [];
29 $this->debug = '';
30 $this->description = $this->description();
31 $this->from = '';
32 $this->name = $this->name();
33 $this->tag = $this->tag();
34 }
35
36 /**
37 * The attributes added to the unwrapped rendered root HTML element.
38 */
39 public function attributes(array $values, string $from = 'function'): array
40 {
41 $attributes = $this->defaults()->dataAttributes($values);
42 $attributes = wp_parse_args($attributes, [
43 'class' => $this->classAttr($values['class'] ?? '', isWrapper: false),
44 'data-from' => ($values['from'] ?? '') ?: $from,
45 'data-shortcode' => $this->tag,
46 'id' => $values['id'] ?? '',
47 ]);
48 unset($attributes['data-class']);
49 unset($attributes['data-id']);
50 unset($attributes['data-form_id']);
51 $attributes = glsr()->filterArray("shortcode/attributes/{$this->tag}", $attributes, $this);
52 $attributes = glsr()->filterArray('shortcode/attributes', $attributes, $this);
53 $attributes = array_map('esc_attr', $attributes);
54 return $attributes;
55 }
56
57 public function build(array $args = [], string $from = 'shortcode', bool $isWrapped = true): string
58 {
59 $this->normalize($args, $from);
60 $template = $this->buildTemplate();
61 if (empty($template)) {
62 return '';
63 }
64 $attributes = $this->attributes($this->args, $this->from);
65 $html = glsr(Builder::class)->div($template, $attributes);
66 $rendered = $this->debug.$html;
67 if ($isWrapped) {
68 return $this->wrap($rendered, $args); // pass the original $args here
69 }
70 return $rendered;
71 }
72
73 public function buildCallback(array $args = []): string
74 {
75 $this->enqueue();
76 glsr()->action('shortcode/enqueue', $this);
77 return $this->build($args);
78 }
79
80 public function classAttr(string $classAttr, bool $isWrapper = false): string
81 {
82 $prefixes = [
83 'has-', 'is-', 'items-',
84 ];
85 $rootClasses = [
86 glsr(Style::class)->styleClasses(),
87 ];
88 $wrapClasses = [
89 Str::dashCase("{$this->from}-{$this->tag}"),
90 ];
91 foreach (array_filter(explode(' ', $classAttr)) as $class) {
92 $isPrefixed = !empty(array_filter($prefixes, fn ($p) => str_starts_with($class, $p)));
93 if ($isPrefixed) {
94 $wrapClasses[] = $class;
95 continue;
96 }
97 $rootClasses[] = $class;
98 }
99 $classes = $isWrapper ? $wrapClasses : $rootClasses;
100 return glsr(Sanitizer::class)->sanitizeAttrClass(implode(' ', $classes));
101 }
102
103 public function defaults(): DefaultsAbstract
104 {
105 $classname = str_replace('Shortcodes\\', 'Defaults\\', get_class($this));
106 $classname = str_replace('Shortcode', 'Defaults', $classname);
107 return glsr($classname);
108 }
109
110 public function enqueue(): void
111 {
112 }
113
114 public function hasVisibleFields(array $args = []): bool
115 {
116 if (!empty($args)) {
117 $this->normalize($args);
118 }
119 $defaults = $this->options('hide');
120 $hide = $this->args['hide'] ?? [];
121 $hide = array_flip(Arr::consolidate($hide));
122 unset($defaults['if_empty'], $hide['if_empty']);
123 return !empty(array_diff_key($defaults, $hide));
124 }
125
126 public function normalize(array $args, string $from = ''): ShortcodeContract
127 {
128 $this->args = [];
129 $this->from = ($args['from'] ?? $from) ?: $this->from;
130 $args = glsr()->filterArray("shortcode/args/{$this->tag}", $args, $this);
131 $args = glsr()->filterArray('shortcode/args', $args, $this);
132 $args = $this->defaults()->unguardedRestrict($args);
133 foreach ($args as $key => $value) {
134 $method = Helper::buildMethodName('normalize', $key);
135 if (method_exists($this, $method)) {
136 $value = call_user_func([$this, $method], $value);
137 }
138 $this->args[$key] = $value;
139 }
140 return $this;
141 }
142
143 /**
144 * Returns the options for a shortcode setting. Results are filtered
145 * by the "site-reviews/shortcode/options/{$options}" hook.
146 */
147 public function options(string $option, array $args = []): array
148 {
149 $args['option'] = $option;
150 $args['shortcode'] = $this->tag;
151 return glsr(ShortcodeOptionManager::class)->get($option, $args);
152 }
153
154 public function register(): void
155 {
156 $shortcode = (new \ReflectionClass($this))->getShortName();
157 $shortcode = Str::snakeCase($shortcode);
158 $shortcode = str_replace('_shortcode', '', $shortcode);
159 add_shortcode($shortcode, [$this, 'buildCallback']);
160 glsr()->alias($shortcode, fn () => glsr(get_class($this)));
161 glsr()->append('shortcodes', get_class($this), $shortcode);
162 }
163
164 /**
165 * Returns the filtered shortcode settings configuration.
166 */
167 public function settings(): array
168 {
169 $config = $this->config();
170 $config = glsr()->filterArray("shortcode/config/{$this->tag}", $config, $this);
171 $config = glsr()->filterArray('shortcode/config', $config, $this);
172 return $config;
173 }
174
175 public function tag(): string
176 {
177 $shortName = (new \ReflectionClass($this))->getShortName();
178 return Str::snakeCase(str_replace('Shortcode', '', $shortName));
179 }
180
181 /**
182 * @param array $args The unmodified $args as passed to the build method
183 */
184 public function wrap(string $html, array $args = []): string
185 {
186 return glsr(Builder::class)->div($html, $this->wrapperAttributes($args));
187 }
188
189 /**
190 * Returns the unfiltered shortcode settings configuration.
191 */
192 abstract protected function config(): array;
193
194 protected function debug(array $data = []): void
195 {
196 if (empty($this->args['debug']) || 'shortcode' !== $this->from) {
197 return;
198 }
199 $data = wp_parse_args($data, [
200 'args' => $this->args,
201 'shortcode' => $this->tag,
202 ]);
203 ksort($data);
204 ob_start();
205 glsr_debug($data);
206 $this->debug = ob_get_clean();
207 }
208
209 protected function hideOptions(): array
210 {
211 return [];
212 }
213
214 /**
215 * @param string $value
216 */
217 protected function normalizeAssignedPosts($value): string
218 {
219 $values = Cast::toArray($value);
220 $postTypes = [];
221 foreach ($values as $postType) {
222 if (!is_numeric($postType) && post_type_exists((string) $postType)) {
223 $postTypes[] = $postType;
224 }
225 }
226 $values = glsr(Sanitizer::class)->sanitizePostIds($values);
227 $values = glsr(Multilingual::class)->getPostIdsForAllLanguages($values);
228 $values = array_merge($values, $postTypes);
229 return implode(',', $values);
230 }
231
232 /**
233 * @param string $value
234 */
235 protected function normalizeAssignedTerms($value): string
236 {
237 $values = glsr(Sanitizer::class)->sanitizeTermIds($value);
238 $values = glsr(Multilingual::class)->getTermIdsForAllLanguages($values);
239 return implode(',', $values);
240 }
241
242 /**
243 * @param string $value
244 */
245 protected function normalizeAssignedUsers($value): string
246 {
247 $values = glsr(Sanitizer::class)->sanitizeUserIds($value);
248 return implode(',', $values);
249 }
250
251 protected function normalizeClass(string $value): string
252 {
253 return $this->classAttr($value, isWrapper: false);
254 }
255
256 /**
257 * @param string|array $value
258 */
259 protected function normalizeHide($value): array
260 {
261 $hideKeys = array_keys($this->options('hide'));
262 return array_filter(Cast::toArray($value),
263 fn ($value) => in_array($value, $hideKeys)
264 );
265 }
266
267 protected function wrapperAttributes(array $args): array
268 {
269 $attributes = [
270 'class' => $this->classAttr($args['class'] ?? '', isWrapper: true),
271 'style' => $args['style'] ?? '',
272 ];
273 $attributes = glsr()->filterArray('shortcode/wrap/attributes', $attributes, $args, $this);
274 return array_filter([
275 'class' => glsr(Sanitizer::class)->sanitizeAttrClass($attributes['class'] ?? ''),
276 'style' => glsr(Sanitizer::class)->sanitizeAttrStyle($attributes['style'] ?? ''),
277 ]);
278 }
279 }
280