PluginProbe
UpStream: a Project Management Plugin for WordPress / 1.39.2
UpStream: a Project Management Plugin for WordPress v1.39.2
trunk 1.39.0 1.39.1 1.39.2 1.39.3 2.0.7 2.1.0
upstream / includes / up-general-functions.php

up-general-functions.php in UpStream: a Project Management Plugin for WordPress 1.39.2, at includes/up-general-functions.php

1,908 lines 50.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // Exit if accessed directly
4 if ( ! defined('ABSPATH')) {
5 exit;
6 }
7
8 function upstream_esc_w($s)
9 {
10 return wp_kses_post($s);
11 }
12
13 /**
14 * Get a post id,
15 * no matter where we are or what we are doing.
16 */
17 function upstream_post_id()
18 {
19 $post_id = 0;
20 if ( ! $post_id) {
21 $post_id = get_the_ID();
22 }
23 if ( ! $post_id) {
24 $post_id = isset($_GET['post']) ? (int)$_GET['post'] : 0;
25 }
26 if ( ! $post_id) {
27 $post_id = isset($_POST['post']) ? (int)$_POST['post'] : 0;
28 }
29 if ( ! $post_id) {
30 $post_id = isset($_POST['post_ID']) ? (int)$_POST['post_ID'] : 0;
31 }
32 if ( ! $post_id) {
33 $post_id = isset($_POST['post_id']) ? (int)$_POST['post_id'] : 0;
34 }
35 if ( ! $post_id) {
36 $post_id = isset($_POST['post']) ? (int)$_POST['post'] : 0;
37 }
38 if ( ! $post_id) {
39 global $wp_query;
40 $post_id = $wp_query->get_queried_object_id();
41 }
42 if ( ! $post_id) {
43 if (isset($_POST['formdata'])) {
44 parse_str(sanitize_text_field($_POST['formdata']), $posted);
45 if (isset($posted['post_id'])) {
46 $post_id = (int)$posted['post_id'];
47 }
48 }
49 }
50
51 // will be an int post ID -- caller will check that it is a real post and accessible
52 return $post_id;
53 }
54
55
56 // Url for logging out, depending on client or WP user
57 function upstream_logout_url()
58 {
59 $url = "";
60
61 if (
62 ( ! empty($_SESSION) && isset($_SESSION['upstream']) && isset($_SESSION['upstream']['user_id'])) ||
63 ( ! is_user_logged_in())
64 ) {
65 $url= '?action=logout';
66 } else {
67 $url= wp_logout_url(get_post_type_archive_link('project'));
68 }
69
70 return apply_filters('upstream_logout_url', $url);
71 }
72
73
74 /**
75 * Disable the bugs option
76 *
77 */
78 function upstream_disable_bugs()
79 {
80 $options = get_option('upstream_general');
81 $disable_bugs = isset($options['disable_bugs']) ? $options['disable_bugs'] : ['no'];
82
83 return $disable_bugs[0] == 'yes';
84 }
85
86 function upstream_filesytem_enabled()
87
88 {
89 $options = get_option('upstream_general');
90 $use_upfs = isset($options['use_upfs']) ? (int)$options['use_upfs'] : 0;
91
92 return $use_upfs == 1 && trim(upstream_filesystem_path()) != '';
93 }
94
95 function upstream_filesystem_max_size()
96 {
97 return 100000000;
98 }
99
100 function upstream_filesystem_path()
101 {
102 $options = get_option('upstream_general');
103 $upfs_location = isset($options['upfs_location']) ? $options['upfs_location'] : '';
104
105 return $upfs_location;
106 }
107
108 /**
109 * set a unique id.
110 *
111 */
112 function upstream_admin_set_unique_id()
113 {
114 return uniqid(get_current_user_id());
115 }
116
117 /**
118 * Is a user logged in.
119 *
120 * @since 1.0.0
121 */
122 function upstream_is_user_logged_in()
123 {
124 // Checks if the user is logged in through WordPress.
125 if (is_user_logged_in()) {
126 return true;
127 }
128
129 return UpStream_Login::userIsLoggedIn();
130 }
131
132 /**
133 * Checks if current user is a wordpress user or client.
134 *
135 * @since 1.0.0
136 */
137 function upstream_current_user_id()
138 {
139 if (is_user_logged_in()) {
140 return get_current_user_id();
141 } else {
142 return isset($_SESSION['upstream']) && isset($_SESSION['upstream']['user_id']) ? $_SESSION['upstream']['user_id'] : 0;
143 }
144 }
145
146 // checks if current user is a wordpress user or client
147 function upstream_user_type()
148 {
149 if (is_user_logged_in()) {
150 return 'wp';
151 } else {
152 return 'client';
153 }
154 }
155
156
157 // gets the client id that a user belongs to
158 function upstream_get_users_client_id($user_id)
159 {
160 $r = upstream_get_users_client_ids($user_id);
161
162 if (count($r) > 0) {
163 return $r[0];
164 }
165 return 0;
166 }
167
168 function upstream_get_users_client_ids($user_id)
169 {
170 global $wpdb;
171
172 $client_ids = Upstream_Cache::get_instance()->get('upstream_get_users_client_ids'.$user_id);
173
174 if ($client_ids === false) {
175 $client_ids = [];
176
177 $rowset = $wpdb->get_results( sprintf(
178 '
179 SELECT `ID`, `post_title`
180 FROM `%s`
181 WHERE `post_type` = "client"
182 AND `post_status` = "publish"',
183 $wpdb->prefix . 'posts'
184 ) );
185
186 foreach ( $rowset as $row ) {
187
188 $client_id = $row->ID;
189 $clientUsersList = array_filter( (array) get_post_meta( $client_id, '_upstream_new_client_users', true ) );
190 if ( count( $clientUsersList ) > 0 ) {
191 foreach ( $clientUsersList as $clientUser ) {
192 if ( isset( $clientUser['user_id'] ) && $clientUser['user_id'] == $user_id ) {
193 $client_ids[] = $client_id;
194 }
195 }
196 }
197 }
198
199 Upstream_Cache::get_instance()->set('upstream_get_users_client_ids'.$user_id, $client_ids);
200
201 }
202
203 return $client_ids;
204 }
205
206 // get some data for current user
207 // returns a single item
208 // basically a wrapper for upstream_user_data()
209 function upstream_current_user($item = null)
210 {
211 if ( ! $item) {
212 return;
213 }
214 $user_data = upstream_user_data(upstream_current_user_id());
215 $return = isset($user_data[$item]) ? $user_data[$item] : '';
216
217 return $return;
218 }
219
220 // get some data for a user with ID passed
221 // returns a single item
222 // basically a wrapper for upstream_user_data()
223 function upstream_user_item($id = 0, $item = null)
224 {
225 if ( ! $item || ! $id) {
226 return;
227 }
228 $user_data = upstream_user_data($id);
229 $return = isset($user_data[$item]) ? $user_data[$item] : '';
230
231 return $return;
232 }
233
234 // get the user avatar with full name in tooltips
235 function upstream_user_avatar($user_id, $displayTooltip = true)
236 {
237 if ( ! $user_id) {
238 return;
239 }
240
241 // get user data & ignore current user.
242 // if we want current user, pass the ID
243 $user_data = upstream_user_data($user_id, true);
244 $userDisplayName = $user_data['display_name'];
245
246 //Display the name only
247 if ( ! upstream_show_users_name()) {
248 $url = isset($user_data['avatar']) ? $user_data['avatar'] : '';
249 $tooltip = (bool)$displayTooltip ?
250 sprintf(
251 'title="%s" data-toggle="tooltip" data-placement="top" data-original-title="%1$s"',
252 esc_attr($userDisplayName)
253 ) : '';
254 $return = sprintf(
255 '<img class="avatar" src="%s" %s />',
256 esc_attr($url),
257 $tooltip
258 );
259 } else {
260 $return = '<span class="avatar_custom_text">' . esc_html($userDisplayName) . '</span>';
261 }
262
263 return apply_filters('upstream_user_avatar', $return);
264 }
265
266 // get data for any user including current
267 // can send id
268 function upstream_user_data_uncached($data = 0, $ignore_current = false)
269 {
270
271 // if no data sent, find current user email
272 if ( ! $data && ! $ignore_current) {
273 $data = upstream_get_email_address();
274 }
275
276 $user_data = null;
277 $type = is_email($data) ? 'email' : 'id';
278
279 $wp_user = Upstream_Cache::get_instance()->get('upstream_user_data_by'.$type.'.'.$data);
280
281 if ($wp_user === false) {
282 $wp_user = get_user_by($type, $data);
283 }
284 Upstream_Cache::get_instance()->set('upstream_user_data_by'.$type.'.'.$data, $wp_user);
285
286 if (empty($wp_user)) {
287 $wp_user = wp_get_current_user();
288 }
289
290 if ( ! function_exists('is_plugin_active')) {
291 include_once ABSPATH . 'wp-admin/includes/plugin.php';
292 }
293
294 $isBuddyPressRunning = is_plugin_active('buddypress/bp-loader.php') && class_exists('BuddyPress') && function_exists('bp_core_fetch_avatar');
295
296 if ($wp_user && is_object($wp_user)) {
297 $role = '';
298
299 if (isset($wp_user->roles)
300 && is_array($wp_user->roles)
301 && count($wp_user->roles) > 0
302 ) {
303 $role = ucwords(array_values($wp_user->roles)[0]);
304 }
305
306 if (in_array('upstream_user', $wp_user->roles)) {
307 $role = sprintf(__('%s User', 'upstream'), upstream_project_label());
308 }
309 if (in_array('upstream_manager', $wp_user->roles)) {
310 $role = sprintf(__('%s Manager', 'upstream'), upstream_project_label());
311 }
312 if (in_array('upstream_client_user', $wp_user->roles)) {
313 $role = sprintf(__('%s Client User', 'upstream'), upstream_project_label());
314 }
315
316 $user_data = [
317 'id' => $wp_user->ID,
318 'fname' => $wp_user->first_name,
319 'lname' => $wp_user->last_name,
320 'full_name' => $wp_user->first_name . ' ' . $wp_user->last_name,
321 'email' => $wp_user->user_email,
322 'display_name' => $wp_user->display_name,
323 'phone' => '',
324 'projects' => upstream_get_users_projects($wp_user->ID),
325 'role' => $role,
326 'avatar' => "",
327 ];
328
329 if ($isBuddyPressRunning) {
330 $user_data['avatar'] = bp_core_fetch_avatar([
331 'item_id' => $wp_user->ID,
332 'type' => 'thumb',
333 'html' => false,
334 ]);
335 } else {
336 if (is_plugin_active('wp-user-avatar/wp-user-avatar.php') && function_exists('wpua_functions_init')) {
337 global $wp_query;
338
339 // Make sure WP_Query is loaded.
340 if ( ! ($wp_query instanceof \WP_Query)) {
341 $wp_query = new WP_Query();
342 }
343
344 try {
345 // Make sure WP User Avatar dependencies are loaded.
346 require_once ABSPATH . 'wp-settings.php';
347 require_once ABSPATH . 'wp-includes/pluggable.php';
348 require_once ABSPATH . 'wp-includes/query.php';
349 require_once WP_PLUGIN_DIR . '/wp-user-avatar/wp-user-avatar.php';
350
351 // Load WP User Avatar plugin and its dependencies.
352 wpua_functions_init();
353
354 // Retrieve current user id.
355 $user_id = upstream_current_user_id();
356
357 // Retrieve the current user avatar URL.
358 $user_data['avatar'] = get_wp_user_avatar_src($wp_user->ID);
359 } catch (Exception $e) {
360 // Do nothing.
361 }
362 } elseif (is_plugin_active('custom-user-profile-photo/3five_cupp.php') && function_exists('get_cupp_meta')) {
363 $user_data['avatar'] = get_cupp_meta($wp_user->ID);
364 }
365
366 if (empty($user_data['avatar'])) {
367 if ( ! function_exists('get_avatar_url')) {
368 require_once ABSPATH . 'wp-includes/link-template.php';
369 }
370
371 $user_data['avatar'] = get_avatar_url(
372 $wp_user->user_email,
373 96,
374 get_option('avatar_default', 'mystery')
375 );
376 }
377 }
378 } else {
379 global $wpdb;
380 $users = $wpdb->get_results(
381 "SELECT * FROM `" . $wpdb->postmeta .
382 "` WHERE `meta_key` = '_upstream_client_users' AND
383 `meta_value` REGEXP '.*\"" . $type . "\";s:[0-9]+:\"" . $data . "\".*'"
384 );
385
386 if ( ! $users) {
387 return;
388 }
389
390 $metavalue = unserialize($users[0]->meta_value);
391
392 foreach ($metavalue as $key => $user) {
393
394 // get the matching user
395 if (in_array($data, [$user['id'], $user['email']])) {
396 $fname = isset($user['fname']) ? trim($user['fname']) : '';
397 $lname = isset($user['lname']) ? trim($user['lname']) : '';
398 $user_data = [
399 'id' => $user['id'],
400 'fname' => $fname,
401 'lname' => $lname,
402 'full_name' => trim($fname . ' ' . $lname),
403 'email' => isset($user['email']) ? $user['email'] : '',
404 'phone' => isset($user['phone']) ? $user['phone'] : '',
405 'projects' => upstream_get_users_projects($user['id']),
406 'role' => __('Client User', 'upstream'),
407 ];
408
409 $displayName = ! empty($user_data['full_name']) ? $user_data['full_name'] : $user_data['email'];
410 $user_data['display_name'] = $displayName;
411
412 if ($isBuddyPressRunning) {
413 $user_data['avatar'] = bp_core_fetch_avatar([
414 'item_id' => $user['id'],
415 'type' => 'thumb',
416 'html' => false,
417 ]);
418 } else {
419 if ( ! function_exists('get_avatar_url')) {
420 require_once ABSPATH . 'wp-includes/link-template.php';
421 }
422
423 $user_data['avatar'] = get_avatar_url(
424 $user['email'],
425 96,
426 get_option('avatar_default', 'mystery')
427 );
428 }
429 }
430 }
431 }
432
433 return $user_data;
434 }
435
436 function upstream_user_data($data = 0, $ignore_current = false) {
437
438 $res = Upstream_Cache::get_instance()->get('upstream_user_data'.$data.".".$ignore_current);
439
440 if ($res === false) {
441
442 $res = upstream_user_data_uncached($data, $ignore_current);
443 Upstream_Cache::get_instance()->set('upstream_user_data'.$data.".".$ignore_current, $res);
444 }
445 return $res;
446 }
447
448
449 // get a users email address from anything
450 // normalizes things as we can pass either nothing, or an id or an email.
451 function upstream_get_email_address($user = 0)
452 {
453
454 // if $user is already an email, simply return it
455 if (is_email($user)) {
456 return $user;
457 }
458
459 $email = null;
460
461 // this assumes that $user is a wordpress user id
462 if ($user != 0 && is_numeric($user)) {
463 $wp_user = get_user_by('id', $user);
464 $email = $wp_user->user_email;
465 }
466
467 // this assumes that $user is a client user id
468 if ($user != 0 && ! is_numeric($user)) {
469 $client_id = upstream_get_users_client_id($user);
470 $users = get_post_meta($client_id, '_upstream_client_users', true);
471 if (is_array($users) && count($users) > 0) :
472 foreach ($users as $key => $user) {
473 if ($user['id'] == $user) {
474 $email = $user['email'];
475 }
476 }
477 endif;
478 }
479
480 // this assumes we are a logged in wordpress user looking for our own info
481 if ( ! $user && upstream_user_type() == 'wp') {
482 $wp_user = get_user_by('id', get_current_user_id());
483 $email = $wp_user->user_email;
484 }
485
486 // this assumes we are a logged in client user looking for our own info
487 if ( ! $user && upstream_user_type() == 'client') {
488 if ( ! isset($_SESSION['upstream'])) {
489 return null;
490 }
491
492 $client_id = $_SESSION['upstream']['client_id'];
493 $user_id = $_SESSION['upstream']['user_id'];
494 $users = get_post_meta($client_id, '_upstream_client_users', true);
495 if (is_array($users) && count($users) > 0) :
496 foreach ($users as $key => $user) {
497 if ($user['id'] == $user_id) {
498 $email = isset($user['email']) ? $user['email'] : '';
499 }
500 }
501 endif;
502 }
503
504 return $email;
505 }
506
507
508 // gets a users name
509 // displays full name or email if no name set
510 function upstream_users_name($id = 0, $show_email = false)
511 {
512 $user = upstream_user_data($id, true);
513
514 if ( ! $user) {
515 return;
516 }
517
518 // if first name exists, then show name. Else show email.
519 $output = $user['display_name'];
520
521 if ($show_email && ! empty($user['email'])) {
522 $output .= " <a target='_blank' href='mailto:" . esc_html($user['email']) . "' title='" . esc_html($user['email']) . "'><span class='dashicons dashicons-email-alt'></span></a>";
523 }
524
525 return $output;
526 }
527
528
529 /**
530 * Retrieve all projects where the user has access to.
531 *
532 * @param numeric/WP_User $user The user to be checked.
533 *
534 * @return array
535 * @since 1.12.2
536 *
537 */
538 function upstream_get_users_projects($user)
539 {
540 $user = $user instanceof \WP_User ? $user : new \WP_User($user);
541 if ($user->ID === 0 && !apply_filters('upstream_permissions_filter_page_access', false)) {
542 return [];
543 }
544
545 $data = [];
546
547 $rowset = Upstream_Cache::get_instance()->get('upstream_get_users_projects');
548
549 if ($rowset === false) {
550 $rowset = (array)get_posts([
551 'post_type' => "project",
552 'post_status' => "publish",
553 'posts_per_page' => -1,
554 ]);
555 Upstream_Cache::get_instance()->set('upstream_get_users_projects', $rowset);
556 }
557
558 if (count($rowset) > 0) {
559 foreach ($rowset as $project) {
560 if (upstream_user_can_access_project($user, $project->ID)) {
561 $data[$project->ID] = $project;
562 }
563 }
564 }
565
566 return $data;
567 }
568
569
570 /**
571 * Returns percentages for use in dropdowns.
572 *
573 * @return
574 */
575 function upstream_get_percentages_for_dropdown()
576 {
577 $array = [
578 '' => '0%',
579 '5' => '5%',
580 '10' => '10%',
581 '15' => '15%',
582 '20' => '20%',
583 '25' => '25%',
584 '30' => '30%',
585 '35' => '35%',
586 '40' => '40%',
587 '45' => '45%',
588 '50' => '50%',
589 '55' => '55%',
590 '60' => '60%',
591 '65' => '65%',
592 '70' => '70%',
593 '75' => '75%',
594 '80' => '80%',
595 '85' => '85%',
596 '90' => '90%',
597 '95' => '95%',
598 '100' => '100%',
599 ];
600
601 return apply_filters('upstream_percentages', $array);
602 }
603
604 /*
605 * Run date formatting through here
606 */
607 function upstream_format_date($timestamp, $dateFormat = null)
608 {
609 if (empty($dateFormat)) {
610 $dateFormat = get_option('date_format', 'Y-m-d');
611 }
612
613 if ( ! $timestamp) {
614 $date = null;
615 } else {
616 $date = date_i18n($dateFormat, $timestamp);
617 }
618
619 return apply_filters('upstream_format_date', $date, $timestamp);
620 }
621
622 /*
623 * Convert date to unixtime format
624 *
625 * @return mixed
626 */
627 function upstream_date_unixtime($timestamp, $dateFormat = null)
628 {
629 // Return empty string if timestamp is empty.
630 if (is_string($timestamp)) {
631 $timestamp = trim($timestamp);
632 }
633
634 if (empty($timestamp)) {
635 return '';
636 }
637
638 if (is_null($dateFormat)) {
639 $dateFormat = get_option('date_format', 'Y-m-d');
640 }
641
642 $date = \DateTime::createFromFormat($dateFormat, $timestamp);
643
644 if ($date) {
645 $date = $date->format('U');
646 }
647
648 return apply_filters('upstream_date_mysql', $date, $timestamp);
649 }
650
651 /*
652 * Run time formatting through here
653 */
654 function upstream_format_time($timestamp)
655 {
656 if ( ! $timestamp) {
657 $time = null;
658 } else {
659 $time = date_i18n(get_option('time_format'), $timestamp, false);
660 }
661
662 return apply_filters('upstream_format_date', $time, $timestamp);
663 }
664
665 /*
666 * Used within class-up-project
667 */
668 function upstream_timestamp_from_date($value)
669 {
670
671 // if blank, return empty string
672 if ( ! $value || empty($value)) {
673 return '';
674 }
675
676 $timestamp = null;
677
678 // if already a timestamp, return the timestamp
679 if (is_numeric($value) && (int)$value == $value) {
680 $timestamp = $value;
681 }
682
683 if ( ! $timestamp) {
684 if (empty($value)) {
685 return 0;
686 }
687
688 $date_format = get_option('date_format');
689 $date = DateTime::createFromFormat($date_format, trim($value));
690
691 if ($date) {
692 $timestamp = $date->getTimestamp();
693 } else {
694 $date_object = date_create_from_format($date_format, $value);
695 $timestamp = $date_object ? $date_object->setTime(0, 0, 0)->getTimeStamp() : strtotime($value);
696 }
697 }
698
699 // returns the timestamp and sets it to the start of the day
700 return strtotime('today', $timestamp);
701 }
702
703 // function to convert date format
704 // pinched from CMB2
705 function upstream_php_to_js_dateformat()
706 {
707 $format = get_option('date_format');
708 $supported_options = [
709 'd' => 'dd', // Day, leading 0
710 'j' => 'd', // Day, no 0
711 'z' => 'o', // Day of the year, no leading zeroes,
712 // 'D' => 'D', // Day name short, not sure how it'll work with translations
713 // 'l' => 'DD', // Day name full, idem before
714 'm' => 'mm', // Month of the year, leading 0
715 'n' => 'm', // Month of the year, no leading 0
716 // 'M' => 'M', // Month, Short name
717 'F' => 'MM', // Month, full name,
718 'y' => 'y', // Year, two digit
719 'Y' => 'yy', // Year, full
720 'H' => 'HH', // Hour with leading 0 (24 hour)
721 'G' => 'H', // Hour with no leading 0 (24 hour)
722 'h' => 'hh', // Hour with leading 0 (12 hour)
723 'g' => 'h', // Hour with no leading 0 (12 hour),
724 'i' => 'mm', // Minute with leading 0,
725 's' => 'ss', // Second with leading 0,
726 'a' => 'tt', // am/pm
727 'A' => 'TT' // AM/PM
728 ];
729
730 foreach ($supported_options as $php => $js) {
731 // replaces every instance of a supported option, but skips escaped characters
732 $format = preg_replace("~(?<!\\\\)$php~", $js, $format);
733 }
734
735 $format = preg_replace_callback('~(?:\\\.)+~', 'upstream_wrap_escaped_chars', $format);
736
737 return $format;
738 }
739
740 function upstream_wrap_escaped_chars($value)
741 {
742 return "&#39;" . str_replace('\\', '', $value[0]) . "&#39;";
743 }
744
745 function upstream_filter_closed_items()
746 {
747 $option = get_option('upstream_general');
748
749 return isset($option['filter_closed_items']) ? (bool)$option['filter_closed_items'] : false;
750 }
751
752 function upstream_archive_closed_items()
753 {
754 $option = get_option('upstream_general');
755
756 return isset($option['archive_closed_items']) ? (bool)$option['archive_closed_items'] : true;
757 }
758
759 function upstream_show_users_name()
760 {
761 $option = get_option('upstream_general');
762
763 return isset($option['show_users_name']) ? (bool)$option['show_users_name'] : false;
764 }
765
766 function upstream_logo_url()
767 {
768 $option = get_option('upstream_general');
769 $logo = $option['logo'];
770
771 return apply_filters('upstream_logo', $logo);
772 }
773
774 function upstream_login_heading()
775 {
776 $option = get_option('upstream_general');
777
778 return isset($option['login_heading']) ? $option['login_heading'] : '';
779 }
780
781 function upstream_login_text()
782 {
783 $option = get_option('upstream_general');
784
785 return isset($option['login_text']) ? wp_kses_post(wpautop($option['login_text'])) : '';
786 }
787
788 function upstream_admin_email()
789 {
790 $option = get_option('upstream_general');
791
792 return isset($option['admin_email']) ? $option['admin_email'] : '';
793 }
794
795 /**
796 * Retrieve the `admin_support_link` option value.
797 *
798 * @param array $option Array of options. If provided, there's no need to fetch everything again from DB.
799 *
800 * @return string
801 * @since 1.12.0
802 *
803 * @see https://github.com/upstreamplugin/UpStream/issues/81
804 *
805 */
806 function upstream_admin_support($option)
807 {
808 if (empty($option)) {
809 $option = get_option('upstream_general');
810 }
811
812 if (isset($option['admin_support_link'])) {
813 return ! empty($option['admin_support_link']) ? $option['admin_support_link'] : 'mailto:' . $option['admin_email'];
814 } else {
815 return isset($option['admin_email']) ? $option['admin_email'] : '#';
816 }
817 }
818
819 /**
820 * Retrieve the `admin_support_link_label` option value.
821 *
822 * @param array $option Array of options. If provided, there's no need to fetch everything again from DB.
823 *
824 * @return string
825 * @since 1.12.0
826 *
827 * @see https://github.com/upstreamplugin/UpStream/issues/81
828 *
829 */
830 function upstream_admin_support_label($option)
831 {
832 if (empty($option)) {
833 $option = get_option('upstream_general');
834 }
835
836 if (isset($option['admin_support_label'])) {
837 return ! empty($option['admin_support_label']) ? $option['admin_support_label'] : '';
838 } else {
839 return __('Contact Admin', 'upstream');
840 }
841 }
842
843 /**
844 * Check if Milestones are disabled for the current open project.
845 * If no ID is passed, this function tries to guess it by checking $_GET/$_POST vars.
846 *
847 * @param int $post_id The project ID to be checked
848 *
849 * @return bool
850 * @since 1.8.0
851 *
852 */
853 function upstream_are_milestones_disabled($post_id = 0)
854 {
855 $areMilestonesDisabled = false;
856 $post_id = (int)$post_id;
857
858 if ($post_id <= 0) {
859 $post_id = (int)upstream_post_id();
860 }
861
862 if ($post_id > 0) {
863 $theMeta = get_post_meta($post_id, '_upstream_project_disable_milestones', false);
864 $areMilestonesDisabled = ! empty($theMeta) && $theMeta[0] === 'on';
865 }
866
867 return $areMilestonesDisabled;
868 }
869
870 /**
871 * Check if Tasks are disabled for the current open project.
872 * If no ID is passed, this function tries to guess it by checking $_GET/$_POST vars.
873 *
874 * @param int $post_id The project ID to be checked
875 *
876 * @return bool
877 * @since 1.8.0
878 *
879 */
880 function upstream_are_tasks_disabled($post_id = 0)
881 {
882 $areTasksDisabled = false;
883 $post_id = (int)$post_id;
884
885 if ($post_id <= 0) {
886 $post_id = (int)upstream_post_id();
887 }
888
889 if ($post_id > 0) {
890 $theMeta = get_post_meta($post_id, '_upstream_project_disable_tasks', false);
891 $areTasksDisabled = ! empty($theMeta) && $theMeta[0] === 'on';
892 }
893
894 return $areTasksDisabled;
895 }
896
897 /**
898 * Check if Bugs are disabled for the current open project.
899 * If no ID is passed, this function tries to guess it by checking $_GET/$_POST vars.
900 *
901 * @param int $post_id The project ID to be checked
902 *
903 * @return bool
904 * @since 1.8.0
905 *
906 */
907 function upstream_are_bugs_disabled($post_id = 0)
908 {
909 $areBugsDisabled = false;
910 $post_id = (int)$post_id;
911
912 if ($post_id <= 0) {
913 $post_id = (int)upstream_post_id();
914 }
915
916 if ($post_id > 0) {
917 $theMeta = get_post_meta($post_id, '_upstream_project_disable_bugs', false);
918 $areBugsDisabled = ! empty($theMeta) && $theMeta[0] === 'on';
919 }
920
921 return $areBugsDisabled;
922 }
923
924 /**
925 * Check if Files are disabled for the current open project.
926 * If no ID is passed, this function tries to guess it by checking $_GET/$_POST vars.
927 *
928 * @param int $post_id The project ID to be checked
929 *
930 * @return bool
931 * @since 1.8.0
932 *
933 */
934 function upstream_are_files_disabled($post_id = 0)
935 {
936 $areBugsDisabled = false;
937 $post_id = (int)$post_id;
938
939 if ($post_id <= 0) {
940 $post_id = (int)upstream_post_id();
941 }
942
943 if ($post_id > 0) {
944 $theMeta = get_post_meta($post_id, '_upstream_project_disable_files', false);
945 $areBugsDisabled = ! empty($theMeta) && $theMeta[0] === 'on';
946 }
947
948 return $areBugsDisabled;
949 }
950
951 function upstream_tinymce_quicktags_settings($tinyMCE)
952 {
953 if (preg_match('/^(?:_upstream_project_|description|notes|new_message)/i', $tinyMCE['id'])) {
954 $buttons = 'strong,em,link,del,ul,ol,li,close';
955
956 /**
957 * @param array $buttons
958 */
959 $buttons = apply_filters('upstream_tinymce_buttons', $buttons);
960
961 $tinyMCE['buttons'] = $buttons;
962 }
963
964 return $tinyMCE;
965 }
966
967 function upstream_tinymce_before_init_setup_toolbar($tinyMCE)
968 {
969 if ( ! isset($tinyMCE['selector'])) {
970 return $tinyMCE;
971 }
972
973 if (preg_match('/_upstream_project_|#description|#notes|#new_message|#upstream/i', $tinyMCE['selector'])) {
974 /**
975 * @param string $buttons
976 * @param string $toolbar
977 */
978 $tinyMCE['toolbar1'] = apply_filters(
979 'upstream_tinymce_toolbar',
980 'bold,italic,underline,strikethrough,bullist,numlist,link',
981 'toolbar1'
982 );
983
984 /**
985 * This filter is documented above.
986 */
987 $tinyMCE['toolbar2'] = apply_filters('upstream_tinymce_toolbar', '', 'toolbar2');
988 /**
989 * This filter is documented above.
990 */
991 $tinyMCE['toolbar3'] = apply_filters('upstream_tinymce_toolbar', '', 'toolbar3');
992 /**
993 * This filter is documented above.
994 */
995 $tinyMCE['toolbar4'] = apply_filters('upstream_tinymce_toolbar', '', 'toolbar4');
996 }
997
998 return $tinyMCE;
999 }
1000
1001 function upstream_tinymce_before_init($tinyMCE)
1002 {
1003 if ( ! isset($tinyMCE['selector'])) {
1004 return $tinyMCE;
1005 }
1006
1007 if (preg_match('/_upstream_project_|#description|#notes|#new_message|#upstream/i', $tinyMCE['selector'])) {
1008 if (isset($tinyMCE['plugins'])) {
1009 $pluginsToBeAdded = [
1010 'charmap',
1011 'hr',
1012 'media',
1013 'paste',
1014 'tabfocus',
1015 'textcolor',
1016 'wpautoresize',
1017 'wpemoji',
1018 'wpgallery',
1019 'wpdialogs',
1020 'wptextpattern',
1021 'wpview',
1022 ];
1023
1024 $pluginsList = explode(',', $tinyMCE['plugins']);
1025 $pluginsListUnique = array_unique(array_merge($pluginsList, $pluginsToBeAdded));
1026
1027 /**
1028 * @param array $pluginsList
1029 */
1030 $pluginsListUnique = apply_filters('upstream_tinymce_plugins', $pluginsListUnique);
1031
1032 $tinyMCE['plugins'] = implode(',', $pluginsListUnique);
1033 }
1034
1035 $externalPlugins = apply_filters('upstream_tinymce_external_plugins', []);
1036 $tinyMCE['external_plugins'] = wp_json_encode($externalPlugins);
1037 }
1038
1039 return $tinyMCE;
1040 }
1041
1042 function upstream_disable_tasks()
1043 {
1044 $options = get_option('upstream_general');
1045
1046 $disable_tasks = isset($options['disable_tasks']) ? (array)$options['disable_tasks'] : ['no'];
1047
1048 $areTasksDisabled = $disable_tasks[0] === 'yes';
1049
1050 return $areTasksDisabled;
1051 }
1052
1053 function upstream_disable_milestones()
1054 {
1055 $options = get_option('upstream_general');
1056
1057 $disable_milestones = isset($options['disable_milestones']) ? (array)$options['disable_milestones'] : ['no'];
1058
1059 $areMilestonesDisabled = $disable_milestones[0] === 'yes';
1060
1061 return $areMilestonesDisabled;
1062 }
1063
1064 function upstream_disable_milestone_categories()
1065 {
1066 $options = get_option('upstream_general');
1067
1068 $checked = isset($options['disable_milestone_categories']) ? (array)$options['disable_milestone_categories'] : [0];
1069
1070 return $checked[0] == 1;
1071 }
1072
1073 function upstream_disable_files()
1074 {
1075 $options = get_option('upstream_general');
1076
1077 $disable_files = isset($options['disable_files']) ? (array)$options['disable_files'] : ['no'];
1078
1079 $areFilesDisabled = $disable_files[0] === 'yes';
1080
1081 return $areFilesDisabled;
1082 }
1083
1084 /**
1085 * Apply OEmbed filters to a given string in an attempt to render potential embeddable content.
1086 * This function is called as a callback from CMB2 field method 'escape_cb'.
1087 *
1088 * @param mixed $content The unescaped content to be analyzed.
1089 * @param array $field_args Array of field arguments.
1090 * @param \CMB2_Field $field The field instance.
1091 *
1092 * @return mixed Escaped value to be displayed.
1093 * @see https://github.com/CMB2/CMB2/wiki/Field-Parameters#escape_cb
1094 *
1095 * @uses $wp_embed
1096 *
1097 * @since 1.10.0
1098 *
1099 */
1100 function applyOEmbedFiltersToWysiwygEditorContent($content, $field_args, $field)
1101 {
1102 global $wp_embed;
1103
1104 $content = (string)$content;
1105
1106 if (strlen($content) > 0) {
1107 $content = $wp_embed->autoembed($content);
1108 $content = $wp_embed->run_shortcode($content);
1109 $content = wpautop($content);
1110 $content = do_shortcode($content);
1111 }
1112
1113 return $content;
1114 }
1115
1116 /**
1117 * Check if Comments/Discussion are disabled for the current open project.
1118 * If no ID is passed, this function tries to guess it by checking $_GET/$_POST vars.
1119 *
1120 * @param int $post_id The project ID to be checked
1121 *
1122 * @param int $post_id The project ID to be checked
1123 *
1124 * @return bool
1125 * @since 1.8.0
1126 *
1127 */
1128 function upstream_are_comments_disabled($post_id = 0)
1129 {
1130 // General settings
1131 $pluginOptions = get_option('upstream_general');
1132 $disabled = isset($pluginOptions['disable_project_comments']) && (bool)$pluginOptions['disable_project_comments'] === false;
1133
1134 if ($disabled) {
1135 return true;
1136 }
1137
1138 // Project's settings
1139 $areCommentsDisabled = false;
1140 $post_id = (int)$post_id;
1141
1142 if ($post_id <= 0) {
1143 $post_id = (int)upstream_post_id();
1144 }
1145
1146 if ($post_id > 0) {
1147 $theMeta = get_post_meta($post_id, '_upstream_project_disable_comments', false);
1148 $areCommentsDisabled = ! empty($theMeta) && $theMeta[0] === 'on';
1149 }
1150
1151 return $areCommentsDisabled;
1152 }
1153
1154 /**
1155 * Check if Projects Categorization is currently disabled.
1156 *
1157 * @return bool
1158 * @since 1.12.0
1159 *
1160 */
1161 function is_project_categorization_disabled()
1162 {
1163 $options = get_option('upstream_general');
1164
1165 $isDisabled = isset($options['disable_categories']) ? (bool)$options['disable_categories'] : false;
1166
1167 return $isDisabled;
1168 }
1169
1170 /**
1171 * Check if Clients feature is disabled.
1172 *
1173 * @return bool
1174 * @since 1.12.0
1175 *
1176 */
1177 function is_clients_disabled()
1178 {
1179 $options = get_option('upstream_general');
1180
1181 $isDisabled = isset($options['disable_clients']) ? (bool)$options['disable_clients'] : false;
1182
1183 return $isDisabled;
1184 }
1185
1186 /**
1187 * Check if should Select Users by Default.
1188 *
1189 * @return bool
1190 */
1191 function select_users_by_default()
1192 {
1193 $options = get_option('upstream_general');
1194
1195 $enabled = isset($options['pre_select_users']) ? (bool)$options['pre_select_users'] : false;
1196
1197 return $enabled;
1198 }
1199
1200 /**
1201 * Retrieve the avatar URL from a given user.
1202 *
1203 * @param int $user_id The user ID.
1204 *
1205 * @return string|bool
1206 * @since 1.12.0
1207 *
1208 */
1209 function getUserAvatarURL($user_id)
1210 {
1211 $user_id = (int)$user_id;
1212 if ($user_id <= 0) {
1213 return false;
1214 }
1215
1216 if ( ! function_exists('is_plugin_active')) {
1217 include_once ABSPATH . 'wp-admin/includes/plugin.php';
1218 }
1219
1220 $avatarURL = "";
1221
1222 // Check if BuddyPress is running so we can borrow its functions.
1223 $isBuddyPressRunning = is_plugin_active('buddypress/bp-loader.php') && class_exists('BuddyPress') && function_exists('bp_core_fetch_avatar');
1224 if ($isBuddyPressRunning) {
1225 $avatarURL = (string)bp_core_fetch_avatar([
1226 'item_id' => $user_id,
1227 'type' => "thumb",
1228 'html' => false,
1229 ]);
1230 }
1231
1232 // Check if WP-User-Avatar is running so we can borrow its functions.
1233 if (empty($avatarURL) && is_plugin_active('wp-user-avatar/wp-user-avatar.php') && function_exists('wpua_functions_init')) {
1234 global $wp_query;
1235
1236 // Make sure WP_Query is loaded.
1237 if ( ! ($wp_query instanceof \WP_Query)) {
1238 $wp_query = new WP_Query();
1239 }
1240
1241 try {
1242 // Make sure WP User Avatar dependencies are loaded.
1243 require_once ABSPATH . 'wp-settings.php';
1244 require_once ABSPATH . 'wp-includes/pluggable.php';
1245 require_once ABSPATH . 'wp-includes/query.php';
1246 require_once WP_PLUGIN_DIR . '/wp-user-avatar/wp-user-avatar.php';
1247
1248 // Load WP User Avatar plugin and its dependencies.
1249 wpua_functions_init();
1250
1251 // Retrieve the current user avatar URL.
1252 $avatarURL = (string)get_wp_user_avatar_src($user_id);
1253 } catch (Exception $e) {
1254 // Do nothing.
1255 }
1256 }
1257
1258 // Check if Custom User Profile Photo is running so we can borrow its functions.
1259 if (empty($avatarURL) && is_plugin_active('custom-user-profile-photo/3five_cupp.php') && function_exists('get_cupp_meta')) {
1260 $avatarURL = (string)get_cupp_meta($user_id);
1261 }
1262
1263 if (empty($avatarURL)) {
1264 if ( ! function_exists('get_avatar_url')) {
1265 require_once ABSPATH . 'wp-includes/link-template.php';
1266 }
1267
1268 $avatarURL = (string)get_avatar_url($user_id, 96, get_option('avatar_default', 'mystery'));
1269 }
1270
1271 return $avatarURL;
1272 }
1273
1274 /**
1275 * Check if the current user is either administrator or UpStream Manager.
1276 *
1277 * @return bool
1278 * @since 1.12.0
1279 *
1280 */
1281 function isUserEitherManagerOrAdmin($user = null)
1282 {
1283 if (empty($user) || ! ($user instanceof \WP_User)) {
1284 $user = wp_get_current_user();
1285 }
1286
1287 if ($user->ID > 0 && isset($user->roles)) {
1288 return count(array_intersect((array)$user->roles, ['administrator', 'upstream_manager'])) > 0;
1289 }
1290
1291 return false;
1292 }
1293
1294 /**
1295 * Generates a random string of custom length.
1296 *
1297 * @param int $length The length of the random string.
1298 * @param string $charsPool The characters that might compose the string.
1299 *
1300 * @return string
1301 * @since 1.12.2
1302 *
1303 */
1304 function upstreamGenerateRandomString(
1305 $length,
1306 $charsPool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
1307 ) {
1308 $randomString = "";
1309 $maxCharsPoolLength = mb_strlen($charsPool, '8bit') - 1;
1310
1311 for ($lengthIndex = 0; $lengthIndex < $length; ++$lengthIndex) {
1312 $randomString .= $charsPool[random_int(0, $maxCharsPoolLength)];
1313 }
1314
1315 return $randomString;
1316 }
1317
1318 /**
1319 * Check if comments are allowed on projects.
1320 *
1321 * @return bool
1322 * @since 1.13.0
1323 *
1324 */
1325 function upstreamAreProjectCommentsEnabled()
1326 {
1327 // Retrieve UpStream general options.
1328 $options = get_option('upstream_general');
1329 $optionName = 'disable_project_comments';
1330 // Check if the option exists.
1331 if (isset($options[$optionName])) {
1332 $allow = (bool)$options[$optionName];
1333 } else {
1334 $legacyOptionName = 'disable_discussion';
1335 // Check if user has legacy option set.
1336 if (isset($options[$legacyOptionName])) {
1337 if (is_array($options[$legacyOptionName]) || is_object($options[$legacyOptionName])) {
1338 $options[$legacyOptionName] = json_decode(json_encode($options[$legacyOptionName]), true);
1339 if ( ! empty($options[$legacyOptionName])) {
1340 $options[$legacyOptionName] = array_reverse($options[$legacyOptionName]);
1341 $legacyOptionValue = array_pop($options[$legacyOptionName]);
1342 } else {
1343 $legacyOptionValue = "";
1344 }
1345 } else {
1346 $legacyOptionValue = (string)$options[$legacyOptionName];
1347 }
1348
1349 if (is_string($legacyOptionValue)) {
1350 $allow = strtoupper(trim($legacyOptionValue)) !== 'YES';
1351 } else {
1352 $allow = true;
1353 }
1354
1355 unset($options[$legacyOptionName]);
1356
1357 // Migrate existent legacy option.
1358 $options[$optionName] = (int) ! $allow;
1359
1360 // Update options.
1361 update_option('upstream_general', $options);
1362 } else {
1363 // Default value.
1364 $allow = true;
1365 }
1366 }
1367
1368 return $allow;
1369 }
1370
1371 /**
1372 * Check if comments are allowed on milestones.
1373 *
1374 * @return bool
1375 * @since 1.13.0
1376 *
1377 */
1378 function upstreamAreCommentsEnabledOnMilestones()
1379 {
1380 $options = get_option('upstream_general');
1381
1382 $optionName = 'disable_comments_on_milestones';
1383
1384 $allow = isset($options[$optionName]) ? (bool)$options[$optionName] : true;
1385
1386 return $allow;
1387 }
1388
1389 /**
1390 * Check if comments are allowed on tasks.
1391 *
1392 * @return bool
1393 * @since 1.13.0
1394 *
1395 */
1396 function upstreamAreCommentsEnabledOnTasks()
1397 {
1398 $options = get_option('upstream_general');
1399
1400 $optionName = 'disable_comments_on_tasks';
1401
1402 $allow = isset($options[$optionName]) ? (bool)$options[$optionName] : true;
1403
1404 return $allow;
1405 }
1406
1407 /**
1408 * Check if comments are allowed on bugs.
1409 *
1410 * @return bool
1411 * @since 1.13.0
1412 *
1413 */
1414 function upstreamAreCommentsEnabledOnBugs()
1415 {
1416 $options = get_option('upstream_general');
1417
1418 $optionName = 'disable_comments_on_bugs';
1419
1420 $allow = isset($options[$optionName]) ? (bool)$options[$optionName] : true;
1421
1422 return $allow;
1423 }
1424
1425 /**
1426 * Check if comments are allowed on files.
1427 *
1428 * @return bool
1429 * @since 1.13.0
1430 *
1431 */
1432 function upstreamAreCommentsEnabledOnFiles()
1433 {
1434 $options = get_option('upstream_general');
1435
1436 $optionName = 'disable_comments_on_files';
1437
1438 $allow = isset($options[$optionName]) ? (bool)$options[$optionName] : true;
1439
1440 return $allow;
1441 }
1442
1443 /**
1444 * Check if should show all the projects in the sidebar.
1445 *
1446 * @return bool
1447 * @since 1.13.0
1448 *
1449 */
1450 function upstreamShowAllProjectsInSidebar()
1451 {
1452 $options = get_option('upstream_general');
1453
1454 $optionName = 'show_all_projects_sidebar';
1455
1456 $allow = isset($options[$optionName]) ? (bool)$options[$optionName] : false;
1457
1458 return $allow;
1459 }
1460
1461
1462 /**
1463 * Check if should send emails on new comment.
1464 *
1465 * @return bool
1466 * @since 1.13.0
1467 *
1468 */
1469 function upstreamSendNotificationsForNewComments()
1470 {
1471 $options = get_option('upstream_general');
1472
1473 $optionName = 'send_notifications_for_new_comments';
1474
1475 $allow = isset($options[$optionName]) ? (bool)$options[$optionName] : true;
1476
1477 return $allow;
1478 }
1479
1480 /**
1481 * Slighted modification of PHP's native nl2br function.
1482 *
1483 * @param string $subject String to be processed.
1484 *
1485 * @return string
1486 * @since 1.13.1
1487 *
1488 */
1489 function upstream_nl2br($subject)
1490 {
1491 // Step 1: Add <br /> tags for each line-break.
1492 $subject = nl2br($subject);
1493
1494 // Step 2: Remove the actual line-breaks.
1495 $subject = str_replace("\n", "", $subject);
1496 $subject = str_replace("\r", "", $subject);
1497
1498 // Step 3: Restore the line-breaks that are inside <pre></pre> tags.
1499 if (preg_match_all('/\<pre\>(.*?)\<\/pre\>/', $subject, $match)) {
1500 foreach ($match as $a) {
1501 foreach ($a as $b) {
1502 $subject = str_replace(
1503 '<pre>' . $b . '</pre>',
1504 "<pre>" . str_replace("<br />", PHP_EOL, $b) . "</pre>",
1505 $subject
1506 );
1507 }
1508 }
1509 }
1510
1511 // Step 4: Removes extra <br /> tags.
1512
1513 // Before <pre> tags.
1514 $subject = str_replace("<br /><br /><br /><pre>", '<br /><br /><pre>', $subject);
1515 // After </pre> tags.
1516 $subject = str_replace("</pre><br /><br />", '</pre><br />', $subject);
1517
1518 // Arround <ul></ul> tags.
1519 $subject = str_replace("<br /><br /><ul>", '<br /><ul>', $subject);
1520 $subject = str_replace("</ul><br /><br />", '</ul><br />', $subject);
1521 // Inside <ul> </ul> tags.
1522 $subject = str_replace("<ul><br />", '<ul>', $subject);
1523 $subject = str_replace("<br /></ul>", '</ul>', $subject);
1524
1525 // Arround <ol></ol> tags.
1526 $subject = str_replace("<br /><br /><ol>", '<br /><ol>', $subject);
1527 $subject = str_replace("</ol><br /><br />", '</ol><br />', $subject);
1528 // Inside <ol> </ol> tags.
1529 $subject = str_replace("<ol><br />", '<ol>', $subject);
1530 $subject = str_replace("<br /></ol>", '</ol>', $subject);
1531
1532 // Arround <li></li> tags.
1533 $subject = str_replace("<br /><li>", '<li>', $subject);
1534 $subject = str_replace("</li><br />", '</li>', $subject);
1535
1536 return $subject;
1537 }
1538
1539 function upstreamShouldRunCmb2()
1540 {
1541 global $pagenow;
1542
1543 if ($pagenow === 'post.php'
1544 || $pagenow === 'post-new.php'
1545 ) {
1546 $post_id = isset($_GET['post']) ? (int)$_GET['post'] : 0;
1547 $postType = get_post_type($post_id);
1548 if (empty($postType)) {
1549 $postType = isset($_GET['post_type']) ? sanitize_text_field($_GET['post_type']) : '';
1550 if (empty($postType)
1551 && isset($_POST['post_type'])
1552 ) {
1553 // checked fr an actual valid post type later
1554 $postType = sanitize_text_field($_POST['post_type']);
1555 }
1556 }
1557
1558 $postTypesUsingCmb2 = apply_filters('upstream:post_types_using_cmb2', ['project', 'client']);
1559
1560 if (in_array($postType, $postTypesUsingCmb2)) {
1561 return true;
1562 }
1563 } elseif ($pagenow === 'admin.php'
1564 && isset($_GET['page'])
1565 && preg_match('/^upstream_/i', sanitize_text_field($_GET['page']))
1566 ) {
1567 return true;
1568 }
1569
1570 return false;
1571 }
1572
1573 function upstreamGetUsersMap()
1574 {
1575 $map = [];
1576
1577 $rowset = get_users([
1578 'fields' => ['ID', 'display_name'],
1579 ]);
1580
1581 foreach ($rowset as $user) {
1582 $map[(int)$user->ID] = $user->display_name;
1583 }
1584
1585 return $map;
1586 }
1587
1588 function upstreamGetDateFormatForJsDatepicker()
1589 {
1590 $format = get_option('date_format');
1591 $supported_options = [
1592 'd' => 'dd', // Day, leading 0
1593 'j' => 'd', // Day, no 0
1594 'z' => 'o', // Day of the year, no leading zeroes,
1595 // 'D' => 'D', // Day name short, not sure how it'll work with translations
1596 // 'l' => 'DD', // Day name full, idem before
1597 'm' => 'mm', // Month of the year, leading 0
1598 'n' => 'm', // Month of the year, no leading 0
1599 // 'M' => 'M', // Month, Short name
1600 'F' => 'MM', // Month, full name,
1601 'y' => 'yy', // Year, two digit
1602 'Y' => 'yyyy', // Year, full
1603 'H' => 'HH', // Hour with leading 0 (24 hour)
1604 'G' => 'H', // Hour with no leading 0 (24 hour)
1605 'h' => 'hh', // Hour with leading 0 (12 hour)
1606 'g' => 'h', // Hour with no leading 0 (12 hour),
1607 'i' => 'mm', // Minute with leading 0,
1608 's' => 'ss', // Second with leading 0,
1609 'a' => 'tt', // am/pm
1610 'A' => 'TT', // AM/PM
1611 'S' => '', // th, rd, st
1612 ];
1613
1614 foreach ($supported_options as $php => $js) {
1615 // replaces every instance of a supported option, but skips escaped characters
1616 $format = preg_replace("~(?<!\\\\)$php~", $js, $format);
1617 }
1618
1619 $format = preg_replace_callback('~(?:\\\.)+~', 'upstream_wrap_escaped_chars', $format);
1620
1621 return $format;
1622 }
1623
1624 function userCanReceiveCommentRepliesNotification($user_id = 0)
1625 {
1626 if ( ! is_numeric($user_id)) {
1627 return false;
1628 }
1629
1630 if ((int)$user_id <= 0) {
1631 $user_id = get_current_user_id();
1632 }
1633
1634 $receiveNotifications = get_user_meta($user_id, 'upstream_comment_replies_notification', true) !== 'no';
1635
1636 return $receiveNotifications;
1637 }
1638
1639 /**
1640 * Retrieve a list of Milestones available on this instance.
1641 *
1642 * @return array
1643 * @since 1.17.0
1644 *
1645 */
1646 function getMilestones()
1647 {
1648 $posts = get_posts(
1649 [
1650 'post_status' => 'publish',
1651 'posts_per_page' => -1,
1652 'post_type' => 'upst_milestone',
1653 'orderby' => 'menu_order',
1654 'order' => 'ASC',
1655 ]
1656 );
1657
1658 $milestones = [];
1659
1660 if ( ! empty($posts)) {
1661 foreach ($posts as $post) {
1662 $data = $post;
1663 $milestones[$post->ID] = $data;
1664 }
1665 }
1666
1667 return $milestones;
1668
1669 }
1670
1671 /**
1672 * Retrieve a list of Milestones titles available on this instance.
1673 *
1674 * @return array
1675 * @since 1.17.0
1676 *
1677 */
1678 function getMilestonesTitles()
1679 {
1680 $data = [];
1681
1682 $milestones = getMilestones();
1683 foreach ($milestones as $milestone) {
1684 if (isset($milestone->ID)) {
1685 $data[$milestone->ID] = $milestone->post_title;
1686 }
1687 }
1688
1689 return $data;
1690 }
1691
1692 /**
1693 * Retrieve a list of Tasks available on this instance.
1694 *
1695 * @return array
1696 * @since 1.17.0
1697 *
1698 */
1699 function getTasksStatuses()
1700 {
1701 $data = [];
1702
1703 $tasks = (array)get_option('upstream_tasks');
1704 if (isset($tasks['statuses'])) {
1705 foreach ($tasks['statuses'] as $index => $task) {
1706 if (isset($task['id'])) {
1707 $task['order'] = $index;
1708
1709 $data[$task['id']] = $task;
1710 }
1711 }
1712 }
1713
1714 return $data;
1715 }
1716
1717 /**
1718 * Retrieve a list of Task statuses titles available on this instance.
1719 *
1720 * @return array
1721 * @since 1.17.0
1722 *
1723 */
1724 function getTasksStatusesTitles()
1725 {
1726 $data = [];
1727
1728 $tasks = getTasksStatuses();
1729 foreach ($tasks as $task) {
1730 if (isset($task['id'])) {
1731 $data[$task['id']] = $task['name'];
1732 }
1733 }
1734
1735 return $data;
1736 }
1737
1738
1739 function getBugsStatuses()
1740 {
1741 $data = [];
1742
1743 $bugs = (array)get_option('upstream_bugs');
1744 if (isset($bugs['statuses'])) {
1745 foreach ($bugs['statuses'] as $index => $bugStatus) {
1746 if (isset($bugStatus['id'])) {
1747 $bugStatus['order'] = $index;
1748
1749 $data[$bugStatus['id']] = $bugStatus;
1750 }
1751 }
1752 }
1753
1754 return $data;
1755 }
1756
1757 function getBugsSeverities()
1758 {
1759 $data = [];
1760
1761 $bugs = (array)get_option('upstream_bugs');
1762 if (isset($bugs['severities'])) {
1763 foreach ($bugs['severities'] as $index => $bugSeverity) {
1764 if (isset($bugSeverity['id'])) {
1765 $bugSeverity['order'] = $index;
1766
1767 $data[$bugSeverity['id']] = $bugSeverity;
1768 }
1769 }
1770 }
1771
1772 return $data;
1773 }
1774
1775 function upstream_media_unrestricted_roles()
1776 {
1777 $option = get_option('upstream_general');
1778
1779 return isset($option['media_unrestricted_roles']) ? $option['media_unrestricted_roles'] : ['administrator'];
1780 }
1781
1782
1783 /**
1784 * DEPRECATED
1785 */
1786
1787 /**
1788 * Retrieve a DateTimeZone object of the current WP's timezone.
1789 * This function falls back to UTC in case of an invalid/empty timezone option.
1790 *
1791 * @return \DateTimeZone
1792 * @deprecated
1793 *
1794 * @since 1.12.3
1795 */
1796 function upstreamGetTimeZone()
1797 {
1798 $tz = (string)get_option('timezone_string');
1799
1800 try {
1801 $theTimeZone = new DateTimeZone($tz);
1802 } catch (Exception $e) {
1803 $theTimeZone = new DateTimeZone('UTC');
1804 }
1805
1806 return $theTimeZone;
1807 }
1808
1809 /**
1810 * Convert a given date (UTC)/timestamp to the instance's timezone.
1811 *
1812 * @param int|string $subject The date to be converted. If int, assume it's a timestamp.
1813 *
1814 * @return string|false The converted string or false in case of failure.
1815 * @since 1.11.0
1816 * @deprecated
1817 *
1818 */
1819 function upstream_convert_UTC_date_to_timezone($subject, $includeTime = true)
1820 {
1821 try {
1822 $dateFormat = get_option('date_format');
1823
1824 if ($includeTime === true) {
1825 $dateFormat .= ' ' . get_option('time_format');
1826 }
1827
1828 if (is_numeric($subject)) {
1829 $theDate = new DateTime();
1830 $theDate->setTimestamp($subject);
1831 } else {
1832 $theDate = new DateTime($subject);
1833 }
1834
1835 $instanceTimezone = upstreamGetTimeZone();
1836 $theDate->setTimeZone($instanceTimezone);
1837
1838 return $theDate->format($dateFormat);
1839 } catch (Exception $e) {
1840 return false;
1841 }
1842 }
1843
1844 /**
1845 * @param array $users
1846 *
1847 * @return string
1848 */
1849 function upstream_get_users_display_name($users)
1850 {
1851 return upstream_get_users_display_name_cached($users);
1852
1853 $html = 0;
1854
1855 $usersIds = array_filter(array_unique($users));
1856 $usersCount = count($usersIds);
1857
1858 if ($usersCount > 0) {
1859 $users = get_users([
1860 'include' => $usersIds,
1861 ]);
1862
1863 $columnValue = [];
1864 foreach ($users as $user) {
1865 $columnValue[] = esc_html($user->display_name);
1866 }
1867 unset($user, $users);
1868
1869 $html = implode(',<br>', $columnValue);
1870 }
1871
1872 unset($usersCount, $usersIds);
1873
1874 return $html;
1875 }
1876
1877 function upstream_get_users_display_name_cached($users)
1878 {
1879 $allusers = Upstream_Cache::get_instance()->get('upstream_get_users_display_name_cached');
1880 if ($allusers === false) {
1881 $allusers = get_users();
1882 Upstream_Cache::get_instance()->set('upstream_get_users_display_name_cached', $allusers);
1883 }
1884
1885 $userhash = [];
1886 foreach ($users as $u) {
1887 $userhash[$u] = true;
1888 }
1889
1890 $allusernames = [];
1891 foreach ($allusers as $user) {
1892 if (isset($userhash[$user->ID])) {
1893 $allusernames[] = $user->display_name;
1894 }
1895 }
1896
1897 return implode('<br>', $allusernames);
1898 }
1899
1900 /**
1901 * @return array
1902 * @deprecated
1903 */
1904 function upstream_admin_get_options_milestones()
1905 {
1906 return upstream_project_milestones();
1907 }
1908