PluginProbe
UpStream: a Project Management Plugin for WordPress / trunk
UpStream: a Project Management Plugin for WordPress vtrunk
trunk 1.39.0 1.39.1 1.39.2 1.39.3 2.0.7 2.1.0
← All changes | class-upstream.php +600 -597 2.0.7trunk View file →
@@ -1,597 +1,600 @@
1 -<?php
2 -/**
3 - * Main UpStream Class.
4 - *
5 - * @package UpStream
6 - */
7 -
8 -use UpStream\Comments;
9 -
10 -if ( ! defined( 'ABSPATH' ) ) {
11 - exit;
12 -}
13 -
14 -if ( ! class_exists( 'UpStream' ) ) :
15 -
16 - /**
17 - * Main UpStream Class.
18 - *
19 - * @since 1.0.0
20 - */
21 - final class UpStream {
22 -
23 - /**
24 - * The one true UpStream
25 - *
26 - * @var UpStream
27 - * @since 1.0.0
28 - */
29 - protected static $instance = null;
30 -
31 - /**
32 - * Twig Environment
33 - *
34 - * @var Twig_Environment
35 - */
36 - protected $twig;
37 -
38 - /**
39 - * Container
40 - *
41 - * @var Container
42 - */
43 - protected $container;
44 -
45 - /**
46 - * Main UpStream Instance.
47 - */
48 - public static function instance() {
49 - if ( is_null( self::$instance ) ) {
50 - self::$instance = new self();
51 - }
52 -
53 - return self::$instance;
54 - }
55 -
56 - /**
57 - * Throw error on object clone.
58 - *
59 - * The whole idea of the singleton design pattern is that there is a single
60 - * object therefore, we don't want the object to be cloned.
61 - *
62 - * @since 1.0.0
63 - */
64 - public function __clone() {
65 - _doing_it_wrong( __FUNCTION__, 'You\'re not supposed to clone this class.', esc_html( UPSTREAM_VERSION ) );
66 - }
67 -
68 - /**
69 - * Disable unserializing of the class.
70 - *
71 - * @since 1.0.0
72 - */
73 - public function __wakeup() {
74 - _doing_it_wrong( __FUNCTION__, 'You\'re not supposed to unserialize this class.', esc_html( UPSTREAM_VERSION ) );
75 - }
76 -
77 - /**
78 - * Prevent the class instance being serialized.
79 - *
80 - * @since 1.10.2
81 - */
82 - public function __sleep() {
83 - _doing_it_wrong( __FUNCTION__, 'You\'re not supposed to serialize this class.', esc_html( UPSTREAM_VERSION ) );
84 - }
85 -
86 - /**
87 - * Class constructor.
88 - */
89 - public function __construct() {
90 - $this->define_constants();
91 - $this->includes();
92 - $this->container = Container::get_instance();
93 - $this->init_framework();
94 -
95 - if ( UpStream_Debug::is_enabled() ) {
96 - UpStream_Debug::init();
97 - }
98 -
99 - $this->init_hooks();
100 -
101 - do_action( 'upstream_loaded' );
102 - }
103 -
104 - /**
105 - * Hook into actions and filters.
106 - *
107 - * @since 1.0.0
108 - */
109 - private function init_hooks() {
110 - add_action( 'init', array( $this, 'init' ) );
111 - add_filter( 'plugin_row_meta', array( $this, 'plugin_row_meta' ), 10, 2 );
112 - add_filter( 'plugin_action_links_upstream/upstream.php', array( $this, 'handle_action_links' ) );
113 - add_filter( 'http_request_host_is_external', array( 'UpStream', 'allow_external_update_host' ), 10, 3 );
114 - add_filter( 'quicktags_settings', 'upstream_tinymce_quicktags_settings' );
115 - add_filter( 'tiny_mce_before_init', 'upstream_tinymce_before_init_setup_toolbar' );
116 - add_filter( 'tiny_mce_before_init', 'upstream_tinymce_before_init' );
117 - add_filter( 'teeny_mce_before_init', 'upstream_tinymce_before_init_setup_toolbar' );
118 - add_filter( 'comments_clauses', array( $this, 'filter_comments_on_dashboard' ), 10, 2 );
119 - add_filter( 'views_dashboard', array( 'UpStream_Admin', 'comment_status_links' ), 10, 1 );
120 - add_action( 'plugins_loaded', array( $this, 'load_plugin_textdomain' ) );
121 -
122 - if ( is_admin() ) {
123 - add_action( 'admin_init', array( $this->container['reviews'], 'init' ) );
124 - }
125 -
126 - global $pagenow;
127 -
128 - if ( 'plugins.php' === $pagenow ) {
129 - add_action(
130 - 'in_plugin_update_message-' . UPSTREAM_PLUGIN_BASENAME,
131 - array( $this, 'render_additional_update_info' ),
132 - 20,
133 - 2
134 - );
135 - }
136 - }
137 -
138 - /**
139 - * Initialize the Alledia Framework.
140 - */
141 - private function init_framework() {
142 - $this->container['framework']->init();
143 - }
144 -
145 -
146 - /**
147 - * Prevent a Client User from accessing any page other than the profile.
148 - *
149 - * @since 1.11.0
150 - *
151 - * @global $pagenow
152 - */
153 - public function limit_client_users_admin_access() {
154 - global $pagenow;
155 -
156 - $profile_age = 'profile.php';
157 - if ( $pagenow !== $profile_age && 'edit.php' !== $pagenow && ! wp_doing_ajax() ) {
158 - wp_safe_redirect( admin_url( $profile_age ) );
159 - exit();
160 - }
161 - }
162 -
163 - /**
164 - * Make sure Client Users can only see the Profile menu item.
165 - *
166 - * @since 1.11.0
167 - *
168 - * @global $menu
169 - */
170 - public function limit_client_users_menu() {
171 - global $menu;
172 -
173 - foreach ( $menu as $menu_index => $menu_data ) {
174 - $menu_file = isset( $menu_data[2] ) ? $menu_data[2] : null;
175 -
176 - if ( null !== $menu_file ) {
177 - if ( 'profile.php' === $menu_file || 'edit.php?post_type=project' === $menu_file ) {
178 - continue;
179 - }
180 -
181 - remove_menu_page( $menu_file );
182 - }
183 - }
184 - }
185 -
186 - /**
187 - * Hide some toolbar items from Client Users.
188 - *
189 - * @param \WP_Admin_Bar $wp_admin_bar WordPress admin bar.
190 - *
191 - * @since 1.11.0
192 - */
193 - public function limit_client_users_toolbar_items( $wp_admin_bar ) {
194 - $user = wp_get_current_user();
195 - $user_roles = (array) $user->roles;
196 -
197 - if ( count( array_intersect( $user_roles, array( 'administrator', 'upstream_manager' ) ) ) === 0
198 - && in_array( 'upstream_client_user', $user_roles, true )
199 - ) {
200 - $menu_items = array( 'about', 'comments', 'new-content' );
201 -
202 - if ( ! is_admin() ) {
203 - $menu_items = array_merge( $menu_items, array( 'dashboard', 'edit' ) );
204 - }
205 -
206 - foreach ( $menu_items as $menu_item ) {
207 - $wp_admin_bar->remove_menu( $menu_item );
208 - }
209 - }
210 - }
211 -
212 - /**
213 - * Get container.
214 - *
215 - * @return Container
216 - */
217 - public function get_container() {
218 - return $this->container;
219 - }
220 -
221 - /**
222 - * Define Constants.
223 - *
224 - * @since 1.0.0
225 - */
226 - private function define_constants() {
227 - $upload_dir = wp_upload_dir();
228 -
229 - $this->define( 'UPSTREAM_PLUGIN_DIR', plugin_dir_path( UPSTREAM_PLUGIN_FILE ) );
230 - $this->define( 'UPSTREAM_PLUGIN_URL', plugin_dir_url( UPSTREAM_PLUGIN_FILE ) );
231 - $this->define( 'UPSTREAM_PLUGIN_BASENAME', plugin_basename( UPSTREAM_PLUGIN_FILE ) );
232 - $this->define( 'UPSTREAM_PLUGIN_RELATIVE_PATH', 'upstream' );
233 -
234 - include_once __DIR__ . '/includes.php';
235 - }
236 -
237 - /**
238 - * Define constant if not already set.
239 - *
240 - * @param string $name Definition name.
241 - * @param string|bool $value Definition value.
242 - *
243 - * @since 1.0.0
244 - */
245 - private function define( $name, $value ) {
246 - if ( ! defined( $name ) ) {
247 - define( $name, $value );
248 - }
249 - }
250 -
251 - /**
252 - * What type of request is this?
253 - * string $type frontend or admin.
254 - *
255 - * @param string $type Request type.
256 - * @return bool
257 - * @since 1.0.0
258 - */
259 - private function is_request( $type ) {
260 - switch ( $type ) {
261 - case 'admin':
262 - return is_admin();
263 - case 'frontend':
264 - return ( ! is_admin() || defined( 'DOING_AJAX' ) ) && ! defined( 'DOING_CRON' );
265 - }
266 - }
267 -
268 - /**
269 - * Include required core files used in admin and on the frontend.
270 - *
271 - * @since 1.0.0
272 - */
273 - public function includes() {
274 - if ( file_exists( __DIR__ . '/vendor/autoload.php' ) ) {
275 - require_once __DIR__ . '/vendor/autoload.php';
276 - }
277 -
278 - include_once __DIR__ . '/includes/class-exception.php';
279 - include_once __DIR__ . '/includes/trait-up-singleton.php';
280 - include_once __DIR__ . '/includes/trait-up-post-metadata.php';
281 - include_once __DIR__ . '/includes/class-struct.php';
282 - include_once __DIR__ . '/includes/class-upstream-debug.php';
283 - include_once __DIR__ . '/includes/class-container.php';
284 - include_once __DIR__ . '/includes/up-install.php';
285 - include_once __DIR__ . '/includes/class-upstream-autoloader.php';
286 - include_once __DIR__ . '/includes/class-upstream-roles.php';
287 - include_once __DIR__ . '/includes/class-upstream-counts.php';
288 - include_once __DIR__ . '/includes/class-upstream-counter.php';
289 - include_once __DIR__ . '/includes/class-upstream-project-activity.php';
290 - include_once __DIR__ . '/includes/up-permalinks.php';
291 - include_once __DIR__ . '/includes/up-general-functions.php';
292 - include_once __DIR__ . '/includes/up-post-types.php';
293 - include_once __DIR__ . '/includes/up-labels.php';
294 - include_once __DIR__ . '/includes/class-milestones.php';
295 - include_once __DIR__ . '/includes/class-milestone.php';
296 - include_once __DIR__ . '/includes/class-factory.php';
297 - include_once __DIR__ . '/includes/up-install.php';
298 - include_once __DIR__ . '/includes/up-filesystem.php';
299 - include_once __DIR__ . '/includes/up-register-nonce-fields.php';
300 - include_once __DIR__ . '/includes/up-wcs-helper.php';
301 -
302 - $request = wp_unslash( $_REQUEST );
303 -
304 - if ( $this->is_request( 'admin' ) ) {
305 - global $pagenow;
306 -
307 - $is_multisite = (bool) is_multisite();
308 - $load_cmb2 = false;
309 - $server = wp_unslash( $_SERVER );
310 -
311 - if ( $is_multisite ) {
312 - $current_page = isset( $_SERVER['PHP_SELF'] ) ? preg_replace(
313 - '/^\/wp-admin\//i',
314 - '',
315 - sanitize_text_field( $server['PHP_SELF'] )
316 - ) : '';
317 - } else {
318 - $current_page = (string) $pagenow;
319 - }
320 -
321 - if ( in_array( $current_page, array( 'post.php', 'post-new.php' ), true ) ) {
322 - $post_type = isset( $request['post_type'] ) ? sanitize_text_field( $request['post_type'] ) : null;
323 -
324 - if ( empty( $post_type ) ) {
325 - $project_id = isset( $request['post'] ) ? absint( $request['post'] ) : 0;
326 - $post_type = get_post_type( $project_id );
327 - }
328 -
329 - if ( ! empty( $post_type ) ) {
330 - $post_types_using_cmb2 = apply_filters( 'upstream:post_types_using_cmb2', array( 'project', 'client' ) );
331 - $load_cmb2 = in_array( $post_type, $post_types_using_cmb2, true );
332 - }
333 - } elseif (
334 - 'admin.php' === $current_page
335 - && isset( $request['page'] )
336 - && preg_match( '/^upstream_/i', sanitize_text_field( $request['page'] ) )
337 - ) {
338 - $load_cmb2 = true;
339 - }
340 -
341 - if ( $load_cmb2 ) {
342 - include_once __DIR__ . '/includes/libraries/cmb2/init.php';
343 - include_once __DIR__ . '/includes/libraries/cmb2-grid/Cmb2GridPlugin.php';
344 - }
345 -
346 - include_once __DIR__ . '/includes/admin/class-upstream-admin.php';
347 - include_once __DIR__ . '/includes/admin/class-upstream-admin-tasks-page.php';
348 - include_once __DIR__ . '/includes/admin/class-upstream-admin-bugs-page.php';
349 - include_once __DIR__ . '/includes/admin/class-upstream-admin-reviews.php';
350 - }
351 -
352 - if ( $this->is_request( 'frontend' ) ) {
353 - include_once __DIR__ . '/includes/frontend/class-upstream-template-loader.php';
354 - include_once __DIR__ . '/includes/frontend/class-upstream-login.php';
355 - include_once __DIR__ . '/includes/frontend/class-upstream-style-output.php';
356 - include_once __DIR__ . '/includes/frontend/up-enqueues.php';
357 - include_once __DIR__ . '/includes/frontend/up-template-functions.php';
358 - include_once __DIR__ . '/includes/frontend/up-table-functions.php';
359 - include_once __DIR__ . '/includes/frontend/class-upstream-view.php';
360 - include_once __DIR__ . '/includes/frontend/class-upstream-ajax.php';
361 - }
362 -
363 - include_once __DIR__ . '/includes/up-project-functions.php';
364 - include_once __DIR__ . '/includes/up-client-functions.php';
365 - include_once __DIR__ . '/includes/up-permissions-functions.php';
366 - include_once __DIR__ . '/includes/class-comments-migration.php';
367 - include_once __DIR__ . '/includes/class-comments.php';
368 - include_once __DIR__ . '/includes/class-comment.php';
369 - }
370 -
371 - /**
372 - * Init UpStream when WordPress Initialises.
373 - */
374 - public function init() {
375 - UpStream\Milestones::instantiate();
376 -
377 - do_action( 'before_upstream_init' );
378 -
379 - $this->project = new UpStream_Project();
380 - $this->project_activity = new UpStream_Project_Activity();
381 -
382 - if ( version_compare( PHP_VERSION, '5.5', '<' ) ) {
383 - require_once UPSTREAM_PLUGIN_DIR . 'includes/libraries/password_compat-1.0.4/lib/password.php';
384 - }
385 -
386 - \UpStream\Migrations\Comments_Migration::run();
387 -
388 - $user = wp_get_current_user();
389 - $user_roles = (array) $user->roles;
390 -
391 - if (
392 - count( array_intersect( $user_roles, array( 'administrator', 'upstream_manager' ) ) ) === 0
393 - && in_array( 'upstream_client_user', $user_roles, true )
394 - ) {
395 - add_filter( 'admin_init', array( $this, 'limit_client_users_admin_access' ) );
396 - add_filter( 'admin_head', array( $this, 'limit_client_users_menu' ) );
397 - add_action( 'admin_bar_menu', array( $this, 'limit_client_users_toolbar_items' ), 999 );
398 - }
399 -
400 - $edit_other_projects_permission_were_removed = (bool) get_option( 'upstream:role_upstream_users:drop_edit_others_projects' );
401 -
402 - if ( ! $edit_other_projects_permission_were_removed ) {
403 - $role = get_role( 'upstream_user' );
404 -
405 - if ( $role ) {
406 - $role->remove_cap( 'edit_others_projects' );
407 - }
408 -
409 - unset( $role );
410 - update_option( 'upstream:role_upstream_users:drop_edit_others_projects', 1 );
411 - }
412 -
413 - UpStream_Options_Projects::create_projects_statuses_ids();
414 - UpStream_Options_Tasks::create_tasks_statuses_ids();
415 - UpStream_Options_Bugs::create_bugs_statuses_ids();
416 -
417 - Comments::instantiate();
418 -
419 - if ( $this->is_request( 'frontend' ) ) {
420 - UpStream_Ajax::instantiate();
421 - }
422 -
423 - do_action( 'upstream_init' );
424 - }
425 -
426 - /**
427 - * Load Localisation files.
428 - */
429 - public function load_plugin_textdomain() {
430 - load_plugin_textdomain( 'upstream', false, UPSTREAM_PLUGIN_RELATIVE_PATH . '/languages/' );
431 - }
432 -
433 -
434 - /**
435 - * Show row meta on the plugin screen.
436 - *
437 - * @param mixed $links Plugin Row Meta.
438 - * @param mixed $file Plugin Base file.
439 - *
440 - * @return array
441 - */
442 - public function plugin_row_meta( $links, $file ) {
443 - if ( UPSTREAM_PLUGIN_BASENAME === $file ) {
444 - $row_meta = array(
445 - 'docs' => sprintf(
446 - '<a href="%s" title="%s">%s</a>',
447 - esc_url( 'http://upstreamplugin.com/documentation' ),
448 - esc_attr__( 'View Documentation', 'upstream' ),
449 - esc_html__( 'Docs', 'upstream' )
450 - ),
451 - 'quick-start' => sprintf(
452 - '<a href="%s" title="%s">%s</a>',
453 - esc_url( 'http://upstreamplugin.com/quick-start-guide' ),
454 - esc_attr__( 'View Quick Start Guide', 'upstream' ),
455 - esc_html__( 'Quick Start Guide', 'upstream' )
456 - ),
457 - );
458 -
459 - return array_merge( $links, $row_meta );
460 - }
461 -
462 - return (array) $links;
463 - }
464 -
465 - /**
466 - * Callback called to setup the links to display on the plugins page, besides active/deactivate links.
467 - *
468 - * @param array $links The list of links to be displayed.
469 - *
470 - * @return array
471 - * @since 1.11.1
472 - * @static
473 - */
474 - public static function handle_action_links( $links ) {
475 - $links['settings'] = sprintf(
476 - '<a href="%s" title="%2$s" aria-label="%2$s">%3$s</a>',
477 - esc_url( admin_url( 'admin.php?page=upstream_general' ) ),
478 - esc_attr__( 'Open Settings Page', 'upstream' ),
479 - esc_html__( 'Settings', 'upstream' )
480 - );
481 -
482 - return $links;
483 - }
484 -
485 - /**
486 - * Ensures the plugins update API's host is whitelisted to WordPress external requests.
487 - *
488 - * @param boolean $is_allowed Is allowed or not.
489 - * @param string $host Host.
490 - * @param string $url Url.
491 - *
492 - * @return boolean
493 - * @since 1.11.1
494 - * @static
495 - */
496 - public static function allow_external_update_host( $is_allowed, $host, $url ) {
497 - if ( 'upstreamplugin.com' === $host ) {
498 - return true;
499 - }
500 -
501 - return $is_allowed;
502 - }
503 -
504 - /**
505 - * Render additional update info if needed.
506 - *
507 - * @param array $plugin_data Plugin metadata.
508 - * @param object $response Metadata about the available plugin update.
509 - *
510 - * @since 1.12.5
511 - * @static
512 - *
513 - * @see https://developer.wordpress.org/reference/hooks/in_plugin_update_message-file
514 - */
515 - public static function render_additional_update_info( $plugin_data, $response ) {
516 - $update_notice_title_html = sprintf(
517 - '<strong style="font-size: 1.25em; display: block; margin-top: 10px;">%s</strong>',
518 - esc_html__( 'Update notice:', 'upstream' )
519 - );
520 -
521 - if ( version_compare( UPSTREAM_VERSION, '1.12.5', '<' ) ) {
522 - printf(
523 - esc_html( $update_notice_title_html ) .
524 - // translators: '%1$s: plugin version, %2$s: capability name, %3$s: UpStream User role'.
525 - esc_html__(
526 - 'Starting from <strong>%1$s</strong> <code>%2$s</code> capability was removed from <code>%3$s</code> users role.',
527 - 'upstream'
528 - ),
529 - 'v1.12.5',
530 - 'edit_others_projects',
531 - esc_html__( 'UpStream User', 'upstream' )
532 - );
533 - }
534 - }
535 -
536 - /**
537 - * Make sure Recent Comments section on admin Dashboard display only comments
538 - * current user is allowed to see from projects he's allowed to access.
539 - *
540 - * @param array $query_args Query clauses.
541 - * @param WP_Comment_Query $query Current query instance.
542 - *
543 - * @return array $queryArgs
544 - * @global $pagenow, $wpdb
545 - *
546 - * @since 1.13.0
547 - * @static
548 - */
549 - public static function filter_comments_on_dashboard( $query_args, $query ) {
550 - global $pagenow;
551 -
552 - if ( is_admin() && 'index.php' === $pagenow && ! upstream_is_user_either_manager_or_admin() ) {
553 - global $wpdb;
554 -
555 - $query_args['join'] = 'LEFT JOIN ' . $wpdb->prefix . 'posts AS post ON post.ID = ' . $wpdb->prefix . 'comments.comment_post_ID';
556 - $user = wp_get_current_user();
557 -
558 - if ( in_array( 'upstream_user', $user->roles, true ) || in_array( 'upstream_client_user', $user->roles, true ) ) {
559 - $projects = (array) upstream_get_users_projects( $user );
560 -
561 - if ( count( $projects ) === 0 ) {
562 - $query_args['where'] = '(post.ID = -1)';
563 - } else {
564 - $query_args['where'] = "(post.post_type = 'project' AND post.ID IN (" . implode( ', ', array_keys( $projects ) ) . '))';
565 - $user_can_moderate_comments = user_can( $user, 'moderate_comments' );
566 -
567 - if ( ! $user_can_moderate_comments ) {
568 - $query_args['where'] .= " AND ( comment_approved = '1' )";
569 - } else {
570 - $query_args['where'] .= " AND ( comment_approved = '1' OR comment_approved = '0' )";
571 - }
572 - }
573 - } else {
574 - $query_args['where'] .= " AND (post.post_type != 'project')";
575 - }
576 - }
577 -
578 - return $query_args;
579 - }
580 - }
581 -endif;
582 -
583 -
584 -/**
585 - * Main instance of UpStream.
586 - *
587 - * Returns the main instance of UpStream to prevent the need to use globals.
588 - *
589 - * @return UpStream
590 - * @since 1.0.0
591 - */
592 -function upstream() {
593 - return UpStream::instance();
594 -}
595 -
596 -upstream();
597 -do_action( 'upstream_run' );
1 +<?php
2 +/**
3 + * Main UpStream Class.
4 + *
5 + * @package UpStream
6 + */
7 +
8 +use UpStream\Container;
9 +use UpStream\Comments;
10 +
11 +if ( ! defined( 'ABSPATH' ) ) {
12 + exit;
13 +}
14 +
15 +if ( ! class_exists( 'UpStream' ) ) :
16 +
17 + /**
18 + * Main UpStream Class.
19 + *
20 + * @since 1.0.0
21 + */
22 + final class UpStream {
23 +
24 + /**
25 + * The one true UpStream
26 + *
27 + * @var UpStream
28 + * @since 1.0.0
29 + */
30 + protected static $instance = null;
31 +
32 + /**
33 + * Twig Environment
34 + *
35 + * @var Twig_Environment
36 + */
37 + protected $twig;
38 +
39 + /**
40 + * Container
41 + *
42 + * @var Container
43 + */
44 + protected $container;
45 +
46 + /**
47 + * Main UpStream Instance.
48 + */
49 + public static function instance() {
50 + if ( is_null( self::$instance ) ) {
51 + self::$instance = new self();
52 + }
53 +
54 + return self::$instance;
55 + }
56 +
57 + /**
58 + * Throw error on object clone.
59 + *
60 + * The whole idea of the singleton design pattern is that there is a single
61 + * object therefore, we don't want the object to be cloned.
62 + *
63 + * @since 1.0.0
64 + */
65 + public function __clone() {
66 + _doing_it_wrong( __FUNCTION__, 'You\'re not supposed to clone this class.', esc_html( UPSTREAM_VERSION ) );
67 + }
68 +
69 + /**
70 + * Disable unserializing of the class.
71 + *
72 + * @since 1.0.0
73 + */
74 + public function __wakeup() {
75 + _doing_it_wrong( __FUNCTION__, 'You\'re not supposed to unserialize this class.', esc_html( UPSTREAM_VERSION ) );
76 + }
77 +
78 + /**
79 + * Prevent the class instance being serialized.
80 + *
81 + * @since 1.10.2
82 + */
83 + public function __sleep() {
84 + _doing_it_wrong( __FUNCTION__, 'You\'re not supposed to serialize this class.', esc_html( UPSTREAM_VERSION ) );
85 + }
86 +
87 + /**
88 + * Class constructor.
89 + */
90 + public function __construct() {
91 + $this->define_constants();
92 + $this->includes();
93 + $this->container = Container::get_instance();
94 + $this->init_framework();
95 +
96 + if ( UpStream_Debug::is_enabled() ) {
97 + UpStream_Debug::init();
98 + }
99 +
100 + $this->init_hooks();
101 +
102 + do_action( 'upstream_loaded' );
103 + }
104 +
105 + /**
106 + * Hook into actions and filters.
107 + *
108 + * @since 1.0.0
109 + */
110 + private function init_hooks() {
111 + add_action( 'init', array( $this, 'init' ) );
112 + add_filter( 'plugin_row_meta', array( $this, 'plugin_row_meta' ), 10, 2 );
113 + add_filter( 'plugin_action_links_upstream/upstream.php', array( $this, 'handle_action_links' ) );
114 + add_filter( 'http_request_host_is_external', array( 'UpStream', 'allow_external_update_host' ), 10, 3 );
115 + add_filter( 'quicktags_settings', 'upstream_tinymce_quicktags_settings' );
116 + add_filter( 'tiny_mce_before_init', 'upstream_tinymce_before_init_setup_toolbar' );
117 + add_filter( 'tiny_mce_before_init', 'upstream_tinymce_before_init' );
118 + add_filter( 'teeny_mce_before_init', 'upstream_tinymce_before_init_setup_toolbar' );
119 + add_filter( 'comments_clauses', array( $this, 'filter_comments_on_dashboard' ), 10, 2 );
120 + add_filter( 'views_dashboard', array( 'UpStream_Admin', 'comment_status_links' ), 10, 1 );
121 + add_action( 'plugins_loaded', array( $this, 'load_plugin_textdomain' ) );
122 +
123 + if ( is_admin() ) {
124 + add_action( 'admin_init', array( $this->container['reviews'], 'init' ) );
125 + }
126 +
127 + global $pagenow;
128 +
129 + if ( 'plugins.php' === $pagenow ) {
130 + add_action(
131 + 'in_plugin_update_message-' . UPSTREAM_PLUGIN_BASENAME,
132 + array( $this, 'render_additional_update_info' ),
133 + 20,
134 + 2
135 + );
136 + }
137 + }
138 +
139 + /**
140 + * Initialize the Alledia Framework.
141 + */
142 + private function init_framework() {
143 + $this->container['framework']->init();
144 + }
145 +
146 +
147 + /**
148 + * Prevent a Client User from accessing any page other than the profile.
149 + *
150 + * @since 1.11.0
151 + *
152 + * @global $pagenow
153 + */
154 + public function limit_client_users_admin_access() {
155 + global $pagenow;
156 +
157 + $profile_age = 'profile.php';
158 + if ( $pagenow !== $profile_age && 'edit.php' !== $pagenow && ! wp_doing_ajax() ) {
159 + wp_safe_redirect( admin_url( $profile_age ) );
160 + exit();
161 + }
162 + }
163 +
164 + /**
165 + * Make sure Client Users can only see the Profile menu item.
166 + *
167 + * @since 1.11.0
168 + *
169 + * @global $menu
170 + */
171 + public function limit_client_users_menu() {
172 + global $menu;
173 +
174 + foreach ( $menu as $menu_index => $menu_data ) {
175 + $menu_file = isset( $menu_data[2] ) ? $menu_data[2] : null;
176 +
177 + if ( null !== $menu_file ) {
178 + if ( 'profile.php' === $menu_file || 'edit.php?post_type=project' === $menu_file ) {
179 + continue;
180 + }
181 +
182 + remove_menu_page( $menu_file );
183 + }
184 + }
185 + }
186 +
187 + /**
188 + * Hide some toolbar items from Client Users.
189 + *
190 + * @param \WP_Admin_Bar $wp_admin_bar WordPress admin bar.
191 + *
192 + * @since 1.11.0
193 + */
194 + public function limit_client_users_toolbar_items( $wp_admin_bar ) {
195 + $user = wp_get_current_user();
196 + $user_roles = (array) $user->roles;
197 +
198 + if ( count( array_intersect( $user_roles, array( 'administrator', 'upstream_manager' ) ) ) === 0
199 + && in_array( 'upstream_client_user', $user_roles, true )
200 + ) {
201 + $menu_items = array( 'about', 'comments', 'new-content' );
202 +
203 + if ( ! is_admin() ) {
204 + $menu_items = array_merge( $menu_items, array( 'dashboard', 'edit' ) );
205 + }
206 +
207 + foreach ( $menu_items as $menu_item ) {
208 + $wp_admin_bar->remove_menu( $menu_item );
209 + }
210 + }
211 + }
212 +
213 + /**
214 + * Get container.
215 + *
216 + * @return Container
217 + */
218 + public function get_container() {
219 + return $this->container;
220 + }
221 +
222 + /**
223 + * Define Constants.
224 + *
225 + * @since 1.0.0
226 + */
227 + private function define_constants() {
228 + $upload_dir = wp_upload_dir();
229 +
230 + $this->define( 'UPSTREAM_PLUGIN_DIR', plugin_dir_path( UPSTREAM_PLUGIN_FILE ) );
231 + $this->define( 'UPSTREAM_PLUGIN_URL', plugin_dir_url( UPSTREAM_PLUGIN_FILE ) );
232 + $this->define( 'UPSTREAM_PLUGIN_BASENAME', plugin_basename( UPSTREAM_PLUGIN_FILE ) );
233 + $this->define( 'UPSTREAM_PLUGIN_RELATIVE_PATH', 'upstream' );
234 +
235 + include_once __DIR__ . '/includes.php';
236 + }
237 +
238 + /**
239 + * Define constant if not already set.
240 + *
241 + * @param string $name Definition name.
242 + * @param string|bool $value Definition value.
243 + *
244 + * @since 1.0.0
245 + */
246 + private function define( $name, $value ) {
247 + if ( ! defined( $name ) ) {
248 + define( $name, $value );
249 + }
250 + }
251 +
252 + /**
253 + * What type of request is this?
254 + * string $type frontend or admin.
255 + *
256 + * @param string $type Request type.
257 + * @return bool
258 + * @since 1.0.0
259 + */
260 + private function is_request( $type ) {
261 + switch ( $type ) {
262 + case 'admin':
263 + return is_admin();
264 + case 'frontend':
265 + return ( ! is_admin() || defined( 'DOING_AJAX' ) ) && ! defined( 'DOING_CRON' );
266 + }
267 + }
268 +
269 + /**
270 + * Include required core files used in admin and on the frontend.
271 + *
272 + * @since 1.0.0
273 + */
274 + public function includes() {
275 + if ( file_exists( __DIR__ . '/vendor/autoload.php' ) ) {
276 + require_once __DIR__ . '/vendor/autoload.php';
277 + }
278 +
279 + include_once __DIR__ . '/includes/class-exception.php';
280 + include_once __DIR__ . '/includes/trait-up-singleton.php';
281 + include_once __DIR__ . '/includes/trait-up-post-metadata.php';
282 + include_once __DIR__ . '/includes/class-struct.php';
283 + include_once __DIR__ . '/includes/class-upstream-debug.php';
284 + include_once __DIR__ . '/includes/class-container.php';
285 + include_once __DIR__ . '/includes/up-install.php';
286 + include_once __DIR__ . '/includes/class-upstream-autoloader.php';
287 + include_once __DIR__ . '/includes/class-upstream-roles.php';
288 + include_once __DIR__ . '/includes/class-upstream-counts.php';
289 + include_once __DIR__ . '/includes/class-upstream-counter.php';
290 + include_once __DIR__ . '/includes/class-upstream-project-activity.php';
291 + include_once __DIR__ . '/includes/up-permalinks.php';
292 + include_once __DIR__ . '/includes/up-general-functions.php';
293 + include_once __DIR__ . '/includes/up-post-types.php';
294 + include_once __DIR__ . '/includes/up-labels.php';
295 + include_once __DIR__ . '/includes/class-milestones.php';
296 + include_once __DIR__ . '/includes/class-milestone.php';
297 + include_once __DIR__ . '/includes/class-factory.php';
298 + include_once __DIR__ . '/includes/up-install.php';
299 + include_once __DIR__ . '/includes/up-filesystem.php';
300 + include_once __DIR__ . '/includes/up-register-nonce-fields.php';
301 + include_once __DIR__ . '/includes/up-wcs-helper.php';
302 + include_once __DIR__ . '/includes/class-license-checker.php';
303 + include_once __DIR__ . '/includes/up-cron-license-checker.php';
304 +
305 + $request = wp_unslash( $_REQUEST );
306 +
307 + if ( $this->is_request( 'admin' ) ) {
308 + global $pagenow;
309 +
310 + $is_multisite = (bool) is_multisite();
311 + $load_cmb2 = false;
312 + $server = wp_unslash( $_SERVER );
313 +
314 + if ( $is_multisite ) {
315 + $current_page = isset( $_SERVER['PHP_SELF'] ) ? preg_replace(
316 + '/^\/wp-admin\//i',
317 + '',
318 + sanitize_text_field( $server['PHP_SELF'] )
319 + ) : '';
320 + } else {
321 + $current_page = (string) $pagenow;
322 + }
323 +
324 + if ( in_array( $current_page, array( 'post.php', 'post-new.php' ), true ) ) {
325 + $post_type = isset( $request['post_type'] ) ? sanitize_text_field( $request['post_type'] ) : null;
326 +
327 + if ( empty( $post_type ) ) {
328 + $project_id = isset( $request['post'] ) ? absint( $request['post'] ) : 0;
329 + $post_type = get_post_type( $project_id );
330 + }
331 +
332 + if ( ! empty( $post_type ) ) {
333 + $post_types_using_cmb2 = apply_filters( 'upstream:post_types_using_cmb2', array( 'project', 'client' ) );
334 + $load_cmb2 = in_array( $post_type, $post_types_using_cmb2, true );
335 + }
336 + } elseif (
337 + 'admin.php' === $current_page
338 + && isset( $request['page'] )
339 + && preg_match( '/^upstream_/i', sanitize_text_field( $request['page'] ) )
340 + ) {
341 + $load_cmb2 = true;
342 + }
343 +
344 + if ( $load_cmb2 ) {
345 + include_once __DIR__ . '/includes/libraries/cmb2/init.php';
346 + include_once __DIR__ . '/includes/libraries/cmb2-grid/Cmb2GridPlugin.php';
347 + }
348 +
349 + include_once __DIR__ . '/includes/admin/class-upstream-admin.php';
350 + include_once __DIR__ . '/includes/admin/class-upstream-admin-tasks-page.php';
351 + include_once __DIR__ . '/includes/admin/class-upstream-admin-bugs-page.php';
352 + include_once __DIR__ . '/includes/admin/class-upstream-admin-reviews.php';
353 + }
354 +
355 + if ( $this->is_request( 'frontend' ) ) {
356 + include_once __DIR__ . '/includes/frontend/class-upstream-template-loader.php';
357 + include_once __DIR__ . '/includes/frontend/class-upstream-login.php';
358 + include_once __DIR__ . '/includes/frontend/class-upstream-style-output.php';
359 + include_once __DIR__ . '/includes/frontend/up-enqueues.php';
360 + include_once __DIR__ . '/includes/frontend/up-template-functions.php';
361 + include_once __DIR__ . '/includes/frontend/up-table-functions.php';
362 + include_once __DIR__ . '/includes/frontend/class-upstream-view.php';
363 + include_once __DIR__ . '/includes/frontend/class-upstream-ajax.php';
364 + }
365 +
366 + include_once __DIR__ . '/includes/up-project-functions.php';
367 + include_once __DIR__ . '/includes/up-client-functions.php';
368 + include_once __DIR__ . '/includes/up-permissions-functions.php';
369 + include_once __DIR__ . '/includes/class-comments-migration.php';
370 + include_once __DIR__ . '/includes/class-comments.php';
371 + include_once __DIR__ . '/includes/class-comment.php';
372 + }
373 +
374 + /**
375 + * Init UpStream when WordPress Initialises.
376 + */
377 + public function init() {
378 + UpStream\Milestones::instantiate();
379 +
380 + do_action( 'before_upstream_init' );
381 +
382 + $this->project = new UpStream_Project();
383 + $this->project_activity = new UpStream_Project_Activity();
384 +
385 + if ( version_compare( PHP_VERSION, '5.5', '<' ) ) {
386 + require_once UPSTREAM_PLUGIN_DIR . 'includes/libraries/password_compat-1.0.4/lib/password.php';
387 + }
388 +
389 + \UpStream\Migrations\Comments_Migration::run();
390 +
391 + $user = wp_get_current_user();
392 + $user_roles = (array) $user->roles;
393 +
394 + if (
395 + count( array_intersect( $user_roles, array( 'administrator', 'upstream_manager' ) ) ) === 0
396 + && in_array( 'upstream_client_user', $user_roles, true )
397 + ) {
398 + add_filter( 'admin_init', array( $this, 'limit_client_users_admin_access' ) );
399 + add_filter( 'admin_head', array( $this, 'limit_client_users_menu' ) );
400 + add_action( 'admin_bar_menu', array( $this, 'limit_client_users_toolbar_items' ), 999 );
401 + }
402 +
403 + $edit_other_projects_permission_were_removed = (bool) get_option( 'upstream:role_upstream_users:drop_edit_others_projects' );
404 +
405 + if ( ! $edit_other_projects_permission_were_removed ) {
406 + $role = get_role( 'upstream_user' );
407 +
408 + if ( $role ) {
409 + $role->remove_cap( 'edit_others_projects' );
410 + }
411 +
412 + unset( $role );
413 + update_option( 'upstream:role_upstream_users:drop_edit_others_projects', 1 );
414 + }
415 +
416 + UpStream_Options_Projects::create_projects_statuses_ids();
417 + UpStream_Options_Tasks::create_tasks_statuses_ids();
418 + UpStream_Options_Bugs::create_bugs_statuses_ids();
419 +
420 + Comments::instantiate();
421 +
422 + if ( $this->is_request( 'frontend' ) ) {
423 + UpStream_Ajax::instantiate();
424 + }
425 +
426 + do_action( 'upstream_init' );
427 + }
428 +
429 + /**
430 + * Load Localisation files.
431 + */
432 + public function load_plugin_textdomain() {
433 + load_plugin_textdomain( 'upstream', false, UPSTREAM_PLUGIN_RELATIVE_PATH . '/languages/' );
434 + }
435 +
436 +
437 + /**
438 + * Show row meta on the plugin screen.
439 + *
440 + * @param mixed $links Plugin Row Meta.
441 + * @param mixed $file Plugin Base file.
442 + *
443 + * @return array
444 + */
445 + public function plugin_row_meta( $links, $file ) {
446 + if ( UPSTREAM_PLUGIN_BASENAME === $file ) {
447 + $row_meta = array(
448 + 'docs' => sprintf(
449 + '<a href="%s" title="%s">%s</a>',
450 + esc_url( 'http://upstreamplugin.com/documentation' ),
451 + esc_attr__( 'View Documentation', 'upstream' ),
452 + esc_html__( 'Docs', 'upstream' )
453 + ),
454 + 'quick-start' => sprintf(
455 + '<a href="%s" title="%s">%s</a>',
456 + esc_url( 'http://upstreamplugin.com/quick-start-guide' ),
457 + esc_attr__( 'View Quick Start Guide', 'upstream' ),
458 + esc_html__( 'Quick Start Guide', 'upstream' )
459 + ),
460 + );
461 +
462 + return array_merge( $links, $row_meta );
463 + }
464 +
465 + return (array) $links;
466 + }
467 +
468 + /**
469 + * Callback called to setup the links to display on the plugins page, besides active/deactivate links.
470 + *
471 + * @param array $links The list of links to be displayed.
472 + *
473 + * @return array
474 + * @since 1.11.1
475 + * @static
476 + */
477 + public static function handle_action_links( $links ) {
478 + $links['settings'] = sprintf(
479 + '<a href="%s" title="%2$s" aria-label="%2$s">%3$s</a>',
480 + esc_url( admin_url( 'admin.php?page=upstream_general' ) ),
481 + esc_attr__( 'Open Settings Page', 'upstream' ),
482 + esc_html__( 'Settings', 'upstream' )
483 + );
484 +
485 + return $links;
486 + }
487 +
488 + /**
489 + * Ensures the plugins update API's host is whitelisted to WordPress external requests.
490 + *
491 + * @param boolean $is_allowed Is allowed or not.
492 + * @param string $host Host.
493 + * @param string $url Url.
494 + *
495 + * @return boolean
496 + * @since 1.11.1
497 + * @static
498 + */
499 + public static function allow_external_update_host( $is_allowed, $host, $url ) {
500 + if ( 'upstreamplugin.com' === $host ) {
501 + return true;
502 + }
503 +
504 + return $is_allowed;
505 + }
506 +
507 + /**
508 + * Render additional update info if needed.
509 + *
510 + * @param array $plugin_data Plugin metadata.
511 + * @param object $response Metadata about the available plugin update.
512 + *
513 + * @since 1.12.5
514 + * @static
515 + *
516 + * @see https://developer.wordpress.org/reference/hooks/in_plugin_update_message-file
517 + */
518 + public static function render_additional_update_info( $plugin_data, $response ) {
519 + $update_notice_title_html = sprintf(
520 + '<strong style="font-size: 1.25em; display: block; margin-top: 10px;">%s</strong>',
521 + esc_html__( 'Update notice:', 'upstream' )
522 + );
523 +
524 + if ( version_compare( UPSTREAM_VERSION, '1.12.5', '<' ) ) {
525 + printf(
526 + esc_html( $update_notice_title_html ) .
527 + // translators: '%1$s: plugin version, %2$s: capability name, %3$s: UpStream User role'.
528 + esc_html__(
529 + 'Starting from <strong>%1$s</strong> <code>%2$s</code> capability was removed from <code>%3$s</code> users role.',
530 + 'upstream'
531 + ),
532 + 'v1.12.5',
533 + 'edit_others_projects',
534 + esc_html__( 'UpStream User', 'upstream' )
535 + );
536 + }
537 + }
538 +
539 + /**
540 + * Make sure Recent Comments section on admin Dashboard display only comments
541 + * current user is allowed to see from projects he's allowed to access.
542 + *
543 + * @param array $query_args Query clauses.
544 + * @param WP_Comment_Query $query Current query instance.
545 + *
546 + * @return array $queryArgs
547 + * @global $pagenow, $wpdb
548 + *
549 + * @since 1.13.0
550 + * @static
551 + */
552 + public static function filter_comments_on_dashboard( $query_args, $query ) {
553 + global $pagenow;
554 +
555 + if ( is_admin() && 'index.php' === $pagenow && ! upstream_is_user_either_manager_or_admin() ) {
556 + global $wpdb;
557 +
558 + $query_args['join'] = 'LEFT JOIN ' . $wpdb->prefix . 'posts AS post ON post.ID = ' . $wpdb->prefix . 'comments.comment_post_ID';
559 + $user = wp_get_current_user();
560 +
561 + if ( in_array( 'upstream_user', $user->roles, true ) || in_array( 'upstream_client_user', $user->roles, true ) ) {
562 + $projects = (array) upstream_get_users_projects( $user );
563 +
564 + if ( count( $projects ) === 0 ) {
565 + $query_args['where'] = '(post.ID = -1)';
566 + } else {
567 + $query_args['where'] = "(post.post_type = 'project' AND post.ID IN (" . implode( ', ', array_keys( $projects ) ) . '))';
568 + $user_can_moderate_comments = user_can( $user, 'moderate_comments' );
569 +
570 + if ( ! $user_can_moderate_comments ) {
571 + $query_args['where'] .= " AND ( comment_approved = '1' )";
572 + } else {
573 + $query_args['where'] .= " AND ( comment_approved = '1' OR comment_approved = '0' )";
574 + }
575 + }
576 + } else {
577 + $query_args['where'] .= " AND (post.post_type != 'project')";
578 + }
579 + }
580 +
581 + return $query_args;
582 + }
583 + }
584 +endif;
585 +
586 +
587 +/**
588 + * Main instance of UpStream.
589 + *
590 + * Returns the main instance of UpStream to prevent the need to use globals.
591 + *
592 + * @return UpStream
593 + * @since 1.0.0
594 + */
595 +function upstream() {
596 + return UpStream::instance();
597 +}
598 +
599 +upstream();
600 +do_action( 'upstream_run' );