Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
32.14% covered (danger)
32.14%
18 / 56
44.44% covered (danger)
44.44%
4 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
Notification
32.14% covered (danger)
32.14%
18 / 56
44.44% covered (danger)
44.44%
4 / 9
237.22
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 dismiss
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
2
 isExpired
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 shouldShow
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
210
 __isset
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 __get
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 asArray
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 asJson
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 __toString
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace NewfoldLabs\WP\Module\Notifications;
4
5use NewfoldLabs\WP\Module\Data\HiiveConnection;
6use WP_Forge\Helpers\Arr;
7use wpscholar\Url;
8
9use function NewfoldLabs\WP\ModuleLoader\container;
10
11/**
12 * Class Notification
13 *
14 * @property string $id         Notification ID.
15 * @property array  $locations  Locations where the notification should display.
16 * @property int    $expiration Notification expiration.
17 * @property string $content    Notification content in HTML format.
18 */
19class Notification {
20
21    /**
22     * ID for the notification.
23     *
24     * @var string
25     */
26    protected $id;
27
28    /**
29     * Collection of location contexts where the notification should show.
30     *
31     * @var array
32     */
33    protected $locations;
34
35    /**
36     * Expiration timestamp indicating when the notification should no longer be shown.
37     *
38     * @var int
39     */
40    protected $expiration;
41
42    /**
43     * The HTML content to be rendered for this notification.
44     *
45     * @var string
46     */
47    protected $content;
48
49    /**
50     * Notification constructor.
51     *
52     * @param string|array $data Notification data in JSON or array format.
53     */
54    public function __construct( $data ) {
55        if ( is_string( $data ) ) {
56            $data = json_decode( $data, true );
57        }
58        if ( is_array( $data ) ) {
59            $this->id         = Arr::get( $data, 'id', '' );
60            $this->locations  = Arr::get( $data, 'locations', array() );
61            $this->expiration = Arr::get( $data, 'expiration', 0 );
62            $this->content    = Arr::get( $data, 'content', '' );
63        }
64    }
65
66    /**
67     * Dismiss a message.
68     */
69    public function dismiss() {
70        wp_remote_post(
71            NFD_HIIVE_URL . '/notifications/' . $this->id,
72            array(
73                'headers'  => array(
74                    'Content-Type'  => 'application/json',
75                    'Accept'        => 'application/json',
76                    'Authorization' => 'Bearer ' . HiiveConnection::get_auth_token(),
77                ),
78                'blocking' => false,
79            )
80        );
81    }
82
83    /**
84     * Check if notification is expired.
85     *
86     * @return bool
87     */
88    public function isExpired() {
89        return ! empty( $this->expiration ) && time() >= $this->expiration;
90    }
91
92    /**
93     * Check if a notification should be displayed in a particular context.
94     *
95     * @param string $context     Context.
96     * @param array  $contextData Context data.
97     *
98     * @return bool
99     */
100    public function shouldShow( $context, array $contextData ) {
101
102        // Never show expired notifications
103        if ( $this->isExpired() ) {
104            return false;
105        }
106
107        // Loop through each location to handle any that are applicable
108        foreach ( $this->locations as $location ) {
109
110            // If the location context doesn't match the current context, skip this location.
111            if ( Arr::get( $location, 'context' ) !== $context ) {
112                continue;
113            }
114
115            switch ( $context ) {
116
117                case 'wp-admin-notice': // phpcs:ignore PSR2.ControlStructures.SwitchDeclaration.TerminatingComment
118                    // Don't show standard WordPress admin notices on the plugin pages.
119                    if ( false !== strpos( Url::getCurrentUrl(), 'admin.php?page=' . container()->plugin()->id ) ) {
120                        return false;
121                    }
122
123                case container()->plugin()->id . '-plugin':
124                case container()->plugin()->id . '-app-nav':
125                    // The current page
126                    $current_page = Arr::get( $contextData, 'page' );
127
128                    // If plugin: Page is the value of the plugin page hash (e.g. /home).
129                    // If wp-admin-notice: Page is the URL path relative to the wp-admin/ directory.
130                    $pages = Arr::get( $location, 'pages', array() );
131
132                    // If pages is a string, it means we should show on all pages.
133                    if ( is_string( $pages ) ) {
134                        return true;
135                    }
136
137                    if ( is_array( $pages ) ) {
138                        // If current page is not set, assume we want to show on all pages (makes REST API work properly).
139                        if ( is_null( $current_page ) ) {
140                            return true;
141                        }
142
143                        foreach ( $pages as $path ) {
144                            if ( false !== strpos( $current_page, $path ) ) {
145                                return true;
146                            }
147                        }
148                    }
149
150                    break;
151
152                case 'wp-plugin-search':
153                    // TODO: Implement checks for keywords searched and plugin slugs in the results.
154                    break;
155            }
156        }
157
158        return false;
159    }
160
161    /**
162     * Magic method for checking if a property exists.
163     *
164     * @param string $name Property name.
165     *
166     * @return bool
167     */
168    public function __isset( $name ) {
169        return property_exists( $this, $name );
170    }
171
172    /**
173     * Magic method for fetching protected properties.
174     *
175     * @param string $name Property name.
176     *
177     * @return mixed
178     */
179    public function __get( $name ) {
180        $value = null;
181        if ( property_exists( $this, $name ) ) {
182            $value = $this->{$name};
183        }
184
185        return $value;
186    }
187
188    /**
189     * Get data in array format.
190     *
191     * @return array
192     */
193    public function asArray() {
194        return array(
195            'id'         => $this->id,
196            'locations'  => $this->locations,
197            'expiration' => $this->expiration,
198            'content'    => $this->content,
199        );
200    }
201
202    /**
203     * Get data in JSON format.
204     *
205     * @return string
206     */
207    public function asJson() {
208        return (string) wp_json_encode( $this->asArray(), JSON_PRETTY_PRINT );
209    }
210
211    /**
212     * Convert object to string.
213     *
214     * @return string
215     */
216    public function __toString() {
217        return $this->asJson();
218    }
219
220}