PluginProbe
wpForo Forum / 3.0.8
wpForo Forum v3.0.8
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 / classes / PostMeta.php

PostMeta.php in wpForo Forum 3.0.8, at classes/PostMeta.php

675 lines 25.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace wpforo\classes;
4
5 use stdClass;
6
7 // Exit if accessed directly
8 if( ! defined( 'ABSPATH' ) ) exit;
9
10 class PostMeta {
11 private $default;
12
13 public function __construct() {
14 $this->init_defaults();
15 $this->init_hooks();
16 }
17
18 private function init_hooks() {
19 add_action( 'wpforo_after_add_topic', [ $this, 'after_add_topic' ], 10, 2 );
20 add_action( 'wpforo_after_edit_topic', [ $this, 'after_edit_topic' ], 10, 3 );
21 add_action( 'wpforo_after_add_post', [ $this, 'after_add_post' ], 10, 3 );
22 add_action( 'wpforo_after_edit_post', [ $this, 'after_edit_post' ], 10, 4 );
23 add_action( 'wpforo_after_move_topic', [ $this, 'after_move_topic' ], 10, 2 );
24 add_action( 'wpforo_after_merge_topic', [ $this, 'after_merge_topic' ], 10, 3 );
25 add_action( 'wpforo_after_delete_post', [ $this, 'after_delete_post' ] );
26 add_action( 'wpforo_post_status_update', [ $this, 'after_post_status_update' ], 10, 2 );
27 add_action( 'wpforo_topic_private_update', [ $this, 'after_topic_private_update' ], 10, 2 );
28 add_action( 'wpforo_after_is_first_post_update', [ $this, 'after_is_first_post_update' ], 10, 2 );
29 }
30
31 private function init_defaults() {
32 $this->default = new stdClass();
33 $this->default->postmeta = [
34 'metaid' => 0,
35 'postid' => 0,
36 'metakey' => '',
37 'metavalue' => '',
38 'forumid' => 0,
39 'topicid' => 0,
40 'status' => 0,
41 'private' => 0,
42 'is_first_post' => 0,
43 ];
44 $this->default->postmeta_format = [
45 'metaid' => '%d',
46 'postid' => '%d',
47 'metakey' => '%s',
48 'metavalue' => '%s',
49 'forumid' => '%d',
50 'topicid' => '%d',
51 'status' => '%d',
52 'private' => '%d',
53 'is_first_post' => '%d',
54 ];
55 $this->default->sql_select_args = [
56 'include' => [],
57 'exclude' => [],
58 'postids_include' => [],
59 'postids_exclude' => [],
60 'metakeys_include' => [],
61 'metakeys_exclude' => [],
62 'metavalues_include' => [],
63 'metavalues_exclude' => [],
64 'metavalue_like' => null,
65 'metavalue_notlike' => null,
66 'forumids_include' => [],
67 'forumids_exclude' => [],
68 'topicids_include' => [],
69 'topicids_exclude' => [],
70 'is_first_post' => null,
71 'status' => null,
72 'private' => null,
73 'orderby' => null,
74 'offset' => null,
75 'row_count' => null,
76 ];
77 }
78
79 public function fix_postmeta( $postmeta ) {
80 $postmeta = wpforo_array_args_cast_and_merge( $postmeta, $this->default->postmeta );
81 $postmeta['metavalue'] = wpforo_is_json( $postmeta['metavalue'] ) ? json_decode( $postmeta['metavalue'], true ) : $postmeta['metavalue'];
82
83 return $postmeta;
84 }
85
86 /**
87 * @param array $postmeta
88 *
89 * @return bool|int
90 */
91 public function add( $postmeta ) {
92 if( ! wpfval( $postmeta, 'postid' ) || ! wpfval( $postmeta, 'metakey' ) ) return false;
93
94 if( ! wpfkey( $postmeta, 'topicid' ) || ! wpfkey( $postmeta, 'forumid' ) || ! wpfkey( $postmeta, 'status' ) || ! wpfkey( $postmeta, 'private' ) || ! wpfkey( $postmeta, 'is_first_post' ) ) {
95 if( ! $post = WPF()->post->get_post( $postmeta['postid'], false ) ) return false;
96 $postmeta['topicid'] = wpforo_bigintval( wpfval( $post, 'topicid' ) );
97 $postmeta['forumid'] = intval( wpfval( $post, 'forumid' ) );
98 $postmeta['status'] = intval( wpfval( $post, 'status' ) );
99 $postmeta['private'] = intval( wpfval( $post, 'private' ) );
100 $postmeta['is_first_post'] = intval( wpfval( $post, 'is_first_post' ) );
101 }
102
103 $postmeta = wpforo_array_args_cast_and_merge( (array) $postmeta, $this->default->postmeta );
104 unset( $postmeta['metaid'] );
105
106 if( is_null( $postmeta['metavalue'] ) ) $postmeta['metavalue'] = '';
107 $postmeta['metavalue'] = wp_unslash( $postmeta['metavalue'] );
108 if( ! is_scalar( $postmeta['metavalue'] ) ) $postmeta['metavalue'] = wp_json_encode( (array) $postmeta['metavalue'] );
109
110 $postmeta = wpforo_array_ordered_intersect_key( $postmeta, $this->default->postmeta_format );
111 if( WPF()->db->insert(
112 WPF()->tables->postmeta,
113 $postmeta,
114 wpforo_array_ordered_intersect_key( $this->default->postmeta_format, $postmeta )
115 ) ) {
116 $metaid = WPF()->db->insert_id;
117 do_action( 'wpforo_after_add_postmeta', $postmeta, $metaid );
118 return $metaid;
119 }
120
121 return false;
122 }
123
124 /**
125 * @param array $postmeta
126 * @param array|int $where
127 *
128 * @return bool
129 */
130 public function edit( $postmeta, $where ) {
131 if( empty( $postmeta ) || empty( $where ) ) return false;
132 if( wpforo_is_id( $where ) ) $where = [ 'metaid' => $where ];
133 $postmeta = (array) $postmeta;
134 $where = (array) $where;
135
136 if( wpfkey( $postmeta, 'metavalue' ) ) {
137 if( is_null( $postmeta['metavalue'] ) ) $postmeta['metavalue'] = '';
138 $postmeta['metavalue'] = wp_unslash( $postmeta['metavalue'] );
139 if( ! is_scalar( $postmeta['metavalue'] ) ) $postmeta['metavalue'] = wp_json_encode( (array) $postmeta['metavalue'] );
140 }
141
142 $postmeta = wpforo_array_ordered_intersect_key( $postmeta, $this->default->postmeta_format );
143 $where = wpforo_array_ordered_intersect_key( $where, $this->default->postmeta_format );
144 if( false !== WPF()->db->update(
145 WPF()->tables->postmeta,
146 $postmeta,
147 $where,
148 wpforo_array_ordered_intersect_key( $this->default->postmeta_format, $postmeta ),
149 wpforo_array_ordered_intersect_key( $this->default->postmeta_format, $where )
150 ) ) {
151 do_action( 'wpforo_after_edit_postmeta', $postmeta, $where );
152 return true;
153 }
154
155 return false;
156 }
157
158 /**
159 * @param array|int $where
160 *
161 * @return bool
162 */
163 public function delete( $where ) {
164 if( empty( $where ) ) return false;
165 if( wpforo_is_id( $where ) ) $where = [ 'metaid' => $where ];
166 $where = (array) $where;
167
168 $where = wpforo_array_ordered_intersect_key( $where, $this->default->postmeta_format );
169 if( false !== WPF()->db->delete(
170 WPF()->tables->postmeta,
171 $where,
172 wpforo_array_ordered_intersect_key( $this->default->postmeta_format, $where )
173 ) ) {
174 do_action( 'wpforo_after_delete_postmeta', $where );
175 return true;
176 }
177
178 return false;
179 }
180
181 private function parse_args( $args ) {
182 $args = wpforo_parse_args( $args, $this->default->sql_select_args );
183 $args = wpforo_array_ordered_intersect_key( $args, $this->default->sql_select_args );
184 $args['include'] = wpforo_parse_args( $args['include'] );
185 $args['exclude'] = wpforo_parse_args( $args['exclude'] );
186 $args['postids_include'] = wpforo_parse_args( $args['postids_include'] );
187 $args['postids_exclude'] = wpforo_parse_args( $args['postids_exclude'] );
188 $args['metakeys_include'] = wpforo_parse_args( $args['metakeys_include'] );
189 $args['metakeys_exclude'] = wpforo_parse_args( $args['metakeys_exclude'] );
190 $args['metavalues_include'] = wpforo_parse_args( $args['metavalues_include'] );
191 $args['metavalues_exclude'] = wpforo_parse_args( $args['metavalues_exclude'] );
192 $args['forumids_include'] = wpforo_parse_args( $args['forumids_include'] );
193 $args['forumids_exclude'] = wpforo_parse_args( $args['forumids_exclude'] );
194 $args['topicids_include'] = wpforo_parse_args( $args['topicids_include'] );
195 $args['topicids_exclude'] = wpforo_parse_args( $args['topicids_exclude'] );
196
197 return $args;
198 }
199
200 private function build_sql_select( $args, $select = '' ) {
201 $args = $this->parse_args( $args );
202 if( ! $select ) $select = '*';
203
204 $wheres = [];
205
206 if( ! is_null( $args['is_first_post'] ) ) $wheres[] = "`is_first_post` = '" . intval( $args['is_first_post'] ) . "'";
207 if( ! is_null( $args['status'] ) ) $wheres[] = "`status` = '" . intval( $args['status'] ) . "'";
208 if( ! is_null( $args['private'] ) ) $wheres[] = "`private` = " . intval( $args['private'] );
209
210 if( ! empty( $args['include'] ) ) $wheres[] = "`metaid` IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['include'] ) ) . ")";
211 if( ! empty( $args['exclude'] ) ) $wheres[] = "`metaid` NOT IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['exclude'] ) ) . ")";
212
213 if( ! empty( $args['postids_include'] ) ) $wheres[] = "`postid` IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['postids_include'] ) ) . ")";
214 if( ! empty( $args['postids_exclude'] ) ) $wheres[] = "`postid` NOT IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['postids_exclude'] ) ) . ")";
215
216 if( ! empty( $args['metakeys_include'] ) ) $wheres[] = "`metakey` IN('" . implode( "','", array_map( 'trim', $args['metakeys_include'] ) ) . "')";
217 if( ! empty( $args['metakeys_exclude'] ) ) $wheres[] = "`metakey` NOT IN('" . implode( "','", array_map( 'trim', $args['metakeys_exclude'] ) ) . "')";
218
219 if( ! empty( $args['metavalues_include'] ) ) $wheres[] = "`metavalue` IN('" . implode( "','", array_map( 'trim', $args['metavalues_include'] ) ) . "')";
220 if( ! empty( $args['metavalues_exclude'] ) ) $wheres[] = "`metavalue` NOT IN('" . implode( "','", array_map( 'trim', $args['metavalues_exclude'] ) ) . "')";
221
222 if( ! is_null( $args['metavalue_like'] ) ) $wheres[] = "`metavalue` LIKE '" . esc_sql( $args['metavalue_like'] ) . "'";
223 if( ! is_null( $args['metavalue_notlike'] ) ) $wheres[] = "`metavalue` NOT LIKE '" . esc_sql( $args['metavalue_notlike'] ) . "'";
224
225 if( ! empty( $args['forumids_include'] ) ) $wheres[] = "`forumid` IN(" . implode( ',', array_map( 'intval', $args['forumids_include'] ) ) . ")";
226 if( ! empty( $args['forumids_exclude'] ) ) $wheres[] = "`forumid` NOT IN(" . implode( ',', array_map( 'intval', $args['forumids_exclude'] ) ) . ")";
227
228 if( ! empty( $args['topicids_include'] ) ) $wheres[] = "`topicid` IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['topicids_include'] ) ) . ")";
229 if( ! empty( $args['topicids_exclude'] ) ) $wheres[] = "`topicid` NOT IN(" . implode( ',', array_map( 'wpforo_bigintval', $args['topicids_exclude'] ) ) . ")";
230
231 $sql = "SELECT $select FROM " . WPF()->tables->postmeta;
232 if( $wheres ) $sql .= " WHERE " . implode( " AND ", $wheres );
233 if( $args['orderby'] ) $sql .= " ORDER BY " . $args['orderby'];
234 if( $args['row_count'] ) $sql .= " LIMIT " . wpforo_bigintval( $args['offset'] ) . "," . wpforo_bigintval( $args['row_count'] );
235
236 return $sql;
237 }
238
239 /**
240 * @param int $metaid
241 *
242 * @return array|mixed
243 */
244 public function get_postmeta_by_id( $metaid ) {
245 if( ! $metaid = wpforo_bigintval( $metaid ) ) return null;
246
247 $key = 'get_postmeta_by_id_' . $metaid;
248 if( WPF()->ram_cache->exists( $key ) ) return WPF()->ram_cache->get( $key );
249
250 if( $postmeta = (array) WPF()->db->get_row( $this->build_sql_select( [ 'include' => $metaid ] ), ARRAY_A ) ) {
251 $postmeta = $this->fix_postmeta( $postmeta );
252 }
253
254 WPF()->ram_cache->set( $key, $postmeta );
255
256 return $postmeta;
257 }
258
259 /**
260 * @param int $postid
261 * @param string $metakey
262 *
263 * @return bool
264 */
265 public function exists( $postid, $metakey ) {
266 if( ! $metakey || ! ( $postid = wpforo_bigintval( $postid ) ) ) return false;
267 $sql = "SELECT EXISTS(
268 SELECT * FROM `" . WPF()->tables->postmeta . "`
269 WHERE `postid` = %d
270 AND `metakey` = %s
271 ) AS is_exists";
272
273 return (bool) WPF()->db->get_var( WPF()->db->prepare( $sql, $postid, $metakey ) );
274 }
275
276 /**
277 * @param array $args
278 * @param string $field
279 *
280 * @return array
281 */
282 public function get_postmetas_col( $args, $field ) {
283 $args = wpforo_array_ordered_intersect_key( (array) $args, $this->default->sql_select_args );
284 if( empty( $args ) ) return [];
285
286 $key = 'get_postmetas_' . wp_json_encode( $args ) . '_' . $field;
287 if( WPF()->ram_cache->exists( $key ) ) return WPF()->ram_cache->get( $key );
288
289 $postmetas = WPF()->db->get_col( $this->build_sql_select( $args, "`$field`" ) );
290
291 WPF()->ram_cache->set( $key, $postmetas );
292
293 return $postmetas;
294 }
295
296 /**
297 * @param array $args
298 *
299 * @return array
300 */
301 public function get_postmetas( $args ) {
302 $args = wpforo_array_ordered_intersect_key( (array) $args, $this->default->sql_select_args );
303 if( empty( $args ) ) return [];
304
305 $key = 'get_postmetas_' . wp_json_encode( $args );
306 if( WPF()->ram_cache->exists( $key ) ) return WPF()->ram_cache->get( $key );
307
308 if( $postmetas = (array) WPF()->db->get_results( $this->build_sql_select( $args ), ARRAY_A ) ) {
309 $postmetas = array_map( [ $this, 'fix_postmeta' ], $postmetas );
310 }
311
312 WPF()->ram_cache->set( $key, $postmetas );
313
314 return $postmetas;
315 }
316
317 /**
318 * @param int $postid
319 * @param string|array $metakeys
320 * @param bool $single
321 *
322 * @return array|mixed|null
323 */
324 public function get_postmeta( $postid, $metakeys = '', $single = false ) {
325 if( ! $postid = wpforo_bigintval( $postid ) ) return null;
326 $metakeys = array_filter( (array) $metakeys );
327
328 $key = 'get_postmeta_' . $postid . '_' . wp_json_encode( $metakeys ) . '_' . $single;
329 if( WPF()->ram_cache->exists( $key ) ) return WPF()->ram_cache->get( $key );
330
331 $postmeta = null;
332
333 $args = [
334 'postids_include' => $postid,
335 'metakeys_include' => $metakeys,
336 'orderby' => '`metaid` ASC',
337 'row_count' => $single && $metakeys ? 1 : null,
338 ];
339 if( $postmetas = $this->get_postmetas( $args ) ) {
340 if( count( $metakeys ) === 1 ) {
341 if( $single ) {
342 $first = current( $postmetas );
343 $postmeta = $first['metavalue'];
344 } else {
345 $postmeta = [];
346 foreach( $postmetas as $p ) $postmeta[] = $p['metavalue'];
347 }
348 } else {
349 $postmeta = [];
350 foreach( $postmetas as $p ) {
351 if( $single ) {
352 if( ! array_key_exists( $p['metakey'], $postmeta ) ) $postmeta[ $p['metakey'] ] = $p['metavalue'];
353 } else {
354 $postmeta[ $p['metakey'] ][] = $p['metavalue'];
355 }
356 }
357 }
358 }
359
360 WPF()->ram_cache->set( $key, $postmeta );
361
362 return $postmeta;
363 }
364
365 public function search( $args ) {
366 $args = array_filter( (array) $args );
367 if( ! $args ) return [];
368
369 $selects = [];
370 foreach( $args as $key => $value ) {
371 if( $field = WPF()->post->get_field( $key ) ) {
372 $value = (array) $value;
373 $wheres = [];
374 if( in_array( $field['type'], [ 'text', 'textarea', 'email', 'search', 'tel' ] ) ) {
375 foreach( $value as $v ) $wheres[] = "`metavalue` LIKE '%" . esc_sql( wp_unslash( $v ) ) . "%'";
376 } elseif( $field['type'] === 'checkbox' || ( $field['type'] === 'select' && wpfval( $field, 'isMultiChoice' ) ) || ( $field['type'] === 'autocomplete' && wpfval( $field, 'isMultiChoice' ) ) ) {
377 foreach( $value as $v ) {
378 $v = preg_quote( preg_quote( wp_unslash( $v ) ) );
379 $wheres[] = "`metavalue` REGEXP '[\\\[,]\"" . $v . "\"[,\\\]]'";
380 }
381 } else {
382 foreach( $value as $v ) $wheres[] = "`metavalue` LIKE '" . esc_sql( wp_unslash( $v ) ) . "'";
383 }
384 if( $wheres ) {
385 $selects[] = "SELECT `postid`, `metakey` FROM `" . WPF()->tables->postmeta . "`
386 WHERE `metakey` = '" . esc_sql( $key ) . "' AND " . implode( ' AND ', $wheres );
387 }
388 }
389 }
390
391 if( $selects ) {
392 $sql = "SELECT `postid`, COUNT(`postid`) AS pcount FROM
393 (" . implode( ' UNION ', $selects ) . ") AS pm
394 GROUP BY `postid` HAVING pcount = " . count( $selects );
395
396 return WPF()->db->get_col( $sql );
397 }
398
399 return [];
400 }
401
402 private function delete_file( $postid, $metakey ) {
403 $postid = wpforo_bigintval( $postid );
404 if( $postid && $metakey ) {
405 if( $postmeta = $this->get_postmeta( $postid, $metakey ) ) {
406 foreach( $postmeta as $file ) {
407 $mediaid = (int) wpfval( $file, 'mediaid' );
408 $fileurl = (string) wpfval( $file, 'fileurl' );
409 $filedir = wpforo_fix_upload_dir( $fileurl );
410 if( $mediaid ) wp_delete_attachment( $mediaid );
411 // Security: Only delete files within wpforo upload directory
412 if( $filedir ) {
413 $realpath = realpath( $filedir );
414 $upload_base = realpath( WPF()->folders['wp_upload']['dir'] );
415 if( $realpath && $upload_base && strpos( $realpath, $upload_base . DIRECTORY_SEPARATOR . 'wpforo' ) === 0 ) {
416 wp_delete_file( $filedir );
417 }
418 }
419 }
420 $this->delete( [ 'postid' => $postid, 'metakey' => $metakey ] );
421 }
422 }
423 }
424
425 private function add_file( $type, $post ) {
426 $postid = (int) ( wpfval( $post, 'first_postid' ) ? $post['first_postid'] : wpfval( $post, 'postid' ) );
427
428 if( $wpftcf_delete = array_filter( (array) wpfval( $_POST, 'wpftcf_delete' ) ) ) {
429 foreach( $wpftcf_delete as $metakey ) {
430 $this->delete_file( $postid, $metakey );
431 }
432 }
433
434 if( ! empty( wpfval( $_FILES, 'data', 'type' ) ) ) {
435 $forum = WPF()->forum->get_forum( $post['forumid'] );
436 $fields_list = WPF()->post->get_topic_fields_list( false, $forum, ! WPF()->current_userid );
437 $mime_types = wp_get_mime_types();
438 $allowed_mime_types = get_allowed_mime_types();
439 foreach( $_FILES['data']['type'] as $k => $mime_type ) {
440 if( in_array( $k, $fields_list ) ) {
441 $field = WPF()->post->get_field( $k, $type, $forum );
442 $label = ( $field['label'] ?: $field['fieldKey'] );
443
444 if( $error = intval( wpfval( $_FILES, 'data', 'error', $k ) ) ) {
445 $phpFileUploadErrors = [
446 0 => 'There is no error, the file uploaded with success',
447 1 => 'The uploaded file size is too big',
448 2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
449 3 => 'The uploaded file was only partially uploaded',
450 // 4 => 'No file was uploaded',
451 6 => 'Missing a temporary folder',
452 7 => 'Failed to write file to disk.',
453 8 => 'A PHP extension stopped the file upload.',
454 ];
455 if( $n = wpfval( $phpFileUploadErrors, $error ) ) WPF()->notice->add( $n, 'error' );
456 } else {
457 $name = wpfval( $_FILES, 'data', 'name', $k );
458 $tmp_name = wpfval( $_FILES, 'data', 'tmp_name', $k );
459 $ext = pathinfo( $name, PATHINFO_EXTENSION );
460 $size = intval( $field['fileSize'] );
461 $fileExtensions = array_filter( (array) is_scalar( $field['fileExtensions'] ) ? explode( ',', trim( (string) $field['fileExtensions'] ) ) : $field['fileExtensions'] );
462 if( $fileExtensions ) {
463 if( in_array( $ext, $fileExtensions ) ) {
464 $extensions = explode( '|', array_search( $mime_type, $mime_types ) );
465 $e = in_array( $ext, $extensions );
466 } else {
467 $e = false;
468 }
469 } else {
470 $extensions = explode( '|', array_search( $mime_type, $allowed_mime_types ) );
471 $e = in_array( $ext, $extensions );
472 }
473
474 if( ! empty( $e ) ) {
475 if( wpfval( $_FILES, 'data', 'size', $k ) <= ( $size * 1024 * 1024 ) ) {
476 $attach_dir = WPF()->folders['default_attachments']['dir'] . DIRECTORY_SEPARATOR . WPF()->current_userid;
477 $attach_url = WPF()->folders['default_attachments']['url//'] . "/" . WPF()->current_userid;
478 if( ! is_dir( $attach_dir ) ) wp_mkdir_p( $attach_dir );
479
480 $fnm = pathinfo( $name, PATHINFO_FILENAME );
481 $fnm = str_replace( ' ', '-', $fnm );
482 while( strpos( (string) $fnm, '--' ) !== false ) $fnm = str_replace( '--', '-', $fnm );
483 $fnm = preg_replace( "/[^-a-zA-Z0-9_]/", "", (string) $fnm );
484 $fnm = trim( (string) $fnm, "-" );
485 $fnm_empty = ! $fnm;
486
487 $file_name = $fnm . "." . $ext;
488
489 $attach_fname = time() . ( ! $fnm_empty ? '-' : '' ) . $file_name;
490 $attach_path = $attach_dir . DIRECTORY_SEPARATOR . $attach_fname;
491
492 if( is_dir( $attach_dir ) && move_uploaded_file( $tmp_name, $attach_path ) ) {
493 $this->delete_file( $postid, $field['fieldKey'] );
494
495 $attach_id = wpforo_insert_to_media_library( $attach_path, $fnm );
496 $file = [
497 'fileurl' => $attach_url . '/' . $attach_fname,
498 'filename' => basename( (string) $name ),
499 'mediaid' => $attach_id,
500 ];
501
502 $postmeta = [
503 'postid' => $postid,
504 'metakey' => $field['fieldKey'],
505 'metavalue' => $file,
506 'forumid' => $post['forumid'],
507 'topicid' => $post['topicid'],
508 'is_first_post' => 1,
509 'status' => $post['status'],
510 'private' => $post['private'],
511 ];
512 $this->add( $postmeta );
513
514 } else {
515 WPF()->notice->add( 'Can\'t upload file', 'error' );
516 }
517
518 } else {
519 WPF()->notice->add( '%1$s - File is too large. Maximum allowed file size is %2$s MB', 'error', [ $label, $size ] );
520 }
521 } else {
522 WPF()->notice->add( '%1$s - File type is not allowed.', 'error', $label );
523 }
524 }
525 }
526 }
527 }
528 }
529
530 public function after_add_topic( $topic, $forum ) {
531 $this->add_file( 'topic', $topic );
532
533 if( ! empty( $topic['postmetas'] ) ) {
534 $fields_list = WPF()->post->get_topic_fields_list( false, $forum, ! WPF()->current_userid );
535 foreach( $topic['postmetas'] as $metakey => $metavalue ) {
536 if( in_array( $metakey, $fields_list ) ) {
537 // Security: Only accept array values for file-type fields to prevent file path injection
538 $field = WPF()->post->get_field( $metakey, 'topic', $forum );
539 if( is_array( $metavalue ) && wpfval( $field, 'type' ) !== 'file' ) continue;
540 $postmeta = [
541 'postid' => $topic['first_postid'],
542 'metakey' => $metakey,
543 'metavalue' => $metavalue,
544 'forumid' => $topic['forumid'],
545 'topicid' => $topic['topicid'],
546 'is_first_post' => 1,
547 'status' => $topic['status'],
548 'private' => $topic['private'],
549 ];
550 $this->add( $postmeta );
551 }
552 }
553 }
554 }
555
556 public function after_edit_topic( $topic, $args, $forum ) {
557 $this->add_file( 'topic', $topic );
558
559 if( ! empty( $args['postmetas'] ) ) {
560 $fields_list = WPF()->post->get_topic_fields_list( false, $forum, ! WPF()->current_userid );
561 foreach( $args['postmetas'] as $metakey => $metavalue ) {
562 if( in_array( $metakey, $fields_list ) ) {
563 // Security: Only accept array values for file-type fields to prevent file path injection
564 $field = WPF()->post->get_field( $metakey, 'topic', $forum );
565 if( is_array( $metavalue ) && wpfval( $field, 'type' ) !== 'file' ) continue;
566 $postmeta = [
567 'metavalue' => $metavalue,
568 'forumid' => $topic['forumid'],
569 'topicid' => $topic['topicid'],
570 'is_first_post' => 1,
571 'status' => $topic['status'],
572 'private' => $topic['private'],
573 ];
574 if( $this->exists( $topic['first_postid'], $metakey ) ) {
575 $this->edit( $postmeta, [
576 'postid' => $topic['first_postid'],
577 'metakey' => $metakey,
578 ] );
579 } else {
580 $postmeta['postid'] = $topic['first_postid'];
581 $postmeta['metakey'] = $metakey;
582 $this->add( $postmeta );
583 }
584 }
585 }
586 }
587 }
588
589 public function after_add_post( $post, $topic, $forum ) {
590 $this->add_file( 'post', $post );
591
592 if( ! empty( $post['postmetas'] ) ) {
593 $fields_list = WPF()->post->get_post_fields_list( false, $forum, ! WPF()->current_userid );
594 foreach( $post['postmetas'] as $metakey => $metavalue ) {
595 if( in_array( $metakey, $fields_list ) ) {
596 // Security: Only accept array values for file-type fields to prevent file path injection
597 $field = WPF()->post->get_field( $metakey, 'post', $forum );
598 if( is_array( $metavalue ) && wpfval( $field, 'type' ) !== 'file' ) continue;
599 $postmeta = [
600 'postid' => $post['postid'],
601 'metakey' => $metakey,
602 'metavalue' => $metavalue,
603 'forumid' => $post['forumid'],
604 'topicid' => $post['topicid'],
605 'is_first_post' => 0,
606 'status' => $post['status'],
607 'private' => $post['private'],
608 ];
609 $this->add( $postmeta );
610 }
611 }
612 }
613 }
614
615 public function after_edit_post( $post, $topic, $forum, $args ) {
616 $this->add_file( 'post', $post );
617
618 if( ! empty( $args['postmetas'] ) ) {
619 $fields_list = WPF()->post->get_post_fields_list( false, $forum, ! WPF()->current_userid );
620 foreach( $args['postmetas'] as $metakey => $metavalue ) {
621 if( in_array( $metakey, $fields_list ) ) {
622 // Security: Only accept array values for file-type fields to prevent file path injection
623 $field = WPF()->post->get_field( $metakey, 'post', $forum );
624 if( is_array( $metavalue ) && wpfval( $field, 'type' ) !== 'file' ) continue;
625 $postmeta = [
626 'metavalue' => $metavalue,
627 'forumid' => $post['forumid'],
628 'topicid' => $post['topicid'],
629 'is_first_post' => 0,
630 'status' => $post['status'],
631 'private' => $post['private'],
632 ];
633 if( $this->exists( $post['postid'], $metakey ) ) {
634 $this->edit( $postmeta, [
635 'postid' => $post['postid'],
636 'metakey' => $metakey,
637 ] );
638 } else {
639 $postmeta['postid'] = $post['postid'];
640 $postmeta['metakey'] = $metakey;
641 $this->add( $postmeta );
642 }
643 }
644 }
645 }
646 }
647
648 public function after_move_topic( $topic, $forumid ) {
649 $this->edit( [ 'forumid' => $forumid ], [ 'topicid' => $topic['topicid'] ] );
650 }
651
652 public function after_merge_topic( $target, $current, $postids ) {
653 $sql = "UPDATE `" . WPF()->tables->postmeta . "` SET `topicid` = %d, `forumid` = %d, `private` = %d, `is_first_post` = 0 WHERE `topicid` = %d";
654 $sql = WPF()->db->prepare( $sql, $target['topicid'], $target['forumid'], (int) wpfval( $target, 'private' ), $current['topicid'] );
655 if( $postids ) $sql .= " AND `postid` IN(" . implode( ',', array_map( 'wpforo_bigintval', (array) $postids ) ) . ")";
656 WPF()->db->query( $sql );
657 }
658
659 public function after_delete_post( $post ) {
660 $this->delete( [ 'postid' => $post['postid'] ] );
661 }
662
663 public function after_post_status_update( $post, $status ) {
664 $this->edit( [ 'status' => $status ], [ 'postid' => $post['postid'] ] );
665 }
666
667 public function after_topic_private_update( $topicid, $private ) {
668 $this->edit( [ 'private' => $private ], [ 'topicid' => $topicid ] );
669 }
670
671 public function after_is_first_post_update( $postid, $is_first_post ) {
672 $this->edit( [ 'is_first_post' => $is_first_post ], [ 'postid' => $postid ] );
673 }
674 }
675