PluginProbe
Awesome Support – WordPress HelpDesk & Support Plugin / trunk
Awesome Support – WordPress HelpDesk & Support Plugin vtrunk
6.4.0 trunk 3.0.0 3.0.1 3.1.0 3.1.1 3.1.10 3.1.11 3.1.12 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 All 95 releases
awesome-support / includes / admin / functions-misc.php

functions-misc.php in Awesome Support – WordPress HelpDesk & Support Plugin trunk, at includes/admin/functions-misc.php

680 lines 18.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * @package Awesome Support/Admin/Functions/Misc
5 * @author AwesomeSupport <contact@getawesomesupport.com>
6 * @license GPL-2.0+
7 * @link https://getawesomesupport.com
8 * @copyright 2015-2017 AwesomeSupport
9 */
10
11 // If this file is called directly, abort.
12 if (!defined('WPINC')) {
13 die;
14 }
15
16 add_filter('plugin_action_links_' . WPAS_PLUGIN_BASENAME, 'wpas_settings_page_link');
17 /**
18 * Add a link to the settings page.
19 *
20 * @since 3.1.5
21 *
22 * @param array $links Plugin links
23 *
24 * @return array Links with the settings
25 */
26 function wpas_settings_page_link($links) {
27
28 $link = wpas_get_settings_page_url();
29 $links[] = "<a href='$link'>" . __('Settings', 'awesome-support') . "</a>";
30
31 return $links;
32 }
33
34 add_filter('postbox_classes_ticket_wpas-mb-details', 'wpas_add_metabox_details_classes');
35 /**
36 * Add new class to the details metabox.
37 *
38 * @param array $classes Current metabox classes
39 *
40 * @return array The updated list of classes
41 */
42 function wpas_add_metabox_details_classes($classes) {
43 array_push($classes, 'submitdiv');
44
45 return $classes;
46 }
47
48 add_action('admin_notices', 'wpas_admin_notices');
49 /**
50 * Display custom admin notices.
51 *
52 * Custom admin notices are usually triggered by custom actions.
53 *
54 * @since 3.0.0
55 * @return void
56 */
57 function wpas_admin_notices() {
58
59 if (isset($_GET['wpas-message'])) {
60
61 // translators: %s is the ticket id.
62 $x_content1 = __('The ticket #%s has been (re)opened.', 'awesome-support');
63
64 // translators: %s is the ticket id.
65 $x_content2 = __('The ticket #%s has been closed.', 'awesome-support');
66
67 switch ($_GET['wpas-message']) {
68
69 case 'opened':
70 ?>
71 <div class="updated">
72 <p><?php printf(esc_html($x_content1), isset( $_GET['post'] ) ? intval( $_GET['post'] ) : 0); ?></p>
73 </div>
74 <?php
75 break;
76
77 case 'closed':
78 ?>
79 <div class="updated">
80 <p><?php printf(esc_html($x_content2), isset( $_GET['post'] ) ? intval( $_GET['post'] ) : 0); ?></p>
81 </div>
82 <?php
83 break;
84 }
85 }
86 }
87
88 add_filter('wpas_toolbar_ticket_reply', 'wpas_ticket_reply_controls', 10, 3);
89 /**
90 * Add ticket reply controls
91 *
92 * @since 3.2.6
93 *
94 * @param array $controls List of existing controls
95 * @param int $ticket_id ID of the ticket current reply belongs to
96 * @param WP_Post $reply Reply post object
97 *
98 * @return array
99 */
100 function wpas_ticket_reply_controls($controls, $ticket_id, $reply) {
101
102 if (0 !== $ticket_id) {
103
104 // Create a delete link and then add the delete reply icon.
105 if (((true === boolval(wpas_get_option('agent_delete_own_reply', false)) && get_current_user_id() == $reply->post_author))
106 || true === wpas_is_asadmin()
107 || true === wpas_current_role_in_list(wpas_get_option('roles_delete_all_replies', '')) ) {
108
109 // Create delete link
110 $_GET['del_id'] = $reply->ID;
111 $url = add_query_arg($_GET, admin_url('post.php'));
112 remove_query_arg('message', $url);
113 $delete = wpas_do_url(admin_url('post.php'), 'admin_trash_reply', array('post' => $ticket_id, 'action' => 'edit', 'reply_id' => $reply->ID));
114 wp_nonce_url(add_query_arg(array(
115 'post' => $ticket_id,
116 'rid' => $reply->ID,
117 'action' => 'edit_reply'
118 ), admin_url('post.php')), 'delete_reply_' . $reply->ID);
119
120 /* Add delete reply icon */
121 $controls['delete_reply'] = array(
122 'tool_tip_text' => esc_html_x('Delete', 'Link to delete a ticket reply', 'awesome-support'),
123 'type' => 'link',
124 'link' => esc_url($delete),
125 'icon' => 'icon-delete-ticket-replies',
126 'id_param' => 'css',
127 'classes' => 'wpas-delete'
128 );
129 }
130
131 /* Add edit reply icon */
132 if (((true === boolval(wpas_get_option('agent_edit_own_reply', false)) && get_current_user_id() == $reply->post_author))
133 || true === wpas_is_asadmin()
134 || true === wpas_current_role_in_list(wpas_get_option('roles_edit_all_replies', '')) ) {
135
136 $controls['edit_reply'] = array(
137 'tool_tip_text' => esc_html_x('Edit', 'Link to edit a ticket reply', 'awesome-support'),
138 'icon' => 'icon-edit-ticket-replies',
139 'id_param' => 'css',
140 'classes' => 'wpas-edit',
141 'data' => array(
142 'origin' => "#wpas-reply-{$reply->ID}",
143 'replyid' => $reply->ID,
144 'reply' => "wpas-editwrap-{$reply->ID}",
145 'wysiwygid' => "wpas-editreply-{$reply->ID}"
146 )
147 );
148 }
149
150 /** Add reply history icon */
151 if (get_current_user_id() == $reply->post_author || true === wpas_is_asadmin()) {
152
153 $controls['reply_history'] = array(
154 'tool_tip_text' => esc_html_x('History', 'View ticket reply history', 'awesome-support'),
155 'icon' => 'icon-due-date',
156 'id_param' => 'css',
157 'classes' => 'wpas-show-reply-history',
158 'data' => array(
159 'replyid' => $reply->ID
160 )
161 );
162 }
163 }
164
165 if (get_current_user_id() !== $reply->post_author && 'unread' === $reply->post_status) {
166
167 /* Add mark as read icon */
168 $controls['mark_read'] = array(
169 'tool_tip_text' => esc_html_x('Mark as Read', 'Mark a user reply as read', 'awesome-support'),
170 'icon' => 'icon-prewriten-responses',
171 'id_param' => 'css',
172 'classes' => 'wpas-mark-read',
173 'data' => array(
174 'replyid' => $reply->ID
175 )
176 );
177 }
178
179 return $controls;
180 }
181
182 /**
183 * Check if the ticket is old.
184 *
185 * A simple check based on the value of the "Ticket old" option.
186 * If the last reply (or the ticket itself if no reply) is older
187 * than the post date + the allowed delay, then it is considered old.
188 *
189 * @since 3.0.0
190 *
191 * @param integer $post_id The ID of the ticket to check
192 * @param WP_Query|null $replies The object containing the ticket replies. If the object was previously generated we
193 * pass it directly in order to avoid re-querying
194 *
195 * @return boolean True if the ticket is old, false otherwise
196 */
197 function wpas_is_ticket_old($post_id, $replies = null) {
198
199 if ('closed' === wpas_get_ticket_status($post_id)) {
200 return false;
201 }
202
203 // Prepare the new object
204 if (is_null($replies) || is_object($replies) && !is_a($replies, 'WP_Query')) {
205 $replies = WPAS_Tickets_List::get_instance()->get_replies_query($post_id);
206 }
207
208 /**
209 * We check when was the last reply (if there was a reply).
210 * Then, we compute the ticket age and if it is considered as
211 * old, we display an informational tag.
212 */
213 if (empty($replies->posts)) {
214
215 $post = get_post($post_id);
216
217 /* We get the post date */
218 $date_created = $post->post_date;
219 } else {
220
221 $last = $replies->post_count - 1;
222
223 /* We get the post date */
224 $date_created = $replies->posts[$last]->post_date;
225 }
226
227 $old_after = (int) wpas_get_option('old_ticket');
228 $post_date_timestamp = mysql2date('U', $date_created);
229
230 if ($post_date_timestamp + ($old_after * 86400) < strtotime('now')) {
231 return true;
232 }
233
234 return false;
235 }
236
237 /**
238 * Check if a reply is needed.
239 *
240 * Takes a ticket ID and checks if a reply is needed. The check is based
241 * on who replied last. If a client was the last to reply, or if the ticket
242 * was just transferred from one agent to another, then it is considered
243 * as "awaiting reply".
244 *
245 * @since 3.0.0
246 *
247 * @param integer $post_id The ID of the ticket to check
248 * @param WP_Query|null $replies The object containing the ticket replies. If the object was previously generated we
249 * pass it directly in order to avoid re-querying
250 *
251 * @return boolean True if a reply is needed, false otherwise
252 */
253 function wpas_is_reply_needed($post_id, $replies = null) {
254
255 if ('closed' === wpas_get_ticket_status($post_id)) {
256 return false;
257 }
258
259 /* Prepare the new object */
260 if (is_null($replies) || is_object($replies) && !is_a($replies, 'WP_Query')) {
261 $replies = WPAS_Tickets_List::get_instance()->get_replies_query($post_id);
262 }
263
264 /* No reply yet. */
265 if (empty($replies->posts)) {
266
267 $post = get_post($post_id);
268
269 /* Make sure the ticket wan not created by an agent on behalf of the client. */
270 if (!user_can($post->post_author, 'edit_ticket')) {
271 return true;
272 }
273 } else {
274
275 $last = $replies->post_count - 1;
276
277 // If the last reply was from an agent then return false - the ticket is waiting for the customer to reply.
278 if (user_can($replies->posts[$last]->post_author, 'edit_ticket')) {
279
280 return false;
281 } else {
282
283 // Or the ticket has just been transferred, in which case we want to show the awaiting reply tag
284 return true;
285 }
286
287 // If the last reply is not from an agent return true since ticket is waiting for a reply from an agent...
288 if (!user_can($replies->posts[$last]->post_author, 'edit_ticket')) {
289 return true;
290 }
291 }
292
293 return false;
294 }
295
296 /**
297 * Check if the ticket is a ticket template.
298 *
299 *
300 * @since 5.9.0
301 *
302 * @param integer $post_id The ID of the ticket to check
303 *
304 * @return boolean True if the ticket is a ticket template, false otherwise
305 */
306 function wpas_is_ticket_template($post_id) {
307 return boolval(get_post_meta($post_id, '_wpas_is_ticket_template', true));
308 }
309
310 /**
311 * Returns the close date of the ticket based on the ticket/post id passed
312 *
313 * @since 4.0.4
314 *
315 * @param integer $post_id The ID of the ticket to check
316 *
317 * @return date|string Close date of ticket, an empty string otherwise
318 */
319 function wpas_get_close_date($post_id) {
320
321 $close_date = get_post_meta($post_id, '_ticket_closed_on', true);
322
323 if (!empty($close_date)) {
324 return $close_date;
325 } else {
326 return '';
327 }
328 }
329
330 /**
331 * Returns the close date in GMT of the ticket based on the ticket/post id passed
332 *
333 * @since 4.0.5
334 *
335 * @param integer $post_id The ID of the ticket to check
336 *
337 * @return date|string Close date of ticket, an empty string otherwise
338 */
339 function wpas_get_close_date_gmt($post_id) {
340
341 $close_date = get_post_meta($post_id, '_ticket_closed_on_gmt', true);
342
343 if (!empty($close_date)) {
344 return $close_date;
345 } else {
346 return '';
347 }
348 }
349
350 /**
351 * Returns the open date of the ticket based on the ticket/post id passed
352 *
353 * @since 4.0.5
354 *
355 * @param integer $post_id The ID of the ticket to check
356 *
357 * @return date|string open date of ticket, an empty string otherwise
358 */
359 function wpas_get_open_date($post_id) {
360
361 // Return if not a ticket...
362 if ('ticket' <> get_post_type($post_id)) {
363 return '';
364 }
365
366 $the_ticket = get_post($post_id);
367
368 $open_date = $the_ticket->post_date;
369
370 if (!empty($open_date)) {
371 return $open_date;
372 } else {
373 return '';
374 }
375 }
376
377 /**
378 * Returns the open date in GMT of the ticket based on the ticket/post id passed
379 *
380 * @since 4.0.5
381 *
382 * @param integer $post_id The ID of the ticket to check
383 *
384 * @return date|string open date of ticket, an empty string otherwise
385 */
386 function wpas_get_open_date_gmt($post_id) {
387
388 // Return if not a ticket...
389 if ('ticket' <> get_post_type($post_id)) {
390 return '';
391 }
392
393 $the_ticket = get_post($post_id);
394
395 $open_date = $the_ticket->post_date_gmt;
396
397 if (!empty($open_date)) {
398 return $open_date;
399 } else {
400 return '';
401 }
402 }
403
404 /**
405 * Returns difference between two dates in string format to help with debugging.
406 * Formatted string will look like this sample : 0 day(s) 14 hour(s) 33 minute(s)
407 *
408 * @since 4.0.5
409 *
410 * @param date $firstdate First date in the format you get when using post->post_date to get a date from a post
411 * @param date $seconddate Second date in the format you get when using post->post_date to get a date from a post
412 *
413 * @return string difference between two dates, an empty string otherwise
414 */
415 function wpas_get_date_diff_string($firstdate, $seconddate) {
416
417 // Calculate difference object...
418 $date1 = new DateTime($firstdate);
419 $date2 = new DateTime($seconddate);
420 $diff_dates = $date2->diff($date1);
421
422 $date_string = '';
423 $date_string .= ' ' . $diff_dates->format('%d') . __(' day(s)', 'awesome-support');
424 $date_string .= ' ' . $diff_dates->format('%h') . __(' hour(s)', 'awesome-support');
425 $date_string .= ' ' . $diff_dates->format('%i') . __(' minute(s)', 'awesome-support');
426
427 return $date_string;
428 }
429
430 add_filter('admin_footer_text', 'wpas_admin_footer_text', 999, 1);
431 /**
432 * Add a custom admin footer text
433 *
434 * @since 3.2.8
435 *
436 * @param string $text Footer text
437 *
438 * @return string
439 */
440 function wpas_admin_footer_text($text) {
441
442 if (!is_admin() || !wpas_is_plugin_page()) {
443 return $text;
444 }
445
446 // Do not show message if installed in an SAAS environment
447 if (defined('WPAS_SAAS') && true === WPAS_SAAS) {
448 return;
449 }
450
451 if (!boolval(wpas_get_option('remove_admin_ratings_request', false))) {
452 // translators: %1$s is the HTML attribute for the link, %2$s is the rating (e.g., star) the user is asked to leave.
453 $x_content = __('If you like Awesome Support <a %1$s>please leave us a %2$s rating</a>. Many thanks from the Awesome Support team in advance :)', 'awesome-support');
454 return sprintf($x_content, 'href="https://wordpress.org/support/view/plugin-reviews/awesome-support?rate=5#postform" target="_blank"', '&#9733&#9733&#9733&#9733&#9733 ');
455 }
456 }
457
458 /**
459 * Check if the free addon page has been dismissed or not
460 *
461 * @since 3.3.3
462 * @return bool
463 */
464 function wpas_is_free_addon_page_dismissed() {
465 return (bool) get_option('wpas_dismiss_free_addon_page', false);
466 }
467
468 add_action('admin_notices', 'wpas_free_addon_notice');
469 /**
470 * Add free addon notice
471 *
472 * After the plugin has been activated, we display a notice to admins telling them that they can get a free addon for
473 * Awesome Support.
474 *
475 * @since 3.3.3
476 * @return void
477 */
478 function wpas_free_addon_notice() {
479
480 // Do not show message if installed in an SAAS environment
481 if (defined('WPAS_SAAS') && true === WPAS_SAAS) {
482 return;
483 }
484
485 // Only show this message to admins
486 if (!current_user_can('administrator')) {
487 return;
488 }
489
490 // Don't show the notice if user already claimed the addon
491 if (wpas_is_free_addon_page_dismissed()) {
492 return;
493 }
494
495 // Only show the notice on the plugin pages
496 if (!wpas_is_plugin_page()) {
497 return;
498 }
499
500 // No need to show the notice on the free addon page itself
501 if (isset($_GET['page']) && 'wpas-optin' === $_GET['page']) {
502 return;
503 }
504
505 // translators: %1$s is the URL for more information about the free add-on.
506 $x_content = __('Hey! Did you know you can get a <strong>free add-on for unlimited sites</strong> (a $61.00 USD value) for Awesome Support? <a href="%1$s">Click here to read more</a>.', 'awesome-support');
507
508 WPAS()->admin_notices->add_notice('updated', 'wpas_get_free_addon', wp_kses(sprintf($x_content, add_query_arg(array(
509 'post_type' => 'ticket',
510 'page' => 'wpas-optin',
511 ), admin_url('edit.php'))), array('strong' => array(), 'a' => array('href' => array()))));
512 }
513
514 add_action('admin_notices', 'wpas_request_first_5star_rating');
515 /**
516 * Request 5 star rating after 25 closed tickets.
517 *
518 * After 25 closed tickets we ask the admin for a 5 star rating
519 *
520 * @since 4.0.0
521 * @return void
522 */
523 function wpas_request_first_5star_rating() {
524
525 // Do not show message if installed in an SAAS environment
526 if (defined('WPAS_SAAS') && true === WPAS_SAAS) {
527 return;
528 }
529
530 // Only show this message to admins
531 if (!current_user_can('administrator')) {
532 return;
533 }
534
535 // Only show the notice on the plugin pages
536 if (!wpas_is_plugin_page()) {
537 return;
538 }
539
540 // If notice has been dismissed, return since everything else after this is expensive operations!
541 if (wpas_is_notice_dismissed('wpas_request_first_5star_rating')) {
542 return;
543 }
544
545 // How many tickets have been closed?
546 $closed_tickets = wpas_get_tickets('closed', array('posts_per_page' => 1), 'any', true, true);
547
548 // Show notice if number of closed tickets greater than 25.
549 if ($closed_tickets >= 25) {
550
551 // translators: %1$s is the URL where users can leave a rating.
552 $x_content = __('Wow! It looks like you have closed a lot of tickets which is pretty awesome! We guess you must really like Awesome Support, huh? Could you please do us a favor and leave a 5 star rating on WordPress? It will only take a minute and helps to motivate our developers and volunteers. <a href="%1$s">Yes, you deserve it!</a>.', 'awesome-support');
553
554 WPAS()->admin_notices->add_notice('updated', 'wpas_request_first_5star_rating', wp_kses(
555 sprintf( $x_content, 'https://wordpress.org/support/plugin/awesome-support/reviews/'),
556 array('strong' => array(), 'a' => array('href' => array()))
557 ));
558 }
559 }
560
561 /**
562 * Generate admin tabs html
563 *
564 * @param string $type
565 * @param array $tabs
566 *
567 * @return string
568 */
569 function wpas_admin_tabs($type, $tabs = array()) {
570
571 // Unique tabs widget id
572 $id = "wpas_admin_tabs_{$type}";
573
574
575 $tabs = apply_filters($id, $tabs);
576
577 // Stop processing if no tab exist
578 if (empty($tabs)) {
579 return;
580 }
581
582
583 $tab_order = 1;
584 $tab_content_items_ar = array();
585 $tab_content_ar = array();
586
587 foreach ($tabs as $tab_id => $tab_name) {
588 $_id = "{$id}_{$tab_id}";
589
590 $tab_content = apply_filters("{$_id}_content", "");
591
592 if ($tab_content) {
593 $tab_content_items_ar[] = sprintf('<li data-tab-order="%s" rel="%s" class="wpas_tab_name">%s</li>', $tab_order, $_id, $tab_name);
594 $tab_content_ar[] = '<div class="wpas_admin_tab_content" id="' . $_id . '">' . $tab_content . '</div>';
595 $tab_order++;
596 }
597 }
598
599
600 // Stop processing if no tab's data exist
601 if (empty($tab_content_items_ar)) {
602 return;
603 }
604
605 ob_start();
606
607 ?>
608
609
610 <div class="wpas_admin_tabs" id="<?php echo esc_attr($id); ?>">
611 <div class="wpas_admin_tabs_names_wrapper">
612 <ul>
613 <?php echo wp_kses(implode('', $tab_content_items_ar), get_allowed_html_wp_notifications()); ?>
614 <li class="moreTab">
615 <ul class="dropdown-menu tabs_collapsed"></ul>
616 </li>
617 <li class="clear clearfix"></li>
618
619 </ul>
620 </div>
621 <?php echo wp_kses(implode('', $tab_content_ar), get_allowed_html_wp_notifications()); ?>
622 </div>
623 <?php
624
625
626 $output = ob_get_contents();
627 ob_end_clean();
628 return apply_filters('wpas_admin_tabs', $output, $tab_content_items_ar, $tab_content_ar, $id, $tabs);
629 }
630
631
632 add_action('wpas_admin_after_wysiwyg', 'reply_tabs', 8, 0);
633
634 /**
635 * Add tabs under reply wysiwyg editor
636 */
637 function reply_tabs() {
638
639 $tabs_content = wpas_admin_tabs('after_reply_wysiwyg');
640 echo wp_kses($tabs_content, get_allowed_html_wp_notifications());
641 }
642
643 /**
644 * If a session has already been started by some external system, end one!
645 * Added this functionality due to new site health check system added by WordPress.org.
646 */
647 function wpas_end_session() {
648 if (session_status() === PHP_SESSION_ACTIVE) {
649 session_write_close();
650 }
651 }
652
653 // End session, if we're not in the CLI.
654 if (session_status() !== PHP_SESSION_DISABLED && (!defined('WP_CLI') || false === WP_CLI)) {
655 // If we're not in a cron, end the session
656 if (!defined('DOING_CRON') || false === DOING_CRON) {
657 add_action('wp_loaded', 'wpas_end_session', 10, 0);
658 }
659 }
660
661 if (is_admin()) {
662 function wp_default_custom_scripts($scripts)
663 {
664 $scripts->add('wp-color-picker', "/wp-admin/js/color-picker.js", array('iris'), false, 1);
665 did_action('init') && $scripts->localize(
666 'wp-color-picker',
667 'wpColorPickerL10n',
668 array(
669 'clear' => __('Clear', 'awesome-support' ),
670 'clearAriaLabel' => __('Clear color', 'awesome-support' ),
671 'defaultString' => __('Default', 'awesome-support' ),
672 'defaultAriaLabel' => __('Select default color', 'awesome-support' ),
673 'pick' => __('Select Color', 'awesome-support' ),
674 'defaultLabel' => __('Color value', 'awesome-support' ),
675 )
676 );
677 }
678 add_action('wp_default_scripts', 'wp_default_custom_scripts');
679 }
680