PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 3.8.9
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v3.8.9
4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.5.2 All 199 releases
betterdocs / includes / Admin / ReportEmail.php

ReportEmail.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 3.8.9, at includes/Admin/ReportEmail.php

489 lines 15.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPDeveloper\BetterDocs\Admin;
4
5 use WP_Error;
6 use WP_Query;
7 use stdClass;
8 use WPDeveloper\BetterDocs\Utils\Base;
9 use WPDeveloper\BetterDocs\Utils\Views;
10 use WPDeveloper\BetterDocs\Core\Settings;
11
12 /**
13 * This class is responsible for sending weekly email with reports
14 *
15 * @since 1.4.4
16 */
17 class ReportEmail extends Base {
18 /**
19 * Get a single Instance of Analytics
20 * @var Settings
21 */
22 public $settings;
23
24 private static $current_timestamp = null;
25 private static $current_date = null;
26
27 /**
28 * Initially Invoked by Default.
29 */
30 public function __construct( Settings $settings ) {
31 $this->settings = $settings;
32
33 add_action( 'betterdocs::settings::saved', [ $this, 'after_save_settings' ] );
34
35 if ( $this->settings->get( 'enable_reporting', false ) ) {
36 $this->init();
37 }
38 }
39
40 public function init() {
41 add_filter( 'cron_schedules', [ $this, 'schedules_cron' ] );
42 add_action( 'admin_init', [ $this, 'activate' ] );
43 add_action( 'betterdocs_weekly_email_reporting', [ $this, 'send_email' ] );
44 }
45
46 public function after_save_settings() {
47 if ( $this->settings->get( 'enable_reporting', false ) ) {
48 $this->activate();
49 } else {
50 $this->deactivate( true );
51 }
52 }
53
54 public function reporting( $request ) {
55 if ( ! boolval( $request->get_param( 'disable_reporting' ) ) ) {
56 if ( $request->has_param( 'reporting_email' ) ) {
57 $email = $request->get_param( 'reporting_email' );
58 }
59 $email = $this->receiver_email_address( $email );
60
61 if ( ! empty( $email ) ) {
62 $is_send = $this->send_email( $request->get_param( 'reporting_frequency' ), true, $email );
63 if ( $is_send && ! is_wp_error( $is_send ) ) {
64 return [ 'message' => __( 'Successfully Sent an Email', 'betterdocs' ) ];
65 } elseif ( is_wp_error( $is_send ) ) {
66 return $is_send;
67 } else {
68 return $this->error( 'betterdocs_unknown_reason', __( 'Email cannot be sent for some reason.', 'betterdocs' ) );
69 }
70 }
71 return $this->error( 'betterdocs_unknown_reason', __( 'Invalid email address.', 'betterdocs' ) );
72 } else {
73 return $this->error( 'betterdocs_disabled_reporting', __( 'You have to enable Reporting first.', 'betterdocs' ) );
74 }
75 }
76
77 public function error( $code, $message ) {
78 return new WP_Error( $code, $message );
79 }
80
81 private static function timestamps( $date = false ) {
82 if ( is_null( self::$current_timestamp ) ) {
83 self::$current_timestamp = current_time( 'timestamp' );
84 }
85 if ( $date ) {
86 if ( is_null( self::$current_date ) ) {
87 self::$current_date = current_time( 'Y-m-d' );
88 }
89 return self::$current_date;
90 }
91 return self::$current_timestamp;
92 }
93
94 public function create_date( $count = '-7days' ) {
95 return date( 'Y-m-d', strtotime( $count, self::timestamps() ) );
96 }
97
98 public function get_views( $start_date, $end_date = null ) {
99 global $wpdb;
100 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
101 $query = $wpdb->get_results(
102 "
103 SELECT sum(impressions) as views, sum(unique_visit) as unique_visit, sum(happy + sad + normal) as reactions
104 FROM {$wpdb->prefix}betterdocs_analytics
105 WHERE (created_at BETWEEN '" . $start_date . "' AND '" . $end_date . "')
106 "
107 );
108 return $query;
109 }
110
111 public function get_search( $start_date, $end_date = null ) {
112 global $wpdb;
113 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
114 $query = $wpdb->get_results(
115 "
116 SELECT SUM(count + not_found_count) as search_count, SUM(count) as search_found, SUM(not_found_count) as search_not_found_count
117 FROM {$wpdb->prefix}betterdocs_search_log as search_log
118 WHERE (search_log.created_at BETWEEN '" . $start_date . "' AND '" . $end_date . "')
119 "
120 );
121
122 return $query;
123 }
124
125 public function count_new_docs( $start_date, $end_date = null ) {
126 $args = [
127 'post_type' => 'docs',
128 'post_status' => 'publish',
129 'posts_per_page' => -1,
130 'date_query' => [
131 [
132 'after' => $start_date,
133 'before' => $end_date,
134 'inclusive' => true
135 ]
136 ]
137 ];
138 $query = new WP_Query( $args );
139
140 return $query->found_posts;
141 }
142
143 public function get_search_keywords( $start_date, $end_date = null ) {
144 global $wpdb;
145 $select = 'SELECT search_keyword.keyword, search_log.keyword_id, SUM(search_log.count + search_log.not_found_count) as total_search, SUM(search_log.count) as count, SUM(search_log.not_found_count) as not_found';
146 $join = "FROM {$wpdb->prefix}betterdocs_search_keyword as search_keyword
147 JOIN {$wpdb->prefix}betterdocs_search_log as search_log on search_keyword.id = search_log.keyword_id";
148 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
149 $query = $wpdb->get_results(
150 "
151 {$select}
152 {$join}
153 WHERE (search_log.created_at BETWEEN '" . $start_date . "' AND '" . $end_date . "')
154 GROUP BY search_log.keyword_id
155 ORDER BY count DESC LIMIT 5
156 "
157 );
158
159 return $query;
160 }
161
162 public function get_docs_items( $request ) {
163 $prepared_post = new stdClass();
164
165 if ( isset( $request->ID ) ) {
166 $prepared_post->ID = $request->ID;
167 }
168
169 if ( isset( $request->post_title ) ) {
170 $prepared_post->title = betterdocs()->template_helper->kses( $request->post_title );
171 }
172
173 if ( isset( $request->total_views ) ) {
174 $prepared_post->total_views = $request->total_views;
175 }
176
177 if ( isset( $request->total_unique_visit ) ) {
178 $prepared_post->total_unique_visit = $request->total_unique_visit;
179 }
180
181 if ( isset( $request->total_reactions ) ) {
182 $prepared_post->total_reactions = $request->total_reactions;
183 }
184
185 if ( isset( $request->ID ) ) {
186 $prepared_post->link = get_permalink( $request->ID );
187 }
188
189 return $prepared_post;
190 }
191
192 public function get_leading_docs( $start_date, $end_date = null ) {
193 global $wpdb;
194
195 $select = 'SELECT docs.ID, docs.post_author, docs.post_title, SUM(analytics.impressions) as total_views, SUM(analytics.unique_visit) as total_unique_visit, SUM(analytics.happy + analytics.sad + analytics.normal) as total_reactions';
196 $join = "FROM {$wpdb->prefix}posts as docs
197 JOIN {$wpdb->prefix}betterdocs_analytics as analytics on docs.ID = analytics.post_id";
198 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
199 $query = $wpdb->get_results(
200 "
201 {$select}
202 {$join}
203 WHERE post_type = 'docs' AND post_status = 'publish' AND (analytics.created_at BETWEEN '" . $start_date . "' AND '" . $end_date . "')
204 GROUP BY analytics.post_id
205 ORDER BY total_views DESC LIMIT 5
206 "
207 );
208
209 $docs = [];
210 foreach ( $query as $key => $value ) {
211 $docs[ $key ] = $this->get_docs_items( $value );
212 }
213
214 return $docs;
215 }
216
217 /**
218 * Calculate Total BetterDocs Views
219 *
220 * @return array
221 */
222 public function get_data( $frequency = 'betterdocs_weekly' ) {
223 if ( $frequency == 'betterdocs_daily' ) {
224 $start_date = $this->create_date( 'last day' );
225 $end_date = $this->create_date( 'last day' );
226 $previous_start_date = $this->create_date( 'last day last day' );
227 $previous_end_date = $this->create_date( 'last day last day' );
228 }
229
230 if ( $frequency == 'betterdocs_weekly' ) {
231 $start_date = $this->create_date( '-7days' );
232 $end_date = $this->create_date( 'last day' );
233 $previous_start_date = $this->create_date( '-14days' );
234 $previous_end_date = $this->create_date( '-7days' );
235 }
236 if ( $frequency == 'betterdocs_monthly' ) {
237 $previous_start_date = $this->create_date( 'first day of last month last month' );
238 $previous_end_date = $this->create_date( 'last day of last month last month' );
239 $start_date = $this->create_date( 'first day of last month' );
240 $end_date = $this->create_date( 'last day of last month' );
241 }
242
243 $views_current_data = $this->get_views( $start_date, $end_date );
244 $views_previous_data = $this->get_views( $previous_start_date, $previous_end_date );
245 $search_current_data = $this->get_search( $start_date, $end_date );
246 $search_previous_data = $this->get_search( $previous_start_date, $previous_end_date );
247 $docs_current_data = $this->get_leading_docs( $start_date, $end_date );
248 $docs_total_current_reactions = array_sum( array_column( $docs_current_data, 'total_reactions' ) );
249 $search_keyword_data = $this->get_search_keywords( $start_date, $end_date );
250 $count_new_docs_current = $this->count_new_docs( $start_date, $end_date );
251 $count_new_docs_previous = $this->count_new_docs( $previous_start_date, $previous_end_date );
252
253 $from_date = $start_date;
254 $to_date = $frequency == 'betterdocs_daily' ? $start_date : $end_date;
255
256 $data = [
257 'views' => [
258 'from_date' => $from_date,
259 'to_date' => $to_date,
260 'current_data' => $views_current_data,
261 'previous_data' => $views_previous_data
262 ],
263 'search' => [
264 'from_date' => $from_date,
265 'to_date' => $to_date,
266 'current_data' => $search_current_data,
267 'previous_data' => $search_previous_data,
268 'keywords' => $search_keyword_data
269 ],
270 'new_docs' => [
271 'from_date' => $from_date,
272 'to_date' => $to_date,
273 'current_data' => $count_new_docs_current,
274 'previous_data' => $count_new_docs_previous
275 ],
276 'docs' => [
277 'from_date' => $from_date,
278 'to_date' => $to_date,
279 'current_data' => $docs_current_data,
280 'total_current_reactions' => $docs_total_current_reactions
281 ]
282 ];
283 return $data;
284 }
285
286 /**
287 * Adds a custom cron schedule for Weekly.
288 *
289 * @param array $schedules An array of non-default cron schedules.
290 * @return array Filtered array of non-default cron schedules.
291 */
292 function schedules_cron( $schedules = [] ) {
293 $schedules['betterdocs_weekly'] = [
294 'interval' => 604800,
295 'display' => __( 'Once Weekly', 'betterdocs' )
296 ];
297 $schedules['betterdocs_daily'] = [
298 'interval' => 86400,
299 'display' => __( 'Once Daily', 'betterdocs' )
300 ];
301 $schedules['betterdocs_monthly'] = [
302 'interval' => 2635200,
303 'display' => __( 'Once Monthly', 'betterdocs' )
304 ];
305 return $schedules;
306 }
307
308 /**
309 * Set Email Receiver mail address
310 * By Default, Admin Email Address
311 * Admin can set Custom email from NotificationX Advanced Settings Panel
312 *
313 * @return string|array<string>
314 */
315 public function receiver_email_address( $email = '' ) {
316 if ( empty( $email ) ) {
317 $email = $this->settings->get( 'reporting_email', '' );
318 if ( empty( $email ) ) {
319 $email = get_option( 'admin_email' );
320 }
321 }
322 if ( strpos( $email, ',' ) !== false ) {
323 $email = str_replace( ' ', '', $email );
324 $email = explode( ',', $email );
325 } else {
326 $email = trim( $email );
327 }
328 return $email;
329 }
330
331 /**
332 * Set Email Subject
333 * By Default, subject will be "Weekly Reporting for NotificationX"
334 * Admin can set Custom Subject from NotificationX Advanced Settings Panel
335 *
336 * @return string
337 */
338 public function email_subject() {
339 return wp_sprintf(
340 // Translators: %s is replaced with the website name.
341 __( 'Your Documentation Performance of %s Website', 'betterdocs' ),
342 get_bloginfo( 'name' )
343 );
344 }
345
346 /**
347 * Enable Cron Function
348 * Hook: admin_init
349 */
350 public function activate() {
351 $day = $this->settings->get( 'reporting_day', 'monday' );
352 $frequency = $this->settings->get( 'reporting_frequency', 'betterdocs_weekly' );
353
354 if ( $frequency === 'betterdocs_weekly' ) {
355 $datetime = strtotime( "next $day 9AM", current_time( 'timestamp' ) );
356 $this->schedule_event( $datetime, $frequency, 'betterdocs_weekly_email_reporting' );
357 }
358 }
359
360 /**
361 * Execute Cron Function
362 * Hook: admin_init
363 */
364 public function send_email( $frequency = 'betterdocs_weekly', $test = false, $email = null ) {
365 // if ( ! $this->settings->get( 'enable_analytics', false ) && ! $test ) {
366 // return $this->error( 'betterdocs_disabled_analytics', __( 'Analytics disabled. No data found.', 'betterdocs' ) );
367 // }
368
369 $data = $this->get_data( $frequency );
370 if ( empty( $data ) && ! $test ) {
371 return $this->error( 'betterdocs_no_reporting_data', __( 'No data found.', 'betterdocs' ) );
372 }
373
374 $to = null == $email ? $this->receiver_email_address() : $email;
375 if ( empty( $to ) ) {
376 return $this->error( 'betterdocs_reporting_email', __( 'No email found.', 'betterdocs' ) );
377 }
378
379 $subject = $this->email_subject();
380
381 ob_start();
382 betterdocs()->views->get( 'admin/email/header' );
383
384 betterdocs()->views->get(
385 'admin/email/body',
386 [
387 'days' => $this->frequency( $frequency ),
388 'bloginfo' => get_bloginfo( 'name' ),
389 'frequency' => $frequency,
390 'args' => $data,
391 'report_email' => $this
392 ]
393 );
394
395 betterdocs()->views->get( 'admin/email/footer' );
396 $message = ob_get_clean();
397 $admin_email = get_option( 'admin_email' );
398 $headers = [ 'Content-Type: text/html; charset=UTF-8', "From: BetterDocs <{$admin_email}>" ];
399
400 return wp_mail( $to, $subject, $message, $headers );
401 }
402
403 /**
404 * Disable Cron Function
405 * Hook: plugin_deactivation
406 */
407 public function deactivate( $clear_hook = 'betterdocs_weekly_email_reporting', $filter = null ) {
408 if ( is_string( $clear_hook ) ) {
409 $this->clear_scheduled_hook( $clear_hook );
410 } elseif ( $clear_hook === true ) {
411 $deactivate_hooks = [
412 'betterdocs_daily_email_reporting',
413 'betterdocs_weekly_email_reporting',
414 'betterdocs_monthly_email_reporting'
415 ];
416
417 array_walk(
418 $deactivate_hooks,
419 function ( $item ) use ( $filter ) {
420 if ( ! ( $filter !== null && $filter === $item ) ) {
421 $this->clear_scheduled_hook( $item );
422 }
423 }
424 );
425 }
426 }
427
428 public function schedule_event( $timestamp, $frequency, $hook ) {
429 if ( ! wp_next_scheduled( $hook ) ) {
430 wp_schedule_event( $timestamp, $frequency, $hook );
431
432 $this->deactivate( true, $hook );
433 }
434 }
435
436 public function clear_scheduled_hook( $hook ) {
437 wp_clear_scheduled_hook( $hook );
438 }
439
440 public function percentage( $previous, $current ) {
441 if ( $previous == 0 ) {
442 $factor = $current * 100;
443 } elseif ( $current == 0 ) {
444 $factor = $previous * 100;
445 } else {
446 $factor = ( ( (int) $current - (int) $previous ) / (int) $previous ) * 100;
447 }
448 return number_format( $factor, 2, '.', '' );
449 }
450
451 public function frequency( $frequency = 'betterdocs_weekly' ) {
452 switch ( $frequency ) {
453 case 'betterdocs_weekly':
454 $days_ago = '7 days';
455 break;
456 case 'betterdocs_daily':
457 $days_ago = '1 day';
458 break;
459 case 'betterdocs_monthly':
460 $initial_timestamp = strtotime( 'first day of last month', current_time( 'timestamp' ) );
461 $days_in_last_month = cal_days_in_month( CAL_GREGORIAN, date( 'm', $initial_timestamp ), date( 'Y', $initial_timestamp ) );
462 $days_ago = $days_in_last_month . ' days';
463 break;
464 }
465
466 return $days_ago;
467 }
468
469 public function test_email_report( $request ) {
470 $email = $this->send_email( $request->get_param( 'reporting_frequency' ), true, $request->get_param( 'reporting_email' ) );
471
472 $response = [
473 'status' => 'success',
474 'data' => [
475 'context' => [
476 'test_report' => $email
477 ]
478 ]
479 ];
480
481 if ( $email ) {
482 return $response;
483 } else {
484 $response['status'] = 'error';
485 return $response;
486 }
487 }
488 }
489