Changeset 1419
- Timestamp:
- 03/19/2015 02:48:45 PM (10 years ago)
- Location:
- sites/trunk/global.wordpress.org/public_html/wp-content/mu-plugins/roles
- Files:
-
- 6 added
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
sites/trunk/global.wordpress.org/public_html/wp-content/mu-plugins/roles/rosetta-roles.php
r1402 r1419 1 1 <?php 2 3 add_filter( 'gettext_with_context', 'ros_rename_user_roles', 10, 4 ); 4 function ros_rename_user_roles( $translated, $text, $context, $domain ) { 5 if ( $domain !== 'default' || $context !== 'User role' ) { 6 return $translated; 7 } 8 if ( 'Validator' === $text ) { 9 return __( 'Validator', 'rosetta' ); 10 } 11 return $translated; 2 /** 3 * Plugin Name: Rosetta Roles 4 * Plugin URI: https://wordpress.org/ 5 * Description: WordPress interface for managing roles. 6 * Author: ocean90 7 * Version: 1.0 8 */ 9 10 class Rosetta_Roles { 11 /** 12 * Endpoint for profiles.wordpress.org updates. 13 */ 14 const PROFILES_HANDLER_URL = 'https://profiles.wordpress.org/wp-admin/admin-ajax.php'; 15 16 /** 17 * Holds the role of a translation editor. 18 * 19 * @var string 20 */ 21 public $translation_editor_role = 'translation_editor'; 22 23 /** 24 * Holds the meta key of the project access list. 25 * 26 * @var string 27 */ 28 public $project_access_meta_key = 'translation_editor_project_access_list'; 29 30 /** 31 * Constructor. 32 */ 33 public function __construct() { 34 add_action( 'plugins_loaded', array( $this, 'plugins_loaded' ) ); 35 } 36 37 /** 38 * Attaches hooks once plugins are loaded. 39 */ 40 public function plugins_loaded() { 41 add_filter( 'editable_roles', array( $this, 'editable_roles' ) ); 42 add_filter( 'manage_users_columns', array( $this, 'add_roles_column' ) ); 43 add_filter( 'manage_users_custom_column', array( $this, 'display_user_roles' ), 10, 3 ); 44 add_action( 'admin_init', array( $this, 'role_modifications' ) ); 45 add_action( 'set_user_role', array( $this, 'restore_translation_editor_role' ), 10, 3 ); 46 add_filter( 'gettext_with_context', array( $this, 'rename_user_roles' ), 10, 4 ); 47 add_action( 'user_row_actions', array( $this, 'user_row_action_role_editor' ), 10, 2 ); 48 add_action( 'admin_menu', array( $this, 'register_translation_editors_page' ) ); 49 } 50 51 /** 52 * Registers "Translation Editor" role and modifies editor role. 53 */ 54 public function role_modifications() { 55 if ( ! get_role( $this->translation_editor_role ) ) { 56 add_role( $this->translation_editor_role, __( 'Translation Editor', 'rosetta' ), array( 'read' => true, 'level_0' => true ) ); 57 } 58 59 $editor_role = get_role( 'editor' ); 60 if ( $editor_role && ! $editor_role->has_cap( 'remove_users' ) ) { 61 $editor_role->add_cap( 'edit_theme_options' ); 62 $editor_role->add_cap( 'list_users' ); 63 $editor_role->add_cap( 'promote_users' ); 64 $editor_role->add_cap( 'remove_users' ); 65 } 66 67 // Remove deprecated validator role. 68 /*$validator_role = get_role( 'validator' ); 69 if ( $validator_role ) { 70 remove_role( 'validator' ); 71 }*/ 72 } 73 74 /** 75 * Restores the "Translation Editor" role if an user is promoted. 76 * 77 * @param int $user_id The user ID. 78 * @param string $role The new role. 79 * @param array $old_roles An array of the user's previous roles. 80 */ 81 public function restore_translation_editor_role( $user_id, $role, $old_roles ) { 82 if ( ! in_array( $this->translation_editor_role, $old_roles ) ) { 83 return; 84 } 85 86 $user = new WP_User( $user_id ); 87 $user->add_role( $this->translation_editor_role ); 88 } 89 90 /** 91 * Removes "Translation Editor" role and "Administrator" role from 92 * the list of editable roles. 93 * 94 * The list used in wp_dropdown_roles() on users list table. 95 * 96 * @param array $all_roles List of roles. 97 * @return array Filtered list of editable roles. 98 */ 99 public function editable_roles( $roles ) { 100 unset( $roles[ $this->translation_editor_role ] ); 101 102 if ( ! is_super_admin() && ! is_main_site() ) { 103 unset( $roles['administrator'] ); 104 } 105 106 return $roles; 107 } 108 109 /** 110 * Translates the "Translation Editor" role. 111 * 112 * @param string $translation Translated text. 113 * @param string $text Text to translate. 114 * @param string $context Context information for the translators. 115 * @param string $domain Text domain. 116 * @return string Translated user role. 117 */ 118 public function rename_user_roles( $translation, $text, $context, $domain ) { 119 if ( $domain !== 'default' || $context !== 'User role' ) { 120 return $translation; 121 } 122 123 if ( 'Translation Editor' === $text ) { 124 return __( 'Translation Editor', 'rosetta' ); 125 } 126 127 return $translation; 128 } 129 130 /** 131 * Replaces the "Role" column with a "Roles" column. 132 * 133 * @param array $columns An array of column headers. 134 * @return array An array of column headers. 135 */ 136 public function add_roles_column( $columns ) { 137 $posts = $columns['posts']; 138 unset( $columns['role'], $columns['posts'] ); 139 reset( $columns ); 140 $columns['roles'] = __( 'Roles', 'rosetta' ); 141 $columns['posts'] = $posts; 142 143 return $columns; 144 } 145 146 /** 147 * Displays a comma separated list of user's roles. 148 * 149 * @param string $output Custom column output. 150 * @param string $column_name Column name. 151 * @param int $user_id ID of the currently-listed user. 152 * @return string Comma separated list of user's roles. 153 */ 154 public function display_user_roles( $output, $column_name, $user_id ) { 155 global $wp_roles; 156 157 if ( 'roles' == $column_name ) { 158 $user_roles = array(); 159 $user = new WP_User( $user_id ); 160 foreach ( $user->roles as $role ) { 161 $role_name = $wp_roles->role_names[ $role ]; 162 $role_name = translate_user_role( $role_name ); 163 $user_roles[] = $role_name; 164 } 165 166 return implode( ', ', $user_roles ); 167 } 168 169 return $output; 170 } 171 172 /** 173 * Registers page for managing translation editors. 174 */ 175 public function register_translation_editors_page() { 176 $this->translation_editors_page = add_users_page( 177 __( 'Translation Editors', 'rosetta' ), 178 __( 'Translation Editors', 'rosetta' ), 179 'list_users', 180 'translation-editors', 181 array( $this, 'render_translation_editors_page' ) 182 ); 183 184 add_action( 'load-' . $this->translation_editors_page, array( $this, 'load_translation_editors_page' ) ); 185 add_action( 'admin_print_scripts-' . $this->translation_editors_page, array( $this, 'enqueue_scripts' ) ); 186 } 187 188 /** 189 * Enqueues scripts. 190 */ 191 public function enqueue_scripts() { 192 wp_enqueue_script( 'rosetta-roles', plugins_url( '/js/rosetta-roles.js', __FILE__ ), array( 'jquery' ), '1', true ); 193 } 194 195 /** 196 * Loads either the overview or the edit handler. 197 */ 198 public function load_translation_editors_page() { 199 if ( ! empty( $_REQUEST['user_id'] ) ) { 200 $this->load_edit_translation_editor( $_REQUEST['user_id'] ); 201 } else { 202 $this->load_translation_editors(); 203 } 204 } 205 206 /** 207 * Renders either the overview or the edit view. 208 */ 209 public function render_translation_editors_page() { 210 if ( ! empty( $_REQUEST['user_id'] ) ) { 211 $this->render_edit_translation_editor( $_REQUEST['user_id'] ); 212 } else { 213 $this->render_translation_editors(); 214 } 215 } 216 217 /** 218 * Handler for overview page. 219 */ 220 private function load_translation_editors() { 221 global $wpdb; 222 223 $list_table = $this->get_translation_editors_list_table(); 224 $action = $list_table->current_action(); 225 $redirect = menu_page_url( 'translation-editors', false ); 226 227 if ( $action ) { 228 switch ( $action ) { 229 case 'add-translation-editor': 230 check_admin_referer( 'add-translation-editor', '_nonce_add-translation-editor' ); 231 232 if ( ! current_user_can( 'promote_users' ) ) { 233 wp_redirect( $redirect ); 234 exit; 235 } 236 237 $user_details = null; 238 $user = wp_unslash( $_REQUEST['user'] ); 239 if ( false !== strpos( $user_email, '@' ) ) { 240 $user_details = get_user_by( 'email', $user ); 241 } else { 242 $user_details = get_user_by( 'login', $user ); 243 } 244 245 if ( ! $user_details ) { 246 wp_redirect( add_query_arg( array( 'error' => 'no-user-found' ), $redirect ) ); 247 exit; 248 } 249 250 if ( ! is_user_member_of_blog( $user_details->ID ) ) { 251 wp_redirect( add_query_arg( array( 'error' => 'not-a-member' ), $redirect ) ); 252 exit; 253 } 254 255 if ( user_can( $user_details, $this->translation_editor_role ) ) { 256 wp_redirect( add_query_arg( array( 'error' => 'user-exists' ), $redirect ) ); 257 exit; 258 } 259 260 $user_details->add_role( $this->translation_editor_role ); 261 $this->notify_translation_editor_update( $user_details->ID, 'add' ); 262 263 $projects = empty( $_REQUEST['projects'] ) ? '' : $_REQUEST['projects']; 264 if ( 'custom' === $projects ) { 265 $redirect = add_query_arg( 'user_id', $user_details->ID, $redirect ); 266 wp_redirect( add_query_arg( array( 'update' => 'user-added-custom-projects' ), $redirect ) ); 267 exit; 268 } 269 270 $meta_key = $wpdb->get_blog_prefix() . $this->project_access_meta_key; 271 update_user_meta( $user_details->ID, $meta_key, array( 'all' ) ); 272 273 wp_redirect( add_query_arg( array( 'update' => 'user-added' ), $redirect ) ); 274 exit; 275 case 'remove-translation-editors': 276 check_admin_referer( 'bulk-translation-editors' ); 277 278 if ( ! current_user_can( 'promote_users' ) ) { 279 wp_redirect( $redirect ); 280 exit; 281 } 282 283 if ( empty( $_REQUEST['translation-editors'] ) ) { 284 wp_redirect( $redirect ); 285 exit; 286 } 287 288 $count = 0; 289 $meta_key = $wpdb->get_blog_prefix() . $this->project_access_meta_key; 290 $user_ids = array_map( 'intval', (array) $_REQUEST['translation-editors'] ); 291 foreach ( $user_ids as $user_id ) { 292 $user = get_user_by( 'id', $user_id ); 293 $user->remove_role( $this->translation_editor_role ); 294 delete_user_meta( $user_id, $meta_key ); 295 $this->notify_translation_editor_update( $user_id, 'remove' ); 296 $count++; 297 } 298 299 wp_redirect( add_query_arg( array( 'update' => 'user-removed', 'count' => $count ), $redirect ) ); 300 exit; 301 case 'remove-translation-editor': 302 check_admin_referer( 'remove-translation-editor' ); 303 304 if ( ! current_user_can( 'promote_users' ) ) { 305 wp_redirect( $redirect ); 306 exit; 307 } 308 309 if ( empty( $_REQUEST['translation-editor'] ) ) { 310 wp_redirect( $redirect ); 311 exit; 312 } 313 314 $user_id = (int) $_REQUEST['translation-editor']; 315 $user = get_user_by( 'id', $user_id ); 316 $user->remove_role( $this->translation_editor_role ); 317 $meta_key = $wpdb->get_blog_prefix() . $this->project_access_meta_key; 318 delete_user_meta( $user_id, $meta_key ); 319 $this->notify_translation_editor_update( $user_id, 'remove' ); 320 321 wp_redirect( add_query_arg( array( 'update' => 'user-removed' ), $redirect ) ); 322 exit; 323 } 324 } 325 } 326 327 /** 328 * Handler for editing a translation editor. 329 * 330 * @param int $user_id User ID of a translation editor. 331 */ 332 private function load_edit_translation_editor( $user_id ) { 333 global $wpdb; 334 335 $redirect = menu_page_url( 'translation-editors', false ); 336 337 if ( ! current_user_can( 'promote_users' ) ) { 338 wp_redirect( $redirect ); 339 exit; 340 } 341 342 $user_details = get_user_by( 'id', $user_id ); 343 344 if ( ! $user_details ) { 345 wp_redirect( add_query_arg( array( 'error' => 'no-user-found' ), $redirect ) ); 346 exit; 347 } 348 349 if ( ! is_user_member_of_blog( $user_details->ID ) ) { 350 wp_redirect( add_query_arg( array( 'error' => 'not-a-member' ), $redirect ) ); 351 exit; 352 } 353 354 if ( ! user_can( $user_details, $this->translation_editor_role ) ) { 355 wp_redirect( add_query_arg( array( 'error' => 'user-cannot' ), $redirect ) ); 356 exit; 357 } 358 359 $action = empty( $_REQUEST['action'] ) ? '' : $_REQUEST['action']; 360 switch ( $action ) { 361 case 'update-translation-editor': 362 check_admin_referer( 'update-translation-editor_' . $user_details->ID ); 363 364 $redirect = add_query_arg( 'user_id', $user_details->ID, $redirect ); 365 366 $all_projects = $this->get_translate_top_level_projects(); 367 $all_projects = wp_list_pluck( $all_projects, 'id' ); 368 $all_projects = array_map( 'intval', $all_projects ); 369 370 $projects = (array) $_REQUEST['projects']; 371 if ( in_array( 'all', $projects ) ) { 372 $projects = array( 'all' ); 373 } else { 374 $projects = array_map( 'intval', $projects ); 375 $projects = array_values( array_intersect( $all_projects, $projects ) ); 376 } 377 378 $meta_key = $wpdb->get_blog_prefix() . $this->project_access_meta_key; 379 update_user_meta( $user_details->ID, $meta_key, $projects ); 380 381 wp_redirect( add_query_arg( array( 'update' => 'user-updated' ), $redirect ) ); 382 exit; 383 } 384 } 385 386 /** 387 * Renders the overview page. 388 */ 389 private function render_translation_editors() { 390 $list_table = $this->get_translation_editors_list_table(); 391 $list_table->prepare_items(); 392 393 $feedback_message = $this->get_feedback_message(); 394 395 require __DIR__ . '/views/translation-editors.php'; 396 } 397 398 /** 399 * Renders the edit page. 400 */ 401 private function render_edit_translation_editor( $user_id ) { 402 global $wpdb; 403 404 $projects = $this->get_translate_top_level_projects(); 405 406 $meta_key = $wpdb->get_blog_prefix() . $this->project_access_meta_key; 407 $project_access_list = get_user_meta( $user_id, $meta_key, true ); 408 if ( ! $project_access_list ) { 409 $project_access_list = array(); 410 } 411 412 $feedback_message = $this->get_feedback_message(); 413 414 require __DIR__ . '/views/edit-translation-editor.php'; 415 } 416 417 /** 418 * Returns a feedback message based on the current request. 419 * 420 * @return string HTML formatted message. 421 */ 422 private function get_feedback_message() { 423 $message = ''; 424 425 if ( ! empty( $_REQUEST['update'] ) && ! empty( $_REQUEST['error'] ) ) { 426 return $message; 427 } 428 429 $count = empty( $_REQUEST['count'] ) ? 1 : (int) $_REQUEST['count']; 430 431 $messages = array( 432 'update' => array( 433 'user-updated' => __( 'Translation editor updated.', 'rosetta' ), 434 'user-added' => __( 'New translation editor added.', 'rosetta' ), 435 'user-added-custom-projects' => __( 'New translation editor added. You can select the projects now.', 'rosetta' ), 436 'user-removed' => sprintf( _n( '%s translation editor removed.', '%s translation editors removed.', $count, 'rosetta' ), number_format_i18n( $count ) ), 437 ), 438 439 'error' => array( 440 'no-user-found' => __( 'The user couldn’t be found.', 'rosetta' ), 441 'not-a-member' => __( 'The user is not a member of this site.', 'rosetta' ), 442 'user-cannot' => __( 'The user is not a translation editor.', 'rosetta' ), 443 'user-exists' => __( 'The user is already a translation editor.', 'rosetta' ), 444 ), 445 ); 446 447 if ( isset( $_REQUEST['error'], $messages['error'][ $_REQUEST['error'] ] ) ) { 448 $message = sprintf( 449 '<div class="notice notice-error"><p>%s</p></div>', 450 $messages['error'][ $_REQUEST['error'] ] 451 ); 452 } elseif( isset( $_REQUEST['update'], $messages['update'][ $_REQUEST['update'] ] ) ) { 453 $message = sprintf( 454 '<div class="notice notice-success"><p>%s</p></div>', 455 $messages['update'][ $_REQUEST['update'] ] 456 ); 457 } 458 459 return $message; 460 } 461 462 /** 463 * Wrapper for the custom list table which lists translation editors. 464 * 465 * @return Rosetta_Translation_Editors_List_Table The list table. 466 */ 467 private function get_translation_editors_list_table() { 468 global $wpdb; 469 static $list_table; 470 471 require_once __DIR__ . '/class-translation-editors-list-table.php'; 472 473 if ( isset( $list_table ) ) { 474 return $list_table; 475 } 476 477 $args = array( 478 'user_role' => $this->translation_editor_role, 479 'projects' => $this->get_translate_top_level_projects(), 480 'project_access_meta_key' => $wpdb->get_blog_prefix() . $this->project_access_meta_key, 481 ); 482 $list_table = new Rosetta_Translation_Editors_List_Table( $args ); 483 484 return $list_table; 485 } 486 487 /** 488 * Notifies profiles.wordpress.org about a change. 489 * 490 * @param int $user_id User ID. 491 * @param string $action Can be 'add' or 'remove'. 492 */ 493 private function notify_translation_editor_update( $user_id, $action ) { 494 $args = array( 495 'body' => array( 496 'action' => 'wporg_handle_association', 497 'source' => 'polyglots', 498 'command' => $action, 499 'user_id' => $user_id, 500 'association' => 'translation-editor', 501 ) 502 ); 503 504 wp_remote_post( self::PROFILES_HANDLER_URL, $args ); 505 } 506 507 /** 508 * Fetches all top level projects from translate.wordpress.org. 509 * 510 * @return array List of projects. 511 */ 512 private function get_translate_top_level_projects() { 513 global $wpdb; 514 515 $cache = get_site_transient( 'translate-top-level-projects' ); 516 if ( false !== $cache ) { 517 return $cache; 518 } 519 520 $_projects = $wpdb->get_results( " 521 SELECT id, name 522 FROM translate_projects 523 WHERE parent_project_id IS NULL 524 ORDER BY name ASC 525 " ); 526 527 $projects = array(); 528 foreach ( $_projects as $project ) { 529 $projects[ $project->id ] = $project; 530 } 531 532 set_site_transient( 'translate-top-level-projects', $projects, DAY_IN_SECONDS ); 533 534 return $projects; 535 } 12 536 } 13 537 14 add_action( 'admin_menu', 'ros_remove_widgets_menu' ); 15 function ros_remove_widgets_menu() { 16 remove_submenu_page( 'themes.php', 'themes.php' ); 17 remove_submenu_page( 'themes.php', 'widgets.php' ); 18 } 19 20 add_filter( 'editable_roles', 'ros_editable_roles' ); 21 function ros_editable_roles( $roles ) { 22 $subscriber = $roles['subscriber']; 23 unset( $roles['subscriber'] ); 24 reset( $roles ); 25 $roles['subscriber'] = $subscriber; 26 if ( ! is_super_admin() && ! is_main_site() ) { 27 unset( $roles['administrator'] ); 28 } 29 return $roles; 30 } 31 32 add_filter( 'admin_init', 'ros_role_modifications' ); 33 function ros_role_modifications() { 34 if ( ! get_role( 'validator' ) ) { 35 add_role( 'validator', __( 'Validator', 'rosetta' ), array( 'read' => true, 'level_0' => true ) ); 36 } 37 $editor_role = get_role( 'editor' ); 38 if ( $editor_role && ! $editor_role->has_cap( 'remove_users' ) ) { 39 $editor_role->add_cap( 'edit_theme_options' ); 40 $editor_role->add_cap( 'list_users' ); 41 $editor_role->add_cap( 'promote_users' ); 42 $editor_role->add_cap( 'remove_users' ); 43 } 44 } 538 $GLOBALS['rosetta_roles'] = new Rosetta_Roles();
Note: See TracChangeset
for help on using the changeset viewer.