Test Failed
Push — develop ( 4ba53a...c20cce )
by Paul
10:12
created

Migrate_8_0_0::updateElementorAssignments()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 11
c 1
b 1
f 0
dl 0
loc 17
rs 9.9
cc 4
nc 5
nop 1
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Migrations;
4
5
use GeminiLabs\SiteReviews\Contracts\MigrateContract;
6
use GeminiLabs\SiteReviews\Database;
7
use GeminiLabs\SiteReviews\Database\Query;
8
use GeminiLabs\SiteReviews\Database\Tables\TableStats;
9
use GeminiLabs\SiteReviews\Helpers\Str;
10
11
class Migrate_8_0_0 implements MigrateContract
12
{
13
    protected bool $updateElementor;
14
15
    public function run(): bool
16
    {
17
        $this->migrateElementor();
18
        $this->migrateFusionBuilder();
19
        return $this->migrateDatabase();
20
    }
21
22
    public function migrateDatabase(): bool
23
    {
24
        $isDirty = false;
25
        $indexes = glsr(Database::class)->dbGetResults(
26
            glsr(Query::class)->sql("SHOW INDEXES FROM table|ratings")
27
        );
28
        $keyNames = wp_list_pluck($indexes, 'Key_name');
29
        if (!in_array('glsr_ratings_ip_address_index', $keyNames)) {
30
            $sql = glsr(Query::class)->sql("
31
                ALTER TABLE table|ratings ADD INDEX glsr_ratings_ip_address_index (ip_address)
32
            ");
33
            if (false === glsr(Database::class)->dbQuery($sql)) {
34
                glsr_log()->error("The ratings table could not be altered, the [ip_address_index] index was not added.");
35
                $isDirty = true;
36
            }
37
        }
38
        glsr(TableStats::class)->create();
39
        glsr(TableStats::class)->addForeignConstraints();
40
        if (!glsr(TableStats::class)->exists() || $isDirty) {
41
            return false;
42
        }
43
        update_option(glsr()->prefix.'db_version', '1.5');
44
        return true;
45
    }
46
47
    public function migrateElementor(): void
48
    {
49
        if (!class_exists('Elementor\Plugin')) {
50
            return;
51
        }
52
        $sql = glsr(Query::class)->sql("
53
            SELECT post_id
54
            FROM table|postmeta 
55
            WHERE meta_key = '_elementor_data'
56
            AND meta_value LIKE %s
57
        ", '%"widgetType":"site_review%');
58
        $postIds = glsr(Database::class)->dbGetCol($sql);
59
        if (empty($postIds)) {
60
            return;
61
        }
62
        foreach ($postIds as $postId) {
63
            $this->updateElementor = false;
64
            $document = \Elementor\Plugin::$instance->documents->get($postId);
65
            if ($document) {
66
                $data = $document->get_elements_data(); // @phpstan-ignore-line
67
            }
68
            if (empty($data)) {
69
                continue;
70
            }
71
            $data = \Elementor\Plugin::$instance->db->iterate_data($data, function ($element) {
72
                $shortcode = $element['widgetType'] ?? '';
73
                if (!str_starts_with($shortcode, 'site_review')) {
74
                    return $element;
75
                }
76
                if (empty($element['settings'])) {
77
                    return $element;
78
                }
79
                $element = $this->updateElementorAssignments($element);
80
                $element = $this->updateElementorCheckboxes($element);
81
                $element = $this->updateElementorStyles($element);
82
                return $element;
83
            });
84
            if (!$this->updateElementor) { // @phpstan-ignore-line
85
                continue;
86
            }
87
            // We need the `wp_slash` because `update_post_meta` does `wp_unslash`
88
            $json = wp_slash(wp_json_encode($data)); // @phpstan-ignore-line
0 ignored issues
show
Bug introduced by
It seems like wp_json_encode($data) can also be of type false; however, parameter $value of wp_slash() does only seem to accept array|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

88
            $json = wp_slash(/** @scrutinizer ignore-type */ wp_json_encode($data)); // @phpstan-ignore-line
Loading history...
89
            update_metadata('post', $postId, '_elementor_data', $json);
90
        }
91
    }
92
93
    public function migrateFusionBuilder(): void
94
    {
95
        if (!defined('FUSION_BUILDER_VERSION')) {
96
            return;
97
        }
98
        $sql = glsr(Query::class)->sql("
99
            SELECT ID, post_content
100
            FROM table|posts
101
            WHERE 1=1
102
            AND post_type != 'revision'
103
            AND post_content LIKE %s
104
            AND (post_content LIKE %s OR post_content LIKE %s)
105
        ", '%[fusion_builder_container%', '%assigned_posts_custom=%', '%assigned_users_custom=%');
106
        $posts = glsr(Database::class)->dbGetResults($sql);
107
        if (empty($posts)) {
108
            return;
109
        }
110
        foreach ($posts as $post) {
111
            $pattern = '/assigned_(posts|users)="([^"]*)"\s*assigned_\1_custom="([^"]*)"/';
112
            $replace = function (array $matches): string {
113
                $type = $matches[1];
114
                $custom = explode(',', (string) $matches[3]);
115
                $values = explode(',', (string) $matches[2]);
116
                $values = array_filter($values, fn ($value) => 'custom' !== trim($value));
117
                $values = array_filter(array_merge($custom, $values));
118
                $values = implode(',', $values);
119
                return sprintf('assigned_%s="%s"', $type, $values);
120
            };
121
            $count = 0;
122
            $content = preg_replace_callback($pattern, $replace, $post->post_content, -1, $count);
123
            if ($count > 0) {
124
                $result = wp_update_post([
125
                    'ID' => $post->ID,
126
                    'post_content' => $content,
127
                ], true);
128
                if (is_wp_error($result)) {
129
                    glsr_log()->error("Failed to migrate Fusion Builder Post {$post->ID}: {$result->get_error_message()}");
130
                }
131
            }
132
        }
133
    }
134
135
    protected function updateElementorAssignments(array $element): array
136
    {
137
        $assignments = [
138
            'assigned_posts' => 'assigned_posts_custom',
139
            'assigned_users' => 'assigned_users_custom',
140
        ];
141
        foreach ($assignments as $assignment => $custom) {
142
            if ('custom' === ($element['settings'][$assignment] ?? '')) {
143
                $this->updateElementor = true;
144
                $element['settings'][$assignment] = $element['settings'][$custom] ?? '';
145
            }
146
            if (isset($element['settings'][$custom])) {
147
                $this->updateElementor = true;
148
                unset($element['settings'][$custom]);
149
            }
150
        }
151
        return $element;
152
    }
153
154
    protected function updateElementorCheckboxes(array $element): array
155
    {
156
        $replacements = [
157
            'hide' => [
158
                'prefix' => 'hide-',
159
                'values' => [],
160
            ],
161
            'filters' => [ // used by the Review Filters addon
162
                'prefix' => 'filter-',
163
                'values' => [],
164
            ],
165
        ];
166
        foreach ($element['settings'] as $key => $value) {
167
            foreach ($replacements as $setting => $r) {
168
                if (str_starts_with($key, $r['prefix']) && !empty($value)) {
169
                    $replacements[$setting]['values'][] = Str::removePrefix($key, $r['prefix']);
170
                    unset($element['settings'][$key]);
171
                    break;
172
                }
173
            }
174
        }
175
        foreach ($replacements as $setting => $r) {
176
            if (!empty($r['values'])) {
177
                $this->updateElementor = true;
178
                $element['settings'][$setting] = implode(',', $r['values']);
179
            }
180
        }
181
        return $element;
182
    }
183
184
    protected function updateElementorStyles(array $element): array
185
    {
186
        $replacements = [
187
            'alignment' => 'style_align',
188
            'alignment_mobile' => 'style_align_mobile',
189
            'alignment_tablet' => 'style_align_tablet',
190
            'max_width' => 'style_max_width',
191
            'max_width_mobile' => 'style_max_width_mobile',
192
            'max_width_tablet' => 'style_max_width_tablet',
193
            'percentage_bar_height' => 'style_bar_size',
194
            'percentage_bar_height_mobile' => 'style_bar_size_mobile',
195
            'percentage_bar_height_tablet' => 'style_bar_size_tablet',
196
            'percentage_bar_spacing' => 'style_bar_gap',
197
            'percentage_bar_spacing_mobile' => 'style_bar_gap_mobile',
198
            'percentage_bar_spacing_tablet' => 'style_bar_gap_tablet',
199
            'rating_color' => 'style_rating_color',
200
            'rating_size' => 'style_rating_size',
201
            'rating_size_mobile' => 'style_rating_size_mobile',
202
            'rating_size_tablet' => 'style_rating_size_tablet',
203
            'rating_spacing' => 'style_rating_gap',
204
            'rating_spacing_mobile' => 'style_rating_gap_mobile',
205
            'rating_spacing_tablet' => 'style_rating_gap_tablet',
206
            'spacing' => 'style_row_gap',
207
            'spacing_mobile' => 'style_row_gap_mobile',
208
            'spacing_tablet' => 'style_row_gap_tablet',
209
        ];
210
        foreach ($replacements as $old => $new) {
211
            if (!array_key_exists($old, $element['settings'])) {
212
                continue;
213
            }
214
            $element['settings'][$new] = $element['settings'][$old];
215
            unset($element['settings'][$old]);
216
            $this->updateElementor = true;
217
        }
218
        if ($color = $element['settings']['__globals__']['rating_color'] ?? false) {
219
            $element['settings']['__globals__']['style_rating_color'] = $color;
220
            unset($element['settings']['__globals__']['rating_color']);
221
            $this->updateElementor = true;
222
        }
223
        return $element;
224
    }
225
}
226