PluginProbe
wpForo Forum / 3.1.5
wpForo Forum v3.1.5
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 / subscriptions / Subscriptions.php

Subscriptions.php in wpForo Forum 3.1.5, at modules/subscriptions/Subscriptions.php

466 lines 16.6 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\subscriptions;
4
5 use stdClass;
6 use wpforo\modules\follows\Follows;
7 use wpforo\modules\mentioning\Mentioning;
8 use wpforo\modules\subscriptions\classes\Actions;
9 use wpforo\modules\subscriptions\classes\Template;
10
11 // Exit if accessed directly
12 if( ! defined( 'ABSPATH' ) ) exit;
13
14 class Subscriptions {
15 public $default;
16 /* @var Template */
17 public $Template;
18 /* @var Actions */
19 public $Actions;
20 /* @var Mentioning */
21 public $Mentioning;
22 /* @var Follows */
23 public $Follows;
24
25 function __construct() {
26 $this->init_defaults();
27 $this->init_classes();
28 $this->init_hooks();
29 }
30
31 private function init_defaults() {
32 $this->default = new stdClass;
33 $this->default->subscribe_format = [
34 'subid' => '%d',
35 'type' => '%s',
36 'itemid' => '%d',
37 'userid' => '%d',
38 'confirmkey' => '%s',
39 'active' => '%d',
40 'user_name' => '%s',
41 'user_email' => '%s',
42 ];
43 }
44
45 private function init_classes() {
46 $this->Template = new Template();
47 $this->Actions = new Actions();
48 $this->Mentioning = new Mentioning();
49 $this->Follows = new Follows();
50 }
51
52 private function init_hooks() {
53 add_filter( 'wpforo_init_member_templates', [ $this, 'add_templates' ] );
54 add_filter( 'wpforo_after_init_current_object', [ $this, 'init_current_object' ] );
55 }
56
57 public function add_templates( $templates ) {
58 $templates['subscriptions'] = [
59 'type' => 'callback',
60 'key' => 'subscriptions',
61 'menu_shortcode' => 'wpforo-profile-subscriptions',
62 'ico' => '<svg height="12" width="12" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M0 64C0 46.3 14.3 32 32 32c229.8 0 416 186.2 416 416c0 17.7-14.3 32-32 32s-32-14.3-32-32C384 253.6 226.4 96 32 96C14.3 96 0 81.7 0 64zM0 416a64 64 0 1 1 128 0A64 64 0 1 1 0 416zM32 160c159.1 0 288 128.9 288 288c0 17.7-14.3 32-32 32s-32-14.3-32-32c0-123.7-100.3-224-224-224c-17.7 0-32-14.3-32-32s14.3-32 32-32z"/></svg>',
63 'title' => 'Subscriptions',
64 'is_default' => 1,
65 'can' => 'vprs',
66 'add_in_member_menu' => 1,
67 'add_in_member_buttons' => 1,
68 'callback_for_page' => function() {
69 require wpftpl( 'profile-subscriptions.php' );
70 },
71 ];
72
73 return $templates;
74 }
75
76 public function init_current_object( $current_object ) {
77 if( ! $current_object['is_404'] && $current_object['template'] === 'subscriptions' ) {
78 $args = [
79 'offset' => ( $current_object['paged'] - 1 ) * $current_object['items_per_page'],
80 'row_count' => $current_object['items_per_page'],
81 'userid' => $current_object['userid'],
82 'order' => 'DESC',
83 ];
84 $current_object['items_count'] = 0;
85 $current_object['subscribes'] = $this->get_subscribes( $args, $current_object['items_count'] );
86 }
87
88 return $current_object;
89 }
90
91 function get_confirm_key() {
92 return substr( md5( rand() . time() ), 0, 32 );
93 }
94
95 function add( $args = [] ) {
96 if( empty( $args ) && empty( $_REQUEST['sbscrb'] ) ) return false;
97 if( empty( $args ) && ! empty( $_REQUEST['sbscrb'] ) ) $args = $_REQUEST['sbscrb'];
98 if( ! isset( $args['active'] ) || ! $args['active'] ) $args['active'] = 0;
99
100 // Security: extract only expected keys instead of extract()
101 $itemid = isset( $args['itemid'] ) ? $args['itemid'] : null;
102 $userid = isset( $args['userid'] ) ? $args['userid'] : null;
103 $user_email = isset( $args['user_email'] ) ? $args['user_email'] : null;
104 $type = isset( $args['type'] ) ? $args['type'] : null;
105 $confirmkey = isset( $args['confirmkey'] ) ? $args['confirmkey'] : null;
106 $user_name = isset( $args['user_name'] ) ? $args['user_name'] : null;
107 $active = isset( $args['active'] ) ? $args['active'] : null;
108 if( ! isset( $itemid ) || ! ( ( isset( $userid ) && $userid ) || ( isset( $user_email ) && $user_email ) ) || ! isset( $type ) || ! $type ) return false;
109
110 if( empty( $confirmkey ) ) $confirmkey = $this->get_confirm_key();
111
112 $user_name = ( isset( $user_name ) && $user_name ? $user_name : '' );
113 $user_email = ( isset( $user_email ) && $user_email ? $user_email : '' );
114
115 $sql = "SELECT EXISTS(
116 SELECT * FROM `" . WPF()->tables->subscribes . "`
117 WHERE `itemid` = %d
118 AND `type` = %s
119 AND `userid` = %d
120 AND `user_email` = %s
121 ) AS is_exists";
122 $sql = WPF()->db->prepare( $sql, $itemid, $type, $userid, $user_email );
123 if( ! WPF()->db->get_var( $sql ) ) {
124 if( WPF()->db->insert(
125 WPF()->tables->subscribes, [
126 'itemid' => $itemid,
127 'type' => $type,
128 'confirmkey' => $confirmkey,
129 'userid' => $userid,
130 'active' => $active,
131 'user_name' => $user_name,
132 'user_email' => $user_email,
133 ], [
134 '%d',
135 '%s',
136 '%s',
137 '%d',
138 '%d',
139 '%s',
140 '%s',
141 ]
142 ) ) {
143 if( isset( $active ) && $active == 1 ) {
144 if( intval( $userid ) === WPF()->current_userid ) {
145 WPF()->notice->add( 'You have been successfully subscribed', 'success' );
146 } else {
147 WPF()->notice->add( 'Success!', 'success' );
148 }
149 } else {
150 if( intval( $userid ) === WPF()->current_userid ) {
151 WPF()->notice->add( 'Success! Thank you. Please check your email and click confirmation link below to complete this step.', 'success' );
152 } else {
153 WPF()->notice->add( 'Success!', 'success' );
154 }
155 }
156
157 return $confirmkey;
158 }
159
160 }
161
162 WPF()->notice->add( 'Can\'t subscribe to this item', 'error' );
163
164 return false;
165 }
166
167 function edit( $confirmkey = '' ) {
168 if( ! $confirmkey && isset( $_REQUEST['key'] ) && $_REQUEST['key'] ) $confirmkey = $_REQUEST['key'];
169 if( ! $confirmkey ) {
170 WPF()->notice->add( 'Invalid request!', 'error' );
171
172 return false;
173 }
174
175 if( WPF()->db->update( WPF()->tables->subscribes, [ 'active' => 1 ], [ 'confirmkey' => sanitize_text_field( $confirmkey ) ], [ '%d' ], [ '%s' ] ) ) {
176 if( $sbs = $this->get_subscribe( $confirmkey ) ) WPF()->member->set_is_email_confirmed( $sbs['userid'], 1 );
177
178 WPF()->notice->add( 'You have been successfully subscribed', 'success' );
179
180 return true;
181 }
182
183 WPF()->notice->add( 'Your subscription for this item could not be confirmed', 'error' );
184
185 return false;
186 }
187
188 public function reset( $data = [], $all = '', $user = null ) {
189 if( ! $user && ! WPF()->current_userid && ! WPF()->current_user_email ) return false;
190 if( ! $user ) $user = ( WPF()->current_userid ? WPF()->current_userid : WPF()->current_user_email );
191 $data = array_filter( (array) $data );
192 $args = [
193 'itemid' => 0,
194 'type' => '',
195 'userid' => 0,
196 'active' => ( wpforo_setting( 'subscriptions', 'subscribe_confirmation' ) ? 0 : 1 ),
197 'user_name' => '',
198 'user_email' => '',
199 ];
200 if( is_numeric( $user ) ) {
201 $args['userid'] = $user;
202 $where = WPF()->db->prepare( "`userid` = %d", $user );
203 } else {
204 $args['user_email'] = $user;
205 $args['user_name'] = WPF()->current_user_display_name;
206 $where = WPF()->db->prepare( "`user_email` = %s", $user );
207 }
208
209 if( $args['active'] !== 1 && $this->is_email_confirmed( $user ) ) $args['active'] = 1;
210
211 $types = [ 'forum', 'forum-topic' ];
212 if( ! $all ) array_push( $types, 'forums', 'forums-topics' );
213 $sql = "DELETE FROM `" . WPF()->tables->subscribes . "` WHERE `type` IN('" . implode( "','", $types ) . "') AND " . $where;
214 if( ! $all && $data ) {
215 $forumids = array_map('intval', array_keys( $data ));
216 $sql .= " AND `itemid` NOT IN(" . implode( ',', $forumids ) . ")";
217 }
218 WPF()->db->query( $sql );
219
220 if( ! $all && $data ) {
221 foreach( $data as $forumid => $type ) {
222 $sql = "SELECT `subid` FROM `" . WPF()->tables->subscribes . "` WHERE `type` IN('forum', 'forum-topic') AND `itemid` = %d AND " . $where;
223 $sql = WPF()->db->prepare( $sql, $forumid );
224 if( $subid = WPF()->db->get_var( $sql ) ) {
225 WPF()->db->update( WPF()->tables->subscribes, [ 'type' => sanitize_text_field( $type ), 'active' => $args['active'] ], [ 'subid' => $subid ], [ '%s', '%d' ], [ '%d' ] );
226 } else {
227 $args['itemid'] = intval( $forumid );
228 $args['type'] = sanitize_text_field( $type );
229 $this->add( $args );
230 }
231 }
232 }
233
234 if( $all ) {
235 $sql = "SELECT `subid` FROM `" . WPF()->tables->subscribes . "` WHERE `type` IN('forums', 'forums-topics') AND `itemid` = 0 AND " . $where;
236 if( $subid = WPF()->db->get_var( $sql ) ) {
237 WPF()->db->update( WPF()->tables->subscribes, [ 'type' => sanitize_text_field( $all ), 'active' => $args['active'] ], [ 'subid' => $subid ], [ '%s', '%d' ], [ '%d' ] );
238 } else {
239 $args['itemid'] = 0;
240 $args['type'] = sanitize_text_field( $all );
241 $this->add( $args );
242 }
243 }
244
245 return true;
246 }
247
248 function delete( $where = [], $table = '' ) {
249 if( ! $where && $confirmkey = wpfval( $_REQUEST, 'confirmkey' ) ) $where = $confirmkey;
250 if( ! $where ) {
251 WPF()->notice->add( 'Invalid request!', 'error' );
252
253 return false;
254 }
255
256 if( ! is_array( $where ) ) $where = [ 'confirmkey' => $where ];
257 $where = (array) $where;
258
259 $where = wpforo_array_ordered_intersect_key( $where, $this->default->subscribe_format );
260 if( false !== WPF()->db->delete(
261 $table ?: WPF()->tables->subscribes,
262 $where,
263 wpforo_array_ordered_intersect_key( $this->default->subscribe_format, $where )
264 ) ) {
265 WPF()->notice->add( 'You have been successfully unsubscribed', 'success' );
266
267 return true;
268 }
269
270 WPF()->notice->add( 'Could not be unsubscribe from this item', 'error' );
271
272 return false;
273 }
274
275 public function delete_for_all_active_boards( $where = [] ) {
276 if( $boardids = WPF()->board->get_active_boardids() ) {
277 foreach( $boardids as $boardid ) {
278 WPF()->change_board( $boardid );
279 $this->delete( $where );
280 }
281 }
282 }
283
284 /**
285 * @param $args array|string
286 * @param $operator string
287 *
288 * @return array
289 */
290 function get_subscribe( $args, $operator = 'AND' ) {
291 if( is_string( $args ) ) $args = [ "confirmkey" => sanitize_text_field( $args ) ];
292
293 $operator = strtoupper( (string) $operator );
294 if( ! in_array( $operator, [ 'AND', 'OR' ], true ) ) $operator = 'AND';
295
296 $default = [
297 'subid' => null,
298 'itemid' => null,
299 'type' => null,
300 'confirmkey' => null,
301 'userid' => null,
302 'user_email' => null,
303 'active' => null,
304 'order' => '`subid` DESC',
305 'offset' => null,
306 'row_count' => null,
307 ];
308
309 $args = array_merge( $default, $args );
310
311 $sql = "SELECT * FROM `" . WPF()->tables->subscribes . "`";
312
313 $wheres = [];
314 if( ! is_null( $args['subid'] ) ) $wheres[] = "`subid` = " . wpforo_bigintval( $args['subid'] );
315 if( ! is_null( $args['itemid'] ) ) $wheres[] = "`itemid` = " . wpforo_bigintval( $args['itemid'] );
316 if( ! is_null( $args['userid'] ) ) $wheres[] = "`userid` = " . wpforo_bigintval( $args['userid'] );
317 if( ! wpforo_bigintval( $args['userid'] ) && ! is_null( $args['user_email'] ) ) {
318 $wheres[] = "`user_email` = '" . esc_sql( $args['user_email'] ) . "'";
319 }
320 if( ! is_null( $args['confirmkey'] ) ) $wheres[] = "`confirmkey` = '" . esc_sql( $args['confirmkey'] ) . "'";
321 if( ! is_null( $args['active'] ) ) $wheres[] = "`active` = " . intval( $args['active'] );
322 if( ! is_null( $args['type'] ) ) $wheres[] = "`type` IN( '" . implode( "','", array_map( 'esc_sql', (array) $args['type'] ) ) . "' )";
323
324 if( $wheres ) $sql .= sprintf( ' WHERE %1$s', implode( sprintf( ' %1$s ', $operator ), $wheres ) );
325 if( (int) $args['row_count'] ) $sql .= sprintf( ' LIMIT %1$d,%2$d', intval( $args['offset'] ), intval( $args['row_count'] ) );
326
327 if( WPF()->ram_cache->exists( $sql ) ) {
328 $subscribe = WPF()->ram_cache->get( $sql );
329 } else {
330 $subscribe = (array) WPF()->db->get_row( $sql, ARRAY_A );
331 WPF()->ram_cache->set( $sql, $subscribe );
332 }
333
334 return $subscribe;
335 }
336
337 function get_subscribes( $args = [], &$items_count = 0 ) {
338 $default = [
339 'itemid' => null,
340 'type' => [], // topic | forum
341 'userid' => null, //
342 'active' => 1,
343 'orderby' => 'subid', // order by `field`
344 'order' => 'DESC', // ASC DESC
345 'offset' => null, // OFFSET
346 'row_count' => null, // ROW COUNT
347 ];
348
349 $args = wpforo_parse_args( $args, $default );
350
351 // Security: extract only expected keys instead of extract()
352 $itemid = $args['itemid'];
353 $type = $args['type'];
354 $userid = $args['userid'];
355 $active = $args['active'];
356 $orderby = $args['orderby'];
357 $order = $args['order'];
358 $offset = $args['offset'];
359 $row_count = $args['row_count'];
360
361 $sql = "SELECT * FROM `" . WPF()->tables->subscribes . "`";
362 $wheres = [];
363
364 if( $type ) $wheres[] = " `type` IN( '" . implode( "','", array_map( 'esc_sql', (array) $type ) ) . "')";
365 if( ! is_null( $active ) ) $wheres[] = " `active` = " . intval( $active );
366 if( ! is_null( $itemid ) ) $wheres[] = " `itemid` = " . wpforo_bigintval( $itemid );
367 if( ! is_null( $userid ) ) $wheres[] = " `userid` = " . wpforo_bigintval( $userid );
368
369 if( ! empty( $wheres ) ) $sql .= " WHERE " . implode( " AND ", $wheres );
370
371 $item_count_sql = preg_replace( '#SELECT.+?FROM#isu', 'SELECT count(*) FROM', $sql );
372 if( $item_count_sql ) $items_count = WPF()->db->get_var( $item_count_sql );
373
374 $sql .= " ORDER BY `$orderby` " . $order;
375
376 if( ! is_null( $row_count ) ) {
377 if( ! is_null( $offset ) ) {
378 $sql .= esc_sql( " LIMIT $offset,$row_count" );
379 } else {
380 $sql .= esc_sql( " LIMIT $row_count" );
381 }
382 }
383
384 return WPF()->db->get_results( $sql, ARRAY_A );
385
386 }
387
388 function get_confirm_link( $args ) {
389 if( is_string( $args ) ) return wpforo_home_url( "?wpfaction=sbscrbconfirm&key=" . sanitize_text_field( $args ) );
390
391 if( $args['type'] === 'forum' ) {
392 $url = WPF()->forum->get_forum_url( $args['itemid'] ) . '/';
393 } elseif( $args['type'] === 'topic' ) {
394 $url = WPF()->topic->get_url( $args['itemid'] ) . '/';
395 } else {
396 $url = wpforo_home_url();
397 }
398
399 return wpforo_home_url( $url . "?wpfaction=sbscrbconfirm&key=" . sanitize_text_field( $args['confirmkey'] ) );
400 }
401
402 function get_unsubscribe_link( $confirmkey ) {
403 return wpforo_home_url( "?wpfaction=unsbscrb&key=" . sanitize_text_field( $confirmkey ) );
404 }
405
406 public function is_email_confirmed( $user = null ) {
407 if( ! $user && ! WPF()->current_userid && ! WPF()->current_user_email ) return false;
408 if( ! $user ) $user = ( WPF()->current_userid ? WPF()->current_userid : WPF()->current_user_email );
409
410 $sql = ( is_numeric( $user ) ? "`userid` = %d" : "`user_email` = %s" );
411 $where = WPF()->db->prepare( $sql, $user );
412
413 if( WPF()->current_userid === $user && wpfval( WPF()->current_user, 'is_email_confirmed' ) ) {
414 $has_confirmed = WPF()->current_user['is_email_confirmed'];
415 } elseif( is_numeric( $user ) ) {
416 $has_confirmed = WPF()->member->get_is_email_confirmed( WPF()->current_userid );
417 } else {
418 $has_confirmed = WPF()->db->get_var( "SELECT `subid` FROM `" . WPF()->tables->subscribes . "` WHERE `active` = 1 AND " . $where );
419 }
420
421 return (bool) $has_confirmed;
422 }
423
424 public function get_sbj_msg( $type, $pitem, $item, $owner, $user, $unsubscribe_link ) {
425 $sbj = '';
426 $msg = '';
427 switch( $type ) {
428 case 'new_topic':
429 $sbj = wpforo_setting( 'subscriptions', 'new_topic_notification_email_subject' );
430 $msg = wpforo_setting( 'subscriptions', 'new_topic_notification_email_message' );
431 if( (int) $item['status'] ) {
432 $sbj = __( 'Please Moderate: ', 'wpforo' ) . $sbj;
433 $msg .= sprintf(
434 '<br><br><p style="color: #DD0000;">%1$s</p>',
435 __( 'This topic is currently unapproved. You can approve topics in Dashboard &raquo; Forums &raquo; Moderation admin page.', 'wpforo' )
436 );
437 }
438 break;
439 case 'new_post':
440 $sbj = wpforo_setting( 'subscriptions', 'new_post_notification_email_subject' );
441 $msg = wpforo_setting( 'subscriptions', 'new_post_notification_email_message' );
442 if( (int) $item['status'] ) {
443 $sbj = __( 'Please Moderate: ', 'wpforo' ) . $sbj;
444 $msg .= sprintf(
445 '<br><br><p style="color: #DD0000;">%1$s</p>',
446 __( 'This post is currently unapproved. You can approve posts in Dashboard &raquo; Forums &raquo; Moderation admin page.', 'wpforo' )
447 );
448 }
449 break;
450 case 'user_follow':
451 $sbj = wpforo_setting( 'subscriptions', 'user_following_email_subject' );
452 $msg = wpforo_setting( 'subscriptions', 'user_following_email_message' );
453 break;
454 case 'user_mention':
455 $sbj = wpforo_setting( 'subscriptions', 'user_mention_email_subject' );
456 $msg = wpforo_setting( 'subscriptions', 'user_mention_email_message' );
457 break;
458 }
459
460 $sbj = wpforo_apply_email_shortcodes( $sbj, $pitem, $item, $owner, $user, $unsubscribe_link );
461 $msg = wpforo_apply_email_shortcodes( $msg, $pitem, $item, $owner, $user, $unsubscribe_link );
462
463 return compact( 'sbj', 'msg' );
464 }
465 }
466