Changeset 3952
- Timestamp:
- 09/04/2016 07:57:05 PM (8 years ago)
- Location:
- sites/trunk/wordpress.org/public_html/wp-content/plugins
- Files:
-
- 10 added
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-rosetta-roles/wporg-gp-rosetta-roles.php
r3692 r3952 3 3 * Plugin name: GlotPress: Rosetta Roles 4 4 * Description: Ties roles on Rosetta sites directly into translate.wordpress.org. 5 * Version: 1.05 * Version: 2.0 6 6 * Author: WordPress.org 7 7 * Author URI: http://wordpress.org/ … … 9 9 */ 10 10 11 class WPorg_GP_Rosetta_Roles { 11 namespace WordPressdotorg\GlotPress\Rosetta_Roles; 12 12 13 /** 14 * Cache group. 15 * 16 * @var string 17 */ 18 public $cache_group = 'wporg-translate'; 13 use WordPressdotorg\Autoload; 19 14 20 /** 21 * Holds the plugin ID. 22 * 23 * @var string 24 */ 25 public $id = 'wporg-rosetta-roles'; 15 // Store the root plugin file for usage with functions which use the plugin basename. 16 define( __NAMESPACE__ . '\PLUGIN_FILE', __FILE__ ); 26 17 27 /** 28 * Database table for translation editors. 29 */ 30 const TRANSLATION_EDITORS_TABLE = 'translate_translation_editors'; 31 32 /** 33 * Role of a per project translation editor. 34 */ 35 const TRANSLATION_EDITOR_ROLE = 'translation_editor'; 36 37 /** 38 * Role of a general translation editor. 39 */ 40 const GENERAL_TRANSLATION_EDITOR_ROLE = 'general_translation_editor'; 41 42 /** 43 * Contructor. 44 */ 45 public function __construct() { 46 $GLOBALS['wpdb']->wporg_translation_editors = self::TRANSLATION_EDITORS_TABLE; 47 48 add_filter( 'gp_pre_can_user', array( $this, 'pre_can_user' ), 9 , 2 ); 49 add_action( 'gp_project_created', array( $this, 'project_created' ) ); 50 add_action( 'gp_project_saved', array( $this, 'project_saved' ) ); 51 } 52 53 /** 54 * Filter to check if the current user has permissions to approve strings, based 55 * on a role on the Rosetta site. 56 * 57 * @param string $verdict Verdict. 58 * @param array $args Array of arguments. 59 * @return bool True if user has permissions, false if not. 60 */ 61 public function pre_can_user( $verdict, $args ) { 62 if ( 'delete' === $args['action'] ) { 63 return false; 64 } 65 66 // Administrators on global.wordpress.org are considered global admins in GlotPress. 67 if ( $this->is_global_administrator( $args['user_id'] ) ) { 68 return true; 69 } 70 71 if ( 'approve' !== $args['action'] || ! in_array( $args['object_type'], array( 'project|locale|set-slug', 'translation-set' ) ) ) { 72 return false; 73 } 74 75 // Get locale and current project ID. 76 $locale_and_project_id = (object) $this->get_locale_and_project_id( $args['object_type'], $args['object_id'] ); 77 if ( ! $locale_and_project_id ) { 78 return false; 79 } 80 81 $locale_slug = $locale_and_project_id->locale; 82 $current_project_id = $locale_and_project_id->project_id; 83 84 // Simple check to see if they're an approver or not. 85 if ( ! $this->is_approver_for_locale( $args['user_id'], $locale_slug ) ) { 86 return false; 87 } 88 89 // Grab the list of Projects (or 'all') that the user can approve. 90 $project_access_list = $this->get_project_id_access_list( $args['user_id'], $locale_slug ); 91 if ( ! $project_access_list ) { 92 return false; 93 } 94 95 // Short circuit the check if user can approve all projects. 96 if ( in_array( 'all', $project_access_list ) ) { 97 return true; 98 } 99 100 // If current project is a parent ID. 101 if ( in_array( $current_project_id, $project_access_list ) ) { 102 return true; 103 } 104 105 // A user is allowed to approve sub projects as well. 106 $project_access_list = $this->get_project_id_access_list( $args['user_id'], $locale_slug, /* $include_children = */ true ); 107 if ( in_array( $current_project_id, $project_access_list ) ) { 108 return true; 109 } 110 111 return false; 112 } 113 114 /** 115 * Callback for when a project is created. 116 */ 117 public function project_created() { 118 $this->clear_project_cache(); 119 } 120 121 /** 122 * Callback for when a project is saved. 123 */ 124 public function project_saved() { 125 $this->clear_project_cache(); 126 } 127 128 /** 129 * Determines if a given user is a Global Admin. 130 * 131 * Users present as an administrator on global.wordpress.org are treated as a 132 * global administrator in GlotPress. 133 * 134 * @param int $user_id User ID. 135 * @return bool True, if user is an admin, false if not. 136 */ 137 public function is_global_administrator( $user_id ) { 138 $user = get_user_by( 'id', $user_id ); 139 140 // 115 = global.wordpress.org. Administrators on this site are considered global admins in GlotPress. 141 if ( ! empty( $user->wporg_115_capabilities ) && is_array( $user->wporg_115_capabilities ) && ! empty( $user->wporg_115_capabilities['administrator'] ) ) { 142 return true; 143 } 144 145 return false; 146 } 147 148 /** 149 * Determines if a given user is a Translation Approver for a Locale. 150 * 151 * @param int $user_id User ID. 152 * @param string $locale_slug The Locale for which we are checking. 153 * @return bool True, if user is an approver, false if not. 154 */ 155 public function is_approver_for_locale( $user_id, $locale_slug ) { 156 static $cache = null; 157 158 if ( null === $cache ) { 159 $cache = array(); 160 } 161 162 if ( isset( $cache[ $user_id ][ $locale_slug ] ) ) { 163 return $cache[ $user_id ][ $locale_slug ]; 164 } 165 166 if ( ! isset( $cache[ $user_id ] ) ) { 167 $cache[ $user_id ] = array(); 168 } 169 170 // Get blog prefix of the associated Rosetta site. 171 if ( ! $blog_prefix = $this->get_blog_prefix( $locale_slug ) ) { 172 $cache[ $user_id ][ $locale_slug ] = false; 173 return false; 174 } 175 176 $user = get_user_by( 'id', $user_id ); 177 178 $cap_key = $blog_prefix . 'capabilities'; 179 if ( ! isset( $user->{$cap_key} ) ) { 180 $cache[ $user_id ][ $locale_slug ] = false; 181 return false; 182 } 183 184 $capabilities = $user->{$cap_key}; 185 $is_approver = ! empty( $capabilities[ self::TRANSLATION_EDITOR_ROLE ] ) || ! empty( $capabilities[ self::GENERAL_TRANSLATION_EDITOR_ROLE ] ); 186 $cache[ $user_id ][ $locale_slug ] = $is_approver; 187 188 return $is_approver; 189 } 190 191 /** 192 * Retrieves a list of project ID's which a user can approve for. 193 * 194 * This is likely to be incorrrect in the event that the user is a Translation Editor or Global Admin. 195 * The array item 'all' is special, which means to allow access to all projects. 196 * 197 * @param int $user_id User ID. 198 * @param string $locale_slug The Locale for which we are checking. 199 * @param bool $include_children Whether to include the children project ID's in the return. 200 * @return array A list of the Project ID's for which the current user can approve translations for. 201 */ 202 public function get_project_id_access_list( $user_id, $locale_slug, $include_children = false ) { 203 global $wpdb; 204 static $cache = null; 205 206 if ( null === $cache ) { 207 $cache = array(); 208 } 209 210 if ( isset( $cache[ $user_id ][ $locale_slug ] ) ) { 211 $project_access_list = $cache[ $user_id ][ $locale_slug ]; 212 } else { 213 $project_access_list = $wpdb->get_col( $wpdb->prepare( " 214 SELECT project_id FROM 215 {$wpdb->wporg_translation_editors} 216 WHERE user_id = %d AND locale = %s 217 ", $user_id, $locale_slug ) ); 218 219 if ( ! isset( $cache[ $user_id ] ) ) { 220 $cache[ $user_id ] = array(); 221 } 222 223 $cache[ $user_id ][ $locale_slug ] = $project_access_list; 224 } 225 226 if ( ! $project_access_list ) { 227 return array(); 228 } 229 230 if ( in_array( '0', $project_access_list, true ) ) { 231 $project_access_list = array( 'all' ); 232 } 233 234 // If we don't want the children, or the user has access to all projects. 235 if ( ! $include_children || in_array( 'all', $project_access_list ) ) { 236 return $project_access_list; 237 } 238 239 // A user is allowed to approve sub projects as well. 240 $allowed_sub_project_ids = array(); 241 foreach ( $project_access_list as $project_id ) { 242 if ( 'all' === $project_id ) { 243 continue; 244 } 245 $sub_project_ids = $this->get_sub_project_ids( $project_id ); 246 if ( $sub_project_ids ) { 247 $allowed_sub_project_ids = array_merge( $allowed_sub_project_ids, $sub_project_ids ); 248 } 249 } 250 251 // $project_access_list contains parent project IDs, merge them with the sub-project IDs. 252 $project_access_list = array_merge( $project_access_list, $allowed_sub_project_ids ); 253 254 return $project_access_list; 255 } 256 257 /** 258 * Fetches all projects from database. 259 * 260 * @return array List of projects with ID and parent ID. 261 */ 262 public function get_all_projects() { 263 global $wpdb; 264 static $projects = null; 265 266 if ( null !== $projects ) { 267 return $projects; 268 } 269 270 $_projects = $wpdb->get_results( " 271 SELECT 272 id, parent_project_id 273 FROM {$wpdb->gp_projects} 274 ORDER BY id 275 " ); 276 277 $projects = array(); 278 foreach ( $_projects as $project ) { 279 $project->sub_projects = array(); 280 $projects[ $project->id ] = $project; 281 } 282 283 return $projects; 284 } 285 286 /** 287 * Returns projects as a hierarchy tree. 288 * 289 * @return array The project tree. 290 */ 291 public function get_project_tree() { 292 static $project_tree = null; 293 294 if ( null !== $project_tree ) { 295 return $project_tree; 296 } 297 298 $projects = $this->get_all_projects(); 299 300 $project_tree = array(); 301 foreach ( $projects as $project_id => $project ) { 302 $projects[ $project->parent_project_id ]->sub_projects[ $project_id ] = &$projects[ $project_id ]; 303 if ( ! $project->parent_project_id ) { 304 $project_tree[ $project_id ] = &$projects[ $project_id ]; 305 } 306 } 307 308 return $project_tree; 309 } 310 311 /** 312 * Returns all sub project IDs of a parent ID. 313 * 314 * @param int $project_id Parent ID. 315 * @return array IDs of the sub projects. 316 */ 317 public function get_sub_project_ids( $project_id ) { 318 $cache_key = 'project:' . $project_id . ':childs'; 319 $cache = wp_cache_get( $cache_key, $this->cache_group ); 320 if ( false !== $cache ) { 321 return $cache; 322 } 323 324 $project_tree = $this->get_project_tree(); 325 $project_branch = $this->get_project_branch( $project_id, $project_tree ); 326 327 $project_ids = array(); 328 if ( isset( $project_branch->sub_projects ) ) { 329 $project_ids = self::array_keys_multi( $project_branch->sub_projects, 'sub_projects' ); 330 } 331 332 wp_cache_set( $cache_key, $project_ids, $this->cache_group ); 333 334 return $project_ids; 335 } 336 337 /** 338 * Returns a specific branch of a hierarchy tree. 339 * 340 * @param int $project_id Project ID. 341 * @param array $projects Hierarchy tree of projects. 342 * @return mixed False if project ID doesn't exist, project branch on success. 343 */ 344 public function get_project_branch( $project_id, $projects ) { 345 if ( ! is_array( $projects ) ) { 346 return false; 347 } 348 349 foreach ( $projects as $project ) { 350 if ( $project->id == $project_id ) { 351 return $project; 352 } 353 354 if ( isset( $project->sub_projects ) ) { 355 $sub = $this->get_project_branch( $project_id, $project->sub_projects ); 356 if ( $sub ) { 357 return $sub; 358 } 359 } 360 } 361 362 return false; 363 } 364 365 /** 366 * Removes all of the project ids from the cache. 367 */ 368 public function clear_project_cache() { 369 $projects = $this->get_all_projects(); 370 371 foreach ( $projects as $project ) { 372 $cache_key = 'project:' . $project->id . ':childs'; 373 wp_cache_delete( $cache_key, $this->cache_group ); 374 } 375 } 376 377 /** 378 * Extracts project ID and locale slug from object type and ID. 379 * 380 * @param string $object_type Current object type. 381 * @param string $object_id Current object ID. 382 * @return array|false Locale and project ID, false on failure. 383 */ 384 public function get_locale_and_project_id( $object_type, $object_id ) { 385 switch ( $object_type ) { 386 case 'translation-set' : 387 $set = GP::$translation_set->get( $object_id ); 388 return array( 'locale' => $set->locale, 'project_id' => (int) $set->project_id ); 389 390 case 'project|locale|set-slug' : 391 list( $project_id, $locale ) = explode( '|', $object_id ); 392 return array( 'locale' => $locale, 'project_id' => (int) $project_id ); 393 } 394 return false; 395 } 396 397 /** 398 * Returns the blog prefix of a locale. 399 * 400 * @param string $locale_slug Slug of GlotPress locale. 401 * @return bool|string Blog prefix on success, false on failure. 402 */ 403 public function get_blog_prefix( $locale_slug ) { 404 global $wpdb; 405 static $ros_locale_assoc; 406 407 $gp_locale = GP_Locales::by_slug( $locale_slug ); 408 if ( ! $gp_locale || ! isset( $gp_locale->wp_locale ) ) { 409 return false; 410 } 411 412 $wp_locale = $gp_locale->wp_locale; 413 414 if ( ! isset( $ros_locale_assoc ) ) { 415 $ros_locale_assoc = $wpdb->get_results( 'SELECT locale, subdomain FROM locales', OBJECT_K ); 416 } 417 418 if ( isset( $ros_locale_assoc[ $wp_locale ] ) ) { 419 $subdomain = $ros_locale_assoc[ $wp_locale ]->subdomain; 420 } else { 421 return false; 422 } 423 424 $result = get_sites( [ 425 'network_id' => get_current_network_id(), 426 'domain' => "$subdomain.wordpress.org", 427 'path' => '/', 428 'number' => 1, 429 ] ); 430 $site = array_shift( $result ); 431 432 if ( $site ) { 433 return 'wporg_' . $site->blog_id . '_'; 434 } 435 436 return false; 437 } 438 439 /** 440 * Returns all keys of a multidimensional array. 441 * 442 * @param array $array Multidimensional array to extract keys from. 443 * @param string $childs_key Optional. Key of the child elements. Default 'childs'. 444 * @return array Array keys. 445 */ 446 public static function array_keys_multi( $array, $childs_key = 'childs' ) { 447 $keys = array(); 448 449 foreach ( $array as $key => $value ) { 450 $keys[] = $key; 451 452 if ( isset( $value->$childs_key ) && is_array( $value->$childs_key ) ) { 453 $keys = array_merge( $keys, self::array_keys_multi( $value->$childs_key ) ); 454 } 455 } 456 457 return $keys; 458 } 18 if ( ! class_exists( '\WordPressdotorg\Autoload\Autoloader', false ) ) { 19 include __DIR__ . '/vendor/wordpressdotorg/class-autoloader.php'; 459 20 } 460 21 461 function wporg_gp_rosetta_roles() { 462 global $wporg_gp_rosetta_roles;22 // Register an Autoloader for all files. 23 Autoload\register_class_path( __NAMESPACE__, __DIR__ . '/inc' ); 463 24 464 if ( ! isset( $wporg_gp_rosetta_roles ) ) { 465 $wporg_gp_rosetta_roles = new WPorg_GP_Rosetta_Roles(); 466 } 467 468 return $wporg_gp_rosetta_roles; 469 } 470 add_action( 'plugins_loaded', 'wporg_gp_rosetta_roles' ); 25 // Instantiate the Plugin. 26 Plugin::get_instance(); -
sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-routes/inc/routes/class-locale.php
r3277 r3952 8 8 use GP_Route; 9 9 use stdClass; 10 use WordPressdotorg\GlotPress\Rosetta_Roles\Plugin as Rosetta_Roles; 10 11 11 12 /** … … 17 18 18 19 /** 20 * Adapter for the rosetta roles plugin. 21 * 22 * @var null|Rosetta_Roles 23 */ 24 private $roles_adapter = null; 25 26 public function __construct() { 27 parent::__construct(); 28 29 if ( method_exists( Rosetta_Roles::class, 'get_instance' ) ) { 30 $this->roles_adapter = Rosetta_Roles::get_instance(); 31 } 32 } 33 34 /** 19 35 * Prints projects/translation sets of a top level project. 20 36 * … … 45 61 if ( 46 62 ! is_user_logged_in() || 47 ! function_exists( 'wporg_gp_rosetta_roles' )|| // Rosetta Roles plugin is not enabled63 ! $this->roles_adapter || // Rosetta Roles plugin is not enabled 48 64 ! ( 49 wporg_gp_rosetta_roles()->is_global_administrator( $user_id ) || // Not a global admin50 wporg_gp_rosetta_roles()->is_approver_for_locale( $user_id, $locale_slug ) // Doesn't have project-level access either65 $this->roles_adapter->is_global_administrator( $user_id ) || // Not a global admin 66 $this->roles_adapter->is_approver_for_locale( $user_id, $locale_slug ) // Doesn't have project-level access either 51 67 ) 52 68 // Add check to see if there are any waiting translations for this locale? … … 565 581 // Special Waiting Project Tab 566 582 // This removes the parent_project_id restriction and replaces it with all-translation-editer-projects 567 if ( 'waiting' == $project->slug && is_user_logged_in() && function_exists( 'wporg_gp_rosetta_roles' )) {583 if ( 'waiting' == $project->slug && is_user_logged_in() && $this->roles_adapter ) { 568 584 569 585 if ( ! $filter ) { … … 574 590 575 591 // Global Admin or Locale-specific admin 576 $can_approve_for_all = wporg_gp_rosetta_roles()->is_global_administrator( $user_id );592 $can_approve_for_all = $this->roles_adapter->is_global_administrator( $user_id ); 577 593 578 594 // Check to see if they have any special approval permissions 579 595 $allowed_projects = array(); 580 if ( ! $can_approve_for_all && wporg_gp_rosetta_roles()->is_approver_for_locale( $user_id, $locale ) ) {581 $allowed_projects = wporg_gp_rosetta_roles()->get_project_id_access_list( $user_id, $locale, true );596 if ( ! $can_approve_for_all && $this->roles_adapter->is_approver_for_locale( $user_id, $locale ) ) { 597 $allowed_projects = $this->roles_adapter->get_project_id_access_list( $user_id, $locale, true ); 582 598 583 599 // Check to see if they can approve for all projects in this locale. … … 588 604 } 589 605 590 $parent_project_sql = '';591 606 if ( $can_approve_for_all ) { 592 607 // The current user can approve for all projects, so just grab all with any waiting strings. … … 607 622 // Limit to only showing base-level projects 608 623 $parent_project_sql .= " AND tp.parent_project_id IN( (SELECT id FROM {$wpdb->gp_projects} WHERE parent_project_id IS NULL AND active = 1) )"; 609 610 624 } 611 625
Note: See TracChangeset
for help on using the changeset viewer.