PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 3.1.0
Defender Security – Malware Scanner, Login Security & Firewall v3.1.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / src / model / notification.php

notification.php in Defender Security – Malware Scanner, Login Security & Firewall 3.1.0, at src/model/notification.php

409 lines 9.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare( strict_types=1 );
4
5 namespace WP_Defender\Model;
6
7 use Calotes\Model\Setting;
8 use WP_Defender\Traits\Formats;
9 use WP_Defender\Traits\User;
10 use WP_Defender\Model\Email_Track;
11
12 /**
13 *
14 * Class Notification
15 *
16 * @package WP_Defender\Model
17 */
18 abstract class Notification extends Setting {
19 use User, Formats;
20
21 public const STATUS_DISABLED = 'disabled', STATUS_ACTIVE = 'enabled';
22 public const USER_SUBSCRIBED = 'subscribed', USER_SUBSCRIBE_WAITING = 'waiting', USER_SUBSCRIBE_CANCELED = 'cancelled', USER_SUBSCRIBE_NA = 'na';
23
24 /**
25 * Notification title.
26 *
27 * @var string
28 * @defender_property
29 * @sanitize_text_field
30 */
31 public $title;
32
33 /**
34 * Unique ID for this notification.
35 *
36 * @var string
37 * @defender_property
38 * @sanitize_text_field
39 */
40 public $slug;
41
42 /**
43 * @var string
44 * @defender_property
45 */
46 public $description;
47
48 /**
49 * This is the status of the current notification, can be active or disabled.
50 *
51 * @var string
52 * @defender_property
53 * @sanitize_text_field
54 */
55 public $status;
56
57 /**
58 * This is notification or report.
59 *
60 * @var string
61 * @defender_property
62 * @sanitize_text_field
63 */
64 public $type;
65
66 /**
67 * Report sending frequency. Only when $type is report.
68 *
69 * @var string
70 * @defender_property
71 * @sanitize_text_field
72 */
73 public $frequency;
74
75 /**
76 * Report sending day. Only when $type is report.
77 *
78 * @var string
79 * @defender_property
80 * @sanitize_text_field
81 */
82 public $day;
83
84 /**
85 * This is for when user select report as monthly, we will have the day number, instead of text.
86 *
87 * @var int
88 * @sanitize_text_field
89 * @defender_property
90 */
91 public $day_n;
92
93 /**
94 * Same as $day.
95 *
96 * @var string
97 * @defender_property
98 * @sanitize_text_field
99 */
100 public $time;
101
102 /**
103 * Holding a list of site user ids, so when sending, we send though this list.
104 *
105 * @var array
106 * @defender_property
107 * @sanitize_text_field
108 */
109 public $in_house_recipients = [];
110
111 /**
112 * For additional users, this should contain a list of email and name.
113 *
114 * @var array
115 * @defender_property
116 * @sanitize_text_field
117 */
118 public $out_house_recipients = [];
119
120 /**
121 * This when we want to run the report/notification without any email sending.
122 *
123 * @var bool
124 * @defender_property
125 */
126 public $dry_run = false;
127
128 /**
129 * This is contains the meta settings of this notification.
130 *
131 * @var array
132 * @defender_property
133 */
134 public $configs = [];
135
136 /**
137 * Tracking.
138 *
139 * @var int
140 * @defender_property
141 */
142 public $last_sent = 0;
143
144 /**
145 * @var int
146 * @defender_property
147 */
148 public $est_timestamp;
149
150 /**
151 * Return the default user, we will use this if there is no user in the notification.
152 *
153 * @return array
154 */
155 protected function get_default_user(): array {
156 $user_id = get_current_user_id();
157
158 return [
159 'name' => $this->get_user_display( $user_id ),
160 'id' => $user_id,
161 'email' => $this->get_current_user_email( $user_id ),
162 'role' => $this->get_current_user_role( $user_id ),
163 'avatar' => get_avatar_url( $this->get_current_user_email( $user_id ) ),
164 'status' => self::USER_SUBSCRIBED,
165 ];
166 }
167
168 /**
169 * Check if the current moment is right for sending.
170 *
171 * @return bool|void
172 */
173 public function maybe_send() {
174 // @since 2.7.0 We can remove 'dry_run'-condition in the next version.
175 if ( true === $this->dry_run ) {
176 // No send, but need to track as sent, so we can requeue it.
177 if ( 'report' === $this->type ) {
178 $this->last_sent = $this->est_timestamp;
179 $this->est_timestamp = $this->get_next_run()->getTimestamp();
180 $this->save();
181 }
182
183 return;
184 }
185
186 if ( ! $this->check_active_status() ) {
187 return false;
188 }
189
190 if ( 'notification' === $this->type ) {
191 return true;
192 }
193
194 if ( 0 === $this->last_sent ) {
195 return false;
196 }
197
198 $now = new \DateTime( 'now', \wp_timezone() );
199 $time = apply_filters( 'defender_current_time_for_report', $now );
200 // Testing.
201 if ( defined( 'WP_DEFENDER_TESTING' ) && true === constant( 'WP_DEFENDER_TESTING' ) ) {
202 return true;
203 }
204
205 return $time->getTimestamp() >= $this->est_timestamp;
206 }
207
208 /**
209 * @return \DateTime|false
210 * @throws \Exception
211 */
212 public function get_next_run() {
213 if ( 'notification' === $this->type ) {
214 return false;
215 }
216 if ( ! $this->check_active_status() ) {
217 return false;
218 }
219
220 // Create estimate object.
221 $est = new \DateTime( 'now', \wp_timezone() );
222 if ( ! empty( $this->last_sent ) ) {
223 //set the timestamp of previous
224 $est->setTimestamp( $this->last_sent );
225 }
226
227 // Est should be set as the last send. Create now timestamp.
228 $now = new \DateTime( 'now', \wp_timezone() );
229 $interval = \DateInterval::createFromDateString( (string) $est->getOffset() . 'seconds' );
230 [$hour, $min] = explode( ':', $this->time );
231 $hour = (int) $hour;
232 $min = (int) $min;
233 switch ( $this->frequency ) {
234 case 'daily':
235 // Set the time.
236 $est->add( $interval );
237 $est->setTime( $hour, $min, 0 );
238 // Convert to current timezone.
239 while ( $est->getTimestamp() < $now->getTimestamp() ) {
240 $est->add( new \DateInterval( 'P1D' ) );
241 $est->setTime( $hour, $min, 0 );
242 }
243 break;
244 case 'weekly':
245 $est->modify( 'this ' . $this->day );
246 $est->add( $interval );
247 $est->setTime( $hour, $min, 0 );
248 while ( $est->getTimestamp() < $now->getTimestamp() ) {
249 $est->modify( 'next ' . $this->day );
250 $est->setTime( $hour, $min, 0 );
251 }
252 break;
253 case 'monthly':
254 // We will need to check if the date is passed today, if not, use this, if yes, then queue for next month.
255 $est->setDate( $est->format( 'Y' ), $est->format( 'm' ), 1 );
256 if ( 31 === (int) $this->day_n ) {
257 $this->day_n = $est->format( 't' );
258 }
259 $est->add( new \DateInterval( 'P' . ( $this->day_n - 1 ) . 'D' ) );
260 $est->setTime( $hour, $min, 0 );
261 while ( $est->getTimestamp() < $now->getTimestamp() ) {
262 // Already over, move to next month.
263 $est->modify( 'next month' );
264 $est->setTime( $hour, $min, 0 );
265 }
266 break;
267 }
268
269 return $est;
270 }
271
272 /**
273 * We have multiple issues where the email keep sending for no reason, this for debugging later.
274 *
275 * @param string $email
276 */
277 public function save_log( $email ): void {
278 $track = new Email_Track();
279 $track->timestamp = time();
280 $track->source = $this->slug;
281 $track->to = $email;
282 $track->save();
283 }
284
285 public function check_active_status(): bool {
286 // Exception after migrating Scheduled scanning to Scan settings.
287 if ( 'malware-report' === $this->slug && true === ( new \WP_Defender\Model\Setting\Scan() )->scheduled_scanning ) {
288 return true;
289 }
290 return $this->status === self::STATUS_ACTIVE;
291 }
292
293 /**
294 * This will return the interval at string.
295 *
296 * @return string
297 */
298 public function to_string(): string {
299 if ( ! $this->check_active_status() ) {
300 return '-';
301 }
302 $date = new \DateTime( 'now', \wp_timezone() );
303 $date->setTimestamp( $this->est_timestamp );
304 switch ( $this->frequency ) {
305 case 'daily':
306 return sprintf( __( '%1$s at %2$s', 'wpdef' ), ucfirst( $this->frequency ), $date->format( 'h:i A' ) );
307 case 'weekly':
308 return sprintf( __( '%1$s on %2$s at %3$s', 'wpdef' ), ucfirst( $this->frequency ), ucfirst( $this->day ), $date->format( 'h:i A' ) );
309 case 'monthly':
310 default:
311 return sprintf( __( '%1$s/%2$d, %3$s', 'wpdef' ), ucfirst( $this->frequency ), $this->day_n, $date->format( 'h:i A' ) );
312 }
313 }
314
315 /**
316 * @param false $for_hub
317 *
318 * @return false|string|void
319 * @throws \Exception
320 */
321 public function get_next_run_as_string( $for_hub = false ) {
322 if ( 'notification' === $this->type ) {
323
324 return $for_hub ? false : __( 'Never', 'wpdef' );
325 }
326
327 if ( $for_hub ) {
328 return $this->check_active_status()
329 ? $this->persistent_hub_datetime_format( $this->est_timestamp )
330 : false;
331 } else {
332 if ( $this->check_active_status() ) {
333 $format = get_option( 'date_format' ) . ' ' . get_option( 'time_format' );
334 $date = new \DateTime( 'now', \wp_timezone() );
335 $date->setTimestamp( $this->est_timestamp );
336
337 return $date->format( $format );
338 } else {
339 return __( 'Never', 'wpdef' );
340 }
341 }
342 }
343
344 /**
345 * We still need to validate the out house recipients email.
346 */
347 public function after_validate() {
348 foreach ( $this->out_house_recipients as $recipient ) {
349 $recipient['email'] = trim( $recipient['email'] );
350 if ( empty( $recipient['email'] ) ) {
351 continue;
352 }
353 if ( ! filter_var( $recipient['email'], FILTER_VALIDATE_EMAIL ) ) {
354 $this->errors[] = sprintf( __( 'Email %s is invalid format', 'wpdef' ), $recipient['email'] );
355 }
356 }
357 }
358
359 public function save() {
360 if ( empty( $this->last_sent ) ) {
361 $this->last_sent = time();
362 }
363 $next_run = $this->get_next_run();
364 if ( is_object( $next_run ) ) {
365 $this->est_timestamp = $next_run->getTimestamp();
366 }
367 parent::save();
368 }
369
370 /**
371 * Inject next run to parent function.
372 *
373 * @return array
374 */
375 public function export(): array {
376
377 $data = parent::export();
378
379 global $l10n;
380
381 if ( isset( $l10n['wpdef'] ) ) {
382 $data['title'] = __( $data['title'], 'wpdef' );
383 $data['description'] = __( $data['description'], 'wpdef' );
384 }
385
386 $data['next_run'] = $this->get_next_run_as_string();
387 $data['all_subscribers'] = array_merge( $this->in_house_recipients, $this->out_house_recipients );
388
389 return $data;
390 }
391
392 /**
393 * Overrided method to manipulate user details dynamically.
394 */
395 protected function after_load(): string {
396 foreach ( $this->in_house_recipients as $key => $recipient ) {
397 $id = $recipient['id'];
398 $user_data = get_userdata( $id );
399
400 $this->in_house_recipients[ $key ]['name'] = $user_data->display_name;
401 $this->in_house_recipients[ $key ]['email'] = $user_data->user_email;
402 $this->in_house_recipients[ $key ]['role'] = ucfirst( array_shift( $user_data->roles ) );
403 $this->in_house_recipients[ $key ]['avatar'] = get_avatar_url( $id );
404 }
405
406 return '';
407 }
408 }
409