Listing.php
2 years ago
ListingActions.php
8 years ago
ListingQuery.php
3 years ago
ListingRepository.php
2 days ago
Listing.php
489 lines
| 1 | <?php |
| 2 | namespace NestedPages\Entities\Listing; |
| 3 | |
| 4 | use NestedPages\Helpers; |
| 5 | use NestedPages\Entities\Confirmation\ConfirmationFactory; |
| 6 | use NestedPages\Entities\Post\PostDataFactory; |
| 7 | use NestedPages\Entities\Post\PostRepository; |
| 8 | use NestedPages\Entities\User\UserRepository; |
| 9 | use NestedPages\Entities\PostType\PostTypeRepository; |
| 10 | use NestedPages\Entities\Listing\ListingRepository; |
| 11 | use NestedPages\Entities\Listing\ListingQuery; |
| 12 | use NestedPages\Config\SettingsRepository; |
| 13 | use NestedPages\Entities\PluginIntegration\IntegrationFactory; |
| 14 | use NestedPages\Entities\PostType\PostTypeCustomFields; |
| 15 | |
| 16 | /** |
| 17 | * Primary Post Listing |
| 18 | */ |
| 19 | class Listing |
| 20 | { |
| 21 | /** |
| 22 | * Post Type |
| 23 | * @var object WP Post Type Object |
| 24 | */ |
| 25 | private $post_type; |
| 26 | |
| 27 | /** |
| 28 | * Query Results |
| 29 | * @var array of post objects (WP Query) |
| 30 | */ |
| 31 | private $all_posts; |
| 32 | |
| 33 | /** |
| 34 | * Hierarchical Taxonomies |
| 35 | * @var array |
| 36 | */ |
| 37 | private $h_taxonomies; |
| 38 | |
| 39 | /** |
| 40 | * Flat Taxonomies |
| 41 | * @var array |
| 42 | */ |
| 43 | private $f_taxonomies; |
| 44 | |
| 45 | /** |
| 46 | * Post Data Factory |
| 47 | */ |
| 48 | private $post_data_factory; |
| 49 | |
| 50 | /** |
| 51 | * Post Data |
| 52 | * @var object |
| 53 | */ |
| 54 | private $post; |
| 55 | |
| 56 | /** |
| 57 | * Post Repository |
| 58 | */ |
| 59 | private $post_repo; |
| 60 | |
| 61 | /** |
| 62 | * Post Type Repository |
| 63 | */ |
| 64 | private $post_type_repo; |
| 65 | |
| 66 | /** |
| 67 | * Listing Repository |
| 68 | */ |
| 69 | private $listing_repo; |
| 70 | |
| 71 | /** |
| 72 | * Listing Query |
| 73 | */ |
| 74 | private $listing_query; |
| 75 | |
| 76 | /** |
| 77 | * Confirmation Factory |
| 78 | */ |
| 79 | private $confirmation; |
| 80 | |
| 81 | /** |
| 82 | * User Repository |
| 83 | */ |
| 84 | private $user; |
| 85 | |
| 86 | /** |
| 87 | * Settings Repository |
| 88 | */ |
| 89 | private $settings; |
| 90 | |
| 91 | /** |
| 92 | * Custom Field Repository |
| 93 | */ |
| 94 | private $custom_fields_repo; |
| 95 | |
| 96 | /** |
| 97 | * Post Type Settings |
| 98 | * @var object from post type repo |
| 99 | */ |
| 100 | private $post_type_settings; |
| 101 | |
| 102 | /** |
| 103 | * Assigned Pages for post types |
| 104 | * @var array from post type repo |
| 105 | */ |
| 106 | private $assigned_pt_pages; |
| 107 | |
| 108 | /** |
| 109 | * Plugin Integrations |
| 110 | */ |
| 111 | private $integrations; |
| 112 | |
| 113 | /** |
| 114 | * Disabled Standard Fields |
| 115 | */ |
| 116 | private $disabled_standard_fields; |
| 117 | |
| 118 | /** |
| 119 | * Sticky Posts |
| 120 | * @var array |
| 121 | */ |
| 122 | private $sticky_posts; |
| 123 | |
| 124 | /** |
| 125 | * User status preference |
| 126 | * @var array |
| 127 | */ |
| 128 | private $status_preference; |
| 129 | |
| 130 | /** |
| 131 | * User can perform bulk actions |
| 132 | */ |
| 133 | private $can_user_perform_bulk_actions; |
| 134 | |
| 135 | /** |
| 136 | * Enabled Custom Fields |
| 137 | */ |
| 138 | private $enabled_custom_fields; |
| 139 | |
| 140 | public function __construct($post_type) |
| 141 | { |
| 142 | $this->setPostType($post_type); |
| 143 | $this->setStickyPosts(); |
| 144 | $this->integrations = new IntegrationFactory; |
| 145 | $this->post_repo = new PostRepository; |
| 146 | $this->user = new UserRepository; |
| 147 | $this->confirmation = new ConfirmationFactory; |
| 148 | $this->post_type_repo = new PostTypeRepository; |
| 149 | $this->listing_repo = new ListingRepository; |
| 150 | $this->listing_query = new ListingQuery; |
| 151 | $this->post_data_factory = new PostDataFactory; |
| 152 | $this->settings = new SettingsRepository; |
| 153 | $this->custom_fields_repo = new PostTypeCustomFields; |
| 154 | $this->setTaxonomies(); |
| 155 | $this->setPostTypeSettings(); |
| 156 | $this->setStandardFields(); |
| 157 | $this->setStatusPreference(); |
| 158 | $this->setCanUserPerformBulkActions(); |
| 159 | } |
| 160 | |
| 161 | /** |
| 162 | * Called by Menu Class |
| 163 | * Instantiates Listing Class |
| 164 | * @since 1.2.0 |
| 165 | */ |
| 166 | public static function admin_menu($post_type) |
| 167 | { |
| 168 | $classinstance = new \NestedPages\Entities\Listing\Listing($post_type); |
| 169 | return [&$classinstance, "listPosts"]; |
| 170 | } |
| 171 | |
| 172 | /** |
| 173 | * Get the Current Page URL |
| 174 | */ |
| 175 | private function pageURL() |
| 176 | { |
| 177 | $base = ( $this->post_type->name == 'post' ) ? admin_url('edit.php') : admin_url('admin.php'); |
| 178 | return $base . '?page=' . sanitize_text_field($_GET['page']); |
| 179 | } |
| 180 | |
| 181 | /** |
| 182 | * Set the Post Type |
| 183 | * @since 1.1.16 |
| 184 | */ |
| 185 | private function setPostType($post_type) |
| 186 | { |
| 187 | $this->post_type = get_post_type_object($post_type); |
| 188 | } |
| 189 | |
| 190 | /** |
| 191 | * Set the Sticky Posts |
| 192 | * @since 2.0.1 |
| 193 | */ |
| 194 | private function setStickyPosts() |
| 195 | { |
| 196 | $this->sticky_posts = get_option('sticky_posts'); |
| 197 | if ( !$this->sticky_posts ) $this->sticky_posts = []; |
| 198 | } |
| 199 | |
| 200 | /** |
| 201 | * Set the Post Type Settings |
| 202 | * @since 1.6.9 |
| 203 | */ |
| 204 | private function setPostTypeSettings() |
| 205 | { |
| 206 | $this->post_type_settings = $this->post_type_repo->getSinglePostType($this->post_type->name); |
| 207 | $this->assigned_pt_pages = $this->post_type_repo->getAssignedPages(); |
| 208 | } |
| 209 | |
| 210 | /** |
| 211 | * Set the Quick Edit Field Options |
| 212 | */ |
| 213 | private function setStandardFields() |
| 214 | { |
| 215 | // The standard fields checkbox is explicitly not set |
| 216 | if ( isset($this->post_type_settings->standard_fields_enabled) && !$this->post_type_settings->standard_fields_enabled ){ |
| 217 | $this->disabled_standard_fields = []; |
| 218 | return; |
| 219 | } |
| 220 | |
| 221 | if ( isset($this->post_type_settings->standard_fields) && is_array($this->post_type_settings->standard_fields) ){ |
| 222 | $this->disabled_standard_fields = $this->post_type_settings->standard_fields; |
| 223 | foreach ( $this->post_type_settings->standard_fields as $key => $fields ){ |
| 224 | if ( $key == 'standard' ) $this->disabled_standard_fields = $fields; |
| 225 | } |
| 226 | return; |
| 227 | } |
| 228 | $this->disabled_standard_fields = []; |
| 229 | return; |
| 230 | } |
| 231 | |
| 232 | /** |
| 233 | * Get the Post States |
| 234 | */ |
| 235 | private function postStates($assigned_pt) |
| 236 | { |
| 237 | $out = ''; |
| 238 | $post_states = []; |
| 239 | if ( !$assigned_pt ) { |
| 240 | if ( $this->post->id == get_option('page_on_front') ) $post_states['page_on_front'] = '– ' . __('Front Page', 'wp-nested-pages'); |
| 241 | if ( $this->post->id == get_option('page_for_posts') ) $post_states['page_for_posts'] = '– ' . __('Posts Page', 'wp-nested-pages'); |
| 242 | } |
| 243 | $post_states = apply_filters('display_post_states', $post_states, $this->post); |
| 244 | if ( empty($post_states) ) return $out; |
| 245 | $state_count = count($post_states); |
| 246 | $i = 0; |
| 247 | foreach ( $post_states as $state ) { |
| 248 | ++$i; |
| 249 | ( $i == $state_count ) ? $sep = '' : $sep = ','; |
| 250 | $out .= " <em class='np-page-type'><strong>$state</strong>$sep</em>"; |
| 251 | } |
| 252 | return $out; |
| 253 | } |
| 254 | |
| 255 | /** |
| 256 | * Row Actions |
| 257 | * Adds assigned pt actions as well as any custom actions registered through page_row_actions filter |
| 258 | */ |
| 259 | private function rowActions($assigned_pt) |
| 260 | { |
| 261 | $actions = []; |
| 262 | if ( $assigned_pt ) { |
| 263 | if ( current_user_can('publish_posts') ) $actions['add_new'] = '<a href="' . $this->post_type_repo->addNewPostLink($assigned_pt->name) . '">' . $assigned_pt->labels->add_new . '</a>'; |
| 264 | $actions['view_all'] = '<a href="' . $this->post_type_repo->allPostsLink($assigned_pt->name) . '">' . $assigned_pt->labels->all_items . ' (' . $this->listing_repo->postCount($assigned_pt->name) . ')</a>'; |
| 265 | } |
| 266 | $actions = apply_filters('post_row_actions', $actions, $this->post); |
| 267 | if ( $this->post_type->name == 'page' ) $actions = apply_filters('page_row_actions', $actions, $this->post); |
| 268 | if ( empty($actions) ) return null; |
| 269 | $out = '<ul class="np-assigned-pt-actions">'; |
| 270 | foreach ( $actions as $key => $action ){ |
| 271 | $out .= '<li class="' . $key; |
| 272 | if ( $key == 'add_new' || $key == 'view_all' ) $out .= ' visible'; |
| 273 | $out .= '">' . $action . '</li>'; |
| 274 | } |
| 275 | $out .= '</ul>'; |
| 276 | return $out; |
| 277 | } |
| 278 | |
| 279 | /** |
| 280 | * The Main View |
| 281 | * Replaces Default Post Listing |
| 282 | */ |
| 283 | public function listPosts() |
| 284 | { |
| 285 | include( Helpers::view('listing') ); |
| 286 | } |
| 287 | |
| 288 | /** |
| 289 | * Set the Taxonomies for Post Type |
| 290 | */ |
| 291 | private function setTaxonomies() |
| 292 | { |
| 293 | $this->h_taxonomies = $this->post_type_repo->getTaxonomies($this->post_type->name, true); |
| 294 | $this->f_taxonomies = $this->post_type_repo->getTaxonomies($this->post_type->name, false); |
| 295 | } |
| 296 | |
| 297 | /** |
| 298 | * Set if the user can perform bulk actions |
| 299 | */ |
| 300 | private function setCanUserPerformBulkActions() |
| 301 | { |
| 302 | $this->can_user_perform_bulk_actions = $this->user->canPerformBulkActions($this->post_type_settings); |
| 303 | } |
| 304 | |
| 305 | /** |
| 306 | * Set the user status preference |
| 307 | */ |
| 308 | private function setStatusPreference() |
| 309 | { |
| 310 | $this->status_preference = $this->user->getStatusPreference($this->post_type->name); |
| 311 | } |
| 312 | |
| 313 | /** |
| 314 | * Opening list tag <ol> |
| 315 | * @param array $pages - array of page objects from current query |
| 316 | * @param int $count - current count in loop |
| 317 | */ |
| 318 | private function listOpening($pages, $count, $sortable = true) |
| 319 | { |
| 320 | if ( $this->listing_repo->isSearch() ) $sortable = false; |
| 321 | if ( $this->post_type_settings->disable_sorting ) $sortable = false; |
| 322 | |
| 323 | // Get array of child pages |
| 324 | $children = []; |
| 325 | $all_children = $pages; |
| 326 | foreach($all_children as $child){ |
| 327 | array_push($children, $child->ID); |
| 328 | } |
| 329 | // Compare child pages with user's toggled pages |
| 330 | $compared = array_intersect($this->listing_repo->visiblePages($this->post_type->name), $children); |
| 331 | |
| 332 | $list_classes = 'sortable visible nplist'; |
| 333 | if ( !$this->user->canSortPosts($this->post_type->name) || !$sortable || $this->listing_repo->isSearch() ) $list_classes .= ' no-sort'; |
| 334 | if ( $this->listing_repo->isOrdered($this->post_type->name) ) $list_classes .= ' no-sort'; |
| 335 | if ( $this->integrations->plugins->wpml->installed && $this->integrations->plugins->wpml->getCurrentLanguage() == 'all' ) $list_classes .= ' no-sort'; |
| 336 | if ( $this->integrations->plugins->yoast->installed ) $list_classes .= ' has-yoast'; |
| 337 | if ( $this->listing_repo->isSearch() ) $list_classes .= ' np-search-results'; |
| 338 | if ( $this->settings->nonIndentEnabled() ) $list_classes .= ' non-indent'; |
| 339 | |
| 340 | // Primary List |
| 341 | if ( $count == 0 ) { |
| 342 | if ( $this->can_user_perform_bulk_actions ) { |
| 343 | include( Helpers::view('partials/list-header') ); // List Header |
| 344 | include( Helpers::view('partials/bulk-edit') ); // Bulk Edit |
| 345 | } |
| 346 | echo '<ol class="' . $list_classes . '" id="np-' . $this->post_type->name . '">'; |
| 347 | return; |
| 348 | } |
| 349 | |
| 350 | echo '<ol class="nplist'; |
| 351 | if ( count($compared) > 0 ) echo ' visible" style="display:block;'; |
| 352 | echo '" id="np-' . $this->post_type->name . '">'; |
| 353 | } |
| 354 | |
| 355 | /** |
| 356 | * Set Post Data |
| 357 | * @param object post object |
| 358 | */ |
| 359 | private function setPost($post) |
| 360 | { |
| 361 | $this->post = $this->post_data_factory->build($post, $this->h_taxonomies, $this->f_taxonomies); |
| 362 | } |
| 363 | |
| 364 | /** |
| 365 | * Get count of published child posts |
| 366 | * @param object $post |
| 367 | */ |
| 368 | private function publishedChildrenCount($post) |
| 369 | { |
| 370 | $publish_count = 0; |
| 371 | foreach ( $this->all_posts as $p ){ |
| 372 | if ( $p->post_parent == $post->id && $p->post_status !== 'trash' ) $publish_count++; |
| 373 | } |
| 374 | return $publish_count; |
| 375 | } |
| 376 | |
| 377 | /** |
| 378 | * Loop through all the pages and create the nested / sortable list |
| 379 | * Called in listing.php view |
| 380 | */ |
| 381 | private function getPosts() |
| 382 | { |
| 383 | $this->all_posts = $this->listing_query->getPosts($this->post_type, $this->h_taxonomies, $this->f_taxonomies); |
| 384 | $this->listPostLevel(); |
| 385 | return; |
| 386 | } |
| 387 | |
| 388 | /** |
| 389 | * List a single tree node of posts |
| 390 | */ |
| 391 | private function listPostLevel($parent = 0, $count = 0, $level = 1) |
| 392 | { |
| 393 | $wpml = $this->integrations->plugins->wpml->installed; |
| 394 | $wpml_current_language = null; |
| 395 | if ( $wpml ) $wpml_current_language = $this->integrations->plugins->wpml->getCurrentLanguage(); |
| 396 | |
| 397 | if ( $this->listing_repo->isFiltered() ){ |
| 398 | $parent_status = null; |
| 399 | $pages = $this->all_posts; |
| 400 | $level++; |
| 401 | echo '<ol class="sortable nplist visible filtered">'; |
| 402 | } elseif ( !$this->listing_repo->isSearch() ) { |
| 403 | $pages = get_page_children($parent, $this->all_posts); |
| 404 | if ( !$pages ) return; |
| 405 | $parent_status = get_post_status($parent); |
| 406 | $level++; |
| 407 | if ( $parent_status !== 'trash' ) $this->listOpening($pages, $count); |
| 408 | } else { |
| 409 | $parent_status = null; |
| 410 | $pages = $this->all_posts; |
| 411 | echo '<ol class="sortable no-sort nplist visible">'; |
| 412 | } |
| 413 | if ( !$pages ) return; |
| 414 | |
| 415 | foreach($pages as $page) : |
| 416 | |
| 417 | if ( $page->post_parent !== $parent && !$this->listing_repo->isSearch() && !$this->listing_repo->isFiltered() ) continue; |
| 418 | $count++; |
| 419 | |
| 420 | global $post; |
| 421 | $post = $page; |
| 422 | $this->setPost($post); |
| 423 | |
| 424 | if ( $this->post->status !== 'trash' ) : |
| 425 | |
| 426 | $row_parent_classes = 'page-row'; |
| 427 | // Post Type |
| 428 | $row_parent_classes .= ' post-type-' . esc_attr($this->post->post_type); |
| 429 | |
| 430 | // Managed Post Type page? |
| 431 | if ( $this->listing_repo->isAssignedPostType($this->post->id, $this->assigned_pt_pages) ) $row_parent_classes .= ' is-page-assignment'; |
| 432 | |
| 433 | // Published? |
| 434 | if ( $this->post->status == 'publish' ) $row_parent_classes .= ' published'; |
| 435 | if ( $this->post->status == 'draft' ) $row_parent_classes .= ' draft'; |
| 436 | |
| 437 | // Hidden in Nested Pages? |
| 438 | if ( $this->post->np_status == 'hide' ) $row_parent_classes .= ' np-hide'; |
| 439 | |
| 440 | // User Status Preference |
| 441 | if ( $this->status_preference == 'published' && $this->post->status == 'draft' ) $row_parent_classes .= ' np-hide'; |
| 442 | if ( $this->status_preference == 'draft' && $this->post->status !== 'draft' ) $row_parent_classes .= ' np-hide'; |
| 443 | |
| 444 | // Taxonomies |
| 445 | $row_parent_classes .= ' ' . $this->post_repo->getTaxonomyCSS($this->post, $this->h_taxonomies, $this->f_taxonomies); |
| 446 | |
| 447 | echo '<li id="menuItem_' . esc_attr($this->post->id) . '" class="' . apply_filters('nestedpages_row_parent_css_classes', $row_parent_classes, $this->post, $this->post_type) . '">'; |
| 448 | |
| 449 | $count++; |
| 450 | |
| 451 | $row_view = ( $this->post->type !== 'np-redirect' ) ? 'partials/row' : 'partials/row-link'; |
| 452 | |
| 453 | // CSS Classes for the <li> row element |
| 454 | $template = ( $this->post->template ) |
| 455 | ? ' tpl-' . sanitize_html_class( str_replace('.php', '', $this->post->template ) ) |
| 456 | : ''; |
| 457 | |
| 458 | $row_classes = ''; |
| 459 | if ( !$this->post_type->hierarchical ) $row_classes .= ' non-hierarchical'; |
| 460 | if ( !$this->user->canSortPosts($this->post_type->name) ) $row_classes .= ' no-sort'; |
| 461 | if ( $wpml_current_language == 'all' ) $row_classes .= ' no-sort'; |
| 462 | if ( $this->listing_repo->isSearch() || $this->listing_repo->isOrdered($this->post_type->name) ) $row_classes .= ' search'; |
| 463 | if ( $this->post->template ) $row_classes .= $template; |
| 464 | |
| 465 | // Filter sortable per post |
| 466 | $filtered_sortable = apply_filters('nestedpages_post_sortable', true, $this->post, $this->post_type); |
| 467 | if ( !$filtered_sortable && $this->user->canSortPosts($this->post_type->name) && $this->post_type->hierarchical && !$wpml_current_language ) $row_classes .= ' no-sort-filtered'; |
| 468 | |
| 469 | // Page Assignment for Post Type |
| 470 | $assigned_pt = ( $this->listing_repo->isAssignedPostType($this->post->id, $this->assigned_pt_pages) ) |
| 471 | ? $this->listing_repo->assignedPostType($this->post->id, $this->assigned_pt_pages) |
| 472 | : false; |
| 473 | |
| 474 | include( Helpers::view($row_view) ); |
| 475 | |
| 476 | endif; // trash status |
| 477 | |
| 478 | if ( !$this->listing_repo->isSearch() && !$this->listing_repo->isFiltered() ) $this->listPostLevel($page->ID, $count, $level); |
| 479 | |
| 480 | if ( $this->post->status !== 'trash' ) echo '</li>'; |
| 481 | |
| 482 | if ( $this->publishedChildrenCount($this->post) > 0 && !$this->listing_repo->isSearch() && !$this->listing_repo->isFiltered() ) echo '</ol>'; |
| 483 | |
| 484 | endforeach; // Loop |
| 485 | |
| 486 | if ( $parent_status !== 'trash' ) echo '</ol><!-- list close -->'; |
| 487 | } |
| 488 | } |
| 489 |