Passed
Push — develop ( 839f1b...6e51fd )
by Paul
15:21
created

Shortcode::normalizeAssignedPosts()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

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