Completed
Push — beta ( c62a0f...56d793 )
by
unknown
05:09 queued 11s
created

Core::add_attachment()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/*
3
 *
4
 *
5
 * inspired by https://github.com/adamsilverstein/wp-post-meta-revisions/blob/master/wp-post-meta-revisions.php
6
 * many thx @adamsilverstein
7
 *
8
 */
9
10
namespace KMM\Timeshift;
11
12
class Core {
13
    private $plugin_dir;
14
    private $last_author = false;
15
    private $timeshift_cached_meta;
16
17 15
    public function __construct($i18n) {
18
        global $wpdb;
19 15
        $this->i18n = $i18n;
0 ignored issues
show
Bug introduced by
The property i18n does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
20 15
        $this->wpdb = $wpdb;
0 ignored issues
show
Bug introduced by
The property wpdb does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
21 15
        $this->plugin_dir = plugin_dir_url(__FILE__) . '../';
22 15
        $this->timeshift_posts_per_page = 5;
0 ignored issues
show
Bug introduced by
The property timeshift_posts_per_page does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
23 15
        $this->pagination_ajax_action = 'pagination_timeshift';
0 ignored issues
show
Bug introduced by
The property pagination_ajax_action does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
24 15
        $this->add_filters();
25 15
        $this->add_actions();
26 15
        $this->add_metabox();
27
        // Disable WP's own revision system
28 15
        remove_post_type_support('post', 'revisions');
29
    }
30
31 2
    public function hasTimeshifts($post_id) {
32 2
        $post_type = get_post_type($post_id);
33 2
        $table_name = $this->wpdb->prefix . 'timeshift_' . $post_type;
34 2
        $this->checkTable($post_type);
35 2
        $sql = "select count(1) as amount from $table_name where post_id=" . $post_id;
36 2
        $r = $this->wpdb->get_results($sql);
37
38 2
        if ($r && 1 == count($r)) {
39 1
            if (intval($r[0]->amount) > 0) {
40 1
                return true;
41
            }
42
        }
43
44 1
        return false;
45
    }
46
47 15
    public function timeshiftVisible() {
48 15
        $check = apply_filters('krn_timeshift_visible', true);
49
50 15
        return $check;
51
    }
52
53 15
    public function add_metabox() {
54 15
        $cl = $this;
55 15
        if (! $this->timeshiftVisible()) {
56
            return;
57
        }
58 15
        if (! isset($_GET['post']) || ! $this->hasTimeshifts($_GET['post'])) {
59 15
            return;
60
        }
61
        add_action('add_meta_boxes', function () use ($cl) {
62
            add_meta_box('krn-timeshift', __('Timeshift', 'kmm-timeshift'), [$cl, 'timeshift_metabox'], null, 'normal', 'core');
63
        });
64
    }
65
66 1
    public function timeshift_metabox() {
67 1
        if (! isset($_GET['post'])) {
68 1
            return;
69
        }
70
        $prod_post = get_post($_GET['post']);
71
72
        $start = 0;
73
        $timeshift_page = 1;
74
        if (isset($_GET['timeshift_page'])) {
75
            $start = ($_GET['timeshift_page'] - 1) * $this->timeshift_posts_per_page;
76
            $timeshift_page = $_GET['timeshift_page'];
77
        }
78
79
        // pagination
80
        $pagination = $this->get_paginated_links($prod_post, $timeshift_page);
81
        echo $pagination;
82
83
        // load first few & render
84
        $rows = $this->get_next_rows($prod_post, $start);
85
        $timeshift_table = $this->render_metabox_table($prod_post, $rows);
86
        echo $timeshift_table;
87
88
        if (isset($_GET['action']) && $_GET['action'] == $this->pagination_ajax_action) {
89
            wp_die();
90
        }
91
    }
92
93 15
    public function add_filters() {
94
        // When revisioned post meta has changed, trigger a revision save.
95
        //add_filter('wp_save_post_revision_post_has_changed', [$this, '_wp_check_revisioned_meta_fields_have_changed'], 10, 3);
96
97 15
        add_filter('get_post_metadata', [$this, 'inject_metadata_timeshift'], 1, 4);
98 15
        add_filter('update_post_metadata', [$this, 'update_post_metadata'], 1, 5);
99
    }
100
101 1
    public function update_post_metadata($check, int $object_id, string $meta_key, $meta_value, $prev_value) {
0 ignored issues
show
Unused Code introduced by
The parameter $meta_value is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $prev_value is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
102 1
        if ('_edit_last' == $meta_key) {
103 1
            $lo = get_post_meta($object_id, '_edit_last', true);
104 1
            $this->last_author = $lo;
105
        }
106
107 1
        return null;
108
    }
109
110 3
    public function inject_metadata_timeshift($value, $post_id, $key, $single) {
0 ignored issues
show
Unused Code introduced by
The parameter $single is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
111 3
        if (! isset($_GET['timeshift'])) {
112 3
            return;
113
        }
114
        // Load timeshift
115
        if (! $this->timeshift_cached_meta) {
116
            $post_type = get_post_type($post_id);
117
            $table_name = $this->wpdb->prefix . 'timeshift_' . $post_type;
118
            $sql = "select * from $table_name where id=" . intval($_GET['timeshift']);
119
            $r = $this->wpdb->get_results($sql);
120 View Code Duplication
            if ($r && 1 == count($r)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
121
                $payload = unserialize($r[0]->post_payload);
122
                $this->timeshift_cached_meta = $payload->meta;
123
            }
124
        }
125
        // is the requested meta data in the stored snapshot
126
        if ($this->timeshift_cached_meta && isset($this->timeshift_cached_meta[$key])) {
127
            return $this->timeshift_cached_meta[$key];
128
        } else {
129
            // Otherwise return default value, like acf core fields.
130
            return $value;
131
        }
132
    }
133
134 2
    public function inject_timeshift($p) {
0 ignored issues
show
Unused Code introduced by
The parameter $p is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
135
        global $post;
136 2
        if (! isset($_GET['timeshift'])) {
137 1
            return;
138
        }
139
        // Load timeshift
140 1
        $table_name = $this->wpdb->prefix . 'timeshift_' . $post->post_type;
141 1
        $sql = "select * from $table_name where id=" . intval($_GET['timeshift']);
142 1
        $r = $this->wpdb->get_results($sql);
143 1 View Code Duplication
        if ($r && 1 == count($r)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
144
            $payload = unserialize($r[0]->post_payload);
145
            $post = $payload->post;
146
        }
147
    }
148
149 15
    public function add_actions() {
150 15
        add_action('edit_form_top', [$this, 'inject_timeshift'], 1, 1);
151 15
        add_action('pre_post_update', [$this, 'pre_post_update'], 2, 1);
152 15
        add_action('add_attachment', [$this, 'add_attachment'], 1, 1);
153 15
        add_action('admin_notices', [$this, 'admin_notice']);
154 15
        add_action('krn_timeshift_create_snapshot', [$this, 'create_snapshot'], 1, 1);
155 15
        add_action('admin_enqueue_scripts', [$this, 'enqueue_scripts']);
156 15
        add_action('wp_ajax_pagination_timeshift', [$this, 'timeshift_metabox']);
157
    }
158
159
    public function admin_notice() {
160
        if (isset($_GET['timeshift']) && $_GET['timeshift']) {
161
            echo '<div class="notice notice-warning is-dismissible">';
162
            echo '<p style="font-weight: 800; color: red">' . __('You are editing a historical version! if you save or publish, this will replace the current live one', 'kmm-timeshift') . '</p>';
163
            echo '</div>';
164
        }
165
    }
166
167
    public function enqueue_scripts() {
168
        wp_enqueue_script('krn-timeshift-pagination-ajax', plugin_dir_url(__FILE__) . '../assets/js/pagination-ajax.js', ['jquery']);
169
        wp_localize_script('krn-timeshift-pagination-ajax', 'krn_timeshift', [
170
            'action' => $this->pagination_ajax_action,
171
            'post' => isset($_GET['post']) ? $_GET['post'] : false,
172
            'timeshift' => isset($_GET['timeshift']) ? $_GET['timeshift'] : false,
173
        ]);
174
    }
175
176 5
    public function checkTable($postType) {
177 5
        $table_name = $this->wpdb->prefix . 'timeshift_' . $postType;
178
179 5
        $charset_collate = $this->wpdb->get_charset_collate();
180
181 5
        $sql = "CREATE TABLE IF NOT EXISTS $table_name (
182
                id int(12) NOT NULL AUTO_INCREMENT,
183
                post_id int(12) NOT NULL,
184
                create_date datetime default CURRENT_TIMESTAMP,
185
                post_payload LONGTEXT,
186
                PRIMARY KEY (id)
187 5
            ) $charset_collate;";
188
189 5
        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
190 5
        $a = dbDelta($sql);
0 ignored issues
show
Unused Code introduced by
$a is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
191
192 5
        return true;
193
    }
