PluginProbe
wpForo Forum / 3.1.0
wpForo Forum v3.1.0
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 / follows / Follows.php

Follows.php in wpForo Forum 3.1.0, at modules/follows/Follows.php

416 lines 14.1 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\follows;
4
5 use stdClass;
6 use wpforo\modules\follows\classes\Actions;
7 use wpforo\modules\follows\classes\Template;
8
9 class Follows {
10 /** @var stdClass */
11 public $default;
12 /** @var Template */
13 public $Template;
14 /** @var Actions */
15 public $Actions;
16
17
18 public function __construct() {
19 $this->init_defaults();
20 $this->init_classes();
21 $this->init_hooks();
22 }
23
24 private function init_defaults() {
25 $this->default = new stdClass();
26 $this->default->follow = [
27 'followid' => 0,
28 'itemid' => 0,
29 'itemtype' => '',
30 'confirmkey' => '',
31 'userid' => 0,
32 'active' => 0,
33 'name' => '',
34 'email' => '',
35 'confirm_link' => '',
36 'unfollow_link' => '',
37 ];
38 $this->default->follow_format = [
39 'followid' => '%d',
40 'itemid' => '%d',
41 'itemtype' => '%s',
42 'confirmkey' => '%s',
43 'userid' => '%d',
44 'active' => '%d',
45 'name' => '%s',
46 'email' => '%s',
47 ];
48 $this->default->sql_select_args = [
49 'followid' => null,
50 'itemid' => null,
51 'itemtype' => null,
52 'itemtype_include' => [],
53 'itemtype_exclude' => [],
54 'confirmkey' => null,
55 'userid' => null,
56 'active' => null,
57 'name' => null,
58 'email' => null,
59 'orderby' => null,
60 'offset' => null,
61 'row_count' => null,
62 ];
63 }
64
65 private function init_classes() {
66 $this->Template = new Template();
67 $this->Actions = new Actions();
68 }
69
70 private function init_hooks() {
71 add_filter( 'wpforo_init_member_templates', [ $this, 'add_templates' ] );
72 add_filter( 'wpforo_after_init_current_object', [ $this, 'init_current_object' ] );
73 }
74
75 public function add_templates( $templates ) {
76 $templates['followers'] = [
77 'type' => 'callback',
78 'key' => 'followers',
79 'menu_shortcode' => 'wpforo-profile-followers',
80 '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>',
81 'title' => 'Followers',
82 'is_default' => 1,
83 'can' => 'vprs',
84 'add_in_member_menu' => 0,
85 'add_in_member_buttons' => 0,
86 'callback_for_page' => function() {
87 require wpftpl( 'profile-followers.php' );
88 },
89 ];
90 $templates['following'] = [
91 'type' => 'callback',
92 'key' => 'following',
93 'menu_shortcode' => 'wpforo-profile-following',
94 '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>',
95 'title' => 'Following',
96 'is_default' => 1,
97 'can' => 'vprs',
98 'add_in_member_menu' => 0,
99 'add_in_member_buttons' => 0,
100 'callback_for_page' => function() {
101 require wpftpl( 'profile-following.php' );
102 },
103 ];
104
105 return $templates;
106 }
107
108 public function init_current_object( $current_object ) {
109 if( ! $current_object['is_404'] ) {
110 if( $current_object['template'] === 'followers' ) {
111 $args = [
112 'offset' => ( $current_object['paged'] - 1 ) * $current_object['items_per_page'],
113 'row_count' => $current_object['items_per_page'],
114 'itemid' => $current_object['userid'],
115 'itemtype' => 'user',
116 'order' => 'DESC',
117 ];
118 $current_object['items_count'] = 0;
119 $current_object['follows'] = $this->get_follows( $args, $current_object['items_count'] );
120 } elseif( $current_object['template'] === 'following' ) {
121 $args = [
122 'offset' => ( $current_object['paged'] - 1 ) * $current_object['items_per_page'],
123 'row_count' => $current_object['items_per_page'],
124 'userid' => $current_object['userid'],
125 'itemtype' => 'user',
126 'order' => 'DESC',
127 ];
128 $current_object['items_count'] = 0;
129 $current_object['follows'] = $this->get_follows( $args, $current_object['items_count'] );
130 }
131 }
132
133 return $current_object;
134 }
135
136 private function get_confirm_key() {
137 return substr( md5( rand() . time() ), 0, 32 );
138 }
139
140 /**
141 * @param $follow
142 *
143 * @return array
144 */
145 public function decode( $follow ) {
146 $follow = array_merge( $this->default->follow, (array) $follow );
147 $follow['followid'] = wpforo_bigintval( $follow['followid'] );
148 $follow['itemid'] = wpforo_bigintval( $follow['itemid'] );
149 $follow['userid'] = wpforo_bigintval( $follow['userid'] );
150 $follow['active'] = (bool) intval( $follow['active'] );
151 $follow['name'] = trim( strip_tags( (string) $follow['name'] ) );
152 $follow['email'] = sanitize_email( $follow['email'] );
153 if( ! ( $follow['itemtype'] = trim( strip_tags( (string) $follow['itemtype'] ) ) ) ) $follow['itemtype'] = 'user';
154 if( ! ( $follow['confirmkey'] = trim( (string) $follow['confirmkey'] ) ) ) $follow['confirmkey'] = $this->get_confirm_key();
155 $follow['confirm_link'] = $this->get_confirm_link( $follow['confirmkey'] );
156 $follow['unfollow_link'] = $this->get_unfollow_link( $follow['confirmkey'] );
157
158 return $follow;
159 }
160
161 /**
162 * @param $follow
163 *
164 * @return array
165 */
166 private function encode( $follow ) {
167 $follow = $this->decode( $follow );
168 $follow['active'] = intval( $follow['active'] );
169
170 return $follow;
171 }
172
173 /**
174 * @param $follow
175 *
176 * @return false|int
177 */
178 public function add( $follow ) {
179 $follow = $this->encode( $follow );
180 unset( $follow['followid'] );
181 $follow = wpforo_array_ordered_intersect_key( $follow, $this->default->follow_format );
182 if( WPF()->db->insert(
183 WPF()->tables->follows,
184 $follow,
185 wpforo_array_ordered_intersect_key( $this->default->follow_format, $follow )
186 ) ) {
187 $follow['followid'] = WPF()->db->insert_id;
188 do_action( 'wpforo_after_add_follow', $follow );
189
190 return $follow['followid'];
191 }
192
193 return false;
194 }
195
196 /**
197 * @param array $fields
198 * @param array|int|string $where
199 *
200 * @return bool
201 */
202 public function edit( $fields, $where ) {
203 if( is_numeric( $where ) ) {
204 $where = [ 'followid' => wpforo_bigintval( $where ) ];
205 } elseif( is_string( $where ) ) {
206 $where = [ 'confirmkey' => $where ];
207 }
208 $fields = wpforo_array_ordered_intersect_key( $fields, $this->default->follow_format );
209 if( false !== WPF()->db->update(
210 WPF()->tables->follows,
211 $fields = wpforo_array_ordered_intersect_key( $this->encode( $fields ), $fields ),
212 $where = wpforo_array_ordered_intersect_key( $where, $this->default->follow_format ),
213 wpforo_array_ordered_intersect_key( $this->default->follow_format, $fields ),
214 wpforo_array_ordered_intersect_key( $this->default->follow_format, $where )
215 ) ) {
216 do_action( 'wpforo_after_edit_follow', $fields, $where );
217
218 return true;
219 }
220
221 return false;
222 }
223
224 /**
225 * @param array|int|string $args
226 * @param string $operator
227 *
228 * @return bool
229 */
230 public function delete( $args, string $operator = 'AND' ): bool {
231 if( is_numeric( $args ) ) {
232 $args = [ 'followid' => wpforo_bigintval( $args ) ];
233 } elseif( is_string( $args ) ) {
234 $args = [ 'confirmkey' => $args ];
235 }
236 $operator = trim( strtoupper( (string) $operator ) );
237 if( ! in_array( $operator, [ 'AND', 'OR' ], true ) ) $operator = 'AND';
238
239 do_action( 'wpforo_before_delete_follow', $args, $operator );
240
241 $sql = "DELETE FROM " . WPF()->tables->follows;
242 if( $wheres = $this->build_sql_wheres( $args ) ) $sql .= " WHERE " . implode( " $operator ", $wheres );
243
244 $args = $this->parse_args( $args );
245 if( $args['orderby'] ) $sql .= " ORDER BY " . $args['orderby'];
246 if( $args['row_count'] ) $sql .= " LIMIT " . intval( $args['row_count'] );
247
248 $r = WPF()->db->query( $sql );
249
250 do_action( 'wpforo_after_delete_follow', $args, $operator );
251
252 return false !== $r;
253 }
254
255 private function parse_args( $args ) {
256 $args = wpforo_parse_args( $args, $this->default->sql_select_args );
257 $args = wpforo_array_ordered_intersect_key( $args, $this->default->sql_select_args );
258 $args['itemtype_include'] = wpforo_parse_args( $args['itemtype_include'] );
259 $args['itemtype_exclude'] = wpforo_parse_args( $args['itemtype_exclude'] );
260 $args['orderby'] = sanitize_sql_orderby( (string) $args['orderby'] );
261
262 return $args;
263 }
264
265 private function build_sql_wheres( $args ) {
266 $args = $this->parse_args( $args );
267 $wheres = [];
268
269 if( ! is_null( $args['followid'] ) ) $wheres[] = "`followid` = '" . wpforo_bigintval( $args['followid'] ) . "'";
270 if( ! is_null( $args['itemid'] ) ) $wheres[] = "`itemid` = '" . wpforo_bigintval( $args['itemid'] ) . "'";
271 if( ! is_null( $args['itemtype'] ) ) $wheres[] = "`itemtype` = '" . esc_sql( $args['itemtype'] ) . "'";
272 if( ! is_null( $args['confirmkey'] ) ) $wheres[] = "`confirmkey` = '" . esc_sql( $args['confirmkey'] ) . "'";
273 if( ! is_null( $args['userid'] ) ) $wheres[] = "`userid` = '" . wpforo_bigintval( $args['userid'] ) . "'";
274 if( ! is_null( $args['active'] ) ) $wheres[] = "`active` = '" . intval( $args['active'] ) . "'";
275 if( ! is_null( $args['name'] ) ) $wheres[] = "`name` = '" . esc_sql( $args['name'] ) . "'";
276 if( ! is_null( $args['email'] ) ) $wheres[] = "`email` = '" . esc_sql( $args['email'] ) . "'";
277 if( ! empty( $args['itemtype_include'] ) ) $wheres[] = "`itemtype` IN('" . implode( "','", array_map( 'trim', $args['itemtype_include'] ) ) . "')";
278 if( ! empty( $args['itemtype_exclude'] ) ) $wheres[] = "`itemtype` NOT IN(" . implode( "','", array_map( 'trim', $args['itemtype_exclude'] ) ) . "')";
279
280 return $wheres;
281 }
282
283 /**
284 * @param $args
285 * @param $select
286 * @param $operator
287 *
288 * @return string
289 */
290 private function build_sql_select( $args, $select = '', $operator = 'AND' ) {
291 if( ! $select ) $select = '*';
292 $operator = trim( strtoupper( (string) $operator ) );
293 if( ! in_array( $operator, [ 'AND', 'OR' ], true ) ) $operator = 'AND';
294
295 $sql = "SELECT $select FROM " . WPF()->tables->follows;
296 if( $wheres = $this->build_sql_wheres( $args ) ) $sql .= " WHERE " . implode( " $operator ", $wheres );
297
298 $args = $this->parse_args( $args );
299 if( $args['orderby'] ) $sql .= " ORDER BY " . $args['orderby'];
300 if( $args['row_count'] ) $sql .= " LIMIT " . intval( $args['offset'] ) . "," . intval( $args['row_count'] );
301
302 return $sql;
303 }
304
305 /**
306 * @param array|numeric $args
307 *
308 * @return array
309 */
310 public function _get_follow( $args, $operator = 'AND' ) {
311 if( is_numeric( $args ) ) $args = [ 'followid' => wpforo_bigintval( $args ) ];
312 if( ! wpfkey( $args, 'orderby' ) ) $args['orderby'] = '`followid` DESC';
313 $follow = (array) WPF()->db->get_row( $this->build_sql_select( $args, '', $operator ), ARRAY_A );
314 if( $follow ) $follow = $this->decode( $follow );
315
316 return $follow;
317 }
318
319 public function get_follow( $args, $operator = 'AND' ) {
320 return wpforo_ram_get( [ $this, '_get_follow' ], $args, $operator );
321 }
322
323 /**
324 * @param array $args
325 *
326 * @return array
327 */
328 public function _get_follows( $args = [], $operator = 'AND' ) {
329 return array_map( [ $this, 'decode' ], (array) WPF()->db->get_results( $this->build_sql_select( $args, '', $operator ), ARRAY_A ) );
330 }
331
332 public function get_follows( $args = [], $operator = 'AND' ) {
333 return wpforo_ram_get( [ $this, '_get_follows' ], $args, $operator );
334 }
335
336 public function _get_follows_col( $col, $args = [], $operator = 'AND' ) {
337 $r = WPF()->db->get_col( $this->build_sql_select( $args, "`$col`", $operator ) );
338 if( $this->default->follow_format[ $col ] === '%d' ) $r = array_map( 'wpforo_bigintval', $r );
339
340 return $r;
341 }
342
343 public function get_follows_col( $col, $args = [], $operator = 'AND' ) {
344 return wpforo_ram_get( [ $this, '_get_follows_col' ], $col, $args, $operator );
345 }
346
347 /**
348 * @param array $args
349 *
350 * @return int
351 */
352 public function _get_count( $args = [], $operator = 'AND' ) {
353 return (int) WPF()->db->get_var( $this->build_sql_select( $args, 'COUNT(*)', $operator ) );
354 }
355
356 public function get_count( $args = [], $operator = 'AND' ) {
357 return wpforo_ram_get( [ $this, '_get_count' ], $args, $operator );
358 }
359
360 public function user_is_following_this( $userid, $itemid, $itemtype = 'user' ) {
361 return (bool) $this->get_follow( [ 'userid' => $userid, 'itemid' => $itemid, 'itemtype' => $itemtype, 'active' => true ] );
362 }
363
364 private function get_confirm_link( $confirmkey ) {
365 return wpforo_url( "?wpfaction=followconfirm&key=$confirmkey", 'member' );
366 }
367
368 private function get_unfollow_link( $confirmkey ) {
369 return wpforo_url( "?wpfaction=unfollow&key=$confirmkey", 'member' );
370 }
371
372 /**
373 * @param int $userid
374 * @param int $itemid
375 * @param string $itemtype
376 *
377 * @return bool|int
378 */
379 public function follow( $userid, $itemid, $itemtype = 'user' ) {
380 if( ! $this->user_is_following_this( $userid, $itemid, $itemtype ) ) {
381 return $this->add( [
382 'itemid' => $itemid,
383 'itemtype' => $itemtype,
384 'userid' => $userid,
385 'active' => true,
386 ] );
387 }
388
389 return true;
390 }
391
392 /**
393 * @param int $userid
394 * @param int $itemid
395 * @param string $itemtype
396 *
397 * @return bool
398 */
399 public function unfollow( $userid, $itemid, string $itemtype = 'user' ): bool {
400 return $this->delete( [ 'userid' => $userid, 'itemid' => $itemid, 'itemtype' => $itemtype ] );
401 }
402
403 /**
404 * @param string $confirmkey
405 *
406 * @return bool
407 */
408 public function unfollow_by_key( string $confirmkey ): bool {
409 return $this->delete( $confirmkey );
410 }
411
412 public function confirm( $confirmkey ) {
413 return $this->edit( [ 'active' => true ], $confirmkey );
414 }
415 }
416