Passed
Push — master ( 4f783c...449b2c )
by Paul
07:14
created

ListTableController   F

Complexity

Total Complexity 66

Size/Duplication

Total Lines 405
Duplicated Lines 0 %

Test Coverage

Coverage 1.5%

Importance

Changes 2
Bugs 0 Features 1
Metric Value
wmc 66
eloc 183
c 2
b 0
f 1
dl 0
loc 405
ccs 3
cts 200
cp 0.015
rs 3.12

20 Methods

Rating   Name   Duplication   Size   Complexity  
A filterScreenFilters() 0 25 5
A overrideInlineSaveAjax() 0 26 5
A filterDateColumnStatus() 0 4 1
B filterCheckLockedReviews() 0 23 8
A filterDefaultHiddenColumns() 0 9 2
A renderColumnValues() 0 12 2
A overridePostsListTable() 0 10 3
A filterColumnsForPostType() 0 10 4
A filterPostClauses() 0 13 6
A filterSortableColumns() 0 11 4
A renderColumnFilters() 0 9 3
B filterRowActions() 0 33 6
A modifyClauseJoin() 0 5 1
A isListFiltered() 0 3 1
A modifyClauseOrderby() 0 11 3
A setQueryForColumn() 0 18 5
A filterByValues() 0 5 1
A isListOrdered() 0 4 1
A isOrderbyWithIsNull() 0 7 1
A modifyClauseWhere() 0 31 4

How to fix   Complexity   

Complex Class

