Passed
Push — develop ( f6f5f6...710bfb )
by Paul
14:13
created

Shortcode::settings()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 4
c 0
b 0
f 0
dl 0
loc 6
rs 10
ccs 0
cts 5
cp 0
cc 1
nc 1
nop 0
crap 2
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 8
    public function __construct()
27
    {
28 8
        $this->args = [];
29 8
        $this->debug = '';
30 8
        $this->description = $this->description();
31 8
        $this->from = '';
32 8
        $this->name = $this->name();
33 8
        $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
        return $this->build($args);
77
    }
78
79
    public function defaults(): DefaultsAbstract
80
    {
81
        $classname = str_replace('Shortcodes\\', 'Defaults\\', get_class($this));
82
        $classname = str_replace('Shortcode', 'Defaults', $classname);
83
        return glsr($classname);
84
    }
85
86
    public function enqueue(): void
87
    {
88
    }
89
90
    public function hasVisibleFields(array $args = []): bool
91
    {
92
        if (!empty($args)) {
93
            $this->normalize($args);
94
        }
95
        $defaults = $this->options('hide');
96
        $hide = $this->args['hide'] ?? [];
97
        $hide = array_flip(Arr::consolidate($hide));
98
        unset($defaults['if_empty'], $hide['if_empty']);
99
        return !empty(array_diff_key($defaults, $hide));
100
    }
101
102
    public function normalize(array $args, string $from = ''): ShortcodeContract
103
    {
104
        $this->args = [];
105
        $this->from = ($args['from'] ?? $from) ?: $this->from;
106
        $args = glsr()->filterArray("shortcode/args/{$this->tag}", $args, $this);
107
        $args = glsr()->filterArray('shortcode/args', $args, $this);
108
        $args = $this->defaults()->unguardedRestrict($args);
109
        foreach ($args as $key => $value) {
110
            $method = Helper::buildMethodName('normalize', $key);
111
            if (method_exists($this, $method)) {
112
                $value = call_user_func([$this, $method], $value);
113
            }
114
            $this->args[$key] = $value;
115
        }
116
        return $this;
117
    }
118
119
    /**
120
     * Returns the options for a shortcode setting. Results are filtered
121
     * by the "site-reviews/shortcode/options/{$options}" hook.
122
     */
123 8
    public function options(string $option, array $args = []): array
124
    {
125 8
        $args['option'] = $option;
126 8
        $args['shortcode'] = $this->tag;
127 8
        return call_user_func([glsr(ShortcodeOptionManager::class), $option], $args);
128
    }
129
130
    public function register(): void
131
    {
132
        $shortcode = (new \ReflectionClass($this))->getShortName();
133
        $shortcode = Str::snakeCase($shortcode);
134
        $shortcode = str_replace('_shortcode', '', $shortcode);
135
        add_shortcode($shortcode, [$this, 'buildCallback']);
136
        glsr()->alias($shortcode, fn () => glsr(get_class($this)));
137
        glsr()->append('shortcodes', get_class($this), $shortcode);
138
    }
139
140
    /**
141
     * Returns the filtered shortcode settings configuration.
142
     */
143
    public function settings(): array
144
    {
145
        $config = $this->config();
146
        $config = glsr()->filterArray("shortcode/config/{$this->tag}", $config, $this);
147
        $config = glsr()->filterArray('shortcode/config', $config, $this);
148
        return $config;
149
    }
150
151 8
    public function tag(): string
152
    {
153 8
        $shortName = (new \ReflectionClass($this))->getShortName();
154 8
        return Str::snakeCase(str_replace('Shortcode', '', $shortName));
155
    }
156
157
    /**
158
     * @param array $args The unmodified $args as passed to the build method
159
     */
160
    public function wrap(string $html, array $args = []): string
161
    {
162
        $attributes = [
163
            'class' => $this->classAttr($args['class'] ?? '', isWrapper: true),
164
        ];
165
        $attributes = glsr()->filterArray('shortcode/wrap/attributes', $attributes, $args, $this);
166
        $attributes = array_map('esc_attr', $attributes);
167
        return glsr(Builder::class)->div($html, $attributes);
168
    }
169
170
    protected function classAttr(string $attr, bool $isWrapper = false): string
171
    {
172
        $prefixes = [
173
            'has-custom-',
174
            'has-text-align-',
175
            'is-custom-',
176
            'is-style-',
177
            'items-justified-',
178
        ];
179
        $classes = array_filter(explode(' ', trim($attr)));
180
        $rootClasses = [
181
            glsr(Style::class)->styleClasses(),
182
        ];
183
        $wrapClasses = [
184
            Str::dashCase("{$this->from}-{$this->tag}"),
185
        ];
186
        foreach ($classes as $class) {
187
            $isPrefixed = !empty(array_filter($prefixes, fn ($p) => str_starts_with($class, $p)));
188
            if ($isPrefixed) {
189
                $wrapClasses[] = $class;
190
                continue;
191
            }
192
            $rootClasses[] = $class;
193
        }
194
        return glsr(Sanitizer::class)->sanitizeAttrClass(
195
            implode(' ', $isWrapper ? $wrapClasses : $rootClasses)
196
        );
197
    }
198
199
    /**
200
     * Returns the unfiltered shortcode settings configuration.
201
     */
202
    abstract protected function config(): array;
203
204
    protected function debug(array $data = []): void
205
    {
206
        if (empty($this->args['debug']) || 'shortcode' !== $this->from) {
207
            return;
208
        }
209
        $data = wp_parse_args($data, [
210
            'args' => $this->args,
211
            'shortcode' => $this->tag,
212
        ]);
213
        ksort($data);
214
        ob_start();
215
        glsr_debug($data);
216
        $this->debug = ob_get_clean();
217
    }
218
219
    protected function hideOptions(): array
220
    {
221
        return [];
222
    }
223
224
    /**
225
     * @param string $value
226
     */
227
    protected function normalizeAssignedPosts($value): string
228
    {
229
        $values = Cast::toArray($value);
230
        $postTypes = [];
231
        foreach ($values as $postType) {
232
            if (!is_numeric($postType) && post_type_exists((string) $postType)) {
233
                $postTypes[] = $postType;
234
            }
235
        }
236
        $values = glsr(Sanitizer::class)->sanitizePostIds($values);
237
        $values = glsr(Multilingual::class)->getPostIdsForAllLanguages($values);
238
        $values = array_merge($values, $postTypes);
239
        return implode(',', $values);
240
    }
241
242
    /**
243
     * @param string $value
244
     */
245
    protected function normalizeAssignedTerms($value): string
246
    {
247
        $values = glsr(Sanitizer::class)->sanitizeTermIds($value);
248
        $values = glsr(Multilingual::class)->getTermIdsForAllLanguages($values);
249
        return implode(',', $values);
250
    }
251
252
    /**
253
     * @param string $value
254
     */
255
    protected function normalizeAssignedUsers($value): string
256
    {
257
        $values = glsr(Sanitizer::class)->sanitizeUserIds($value);
258
        return implode(',', $values);
259
    }
260
261
    protected function normalizeClass(string $value): string
262
    {
263
        return $this->classAttr($value, isWrapper: false);
264
    }
265
266
    /**
267
     * @param string|array $value
268
     */
269
    protected function normalizeHide($value): array
270
    {
271
        $hideKeys = array_keys($this->options('hide'));
272
        return array_filter(Cast::toArray($value),
273
            fn ($value) => in_array($value, $hideKeys)
274
        );
275
    }
276
}
277