Skip to content

Combobox with 3 or more page groups #335

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion app/Config/SettingsRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,25 @@ public function defaultHideInNav()
return false;
}


/**
* Number of available page groups before selection is required
* @return int
*/
public function maxNonSelectedPageGroups()
{
$value = 3;
$option = get_option('nestedpages_ui', false);
if ( is_array($option) )
if ( array_key_exists('max_nonselected_pagegroups', $option) ) {
if ( is_numeric($option['max_nonselected_pagegroups']) ) {
if ( $option['max_nonselected_pagegroups'] >= -1 ) $value = $option['max_nonselected_pagegroups'];
}
}
if ( $value < -1 ) $value = -1;
return $value;
}

/**
* Are menus completely disabled?
* @return boolean
Expand Down Expand Up @@ -262,4 +281,5 @@ public function sortViewEnabled()
endif;
return $roles;
}
}

}
4 changes: 3 additions & 1 deletion app/Entities/AdminMenu/BlockEditorLink.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ private function fullPageEditorLink($type)
if ( !$user_can_view ) return;
$link = ( $type->name == 'page' ) ? 'admin.php?page=nestedpages' : 'admin.php?page=' . $this->post_type_repo->getMenuSlug($type);
$url = admin_url($link);
$pagegroup = $this->post_repo->getPageGroup(get_post()->ID);
if ( $pagegroup !== false ) $url .= '&np_page_group=' . $pagegroup;
echo '<script>jQuery(window).on("load", function(){ var headerLink = jQuery(".edit-post-header .edit-post-fullscreen-mode-close"); jQuery(headerLink).attr("href", "' . $url . '"); });</script>';
}
}
}
49 changes: 45 additions & 4 deletions app/Entities/Listing/Listing.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,18 @@ class Listing
*/
private $status_preference;

/**
* Current page group
* @var ?int
*/
private $page_group_id;

/**
* Available page groups
* @var array
*/
private $page_groups;

/**
* Enabled Custom Fields
*/
Expand All @@ -150,6 +162,8 @@ public function __construct($post_type)
$this->setPostTypeSettings();
$this->setStandardFields();
$this->setStatusPreference();
$this->loadPageGroups();
$this->setPageGroup();
}

/**
Expand Down Expand Up @@ -359,15 +373,42 @@ private function publishedChildrenCount($post)
return $publish_count;
}

/**
* Load all the available page groups
*/
private function loadPageGroups() {
$this->page_groups = $this->listing_query->getPosts($this->post_type, $this->h_taxonomies, $this->f_taxonomies, null, true);
}

/**
* Set the page group from the HTTP request
*/
private function setPageGroup() {
$this->page_group_id = null;
if ( array_key_exists( 'np_page_group', $_REQUEST ) ) {
if ( is_numeric( $_REQUEST['np_page_group'] ) )
$this->page_group_id = $_REQUEST['np_page_group'];
}
$this->all_posts = [];
}

/**
* Loop through all the pages and create the nested / sortable list
* Called in listing.php view
*/
private function getPosts()
private function printPostsList()
{
$this->all_posts = $this->listing_query->getPosts($this->post_type, $this->h_taxonomies, $this->f_taxonomies);
$this->listPostLevel();
return;
$max_nonselected_pagegroups = $this->settings->maxNonSelectedPageGroups();
if ( $max_nonselected_pagegroups == -1 )
$list_due_to_pagegroups = true;
else if ( $max_nonselected_pagegroups == 0 )
$list_due_to_pagegroups = false;
else
$list_due_to_pagegroups = count($this->page_groups) <= $max_nonselected_pagegroups;
if ( $list_due_to_pagegroups || $this->page_group_id !== null || $this->listing_repo->isSearch() ) {
$this->all_posts = $this->listing_query->getPosts($this->post_type, $this->h_taxonomies, $this->f_taxonomies, $this->page_group_id, false);
$this->listPostLevel();
}
}

/**
Expand Down
44 changes: 31 additions & 13 deletions app/Entities/Listing/ListingQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use NestedPages\Entities\PostType\PostTypeRepository;
use NestedPages\Config\SettingsRepository;
use NestedPages\Entities\PluginIntegration\IntegrationFactory;
use NestedPages\Entities\Post\PostRepository;

/**
* Performs the query for the page listing and formats into a multidemensional array
Expand Down Expand Up @@ -73,7 +74,7 @@ private function setOrder()
/**
* Get the Posts
*/
public function getPosts($post_type, $h_taxonomies = [], $f_taxonomies = [])
public function getPosts($post_type, $h_taxonomies = [], $f_taxonomies = [], $page_group_id = null, $only_page_groups = false)
{
$this->post_type = $post_type;

Expand All @@ -91,7 +92,8 @@ public function getPosts($post_type, $h_taxonomies = [], $f_taxonomies = [])
$post_type = [$post_type->name];
}

