Completed
Push — constructionplanless ( e8d891...d7f83f )
by Dominik
01:36
created

Options::addGlobal()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 3
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Flynt\Utils;
4
5
use ACFComposer;
6
7
class Options
8
{
9
    const OPTION_TYPES = [
10
        'translatableOptions' => [
11
            'title' => 'Translatable Options',
12
            'icon' => 'dashicons-translation',
13
            'translatable' => true
14
        ],
15
        'globalOptions' => [
16
            'title' => 'Global Options',
17
            'icon' => 'dashicons-admin-site',
18
            'translatable' => false
19
        ]
20
    ];
21
22
    const OPTION_CATEGORIES = [
23
        'component' => [
24
            'title' => 'Component',
25
            'icon' => 'dashicons-editor-table',
26
            'showType' => true
27
        ],
28
        'customPostType' => [
29
            'title' => 'Custom Post Type',
30
            'icon' => 'dashicons-palmtree',
31
            'showType' => true
32
            // 'label' => [ 'labels', 'menu_item' ], // TODO add this functionality
33
        ],
34
        'feature' => [
35
            'title' => 'Feature',
36
            'icon' => 'dashicons-carrot',
37
            'showType' => true
38
        ]
39
    ];
40
41
    protected static $initialized = false;
42
43
    protected static $optionPages = [];
44
45
    protected static function createOptionPages()
46
    {
47
        if (static::$initialized) {
48
            return;
49
        } else {
50
            static::$initialized = true;
51
        }
52
        foreach (static::OPTION_TYPES as $optionType => $option) {
53
            $title = _x($option['title'], 'title', 'flynt-starter-theme');
54
            $slug = ucfirst($optionType);
55
56
            acf_add_options_page([
57
                'page_title'  => $title,
58
                'menu_title'  => $title,
59
                'redirect'    => true,
60
                'menu_slug'   => $slug,
61
                'icon_url'    => $option['icon']
62
            ]);
63
64
            static::$optionPages[$optionType] = [
65
                'menu_slug' => $slug,
66
                'menu_title' => $title
67
            ];
68
            $fieldGroup = ACFComposer\ResolveConfig::forFieldGroup(
69
                [
70
                    'name' => $slug,
71
                    'title' => $title,
72
                    'style' => 'seamless',
73
                    'fields' => [],
74
                    'location' => [
75
                        [
76
                            [
77
                                'param' => 'options_page',
78
                                'operator' => '==',
79
                                'value' => $slug
80
                            ]
81
                        ]
82
                    ]
83
                ]
84
            );
85
            acf_add_local_field_group($fieldGroup);
86
        }
87
88
        add_action('current_screen', function ($currentScreen) {
89
            foreach (static::OPTION_TYPES as $optionType => $option) {
90
                $isTranslatable = $option['translatable'];
91
                $toplevelPageId = 'toplevel_page_' . $optionType;
92
                $menuTitle = static::$optionPages[$optionType]['menu_title'];
93
                $subPageId = sanitize_title($menuTitle) . '_page_' . $optionType;
94
                $isCurrentPage = StringHelpers::startsWith($toplevelPageId, $currentScreen->id)
95
                || StringHelpers::startsWith($subPageId, $currentScreen->id);
96
97
                if (!$isTranslatable && $isCurrentPage) {
98
                    // set acf field values to default language
99
                    add_filter('acf/settings/current_language', 'Flynt\Features\Acf\OptionPages::getDefaultAcfLanguage', 101);
100
101
                    // hide language selector in admin bar
102
                    add_action('wp_before_admin_bar_render', function () {
103
                        $adminBar = $GLOBALS['wp_admin_bar'];
104
                        $adminBar->remove_menu('WPML_ALS');
105
                    });
106
                }
107
            }
108
        });
109
    }
110
111
112
    // ============
113
    // PUBLIC API
114
    // ============
115
116
    /**
117
     * Get option(s) from a sub page.
118
     *
119
     * Returns an option of a sub page. If no field name is provided it will get all option of that sub page.
120
     * Parameters are expected to be camelCase.
121
     *
122
     * @since 0.2.0 introduced as a replacement for OptionPages::getOption and OptionPages::getOptions
123
     * @since 0.2.2 added check for required hooks to have run to alert of timing issues when used incorrectly
124
     *
125
     * @param string $optionType Type of option page. Either globalOptions or translatableOptions.
126
     * @param string $optionCategory Category of option page. One of these three values: component, feature, customPostType.
127
     * @param string $subPageName Name of the sub page.
128
     * @param string $fieldName (optional) Name of the field to get.
129
     * @return mixed The value of the option or array of options. False if subpage doesn't exist or no option was found.
130
     **/
131
    public static function get($optionType, $optionCategory, $subPageName, $fieldName = null)
132
    {
133
        if (!static::checkRequiredHooks($optionType, $optionCategory, $subPageName, $fieldName)) {
134
            return false;
135
        }
136
137
        // convert parameters
138
        $optionType = lcfirst($optionType);
139
        $optionCategory = ucfirst($optionCategory);
140
        $subPageName = ucfirst($subPageName);
141
142
        if (!isset(static::OPTION_TYPES[$optionType])) {
143
            return false;
144
        }
145
146
        $prefix = implode('', [$optionType, $optionCategory, $subPageName, '_']);
147
        $options = static::getOptionFields(static::OPTION_TYPES[$optionType]['translatable']);
148
        $options = static::collectOptionsWithPrefix($options, $prefix);
149
150
        if (isset($fieldName)) {
151
            $fieldName = lcfirst($fieldName);
152
            return array_key_exists($fieldName, $options) ? $options[$fieldName] : false;
153
        }
154
        return $options;
155
    }
156
157
    public static function addTranslatable($scope, $fields, $category = null)
158
    {
159
        static::addOptions($scope, $fields, 'translatableOptions', $category);
160
    }
161
162
    public static function addGlobal($scope, $fields, $category = null)
163
    {
164
        static::addOptions($scope, $fields, 'globalOptions', $category);
165
    }
166
167
    public static function addOptions($scope, $fields, $type, $category = null)
168
    {
169
        static::createOptionPages();
170
        if (empty($category)) {
171
            global $flyntCurrentOptionCategory;
172
            $category = $flyntCurrentOptionCategory ?? 'component';
173
        }
174
        $optionCategorySettings = static::OPTION_CATEGORIES[$category];
175
        $iconClasses = 'flynt-submenu-item dashicons-before ' . $optionCategorySettings['icon'];
176
        $prettyScope = StringHelpers::splitCamelCase($scope);
177
        $fieldGroupTitle = "<span class='{$iconClasses}'>{$prettyScope}</span>";
178
        $optionsPageSlug = self::$optionPages[$type]['menu_slug'];
179
        $fieldGroupName = $type . ucfirst($category) . $scope;
180
        static::addOptionsFieldGroup($fieldGroupName, $fieldGroupTitle, $optionsPageSlug, $fields);
181
    }
182
183
    protected static function addOptionsFieldGroup($name, $title, $optionsPageSlug, $fields)
184
    {
185
        $fieldGroup = ACFComposer\ResolveConfig::forFieldGroup(
186
            [
187
                'name' => $name,
188
                'title' => $title,
189
                'fields' => array_merge([
190
                    [
191
                        'label' => $title,
192
                        'name' => '',
193
                        'type' => 'accordion',
194
                        'placement' => 'left',
195
                        'endpoint' => false,
196
                    ]
197
                ], $fields),
198
                'style' => 'seamless',
199
                'location' => [
200
                    [
201
                        [
202
                            'param' => 'options_page',
203
                            'operator' => '==',
204
                            'value' => $optionsPageSlug
205
                        ]
206
                    ]
207
                ]
208
            ]
209
        );
210
        $fieldGroup['fields'] = static::prefixFields($fieldGroup['fields'], $name);
211
        foreach ($fieldGroup['fields'] as $field) {
212
            acf_add_local_field(array_merge($field, [
213
                'parent' => 'group_' . $optionsPageSlug,
214
            ]));
215
        }
216
        // acf_add_local_field_group($fieldGroup);
217
    }
218
219
    protected static function prefixFields($fields, $prefix)
220
    {
221
        return array_map(function ($field) use ($prefix) {
222
            $field['name'] = $prefix . '_' . $field['name'];
223
            return $field;
224
        }, $fields);
225
    }
226
227
    protected static function checkRequiredHooks($optionType, $optionCategory, $subPageName, $fieldName)
228
    {
229
        if (did_action('acf/init') < 1) {
230
            $parameters = "${optionType}, ${optionCategory}, ${subPageName}, ";
231
            $parameters .= isset($fieldName) ? $fieldName : 'NULL';
232
            trigger_error("Could not get option/s for [${parameters}]. Required hooks have not yet been executed! Please make sure to run `OptionPages::get()` after the `acf/init` action is finished.", E_USER_WARNING);
233
            return false;
234
        }
235
        return true;
236
    }
237
238
    protected static function getOptionFields($translatable)
239
    {
240
        global $sitepress;
241
242
        if (!isset($sitepress)) {
243
            $options = self::getCachedOptionFields();
244
        } else if ($translatable) {
245
            // get options from cache with language namespace
246
            $options = self::getCachedOptionFields(ICL_LANGUAGE_CODE);
247
        } else {
248
            // switch to default language to get global options
249
            $sitepress->switch_lang(acf_get_setting('default_language'));
250
251
            add_filter('acf/settings/current_language', 'Flynt\Features\Acf\OptionPages::getDefaultAcfLanguage', 100);
252
253
            // get optios from cache with global namespace
254
            $options = self::getCachedOptionFields('global');
255
256
            remove_filter('acf/settings/current_language', 'Flynt\Features\Acf\OptionPages::getDefaultAcfLanguage', 100);
257
258
            $sitepress->switch_lang(ICL_LANGUAGE_CODE);
259
        }
260
261
        return $options;
262
    }
263
264
    protected static function getCachedOptionFields($namespace = '')
265
    {
266
        // get cached options
267
        $found = false;
268
        $suffix = !empty($namespace) ? "_${namespace}" : '';
269
        $cacheKey = "Features/Acf/OptionPages/ID=options${suffix}";
270
271
        $options = wp_cache_get($cacheKey, 'flynt', null, $found);
272
273
        if (!$found) {
274
            $options = get_fields('options');
275
            wp_cache_set($cacheKey, $options, 'flynt');
276
        }
277
278
        return $options;
279
    }
280
281
    // find and replace relevant keys, then return an array of all options for this Sub-Page
282
    protected static function collectOptionsWithPrefix($options, $prefix)
283
    {
284
        $optionKeys = is_array($options) ? array_keys($options) : [];
285
        return array_reduce($optionKeys, function ($carry, $key) use ($options, $prefix) {
286
            $count = 0;
287
            $option = $options[$key];
288
            $key = str_replace($prefix, '', $key, $count);
289
            if ($count > 0) {
290
                $carry[$key] = $option;
291
            }
292
            return $carry;
293
        }, []);
294
    }
295
}
296