Skip to content

Commit 1e47e23

Browse files
committed
Schedule comments' closing at certain date
Fixes #6
1 parent e934a73 commit 1e47e23

File tree

9 files changed

+400
-32
lines changed

9 files changed

+400
-32
lines changed

includes/admin/class-admin.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,20 @@
1414
*/
1515
class Admin {
1616

17+
/**
18+
* Instance of Metabox.
19+
*
20+
* @var Metabox
21+
*/
22+
public $metabox;
23+
1724
/**
1825
* Constructor.
1926
*
2027
* @since 3.0.0
2128
*/
2229
public function __construct() {
23-
// Constructor code.
30+
$this->metabox = new Metabox();
2431
}
2532

2633
/**

includes/admin/class-metabox.php

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
<?php
2+
/**
3+
* Admin: Metabox logic for close date and future options.
4+
*
5+
* @package WebberZone\AutoClose
6+
* @subpackage Admin
7+
*/
8+
9+
namespace WebberZone\AutoClose\Admin;
10+
11+
use WebberZone\AutoClose\Admin\Settings\Metabox_API;
12+
use WebberZone\AutoClose\Features\Close_Date;
13+
14+
if ( ! defined( 'WPINC' ) ) {
15+
die;
16+
}
17+
18+
/**
19+
* Class Metabox
20+
*
21+
* Handles the registration, rendering, and saving of the AutoClose metabox.
22+
*
23+
* @since 3.0.0
24+
*/
25+
class Metabox {
26+
/**
27+
* Array of metabox API instances keyed by post type.
28+
*
29+
* @var array
30+
*/
31+
protected $metaboxes = array();
32+
33+
/**
34+
* Settings key for post meta.
35+
*
36+
* @var string
37+
*/
38+
public $settings_key = 'acc_meta';
39+
40+
/**
41+
* Prefix for meta keys and filters.
42+
*
43+
* @var string
44+
*/
45+
public $prefix = 'acc';
46+
47+
/**
48+
* Feature logic instance.
49+
*
50+
* @var Close_Date
51+
*/
52+
protected $close_date_logic;
53+
54+
/**
55+
* Constructor.
56+
*/
57+
public function __construct() {
58+
$this->close_date_logic = new Close_Date();
59+
add_action( 'admin_menu', array( $this, 'initialise_metabox_api' ) );
60+
add_action( 'save_post', array( $this, 'save_metabox' ), 10, 2 );
61+
}
62+
63+
/**
64+
* Register the metabox using Metabox_API for each supported post type.
65+
*/
66+
public function initialise_metabox_api(): void {
67+
foreach ( $this->get_supported_post_types() as $post_type ) {
68+
$this->metaboxes[ $post_type ] = new Metabox_API(
69+
array(
70+
'settings_key' => $this->settings_key,
71+
'prefix' => $this->prefix,
72+
'post_type' => $post_type,
73+
'title' => esc_html__( 'AutoClose Settings', 'autoclose' ),
74+
'registered_settings' => $this->get_registered_settings(),
75+
'checkbox_modified_text' => __( 'Modified from default', 'autoclose' ),
76+
)
77+
);
78+
}
79+
}
80+
81+
/**
82+
* Save the metabox data using the API and trigger close logic.
83+
*
84+
* @param int $post_id Post ID.
85+
*/
86+
public function save_metabox( $post_id ): void {
87+
$post_type = get_post_type( $post_id );
88+
if ( isset( $this->metaboxes[ $post_type ] ) ) {
89+
$this->metaboxes[ $post_type ]->save( $post_id );
90+
}
91+
// After saving, trigger scheduling/close logic.
92+
$this->close_date_logic->maybe_schedule_or_close( $post_id );
93+
}
94+
95+
/**
96+
* Get supported post types for the metabox.
97+
*
98+
* @return array
99+
*/
100+
protected function get_supported_post_types(): array {
101+
$post_types = get_post_types( array( 'public' => true ), 'names' );
102+
return array_filter(
103+
$post_types,
104+
static function ( $type ) {
105+
return post_type_supports( $type, 'comments' );
106+
}
107+
);
108+
}
109+
110+
/**
111+
* Get registered settings for the metabox.
112+
*
113+
* @return array
114+
*/
115+
protected function get_registered_settings() {
116+
$settings = array(
117+
array(
118+
'id' => 'comments_date',
119+
'name' => esc_html__( 'Close comments on', 'autoclose' ),
120+
'type' => 'datetime',
121+
'desc' => esc_html__( 'Select the date/time to close comments.', 'autoclose' ),
122+
),
123+
array(
124+
'id' => 'pings_date',
125+
'name' => esc_html__( 'Close trackbacks on', 'autoclose' ),
126+
'type' => 'datetime',
127+
'desc' => esc_html__( 'Select the date/time to close trackbacks.', 'autoclose' ),
128+
),
129+
);
130+
131+
/**
132+
* Filter array of registered settings for metabox.
133+
*
134+
* @param array $settings Registered settings.
135+
*/
136+
$settings = apply_filters( $this->prefix . '_metabox_settings', $settings );
137+
138+
return $settings;
139+
}
140+
}

includes/admin/settings/class-metabox-api.php

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -264,29 +264,12 @@ public function html( $post ) {
264264
echo '<table class="form-table">';
265265
foreach ( $this->registered_settings as $setting ) {
266266

267-
$args = wp_parse_args(
268-
$setting,
269-
array(
270-
'id' => null,
271-
'name' => '',
272-
'desc' => '',
273-
'type' => null,
274-
'default' => '',
275-
'options' => '',
276-
'max' => null,
277-
'min' => null,
278-
'step' => null,
279-
'size' => null,
280-
'field_class' => '',
281-
'field_attributes' => '',
282-
'placeholder' => '',
283-
)
284-
);
267+
$args = Settings_API::parse_field_args( $setting );
285268

286269
$id = $args['id'];
287270
$value = get_post_meta( $post->ID, "_{$this->prefix}_{$id}", true );
288271
$args['value'] = ! empty( $value ) ? $value : ( isset( $args['default'] ) ? $args['default'] : $args['options'] );
289-
$type = isset( $args['type'] ) ? $args['type'] : 'text';
272+
$type = $args['type'] ?? 'text';
290273
$callback = method_exists( $settings_form, "callback_{$type}" ) ? array( $settings_form, "callback_{$type}" ) : array( $settings_form, 'callback_missing' );
291274

292275
echo '<tr>';

includes/admin/settings/class-settings-api.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
/**
1919
* Settings API wrapper class
2020
*
21-
* @version 2.7.0
21+
* @version 2.7.1
2222
*/
2323
class Settings_API {
2424

@@ -27,7 +27,7 @@ class Settings_API {
2727
*
2828
* @var string
2929
*/
30-
public const VERSION = '2.7.0';
30+
public const VERSION = '2.7.1';
3131

3232
/**
3333
* Settings Key.
@@ -1094,7 +1094,7 @@ public static function parse_field_args( $field, $section = '' ) {
10941094

10951095
// Add required indicator to field name if the field is required.
10961096
if ( ! empty( $field['required'] ) && true === $field['required'] ) {
1097-
$field['name'] = sprintf( '%s <span class="required" title="%s">*</span>', $field['name'], esc_attr__( 'Required', 'glue-link' ) );
1097+
$field['name'] = sprintf( '%s <span class="required" title="%s">*</span>', $field['name'], esc_attr__( 'Required' ) );
10981098
}
10991099

11001100
return $field;

includes/admin/settings/class-settings-form.php

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,7 @@ public function callback_number( $args ) {
535535
$min = isset( $args['min'] ) ? intval( $args['min'] ) : 0;
536536
$step = isset( $args['step'] ) ? intval( $args['step'] ) : 1;
537537
$size = isset( $args['size'] ) ? $args['size'] : 'regular';
538-
$placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="' . esc_attr( $args['placeholder'] ) . '"';
538+
$placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="' . $args['placeholder'] . '"';
539539
$disabled = ( ! empty( $args['disabled'] ) || $args['pro'] ) ? ' disabled="disabled"' : '';
540540
$readonly = ( isset( $args['readonly'] ) && true === $args['readonly'] ) ? ' readonly="readonly"' : '';
541541
$required = ( isset( $args['required'] ) && true === $args['required'] ) ? ' required' : '';
@@ -778,7 +778,7 @@ public function callback_password( $args ) {
778778
$this->settings_key,
779779
sanitize_key( $args['id'] ),
780780
esc_attr( $value ),
781-
! empty( $value ) ? 'placeholder="' . esc_attr__( 'Previously saved', 'glue-link' ) . '"' : ''
781+
! empty( $value ) ? 'placeholder="' . esc_attr__( 'Previously saved' ) . '"' : ''
782782
);
783783
$html .= $this->get_field_description( $args );
784784

@@ -818,7 +818,7 @@ public function callback_repeater( $args ) {
818818
?>
819819
</div>
820820
<button type="button" class="button add-item" data-target="<?php echo esc_attr( $args['id'] ); ?>">
821-
<?php echo esc_html( ! empty( $args['add_button_text'] ) ? $args['add_button_text'] : __( 'Add Item', 'glue-link' ) ); ?>
821+
<?php echo esc_html( ! empty( $args['add_button_text'] ) ? $args['add_button_text'] : __( 'Add Item' ) ); ?>
822822
</button>
823823

824824
<script type="text/template" class="repeater-template" data-id="<?php echo esc_attr( $args['id'] ); ?>">
@@ -956,7 +956,7 @@ private function render_repeater_item( $args, $index, $item = null ) {
956956
<label class="wz-repeater-field-label" for="<?php echo esc_attr( sprintf( '%s_%s_%s', $args['id'], $index, $field_id ) ); ?>">
957957
<?php echo esc_html( $field['name'] ); ?>
958958
<?php if ( ! empty( $field['required'] ) ) : ?>
959-
<span class="required" title="<?php esc_attr_e( 'Required', 'glue-link' ); ?>">*</span>
959+
<span class="required" title="<?php esc_attr_e( 'Required' ); ?>">*</span>
960960
<?php endif; ?>
961961
</label>
962962
</div>
@@ -1009,13 +1009,9 @@ private function render_repeater_item( $args, $index, $item = null ) {
10091009
<?php
10101010
}
10111011

1012-
1013-
10141012
/**
10151013
* Display sensitive fields.
10161014
*
1017-
* @since 1.0.0
1018-
*
10191015
* @param array $args Array of arguments.
10201016
*/
10211017
public function callback_sensitive( $args ) {
@@ -1026,4 +1022,75 @@ public function callback_sensitive( $args ) {
10261022

10271023
$this->callback_text( $args );
10281024
}
1025+
1026+
/**
1027+
* Display date picker fields.
1028+
*
1029+
* @param array $args Array of arguments.
1030+
*/
1031+
public function callback_date( $args ) {
1032+
$value = isset( $args['value'] ) ? $args['value'] : $this->get_option( $args['id'], $args['options'] );
1033+
$size = sanitize_html_class( isset( $args['size'] ) ? $args['size'] : 'regular' );
1034+
$class = sanitize_html_class( $args['field_class'] );
1035+
$placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="' . $args['placeholder'] . '"';
1036+
$disabled = ( ! empty( $args['disabled'] ) || $args['pro'] ) ? ' disabled="disabled"' : '';
1037+
$readonly = ( isset( $args['readonly'] ) && true === $args['readonly'] ) ? ' readonly="readonly"' : '';
1038+
$required = ( isset( $args['required'] ) && true === $args['required'] ) ? ' required' : '';
1039+
$attributes = $disabled . $readonly . $required;
1040+
foreach ( (array) $args['field_attributes'] as $attribute => $val ) {
1041+
$attributes .= sprintf( ' %1$s="%2$s"', $attribute, esc_attr( $val ) );
1042+
}
1043+
$field_attributes = $this->get_field_attributes( $args );
1044+
1045+
$html = sprintf(
1046+
'<input type="date" id="%1$s" name="%2$s" class="%3$s" value="%4$s" %5$s %6$s />',
1047+
$field_attributes['field_id'],
1048+
$field_attributes['field_name'],
1049+
$class . ' ' . $size . '-text',
1050+
esc_attr( stripslashes( $value ) ),
1051+
$attributes,
1052+
$placeholder
1053+
);
1054+
1055+
$html .= $this->get_field_description( $args );
1056+
1057+
/** This filter has been defined in class-settings-api.php */
1058+
echo apply_filters( $this->prefix . '_after_setting_output', $html, $args ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1059+
}
1060+
1061+
/**
1062+
* Display datetime-local picker fields.
1063+
*
1064+
* @param array $args Array of arguments.
1065+
*/
1066+
public function callback_datetime( $args ) {
1067+
$value = isset( $args['value'] ) ? $args['value'] : $this->get_option( $args['id'], $args['options'] );
1068+
$size = sanitize_html_class( isset( $args['size'] ) ? $args['size'] : 'regular' );
1069+
$class = sanitize_html_class( $args['field_class'] );
1070+
$placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="' . $args['placeholder'] . '"';
1071+
$disabled = ( ! empty( $args['disabled'] ) || $args['pro'] ) ? ' disabled="disabled"' : '';
1072+
$readonly = ( isset( $args['readonly'] ) && true === $args['readonly'] ) ? ' readonly="readonly"' : '';
1073+
$required = ( isset( $args['required'] ) && true === $args['required'] ) ? ' required' : '';
1074+
$attributes = $disabled . $readonly . $required;
1075+
1076+
foreach ( (array) $args['field_attributes'] as $attribute => $val ) {
1077+
$attributes .= sprintf( ' %1$s="%2$s"', $attribute, esc_attr( $val ) );
1078+
}
1079+
$field_attributes = $this->get_field_attributes( $args );
1080+
1081+
$html = sprintf(
1082+
'<input type="datetime-local" id="%1$s" name="%2$s" class="%3$s" value="%4$s" %5$s %6$s />',
1083+
$field_attributes['field_id'],
1084+
$field_attributes['field_name'],
1085+
$class . ' ' . $size . '-text',
1086+
esc_attr( stripslashes( $value ) ),
1087+
$attributes,
1088+
$placeholder
1089+
);
1090+
1091+
$html .= $this->get_field_description( $args );
1092+
1093+
/** This filter has been defined in class-settings-api.php */
1094+
echo apply_filters( $this->prefix . '_after_setting_output', $html, $args ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1095+
}
10291096
}

includes/admin/settings/class-settings-sanitize.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,40 @@ public function sanitize_repeater_field( $value, $field = array() ) {
346346
return $sanitized_value;
347347
}
348348

349+
/**
350+
* Sanitize date fields (HTML5 date inputs).
351+
*
352+
* @param string $value Date in 'Y-m-d' format.
353+
* @return string Sanitized date or empty string.
354+
*/
355+
public function sanitize_date_field( $value ) {
356+
$value = sanitize_text_field( wp_unslash( $value ) );
357+
// Validate 'YYYY-MM-DD'.
358+
if ( preg_match( '/^\d{4}-\d{2}-\d{2}$/', $value ) ) {
359+
return $value;
360+
}
361+
return '';
362+
}
363+
364+
/**
365+
* Sanitize datetime-local fields (HTML5 datetime-local inputs).
366+
*
367+
* @param string $value Datetime in 'Y-m-d\TH:i' format, or just 'Y-m-d'.
368+
* @return string Sanitized datetime or empty string.
369+
*/
370+
public function sanitize_datetime_field( $value ) {
371+
$value = sanitize_text_field( wp_unslash( $value ) );
372+
// If only date is given, fallback to 00:00 for time.
373+
if ( preg_match( '/^\d{4}-\d{2}-\d{2}$/', $value ) ) {
374+
return $value . 'T00:00';
375+
}
376+
// Validate 'YYYY-MM-DDThh:mm'.
377+
if ( preg_match( '/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/', $value ) ) {
378+
return $value;
379+
}
380+
return '';
381+
}
382+
349383
/**
350384
* Convert a string to CSV.
351385
*

includes/class-autoclose.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ private function define_feature_hooks() {
117117
$comments = new Features\Comments();
118118
$revisions = new Features\Revisions();
119119
$block_pings = new Features\Block_Pings();
120+
$close_date = new Features\Close_Date();
120121

121122
// Register cron hooks.
122123
$this->loader->add_action( 'acc_cron_hook', $comments, 'process_comments' );

0 commit comments

Comments
 (0)