$statuses = ['publish', 'pending', 'draft', 'private', 'future', 'trash'];
$statuses = ['publish', 'pending', 'draft', 'private', 'future'];
if ( !$only_page_groups ) $statuses[] = 'trash';
$post_type_settings = $this->post_type_repo->getSinglePostType($post_type[0]);
if ( isset($post_type_settings->custom_statuses) ) $statuses = array_merge($statuses, $post_type_settings->custom_statuses);

Expand All @@ -101,23 +103,33 @@ public function getPosts($post_type, $h_taxonomies = [], $f_taxonomies = [])
'author' => $this->sort_options->author,
'orderby' => $this->sort_options->orderby,
'post_status' => apply_filters('nestedpages_listing_statuses', $statuses, $this->post_type),
'order' => $this->sort_options->order
'order' => $this->sort_options->order,
'suppress_filters' => false,
'nested_pages_page_group' => $page_group_id,
];

if ( $this->listing_repo->isSearch() ) $query_args = $this->searchParams($query_args);
if ( $this->listing_repo->isFiltered() ) $query_args = $this->filterParams($query_args);
if ( $this->sort_options->tax_query ) $query_args['tax_query'] = $this->sort_options->tax_query;
if ( $page_group_id !== null )
$query_args['post__in'] = PostRepository::getPostsOfPageGroup(
$page_group_id,
[ 'post_type' =>
[
'include' => ['page']
],
]
);
if ( $only_page_groups ) $query_args['post_parent'] = 0;

$query_args = apply_filters('nestedpages_page_listing', $query_args, $this->post_type);

add_filter( 'posts_clauses', [$this, 'queryFilter']);
add_filter( 'posts_clauses', [$this, 'queryFilter'] );
$all_posts = new \WP_Query($query_args);
remove_filter( 'posts_clauses', [$this, 'queryFilter']);

if ( $all_posts->have_posts() ) :
return $all_posts->posts;
endif; wp_reset_postdata();
return null;
remove_filter( 'posts_clauses', [$this, 'queryFilter'] );

if ( ! $all_posts->have_posts() ) wp_reset_postdata();
return $all_posts->posts;
}

/**
Expand Down Expand Up @@ -166,7 +178,12 @@ private function setTaxonomyFilters()
public function queryFilter($pieces)
{
global $wpdb;


// Restrict fields to avoid loading the post contents
$pieces['fields'] = 'ID, post_author, post_date, post_date_gmt, post_title, post_status,'
. ' post_name, post_modified, post_modified_gmt, post_parent, guid, menu_order,'
. ' post_type, post_mime_type, comment_count';

// Add Hierarchical Categories
$c = 0;
foreach($this->h_taxonomies as $tax){
Expand Down Expand Up @@ -209,11 +226,12 @@ public function queryFilter($pieces)
LEFT JOIN `$wpdb->term_taxonomy` AS $tt ON $tt.term_taxonomy_id = $tr.term_taxonomy_id AND $tt.taxonomy = '$name'
LEFT JOIN `$wpdb->terms` AS $t ON $t.term_id = $tt.term_id";
endif ;
$pieces['fields'] .= ",GROUP_CONCAT(DISTINCT $t.term_id SEPARATOR ',') AS '$name'";
$pieces['fields'] .= ", GROUP_CONCAT(DISTINCT $t.term_id SEPARATOR ',') AS '$name'";
$c++;
}

$pieces['groupby'] = "$wpdb->posts.ID";
return $pieces;
}
}

}
11 changes: 11 additions & 0 deletions app/Entities/PluginIntegration/WPML.php
Original file line number Diff line number Diff line change
Expand Up @@ -344,4 +344,15 @@ public function getPostTypeCountByLanguage($post_type, $language = null)
$query = $wpdb->prepare("SELECT COUNT(*) FROM {$wpdb->prefix}posts AS p LEFT JOIN {$wpdb->prefix}icl_translations AS trans ON p.ID = trans.element_id WHERE post_type = %s AND p.post_status = 'publish' AND trans.language_code = %s", $post_type, $language);
return $wpdb->get_var($query);
}

/**
* Get language of given post ID
* @param $post_id int - the post ID
* @return string or null - language code
*/
public function getPostLanguage($post_id)
{
$langauge_details = apply_filters('wpml_post_language_details', null, $post_id);
return $langauge_details['language_code'] ?? null;
}
}
69 changes: 68 additions & 1 deletion app/Entities/Post/PostRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,71 @@ public function getChildren($parent_id, $post_type, $posts = [])
}
return $posts;
}
}

/**
* Return posts of a page group (recursion function)
*/
private static function getChildPosts_recurse(int $toplevel_id, array &$pages, string &$sqlWhere) {
global $wpdb;
$sqlQuery = "select id from {$wpdb->posts} where post_parent = {$toplevel_id} $sqlWhere";
$rows_sub_ids = $wpdb->get_results($sqlQuery, ARRAY_A);
foreach ( $rows_sub_ids as &$row_sub_id ) {
$sub_id = $row_sub_id['id'];
$pages[] = $sub_id;
self::getChildPosts_recurse($sub_id, $pages, $sqlWhere);
}
unset($row_sub_id);
}

