PluginProbe
wpForo Forum / 3.1.4
wpForo Forum v3.1.4
3.1.5 3.1.4 3.1.2 3.1.1 3.1.0 3.0.9 3.0.8 3.0.7 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.1.1 1.1.2 1.2.0 1.3.0 1.3.1 1.4.0 1.4.1 1.4.10 1.4.11 1.4.12 1.4.13 1.4.2 All 137 releases
wpforo / modules / revisions / Revisions.php

Revisions.php in wpForo Forum 3.1.4, at modules/revisions/Revisions.php

320 lines 10.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace wpforo\modules\revisions;
4
5 use stdClass;
6 use wpforo\modules\revisions\classes\Actions;
7 use wpforo\modules\revisions\classes\Template;
8
9 // Exit if accessed directly
10 if( ! defined( 'ABSPATH' ) ) exit;
11
12 class Revisions {
13 /* @var Template */
14 public $Template;
15 /* @var Actions */
16 public $Actions;
17 public $revision;
18 private $default;
19
20 public function __construct() {
21 $this->init_classes();
22 $this->init();
23 }
24
25 private function init_classes() {
26 $this->Template = new Template();
27 $this->Actions = new Actions();
28 }
29
30 private function init() {
31 $this->init_defaults();
32 $this->revision = $this->default->revision;
33 }
34
35 private function init_defaults() {
36 $this->default = new stdClass();
37 $this->default->revision = [
38 'revisionid' => 0,
39 'userid' => 0,
40 'textareaid' => '',
41 'postid' => 0,
42 'body' => '',
43 'created' => 0,
44 'version' => 0,
45 'email' => '',
46 'url' => '',
47 ];
48 $this->default->revision_format = [
49 'revisionid' => '%d',
50 'userid' => '%d',
51 'textareaid' => '%s',
52 'postid' => '%d',
53 'body' => '%s',
54 'created' => '%d',
55 'version' => '%d',
56 'email' => '%s',
57 'url' => '%s',
58 ];
59 $this->default->sql_select_args = [
60 'include' => [],
61 'exclude' => [],
62 'userids_include' => [],
63 'userids_exclude' => [],
64 'textareaids_include' => [],
65 'textareaids_exclude' => [],
66 'postids_include' => [],
67 'postids_exclude' => [],
68 'urls_include' => [],
69 'urls_exclude' => [],
70 'emails_include' => [],
71 'emails_exclude' => [],
72 'orderby' => 'revisionid',
73 'order' => 'DESC',
74 'offset' => null,
75 'row_count' => null,
76 ];
77 }
78
79 public function get_current_url_query_vars_str() {
80 $url_query_vars_str = wpforo_get_url_query_vars_str();
81 $url_query_vars_str = preg_replace( '#/?\?.*$#isu', '', $url_query_vars_str );
82
83 $wpf_url_parse = array_filter( explode( '/', trim( (string) $url_query_vars_str, '/' ) ) );
84 $wpf_url_parse = array_reverse( $wpf_url_parse );
85 if( in_array( wpforo_settings_get_slug( 'paged' ), $wpf_url_parse ) ) {
86 foreach( $wpf_url_parse as $key => $value ) {
87 unset( $wpf_url_parse[ $key ] );
88 if( $value === wpforo_settings_get_slug( 'paged' ) ) break;
89 }
90 $wpf_url_parse = array_values( $wpf_url_parse );
91 $wpf_url_parse = array_reverse( $wpf_url_parse );
92 $url_query_vars_str = implode( '/', $wpf_url_parse );
93 }
94
95 if( ! $url_query_vars_str ) $url_query_vars_str = 'wpforo_home_url';
96
97 return $url_query_vars_str;
98 }
99
100 public function parse_revision( $revision ) {
101 $revision = array_merge( $this->default->revision, (array) $revision );
102 if( $revision['body'] ) {
103 $revision['body'] = preg_replace( '#</pre>[\r\n\t\s\0]*<pre>#iu', "\r\n", (string) $revision['body'] );
104 $revision['body'] = wpforo_kses( trim( (string) $revision['body'] ) );
105 $revision['body'] = stripslashes( (string) $revision['body'] );
106 }
107
108 return $revision;
109 }
110
111 private function parse_args( $args ) {
112 $args = wpforo_parse_args( $args, $this->default->sql_select_args );
113
114 $args['include'] = wpforo_parse_args( $args['include'] );
115 $args['exclude'] = wpforo_parse_args( $args['exclude'] );
116
117 $args['userids_include'] = wpforo_parse_args( $args['userids_include'] );
118 $args['userids_exclude'] = wpforo_parse_args( $args['userids_exclude'] );
119
120 $args['textareaids_include'] = wpforo_parse_args( $args['textareaids_include'] );
121 $args['textareaids_exclude'] = wpforo_parse_args( $args['textareaids_exclude'] );
122
123 $args['postids_include'] = wpforo_parse_args( $args['postids_include'] );
124 $args['postids_exclude'] = wpforo_parse_args( $args['postids_exclude'] );
125
126 $args['urls_include'] = wpforo_parse_args( $args['urls_include'] );
127 $args['urls_exclude'] = wpforo_parse_args( $args['urls_exclude'] );
128
129 $args['emails_include'] = wpforo_parse_args( $args['emails_include'] );
130 $args['emails_exclude'] = wpforo_parse_args( $args['emails_exclude'] );
131
132 return $args;
133 }
134
135 /**
136 * Build prepared IN() clause for string values using $wpdb->prepare()
137 *
138 * @param string $column Column name (already escaped with backticks)
139 * @param array $values Array of string values
140 * @param bool $not Whether to use NOT IN instead of IN
141 *
142 * @return string|null Prepared SQL fragment or null if no valid values
143 */
144 private function build_string_in_clause( $column, $values, $not = false ) {
145 if( ! is_array( $values ) || empty( $values ) ) {
146 return null;
147 }
148 // Filter out empty strings and reindex
149 $values = array_values( array_filter( $values, function( $v ) {
150 return is_string( $v ) && strlen( $v ) > 0;
151 } ) );
152 if( empty( $values ) ) {
153 return null;
154 }
155 $count = count( $values );
156 $placeholders = implode( ', ', array_fill( 0, $count, '%s' ) );
157 $operator = $not ? 'NOT IN' : 'IN';
158
159 return WPF()->db->prepare( "{$column} {$operator}({$placeholders})", ...$values );
160 }
161
162 public function build_sql_where( $args ) {
163 $where = '';
164 $args = $this->parse_args( $args );
165
166 $wheres = [];
167
168 // Integer fields - safe with wpforo_bigintval (casts to int)
169 if( ! empty( $args['include'] ) ) {
170 $wheres[] = "`revisionid` IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['include'] ) ) . ")";
171 }
172 if( ! empty( $args['exclude'] ) ) {
173 $wheres[] = "`revisionid` NOT IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['exclude'] ) ) . ")";
174 }
175
176 if( ! empty( $args['userids_include'] ) ) {
177 $wheres[] = "`userid` IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['userids_include'] ) ) . ")";
178 }
179 if( ! empty( $args['userids_exclude'] ) ) {
180 $wheres[] = "`userid` NOT IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['userids_exclude'] ) ) . ")";
181 }
182
183 if( ! empty( $args['postids_include'] ) ) {
184 $wheres[] = "`postid` IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['postids_include'] ) ) . ")";
185 }
186 if( ! empty( $args['postids_exclude'] ) ) {
187 $wheres[] = "`postid` NOT IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['postids_exclude'] ) ) . ")";
188 }
189
190 // String fields - use $wpdb->prepare() with placeholders for SQL injection protection
191 if( $clause = $this->build_string_in_clause( '`textareaid`', $args['textareaids_include'], false ) ) {
192 $wheres[] = $clause;
193 }
194 if( $clause = $this->build_string_in_clause( '`textareaid`', $args['textareaids_exclude'], true ) ) {
195 $wheres[] = $clause;
196 }
197
198 if( $clause = $this->build_string_in_clause( '`url`', $args['urls_include'], false ) ) {
199 $wheres[] = $clause;
200 }
201 if( $clause = $this->build_string_in_clause( '`url`', $args['urls_exclude'], true ) ) {
202 $wheres[] = $clause;
203 }
204
205 if( $clause = $this->build_string_in_clause( '`email`', $args['emails_include'], false ) ) {
206 $wheres[] = $clause;
207 }
208 if( $clause = $this->build_string_in_clause( '`email`', $args['emails_exclude'], true ) ) {
209 $wheres[] = $clause;
210 }
211
212 if( $wheres ) {
213 $where = " WHERE " . implode( " AND ", $wheres );
214 }
215
216 return $where;
217 }
218
219 private function build_sql_select( $args ) {
220 $args = $this->parse_args( $args );
221 $sql = "SELECT * FROM " . WPF()->tables->post_revisions;
222 $sql .= $this->build_sql_where( $args );
223 $allowed_orderby = array_keys( $this->default->revision_format );
224 $orderby = in_array( $args['orderby'], $allowed_orderby, true ) ? $args['orderby'] : 'revisionid';
225 $order = strtoupper( $args['order'] ) === 'ASC' ? 'ASC' : 'DESC';
226 $sql .= " ORDER BY `" . $orderby . "` " . $order;
227 if( $args['row_count'] ) $sql .= " LIMIT " . wpforo_bigintval( $args['offset'] ) . "," . wpforo_bigintval( $args['row_count'] );
228
229 return $sql;
230 }
231
232 public function add( $data ) {
233 if( empty( $data ) ) return false;
234 $revision = $this->parse_revision( $data );
235 unset( $revision['revisionid'] );
236
237 if( ! $revision['created'] ) $revision['created'] = time();
238 if( ! $revision['url'] ) $revision['url'] = $this->get_current_url_query_vars_str();
239 if( ! $revision['userid'] ) $revision['userid'] = WPF()->current_userid;
240 if( ! $revision['email'] ) $revision['email'] = WPF()->current_user_email;
241 if( ! $revision['textareaid'] || ! $revision['url'] || ! $revision['body'] || ! ( $revision['userid'] || $revision['email'] ) ) return false;
242
243 $revision = wpforo_array_ordered_intersect_key( $revision, $this->default->revision_format );
244 if( WPF()->db->insert(
245 WPF()->tables->post_revisions,
246 $revision,
247 wpforo_array_ordered_intersect_key( $this->default->revision_format, $revision )
248 ) ) {
249 return WPF()->db->insert_id;
250 }
251
252 return false;
253 }
254
255 public function edit( $data, $where ) {
256 if( empty( $data ) || empty( $where ) ) return false;
257 if( is_numeric( $where ) ) $where = [ 'revisionid' => $where ];
258 $data = (array) $data;
259 $where = (array) $where;
260
261 $data = wpforo_array_ordered_intersect_key( $data, $this->default->revision_format );
262 $where = wpforo_array_ordered_intersect_key( $where, $this->default->revision_format );
263 if( false !== WPF()->db->update(
264 WPF()->tables->post_revisions,
265 $data,
266 $where,
267 wpforo_array_ordered_intersect_key( $this->default->revision_format, $data ),
268 wpforo_array_ordered_intersect_key( $this->default->revision_format, $where )
269 ) ) {
270 return true;
271 }
272
273 return false;
274 }
275
276 public function delete( $where ) {
277 if( empty( $where ) ) return false;
278 if( is_numeric( $where ) ) $where = [ 'revisionid' => $where ];
279 $where = (array) $where;
280
281 $where = wpforo_array_ordered_intersect_key( $where, $this->default->revision_format );
282 if( false !== WPF()->db->delete(
283 WPF()->tables->post_revisions,
284 $where,
285 wpforo_array_ordered_intersect_key( $this->default->revision_format, $where )
286 ) ) {
287 return true;
288 }
289
290 return false;
291 }
292
293 public function get_revision( $args ) {
294 if( empty( $args ) ) return false;
295
296 $revision = (array) WPF()->db->get_row( $this->build_sql_select( $args ), ARRAY_A );
297 if( $revision ) $revision = $this->parse_revision( $revision );
298
299 return $revision;
300 }
301
302 public function get_revisions( $args ) {
303 if( empty( $args ) ) return false;
304
305 return array_map( [ $this, 'parse_revision' ], WPF()->db->get_results( $this->build_sql_select( $args ), ARRAY_A ) );
306 }
307
308 /**
309 * @param array $args
310 *
311 * @return int
312 */
313 public function get_count( $args ) {
314 $sql = "SELECT SQL_NO_CACHE COUNT(*) FROM " . WPF()->tables->post_revisions;
315 $sql .= $this->build_sql_where( $args );
316
317 return intval( WPF()->db->get_var( $sql ) );
318 }
319 }
320