194
195 2
    public function storeTimeshift($timeshift) {
196 2
        $table_name = $this->wpdb->prefix . 'timeshift_' . $timeshift->post->post_type;
197 2
        $sql = "insert into $table_name (post_id, post_payload) VALUES(%d, '%s')";
198 2
        $query = $this->wpdb->prepare($sql, $timeshift->post->ID, serialize($timeshift));
199 2
        $this->wpdb->query($query);
200
    }
201
202
    public function create_snapshot($postID) {
203
        $this->pre_post_update($postID);
204
    }
205
206 2
    public function add_attachment($postID) {
207 2
        $this->pre_post_update($postID);
208
    }
209
210 2
    public function pre_post_update(int $post_ID, array $data = null) {
0 ignored issues
show
Unused Code introduced by
The parameter $data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
211 2
        if (wp_is_post_autosave($post_ID)) {
212
            return;
213
        }
214 2
        if ('auto-draft' == get_post_status($post_ID)) {
215
            return;
216
        }
217 2
        $post_type = get_post_type($post_ID);
218 2
        $this->checkTable($post_type);
219
220 2
        $mdata = get_metadata('post', $post_ID);
221 2
        $post = get_post($post_ID);
222
223 2
        if ($this->last_author) {
224
            $mdata['_edit_last'][0] = $this->last_author;
225
        }
226 2
        unset($mdata['_edit_lock']);
227
228 2
        $timeshift = (object) ['post' => $post, 'meta' => $mdata];
229 2
        $this->storeTimeshift($timeshift);
230
    }