/**
* Get child posts of a post
*/
public static function getChildPosts(int $toplevel_id, array $args = []) {
$sqlWhere = '';
foreach ( ['post_type', 'post_status'] as $field ) {
if ( array_key_exists($field, $args) ) {
foreach ( ['include', 'exclude'] as $rule ) {
if ( array_key_exists( $rule, $args[$field] ) ) {
$sqlWhere .= \NestedPages\Helpers::getSQLWhere($rule == 'include', $field, (array)$args[$field][$rule]);
}
}
}
}
$page_ids = [ $toplevel_id ];
self::getChildPosts_recurse($toplevel_id, $page_ids, $sqlWhere);
return $page_ids;
}

/**
* Get posts of a page group.
* By default, it returns all related posts.
*/
public static function getPostsOfPageGroup(int $post_id, ?array $postTypes = null) {
$page_ids = self::getChildPosts( self::getPageGroup($post_id), $postTypes );
return $page_ids;
}

/**
* Return page group of the given post
*/
public static function getPageGroup(int $post_id) {
global $wpdb;
$pagegroup = false;
$has_error = false;
$p_id = $post_id;
do {
$rows = $wpdb->get_results("select id, post_parent from {$wpdb->posts} where post_type = 'page' and id = " . $p_id, ARRAY_A);
if ( count( $rows ) == 1 ) {
if ( $rows[0]['post_parent'] ) {
$p_id = $rows[0]['post_parent'];
} else {
$pagegroup = $rows[0]['id'];
}
} else
$has_error = true;
} while ( ! $has_error && $pagegroup === false );
return $has_error ? false : $pagegroup;
}


}
22 changes: 21 additions & 1 deletion app/Form/Listeners/ListingSort.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ private function setURL()
$this->setOrder();
$this->setAuthor();
$this->setTaxonomies();
$this->setPageGroup();
$this->setLanguage();
}

/**
Expand Down Expand Up @@ -81,4 +83,22 @@ private function setTaxonomies()
endif;
endforeach;
}
}

/**
* Set page group
*/
private function setPageGroup()
{
if ( isset($_POST['np_page_group']) && $_POST['np_page_group'] !== '' ) $this->url .= '&np_page_group=' . sanitize_text_field($_POST['np_page_group']);
}

/**
* Set language
*/
private function setLanguage()
{
if ( isset($_POST['lang']) ) $this->url .= '&lang=' . sanitize_text_field($_POST['lang']);
}


}
20 changes: 19 additions & 1 deletion app/Helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,22 @@ public static function defaultPagesLink($type = 'page')
$link = esc_url( admin_url('edit.php?post_type=' . $type ) );
return $link;
}
}

/**
* Return a part of an SQL where clause.
* Since this function is used internally and possibly by theme and plugin developers only,
* it is expected that the field name is not vulnerable to SQL injection.
*/
public static function getSQLWhere(bool $include, string $field, array $values) {
$sqlWhere = ' and ' . $field;
if ( !$include ) $sqlWhere .= ' not';
$sqlWhere .= ' in (';
$sep = '';
foreach ($values as $value) {
$sqlWhere .= $sep . "'" . esc_sql($value) . "'";
$sep = ', ';
}
$sqlWhere .= ')';
}

}
11 changes: 7 additions & 4 deletions app/Views/listing.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,17 @@

<div data-nestedpages-error class="updated error notice is-dismissible" style="display:none;"><p></p><button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php esc_html_e('Dismiss this notice.', 'wp-nested-pages'); ?></span></button></div>

<?php include(NestedPages\Helpers::view('partials/tool-list')); ?>
<?php
include(NestedPages\Helpers::view('partials/tool-list'));
?>

<div id="np-error" class="updated error" style="clear:both;display:none;"></div>


<div class="nestedpages">
<?php $this->getPosts(); ?>

<?php
$this->printPostsList();
?>
<div class="quick-edit quick-edit-form np-inline-modal" style="display:none;">
<?php include( NestedPages\Helpers::view('forms/quickedit-post') ); ?>
</div>
Expand All @@ -122,4 +125,4 @@
if ( $this->listing_repo->showLinks($this->post_type, $this->user) ) include( NestedPages\Helpers::view('forms/link-form') );
include( NestedPages\Helpers::view('forms/bulk-add') );
include( NestedPages\Helpers::view('forms/delete-confirmation-modal') );
if ( $this->integrations->plugins->wpml->installed ) include( NestedPages\Helpers::view('partials/wpml-translations') );
if ( $this->integrations->plugins->wpml->installed ) include( NestedPages\Helpers::view('partials/wpml-translations') );
Loading