PluginProbe
UpStream: a Project Management Plugin for WordPress / 2.0.7
UpStream: a Project Management Plugin for WordPress v2.0.7
trunk 1.39.0 1.39.1 1.39.2 1.39.3 2.0.7 2.1.0
upstream / class-upstream.php

class-upstream.php in UpStream: a Project Management Plugin for WordPress 2.0.7, at class-upstream.php

598 lines 17.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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' );
598