231
232
    public function get_paginated_links($prod_post, $paged = 1) {
233
        if (is_null($prod_post)) {
234
            return;
235
        }
236
237
        // count timeshift-versions
238
        $table_name = $this->wpdb->prefix . 'timeshift_' . $prod_post->post_type;
239
        $sql = "select  count(1) as cnt from $table_name where post_id=" . $prod_post->ID;
240
        $maxrows = $this->wpdb->get_results($sql);
241
        $allrows = (int) $maxrows[0]->{'cnt'};
242
243
        // max. number of pages
244
        $max_page = ceil($allrows / $this->timeshift_posts_per_page);
245
246
        // create pagination links
247
        $output = paginate_links([
248
            'current' => max(1, $paged),
249
            'total' => $max_page,
250
            'mid_size' => 1,
251
            'prev_text' => __('«'),
252
            'next_text' => __('»'),
253
        ]);
254
255
        return $output;
256
    }
257
258
    public function get_next_rows($prod_post, $start = 0) {
259
        if (! isset($prod_post)) {
260
            return;
261
        }
262
263
        $table_name = $this->wpdb->prefix . 'timeshift_' . $prod_post->post_type;
264
        $sql = "select  * from $table_name where post_id=" . $prod_post->ID . ' order by create_date desc limit ' . $start . ', ' . $this->timeshift_posts_per_page;
265
        $rows = $this->wpdb->get_results($sql);
266
267
        return $rows;
268
    }
