PluginProbe
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses / 4.4.7
LearnPress – WordPress LMS Plugin for Create and Sell Online Courses v4.4.7
4.4.7 4.4.6 4.4.5 4.4.4 4.4.3 4.4.2 4.4.1 4.4.0 4.3.9.1 4.3.9 4.3.8 4.3.7 4.1.6.9 4.1.6.9.1 4.1.6.9.2 4.1.6.9.3 4.1.6.9.4 4.1.7 4.1.7.1 4.1.7.2 4.1.7.3 4.1.7.3.1 4.1.7.3.2 4.2.0 4.2.1 All 138 releases
learnpress / inc / Models / Webhook / WebhookModel.php

WebhookModel.php in LearnPress – WordPress LMS Plugin for Create and Sell Online Courses 4.4.7, at inc/Models/Webhook/WebhookModel.php

324 lines 7.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace LearnPress\Models\Webhook;
4
5 use Exception;
6 use LearnPress\Databases\WebhookDB;
7 use LearnPress\Filters\WebhookFilter;
8 use LearnPress\Webhook\WebhookEvents;
9
10 defined( 'ABSPATH' ) || exit;
11
12 /**
13 * LearnPress webhook model.
14 */
15 class WebhookModel {
16 const STATUS_ACTIVE = 'active';
17 const STATUS_PAUSED = 'paused';
18
19 /**
20 * @var int
21 */
22 private $webhook_id = 0;
23
24 /**
25 * @var int
26 */
27 public $user_id = 0;
28
29 /**
30 * @var string
31 */
32 public $status = self::STATUS_ACTIVE;
33
34 /**
35 * @var string
36 */
37 public $name = '';
38
39 /**
40 * @var string
41 */
42 public $delivery_url = '';
43
44 /**
45 * @var string
46 */
47 public $secret = '';
48
49 /**
50 * @var string[]
51 */
52 public $events = array();
53
54 /**
55 * @var string
56 */
57 public $created_at = '';
58
59 /**
60 * @var string|null
61 */
62 public $updated_at;
63
64 /**
65 * Initialize model from a DB row or payload.
66 *
67 * @param array|object|null $data Webhook data.
68 */
69 public function __construct( $data = null ) {
70 if ( $data ) {
71 $this->map_to_object( $data );
72 }
73 }
74
75 /**
76 * Map data onto the model.
77 *
78 * @param array|object $data Webhook data.
79 *
80 * @return self
81 */
82 public function map_to_object( $data ): self {
83 foreach ( $data as $key => $value ) {
84 if ( ! property_exists( $this, $key ) ) {
85 continue;
86 }
87
88 if ( 'events' === $key ) {
89 $value = $this->decode_events( $value );
90 } elseif ( in_array( $key, array( 'webhook_id', 'user_id' ), true ) ) {
91 $value = absint( $value );
92 }
93
94 $this->{$key} = $value;
95 }
96
97 return $this;
98 }
99
100 /**
101 * Get webhook ID.
102 *
103 * @return int
104 */
105 public function get_webhook_id(): int {
106 return $this->webhook_id;
107 }
108
109 /**
110 * Find a webhook by ID.
111 *
112 * @param int $webhook_id Webhook ID.
113 *
114 * @return static|false
115 * @throws Exception
116 */
117 public static function find( int $webhook_id ) {
118 $row = WebhookDB::getInstance()->get_webhook( $webhook_id );
119
120 return $row ? new static( $row ) : false;
121 }
122
123 /**
124 * Query webhook models.
125 *
126 * @param WebhookFilter $filter Webhook query filter.
127 * @param int $total_rows Total matching rows.
128 *
129 * @return static[]
130 * @throws Exception
131 */
132 public static function query( WebhookFilter $filter, int &$total_rows = 0 ): array {
133 $rows = WebhookDB::getInstance()->get_webhooks( $filter, $total_rows );
134
135 return is_array( $rows ) ? array_map( static fn( $row ) => new static( $row ), $rows ) : array();
136 }
137
138 /**
139 * Query active webhooks for an event key.
140 *
141 * @param string $event_key Event key.
142 *
143 * @return static[]
144 * @throws Exception
145 */
146 public static function get_active_webhooks_for_event( string $event_key ): array {
147 $event_keys = WebhookEvents::sanitize( array( $event_key ) );
148 if ( empty( $event_keys ) ) {
149 return array();
150 }
151
152 $filter = new WebhookFilter();
153 $filter->status = self::STATUS_ACTIVE;
154 $filter->event = reset( $event_keys );
155 $filter->limit = -1;
156 $filter->run_query_count = false;
157 $filter->order_by = WebhookFilter::COL_WEBHOOK_ID;
158 $filter->order = WebhookFilter::ORDER_ASC;
159
160 return static::query( $filter );
161 }
162
163 /**
164 * Save the webhook.
165 *
166 * @return self
167 * @throws Exception
168 */
169 public function save(): self {
170 $this->validate();
171
172 $db = WebhookDB::getInstance();
173 $now = current_time( 'mysql', true );
174
175 if ( empty( $this->created_at ) ) {
176 $this->created_at = $now;
177 }
178
179 if ( $this->webhook_id > 0 ) {
180 $this->updated_at = $now;
181 }
182
183 $data = array(
184 'webhook_id' => $this->webhook_id,
185 'user_id' => $this->user_id,
186 'status' => $this->status,
187 'name' => $this->name,
188 'delivery_url' => $this->delivery_url,
189 'secret' => $this->secret,
190 'events' => wp_json_encode( $this->events ),
191 'created_at' => $this->created_at,
192 'updated_at' => $this->updated_at,
193 );
194
195 if ( $this->webhook_id > 0 ) {
196 $db->update_webhook( $data );
197 } else {
198 $this->webhook_id = $db->insert_webhook( $data );
199 if ( $this->webhook_id <= 0 ) {
200 throw new Exception( __( 'Could not create webhook.', 'learnpress' ) );
201 }
202 }
203
204 return $this;
205 }
206
207 /**
208 * Delete the webhook.
209 *
210 * @return bool
211 * @throws Exception
212 */
213 public function delete(): bool {
214 if ( $this->webhook_id <= 0 ) {
215 return false;
216 }
217
218 return WebhookDB::getInstance()->delete_webhooks( array( $this->webhook_id ) ) > 0;
219 }
220
221 /**
222 * Generate and save a new secret.
223 *
224 * @return string
225 * @throws Exception
226 */
227 public function regenerate_secret(): string {
228 $this->secret = self::generate_secret();
229 $this->save();
230
231 return $this->secret;
232 }
233
234 /**
235 * Generate a webhook signing secret.
236 *
237 * @return string
238 */
239 public static function generate_secret(): string {
240 return wp_generate_password( 50, true, true );
241 }
242
243 /**
244 * Return admin-safe model data.
245 *
246 * @param bool $include_secret Include raw secret in response.
247 *
248 * @return array<string, mixed>
249 */
250 public function to_array( bool $include_secret = false ): array {
251 $data = array(
252 'webhook_id' => $this->webhook_id,
253 'user_id' => $this->user_id,
254 'status' => $this->status,
255 'name' => $this->name,
256 'delivery_url' => $this->delivery_url,
257 'events' => $this->events,
258 'created_at' => $this->created_at,
259 'updated_at' => $this->updated_at,
260 );
261
262 if ( $include_secret ) {
263 $data['secret'] = $this->secret;
264 }
265
266 return $data;
267 }
268
269 /**
270 * Validate and normalize model values.
271 *
272 * @return void
273 * @throws Exception
274 */
275 protected function validate(): void {
276 $this->user_id = absint( $this->user_id );
277 $this->name = sanitize_text_field( $this->name );
278 $this->status = sanitize_key( $this->status );
279 $this->events = WebhookEvents::sanitize( $this->events );
280
281 if ( '' === $this->name ) {
282 throw new Exception( __( 'Webhook name is required.', 'learnpress' ) );
283 }
284
285 $name_length = function_exists( 'mb_strlen' ) ? mb_strlen( $this->name ) : strlen( $this->name );
286 if ( $name_length > 200 ) {
287 throw new Exception( __( 'Webhook name must not exceed 200 characters.', 'learnpress' ) );
288 }
289
290 $this->delivery_url = esc_url_raw( $this->delivery_url );
291 $scheme = wp_parse_url( $this->delivery_url, PHP_URL_SCHEME );
292 if ( ! in_array( $scheme, array( 'http', 'https' ), true ) || ! wp_http_validate_url( $this->delivery_url ) ) {
293 throw new Exception( __( 'Webhook callback URL is invalid.', 'learnpress' ) );
294 }
295
296 if ( ! in_array( $this->status, array( self::STATUS_ACTIVE, self::STATUS_PAUSED ), true ) ) {
297 throw new Exception( __( 'Webhook status is invalid.', 'learnpress' ) );
298 }
299
300 if ( empty( $this->events ) ) {
301 throw new Exception( __( 'Select at least one webhook event.', 'learnpress' ) );
302 }
303
304 if ( '' === trim( $this->secret ) || preg_match( '/[\r\n]/', $this->secret ) || strlen( $this->secret ) > 255 ) {
305 throw new Exception( __( 'Webhook secret is invalid.', 'learnpress' ) );
306 }
307 }
308
309 /**
310 * Decode events stored as JSON.
311 *
312 * @param mixed $events Raw events.
313 *
314 * @return string[]
315 */
316 protected function decode_events( $events ): array {
317 if ( is_string( $events ) ) {
318 $events = json_decode( $events, true );
319 }
320
321 return is_array( $events ) ? WebhookEvents::sanitize( $events ) : array();
322 }
323 }
324