Making WordPress.org

source: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-bbp-term-subscription/inc/class-plugin.php

Last change on this file was 12012, checked in by SergeyBiryukov, 8 weeks ago

Support Forums: Improve consistency of various error messages.

  • Use sentence case for the word Error, instead of all caps. Using all caps should be avoided for better readability and because screen readers may pronounce all-caps words as abbreviations.
  • Move the colon of the Error: prefix inside the <strong> tags.

This matches similar changes in WordPress core.

Follow-up to [WP47156], [WP53118], [WP53337], [WP53458], [3702], [3719], [3797], [3937], [4043], [11301], [11382], [11383], [11580].

Props NekoJonez.
Fixes #6309.

  • Property svn:eol-style set to native
File size: 28.7 KB
Line 
1<?php
2
3namespace WordPressdotorg\Forums\Term_Subscription;
4
5class Plugin {
6
7        /**
8         * @todo AJAXify subscription action.
9         */
10
11        public $taxonomy  = false;
12        public $labels    = array();
13        public $directory = false;
14
15        protected $term        = false;
16        protected $subscribers = array();
17
18        const META_KEY = '_bbp_term_subscription';
19
20        /**
21         * Valid actions for this plugin.
22         */
23        const VALID_ACTIONS = array(
24                'wporg_bbp_subscribe_term',
25                'wporg_bbp_unsubscribe_term',
26        );
27
28        /**
29         * Length of time the unsubscription links are valid.
30         *
31         * @var int
32         */
33        const UNSUBSCRIBE_LIFETIME = 604800; // WEEK_IN_SECONDS
34
35        public function __construct( $args = array() ) {
36                $r = wp_parse_args( $args, array(
37                        'taxonomy'  => 'topic-tag',
38                        'directory' => false,
39                        'labels'    => array(
40                                'subscribed_header'      => __( 'Subscribed Topic Tags', 'wporg-forums' ),
41                                'subscribed_user_notice' => __( 'You are not currently subscribed to any topic tags.', 'wporg-forums' ),
42                                'subscribed_anon_notice' => __( 'This user is not currently subscribed to any topic tags.', 'wporg-forums' ),
43                                'receipt'                => __( "You are receiving this email because you are subscribed to the %s tag.", 'wporg-forums'),
44                        ),
45                ) );
46
47                $this->taxonomy  = $r['taxonomy'];
48                $this->labels    = $r['labels'];
49                $this->directory = $r['directory'];
50
51                // If no taxonomy was provided, there's nothing we can do.
52                if ( ! $this->taxonomy ) {
53                        return;
54                }
55
56                add_action( 'bbp_init', array( $this, 'bbp_init' ) );
57        }
58
59        /**
60         * Initialize the plugin.
61         */
62        public function bbp_init() {
63                // Add views and actions for users.
64                add_action( 'bbp_get_request', array( $this, 'term_subscribe_handler' ) );
65                add_action( 'bbp_post_request', array( $this, 'term_subscribe_handler' ) );
66                add_action( 'bbp_template_redirect', array( $this, 'fix_bbpress_post_actions' ), 9 ); // before bbp_get_request/bbp_post_request
67
68                // Notify subscribers when a topic or reply with a given term is added.
69                add_action( 'bbp_new_topic', array( $this, 'notify_term_subscribers_of_new_topic' ), 10, 4 );
70                add_action( 'bbp_new_reply', array( $this, 'notify_term_subscribers_of_new_reply' ), 10, 5 );
71
72                // Replace the title of subscription emails with the term-specific prefix.
73                // This applies to all notification emails sent related to the topic, not just the term-specific emails.
74                add_filter( 'bbp_forum_subscription_mail_title', array( $this, 'replace_forum_subscription_mail_title' ), 10, 2 );
75                add_filter( 'bbp_subscription_mail_title',       array( $this, 'replace_topic_subscription_mail_title' ), 10, 3 );
76
77                // Add a section to the user subscriptions list to allow management of subscriptions.
78                add_action( 'bbp_template_after_user_subscriptions', array( $this, 'user_subscriptions' ) );
79
80                // Add a banner above the 'forum' about being subscribed to it.
81                add_action( 'bbp_template_before_topics_index', array( $this, 'before_view' ) );
82                add_action( 'wporg_compat_before_single_view',  array( $this, 'before_view' ) );
83        }
84
85        /**
86         * bbPress has two action handlers, GET and POST, a POST action cannot work with ?action= from the URL.
87         *
88         * This is used by the email-unsubscription handler.
89         */
90        public function fix_bbpress_post_actions() {
91                if (
92                        bbp_is_post_request() &&
93                        empty( $_POST['action'] ) &&
94                        ! empty( $_GET['action'] ) &&
95                        in_array( $_GET['action'], self::VALID_ACTIONS, true )
96                ) {
97                        $_POST['action'] = $_GET['action'];
98                }
99        }
100
101        /**
102         * Add a notice that you're subscribed to the tag/plugin/theme, or have just unsubscribed.
103         */
104        public function before_view() {
105                $term = $this->get_current_term();
106
107                if ( ! $term || $term->taxonomy !== $this->taxonomy ) {
108                        return;
109                }
110
111                do_action( 'bbp_template_notices' );
112
113                $is_subscribed = self::is_user_subscribed_to_term( get_current_user_id(), $term->term_id );
114
115                if ( $is_subscribed ) {
116                        $message = sprintf(
117                                __( 'You are subscribed to this forum, and will receive emails for future topic activity. <a href="%1$s">Unsubscribe from %2$s</a>.', 'wporg-forums' ),
118                                self::get_subscription_url( get_current_user_id(), $term->term_id, $this->taxonomy ),
119                                esc_html( $term->name )
120                        );
121                } elseif ( ! empty( $_GET['success'] ) && 'subscription-removed' === $_GET['success'] ) {
122                        $message = sprintf(
123                                __( 'You have been unsubscribed from future emails for %1$s.', 'wporg-forums' ),
124                                esc_html( $term->name )
125                        );
126                } else {
127                        return;
128                }
129
130                echo '<div class="notice notice-info notice-alt with-dashicon">';
131                echo '<span class="dashicons dashicons-email-alt"></span>';
132                echo "<p>{$message}</p>";
133                echo '</div>';
134        }
135
136        /**
137         * Handle a user subscribing to and unsubscribing from a term that is
138         * in the taxonomy of this instance.
139         *
140         * @param string $action The requested action to compare this function to
141         */
142        public function term_subscribe_handler( $action = '' ) {
143                // Bail if the actions aren't meant for this function.
144                if ( ! in_array( $action, self::VALID_ACTIONS ) ) {
145                        return;
146                }
147
148                if ( ! bbp_is_subscriptions_active() ) {
149                        return false;
150                }
151
152                // Determine the term the request is for, overwrite with ?term_id if specified.
153                $term = $this->get_current_term();
154                if ( ! empty( $_GET['term_id'] ) ) {
155                        $term = get_term( intval( $_GET['term_id'] ), $this->taxonomy );
156                }
157                if ( ! $term ) {
158                        return;
159                }
160
161                $term_id = $term->term_id;
162                $auth    = 'nonce';
163                $user_id = isset( $_REQUEST['user_id'] ) ? $_REQUEST['user_id'] : get_current_user_id(); // Must pass nonce check below.
164
165                // If a user_id + token is provided, verify the request and maybe use the provided user_id.
166                if ( isset( $_GET['token'] ) ) {
167                        $auth    = 'token';
168                        $user_id = $this->has_valid_unsubscription_token();
169
170                        if ( ! $user_id ) {
171                                bbp_add_error( 'wporg_bbp_subscribe_invalid_token', __( '<strong>Error:</strong> Link expired!', 'wporg-forums' ) );
172                                return false;
173                        }
174
175                        // Require a POST request for verification. Gmail will bypass this for one-click unsubscriptions by POST'ing to the URL.
176                        if ( empty( $_POST ) ) {
177                                wp_die(
178                                        sprintf(
179                                                '<h1>%1$s</h1>' .
180                                                '<p>%2$s</p>' .
181                                                '<form method="POST" action="%3$s">' .
182                                                        '<input type="submit" name="confirm" value="%4$s">' .
183                                                        '&nbsp<a href="%5$s">%6$s</a>' .
184                                                '</form>',
185                                                get_bloginfo('name'),
186                                                sprintf(
187                                                        /* translators: 1: Plugin, Theme, or Tag name. */
188                                                        esc_html__( 'Do you wish to unsubscribe from future emails for %s?', 'wporg-forums' ),
189                                                        $term->name
190                                                ),
191                                                esc_attr( $_SERVER['REQUEST_URI'] ),
192                                                esc_attr__( 'Yes, unsubscribe me', 'wporg-forums' ),
193                                                esc_url( get_term_link( $term ) ),
194                                                esc_attr( sprintf(
195                                                        /* translators: 1: Plugin, Theme, or Tag name. */
196                                                        __( 'No, take me to the %s forum.', 'wporg-forums' ),
197                                                        $term->name
198                                                ) )
199                                        )
200                                );
201                                exit;
202                        }
203
204                }
205
206                // Check for empty term id.
207                if ( empty( $user_id ) ) {
208                        bbp_add_error( 'wporg_bbp_subscribe_logged_id', __( '<strong>Error:</strong> You must be logged in to do this!', 'wporg-forums' ) );
209
210                // Check nonce.
211                } elseif ( 'token' !== $auth && ! bbp_verify_nonce_request( 'toggle-term-subscription_' . $user_id . '_' . $term_id . '_' . $this->taxonomy ) ) {
212                        bbp_add_error( 'wporg_bbp_subscribe_nonce', __( '<strong>Error:</strong> Are you sure you wanted to do that?', 'wporg-forums' ) );
213
214                // Check user's ability to spectate if attempting to subscribe to a term.
215                } elseif ( ! user_can( $user_id, 'spectate' ) && 'wporg_bbp_subscribe_term' === $action ) {
216                        bbp_add_error( 'wporg_bbp_subscribe_permissions', __( '<strong>Error:</strong> You don\'t have permission to do this!', 'wporg-forums' ) );
217                }
218
219                if ( bbp_has_errors() ) {
220                        return;
221                }
222
223                $success       = false;
224                $is_subscribed = self::is_user_subscribed_to_term( $user_id, $term_id );
225
226                if ( 'wporg_bbp_unsubscribe_term' === $action ) {
227                        $success = ! $is_subscribed || self::remove_user_subscription( $user_id, $term_id );
228                } elseif ( 'wporg_bbp_subscribe_term' === $action ) {
229                        $success = $is_subscribed || self::add_user_subscription( $user_id, $term_id );
230                }
231
232                // Redirect
233                if ( bbp_is_subscriptions() ) {
234                        $redirect = bbp_get_subscriptions_permalink( $user_id );
235                } else {
236                        $redirect = get_term_link( $term_id );
237                }
238
239                // Success!
240                if ( true === $success ) {
241                        if ( 'wporg_bbp_unsubscribe_term' === $action ) {
242                                $redirect = add_query_arg( 'success', 'subscription-removed', $redirect );
243                        }
244
245                        bbp_redirect( $redirect );
246                } elseif ( true === $is_subscribed && 'wporg_bbp_subscribe_term' === $action ) {
247                        /* translators: Term: topic tag */
248                        bbp_add_error( 'wporg_bbp_subscribe_user', __( '<strong>Error:</strong> There was a problem subscribing to that term!', 'wporg-forums' ) );
249                } elseif ( false === $is_subscribed && 'wporg_bbp_unsubscribe_term' === $action ) {
250                        /* translators: Term: topic tag */
251                        bbp_add_error( 'wporg_bbp_unsubscribe_user', __( '<strong>Error:</strong> There was a problem unsubscribing from that term!', 'wporg-forums' ) );
252                }
253        }
254
255        /**
256         * Use the existing bbp_notify_forum_subscribers() to send out term subscriptions for new topics.
257         * Avoid duplicate notifications for forum subscribers through the judicious use of filters within
258         * the function.
259         *
260         * @param int $topic_id The topic id
261         * @param int $forum_id The forum id
262         * @param mixed $anonymous_data Array of anonymous user data
263         * @param int $topic_author The topic author id
264         */
265        public function notify_term_subscribers_of_new_topic( $topic_id, $forum_id, $anonymous_data = false, $topic_author = 0 ) {
266                $terms = get_the_terms( $topic_id, $this->taxonomy );
267                if ( ! $terms ) {
268                        return;
269                }
270
271                // Users that will be notified another way, or have already been notified.
272                $notified_users = array();
273
274                // Remove the author from being notified of their own topic.
275                $notified_users[] = bbp_get_topic_author_id( $topic_id );
276
277                // Get users who were already notified and exclude them.
278                $forum_subscribers = bbp_get_forum_subscribers( $forum_id, true );
279                if ( ! empty( $forum_subscribers ) ) {
280                        $notified_users = array_merge( $notified_users, $forum_subscribers );
281                }
282
283                // Replace forum-specific messaging with term subscription messaging.
284                add_filter( 'bbp_forum_subscription_mail_message', array( $this, 'replace_forum_subscription_mail_message' ), 10, 4 );
285
286                // Replace forum subscriber list with term subscribers, avoiding duplicates.
287                add_filter( 'bbp_forum_subscription_user_ids', array( $this, 'add_term_subscribers' ) );
288
289                // Personalize the emails.
290                add_filter( 'wporg_bbp_subscription_email', array( $this, 'personalize_subscription_email' ) );
291
292                foreach ( $terms as $term ) {
293                        $subscribers = $this->get_term_subscribers( $term->term_id );
294                        if ( ! $subscribers ) {
295                                continue;
296                        }
297
298                        $subscribers = array_diff( $subscribers, $notified_users );
299                        if ( ! $subscribers ) {
300                                continue;
301                        }
302
303                        $this->term        = $term;
304                        $this->subscribers = $subscribers;
305
306                        // Actually notify the term subscribers.
307                        bbp_notify_forum_subscribers( $topic_id, $forum_id );
308
309                        // Don't email them twice.
310                        $notified_users = array_merge( $notified_users, $subscribers );
311                }
312
313                // Reset
314                $this->term        = false;
315                $this->subscribers = array();
316
317                // Remove filters.
318                remove_filter( 'bbp_forum_subscription_mail_message', array( $this, 'replace_forum_subscription_mail_message' ) );
319                remove_filter( 'bbp_forum_subscription_user_ids',     array( $this, 'add_term_subscribers' ) );
320                remove_filter( 'wporg_bbp_subscription_email',        array( $this, 'personalize_subscription_email' ) );
321        }
322
323        /**
324         * Temporarily replace the forum subscriber list with any unincluded term subscribers.
325         */
326        public function add_term_subscribers( $users ) {
327                return array_diff( $this->subscribers, $users );
328        }
329
330        /**
331         * Replace the forum subscription message with term-specific messaging.
332         *
333         * @param string $message The message
334         * @param int $topic_id The topic id
335         * @param int $forum_id The forum id
336         * @param int $user_id 0
337         */
338        public function replace_forum_subscription_mail_message( $message, $topic_id, $forum_id, $user_id ) {
339                // Poster name.
340                $topic_author_name = bbp_get_topic_author_display_name( $topic_id );
341
342                remove_all_filters( 'bbp_get_topic_content' );
343
344                // Strip tags from text and set up message body.
345                $topic_content = strip_tags( bbp_get_topic_content( $topic_id ) );
346                $topic_url     = get_permalink( $topic_id );
347
348                $message = sprintf(
349                        /* translators: 1: Author, 2: Forum message, 3: Link to topic, 4: Descriptive text of why they're getting the email */
350                        __( '%1$s wrote:
351
352%2$s
353
354Link: %3$s
355
356-----------
357
358To reply, visit the above link and log in.
359Note that replying to this email has no effect.
360
361%4$s
362
363To unsubscribe from future emails, click here:
364####UNSUB_LINK####', 'wporg-forums' ),
365                        $topic_author_name,
366                        $topic_content,
367                        $topic_url,
368                        sprintf(
369                                $this->labels['receipt'],
370                                $this->get_current_term()->name
371                        )
372                );
373
374                return $message;
375        }
376
377        /**
378         * Replace the forum subscription subject/title with term-specific messaging.
379         *
380         * Eg, before it would be similar to `[Plugins] This is my thread title`.
381         * This changes it to `[Plugin Name] Thread title`.
382         *
383         * @param string $title The current title
384         * @param int $topic_id The topic id
385         */
386        public function replace_forum_subscription_mail_title( $title, $topic_id ) {
387                $terms = get_the_terms( $topic_id, $this->taxonomy );
388                if ( ! $terms ) {
389                        return $title;
390                }
391
392                if ( $this->directory && $this->directory->title() ) {
393                        // [Plugin Name] This is my thread title
394                        $title = sprintf(
395                                '[%s] %s',
396                                $this->directory->title(),
397                                strip_tags( bbp_get_topic_title( $topic_id ) )
398                        );
399                }
400
401                return $title;
402        }
403
404        /**
405         * Use the existing bbp_notify_topic_subscribers() to send out term subscriptions for replies.
406         * Avoid duplicate notifications for topic subscribers through the judicious use of filters within
407         * the function.
408         *
409         * @param int $reply_id The reply id
410         * @param int $topic_id The topic id
411         * @param int $forum_id The forum id
412         * @param mixed $anonymous_data
413         * @param int $reply_author
414         */
415        public function notify_term_subscribers_of_new_reply( $reply_id, $topic_id, $forum_id, $anonymous_data, $reply_author ) {
416                $terms = get_the_terms( $topic_id, $this->taxonomy );
417                if ( ! $terms ) {
418                        return;
419                }
420
421                // Users that will be notified another way, or have already been notified.
422                $notified_users = array();
423
424                // Remove the author from being notified of their own topic.
425                $notified_users[] = bbp_get_reply_author_id( $reply_id );
426
427                // Get users who were already notified and exclude them.
428                $topic_subscribers = bbp_get_topic_subscribers( $topic_id, true );
429                if ( ! empty( $topic_subscribers ) ) {
430                        $notified_users = array_merge( $notified_users, $topic_subscribers );
431                }
432
433                // Replace topic-specific messaging with term subscription messaging.
434                add_filter( 'bbp_subscription_mail_message', array( $this, 'replace_topic_subscription_mail_message' ), 10, 3 );
435
436                // Replace forum subscriber list with term subscribers, avoiding duplicates.
437                add_filter( 'bbp_topic_subscription_user_ids', array( $this, 'add_term_subscribers' ) );
438
439                // Personalize the emails.
440                add_filter( 'wporg_bbp_subscription_email', array( $this, 'personalize_subscription_email' ) );
441
442                foreach ( $terms as $term ) {
443                        $subscribers = $this->get_term_subscribers( $term->term_id );
444                        if ( ! $subscribers ) {
445                                continue;
446                        }
447
448                        $subscribers = array_diff( $subscribers, $notified_users );
449                        if ( ! $subscribers ) {
450                                continue;
451                        }
452
453                        $this->term        = $term;
454                        $this->subscribers = $subscribers;
455
456                        // Actually notify our term subscribers.
457                        bbp_notify_topic_subscribers( $reply_id, $topic_id, $forum_id );
458
459                        // Don't email them twice.
460                        $notified_users = array_merge( $notified_users, $subscribers );
461                }
462
463                // Reset
464                $this->term        = false;
465                $this->subscribers = array();
466
467                // Remove filters.
468                remove_filter( 'bbp_subscription_mail_message',   array( $this, 'replace_topic_subscription_mail_message' ) );
469                remove_filter( 'bbp_topic_subscription_user_ids', array( $this, 'add_term_subscribers' ) );
470                remove_filter( 'wporg_bbp_subscription_email',    array( $this, 'personalize_subscription_email' ) );
471        }
472
473        /**
474         * Replace the topic subscription message with term-specific messaging.
475         *
476         * @param string $message The message
477         * @param int $reply_id The reply id
478         * @param int $topic_id The topic id
479         */
480        public function replace_topic_subscription_mail_message( $message, $reply_id, $topic_id ) {
481                // Poster name.
482                $reply_author_name = bbp_get_reply_author_display_name( $reply_id );
483
484                remove_all_filters( 'bbp_get_reply_content' );
485
486                // Strip tags from text and set up message body.
487                $reply_content = strip_tags( bbp_get_reply_content( $reply_id ) );
488                $reply_url = bbp_get_reply_url( $reply_id );
489
490                $message = sprintf(
491                        /* translators: 1: Author, 2: Forum message, 3: Link to topic, 4: Descriptive text of why they're getting the email */
492                        __( '%1$s wrote:
493
494%2$s
495
496Link: %3$s
497
498-----------
499
500To reply, visit the above link and log in.
501Note that replying to this email has no effect.
502
503%4$s
504
505To unsubscribe from future emails, click here:
506####UNSUB_LINK####', 'wporg-forums' ),
507                        $reply_author_name,
508                        $reply_content,
509                        $reply_url,
510                        sprintf(
511                                $this->labels['receipt'],
512                                $this->get_current_term()->name
513                        )
514                );
515
516                return $message;
517        }
518
519        /**
520         * Replace the topic subscription subject/title with term-specific messaging.
521         *
522         * Eg, before it would be similar to `[Plugins] This is my thread title`.
523         * This changes it to `[Plugin Name] Thread title`.
524         *
525         * @param string $title    The current title
526         * @param int    $reply_id The reply id
527         * @param int    $topic_id The topic id
528         */
529        public function replace_topic_subscription_mail_title( $title, $reply_id, $topic_id ) {
530                $terms = get_the_terms( $topic_id, $this->taxonomy );
531                if ( ! $terms ) {
532                        return $title;
533                }
534
535                if ( $this->directory && $this->directory->title() ) {
536                        // [Plugin Name] This is my thread title
537                        $title = sprintf(
538                                '[%s] %s',
539                                $this->directory->title(),
540                                strip_tags( bbp_get_topic_title( $topic_id ) )
541                        );
542                }
543
544                return $title;
545        }
546
547        /**
548         * Personalize subscription emails by adding an unsubscription link.
549         *
550         * @param array $email_parts The email parts.
551         * @return The email parts.
552         */
553        public function personalize_subscription_email( $email_parts ) {
554                // get_tokenised_unsubscribe_url() will validate the user object.
555                $user       = get_user_by( 'email', $email_parts['to'] );
556                $unsub_link = $this->get_tokenised_unsubscribe_url( $user, $this->term );
557
558                if ( ! $unsub_link ) {
559                        return $email_parts;
560                }
561
562                $email_parts['message'] = str_replace(
563                        '####UNSUB_LINK####',
564                        '<' . esc_url_raw( $unsub_link ) . '>',
565                        $email_parts['message']
566                );
567
568                $email_parts['headers'][] = 'List-Unsubscribe: <' . esc_url_raw( $unsub_link ) . '>';
569                $email_parts['headers'][] = 'List-Unsubscribe-Post: List-Unsubscribe=One-Click';
570
571                return $email_parts;
572        }
573
574        /**
575         * Get the WP_Term instance for the currently displayed item.
576         */
577        protected function get_current_term() {
578                if ( $this->term ) {
579                        return $this->term;
580                }
581
582                // The currently queried tag.
583                if (
584                        bbp_is_topic_tag() &&
585                        ( $term = get_queried_object() ) &&
586                        ( $term instanceof \WP_Term )
587                ) {
588                        return $term;
589                }
590
591                // The current directory loaded.
592                if (
593                        $this->directory &&
594                        bbp_is_single_view() &&
595                        $this->directory->compat() == bbp_get_view_id()
596                ) {
597                        return $this->directory->term;
598                }
599
600                return false;
601        }
602
603        /**
604         * Add a term subscription block to the user's profile.
605         */
606        public function user_subscriptions() {
607                $user_id = bbp_get_user_id();
608                if ( empty( $user_id ) ) {
609                        return;
610                }
611
612                // Don't display the subscriptions unless it's the current user, or they can edit that user.
613                if ( ! bbp_is_user_home() && ! current_user_can( 'edit_user', bbp_get_displayed_user_id() ) ) {
614                        return;
615                }
616
617                $terms = self::get_user_taxonomy_subscriptions( $user_id, $this->taxonomy );
618                ?>
619
620                <div class="bbp-user-subscriptions">
621                        <h2 class="entry-title"><?php echo esc_html( $this->labels['subscribed_header'] ); ?></h2>
622                        <div class="bbp-user-section">
623                        <?php
624                        if ( $terms ) {
625                                echo '<p id="bbp-term-' . esc_attr( $this->taxonomy ) . '">' . "\n";
626                                foreach ( $terms as $term ) {
627                                        $unsub_url = self::get_subscription_url( $user_id, $term->term_id, $this->taxonomy );
628                                        echo '<a href="' . esc_url( get_term_link( $term->term_id ) ) . '">' . esc_html( $term->name ) . '</a>';
629                                        echo ' (<a href="' . esc_url( $unsub_url ) . '">' . esc_html( 'Unsubscribe', 'wporg-forums' ) . '</a>)';
630                                        echo "</br>\n";
631                                }
632                                echo "</p>\n";
633                        } else {
634                                if ( bbp_get_user_id() == get_current_user_id() ) {
635                                        echo '<p>' . esc_html( $this->labels['subscribed_user_notice'] ) . '</p>';
636                                } else {
637                                        echo '<p>' . esc_html( $this->labels['subscribed_anon_notice'] ) . '</p>';
638                                }
639                        }
640                        ?>
641                        </div>
642                </div>
643
644                <?php
645        }
646
647        /**
648         * Get the user's subscriptions for a given taxonomy; defaults to 'topic-tag'.
649         *
650         * @param $user_id int The user id
651         * @param $taxonomy string Optional. The taxonomy
652         * @return array|bool Results if user has subscriptions, otherwise false
653         */
654        public static function get_user_taxonomy_subscriptions( $user_id = 0, $taxonomy = 'topic-tag' ) {
655                if ( empty( $user_id ) || empty( $taxonomy ) ) {
656                        return false;
657                }
658
659                $subscriptions = get_terms( array(
660                        'taxonomy'   => $taxonomy,
661                        'meta_key'   => self::META_KEY,
662                        'meta_value' => $user_id,
663                ) );
664
665                // Default to false if empty.
666                $subscriptions = $subscriptions ?: false;
667
668                return apply_filters( 'wporg_bbp_get_user_taxonomy_subscriptions', $subscriptions, $user_id, $taxonomy );
669        }
670
671        /**
672         * Get the user IDs of users subscribed to a given term.
673         *
674         * @param $term_id int The term id
675         * @return array|bool Results if the term is valid, otherwise false
676         */
677        public static function get_term_subscribers( $term_id = 0 ) {
678                if ( empty( $term_id ) ) {
679                        return false;
680                }
681
682                $subscribers = wp_cache_get( 'wporg_bbp_get_term_subscribers_' . $term_id, 'bbpress_users' );
683                if ( false === $subscribers ) {
684                        $subscribers = get_term_meta( $term_id, self::META_KEY );
685                        wp_cache_set( 'wporg_bbp_get_term_subscribers_' . $term_id, $subscribers, 'bbpress_users' );
686                }
687
688                return apply_filters( 'wporg_bbp_get_term_subscribers', $subscribers, $term_id );
689        }
690
691        /**
692         * Is the user subscribed to a term?
693         *
694         * @param $user_id int The user id
695         * @param $term_id int The term id
696         * @return bool True if the user is subscribed, otherwise false
697         */
698        public static function is_user_subscribed_to_term( $user_id = 0, $term_id = 0 ) {
699                if ( empty( $user_id ) || empty( $term_id ) ) {
700                        return false;
701                }
702
703                $subscriptions = self::get_term_subscribers( $term_id );
704                if ( in_array( $user_id, $subscriptions ) ) {
705                        return true;
706                }
707
708                return false;
709        }
710
711        /**
712         * Add a term to a user's subscriptions.
713         *
714         * @param $user_id int The user id
715         * @param $term_id int The term id
716         * @return bool False if invalid, otherwise true
717         */
718        public static function add_user_subscription( $user_id, $term_id ) {
719                if ( empty( $user_id ) || empty( $term_id ) ) {
720                        return false;
721                }
722
723                if ( ! self::is_user_subscribed_to_term( $user_id, $term_id ) ) {
724                        add_term_meta( $term_id, self::META_KEY, $user_id );
725                        wp_cache_delete( 'wporg_bbp_get_term_subscribers_' . $term_id, 'bbpress_users' );
726                }
727
728                do_action( 'wporg_bbp_add_user_term_subscription', $user_id, $term_id );
729
730                return true;
731        }
732
733        /**
734         * Remove a term from a user's subscriptions.
735         *
736         * @param $user_id int The user id
737         * @param $term_id int The term id
738         * @return bool False if invalid, otherwise true
739         */
740        public static function remove_user_subscription( $user_id, $term_id ) {
741                if ( empty( $user_id ) || empty( $term_id ) ) {
742                        return false;
743                }
744
745                if ( self::is_user_subscribed_to_term( $user_id, $term_id ) ) {
746                        delete_term_meta( $term_id, self::META_KEY, $user_id );
747                        wp_cache_delete( 'wporg_bbp_get_term_subscribers_' . $term_id, 'bbpress_users' );
748                }
749
750                do_action( 'wporg_bbp_remove_user_term_subscription', $user_id, $term_id );
751
752                return true;
753        }
754
755        /**
756         * Create the url for subscribing to/unsubscribing from a given term.
757         *
758         * @param int $user_id The user id
759         * @param int $term_id The term id
760         * @param int $taxonomy The taxonomy
761         * @return string
762         */
763        public static function get_subscription_url( $user_id = 0, $term_id, $taxonomy ) {
764                if ( empty( $user_id ) ) {
765                        $user_id = bbp_get_user_id();
766                }
767
768                if ( empty( $user_id ) ) {
769                        return false;
770                }
771
772                $term = get_term( $term_id, $taxonomy );
773                if ( ! $term ) {
774                        return false;
775                }
776
777                if ( self::is_user_subscribed_to_term( $user_id, $term_id ) ) {
778                        $action = 'wporg_bbp_unsubscribe_term';
779                } else {
780                        $action = 'wporg_bbp_subscribe_term';
781                }
782
783                if ( bbp_is_subscriptions() ) {
784                        $permalink = bbp_get_subscriptions_permalink( $user_id );
785                } else {
786                        $permalink = get_term_link( $term_id );
787                }
788
789                $url = wp_nonce_url(
790                        add_query_arg( compact( 'action', 'term_id' ), $permalink ),
791                        'toggle-term-subscription_' . $user_id . '_' . $term_id . '_' . $taxonomy
792                );
793
794                if ( $user_id != get_current_user_id() ) {
795                        $url = add_query_arg( 'user_id', $user_id, $url );
796                }
797
798                return esc_url( $url );
799        }
800
801        /**
802         * Generates a unsubscription token for a user.
803         *
804         * The token form is 'user_id|expiry|hash', and dependant upon the user email, password, and term.
805         *
806         * @param WP_Term $term   The user the token should be for.
807         * @param WP_User $user   The user the token should be for.
808         * @param int     $expiry The expiry of the token. Optional, only required for verifying tokens.
809         * @return string|bool The hashed token, false on failure.
810         */
811        protected static function generate_unsubscribe_token( $term, $user, $expiry = 0 ) {
812                if ( ! $term || ! $user ) {
813                        return false;
814                }
815                if ( ! $expiry ) {
816                        $expiry = time() + self::UNSUBSCRIBE_LIFETIME;
817                }
818
819                $expiry    = intval( $expiry );
820                $pass_frag = substr( $user->user_pass, 8, 4 ); // Password fragment used by cookie auth.
821                $key       = wp_hash( $term->term_id . '|' . $term->taxonomy . '|' . $user->user_email . '|' . $pass_frag . '|' . $expiry, 'forum_subcriptions' );
822                $hash      = hash_hmac( 'sha256',  $term->term_id . '|' . $term->taxonomy . '|' . $user->user_email . '|' . $expiry, $key );
823
824                return $user->ID . '|' . $expiry . '|' . $hash;
825        }
826
827        /**
828         * Validate if the current request has a valid tokenised unsubscription link.
829         *
830         * @return bool|int User ID on success, false on failure.
831         */
832        protected function has_valid_unsubscription_token() {
833                if (
834                        ! isset( $_GET['token'] ) ||
835                        2 !== substr_count( $_GET['token'], '|' )
836                ) {
837                        return false;
838                }
839
840                $provided_token            = rtrim( $_GET['token'], '>' );
841                list( $user_id, $expiry, ) = explode( '|', $provided_token );
842                $term                      = $this->get_current_term();
843                $user                      = get_user_by( 'id', intval( $user_id ) );
844                $expected_token            = self::generate_unsubscribe_token( $term, $user, $expiry );
845
846                if (
847                        $expiry > time() &&
848                        hash_equals( $expected_token, $provided_token )
849                ) {
850                        return $user->ID;
851                }
852
853                return false;
854        }
855
856        /**
857         * Generate a tokenised unsubscription link for a given user & term.
858         *
859         * This link can be used without being logged in.
860         *
861         * @param \WP_User $user The user to generate the link for.
862         * @param \WP_Term $term The term to generate the link for.
863         *
864         * @return bool|string The URL, or false upon failure.
865         */
866        public static function get_tokenised_unsubscribe_url( $user, $term ) {
867                $token  = self::generate_unsubscribe_token( $term, $user );
868                if ( ! $token ) {
869                        return false;
870                }
871
872                return add_query_arg(
873                        array(
874                                'action'   => 'wporg_bbp_unsubscribe_term',
875                                'token'    => $token,
876                        ),
877                        // We don't include the term_id in the URL, and instead rely upon the term coming from the URL
878                        get_term_link( $term )
879                );
880        }
881
882        /**
883         * Generate an unsubscription link for use in a Template.
884         *
885         * @param array $args
886         * @return string
887         */
888        public static function get_subscription_link( $args ) {
889                $r = bbp_parse_args( $args, array(
890                        'user_id'     => get_current_user_id(),
891                        'term_id'     => 0,
892                        'taxonomy'    => 'topic-tag',
893                        'class'       => 'button',
894                        'subscribe'   => esc_html__( 'Subscribe to this topic tag', 'wporg-forums' ),
895                        'unsubscribe' => esc_html__( 'Unsubscribe from this topic tag', 'wporg-forums' ),
896                        'js_confirm'  => esc_html__( 'Are you sure you wish to subscribe by email to all future topics created in this tag?', 'wporg-forums' ),
897                ), 'get_term_subscription_link' );
898
899                $user_id  = $r['user_id'];
900                $term_id  = $r['term_id'];
901                $taxonomy = $r['taxonomy'];
902
903                if ( empty( $user_id ) || empty( $term_id ) || empty( $taxonomy ) ) {
904                        return false;
905                }
906
907                if ( ! user_can( $user_id, 'spectate' ) ) {
908                        return false;
909                }
910
911                $url = self::get_subscription_url( $user_id, $term_id, $taxonomy );
912                if ( self::is_user_subscribed_to_term( $user_id, $term_id ) ) {
913                        $text       = $r['unsubscribe'];
914                        $js_confirm = '';
915                } else {
916                        $text       = $r['subscribe'];
917                        $js_confirm = 'javascript:return confirm(' . json_encode( $r['js_confirm'] ) . ');';
918                }
919
920                return sprintf(
921                        "<div class='wporg-bbp-term-subscription'><a href='%s' class='%s' onclick='%s'>%s</a></div>",
922                        $url,
923                        esc_attr( $r['class'] ),
924                        esc_attr( $js_confirm ),
925                        esc_html( $text )
926                );
927        }
928}
Note: See TracBrowser for help on using the repository browser.