269
270
    public function render_metabox_table($prod_post, $rows = []) {
271
        if (! isset($prod_post)) {
272
            return;
273
        }
274
275
        // get last editor
276
        $table_postmeta = $this->wpdb->prefix . 'postmeta';
277
        $sql_last_editor = 'select meta_value from ' . $table_postmeta . ' where post_id=' . $prod_post->ID . " AND meta_key='_edit_last'";
278
        $last_editor = $this->wpdb->get_var($sql_last_editor);
279
280
        $output = '<table class="widefat fixed">';
281
        $output .= '<thead>';
282
        $output .= '<tr>';
283
        $output .= '<th width=30></th>';
284
        $output .= '<th width="40%" id="columnname" class="manage-column column-columnname"  scope="col">' . __('Title', 'kmm-timeshift') . '</th>';
285
        $output .= '<th width="30%" id="columnname" class="manage-column column-columnname"  scope="col">' . __('Snapshot Date', 'kmm-timeshift') . '</th>';
286
        $output .= '<th width="10%" id="columnname" class="manage-column column-columnname"  scope="col">' . __('Author', 'kmm-timeshift') . '</th>';
287
        $output .= '<th width="10%" id="columnname" class="manage-column column-columnname"  scope="col">' . __('Actions', 'kmm-timeshift') . '</th>';
288
        $output .= '</tr>';
289
        $output .= '</thead>';
290
        $output .= '<tbody>';
291
292
        // live-version
293
        $output .= '<tr style="font-weight: 800;">';
294
        $output .= '<td>' . get_avatar($last_editor, 30) . '</td>';
295
        $output .= '<td>' . $prod_post->post_title . '</td>';
296
        $output .= '<td>' . $prod_post->post_modified . '</td>';
297
        $output .= '<td>' . get_the_author_meta('display_name', $last_editor) . '</td>';
298
        $output .= '<td><a href="post.php?post=' . $_GET['post'] . '&action=edit"><span class="dashicons dashicons-admin-site"></span></A></td>';
299
        $output .= '</tr>';
300
301
        foreach ($rows as $rev) {
302
            $timeshift = unserialize($rev->post_payload);
303
            $style = '';
304
305
            // highlight currently loaded version
306
            if (isset($_GET['timeshift']) && $_GET['timeshift'] == $rev->id) {
307
                $style = 'style="font-style:italic;background-color: lightblue;"';
308
            }
309
310
            // some images dont have _edit_last field
311
            if (! isset($timeshift->meta['_edit_last']) || is_null($timeshift->meta['_edit_last'])) {
312
                $timeshift->meta['_edit_last'] = 0;
313
            }
314
315
            $output .= '<tr ' . $style . '>';
316
            $output .= '<td>' . get_avatar($timeshift->meta['_edit_last'][0], 30) . '</td>';
317
            $output .= '<td>' . $timeshift->post->post_title . '</td>';
318
            $output .= '<td>' . $timeshift->post->post_modified . '</td>';
319
            $output .= '<td>' . get_the_author_meta('display_name', $timeshift->meta['_edit_last'][0]) . '</td>';
320
            $output .= '<td><a href="post.php?post=' . $_GET['post'] . '&action=edit&timeshift=' . $rev->id . '"><span class="dashicons dashicons-backup"></span></a></td>';
321
            $output .= '</tr>';
322
        }
323
324
        $output .= '</tbody>';
325
        $output .= '</table>';
326
327
        return $output;
328
    }
329
}
330