PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 3.5.9
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v3.5.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.5.9, at includes/Admin/ReportEmail.php

471 lines 17.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 } else if ( 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
101 $query = $wpdb->get_results( "
102 SELECT sum(impressions) as views, sum(unique_visit) as unique_visit, sum(happy + sad + normal) as reactions
103 FROM {$wpdb->prefix}betterdocs_analytics
104 WHERE (created_at BETWEEN '" . $start_date . "' AND '" . $end_date . "')
105 " );
106 return $query;
107 }
108
109 public function get_search( $start_date, $end_date = null ) {
110 global $wpdb;
111
112 $query = $wpdb->get_results( "
113 SELECT SUM(count + not_found_count) as search_count, SUM(count) as search_found, SUM(not_found_count) as search_not_found_count
114 FROM {$wpdb->prefix}betterdocs_search_log as search_log
115 WHERE (search_log.created_at BETWEEN '" . $start_date . "' AND '" . $end_date . "')
116 " );
117
118 return $query;
119 }
120
121 public function count_new_docs( $start_date, $end_date = null ) {
122 $args = [
123 'post_type' => 'docs',
124 'post_status' => 'publish',
125 'posts_per_page' => -1,
126 'date_query' => [
127 [
128 'after' => $start_date,
129 'before' => $end_date,
130 'inclusive' => true
131 ]
132 ]
133 ];
134 $query = new WP_Query( $args );
135
136 return $query->found_posts;
137 }
138
139 public function get_search_keywords( $start_date, $end_date = null ) {
140 global $wpdb;
141 $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";
142 $join = "FROM {$wpdb->prefix}betterdocs_search_keyword as search_keyword
143 JOIN {$wpdb->prefix}betterdocs_search_log as search_log on search_keyword.id = search_log.keyword_id";
144
145 $query = $wpdb->get_results( "
146 {$select}
147 {$join}
148 WHERE (search_log.created_at BETWEEN '" . $start_date . "' AND '" . $end_date . "')
149 GROUP BY search_log.keyword_id
150 ORDER BY count DESC LIMIT 5
151 " );
152
153 return $query;
154 }
155
156 public function get_docs_items( $request ) {
157 $prepared_post = new stdClass();
158
159 if ( isset( $request->ID ) ) {
160 $prepared_post->ID = $request->ID;
161 }
162
163 if ( isset( $request->post_title ) ) {
164 $prepared_post->title = betterdocs()->template_helper->kses( $request->post_title );
165 }
166
167 if ( isset( $request->total_views ) ) {
168 $prepared_post->total_views = $request->total_views;
169 }
170
171 if ( isset( $request->total_unique_visit ) ) {
172 $prepared_post->total_unique_visit = $request->total_unique_visit;
173 }
174
175 if ( isset( $request->total_reactions ) ) {
176 $prepared_post->total_reactions = $request->total_reactions;
177 }
178
179 if ( isset( $request->ID ) ) {
180 $prepared_post->link = get_permalink( $request->ID );
181 }
182
183 return $prepared_post;
184 }
185
186 public function get_leading_docs( $start_date, $end_date = null ) {
187 global $wpdb;
188
189 $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";
190 $join = "FROM {$wpdb->prefix}posts as docs
191 JOIN {$wpdb->prefix}betterdocs_analytics as analytics on docs.ID = analytics.post_id";
192
193 $query = $wpdb->get_results( "
194 {$select}
195 {$join}
196 WHERE post_type = 'docs' AND post_status = 'publish' AND (analytics.created_at BETWEEN '" . $start_date . "' AND '" . $end_date . "')
197 GROUP BY analytics.post_id
198 ORDER BY total_views DESC LIMIT 5
199 " );
200
201 $docs = [];
202 foreach ( $query as $key => $value ) {
203 $docs[$key] = $this->get_docs_items( $value );
204 }
205
206 return $docs;
207 }
208
209 /**
210 * Calculate Total BetterDocs Views
211 *
212 * @return array
213 */
214 public function get_data( $frequency = 'betterdocs_weekly' ) {
215 if ( $frequency == 'betterdocs_daily' ) {
216 $start_date = $this->create_date( 'last day' );
217 $end_date = $this->create_date( 'last day' );
218 $previous_start_date = $this->create_date( 'last day last day' );
219 $previous_end_date = $this->create_date( 'last day last day' );
220 }
221
222 if ( $frequency == 'betterdocs_weekly' ) {
223 $start_date = $this->create_date( '-7days' );
224 $end_date = $this->create_date( 'last day' );
225 $previous_start_date = $this->create_date( '-14days' );
226 $previous_end_date = $this->create_date( '-7days' );
227 }
228 if ( $frequency == 'betterdocs_monthly' ) {
229 $previous_start_date = $this->create_date( 'first day of last month last month' );
230 $previous_end_date = $this->create_date( 'last day of last month last month' );
231 $start_date = $this->create_date( 'first day of last month' );
232 $end_date = $this->create_date( 'last day of last month' );
233 }
234
235 $views_current_data = $this->get_views( $start_date, $end_date );
236 $views_previous_data = $this->get_views( $previous_start_date, $previous_end_date );
237 $search_current_data = $this->get_search( $start_date, $end_date );
238 $search_previous_data = $this->get_search( $previous_start_date, $previous_end_date );
239 $docs_current_data = $this->get_leading_docs( $start_date, $end_date );
240 $docs_total_current_reactions = array_sum( array_column( $docs_current_data, 'total_reactions' ) );
241 $search_keyword_data = $this->get_search_keywords( $start_date, $end_date );
242 $count_new_docs_current = $this->count_new_docs( $start_date, $end_date );
243 $count_new_docs_previous = $this->count_new_docs( $previous_start_date, $previous_end_date );
244
245 $from_date = $start_date;
246 $to_date = $frequency == 'betterdocs_daily' ? $start_date : $end_date;
247
248 $data = [
249 'views' => [
250 'from_date' => $from_date,
251 'to_date' => $to_date,
252 'current_data' => $views_current_data,
253 'previous_data' => $views_previous_data
254 ],
255 'search' => [
256 'from_date' => $from_date,
257 'to_date' => $to_date,
258 'current_data' => $search_current_data,
259 'previous_data' => $search_previous_data,
260 'keywords' => $search_keyword_data
261 ],
262 'new_docs' => [
263 'from_date' => $from_date,
264 'to_date' => $to_date,
265 'current_data' => $count_new_docs_current,
266 'previous_data' => $count_new_docs_previous
267 ],
268 'docs' => [
269 'from_date' => $from_date,
270 'to_date' => $to_date,
271 'current_data' => $docs_current_data,
272 'total_current_reactions' => $docs_total_current_reactions
273 ]
274 ];
275 return $data;
276 }
277
278 /**
279 * Adds a custom cron schedule for Weekly.
280 *
281 * @param array $schedules An array of non-default cron schedules.
282 * @return array Filtered array of non-default cron schedules.
283 */
284 function schedules_cron( $schedules = [] ) {
285 $schedules['betterdocs_weekly'] = [
286 'interval' => 604800,
287 'display' => __( 'Once Weekly', 'betterdocs' )
288 ];
289 $schedules['betterdocs_daily'] = [
290 'interval' => 86400,
291 'display' => __( 'Once Daily', 'betterdocs' )
292 ];
293 $schedules['betterdocs_monthly'] = [
294 'interval' => 2635200,
295 'display' => __( 'Once Monthly', 'betterdocs' )
296 ];
297 return $schedules;
298 }
299
300 /**
301 * Set Email Receiver mail address
302 * By Default, Admin Email Address
303 * Admin can set Custom email from NotificationX Advanced Settings Panel
304 *
305 * @return string|array<string>
306 */
307 public function receiver_email_address( $email = '' ) {
308 if ( empty( $email ) ) {
309 $email = $this->settings->get( 'reporting_email', '' );
310 if ( empty( $email ) ) {
311 $email = get_option( 'admin_email' );
312 }
313 }
314 if ( strpos( $email, ',' ) !== false ) {
315 $email = str_replace( ' ', '', $email );
316 $email = explode( ',', $email );
317 } else {
318 $email = trim( $email );
319 }
320 return $email;
321 }
322
323 /**
324 * Set Email Subject
325 * By Default, subject will be "Weekly Reporting for NotificationX"
326 * Admin can set Custom Subject from NotificationX Advanced Settings Panel
327 *
328 * @return string
329 */
330 public function email_subject() {
331 return wp_sprintf( __( 'Your Documentation Performance of %s Website', 'betterdocs-pro' ), get_bloginfo( 'name' ) );
332 }
333
334 /**
335 * Enable Cron Function
336 * Hook: admin_init
337 */
338 public function activate() {
339 $day = $this->settings->get( 'reporting_day', 'monday' );
340 $frequency = $this->settings->get( 'reporting_frequency', 'betterdocs_weekly' );
341
342 if ( $frequency === 'betterdocs_weekly' ) {
343 $datetime = strtotime( "next $day 9AM", current_time( 'timestamp' ) );
344 $this->schedule_event( $datetime, $frequency, 'betterdocs_weekly_email_reporting' );
345 }
346 }
347
348 /**
349 * Execute Cron Function
350 * Hook: admin_init
351 */
352 public function send_email( $frequency = 'betterdocs_weekly', $test = false, $email = null ) {
353 // if ( ! $this->settings->get( 'enable_analytics', false ) && ! $test ) {
354 // return $this->error( 'betterdocs_disabled_analytics', __( 'Analytics disabled. No data found.', 'betterdocs' ) );
355 // }
356
357 $data = $this->get_data( $frequency );
358 if ( empty( $data ) && ! $test ) {
359 return $this->error( 'betterdocs_no_reporting_data', __( 'No data found.', 'betterdocs' ) );
360 }
361
362 $to = null == $email ? $this->receiver_email_address() : $email;
363 if ( empty( $to ) ) {
364 return $this->error( 'betterdocs_reporting_email', __( 'No email found.', 'betterdocs' ) );
365 }
366
367 $subject = $this->email_subject();
368
369 ob_start();
370 betterdocs()->views->get( 'admin/email/header' );
371
372 betterdocs()->views->get( 'admin/email/body', [
373 'days' => $this->frequency( $frequency ),
374 'bloginfo' => get_bloginfo( 'name' ),
375 'frequency' => $frequency,
376 'args' => $data,
377 'report_email' => $this
378 ] );
379
380 betterdocs()->views->get( 'admin/email/footer' );
381 $message = ob_get_clean();
382 $admin_email = get_option( 'admin_email' );
383 $headers = ['Content-Type: text/html; charset=UTF-8', "From: BetterDocs <{$admin_email}>"];
384
385 return wp_mail( $to, $subject, $message, $headers );
386 }
387
388 /**
389 * Disable Cron Function
390 * Hook: plugin_deactivation
391 */
392 public function deactivate( $clear_hook = 'betterdocs_weekly_email_reporting', $filter = null ) {
393 if ( is_string( $clear_hook ) ) {
394 $this->clear_scheduled_hook( $clear_hook );
395 } elseif ( $clear_hook === true ) {
396 $deactivate_hooks = [
397 'betterdocs_daily_email_reporting',
398 'betterdocs_weekly_email_reporting',
399 'betterdocs_monthly_email_reporting'
400 ];
401
402 array_walk( $deactivate_hooks, function ( $item ) use ( $filter ) {
403 if ( ! ( $filter !== null && $filter === $item ) ) {
404 $this->clear_scheduled_hook( $item );
405 }
406 } );
407 }
408 }
409
410 public function schedule_event( $timestamp, $frequency, $hook ) {
411 if ( ! wp_next_scheduled( $hook ) ) {
412 wp_schedule_event( $timestamp, $frequency, $hook );
413
414 $this->deactivate( true, $hook );
415 }
416 }
417
418 public function clear_scheduled_hook( $hook ) {
419 wp_clear_scheduled_hook( $hook );
420 }
421
422 public function percentage( $previous, $current ) {
423 if ( $previous == 0 ) {
424 $factor = $current * 100;
425 } elseif ( $current == 0 ) {
426 $factor = $previous * 100;
427 } else {
428 $factor = ( ( (int)$current - (int)$previous ) / (int)$previous ) * 100;
429 }
430 return number_format( $factor, 2, '.', '' );
431 }
432
433 public function frequency( $frequency = 'betterdocs_weekly' ) {
434 switch ( $frequency ) {
435 case 'betterdocs_weekly':
436 $days_ago = '7 days';
437 break;
438 case 'betterdocs_daily':
439 $days_ago = '1 day';
440 break;
441 case 'betterdocs_monthly':
442 $initial_timestamp = strtotime( 'first day of last month', current_time( 'timestamp' ) );
443 $days_in_last_month = cal_days_in_month( CAL_GREGORIAN, date( 'm', $initial_timestamp ), date( 'Y', $initial_timestamp ) );
444 $days_ago = $days_in_last_month . ' days';
445 break;
446 }
447
448 return $days_ago;
449 }
450
451 public function test_email_report( $request ) {
452 $email = $this->send_email( $request->get_param( 'reporting_frequency' ), true, $request->get_param( 'reporting_email' ) );
453
454 $response = [
455 'status' => 'success',
456 'data' => [
457 'context' => [
458 'test_report' => $email
459 ]
460 ]
461 ];
462
463 if ( $email ) {
464 return $response;
465 } else {
466 $response['status'] = 'error';
467 return $response;
468 }
469 }
470 }
471