| @@ -23,15 +23,37 @@ | ||
| 23 | 23 | exit; |
| 24 | 24 | } |
| 25 | 25 | |
| 26 | 26 | /** |
| 27 | - * Additions to post overview admin screens. | |
| 27 | + * Additions to post overview admin screens: | |
| 28 | + * | |
| 29 | + * - Filter posts by group. | |
| 30 | + * - Apply bulk actions to add or remove group access restrictions. | |
| 28 | 31 | */ |
| 29 | 32 | class Groups_Admin_Posts { |
| 30 | 33 | |
| 31 | - const NOT_RESTRICTED = "#not-restricted#"; | |
| 34 | + /** | |
| 35 | + * Field name. | |
| 36 | + * | |
| 37 | + * @var string | |
| 38 | + */ | |
| 39 | + const GROUPS_READ = 'groups-read'; | |
| 32 | 40 | |
| 33 | 41 | /** |
| 42 | + * Constant for not restricted. | |
| 43 | + * | |
| 44 | + * @var string NOT_RESTRICTED | |
| 45 | + */ | |
| 46 | + const NOT_RESTRICTED = '#not-restricted#'; | |
| 47 | + | |
| 48 | + /** | |
| 49 | + * Constant for restricted. | |
| 50 | + * | |
| 51 | + * @var string RESTRICTED | |
| 52 | + */ | |
| 53 | + const RESTRICTED = '#restricted#'; | |
| 54 | + | |
| 55 | + /** | |
| 34 | 56 | * Sets up an admin_init hook where our actions and filters are added. |
| 35 | 57 | */ |
| 36 | 58 | public static function init() { |
| 37 | 59 | add_action( 'admin_init', array( __CLASS__, 'admin_init' ) ); |
| @@ -41,14 +63,18 @@ | ||
| 41 | 63 | * Adds actions and filters to handle filtering by access restriction |
| 42 | 64 | * capability. |
| 43 | 65 | */ |
| 44 | 66 | public static function admin_init() { |
| 45 | - if ( current_user_can( GROUPS_ACCESS_GROUPS ) ) { | |
| 67 | + if ( Groups_User::current_user_can( GROUPS_ACCESS_GROUPS ) ) { | |
| 46 | 68 | add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_enqueue_scripts' ) ); |
| 47 | 69 | add_action( 'admin_head', array( __CLASS__, 'admin_head' ) ); |
| 48 | 70 | add_action( 'restrict_manage_posts', array( __CLASS__, 'restrict_manage_posts' ) ); |
| 49 | - add_filter( 'parse_query', array( __CLASS__, 'parse_query' ) ); | |
| 71 | + // add_filter( 'parse_query', array( __CLASS__, 'parse_query' ) ); | |
| 50 | 72 | |
| 73 | + add_filter( 'posts_where', array( __CLASS__, 'posts_where' ), 10, 2 ); | |
| 74 | + add_filter( 'posts_join', array( __CLASS__, 'posts_join' ), 10, 2 ); | |
| 75 | + add_filter( 'posts_orderby', array( __CLASS__, 'posts_orderby' ), 10, 2 ); | |
| 76 | + | |
| 51 | 77 | add_action( 'bulk_edit_custom_box', array( __CLASS__, 'bulk_edit_custom_box' ), 10, 2); |
| 52 | 78 | add_action( 'save_post', array( __CLASS__, 'save_post' ) ); |
| 53 | 79 | } |
| 54 | 80 | } |
| @@ -60,12 +86,13 @@ | ||
| 60 | 86 | |
| 61 | 87 | global $pagenow; |
| 62 | 88 | |
| 63 | 89 | if ( $pagenow == 'edit.php' ) { |
| 64 | - $post_type = isset( $_GET['post_type'] ) ? $_GET['post_type'] : 'post'; | |
| 90 | + $post_type = groups_sanitize_get( 'post_type' ) ?? 'post'; | |
| 65 | 91 | $post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() ); |
| 66 | 92 | if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) { |
| 67 | 93 | Groups_UIE::enqueue( 'select' ); |
| 94 | + wp_enqueue_style( 'groups_admin_post' ); | |
| 68 | 95 | } |
| 69 | 96 | } |
| 70 | 97 | } |
| 71 | 98 | |
| @@ -76,23 +103,26 @@ | ||
| 76 | 103 | |
| 77 | 104 | global $pagenow; |
| 78 | 105 | |
| 79 | 106 | if ( $pagenow == 'edit.php' ) { |
| 80 | - $post_type = isset( $_GET['post_type'] ) ? $_GET['post_type'] : 'post'; | |
| 107 | + $post_type = groups_sanitize_get( 'post_type' ) ?? 'post'; | |
| 81 | 108 | $post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() ); |
| 82 | 109 | if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) { |
| 83 | 110 | echo '<style type="text/css">'; |
| 84 | - echo '.groups-capabilities-container { display: inline-block; line-height: 24px; padding-bottom: 1em; vertical-align: top; margin-left: 4px; margin-right: 4px; }'; | |
| 85 | - echo '.groups-capabilities-container .groups-select-container { display: inline-block; vertical-align: top; }'; | |
| 86 | - echo '.groups-capabilities-container .groups-select-container select, .groups-bulk-container select.groups-action { float: none; margin-right: 4px; vertical-align: top; }'; | |
| 87 | - echo '.groups-capabilities-container .selectize-control { min-width: 128px; }'; | |
| 88 | - echo '.groups-capabilities-container .selectize-control, .groups-bulk-container select.groups-action { margin-right: 4px; vertical-align: top; }'; | |
| 89 | - echo '.groups-capabilities-container .selectize-input { font-size: inherit; line-height: 18px; padding: 1px 2px 2px 2px; vertical-align: middle; }'; | |
| 90 | - echo '.groups-capabilities-container .selectize-input input[type="text"] { font-size: inherit; vertical-align: middle; }'; | |
| 91 | - echo '.groups-capabilities-container input.button { margin-top: 1px; vertical-align: top; }'; | |
| 111 | + echo '.groups-groups-container { display: inline-block; line-height: 24px; padding-bottom: 1em; vertical-align: top; margin-left: 4px; margin-right: 4px; }'; | |
| 112 | + echo '.groups-groups-container .groups-select-container { display: inline-block; vertical-align: top; }'; | |
| 113 | + echo '.groups-groups-container .groups-select-container select, .groups-bulk-container select.groups-action { float: none; margin-right: 4px; vertical-align: top; }'; | |
| 114 | + echo '.groups-groups-container .selectize-control { min-width: 128px; }'; | |
| 115 | + echo '.groups-groups-container .selectize-control, .groups-bulk-container select.groups-action { margin-right: 4px; vertical-align: top; }'; | |
| 116 | + echo '.groups-groups-container .selectize-input { min-height: 30px; font-size: inherit; line-height: 18px; padding: 0 4px; vertical-align: middle; }'; | |
| 117 | + echo '.groups-groups-container .selectize-input input[type="text"] { font-size: inherit; vertical-align: middle; }'; | |
| 118 | + echo '.groups-groups-container input.button { margin-top: 1px; vertical-align: top; }'; | |
| 92 | 119 | echo '.inline-edit-row fieldset .capabilities-bulk-container label span.title { min-width: 5em; padding: 2px 1em; width: auto; }'; |
| 93 | 120 | echo '.tablenav .actions { overflow: visible; }'; // this is important so that the selectize options aren't hidden |
| 94 | 121 | echo '.wp-list-table td { overflow: visible; }'; // idem for bulk actions |
| 122 | + echo 'label.groups-read-terms { vertical-align: middle; line-height: 28px; margin-right: 4px; }'; // Terms checkbox label | |
| 123 | + echo 'label.groups-read-terms input[type="checkbox"] { height: 1rem; }'; // Terms checkbox | |
| 124 | + echo 'th.column-groups, th.column-groups-read, th.column-capabilities { width:10%; }'; | |
| 95 | 125 | echo '</style>'; |
| 96 | 126 | } |
| 97 | 127 | } |
| 98 | 128 | } |
| @@ -97,19 +127,19 @@ | ||
| 97 | 127 | } |
| 98 | 128 | } |
| 99 | 129 | |
| 100 | 130 | /** |
| 101 | - * Renders the access restriction field. | |
| 131 | + * Renders the groups access restriction filter field. | |
| 102 | 132 | */ |
| 103 | 133 | public static function restrict_manage_posts() { |
| 104 | 134 | |
| 105 | - global $pagenow, $wpdb; | |
| 135 | + global $pagenow; | |
| 106 | 136 | |
| 107 | 137 | if ( is_admin() ) { |
| 108 | 138 | |
| 109 | 139 | if ( $pagenow == 'edit.php' ) { // check that we're on the right screen |
| 110 | 140 | |
| 111 | - $post_type = isset( $_GET['post_type'] ) ? $_GET['post_type'] : 'post'; | |
| 141 | + $post_type = groups_sanitize_get( 'post_type' ) ?? 'post'; | |
| 112 | 142 | $post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() ); |
| 113 | 143 | |
| 114 | 144 | if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) { |
| 115 | 145 | |
| @@ -115,36 +145,65 @@ | ||
| 115 | 145 | |
| 116 | 146 | $output = ''; |
| 117 | 147 | |
| 118 | 148 | // capabilities select |
| 119 | - $output .= '<div class="groups-capabilities-container">'; | |
| 120 | - $applicable_read_caps = Groups_Options::get_option( Groups_Post_Access::READ_POST_CAPABILITIES, array( Groups_Post_Access::READ_POST_CAPABILITY ) ); | |
| 149 | + $output .= '<div class="groups-groups-container">'; | |
| 121 | 150 | $output .= sprintf( |
| 122 | - '<select class="select capability" name="%s[]" multiple="multiple" placeholder="%s" data-placeholder="%s">', | |
| 123 | - esc_attr( Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ_POST_CAPABILITY ), | |
| 124 | - esc_attr( __( 'Access restrictions …', GROUPS_PLUGIN_DOMAIN ) ) , | |
| 125 | - esc_attr( __( 'Access restrictions …', GROUPS_PLUGIN_DOMAIN ) ) | |
| 151 | + '<select class="select group" name="%s[]" multiple="multiple" placeholder="%s" data-placeholder="%s">', | |
| 152 | + esc_attr( Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ ), | |
| 153 | + esc_attr__( 'Groups …', 'groups' ), | |
| 154 | + esc_attr__( 'Groups …', 'groups' ) | |
| 126 | 155 | ); |
| 127 | 156 | |
| 128 | - $previous_selected = array(); | |
| 129 | - if ( !empty( $_GET[Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ_POST_CAPABILITY] ) ) { | |
| 130 | - $previous_selected = $_GET[Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ_POST_CAPABILITY]; | |
| 131 | - if ( !is_array( $previous_selected ) ) { | |
| 132 | - $previous_selected = array(); | |
| 133 | - } | |
| 157 | + $read = groups_sanitize_get( Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ ); | |
| 158 | + if ( !is_array( $read ) ) { | |
| 159 | + $read = array(); | |
| 134 | 160 | } |
| 135 | - $selected = in_array( self::NOT_RESTRICTED, $previous_selected ) ? ' selected="selected" ' : ''; | |
| 136 | - $output .= sprintf( '<option value="%s" %s >%s</option>', self::NOT_RESTRICTED, esc_attr( $selected ), esc_attr( __( '(only unrestricted)', GROUPS_PLUGIN_DOMAIN ) ) ); | |
| 161 | + $output .= sprintf( | |
| 162 | + '<option value="%s" %s >%s</option>', self::NOT_RESTRICTED, | |
| 163 | + esc_attr( in_array( self::NOT_RESTRICTED, $read ) ? ' selected="selected" ' : '' ), | |
| 164 | + esc_attr__( '(none)', 'groups' ) | |
| 165 | + ); | |
| 166 | + $output .= sprintf( | |
| 167 | + '<option value="%s" %s >%s</option>', self::RESTRICTED, | |
| 168 | + esc_attr( in_array( self::RESTRICTED, $read ) ? ' selected="selected" ' : '' ), | |
| 169 | + esc_attr__( '(any)', 'groups' ) | |
| 170 | + ); | |
| 137 | 171 | |
| 138 | - foreach( $applicable_read_caps as $capability ) { | |
| 139 | - $selected = in_array( $capability, $previous_selected ) ? ' selected="selected" ' : ''; | |
| 140 | - $output .= sprintf( '<option value="%s" %s >%s</option>', esc_attr( $capability ), esc_attr( $selected ), wp_filter_nohtml_kses( $capability ) ); | |
| 172 | + $groups = apply_filters( | |
| 173 | + 'groups_admin_posts_restrict_manage_posts_get_groups', | |
| 174 | + Groups_Group::get_groups( | |
| 175 | + apply_filters( | |
| 176 | + 'groups_admin_posts_restrict_manage_posts_get_groups_options', | |
| 177 | + array( 'order_by' => 'name', 'order' => 'ASC' ) | |
| 178 | + ) | |
| 179 | + ) | |
| 180 | + ); | |
| 181 | + foreach ( $groups as $group ) { | |
| 182 | + $selected = in_array( $group->group_id, $read ) ? ' selected="selected" ' : ''; | |
| 183 | + $output .= sprintf( | |
| 184 | + '<option value="%s" %s >%s</option>', | |
| 185 | + esc_attr( $group->group_id ), | |
| 186 | + esc_attr( $selected ), | |
| 187 | + $group->name ? stripslashes( wp_filter_nohtml_kses( $group->name ) ) : '' | |
| 188 | + ); | |
| 141 | 189 | } |
| 142 | 190 | $output .= '</select>'; |
| 143 | 191 | $output .= '</div>'; |
| 144 | - $output .= Groups_UIE::render_select( '.select.capability' ); | |
| 192 | + $output .= Groups_UIE::render_select( '.select.group' ); | |
| 145 | 193 | |
| 146 | - echo $output; | |
| 194 | + if ( | |
| 195 | + function_exists( 'get_term_meta' ) && // >= WordPress 4.4.0 as we query the termmeta table | |
| 196 | + class_exists( 'Groups_Restrict_Categories' ) && | |
| 197 | + method_exists( 'Groups_Restrict_Categories', 'get_controlled_taxonomies' ) && | |
| 198 | + method_exists( 'Groups_Restrict_Categories', 'get_term_read_groups' ) // >= Groups Restrict Categories 2.0.0, the method isn't used here but it wouldn't make any sense to query unless we're >= 2.0.0 | |
| 199 | + ) { | |
| 200 | + $output .= sprintf( '<label class="groups-read-terms" title="%s">', esc_attr__( 'Also look for groups related to terms', 'groups' ) ); | |
| 201 | + $output .= sprintf( '<input type="checkbox" name="groups-read-terms" value="1" %s />', empty( groups_sanitize_get( 'groups-read-terms' ) ) ? '' : ' checked="checked" ' ); | |
| 202 | + $output .= esc_html__( 'Terms', 'groups' ); | |
| 203 | + $output .= '</label>'; | |
| 204 | + } | |
| 205 | + echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped | |
| 147 | 206 | } |
| 148 | 207 | |
| 149 | 208 | } |
| 150 | 209 | } |
| @@ -151,67 +210,72 @@ | ||
| 151 | 210 | |
| 152 | 211 | } |
| 153 | 212 | |
| 154 | 213 | /** |
| 155 | - * Bulk-edit access restriction capabilities. | |
| 156 | - * | |
| 214 | + * Bulk-edit access restriction groups. | |
| 215 | + * | |
| 157 | 216 | * @param string $column_name |
| 158 | 217 | * @param string $post_type |
| 159 | 218 | */ |
| 160 | 219 | public static function bulk_edit_custom_box( $column_name, $post_type ) { |
| 161 | 220 | |
| 162 | - global $pagenow, $wpdb; | |
| 221 | + global $pagenow; | |
| 163 | 222 | |
| 164 | - if ( $column_name == 'capabilities' ) { | |
| 165 | - | |
| 223 | + if ( $column_name == self::GROUPS_READ ) { | |
| 166 | 224 | if ( $pagenow == 'edit.php' ) { // check that we're on the right screen |
| 167 | 225 | |
| 168 | - $post_type = isset( $_GET['post_type'] ) ? $_GET['post_type'] : 'post'; | |
| 226 | + $post_type = groups_sanitize_get( 'post_type' ) ?? 'post'; | |
| 169 | 227 | $post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() ); |
| 170 | 228 | |
| 171 | 229 | if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) { |
| 172 | 230 | |
| 173 | 231 | $output = '<fieldset class="inline-edit-col-right">'; |
| 174 | - $output .= '<div class="bulk-edit-groups">'; | |
| 232 | + $output .= '<div class="bulk-edit-groups" style="padding:0 0.5em;">'; | |
| 175 | 233 | |
| 176 | 234 | // capability/access restriction bulk actions added through extra_tablenav() |
| 177 | - $output .= '<div id="capability-bulk-actions" class="capabilities-bulk-container" style="display:inline">'; | |
| 235 | + $output .= '<div id="group-bulk-actions" class="groups-bulk-container" style="display:inline">'; | |
| 178 | 236 | |
| 179 | 237 | $output .= '<label style="display:inline;">'; |
| 180 | 238 | $output .= '<span class="title">'; |
| 181 | - $output .= __( 'Access Restrictions', GROUPS_PLUGIN_DOMAIN ); | |
| 239 | + $output .= _x( 'Groups', 'Bulk edit field label', 'groups' ); | |
| 182 | 240 | $output .= '</span>'; |
| 183 | - $output .= '<select class="capabilities-action" name="capabilities-action">'; | |
| 184 | - $output .= '<option selected="selected" value="-1">' . __( '— No Change —', GROUPS_PLUGIN_DOMAIN ) . '</option>'; | |
| 185 | - $output .= '<option value="add-capability">' . __( 'Add restriction', GROUPS_PLUGIN_DOMAIN ) . '</option>'; | |
| 186 | - $output .= '<option value="remove-capability">' . __( 'Remove restriction', GROUPS_PLUGIN_DOMAIN ) . '</option>'; | |
| 241 | + $output .= '<select class="groups-action" name="groups-action">'; | |
| 242 | + $output .= '<option selected="selected" value="-1">' . esc_html__( '— No Change —', 'groups' ) . '</option>'; | |
| 243 | + $output .= '<option value="add-group">' . esc_html__( 'Add restriction', 'groups' ) . '</option>'; | |
| 244 | + $output .= '<option value="remove-group">' . esc_html__( 'Remove restriction', 'groups' ) . '</option>'; | |
| 187 | 245 | $output .= '</select>'; |
| 188 | 246 | $output .= '</label>'; |
| 189 | 247 | |
| 190 | - $output .= '<div class="groups-capabilities-container">'; | |
| 191 | - $valid_read_caps = Groups_Access_Meta_Boxes::get_valid_read_caps_for_user(); | |
| 248 | + $include = Groups_Access_Meta_Boxes::get_user_can_restrict_group_ids( get_current_user_id() ); | |
| 249 | + $groups = Groups_Group::get_groups( array( 'order_by' => 'name', 'order' => 'ASC', 'include' => $include ) ); | |
| 250 | + | |
| 251 | + $output .= '<div class="groups-groups-container">'; | |
| 192 | 252 | $output .= sprintf( |
| 193 | - '<select class="select bulk-capability" name="%s[]" multiple="multiple" placeholder="%s" data-placeholder="%s">', | |
| 194 | - esc_attr( Groups_Post_Access::POSTMETA_PREFIX . 'bulk-' . Groups_Post_Access::READ_POST_CAPABILITY ), | |
| 195 | - esc_attr( __( 'Choose access restrictions …', GROUPS_PLUGIN_DOMAIN ) ) , | |
| 196 | - esc_attr( __( 'Choose access restrictions …', GROUPS_PLUGIN_DOMAIN ) ) | |
| 253 | + '<select class="select bulk-group" name="%s[]" multiple="multiple" placeholder="%s" data-placeholder="%s">', | |
| 254 | + esc_attr( Groups_Post_Access::POSTMETA_PREFIX . 'bulk-' . Groups_Post_Access::READ ), | |
| 255 | + esc_attr__( 'Choose access restriction groups …', 'groups' ), | |
| 256 | + esc_attr__( 'Choose access restriction groups …', 'groups' ) | |
| 197 | 257 | ); |
| 198 | 258 | |
| 199 | - foreach( $valid_read_caps as $capability ) { | |
| 200 | - $output .= sprintf( '<option value="%s" >%s</option>', esc_attr( $capability ), wp_filter_nohtml_kses( $capability ) ); | |
| 259 | + foreach ( $groups as $group ) { | |
| 260 | + $output .= sprintf( | |
| 261 | + '<option value="%s" >%s</option>', | |
| 262 | + esc_attr( $group->group_id ), | |
| 263 | + $group->name ? stripslashes( wp_filter_nohtml_kses( $group->name ) ) : '' | |
| 264 | + ); | |
| 201 | 265 | } |
| 202 | 266 | $output .= '</select>'; |
| 203 | - $output .= '</div>'; // .groups-capabilities-container | |
| 204 | - $output .= Groups_UIE::render_select( '.select.bulk-capability' ); | |
| 267 | + $output .= '</div>'; // .groups-groups-container | |
| 268 | + $output .= Groups_UIE::render_select( '.select.bulk-group' ); | |
| 205 | 269 | |
| 206 | - $output .= '</div>'; // .capabilities-bulk-container | |
| 270 | + $output .= '</div>'; // .groups-bulk-container | |
| 207 | 271 | |
| 208 | 272 | $output .= '</div>'; // .bulk-edit-groups |
| 209 | 273 | $output .= '</fieldset>'; // .inline-edit-col-right |
| 210 | 274 | |
| 211 | - $output .= wp_nonce_field( 'post-capability', 'bulk-post-capability-nonce', true, false ); | |
| 275 | + $output .= wp_nonce_field( 'post-group', 'bulk-post-group-nonce', true, false ); | |
| 212 | 276 | |
| 213 | - echo $output; | |
| 277 | + echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped | |
| 214 | 278 | } |
| 215 | 279 | } |
| 216 | 280 | } |
| 217 | 281 | } |
| @@ -216,34 +280,39 @@ | ||
| 216 | 280 | } |
| 217 | 281 | } |
| 218 | 282 | |
| 219 | 283 | /** |
| 220 | - * Handles access restriction capability modifications from bulk-editing. | |
| 284 | + * Handles access restriction group modifications from bulk-editing. | |
| 221 | 285 | * This is called once for each post that is included in bulk-editing. |
| 222 | 286 | * The fields that are handled here are rendered through the |
| 223 | 287 | * bulk_edit_custom_box() method in this class. |
| 224 | - * | |
| 288 | + * | |
| 225 | 289 | * @param int $post_id |
| 226 | 290 | */ |
| 227 | 291 | public static function save_post( $post_id ) { |
| 228 | - if ( isset( $_REQUEST['capabilities-action'] ) ) { | |
| 229 | - if ( wp_verify_nonce( $_REQUEST['bulk-post-capability-nonce'], 'post-capability' ) ) { | |
| 230 | - $field = Groups_Post_Access::POSTMETA_PREFIX . 'bulk-' . Groups_Post_Access::READ_POST_CAPABILITY; | |
| 231 | - if ( !empty( $_REQUEST[$field] ) && is_array( $_REQUEST[$field] ) ) { | |
| 292 | + if ( isset( $_REQUEST['groups-action'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 293 | + if ( groups_verify_request_nonce( 'bulk-post-group-nonce', 'post-group' ) ) { | |
| 294 | + $read = groups_sanitize_request( Groups_Post_Access::POSTMETA_PREFIX . 'bulk-' . Groups_Post_Access::READ ); | |
| 295 | + if ( !empty( $read ) && is_array( $read ) ) { | |
| 232 | 296 | if ( Groups_Access_Meta_Boxes::user_can_restrict() ) { |
| 233 | - $valid_read_caps = Groups_Access_Meta_Boxes::get_valid_read_caps_for_user(); | |
| 234 | - foreach( $_REQUEST[$field] as $capability_name ) { | |
| 235 | - if ( $capability = Groups_Capability::read_by_capability( $capability_name ) ) { | |
| 236 | - if ( in_array( $capability->capability, $valid_read_caps ) ) { | |
| 237 | - switch( $_REQUEST['capabilities-action'] ) { | |
| 238 | - case 'add-capability' : | |
| 297 | + $include = Groups_Access_Meta_Boxes::get_user_can_restrict_group_ids(); | |
| 298 | + $groups = Groups_Group::get_groups( array( 'order_by' => 'name', 'order' => 'ASC', 'include' => $include ) ); | |
| 299 | + $group_ids = array(); | |
| 300 | + foreach ( $groups as $group ) { | |
| 301 | + $group_ids[] = $group->group_id; | |
| 302 | + } | |
| 303 | + foreach ( $read as $group_id ) { | |
| 304 | + if ( $group = Groups_Group::read( $group_id ) ) { | |
| 305 | + if ( in_array( $group->group_id, $group_ids ) ) { | |
| 306 | + switch ( groups_sanitize_request( 'groups-action' ) ) { | |
| 307 | + case 'add-group' : | |
| 239 | 308 | Groups_Post_Access::create( array( |
| 240 | 309 | 'post_id' => $post_id, |
| 241 | - 'capability' => $capability->capability | |
| 310 | + 'group_id' => $group->group_id | |
| 242 | 311 | ) ); |
| 243 | 312 | break; |
| 244 | - case 'remove-capability' : | |
| 245 | - Groups_Post_Access::delete( $post_id, $capability->capability ); | |
| 313 | + case 'remove-group' : | |
| 314 | + Groups_Post_Access::delete( $post_id, array( 'groups_read' => $group->group_id ) ); | |
| 246 | 315 | break; |
| 247 | 316 | } |
| 248 | 317 | } |
| 249 | 318 | } |
| @@ -254,11 +323,12 @@ | ||
| 254 | 323 | } |
| 255 | 324 | } |
| 256 | 325 | |
| 257 | 326 | /** |
| 258 | - * Query modifier to take the selected access restriction capability into | |
| 259 | - * account. | |
| 260 | - * | |
| 327 | + * Query modifier to take the selected access restriction groups into account. | |
| 328 | + * | |
| 329 | + * @deprecated not used | |
| 330 | + * | |
| 261 | 331 | * @param WP_Query $query query object passed by reference |
| 262 | 332 | */ |
| 263 | 333 | public static function parse_query( &$query ) { |
| 264 | 334 | |
| @@ -267,67 +337,70 @@ | ||
| 267 | 337 | if ( is_admin() ) { |
| 268 | 338 | |
| 269 | 339 | if ( $pagenow == 'edit.php' ) { // check that we're on the right screen |
| 270 | 340 | |
| 271 | - $post_type = isset( $_GET['post_type'] ) ? $_GET['post_type'] : 'post'; | |
| 341 | + $post_type = groups_sanitize_get( 'post_type' ) ?? 'post'; | |
| 272 | 342 | $post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() ); |
| 273 | 343 | |
| 274 | 344 | if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) { |
| 275 | 345 | |
| 276 | - if ( !empty( $_GET[Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ_POST_CAPABILITY] ) && | |
| 277 | - is_array( $_GET[Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ_POST_CAPABILITY] ) | |
| 278 | - ) { | |
| 346 | + $read = groups_sanitize_get( Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ ); | |
| 347 | + if ( !is_array( $read ) ) { | |
| 348 | + $read = array(); | |
| 349 | + } | |
| 279 | 350 | |
| 351 | + if ( count( $read ) > 0 ) { | |
| 352 | + | |
| 280 | 353 | $include_unrestricted = false; |
| 281 | - if ( in_array( self::NOT_RESTRICTED, $_GET[Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ_POST_CAPABILITY] ) ) { | |
| 354 | + if ( in_array( self::NOT_RESTRICTED, $read ) ) { | |
| 282 | 355 | $include_unrestricted = true; |
| 283 | 356 | } |
| 284 | 357 | |
| 285 | - $capabilities = array(); | |
| 286 | - foreach ( $_GET[Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ_POST_CAPABILITY] as $capability ) { | |
| 287 | - if ( Groups_Capability::read_by_capability( $capability ) ) { | |
| 288 | - $capabilities[] = $capability; | |
| 358 | + $group_ids = array(); | |
| 359 | + foreach ( $read as $group_id ) { | |
| 360 | + if ( Groups_Group::read( $group_id ) ) { | |
| 361 | + $group_ids[] = $group_id; | |
| 289 | 362 | } |
| 290 | 363 | } |
| 291 | 364 | |
| 292 | - if ( !empty( $capabilities ) ) { | |
| 365 | + if ( !empty( $group_ids ) ) { // @phpstan-ignore empty.variable | |
| 293 | 366 | if ( $include_unrestricted ) { |
| 294 | 367 | // meta_query does not handle a conjunction |
| 295 | 368 | // on the same meta field correctly |
| 296 | 369 | // (at least not up to WordPress 3.7.1) |
| 297 | -// $query->query_vars['meta_query'] = array ( | |
| 298 | -// 'relation' => 'OR', | |
| 299 | -// array ( | |
| 300 | -// 'key' => Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ_POST_CAPABILITY, | |
| 301 | -// 'value' => $capabilities, | |
| 302 | -// 'compare' => 'IN' | |
| 303 | -// ), | |
| 304 | -// array ( | |
| 305 | -// 'key' => Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ_POST_CAPABILITY, | |
| 306 | -// 'compare' => 'NOT EXISTS' | |
| 307 | -// ) | |
| 308 | -// ); | |
| 370 | + // $query->query_vars['meta_query'] = array ( | |
| 371 | + // 'relation' => 'OR', | |
| 372 | + // array ( | |
| 373 | + // 'key' => Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ, | |
| 374 | + // 'value' => $group_ids, | |
| 375 | + // 'compare' => 'IN' | |
| 376 | + // ), | |
| 377 | + // array ( | |
| 378 | + // 'key' => Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ, | |
| 379 | + // 'compare' => 'NOT EXISTS' | |
| 380 | + // ) | |
| 381 | + // ); | |
| 309 | 382 | // we'll limit it to show just unrestricted entries |
| 310 | 383 | // until the above is solved |
| 311 | - $query->query_vars['meta_query'] = array ( | |
| 384 | + $query->query_vars['meta_query'] = array ( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query | |
| 312 | 385 | array ( |
| 313 | - 'key' => Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ_POST_CAPABILITY, | |
| 386 | + 'key' => Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ, | |
| 314 | 387 | 'compare' => 'NOT EXISTS' |
| 315 | 388 | ) |
| 316 | 389 | ); |
| 317 | 390 | } else { |
| 318 | - $query->query_vars['meta_query'] = array ( | |
| 391 | + $query->query_vars['meta_query'] = array ( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query | |
| 319 | 392 | array ( |
| 320 | - 'key' => Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ_POST_CAPABILITY, | |
| 321 | - 'value' => $capabilities, | |
| 393 | + 'key' => Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ, | |
| 394 | + 'value' => $group_ids, | |
| 322 | 395 | 'compare' => 'IN' |
| 323 | 396 | ) |
| 324 | 397 | ); |
| 325 | 398 | } |
| 326 | 399 | } else if ( $include_unrestricted ) { |
| 327 | - $query->query_vars['meta_query'] = array ( | |
| 400 | + $query->query_vars['meta_query'] = array ( //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query | |
| 328 | 401 | array ( |
| 329 | - 'key' => Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ_POST_CAPABILITY, | |
| 402 | + 'key' => Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ, | |
| 330 | 403 | 'compare' => 'NOT EXISTS' |
| 331 | 404 | ) |
| 332 | 405 | ); |
| 333 | 406 | } |
| @@ -338,6 +411,273 @@ | ||
| 338 | 411 | } |
| 339 | 412 | |
| 340 | 413 | } |
| 341 | 414 | |
| 415 | + /** | |
| 416 | + * Filters out posts by group. This is used when you choose groups on the post admin screen so that | |
| 417 | + * only those posts who are restricted by groups are shown. | |
| 418 | + * | |
| 419 | + * @param string $where | |
| 420 | + * @param WP_Query $query | |
| 421 | + * | |
| 422 | + * @return string | |
| 423 | + */ | |
| 424 | + public static function posts_where( $where, $query ) { | |
| 425 | + | |
| 426 | + global $wpdb; | |
| 427 | + | |
| 428 | + if ( self::extend_for_filter_groups_read( $query ) ) { // also checks that $_GET[Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ] is an array | |
| 429 | + | |
| 430 | + $post_in = array(); | |
| 431 | + $term_in = array(); | |
| 432 | + | |
| 433 | + $filter_terms = false; | |
| 434 | + if ( | |
| 435 | + !empty( $_GET['groups-read-terms'] ) && // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 436 | + function_exists( 'get_term_meta' ) && // >= WordPress 4.4.0 as we query the termmeta table | |
| 437 | + class_exists( 'Groups_Restrict_Categories' ) && | |
| 438 | + method_exists( 'Groups_Restrict_Categories', 'get_controlled_taxonomies' ) && | |
| 439 | + method_exists( 'Groups_Restrict_Categories', 'get_term_read_groups' ) // >= Groups Restrict Categories 2.0.0, the method isn't used here but it wouldn't make any sense to query unless we're >= 2.0.0 | |
| 440 | + ) { | |
| 441 | + $filter_terms = true; | |
| 442 | + } | |
| 443 | + | |
| 444 | + $read = groups_sanitize_get( Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ ); | |
| 445 | + if ( !is_array( $read ) ) { | |
| 446 | + $read = array(); | |
| 447 | + } | |
| 448 | + | |
| 449 | + if ( in_array( self::NOT_RESTRICTED, $read ) ) { | |
| 450 | + $condition = | |
| 451 | + "SELECT ID post_id FROM $wpdb->posts " . | |
| 452 | + "WHERE ID NOT IN (" . | |
| 453 | + "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'groups-read'"; | |
| 454 | + if ( $filter_terms ) { | |
| 455 | + $condition .= | |
| 456 | + " UNION ALL " . | |
| 457 | + "SELECT p.ID post_id FROM $wpdb->posts p " . | |
| 458 | + "LEFT JOIN $wpdb->term_relationships tr ON p.ID = tr.object_id " . | |
| 459 | + "LEFT JOIN $wpdb->term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id " . | |
| 460 | + "LEFT JOIN $wpdb->termmeta tm ON tt.term_id = tm.term_id " . | |
| 461 | + "WHERE tm.meta_key = 'groups-read'"; | |
| 462 | + } | |
| 463 | + $condition .= ")"; | |
| 464 | + $post_in[] = $condition; | |
| 465 | + } | |
| 466 | + | |
| 467 | + if ( in_array( self::RESTRICTED, $read ) ) { | |
| 468 | + $condition = "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = 'groups-read'"; | |
| 469 | + if ( $filter_terms ) { | |
| 470 | + $condition .= | |
| 471 | + " UNION ALL " . | |
| 472 | + "SELECT p.ID post_id FROM $wpdb->posts p " . | |
| 473 | + "LEFT JOIN $wpdb->term_relationships tr ON p.ID = tr.object_id " . | |
| 474 | + "LEFT JOIN $wpdb->term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id " . | |
| 475 | + "LEFT JOIN $wpdb->termmeta tm ON tt.term_id = tm.term_id " . | |
| 476 | + "WHERE tm.meta_key = 'groups-read'"; | |
| 477 | + } | |
| 478 | + $post_in[] = $condition; | |
| 479 | + } | |
| 480 | + | |
| 481 | + $group_ids = array(); | |
| 482 | + foreach ( $read as $group_id ) { | |
| 483 | + if ( $group_id = Groups_Utility::id( $group_id ) ) { | |
| 484 | + if ( Groups_Group::read( $group_id ) ) { | |
| 485 | + $group_ids[] = $group_id; | |
| 486 | + } | |
| 487 | + } | |
| 488 | + } | |
| 489 | + | |
| 490 | + if ( !empty( $group_ids ) ) { // @phpstan-ignore empty.variable | |
| 491 | + $groups = ' ( ' . implode( ',', esc_sql( $group_ids ) ) . ' ) '; | |
| 492 | + $condition = | |
| 493 | + "SELECT post_id FROM $wpdb->postmeta " . | |
| 494 | + "WHERE meta_key = 'groups-read' AND meta_value IN $groups"; | |
| 495 | + if ( $filter_terms ) { | |
| 496 | + $condition .= | |
| 497 | + " UNION ALL " . | |
| 498 | + "SELECT p.ID post_id FROM $wpdb->posts p " . | |
| 499 | + "LEFT JOIN $wpdb->term_relationships tr ON p.ID = tr.object_id " . | |
| 500 | + "LEFT JOIN $wpdb->term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id " . | |
| 501 | + "LEFT JOIN $wpdb->termmeta tm ON tt.term_id = tm.term_id " . | |
| 502 | + "WHERE tm.meta_key = 'groups-read' AND tm.meta_value IN $groups"; | |
| 503 | + } | |
| 504 | + $post_in[] = $condition; | |
| 505 | + } | |
| 506 | + | |
| 507 | + if ( count( $post_in ) > 0 ) { | |
| 508 | + if ( | |
| 509 | + !empty( $_GET['groups-read-terms'] ) && // phpcs:ignore WordPress.Security.NonceVerification.Recommended | |
| 510 | + function_exists( 'get_term_meta' ) && // >= WordPress 4.4.0 as we query the termmeta table | |
| 511 | + class_exists( 'Groups_Restrict_Categories' ) && | |
| 512 | + method_exists( 'Groups_Restrict_Categories', 'get_controlled_taxonomies' ) && | |
| 513 | + method_exists( 'Groups_Restrict_Categories', 'get_term_read_groups' ) // >= Groups Restrict Categories 2.0.0, the method isn't used here but it wouldn't make any sense to query unless we're >= 2.0.0 | |
| 514 | + ) { | |
| 515 | + $post_in = array_merge( $post_in, $term_in ); | |
| 516 | + } | |
| 517 | + $id_in = implode( ' UNION ALL ', $post_in ); | |
| 518 | + $where .= " AND $wpdb->posts.ID IN ( $id_in ) "; | |
| 519 | + } | |
| 520 | + | |
| 521 | + } | |
| 522 | + | |
| 523 | + return $where; | |
| 524 | + } | |
| 525 | + | |
| 526 | + /** | |
| 527 | + * Adds to the join to allow advanced sorting by group on the admin back end for post tables. | |
| 528 | + * | |
| 529 | + * @param string $join | |
| 530 | + * @param WP_Query $query | |
| 531 | + */ | |
| 532 | + public static function posts_join( $join, $query ) { | |
| 533 | + global $wpdb; | |
| 534 | + if ( self::extend_for_orderby_groups_read( $query ) ) { | |
| 535 | + $group_table = _groups_get_tablename( 'group' ); | |
| 536 | + if ( function_exists( 'get_term_meta' ) ) { // >= WordPress 4.4.0 as we query the termmeta table | |
| 537 | + $join .= " | |
| 538 | + LEFT JOIN ( | |
| 539 | + SELECT p.ID post_id, GROUP_CONCAT(DISTINCT groups_read.group_name ORDER BY groups_read.group_name) groups | |
| 540 | + FROM $wpdb->posts p | |
| 541 | + LEFT JOIN ( | |
| 542 | + SELECT post_id, g.name group_name | |
| 543 | + FROM $wpdb->postmeta pm | |
| 544 | + LEFT JOIN $group_table g ON pm.meta_value = g.group_id | |
| 545 | + WHERE pm.meta_key = 'groups-read' | |
| 546 | + UNION ALL | |
| 547 | + SELECT p.ID post_id, g.name group_name | |
| 548 | + FROM $wpdb->posts p | |
| 549 | + LEFT JOIN $wpdb->term_relationships tr ON p.ID = tr.object_id | |
| 550 | + LEFT JOIN $wpdb->term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id | |
| 551 | + LEFT JOIN $wpdb->termmeta tm ON tt.term_id = tm.term_id | |
| 552 | + LEFT JOIN $group_table g ON tm.meta_value = g.group_id | |
| 553 | + WHERE tm.meta_key = 'groups-read' | |
| 554 | + ) as groups_read ON p.ID = groups_read.post_id | |
| 555 | + GROUP BY p.ID | |
| 556 | + ) groups_tmp ON $wpdb->posts.ID = groups_tmp.post_id | |
| 557 | + "; | |
| 558 | + } else { | |
| 559 | + $join .= " | |
| 560 | + LEFT JOIN ( | |
| 561 | + SELECT p.ID post_id, GROUP_CONCAT(DISTINCT groups_read.group_name ORDER BY groups_read.group_name) groups | |
| 562 | + FROM $wpdb->posts p | |
| 563 | + LEFT JOIN ( | |
| 564 | + SELECT post_id, g.name group_name | |
| 565 | + FROM $wpdb->postmeta pm | |
| 566 | + LEFT JOIN $group_table g ON pm.meta_value = g.group_id | |
| 567 | + WHERE pm.meta_key = 'groups-read' | |
| 568 | + ) as groups_read ON p.ID = groups_read.post_id | |
| 569 | + GROUP BY p.ID | |
| 570 | + ) groups_tmp ON $wpdb->posts.ID = groups_tmp.post_id | |
| 571 | + "; | |
| 572 | + } | |
| 573 | + } | |
| 574 | + return $join; | |
| 575 | + } | |
| 576 | + | |
| 577 | + /** | |
| 578 | + * Extend the orderby clause to sort by groups related to the post and its terms. | |
| 579 | + * | |
| 580 | + * @param $string $orderby | |
| 581 | + * @param WP_Query $query | |
| 582 | + * | |
| 583 | + * @return string | |
| 584 | + */ | |
| 585 | + public static function posts_orderby( $orderby, $query ) { | |
| 586 | + if ( self::extend_for_orderby_groups_read( $query ) ) { | |
| 587 | + switch ( $query->get( 'order' ) ) { | |
| 588 | + case 'desc' : | |
| 589 | + case 'DESC' : | |
| 590 | + $order = 'DESC'; | |
| 591 | + break; | |
| 592 | + default : | |
| 593 | + $order = 'ASC'; | |
| 594 | + } | |
| 595 | + $prefix = ' groups_tmp.groups ' . $order; | |
| 596 | + if ( !empty( $orderby ) ) { | |
| 597 | + $prefix .= ' , '; | |
| 598 | + } | |
| 599 | + $orderby = $prefix . $orderby; | |
| 600 | + } | |
| 601 | + return $orderby; | |
| 602 | + } | |
| 603 | + | |
| 604 | + /** | |
| 605 | + * Check if we should apply our posts_join and posts_orderby filters. Used in those. | |
| 606 | + * | |
| 607 | + * @param WP_Query $query | |
| 608 | + * | |
| 609 | + * @return boolean | |
| 610 | + */ | |
| 611 | + private static function extend_for_orderby_groups_read( &$query ) { | |
| 612 | + $result = false; | |
| 613 | + if ( is_admin() ) { | |
| 614 | + // check if query is for a post type we handle | |
| 615 | + $post_types = $query->get( 'post_type' ); | |
| 616 | + if ( !is_array( $post_types ) ) { | |
| 617 | + $post_types = array( $post_types ); | |
| 618 | + } | |
| 619 | + foreach ( $post_types as $post_type ) { | |
| 620 | + $post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() ); | |
| 621 | + if ( | |
| 622 | + !isset( $post_types_option[$post_type]['add_meta_box'] ) || | |
| 623 | + $post_types_option[$post_type]['add_meta_box'] | |
| 624 | + ) { | |
| 625 | + // only act on post etc. screens | |
| 626 | + $screen = get_current_screen(); | |
| 627 | + if ( | |
| 628 | + !empty( $screen ) && | |
| 629 | + !empty( $screen->id ) && | |
| 630 | + ( $screen->id == 'edit-' . $post_type ) | |
| 631 | + ) { | |
| 632 | + if ( $query->get( 'orderby' ) == self::GROUPS_READ ) { | |
| 633 | + $result = true; | |
| 634 | + break; | |
| 635 | + } | |
| 636 | + } | |
| 637 | + } | |
| 638 | + } | |
| 639 | + } | |
| 640 | + return $result; | |
| 641 | + } | |
| 642 | + | |
| 643 | + /** | |
| 644 | + * Check if we should apply our posts_where filter. Used in it. | |
| 645 | + * | |
| 646 | + * @param WP_Query $query | |
| 647 | + * | |
| 648 | + * @return boolean | |
| 649 | + */ | |
| 650 | + private static function extend_for_filter_groups_read( &$query ) { | |
| 651 | + $result = false; | |
| 652 | + if ( is_admin() ) { | |
| 653 | + // check if query is for a post type we handle | |
| 654 | + $post_types = $query->get( 'post_type' ); | |
| 655 | + $post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() ); | |
| 656 | + if ( !is_array( $post_types ) ) { | |
| 657 | + $post_types = array( $post_types ); | |
| 658 | + } | |
| 659 | + foreach ( $post_types as $post_type ) { | |
| 660 | + if ( | |
| 661 | + !isset( $post_types_option[$post_type]['add_meta_box'] ) || | |
| 662 | + $post_types_option[$post_type]['add_meta_box'] | |
| 663 | + ) { | |
| 664 | + // only act on post etc. screens | |
| 665 | + $screen = get_current_screen(); | |
| 666 | + if ( | |
| 667 | + !empty( $screen ) && | |
| 668 | + !empty( $screen->id ) && | |
| 669 | + ( $screen->id == 'edit-' . $post_type ) | |
| 670 | + ) { | |
| 671 | + $read = groups_sanitize_get( Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ ); | |
| 672 | + if ( !empty( $read ) && is_array( $read ) ) { | |
| 673 | + $result = true; | |
| 674 | + break; | |
| 675 | + } | |
| 676 | + } | |
| 677 | + } | |
| 678 | + } | |
| 679 | + } | |
| 680 | + return $result; | |
| 681 | + } | |
| 342 | 682 | } |
| 343 | 683 | Groups_Admin_Posts::init(); |