Passed
Pull Request — master (#170)
by
unknown
02:21
created

Algolia_Attributes::activate_attributes()   B

Complexity

Conditions 7
Paths 64

Size

Total Lines 49
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 29
c 1
b 0
f 0
nc 64
nop 0
dl 0
loc 49
rs 8.5226
1
<?php
2
3
/**
4
 * Main Algolia Woo Indexer class
5
 * Called from main plugin file algolia-woo-indexer.php
6
 *
7
 * @package algolia-woo-indexer
8
 */
9
10
namespace Algowoo;
11
12
/**
13
 * Definitions for attributes
14
 */
15
define('ATTRIBUTES_ENABLED', '_attributes_enabled');
16
define('ATTRIBUTES_VISIBILITY', '_attributes_visibility');
17
define('ATTRIBUTES_VISIBILITY_STATES', array('all', 'visible', 'hidden'));
18
define('ATTRIBUTES_VARIATION', '_attributes_variation');
19
define('ATTRIBUTES_VARIATION_STATES', array('all', 'used', 'notused'));
20
define('ATTRIBUTES_LIST', '_attributes_list');
21
define('ATTRIBUTES_INTERP', '_attributes_interp');
22
define('ATTRIBUTES_TAX_FIELDS', '_attributes_tax_fields');
23
define('ALLOWED_TAXONOMIES', array(
24
    'term_id',
25
    'name',
26
    'slug',
27
    'term_group',
28
    'description',
29
    'count',
30
    'filter'
31
));
32
33
/**
34
 * definitions of available settings
35
 */
36
define('ATTRIBUTES_SETTINGS', array(
37
    'enabled' => 'Enable indexing of attributes',
38
    'visibility' => 'Visibility',
39
    'variation' => 'Used for variations',
40
    'list' => 'Valid Attributes',
41
    'interp' => 'Numeric Interpolation',
42
    'tax_fields' => 'Content of each attribute term'
43
));
44
45
/**
46
 * Abort if this file is called directly
47
 */
48
if (!defined('ABSPATH')) {
49
    exit;
50
}
51
52
53
if (!class_exists('Algolia_Attributes')) {
54
    /**
55
     * Algolia WooIndexer Attributes
56
     */
57
    class Algolia_Attributes
58
    {
59
60
61
        /**
62
         * Class instance
63
         *
64
         * @var object
65
         */
66
        private static $instance;
67
68
69
        /**
70
         * Setup sections and fields to store and retrieve values from Settings API
71
         *
72
         * @return void
73
         */
74
        public static function setup_attributes_settings()
75
        {
76
            /**"
77
             * Make sure we reference the instance of the current class by using self::get_instance()
78
             * This way we can setup the correct callback function for add_settings_section and add_settings_field
79
             */
80
            $algowoo_attributes = self::get_instance();
81
82
            /**
83
             * Add sections and fields for the attributes
84
             */
85
            add_settings_section(
86
                'algolia_woo_indexer_attributes',
87
                esc_html__('Attributes indexing settings', 'algolia-woo-indexer'),
88
                array($algowoo_attributes, 'algolia_woo_indexer_attributes_section_text'),
89
                'algolia_woo_indexer'
90
            );
91
92
            /**
93
             * Add fields based on ATTRIBUTES_SETTINGS
94
             */
95
            foreach (ATTRIBUTES_SETTINGS as $key => $description) {
96
                add_settings_field(
97
                    'algolia_woo_indexer_attributes_' . $key,
98
                    esc_html__($description, 'algolia-woo-indexer'),
99
                    array($algowoo_attributes, 'algolia_woo_indexer_attributes_' . $key . '_output'),
100
                    'algolia_woo_indexer',
101
                    'algolia_woo_indexer_attributes'
102
                );
103
            }
104
        }
105
106
        /**
107
         * Output for attributes if functionality is enabled
108
         *
109
         * @return void
110
         */
111
        public static function algolia_woo_indexer_attributes_enabled_output()
112
        {
113
            $value = get_option(ALGOWOO_DB_OPTION . ATTRIBUTES_ENABLED);
114
            $isChecked = (!empty($value)) ? 1 : 0;
115
?>
116
            <input id="algolia_woo_indexer_attributes_enabled" name="algolia_woo_indexer_attributes_enabled[checked]" type="checkbox" <?php checked(1, $isChecked); ?> />
117
            <?php
118
        }
119
120
        /**
121
         * Output for attributes how to handle visibility setting
122
         *
123
         * @return void
124
         */
125
        public static function algolia_woo_indexer_attributes_visibility_output()
126
        {
127
            $value = get_option(ALGOWOO_DB_OPTION . ATTRIBUTES_VISIBILITY);
128
            foreach (ATTRIBUTES_VISIBILITY_STATES as $state) {
129
                $id = 'algolia_woo_indexer_attributes_visibility_' . $state;
130
            ?>
131
                <p><input id="<?php echo $id; ?>" name="algolia_woo_indexer_attributes_visibility[value]" type="radio" value="<?php echo $state; ?>" <?php checked($state, $value); ?> /><label for="<?php echo $id; ?>"><?php echo esc_html__($state, 'algolia-woo-indexer'); ?></label></p>
132
            <?php
133
            }
134
        }
135
136
        /**
137
         * Output for attributes how to handle variant setting
138
         *
139
         * @return void
140
         */
141
        public static function algolia_woo_indexer_attributes_variation_output()
142
        {
143
            $value = get_option(ALGOWOO_DB_OPTION . ATTRIBUTES_VARIATION);
144
            foreach (ATTRIBUTES_VARIATION_STATES as $state) {
145
                $id = 'algolia_woo_indexer_attributes_variation_' . $state;
146
            ?>
147
                <p><input id="<?php echo $id; ?>" name="algolia_woo_indexer_attributes_variation[value]" type="radio" value="<?php echo $state; ?>" <?php checked($state, $value); ?> /><label for="<?php echo $id; ?>"><?php echo esc_html__($state, 'algolia-woo-indexer'); ?></label></p>
148
            <?php
149
            }
150
        }
151
152
        /**
153
         * Output for attributes list which attributes are whitelisted
154
         *
155
         * @return void
156
         */
157
        public static function algolia_woo_indexer_attributes_list_output()
158
        {
159
            $value = get_option(ALGOWOO_DB_OPTION . ATTRIBUTES_LIST);
160
            $selectedIds = explode(",", $value);
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type false; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

160
            $selectedIds = explode(",", /** @scrutinizer ignore-type */ $value);
Loading history...
161
            $name = "algolia_woo_indexer_attributes_list[list]";
162
            $description = __('Here you can whitelist all the attributes. Use the <b>shift</b> or <b>control</b> buttons to select multiple attributes.', 'algolia-woo-indexer');
163
            self::generic_attributes_select_output($name, $selectedIds, $description);
164
        }
165
166
        /**
167
         * Output for attributes list which are using a numeric interpolation
168
         *
169
         * @return void
170
         */
171
        public static function algolia_woo_indexer_attributes_interp_output()
172
        {
173
            $value = get_option(ALGOWOO_DB_OPTION . ATTRIBUTES_INTERP);
174
            $selectedIds = explode(",", $value);
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type false; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

174
            $selectedIds = explode(",", /** @scrutinizer ignore-type */ $value);
Loading history...
175
            $name = "algolia_woo_indexer_attributes_interp[list]";
176
            $description = __('If you have some attributes based on number which shall be interpd between the lowest to the highest number, you can select it here. A common usecase for this is if you want to have a <b>range slider</b> in aloglia which works for a certain range. Example: a plant grows between 20 and 25cm tall. for this you enter 20 and 25 as attribute values to your product and it will automatically extend the data to [20,21,22,23,24,25]', 'algolia-woo-indexer');
177
            self::generic_attributes_select_output($name, $selectedIds, $description);
178
        }
179
180
        /**
181
         * Output for attributes list which are using a numeric interpolation
182
         *
183
         * @return void
184
         */
185
        public static function algolia_woo_indexer_attributes_tax_fields_output()
186
        {
187
            $selected_raw = get_option(ALGOWOO_DB_OPTION . ATTRIBUTES_TAX_FIELDS);
188
            $selected_entries = explode(",", $selected_raw);
0 ignored issues
show
Bug introduced by
It seems like $selected_raw can also be of type false; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

188
            $selected_entries = explode(",", /** @scrutinizer ignore-type */ $selected_raw);
Loading history...
189
            $name = "algolia_woo_indexer_attributes_tax_fields[list]";
190
            $description = __('Select which taxonomy fields for each attribute shall be indexed', 'algolia-woo-indexer');
191
            $values = ALLOWED_TAXONOMIES;
192
            ?>
193
            <p><?php echo $description; ?></p>
194
            <select multiple="multiple" name="<?php echo $name; ?>[]" size="<?php echo count($values); ?>">
195
                <?php
196
                foreach ($values as $tax) {
197
                    $selected = in_array($tax, $selected_entries) ? ' selected="selected" ' : '';
198
                ?>
199
                    <option value="<?php echo $tax; ?>" <?php echo $selected; ?>>
200
                        <?php echo __($tax, 'algolia-woo-indexer'); ?>
201
                    </option>
202
                <?php
203
                }
204
                ?>
205
            </select>
206
        <?php
207
        }
208
209
        /**
210
         * Generic Output for attributes list where attributes are whitelisted using Woocommerce attributes taxonomies
211
         * @param string $name id and name for select
212
         * @param array $selected_entries will be preselected if matching with WC taxonomies
213
         * @param string $description will be displayed on top
214
         * 
215
         */
216
        public static function generic_attributes_select_output($name, $selected_entries, $description)
217
        {
218
219
220
            $values = wc_get_attribute_taxonomies();
0 ignored issues
show
Bug introduced by
The function wc_get_attribute_taxonomies was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

220
            $values = /** @scrutinizer ignore-call */ wc_get_attribute_taxonomies();
Loading history...
221
            if (!$values) {
222
                echo esc_html__('You don\'t have any attributes defined yet. Go to WooCommerce and add some to use this feature.', 'algolia-woo-indexer');
223
                return;
224
            }
225
226
        ?>
227
            <p><?php echo $description; ?></p>
228
            <select multiple="multiple" name="<?php echo $name; ?>[]" size="<?php echo count($values); ?>">
229
                <?php
230
                foreach ($values as $tax) {
231
232
                    $id = $tax->attribute_id;
233
                    $label = $tax->attribute_label;
234
                    $name = $tax->attribute_name;
235
                    $selected = in_array($id, $selected_entries) ? ' selected="selected" ' : '';
236
                ?>
237
                    <option value="<?php echo $id; ?>" <?php echo $selected; ?>>
238
                        <?php echo $label . ' (' . $name . ')'; ?>
239
                    </option>
240
                <?php
241
                }
242
                ?>
243
            </select>
244
<?php
245
246
        }
247
248
        /**
249
         * Section text for attributes settings section text
250
         *
251
         * @return void
252
         */
253
        public static function algolia_woo_indexer_attributes_section_text()
254
        {
255
            echo esc_html__('Control if and how the attributes shall be indexed.', 'algolia-woo-indexer');
256
        }
257
258
259
        /**
260
         * parse, sanitize and update attribute settings in DB
261
         *
262
         * @return void
263
         */
264
        public static function update_attribute_options()
265
        {
266
267
            /**
268
             * Filter the inputs
269
             *
270
             * @see https://www.php.net/manual/en/function.filter-input.php
271
             */
272
            $attributes_enabled              = filter_input(INPUT_POST, 'algolia_woo_indexer_attributes_enabled', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
273
            $attributes_visibility           = filter_input(INPUT_POST, 'algolia_woo_indexer_attributes_visibility', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
274
            $attributes_variation            = filter_input(INPUT_POST, 'algolia_woo_indexer_attributes_variation', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
275
            $attributes_list                 = filter_input(INPUT_POST, 'algolia_woo_indexer_attributes_list', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
276
            $attributes_interp               = filter_input(INPUT_POST, 'algolia_woo_indexer_attributes_interp', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
277
            $attributes_tax_fields           = filter_input(INPUT_POST, 'algolia_woo_indexer_attributes_tax_fields', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
278
279
            /**
280
             * Properly sanitize text fields before updating data
281
             *
282
             * @see https://developer.wordpress.org/reference/functions/sanitize_text_field/
283
             */
284
            $sanitized = array();
285
            $sanitized['attributes_visibility']     = sanitize_text_field($attributes_visibility['value']);
286
            $sanitized['attributes_variation']      = sanitize_text_field($attributes_variation['value']);
287
288
            /**
289
             * sanitize select list of id's by getting integers and them implode seperated with comma
290
             */
291
292
            $attributes_list_integers = [];
293
            foreach ($attributes_list['list'] as $id) {
294
                $sanitizedId = sanitize_text_field($id);
295
                array_push($attributes_list_integers, (int) $sanitizedId);
296
            }
297
            $sanitized['attributes_list'] = implode(',', $attributes_list_integers);
298
299
            $attributes_interp_int = [];
300
            foreach ($attributes_interp['list'] as $id) {
301
                $sanitizedId = sanitize_text_field($id);
302
                array_push($attributes_interp_int, (int) $sanitizedId);
303
            }
304
            $sanitized['attributes_interp'] = implode(',', $attributes_interp_int);
305
306
            /**
307
             * only allow values from the ALLOWED_TAXONOMIES to be saved
308
             */
309
            $sanitized['attributes_tax_fields'] = [];
310
            foreach ($attributes_tax_fields['list'] as $name) {
311
                if (in_array($name, ALLOWED_TAXONOMIES)) {
312
                    array_push($sanitized['attributes_tax_fields'], $name);
313
                }
314
            }
315
            $sanitized['attributes_tax_fields'] = implode(',', $sanitized['attributes_tax_fields']);
316
317
            /**
318
             * Sanitizing by setting the value to either 1 or 0
319
             */
320
            $sanitized['attributes_enabled'] = (!empty($attributes_enabled)) ? 1 : 0;
321
322
323
            /**
324
             * Values have been filtered and sanitized
325
             * Check if set and not empty and update the database
326
             *
327
             * @see https://developer.wordpress.org/reference/functions/update_option/
328
             */
329
330
            foreach (array_keys(ATTRIBUTES_SETTINGS) as $key) {
331
                $value = $sanitized['attributes_' . $key];
332
                if (isset($value)) {
333
                    $extension = constant('ATTRIBUTES_' . strtoupper($key));
334
                    update_option(
335
                        ALGOWOO_DB_OPTION . $extension,
336
                        $value
337
                    );
338
                }
339
            }
340
        }
341
342
343
        /**
344
         * The actions to execute when the plugin is activated.
345
         *
346
         * @return void
347
         */
348
        public static function activate_attributes()
349
        {
350
351
            /**
352
             * Set default values for options if not already set
353
             */
354
            $attributes_enabled              = get_option(ALGOWOO_DB_OPTION . ATTRIBUTES_ENABLED);
355
            $attributes_visibility           = get_option(ALGOWOO_DB_OPTION . ATTRIBUTES_VISIBILITY);
356
            $attributes_variation            = get_option(ALGOWOO_DB_OPTION . ATTRIBUTES_VARIATION);
357
            $attributes_list                 = get_option(ALGOWOO_DB_OPTION . ATTRIBUTES_LIST);
358
            $attributes_interp               = get_option(ALGOWOO_DB_OPTION . ATTRIBUTES_INTERP);
359
            $attributes_tax_fields           = get_option(ALGOWOO_DB_OPTION . ATTRIBUTES_TAX_FIELDS);
360
361
362
            if (empty($attributes_enabled)) {
363
                add_option(
364
                    ALGOWOO_DB_OPTION . ATTRIBUTES_ENABLED,
365
                    1
366
                );
367
            }
368
369
            if (empty($attributes_visibility)) {
370
                add_option(
371
                    ALGOWOO_DB_OPTION . ATTRIBUTES_VISIBILITY,
372
                    ATTRIBUTES_VISIBILITY_STATES[0]
373
                );
374
            }
375
            if (empty($attributes_variation)) {
376
                add_option(
377
                    ALGOWOO_DB_OPTION . ATTRIBUTES_VARIATION,
378
                    ATTRIBUTES_VARIATION_STATES[0]
379
                );
380
            }
381
            if (empty($attributes_list)) {
382
                add_option(
383
                    ALGOWOO_DB_OPTION . ATTRIBUTES_LIST,
384
                    ''
385
                );
386
            }
387
            if (empty($attributes_interp)) {
388
                add_option(
389
                    ALGOWOO_DB_OPTION . ATTRIBUTES_INTERP,
390
                    ''
391
                );
392
            }
393
            if (empty($attributes_tax_fields)) {
394
                add_option(
395
                    ALGOWOO_DB_OPTION . ATTRIBUTES_TAX_FIELDS,
396
                    'name,slug'
397
                );
398
            }
399
        }
400
        /**
401
         * Get active object instance
402
         *
403
         * @return object
404
         */
405
        public static function get_instance()
406
        {
407
            if (!self::$instance) {
408
                self::$instance = new Algolia_Attributes();
409
            }
410
            return self::$instance;
411
        }
412
        
413
        /**
414
         * format attributes terms according to settings in ATTRIBUTES_TAX_FIELDS
415
         *
416
         * @param  array $terms list of woocommerce attribute taxonomy
417
         * @return array Array with fields set in config as defined in ATTRIBUTEX_TAX_FIELDS.
418
         */
419
        public static function format_product_attribute_terms($terms, $interpolateValues)
420
        {
421
            $allowed_keys_raw = get_option(ALGOWOO_DB_OPTION . ATTRIBUTES_TAX_FIELDS);
422
            $allowed_keys = explode(',', $allowed_keys_raw);
0 ignored issues
show
Bug introduced by
It seems like $allowed_keys_raw can also be of type false; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

422
            $allowed_keys = explode(',', /** @scrutinizer ignore-type */ $allowed_keys_raw);
Loading history...
423
            $final_terms = array();
424
           
425
            switch ($interpolateValues) {
426
                case true:
427
                    $integers = array();
428
                    foreach ($terms as $term) {
429
                        array_push($integers, (int) $term->name);
430
                    }
431
                    if (count($integers) > 0) {
432
                        for ($i = min($integers); $i <= max($integers); $i++) {
433
                            array_push($final_terms, $i);
434
                        }
435
                    }
436
                    break;
437
                    /**
438
                     * normal mixed content case 
439
                     */
440
                default:
441
                    foreach ($terms as $term) {
442
                        $final_term = array();
443
                        foreach ($allowed_keys as $key) {
444
                            $final_term[$key] = esc_html($term->{$key});
445
                        }
446
                        array_push($final_terms, $final_term);
447
                    }
448
            }
449
            return $final_terms;
450
        }
451
        /**
452
         * Get attributes from product
453
         *
454
         * @param  mixed $product Product to check   
455
         * @return array ['pa_name' => ['value1', 'value2']] Array with key set to the product attribute internal name and values as array. returns false if not attributes found.
456
         */
457
        public static function get_product_attributes($product)
458
        {
459
            /**
460
             * ensure that attrobutes are actually enabled
461
             */
462
            $attributes_enabled = (int) get_option(ALGOWOO_DB_OPTION . ATTRIBUTES_ENABLED);
463
            if ($attributes_enabled !== 1) {
464
                return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
465
            }
466
467
            /**
468
             * gather data and settings
469
             */
470
            $rawAttributes = $product->get_attributes("edit");
471
            $setting_visibility = get_option(ALGOWOO_DB_OPTION . ATTRIBUTES_VISIBILITY);
472
            $setting_variation = get_option(ALGOWOO_DB_OPTION . ATTRIBUTES_VARIATION);
473
            $setting_ids = get_option(ALGOWOO_DB_OPTION . ATTRIBUTES_LIST);
474
            $setting_ids = explode(",", $setting_ids);
0 ignored issues
show
Bug introduced by
It seems like $setting_ids can also be of type false; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

474
            $setting_ids = explode(",", /** @scrutinizer ignore-type */ $setting_ids);
Loading history...
475
            $setting_ids_interp = get_option(ALGOWOO_DB_OPTION . ATTRIBUTES_INTERP);
476
            $setting_ids_interp = explode(",", $setting_ids_interp);
477
478
            if (!$rawAttributes) {
479
                return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type array.
Loading history...
480
            }
481
482
            $attributes = [];
483
            foreach ($rawAttributes as $attribute) {
484
485
                $visibility = $attribute["visible"];
486
                $variation = $attribute["variation"];
487
                $id = $attribute->get_id();
488
                /**
489
                 * skip variable related attributes,
490
                 * ensure that taxonomy is whitelisted and
491
                 * ensure that the visibility and variation is respected
492
                 */
493
                if (
494
                    $attribute->get_variation() ||
495
                    !in_array($id, $setting_ids) ||
496
                    ($setting_visibility ===  "visible" && $visibility === false) ||
497
                    ($setting_visibility ===  "hidden" && $visibility === true) ||
498
                    ($setting_variation ===  "used" && $variation === false) ||
499
                    ($setting_variation ===  "notused" && $variation === true)
500
                ) {
501
                    continue;
502
                }
503
504
505
                $name = $attribute->get_name();
506
                if ($attribute->is_taxonomy()) {
507
                    $terms = wp_get_post_terms($product->get_id(), $name, 'all');
0 ignored issues
show
Bug introduced by
'all' of type string is incompatible with the type array expected by parameter $args of wp_get_post_terms(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

507
                    $terms = wp_get_post_terms($product->get_id(), $name, /** @scrutinizer ignore-type */ 'all');
Loading history...
508
                    $is_interpolation = in_array($id, $setting_ids_interp);
509
                    $attributes[$name] = self::format_product_attribute_terms($terms, $is_interpolation);
0 ignored issues
show
Bug introduced by
$terms of type WP_Error is incompatible with the type array expected by parameter $terms of Algowoo\Algolia_Attribut...oduct_attribute_terms(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

509
                    $attributes[$name] = self::format_product_attribute_terms(/** @scrutinizer ignore-type */ $terms, $is_interpolation);
Loading history...
510
                }
511
                
512
            }
513
            return $attributes;
514
        }
515
    }
516
}
517