Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
19.61% covered (danger)
19.61%
20 / 102
9.09% covered (danger)
9.09%
1 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
Gifting_Banner
20.41% covered (danger)
20.41%
20 / 98
9.09% covered (danger)
9.09%
1 / 11
515.54
0.00% covered (danger)
0.00%
0 / 1
 init
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
 register_gifting_banner
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 should_display_expiring_plan_notice
93.75% covered (success)
93.75%
15 / 16
0.00% covered (danger)
0.00%
0 / 1
10.02
 get_checkout_link
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_days_to_dismiss_banner
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 get_more_info_link
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 inject_gifting_banner_wpcom
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 inject_gifting_banner_wpcomsh
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
2
 get_plan_purchase
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 get_title_texts
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
 get_subtitle_texts
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
20
1<?php // phpcs:ignore WordPress.File.FileName.InvalidClassFileName
2/**
3 * Show the gifting banner on Simple & Atomic sites.
4 * This file is duplicated in WPCOM and WPCOMSH.
5 * WPCOM: public_html/wp-content/blog-plugins/gifting-banner.php
6 * WPCOMSH: wpcomsh/frontend-notices/gifting-banner/gifting-banner.php
7 * See: p9Jlb4-5v7-p2
8 *
9 * @package gifting-banner
10 */
11
12/**
13 * Class Gifting_Banner
14 */
15class Gifting_Banner {
16
17    /**
18     * The current purchased plan of the blog.
19     * Used to pass data between methods.
20     *
21     * @var object|null
22     */
23    public $current_plan;
24
25    /**
26     * Maybe show the gifting banner for the current site.
27     */
28    public function init() {
29        // Inject the gifting banner after the launch banner.
30        if ( defined( 'IS_ATOMIC' ) && IS_ATOMIC ) {
31            if ( ! $this->should_display_expiring_plan_notice() ) {
32                return;
33            }
34
35            add_action( 'wp_head', array( $this, 'inject_gifting_banner_wpcomsh' ), 1103 );
36        } else {
37            add_action( 'wp_head', array( $this, 'inject_gifting_banner_wpcom' ), 1103 );
38        }
39    }
40
41    /**
42     * Resolves if the gifting banner should be displayed and adds it to the banner list in necessary.
43     *
44     * @param array $banners Banners.
45     *
46     * @return array
47     */
48    public function register_gifting_banner( $banners ) {
49        // If the banner shouldn't display, don't inject it.
50        if ( ! $this->should_display_expiring_plan_notice() ) {
51            return $banners;
52        }
53
54        return array_merge( $banners, array( 'wpcom_gifting_banner' => array( $this, 'init' ) ) );
55    }
56
57    /**
58     * Determines if the plan expiring notice should display.
59     *
60     * @return bool
61     */
62    public function should_display_expiring_plan_notice() {
63        if ( function_exists( 'is_automattic' ) && is_automattic() ) {
64            return false;
65        }
66
67        $this->current_plan = static::get_plan_purchase();
68
69        // If site doesn't have valid plan -> don't show the banner.
70        if ( ! $this->current_plan ) {
71            return false;
72        }
73
74        // If wpcom_gifting_subscription option exists, we show/hide the banner based on it.
75        if ( 'option-not-exists' !== get_option( 'wpcom_gifting_subscription', 'option-not-exists' ) ) {
76            return (bool) get_option( 'wpcom_gifting_subscription' );
77        }
78
79        // Create parity between WPCOM and WPCOMSH for auto_renew.
80        if ( defined( 'IS_ATOMIC' ) && IS_ATOMIC ) {
81            $this->current_plan->user_allows_auto_renew = $this->current_plan->auto_renew;
82        }
83
84        // Test if gifting is enabled - We default to the inverse of auto-renew but configured options take precedence.
85        if ( ! get_option( 'wpcom_gifting_subscription', ! $this->current_plan->user_allows_auto_renew ) ) {
86            return false;
87        }
88
89        /*
90         * We will display the banner:
91         * - 54 days before the annual plan expiration.
92         * - 5 days before the monthly plan expiration.
93         */
94        $days_of_warning          = false !== strpos( $this->current_plan->product_slug, 'monthly' ) ? 5 : 54;
95        $seconds_until_expiration = strtotime( $this->current_plan->expiry_date ) - time();
96
97        if ( $seconds_until_expiration < $days_of_warning * DAY_IN_SECONDS ) {
98            // Show the banner.
99            return true;
100        }
101
102        return false;
103    }
104
105    /**
106     * Get checkout link.
107     */
108    public function get_checkout_link() {
109        return 'https://wordpress.com/checkout/' . $this->current_plan->product_slug . '/gift/' . $this->current_plan->subscription_id . '?cancel_to=/home';
110    }
111
112    /**
113     * Get how many days the banner should be dismissed based on plan type (30 days for monthly, 1 year for yearly)
114     */
115    public function get_days_to_dismiss_banner() {
116        return false !== strpos( $this->current_plan->product_slug, 'monthly' ) ? 30 : 365;
117    }
118
119    /**
120     * Get more info link.
121     */
122    public function get_more_info_link() {
123        return 'https://wordpress.com/support/gift-a-wordpress-com-subscription/';
124    }
125
126    /**
127     * Inject the gifting banner on WPCOM.
128     */
129    public function inject_gifting_banner_wpcom() {
130        $days_to_expire             = ceil( ( strtotime( $this->current_plan->expiry_date ) - time() ) / DAY_IN_SECONDS );
131        $data                       = array();
132        $data['dismiss_days_count'] = $this->get_days_to_dismiss_banner();
133        $data['checkout_link']      = $this->get_checkout_link();
134        $data['more_info_link']     = localized_wpcom_url( $this->get_more_info_link() );
135        $data['i18n']               = array(
136            'title'       => $this->get_title_texts( $days_to_expire ),
137            'subtitle'    => $this->get_subtitle_texts( $this->current_plan, $days_to_expire ),
138            'button_text' => _x( 'Gift', 'verb', 'wpcomsh' ),
139        );
140        // Change the version if associated files are updated, current: 20230103.
141        wp_enqueue_style( 'gifting-banner', plugins_url( 'gifting-banner/css/gifting-banner.css', __FILE__ ), array(), '20230103' );
142        wp_enqueue_script( 'gifting-banner', plugins_url( 'gifting-banner/js/gifting-banner.js', __FILE__ ), array(), '20230103', true );
143        wp_localize_script( 'gifting-banner', 'gifting_banner', $data );
144        wp_set_script_translations( 'gifting-banner', 'wpcomsh' );
145    }
146
147    /**
148     * Inject the gifting banner on WPCOMSH.
149     */
150    public function inject_gifting_banner_wpcomsh() {
151        $days_to_expire             = ceil( ( strtotime( $this->current_plan->expiry_date ) - time() ) / DAY_IN_SECONDS );
152        $data                       = array();
153        $data['dismiss_days_count'] = $this->get_days_to_dismiss_banner();
154        $data['checkout_link']      = $this->get_checkout_link();
155        $data['more_info_link']     = $this->get_more_info_link();
156        $data['i18n']               = array(
157            'title'       => static::get_title_texts( $days_to_expire ),
158            'subtitle'    => static::get_subtitle_texts( $this->current_plan, $days_to_expire ),
159            'button_text' => _x(
160                'Gift',
161                'verb',
162                'wpcomsh'
163            ),
164        );
165
166        wp_enqueue_style( 'gifting-banner', plugins_url( 'css/gifting-banner.css', __FILE__ ), array(), WPCOMSH_VERSION );
167        wp_enqueue_script( 'gifting-banner', plugins_url( 'js/gifting-banner.js', __FILE__ ), array(), WPCOMSH_VERSION, true );
168        wp_localize_script( 'gifting-banner', 'gifting_banner', $data );
169        wp_set_script_translations( 'gifting-banner', 'wpcomsh' );
170    }
171
172    /**
173     * Get hosting plan purchase from the current site.
174     * See: https://github.com/Automattic/wpcomsh/pull/1118
175     *
176     * @return object|null
177     */
178    private static function get_plan_purchase() {
179        $purchases = wpcom_get_site_purchases();
180
181        foreach ( $purchases as $purchase ) {
182            if ( wpcom_purchase_has_feature( $purchase, WPCOM_Features::SUBSCRIPTION_GIFTING ) ) {
183                return $purchase;
184            }
185        }
186
187        return null;
188    }
189
190    /**
191     * Get title based on days.
192     *
193     * @param int $days_to_expire Days to expire.
194     * @return string
195     */
196    private function get_title_texts( $days_to_expire ) {
197
198        if ( $days_to_expire < 1 ) {
199            return __(
200                'This site\'s plan has expired.',
201                'wpcomsh'
202            );
203        }
204
205        return __(
206            'Enjoy this site?',
207            'wpcomsh'
208        );
209    }
210
211    /**
212     * Get subtitle based on days & type of plan.
213     * - Plan expired
214     * - Annual Plan < 2 weeks before expiration
215     * - Monthly Plan or Annual plan > 2 weeks before expiration
216     *
217     * @param object $current_plan Current Plan.
218     * @param int    $days_to_expire Days to expire.
219     * @return string
220     */
221    private function get_subtitle_texts( $current_plan, $days_to_expire ) {
222
223        if ( $days_to_expire < 1 ) {
224            return sprintf(
225            /* translators: Banner to show the visitor the site gifting option on expired sites. */
226                __(
227                    'Gift the author a WordPress.com upgrade.',
228                    'wpcomsh'
229                ),
230                $days_to_expire
231            );
232        }
233
234        if ( ! strpos( $current_plan->product_slug, 'monthly' ) && $days_to_expire < 15 ) {
235            return sprintf(
236            /* translators: Banner to show the visitor the site gifting option on days before expires. */
237                _n(
238                    'Gift the author a WordPress.com plan before it expires in %d day.',
239                    'Gift the author a WordPress.com plan before it expires in %d days.',
240                    $days_to_expire,
241                    'wpcomsh'
242                ),
243                $days_to_expire
244            );
245        }
246
247        return sprintf(
248        /* translators: Banner to show the visitor the site gifting option, no days shown. */
249            __(
250                'Gift the author a WordPress.com plan.',
251                'wpcomsh'
252            ),
253            $days_to_expire
254        );
255    }
256}
257
258/**
259 * Load the Gifting Banner.
260 */
261$gifting_banner = new Gifting_Banner();
262
263// On Atomic sites we don't have a banner resolver, so we apply the filter directly.
264if ( defined( 'IS_ATOMIC' ) && IS_ATOMIC ) {
265    add_action( 'init', array( $gifting_banner, 'init' ) );
266} else {
267    add_filter( 'wpcom_register_banners', array( $gifting_banner, 'register_gifting_banner' ) );
268}