Complex classes like ListTableController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ListTableController, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace GeminiLabs\SiteReviews\Controllers;
4
5
use GeminiLabs\SiteReviews\Database\Query;
6
use GeminiLabs\SiteReviews\Database\ReviewManager;
7
use GeminiLabs\SiteReviews\Defaults\ColumnFilterbyDefaults;
8
use GeminiLabs\SiteReviews\Defaults\ColumnOrderbyDefaults;
9
use GeminiLabs\SiteReviews\Defaults\ReviewTableFiltersDefaults;
10
use GeminiLabs\SiteReviews\Helper;
11
use GeminiLabs\SiteReviews\Helpers\Arr;
12
use GeminiLabs\SiteReviews\Helpers\Cast;
13
use GeminiLabs\SiteReviews\Helpers\Str;
14
use GeminiLabs\SiteReviews\Modules\Html\Builder;
15
use GeminiLabs\SiteReviews\Modules\Migrate;
16
use GeminiLabs\SiteReviews\Overrides\ReviewsListTable;
17
use WP_Post;
18
use WP_Query;
19
use WP_Screen;
20
21
class ListTableController extends Controller
22
{
23
    /**
24
     * @param array $response
25
     * @param array $data
26
     * @param string $screenId
27
     * @return array
28
     * @filter heartbeat_received
29
     */
30
    public function filterCheckLockedReviews($response, $data, $screenId)
31
    {
32
        $checked = [];
33
        if (!is_array(Arr::get($data, 'wp-check-locked-posts'))) {
34
            return $response;
35
        }
36
        foreach ($data['wp-check-locked-posts'] as $key) {
37
            $postId = absint(substr($key, 5));
38
            $userId = (int) wp_check_post_lock($postId);
39
            $user = get_userdata($userId);
40
            if ($user && !glsr()->can('edit_post', $postId) && glsr()->can('respond_to_post', $postId)) {
41
                $send = ['text' => sprintf(_x('%s is currently editing', 'admin-text', 'site-reviews'), $user->display_name)];
42
                if (get_option('show_avatars')) {
43
                    $send['avatar_src'] = get_avatar_url($user->ID, ['size' => 18]);
44
                    $send['avatar_src_2x'] = get_avatar_url($user->ID, ['size' => 36]);
45
                }
46
                $checked[$key] = $send;
47
            }
48
        }
49
        if (!empty($checked)) {
50
            $response['wp-check-locked-posts'] = $checked;
51
        }
52
        return $response;
53
    }
54
55
    /**
56
     * @param array $columns
57
     * @return array
58
     * @filter manage_{glsr()->post_type}_posts_columns
59
     */
60
    public function filterColumnsForPostType($columns)
61
    {
62
        $columns = Arr::consolidate($columns);
63
        $postTypeColumns = glsr()->retrieve('columns.'.glsr()->post_type, []);
64
        foreach ($postTypeColumns as $key => &$value) {
65
            if (array_key_exists($key, $columns) && empty($value)) {
66
                $value = $columns[$key];
67
            }
68
        }
69
        return array_filter($postTypeColumns, 'strlen');
70
    }
71
72
    /**
73
     * @param string $status
74
     * @param WP_Post $post
75
     * @return string
76
     * @filter post_date_column_status
77
     */
78
    public function filterDateColumnStatus($status, $post)
79
    {
80
        $isReview = glsr()->post_type === Arr::get($post, 'post_type');
81
        return Helper::ifTrue(!$isReview, $status, _x('Submitted', 'admin-text', 'site-reviews'));
82
    }
83
84
    /**
85
     * @param array $hidden
86
     * @param WP_Screen $screen
87
     * @return array
88
     * @filter default_hidden_columns
89
     */
90
    public function filterDefaultHiddenColumns($hidden, $screen)
91
    {
92
        if (Arr::get($screen, 'id') === 'edit-'.glsr()->post_type) {
93
            $hidden = Arr::consolidate($hidden);
94
            $hidden = array_unique(array_merge($hidden, [
95
                'assigned_users', 'author_name', 'author_email', 'ip_address', 'response',
96
            ]));
97
        }
98
        return $hidden;
99
    }
100
101
    /**
102
     * @return array
103
     * @filter posts_clauses
104
     */
105
    public function filterPostClauses(array $clauses, WP_Query $query)
106
    {
107
        if (!$this->hasQueryPermission($query) || (!$this->isListFiltered() && !$this->isListOrdered())) {
108
            return $clauses;
109
        }
110
        $table = glsr(Query::class)->table('ratings');
111
        foreach ($clauses as $key => &$clause) {
112
            $method = Helper::buildMethodName($key, 'modifyClause');
113
            if (method_exists($this, $method)) {
114
                $clause = call_user_func([$this, $method], $clause, $table, $query);
115
            }
116
        }
117
        return glsr()->filterArray('review-table/clauses', $clauses, $table, $query);
118
    }
119
120
    /**
121
     * @param array $actions
122
     * @param WP_Post $post
123
     * @return array
124
     * @filter post_row_actions
125
     */
126
    public function filterRowActions($actions, $post)
127
    {
128
        if (glsr()->post_type !== Arr::get($post, 'post_type') || 'trash' == $post->post_status) {
129
            return $actions;
130
        }
131
        unset($actions['inline hide-if-no-js']);
132
        $newActions = ['id' => sprintf(_x('<span>ID: %d</span>', 'The Review Post ID (admin-text)', 'site-reviews'), $post->ID)];
133
        if (glsr()->can('publish_post', $post->ID)) {
134
            $rowActions = [
135
                'approve' => _x('Approve', 'admin-text', 'site-reviews'),
136
                'unapprove' => _x('Unapprove', 'admin-text', 'site-reviews'),
137
            ];
138
            foreach ($rowActions as $key => $text) {
139
                $newActions[$key] = glsr(Builder::class)->a($text, [
140
                    'aria-label' => esc_attr(sprintf(_x('%s this review', 'Approve the review (admin-text)', 'site-reviews'), $text)),
141
                    'class' => 'glsr-toggle-status',
142
                    'href' => wp_nonce_url(
143
                        admin_url('post.php?post='.$post->ID.'&action='.$key.'&plugin='.glsr()->id),
144
                        $key.'-review_'.$post->ID
145
                    ),
146
                ]);
147
            }
148
        }
149
        if (glsr()->can('respond_to_post', $post->ID)) {
150
            $newActions['respond hide-if-no-js'] = glsr(Builder::class)->button([
151
                'aria-expanded' => false,
152
                'aria-label' => esc_attr(sprintf(_x('Respond inline to &#8220;%s&#8221;', 'admin-text', 'site-reviews'), _draft_or_post_title())),
153
                'class' => 'button-link editinline',
154
                'text' => _x('Respond', 'admin-text', 'site-reviews'),
155
                'type' => 'button',
156
            ]);
157
        }
158
        return $newActions + Arr::consolidate($actions);
159
    }
160
161
    /**
162
     * @param \WP_Screen $screen
163
     * @return string
164
     * @filter screen_settings
165
     */
166
    public function filterScreenFilters($settings, $screen)
167
    {
168
        if ('edit-'.glsr()->post_type === $screen->id) {
169
            $userId = get_current_user_id();
170
            $filters = glsr(ReviewTableFiltersDefaults::class)->defaults();
171
            if (count(glsr()->retrieveAs('array', 'review_types')) < 2) {
172
                unset($filters['type']);
173
            }
174
            foreach ($filters as $key => &$value) {
175
                $value = Str::titleCase($key);
176
            }
177
            ksort($filters);
178
            $setting = 'edit_'.glsr()->post_type.'_filters';
179
            $enabled = get_user_meta($userId, $setting, true);
180
            if (!is_array($enabled)) {
181
                $enabled = ['rating']; // the default enabled filters
182
                update_user_meta($userId, $setting, $enabled);
183
            }
184
            $settings .= glsr()->build('partials/screen/filters', [
185
                'enabled' => $enabled,
186
                'filters' => $filters,
187
                'setting' => $setting,
188
            ]);
189
        }
190
        return $settings;
191
    }
192
193
    /**
194
     * @param array $columns
195
     * @return array
196
     * @filter manage_edit-{glsr()->post_type}_sortable_columns
197
     */
198
    public function filterSortableColumns($columns)
199
    {
200
        $columns = Arr::consolidate($columns);
201
        $postTypeColumns = glsr()->retrieve('columns.'.glsr()->post_type, []);
202
        unset($postTypeColumns['cb']);
203
        foreach ($postTypeColumns as $key => $value) {
204
            if (!Str::startsWith('assigned', $key) && !Str::startsWith('taxonomy', $key)) {
205
                $columns[$key] = $key;
206
            }
207
        }
208
        return $columns;
209
    }
210
211
    /**
212
     * @return void
213
     * @action wp_ajax_inline_save
214
     */
215
    public function overrideInlineSaveAjax()
216
    {
217
        $screen = filter_input(INPUT_POST, 'screen');
218
        if ('edit-'.glsr()->post_type !== $screen) {
219
            return; // don't override
220
        }
221
        global $mode;
222
        check_ajax_referer('inlineeditnonce', '_inline_edit');
223
        if (empty($postId = filter_input(INPUT_POST, 'post_ID', FILTER_VALIDATE_INT))) {
224
            wp_die();
225
        }
226
        if (!glsr()->can('respond_to_post', $postId)) {
227
            wp_die(_x('Sorry, you are not allowed to respond to this review.', 'admin-text', 'site-reviews'));
228
        }
229
        if ($last = wp_check_post_lock($postId)) {
230
            $user = get_userdata($last);
231
            $username = Arr::get($user, 'display_name', _x('Someone', 'admin-text', 'site-reviews'));
232
            $message = _x('Saving is disabled: %s is currently editing this review.', 'admin-text', 'site-reviews');
233
            printf($message, esc_html($username));
234
            wp_die();
235
        }
236
        glsr(ReviewManager::class)->updateResponse($postId, filter_input(INPUT_POST, '_response'));
237
        $mode = Str::restrictTo(['excerpt', 'list'], filter_input(INPUT_POST, 'post_view'), 'list');
238
        $table = new ReviewsListTable(['screen' => convert_to_screen($screen)]);
239
        $table->display_rows([get_post($postId)], 0);
240
        wp_die();
241
    }
242
243
    /**
244
     * @return void
245
     * @action load-edit.php
246
     */
247
    public function overridePostsListTable()
248
    {
249
        if ('edit-'.glsr()->post_type === glsr_current_screen()->id
250
            && glsr()->can('respond_to_posts')) {
251
            $table = new ReviewsListTable();
252
            $table->prepare_items();
253
            add_filter('views_edit-'.glsr()->post_type, function ($views) use ($table) {
254
                global $wp_list_table;
255
                $wp_list_table = clone $table;
256
                return $views;
257
            });
258
        }
259
    }
260
261
    /**
262
     * @param string $postType
263
     * @return void
264
     * @action restrict_manage_posts
265
     */
266
    public function renderColumnFilters($postType)
267
    {
268
        if (glsr()->post_type === $postType) {
269
            $filters = glsr(ReviewTableFiltersDefaults::class)->defaults();
270
            $enabledFilters = Arr::consolidate(
271
                get_user_meta(get_current_user_id(), 'edit_'.glsr()->post_type.'_filters', true)
272
            );
273
            foreach ($filters as $filter) {
274
                echo Cast::toString(glsr()->runIf($filter, $enabledFilters));
275
            }
276
        }
277
    }
278
279
    /**
280
     * @param string $column
281
     * @param int $postId
282
     * @return void
283
     * @action manage_{glsr()->post_type}_posts_custom_column
284
     */
285
    public function renderColumnValues($column, $postId)
286
    {
287
        $review = glsr(Query::class)->review($postId);
288
        if (!$review->isValid()) {
289
            glsr(Migrate::class)->reset(); // looks like a migration is needed!
290
            return;
291
        }
292
        $className = Helper::buildClassName(['ColumnValue', $column], 'Controllers\ListTableColumns');
293
        $className = glsr()->filterString('column/'.$column, $className);
294
        $value = glsr()->runIf($className, $review);
295
        $value = glsr()->filterString('columns/'.$column, $value, $postId);
296
        echo Helper::ifEmpty($value, '&mdash;');
297
    }
298
299
    /**
300
     * @return void
301
     * @action pre_get_posts
302
     */
303 15
    public function setQueryForColumn(WP_Query $query)
304
    {
305 15
        if (!$this->hasQueryPermission($query)) {
306 15
            return;
307
        }
308
        $orderby = $query->get('orderby');
309
        if ('response' === $orderby) {
310
            $query->set('meta_key', Str::prefix($orderby, '_'));
311
            $query->set('orderby', 'meta_value');
312
        }
313
        if ($termId = filter_input(INPUT_GET, 'assigned_term', FILTER_SANITIZE_NUMBER_INT)) {
314
            $taxQuery = ['taxonomy' => glsr()->taxonomy];
315
            if (-1 === Cast::toInt($termId)) {
316
                $taxQuery['operator'] = 'NOT EXISTS';
317
            } else {
318
                $taxQuery['terms'] = $termId;
319
            }
320
            $query->set('tax_query', [$taxQuery]);
321
        }
322
    }
323
324
    /**
325
     * @return array
326
     * */
327
    protected function filterByValues()
328
    {
329
        $filterBy = glsr(ColumnFilterbyDefaults::class)->defaults();
330
        $filterBy = filter_input_array(INPUT_GET, $filterBy);
331
        return Arr::removeEmptyValues(Arr::consolidate($filterBy));
332
    }
333
334
    /**
335
     * @return bool
336
     */
337
    protected function isListFiltered()
338
    {
339
        return !empty($this->filterByValues());
340
    }
341
342
    /**
343
     * @return bool
344
     */
345
    protected function isListOrdered()
346
    {
347
        $columns = glsr(ColumnOrderbyDefaults::class)->defaults();
348
        return array_key_exists(get_query_var('orderby'), $columns);
349
    }
350
351
    /**
352
     * @return bool
353
     */
354
    protected function isOrderbyWithIsNull($column)
355
    {
356
        $columns = [
357
            'email', 'name', 'ip_address', 'type',
358
        ];
359
        $columns = glsr()->filterArray('columns/orderby-is-null', $columns);
360
        return in_array($column, $columns);
361
    }
362
363
    /**
364
     * @param string $join
365
     * @return string
366
     */
367
    protected function modifyClauseJoin($join, $table, WP_Query $query)
368
    {
369
        global $wpdb;
370
        $join .= " INNER JOIN {$table} ON {$table}.review_id = {$wpdb->posts}.ID ";
371
        return $join;
372
    }
373
374
    /**
375
     * @param string $orderby
376
     * @return string
377
     */
378
    protected function modifyClauseOrderby($orderby, $table, WP_Query $query)
379
    {
380
        $columns = glsr(ColumnOrderbyDefaults::class)->defaults();
381
        if ($column = Arr::get($columns, $query->get('orderby'))) {
382
            $order = $query->get('order');
383
            $orderby = "{$table}.{$column} {$order}";
384
            if ($this->isOrderbyWithIsNull($column)) {
385
                $orderby = "NULLIF({$table}.{$column}, '') IS NULL, {$orderby}";
386
            }
387
        }
388
        return $orderby;
389
    }
390
391
    /**
392
     * @param string $where
393
     * @return string
394
     */
395
    protected function modifyClauseWhere($where, $table, WP_Query $query)
396
    {
397
        $mapped = [
398
            'assigned_post' => 'post',
399
            'assigned_user' => 'user',
400
        ];
401
        foreach ($this->filterByValues() as $key => $value) {
402
            if (in_array($key, ['assigned_post', 'assigned_user'])) {
403
                global $wpdb;
404
                $assignedTable = glsr(Query::class)->table($key.'s');
405
                if (-1 === Cast::toInt($value)) {
406
                    $ids = $wpdb->get_col("
407
                        SELECT DISTINCT r.review_id 
408
                        FROM {$table} r
409
                        LEFT JOIN {$assignedTable} at ON at.rating_id = r.ID
410
                        WHERE at.{$mapped[$key]}_id IS NULL
411
                    ");
412
                } else {
413
                    $ids = $wpdb->get_col("
414
                        SELECT DISTINCT r.review_id 
415
                        FROM {$table} r
416
                        INNER JOIN {$assignedTable} at ON at.rating_id = r.ID 
417
                        WHERE at.{$mapped[$key]}_id = '{$value}' 
418
                    ");
419
                }
420
                $where .= sprintf(" AND {$wpdb->posts}.ID IN (%s) ", implode(',', $ids));
421
            } else {
422
                $where .= " AND {$table}.{$key} = '{$value}' ";
423
            }
424
        }
425
        return $where;
426
    }
427
}
428