PluginProbe
RSFirewall! / trunk
RSFirewall! vtrunk
rsfirewall / models / lists.php

lists.php in RSFirewall! trunk, at models/lists.php

709 lines 20.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package RSFirewall!
4 * @copyright (c) 2018 RSJoomla!
5 * @link https://www.rsjoomla.com
6 * @license GNU General Public License http://www.gnu.org/licenses/gpl-3.0.en.html
7 */
8
9 if ( ! defined( 'WPINC' ) ) {
10 die;
11 }
12
13 class RSFirewall_Model_Lists extends RSFirewall_Post {
14 /**
15 * Create a custom post type (Blocklist/Safelist)
16 */
17 public function init() {
18 // Set UI labels for Custom Post Type
19 $labels = array(
20 'name' => _x( 'Lists', 'Post Type General Name', 'rsfirewall' ),
21 'singular_name' => _x( 'List item', 'Post Type Singular Name', 'rsfirewall' ),
22 'menu_name' => esc_html__( 'List', 'rsfirewall' ),
23 'add_new' => esc_html__( 'Add New IP', 'rsfirewall' ),
24 'add_new_item' => esc_html__( 'Add New IP', 'rsfirewall' ),
25 'all_items' => esc_html__( 'All IPs', 'rsfirewall' ),
26 'view_item' => esc_html__( 'View List item', 'rsfirewall' ),
27 'edit_item' => esc_html__( 'Edit IP', 'rsfirewall' ),
28 'update_item' => esc_html__( 'Update List Item', 'rsfirewall' ),
29 'search_items' => esc_html__( 'Search List Item', 'rsfirewall' ),
30 'not_found' => esc_html__( 'Not Found', 'rsfirewall' ),
31 'not_found_in_trash' => esc_html__( 'Not found in Trash', 'rsfirewall' ),
32 );
33
34 // Set other options for Custom Post Type
35 $args = array(
36 'label' => esc_html__( 'Lists', 'rsfirewall' ),
37 'description' => esc_html__( 'White lists / Black lists', 'rsfirewall' ),
38 'labels' => $labels,
39 'supports' => false,
40 'hierarchical' => false,
41 'show_ui' => true,
42 'show_in_menu' => false,
43 'show_in_nav_menus' => false,
44 'show_in_admin_bar' => true,
45 'menu_position' => 5,
46 'can_export' => true,
47 'has_archive' => true,
48 'exclude_from_search' => true,
49 'publicly_queryable' => false,
50 'rewrite' => array( "slug" => $this->prefix."lists" ),
51 'capabilities' => array(
52 'create_posts' => true,
53 'delete_posts' => true,
54 'delete_post' => true,
55 // Removes support for the "Add New" function ( use 'do_not_allow' instead of false for multisite set ups )
56 ),
57 'map_meta_cap' => true,
58 'register_meta_box_cb' => array( $this, 'add_metabox' )
59 );
60
61 // Registering your Custom Post Type
62 register_post_type( $this->prefix.'lists', $args );
63
64 // Show the current ip when adding
65 add_action( 'admin_notices', array($this, 'admin_notice') );
66
67 // Since we do not use the title or description, need to hide the body-content div
68 add_action( 'admin_head', array($this, 'remove_content_div') );
69
70 // Remove the publishing
71 add_action( 'admin_menu', array($this, 'remove_meta_box'));
72
73 // Remove the screen options to avoid selecting the slug box
74 add_filter('screen_options_show_screen', array($this, 'remove_screen_options'));
75
76 // Set the Layout column mode for the add/edit to 1 column
77 add_filter('get_user_option_screen_layout_'.$this->prefix.'lists', function(){ return 1; } );
78
79 // Modify the search so that results are shown
80 add_filter('pre_get_posts', array($this, 'refine_query'));
81
82 // Modify the get_search_query for proper display
83 add_filter('get_search_query', array($this, 'get_search_query'));
84
85 // Modify the standard messages when updating/publishing
86 add_filter('post_updated_messages', array($this, 'updated_messages'));
87
88 if (function_exists('wp_untrash_post_set_previous_status'))
89 {
90 // When restoring a keep last post status
91 add_filter('wp_untrash_post_status', 'wp_untrash_post_set_previous_status', 10, 3);
92 }
93
94 // Add custom filters
95 add_action( 'restrict_manage_posts', array($this, 'add_filters') );
96
97 // Add bulk add IPs button
98 add_action( 'manage_posts_extra_tablenav', array($this, 'add_bulk_button') );
99
100
101 }
102
103 public function remove_meta_box(){
104 remove_meta_box( 'submitdiv', $this->prefix.'lists', 'side' );
105 }
106
107 public function remove_content_div(){
108 global $pagenow, $typenow;
109 if (($pagenow == 'post-new.php' || $pagenow == 'post.php') && $typenow == $this->prefix.'lists') {
110 echo '<style type="text/css"> #post-body-content { display:none; }</style>';
111 }
112 }
113
114 public function remove_screen_options($options) {
115 global $pagenow, $typenow;
116 if (($pagenow == 'post-new.php' || $pagenow == 'post.php') && $typenow == $this->prefix.'lists') {
117 return false;
118 }
119
120 return $options;
121 }
122
123 /**
124 * Function used in the filter of changing the messages
125 */
126 public function updated_messages($messages) {
127 $messages['lists'] = $messages['post'];
128
129 $messages['lists'][1] = __('The IP address has been updated!', 'rsfirewall');
130 $messages['lists'][4] = __('The IP address has been updated!', 'rsfirewall');
131 $messages['lists'][6] = __('The IP address has been added!', 'rsfirewall');
132
133 return $messages;
134 }
135
136 /**
137 * Function to add the filters
138 */
139 function add_filters(){
140 global $pagenow, $typenow;
141
142 if ($pagenow == 'edit.php' && $typenow == $this->prefix.'lists') {
143 $filters = array(
144 'type' => array(
145 esc_attr__('Select Type', 'rsfirewall') => 'all',
146 esc_attr__('Blocklist', 'rsfirewall') => '0',
147 esc_attr__('Safelist', 'rsfirewall') => '1',
148 )
149 );
150
151 foreach ($filters as $filter => $options) {
152 ?>
153 <select name="rsf_filter[<?php echo esc_attr($filter);?>]">
154 <?php
155 $current_v = (isset($_GET['rsf_filter']) && isset($_GET['rsf_filter'][$filter])) ? $_GET['rsf_filter'][$filter] : '';
156 foreach ($options as $label => $value) {
157 printf
158 (
159 '<option value="%s"%s>%s</option>',
160 $value,
161 $value == $current_v ? ' selected="selected"' : '',
162 $label
163 );
164 }
165 ?>
166 </select>
167 <?php
168 }
169 }
170 }
171
172 /**
173 * Function used for handling the
174 */
175 public function refine_query($query) {
176 global $pagenow, $typenow;
177
178 if ($pagenow == 'edit.php' && $typenow == $this->prefix.'lists') {
179 $custom_fields = array(
180 "rsfirewall_ip",
181 "rsfirewall_reason"
182 );
183
184 // Handle the search term
185 $searchterm = $query->query_vars['s'];
186
187 // unset the 's' value from the query, because we do not need it anymore and interferes with our query
188 $query->query_vars['s'] = "";
189
190 $meta_query_search = array('relation' => 'OR');
191 if (strlen($searchterm) != 0) {
192 foreach($custom_fields as $cf) {
193 array_push($meta_query_search, array(
194 'key' => $cf,
195 'value' => esc_sql($searchterm),
196 'compare' => 'LIKE'
197 ));
198 }
199 };
200
201 // Handle our own filter values
202 $meta_query_filters = array('relation' => 'AND');
203 if (isset($_GET['rsf_filter']) && !empty($_GET['rsf_filter'])) {
204 foreach ($_GET['rsf_filter'] as $field => $value){
205 // in case the default value of a filter is set to 'all'
206 if ($value == 'all') {
207 continue;
208 }
209
210 array_push($meta_query_filters, array(
211 'key' => 'rsfirewall_' . esc_sql($field),
212 'value' => esc_sql($value),
213 'compare' => '='
214 ));
215
216 }
217 }
218
219 $is_search = count($meta_query_search) > 1 ? 1 : 0;
220 $is_filters = count($meta_query_filters) > 1 ? 3 : 0;
221
222 $meta_query = array();
223 switch ($combine = ($is_search + $is_filters)) {
224 // when only the search is used
225 case 1:
226 $meta_query = $meta_query_search;
227 break;
228
229 // when only the filters are used
230 case 3:
231 $meta_query = $meta_query_filters;
232 break;
233
234 // when both filters and search are used
235 case 4:
236 array_push($meta_query_filters, $meta_query_search);
237 $meta_query = $meta_query_filters;
238 break;
239 }
240
241 if (!empty($meta_query)) {
242 $query->set('meta_query', $meta_query);
243 }
244 }
245 }
246
247 /**
248 * Function used for handling the text of the query that is outputed in the "Search results for" statement
249 */
250 public function get_search_query($search_term) {
251 global $pagenow, $typenow;
252
253 if ($pagenow == 'edit.php' && $typenow == $this->prefix.'lists') {
254 // Empty the search term in case there is a value
255 $search_term = '';
256
257 if (isset($_GET['s']) && !empty($_GET['s'])) {
258 $search_term = esc_attr($_GET['s']);
259 }
260 }
261
262 return $search_term;
263 }
264
265 /**
266 * Display bulk actions.
267 *
268 * @param $actions
269 *
270 * @return array
271 */
272 public function bulk_actions($actions)
273 {
274 // Remove the edit action
275 if (isset($actions['edit'])) {
276 unset($actions['edit']);
277 }
278
279 return $actions;
280 }
281
282 /**
283 * Save post.
284 *
285 * @param $post_id
286 * @param $post
287 *
288 * @return int|void
289 */
290 public function save( $post_id, $post) {
291 if (isset($_POST['data'])) {
292 $is_update = $this->check_if_update($post);
293
294 // Check if the ip entered is compatible
295 $ip = sanitize_text_field($_POST['data']['rsfirewall_ip']);
296 $type = sanitize_text_field($_POST['data']['rsfirewall_type']);
297
298 if ($this->check_ip($ip, $post_id, $type, $is_update)) {
299 parent::save($post_id, $post);
300 } else {
301 if (!$is_update) {
302 // Get and delete other auto-saves/revisions if any
303 if ($revisions = wp_get_post_revisions($post_id)) {
304 foreach($revisions as $rev_post) {
305 wp_delete_post($rev_post->ID);
306 }
307 }
308
309 // Finally delete the post itself
310 wp_delete_post($post->ID);
311
312 // Redirect to the form
313 wp_redirect( wp_get_referer() );
314 exit();
315 } else {
316 wp_redirect(get_edit_post_link($post_id, 'url'));
317 exit();
318 }
319 }
320 }
321 }
322
323 /**
324 * Function to check if the post is an update or an insert.
325 */
326 protected function check_if_update($post){
327 return strtotime($post->post_date_gmt) != strtotime($post->post_modified_gmt);
328 }
329
330 /**
331 * Function to check if the ip entered is compatible.
332 */
333 public function check_ip($ip, $post_id, $type, $update, $show_error = true) {
334 $ip = trim($ip);
335
336 // Check if the IP is not empty
337 if (strlen($ip) == 0) {
338 if ($show_error) {
339 $this->set_message(__('You did not enter an IP address!', 'rsfirewall'));
340 }
341 return false;
342 }
343
344 // Check if the IP is already listed in the database
345 $args = array(
346 'post_type' => $this->prefix.'lists',
347 'meta_query' => array(
348 array(
349 'key' => 'rsfirewall_ip',
350 'value' => $ip,
351 'compare' => '=',
352 )
353 )
354 );
355
356 if ($update) {
357 $args['post__not_in'] = array($post_id);
358 }
359
360 $query = new WP_Query( $args );
361
362 if ($query->post_count >= 1) {
363 if ($show_error) {
364 $this->set_message(__('This IP is already listed!', 'rsfirewall'));
365 }
366 return false;
367 }
368
369 if ($this->is_range($ip)) {
370 $disallowed = array(
371 '*.*.*.*',
372 '0.0.0.0/0',
373 '0.0.0.0/1',
374 '0.0.0.0-127.255.255.255',
375 '0.0.0.0-255.255.255.255'
376 );
377 if (in_array($ip, $disallowed)) {
378 if ($show_error) {
379 $this->set_message(__('Selected range cannot be added because it will block all IP addresses from visiting your website.', 'rsfirewall'));
380 }
381 return false;
382 }
383
384 } else {
385 // Check if we're attempting to ban server's IP
386 if ($ip == $_SERVER['SERVER_ADDR'] && !$type) {
387 if ($show_error) {
388 $this->set_message(__('You cannot block your own server IP.', 'rsfirewall'));
389 }
390 return false;
391 }
392
393 // Make sure IP is valid
394 try {
395 $class = new RSFirewall_IP($ip);
396
397 // And check if it matches any of the current entries from the db
398 // Done only in the administration section to prevent flooding when autoban is enabled.
399 if (is_admin()) {
400 $args = array(
401 'post_type' => $this->prefix.'lists',
402 'meta_query' => array(
403 'relation' => 'AND',
404 array(
405 'key' => 'rsfirewall_type',
406 'value' => $type,
407 'compare' => '=',
408 ),
409 array(
410 'relation' => 'OR',
411 array(
412 'key' => 'rsfirewall_ip',
413 'value' => '*',
414 'compare' => 'LIKE',
415 ),
416 array(
417 'key' => 'rsfirewall_ip',
418 'value' => '/',
419 'compare' => 'LIKE',
420 ),
421 array(
422 'key' => 'rsfirewall_ip',
423 'value' => '-',
424 'compare' => 'LIKE',
425 )
426 )
427 )
428 );
429
430 $query = new WP_Query( $args );
431 if ($query->post_count >= 1) {
432
433 foreach ($query->posts as $post) {
434 $post_ip = get_post_meta( $post->ID, 'rsfirewall_ip', true );
435 try {
436 if ($class->match($post_ip)) {
437 if ($show_error) {
438 $this->set_message(sprintf(esc_html__('Specified IP (%s) matches a range that\'s already in the database (%s)'), $ip, $post_ip));
439 }
440 return false;
441 }
442 } catch (Exception $e) {
443 continue;
444 }
445 }
446 }
447 }
448 } catch (Exception $e) {
449 if ($show_error) {
450 $this->set_message($e->getMessage());
451 }
452 return false;
453 }
454 }
455
456 return true;
457 }
458
459 /**
460 * Check if an IP is a range
461 */
462
463 protected function is_range($ip) {
464 return strpos($ip, '*') !== false || strpos($ip, '-') !== false || strpos($ip, '/') !== false;
465 }
466
467 /**
468 * Display Info in the table
469 *
470 * @param $column
471 * @param $post_id
472 */
473 public function column( $column, $post_id ) {
474 $value = get_post_meta( $post_id, $column, true );
475
476 switch ( $column ) {
477 case 'rsfirewall_ip':
478 $value = '<a href="'.get_edit_post_link(($post_id)).'">'.esc_html($value).'</a>';
479 break;
480
481 case 'rsfirewall_type':
482 if ( $value == 0 ) {
483 $value = esc_html__( 'Blocklisted', 'rsfirewall' );
484 } else {
485 $value = esc_html__( 'Safelisted', 'rsfirewall' );
486 }
487 break;
488
489 case 'actions':
490 if ($status = get_post_status($post_id)) {
491 $edit_url = admin_url( 'edit.php?post_type='.$this->prefix.'lists');
492 if ($status == 'publish' || $status == 'trash') {
493 $value = '<a href="' . wp_nonce_url($edit_url, 'rsfirewall', 'rsf-actions') . '&handler=lists&task=change_status&id=' . $post_id . '" class="rsfirewall-btn' . ($status == 'publish' ? ' danger' : '') . ' small" type="button" id="rsf-list-status" data-pid="' . $post_id . '">' .($status == 'publish' ? __('Add to trash', 'rsfirewall') : __('Publish', 'rsfirewall')) . '</a>';
494 }
495 else if ($status == 'draft')
496 {
497 $value = '<a href="' . wp_nonce_url($edit_url, 'rsfirewall', 'rsf-actions') . '&handler=lists&task=change_status&id=' . $post_id . '" class="rsfirewall-btn small" type="button" id="rsf-list-status" data-pid="' . $post_id . '">'.__('Publish', 'rsfirewall').'</a>';
498 }
499 else
500 {
501 $value = __($status, 'rsfirewall');
502 }
503 }
504 break;
505
506 case 'rsfirewall_reason':
507 $value = esc_html($value);
508 break;
509 }
510 if ( ! empty( $value ) ) {
511 echo $value;
512 }
513 }
514
515 /**
516 * Function to change the current status of the selected post
517 */
518 public function change_status() {
519 $post_id = (int) (isset($_GET['id']) ? $_GET['id'] : 0);
520 $back_url = wp_get_referer();
521
522 $change_status = true;
523 // Check if the id is not empty (0)
524 if (empty($post_id)) {
525 $this->set_message(__('The post id is not correct!', 'rsfirewall'));
526 $change_status = false;
527 }
528
529 // Check if the id is available
530 if (!get_post_status($post_id)) {
531 $this->set_message(__('This post does not exist!', 'rsfirewall'));
532 $change_status = false;
533 }
534
535 // change post status
536 if ($change_status ) {
537 $current_status = get_post_status($post_id);
538 $new_status = $current_status == 'publish' ? 'trash' : 'publish';
539
540 if ($new_status == 'trash')
541 {
542 wp_trash_post($post_id);
543 }
544 else
545 {
546 wp_update_post(array('ID' => $post_id, 'post_status' => $new_status));
547 }
548 }
549
550 // Redirect to the list
551 RSFirewall_Helper::redirect($back_url);
552 }
553
554 /**
555 * Add Columns to the Lists Tables
556 *
557 * @param $columns
558 *
559 * @return array
560 */
561 public function columns( $columns ) {
562 $columns = array(
563 'cb' => '<input type="checkbox" />',
564 'rsfirewall_ip' => esc_html__( 'IP Address', 'rsfirewall' ),
565 'rsfirewall_type' => esc_html__( 'Type', 'rsfirewall' ),
566 'rsfirewall_reason' => esc_html__( 'Reason', 'rsfirewall' ),
567 'date' => esc_html__( 'Date', 'rsfirewall' ),
568 'actions' => esc_html__( 'Change Status', 'rsfirewall' )
569 );
570
571 return $columns;
572 }
573
574 /**
575 * Make columns sortable
576 *
577 * @param $columns
578 *
579 * @return mixed
580 */
581 public function sortable_columns( $columns ) {
582 $columns['rsfirewall_ip'] = 'rsfirewall_ip';
583 $columns['rsfirewall_type'] = 'rsfirewall_type';
584
585 return $columns;
586 }
587
588 /**
589 * Build the proper metabox
590 */
591 public function add_metabox() {
592 add_meta_box( 'rsfirewall_lists_metaboxes', esc_html__( 'IP Details', 'rsfirewall' ), array(
593 $this,
594 'show_metabox'
595 ), $this->prefix.'lists', 'normal', 'default' );
596
597 // Publish metabox
598 add_meta_box( 'rsfirewall_submitdiv', __( 'Publish', 'rsfirewall' ), array($this, 'show_metabox_publish'), $this->prefix.'lists', 'normal', 'core' );
599 }
600
601 /**
602 * Actual Content of the metabox
603 */
604 public function show_metabox() {
605 global $post;
606 ?>
607 <table class="form-table">
608 <?php
609 foreach ( $this->form->section->field as $field ) {
610 $callback = array( 'RSFirewall_Helper_Fields', (string) $field->attributes()->type );
611 $args = array(
612 'field' => $field,
613 'section' => (string) $this->form->section->attributes()->name,
614 'value' => metadata_exists( 'post', $post->ID, (string) $field->attributes()->name ) ? get_post_meta( $post->ID, (string) $field->attributes()->name, true ) : (string) $field->attributes()->default
615 );
616 if ( is_callable( $callback ) ) {
617 ?>
618 <tr>
619 <th scope="row"><?php echo RSFirewall_Helper_Fields::label_for($field); ?></th>
620 <td><?php call_user_func( $callback, $args ); ?></td>
621 </tr>
622 <?php
623 }
624 }
625 ?>
626 </table>
627 <?php
628 }
629
630 /**
631 * Actual Content of the publish metabox
632 */
633 public function show_metabox_publish() {
634 global $post;
635
636 $post_type = $post->post_type;
637 $post_type_object = get_post_type_object($post_type);
638 $can_publish = current_user_can($post_type_object->cap->publish_posts);
639 $back_url = admin_url( 'edit.php?post_type='.$this->prefix.'lists');
640
641 ?>
642 <div id="back-action" style="float:left;">
643 <a class="button button-primary" href="<?php echo $back_url; ?>"><?php echo __('Back to the list', 'rsfirewall'); ?></a>
644 </div>
645
646 <div id="publishing-action">
647 <?php if ( !in_array( $post->post_status, array('publish', 'future', 'private') ) || 0 == $post->ID ) {
648 // We will only use the publish action
649 if ( $can_publish ) { ?>
650 <input name="original_publish" type="hidden" id="original_publish" value="<?php esc_attr_e('Publish') ?>"/>
651 <?php submit_button(__('Publish'), 'primary large', 'publish', false); ?>
652 <?php } else { ?>
653 <input name="original_publish" type="hidden" id="original_publish" value="<?php esc_attr_e('Submit for Review') ?>"/>
654 <?php submit_button(__('Submit for Review'), 'primary large', 'publish', false); ?>
655 <?php
656 }
657 } else { ?>
658 <input name="original_publish" type="hidden" id="original_publish" value="<?php esc_attr_e('Update') ?>" />
659 <input name="save" type="submit" class="button button-primary button-large" id="publish" value="<?php esc_attr_e( 'Update' ) ?>" />
660 <?php } ?>
661 </div>
662 <div id="delete-action" style="float:right; margin-right:50px">
663 <?php
664 if ( current_user_can( "delete_post", $post->ID ) ) {
665 if ( !EMPTY_TRASH_DAYS )
666 $delete_text = __('Delete Permanently', 'rsfirewall');
667 else
668 $delete_text = __('Move to Trash', 'rsfirewall');
669 ?>
670 <a class="submitdelete deletion rsfirewall-btn danger small" href="<?php echo get_delete_post_link($post->ID); ?>"><?php echo $delete_text; ?></a><?php
671 }
672 ?>
673 </div>
674 <div class="clear"></div>
675 <?php
676 }
677
678 /**
679 * Function to display notices
680 */
681 public function admin_notice() {
682 $screen_id = RSFirewall_Helper::get_current_screen();
683
684 if ($screen_id == $this->prefix.'lists') {
685 ?>
686 <div class="notice notice-warning is-dismissible">
687 <p><?php echo wp_kses_post(__('Your IP address is currently detected as '.RSFirewall_Helper::get_ip().'.', 'rsfirewall')); ?></p>
688 </div>
689 <?php
690 }
691 }
692
693 /**
694 * Add bulk add IPs button
695 */
696 public function add_bulk_button($which) {
697 global $typenow;
698
699 if ($typenow == $this->prefix.'lists' && $which == 'top')
700 {
701 $bulk_url = admin_url( 'admin.php?page=rsfirewall_lists_bulk_add_ips');
702 ?>
703 <div class="alignleft actions">
704 <a href="<?php echo esc_url($bulk_url); ?>" class="button"><?php echo _e('Bulk add IPs', 'rsfirewall'); ?></a>
705 </div>
706 <?php
707 }
708 }
709 }