AdminCustomizationBase.php
2 years ago
AdminCustomizationFactory.php
2 years ago
AdminMenuItems.php
4 years ago
AdminMenuSanitization.php
2 years ago
AdminMenuItems.php
356 lines
| 1 | <?php |
| 2 | namespace NestedPages\Entities\AdminCustomization; |
| 3 | |
| 4 | /** |
| 5 | * Custom Admin Menu Items |
| 6 | */ |
| 7 | class AdminMenuItems extends AdminCustomizationBase |
| 8 | { |
| 9 | public function __construct() |
| 10 | { |
| 11 | parent::__construct(); |
| 12 | add_action('admin_menu', [$this, 'setOriginalMenus']); |
| 13 | add_action('admin_menu', [$this, 'createCustomMenu']); |
| 14 | add_action('admin_menu', [$this, 'relabelItems']); |
| 15 | add_action('admin_menu', [$this, 'createSubmenus']); |
| 16 | } |
| 17 | |
| 18 | /** |
| 19 | * Save the original menu before it is modified (administrators) |
| 20 | */ |
| 21 | public function setOriginalMenus() |
| 22 | { |
| 23 | global $menu; |
| 24 | global $submenu; |
| 25 | global $np_menu_original; |
| 26 | global $np_submenu_original; |
| 27 | $np_menu_original = $menu; |
| 28 | $np_submenu_original = $submenu; |
| 29 | $this->buildNestedMenu(); |
| 30 | } |
| 31 | |
| 32 | /** |
| 33 | * Relabel Items |
| 34 | */ |
| 35 | public function relabelItems() |
| 36 | { |
| 37 | if ( !$this->settings->adminCustomEnabled('enabled_menu') ) return; |
| 38 | $menu_options = $this->settings->adminCustomEnabled('nav_menu_options'); |
| 39 | if ( empty($menu_options) ) return; |
| 40 | |
| 41 | global $menu; |
| 42 | |
| 43 | // Label |
| 44 | foreach ( $menu as $key => $item ){ |
| 45 | if ( isset($menu_options[$this->current_user_role][$item[2]]) && isset($menu_options[$this->current_user_role][$item[2]]['label']) ){ |
| 46 | if ( $menu_options[$this->current_user_role][$item[2]]['label'] == '' ) continue; |
| 47 | |
| 48 | $custom_label = sanitize_text_field($menu_options[$this->current_user_role][$item[2]]['label']); |
| 49 | // Add notification counts if needed |
| 50 | $dom = new \DOMDocument; |
| 51 | $dom->loadHtml($item[0]); |
| 52 | $span_tags = $dom->getElementsByTagName('span'); |
| 53 | if ( $span_tags->length > 0 ) $custom_label .= "<span class='update-plugins count-{$span_tags[0]->nodeValue}'><span class='plugin-count'>{$span_tags[0]->nodeValue}</span></span>"; |
| 54 | $menu[$key][0] = $custom_label; |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | // Icon |
| 59 | foreach ( $menu as $key => $item ){ |
| 60 | if ( !isset($item[6]) ) continue; |
| 61 | if ( isset($menu_options[$this->current_user_role][$item[2]]) && isset($menu_options[$this->current_user_role][$item[2]]['icon']) ){ |
| 62 | if ( $menu_options[$this->current_user_role][$item[2]]['icon'] == '' ) continue; |
| 63 | $menu[$key][6] = sanitize_text_field($menu_options[$this->current_user_role][$item[2]]['icon']); |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | // Link |
| 68 | foreach ( $menu as $key => $item ){ |
| 69 | if ( !isset($item[2]) ) continue; |
| 70 | if ( isset($menu_options[$this->current_user_role][$item[2]]) && isset($menu_options[$this->current_user_role][$item[2]]['link']) ){ |
| 71 | if ( $menu_options[$this->current_user_role][$item[2]]['link'] == '' ) continue; |
| 72 | $menu[$key][2] = sanitize_text_field($menu_options[$this->current_user_role][$item[2]]['link']); |
| 73 | } |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | /** |
| 78 | * Reorder Menu Items |
| 79 | * (Can't hook into the menu_order filter because it receives links. Links may change in plugin settings) |
| 80 | */ |
| 81 | public function createCustomMenu() |
| 82 | { |
| 83 | $this->setCurrentUserRoles(); |
| 84 | global $menu; |
| 85 | global $np_menu_original; |
| 86 | |
| 87 | if ( !$this->settings->adminCustomEnabled('enabled_menu') ) return; |
| 88 | $menu_options = $this->settings->adminCustomEnabled('nav_menu_options'); |
| 89 | if ( !$menu_options ) return; |
| 90 | if ( !isset($menu_options[$this->current_user_role]) ) return; |
| 91 | |
| 92 | $new_menu = array(); |
| 93 | $order = 1; |
| 94 | $separator_index = 1; |
| 95 | |
| 96 | foreach ( $menu_options[$this->current_user_role] as $key => $item ){ |
| 97 | |
| 98 | // Settings can't be hidden (unable to return to reset customizations) |
| 99 | if ( isset($item['hidden']) && $item['link'] !== 'options-general.php' ) continue; |
| 100 | |
| 101 | $slug = ( isset($item['original_link']) ) ? $item['original_link'] : $key; |
| 102 | |
| 103 | // Loop through the original menu and get the item if it exists (associated through original link) |
| 104 | foreach ( $menu as $original_item ){ |
| 105 | if ( isset($original_item[2]) && $original_item[2] == $slug ){ |
| 106 | $new_menu[$order] = $original_item; |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | // Add Separators |
| 111 | if ( strpos($key, 'custom_sep') !== false || strpos($key, 'separator') !== false ) { |
| 112 | $new_menu[$order] = array('', 'read', 'separator' . $separator_index, '', 'wp-menu-separator'); |
| 113 | $separator_index++; |
| 114 | } |
| 115 | |
| 116 | $order++; |
| 117 | } |
| 118 | |
| 119 | |
| 120 | // Append top level menu items added by plugins since customization |
| 121 | foreach ( $np_menu_original[$this->current_user_role] as $key => $item) : |
| 122 | $exists = false; |
| 123 | foreach ( $new_menu as $new_menu_item ) : |
| 124 | if ( $item[2] == $new_menu_item[2] ) $exists = true;; |
| 125 | endforeach; |
| 126 | $is_hidden = false; |
| 127 | foreach ( $menu_options[$this->current_user_role] as $menu_option ) : |
| 128 | if ( !isset($menu_option['link']) ) continue; |
| 129 | if ( $item[2] == $menu_option['link'] && isset($menu_option['hidden']) ) $is_hidden = true; |
| 130 | endforeach; |
| 131 | if ( !$exists && $item[1] !== 'read' && !$is_hidden ) $new_menu[] = $item; |
| 132 | endforeach; |
| 133 | |
| 134 | $menu = $new_menu; |
| 135 | } |
| 136 | |
| 137 | /** |
| 138 | * Build the Submenus |
| 139 | */ |
| 140 | public function createSubmenus() |
| 141 | { |
| 142 | global $submenu; |
| 143 | global $np_submenu_original; |
| 144 | $original_submenu = $np_submenu_original; |
| 145 | |
| 146 | if ( !$this->settings->adminCustomEnabled('enabled_menu') ) return; |
| 147 | $menu_options = $this->settings->adminCustomEnabled('nav_menu_options'); |
| 148 | if ( !$menu_options ) return; |
| 149 | if ( !isset($menu_options[$this->current_user_role]) ) return; |
| 150 | |
| 151 | // First remove all submenus |
| 152 | foreach ( $submenu as $menu_slug => $submenu_items ){ |
| 153 | foreach ( $submenu_items as $sub_pages ){ |
| 154 | if ( !isset($sub_pages[2]) ) continue; |
| 155 | remove_submenu_page($menu_slug, $sub_pages[2]); |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | // Add the Submenu pages back in, ordered and labeled how we want |
| 160 | foreach ( $menu_options[$this->current_user_role] as $menu_option ){ |
| 161 | |
| 162 | if ( !isset($menu_option['original_link']) ) continue; |
| 163 | if ( !isset($menu_option['submenu']) || !$menu_option['submenu'] ){ |
| 164 | if ( !isset($np_submenu_original[$menu_option['original_link']]) ) continue; |
| 165 | $submenu[$menu_option['link']] = $np_submenu_original[$menu_option['original_link']]; |
| 166 | continue; |
| 167 | } |
| 168 | $new_submenu = []; |
| 169 | foreach ( $menu_option['submenu'] as $key => $menu ){ |
| 170 | $index = ($key + 1) * 10; |
| 171 | |
| 172 | // Items saved that no longer exist |
| 173 | if ( !isset($np_submenu_original[$menu_option['link']]) ) continue; |
| 174 | if ( !$this->submenuExists($np_submenu_original[$menu_option['link']], $menu['link'], $menu_option) ) continue; |
| 175 | |
| 176 | $np_submenu_original[$index][0] = $menu['label']; |
| 177 | $np_submenu_original[$index][1] = $menu['role']; |
| 178 | $np_submenu_original[$index][2] = $menu['link']; |
| 179 | $np_submenu_original[$index][3] = $menu['label']; |
| 180 | $np_submenu_original[$index][4] = (isset($menu['hidden']) && $menu['hidden'] == 'true') ? true : false; |
| 181 | |
| 182 | if ( isset($menu['hidden']) && $menu['hidden'] == 'true' ) continue; |
| 183 | $new_submenu[$index][0] = $menu['label']; |
| 184 | $new_submenu[$index][1] = $menu['role']; |
| 185 | $new_submenu[$index][2] = $menu['link']; |
| 186 | $new_submenu[$index][3] = $menu['label']; |
| 187 | } |
| 188 | $submenu[$menu_option['link']] = $new_submenu; |
| 189 | } |
| 190 | |
| 191 | // Submenu pages added by plugins after saving customizations |
| 192 | foreach ( $original_submenu as $id => $submenu_original ){ |
| 193 | foreach ( $submenu_original as $submenu_item ){ |
| 194 | if ( !isset($original_submenu[$id]) || empty($original_submenu[$id]) ) continue; |
| 195 | if ( !isset($submenu[$id]) ) $submenu[$id] = []; |
| 196 | if ( !isset($menu_options[$this->current_user_role][$id]) ) $menu_options[$this->current_user_role][$id] = [ |
| 197 | 'submenu' => [] |
| 198 | ]; |
| 199 | if ( isset($menu_options[$this->current_user_role][$id]) && !$this->submenuExists($submenu[$id], $submenu_item[2]) |
| 200 | && !$this->submenuHidden($menu_options[$this->current_user_role][$id]['submenu'], $submenu_item[2]) ) { |
| 201 | $submenu[$id][] = $submenu_item; |
| 202 | } |
| 203 | } |
| 204 | } |
| 205 | } |
| 206 | |
| 207 | /** |
| 208 | * Does the submenu exist in the source menu? |
| 209 | */ |
| 210 | private function submenuExists($source_menu, $link) |
| 211 | { |
| 212 | $exists = false; |
| 213 | foreach ( $source_menu as $menu_item ){ |
| 214 | if ( $menu_item[2] == $link ) $exists = true; |
| 215 | } |
| 216 | return $exists; |
| 217 | } |
| 218 | |
| 219 | private function submenuHidden($menu_options, $submenu_link) |
| 220 | { |
| 221 | $hidden = false; |
| 222 | foreach ( $menu_options as $option ){ |
| 223 | if ( $option['link'] == $submenu_link && isset($option['hidden']) ) $hidden = true; |
| 224 | } |
| 225 | return $hidden; |
| 226 | } |
| 227 | |
| 228 | /** |
| 229 | * Reorder Menu Items in the original, saved copy |
| 230 | */ |
| 231 | public function buildNestedMenu() |
| 232 | { |
| 233 | global $np_menu_original; |
| 234 | global $np_submenu_original; |
| 235 | global $menu; |
| 236 | |
| 237 | $menu_options = $this->settings->adminCustomEnabled('nav_menu_options'); |
| 238 | if ( !$menu_options ) return $this->setDefaultMenu(); |
| 239 | |
| 240 | $np_menu_ordered = array(); |
| 241 | $np_menu_ordered['default'] = $menu; |
| 242 | |
| 243 | // Set each role's menu order |
| 244 | $user_roles = $this->user_repo->allRoles(array()); |
| 245 | foreach( $user_roles as $role ){ |
| 246 | |
| 247 | if ( !array_key_exists($role['name'], $menu_options) ) continue; |
| 248 | $missing_submenus = []; |
| 249 | foreach ( $menu_options[$role['name']] as $key => $item ){ |
| 250 | foreach ( $np_menu_original as $menu_key => $menu_item ){ |
| 251 | if ( isset($menu_item[2]) && $menu_item[2] == $key ) { |
| 252 | if ( isset($np_submenu_original[$menu_item[2]]) ) { |
| 253 | $menu_item['submenu'] = $np_submenu_original[$menu_item[2]]; |
| 254 | } |
| 255 | $np_menu_ordered[$role['name']][] = $menu_item; |
| 256 | } // submenu |
| 257 | } // np menu original |
| 258 | } // role |
| 259 | } |
| 260 | |
| 261 | // Add the separators |
| 262 | foreach($user_roles as $role){ |
| 263 | $role_name = $role['name']; |
| 264 | if ( !array_key_exists($role_name, $menu_options) ) continue; |
| 265 | $c = 0; |
| 266 | foreach ( $menu_options[$role_name] as $key => $options ){ |
| 267 | if ( strpos($key, 'custom_sep') !== false ){ |
| 268 | $menu_item = array('', 'read', $key, '', 'wp-menu-separator'); |
| 269 | array_splice($np_menu_ordered[$role_name], $c, 0, array($menu_item)); |
| 270 | } |
| 271 | $c++; |
| 272 | } |
| 273 | } |
| 274 | |
| 275 | // Add missing items (added by other plugins after saving a custom menu) |
| 276 | $missing_top_items = []; |
| 277 | $missing_sub_items = []; |
| 278 | foreach ( $np_menu_ordered as $role => $ordered_menu ){ |
| 279 | if ( $role == 'default' ) continue; |
| 280 | $all_items = []; |
| 281 | foreach ( $np_menu_ordered['default'] as $menu_key => $default_menu ){ |
| 282 | if ( $default_menu[2] == 'edit-tags.php?taxonomy=link_category' ) continue; |
| 283 | $all_items[$default_menu[2]] = $menu_key; |
| 284 | } |
| 285 | foreach ( $ordered_menu as $ordered_item ){ |
| 286 | if ( array_key_exists($ordered_item[2], $all_items) ) unset($all_items[$ordered_item[2]]); |
| 287 | } |
| 288 | $missing_top_items[$role] = $all_items; |
| 289 | } |
| 290 | |
| 291 | foreach ( $missing_top_items as $role => $items ){ |
| 292 | foreach ( $items as $item_id => $key ){ |
| 293 | $new_item = $np_menu_ordered['default'][$key]; |
| 294 | if ( array_key_exists($item_id, $np_submenu_original) ) : |
| 295 | $new_item['submenu'] = $np_submenu_original[$item_id]; |
| 296 | endif; |
| 297 | $np_menu_ordered[$role][] = $new_item; |
| 298 | } |
| 299 | } |
| 300 | |
| 301 | $np_menu_original = $np_menu_ordered; |
| 302 | } |
| 303 | |
| 304 | |
| 305 | /** |
| 306 | * Set the default menu/order |
| 307 | * This is used to display the nested admin settings view before a customized version has been saved |
| 308 | */ |
| 309 | private function setDefaultMenu() |
| 310 | { |
| 311 | global $np_menu_original; |
| 312 | global $np_submenu_original; |
| 313 | $np_menu_ordered = array(); |
| 314 | ksort($np_menu_original); |
| 315 | |
| 316 | // Default menu (for new user roles) |
| 317 | foreach ( $np_menu_original as $menu_item ){ |
| 318 | $np_menu_ordered['default'][] = $menu_item; |
| 319 | } |
| 320 | |
| 321 | // Set each role's menu order |
| 322 | $user_roles = $this->user_repo->allRoles([]); |
| 323 | |
| 324 | foreach( $user_roles as $role ){ |
| 325 | |
| 326 | $role_capabilities = $this->user_repo->getSingleRoleCapabilities($role['name']); |
| 327 | |
| 328 | foreach ( $np_menu_original as $menu_item ){ |
| 329 | |
| 330 | if ( $menu_item[1] == 'list_users' && !array_key_exists('list_users', $role_capabilities) ){ |
| 331 | $profile_menu_item = [ |
| 332 | __('Profile', 'wp-nested-pages'), |
| 333 | 'read', |
| 334 | 'profile.php', |
| 335 | '', |
| 336 | 'menu-icon-users', |
| 337 | 'menu-users', |
| 338 | 'dashicons-admin-users', |
| 339 | 'custom-item', |
| 340 | 'no-delete' |
| 341 | ]; |
| 342 | $np_menu_ordered[$role['name']][] = $profile_menu_item; |
| 343 | continue; |
| 344 | } |
| 345 | |
| 346 | if ( $menu_item[1] === '' || !array_key_exists($menu_item[1], $role_capabilities) || !$role_capabilities[$menu_item[1]] ) continue; |
| 347 | if ( isset($menu_item[5]) && $menu_item[5] == 'menu-links' ) continue; |
| 348 | if ( $role['name'] == 'subscriber' && $menu_item[2] == 'separator2') continue; |
| 349 | if ( isset($np_submenu_original[$menu_item[2]]) ) $menu_item['submenu'] = $np_submenu_original[$menu_item[2]]; |
| 350 | $np_menu_ordered[$role['name']][] = $menu_item; |
| 351 | } |
| 352 | |
| 353 | } // roles |
| 354 | $np_menu_original = $np_menu_ordered; |
| 355 | } |
| 356 | } |