{"id":394749,"date":"2026-10-31T16:40:01","date_gmt":"2026-10-31T16:40:01","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/shelfwise-category-post-order-pinned-posts\/"},"modified":"2026-10-31T17:10:23","modified_gmt":"2026-10-31T17:10:23","slug":"jdr-category-post-order","status":"publish","type":"plugin","link":"https:\/\/hr.wordpress.org\/plugins\/jdr-category-post-order\/","author":23595093,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.1.0","stable_tag":"1.1.0","tested":"7.1.4","requires":"6.6","requires_php":"7.4","requires_plugins":null,"header_name":"JDR Category Post Order & Pinned Posts","header_author":"Jun Darwin Ragodos","header_description":"Arrange posts and custom post types inside individual categories, tags and custom taxonomy terms. Pin important posts, sort the rest automatically, and see exactly where each saved order applies.","assets_banners_color":"425578","last_updated":"2026-10-31 17:10:23","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"https:\/\/jundarwinragodos.me","rating":0,"author_block_rating":0,"active_installs":0,"downloads":153,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.0":{"tag":"1.0.0","author":"jundarwinragodos","date":"2026-10-31 16:39:30","revision":3777136},"1.1.0":{"tag":"1.1.0","author":"jundarwinragodos","date":"2026-10-31 17:10:23","revision":3777176}},"upgrade_notice":{"1.1.0":"<p>Adds developer hooks and a feature-request link. No changes to saved orders.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3777136,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3777136,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3777136,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3777136,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.0.0","1.1.0"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3777176,"resolution":"1","location":"assets","locale":"","width":665,"height":779},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3777176,"resolution":"2","location":"assets","locale":"","width":665,"height":779},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3777176,"resolution":"3","location":"assets","locale":"","width":665,"height":635}},"screenshots":{"1":"The Arrange screen: a separate order for each category, shown as cards you can drag, move with buttons or the keyboard, with an ordering preview.","2":"Pinned-first mode: a few posts pinned to the top, the rest sorted automatically.","3":"Settings: choose content types, taxonomies and which archives use a saved order."}},"plugin_section":[],"plugin_tags":[22233,16376,289955,4648,168028],"plugin_category":[],"plugin_contributors":[289956],"plugin_business_model":[],"class_list":["post-394749","plugin","type-plugin","status-publish","hentry","plugin_tags-category-order","plugin_tags-custom-post-order","plugin_tags-pin-posts","plugin_tags-post-order","plugin_tags-reorder-posts","plugin_contributors-jundarwinragodos","plugin_committers-jundarwinragodos"],"banners":{"banner":"https:\/\/ps.w.org\/jdr-category-post-order\/assets\/banner-772x250.png?rev=3777136","banner_2x":"https:\/\/ps.w.org\/jdr-category-post-order\/assets\/banner-1544x500.png?rev=3777136","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/jdr-category-post-order\/assets\/icon-128x128.png?rev=3777136","icon_2x":"https:\/\/ps.w.org\/jdr-category-post-order\/assets\/icon-256x256.png?rev=3777136","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/jdr-category-post-order\/assets\/screenshot-1.png?rev=3777176","caption":"The Arrange screen: a separate order for each category, shown as cards you can drag, move with buttons or the keyboard, with an ordering preview."},{"src":"https:\/\/ps.w.org\/jdr-category-post-order\/assets\/screenshot-2.png?rev=3777176","caption":"Pinned-first mode: a few posts pinned to the top, the rest sorted automatically."},{"src":"https:\/\/ps.w.org\/jdr-category-post-order\/assets\/screenshot-3.png?rev=3777176","caption":"Settings: choose content types, taxonomies and which archives use a saved order."}],"raw_content":"<!--section=description-->\n<p>This plugin gives editors one focused screen (Tools \u2192 Category Post Order) to:<\/p>\n\n<ol>\n<li>Choose a content type (posts or any custom post type) and either its <strong>global order<\/strong> or one <strong>category, tag or custom taxonomy term<\/strong>.<\/li>\n<li>Arrange posts as visual cards, or <strong>pin<\/strong> a few to the top and let the rest sort automatically (newest, oldest, title A\u2013Z or Z\u2013A).<\/li>\n<li>Preview the numbered order, then <strong>Save<\/strong>. Discard, Undo and conflict detection keep edits safe.<\/li>\n<li>See <strong>where the order applies<\/strong> and <strong>check the integration<\/strong> when a listing does not follow it.<\/li>\n<\/ol>\n\n<p>Each term keeps its own order per content type. The same post can be 2nd in \"Residential\" and 9th in \"Commercial\". The global order is stored separately and never mixed with term orders.<\/p>\n\n<h4>What it changes, and what it never changes<\/h4>\n\n<ul>\n<li>It only replaces the ORDER BY of queries that are meant to use it. It never changes which posts a query returns, so visibility rules, drafts, private posts, pagination and post counts are untouched.<\/li>\n<li>Nothing changes on your site until you save an order <strong>and<\/strong> a listing uses it, through native archive integration (opt-in), a Query Loop block set to a curated order, or a custom query that passes the <code>jdcpo<\/code> argument.<\/li>\n<li>Search results, feeds, REST API responses, admin lists and other secondary queries are never reordered unless they opt in explicitly.<\/li>\n<li>When a listing selects several terms, the plugin does not guess. It keeps the normal order unless the developer names the ordering term.<\/li>\n<\/ul>\n\n<p>No external services, tracking, accounts or page builder required.<\/p>\n\n<h3>Using a saved order<\/h3>\n\n<h4>1. Native archives<\/h4>\n\n<p>Enable them in Settings. The main query of an enabled archive uses the saved order unless:<\/p>\n\n<ul>\n<li>the request asks for another sort (for example <code>?orderby=title<\/code>, or a theme's <code>pre_get_posts<\/code> sets <code>orderby<\/code>)<\/li>\n<li>the archive mixes several content types<\/li>\n<li>several terms are selected<\/li>\n<\/ul>\n\n<h4>2. Query Loop block<\/h4>\n\n<p>Select a Query Loop block and open <strong>Curated post order<\/strong> in the block sidebar. Choose <strong>Global order<\/strong> or <strong>A term's order\u2026<\/strong> and pick the term. The editor preview and the published page use the same order. (Query Loops set to \"Use default query \/ inherit\" follow the archive integration instead.)<\/p>\n\n<h4>3. Custom PHP queries<\/h4>\n\n<p>Pass the <code>jdcpo<\/code> argument to <code>WP_Query<\/code>, or use the helper <code>jdcpo_query_args()<\/code>, which also sets <code>suppress_filters =&gt; false<\/code> (required for <code>get_posts()<\/code>).<\/p>\n\n<p>Global order of a custom post type:<\/p>\n\n<pre><code>$q = new WP_Query( jdcpo_query_args( array( 'post_type' =&gt; 'project', 'posts_per_page' =&gt; 12 ), 'global' ) );\n<\/code><\/pre>\n\n<p>One term's order (the query still decides which posts appear):<\/p>\n\n<pre><code>$q = new WP_Query( jdcpo_query_args( array(\n    'post_type' =&gt; 'project',\n    'tax_query' =&gt; array( array( 'taxonomy' =&gt; 'project_category', 'terms' =&gt; 12 ) ),\n), array( 'term' =&gt; 12 ) ) );\n<\/code><\/pre>\n\n<p>Automatic: the single selected term's order, or the global order when no term is selected:<\/p>\n\n<pre><code>$q = new WP_Query( array( 'post_type' =&gt; 'project', 'project_category' =&gt; 'residential', 'jdcpo' =&gt; true ) );\n<\/code><\/pre>\n\n<p>Pinned first: nothing extra in code. Pinned-first vs manual is part of the saved order, so the same query follows whichever mode the editor chose.<\/p>\n\n<p>Multi-term listing with a chosen ordering term, and a fallback to the global order when that term has no saved order:<\/p>\n\n<pre><code>'jdcpo' =&gt; array( 'term' =&gt; 'residential', 'taxonomy' =&gt; 'project_category', 'fallback' =&gt; 'global' )\n<\/code><\/pre>\n\n<p>Accepted values: <code>true<\/code> \/ <code>'auto'<\/code>, <code>'global'<\/code>, <code>'term:123'<\/code>, or an array with <code>term<\/code> (ID or slug), <code>taxonomy<\/code> (required for slugs), <code>post_type<\/code>, <code>fallback<\/code> (<code>'global'<\/code> or <code>'none'<\/code>).<\/p>\n\n<h4>4. AJAX \"load more\"<\/h4>\n\n<p>Use the same arguments on every page request. Pagination stays correct because only the ORDER BY changes.<\/p>\n\n<pre><code>add_action( 'wp_ajax_nopriv_projects_more', 'mytheme_projects_more' );\nadd_action( 'wp_ajax_projects_more', 'mytheme_projects_more' );\nfunction mytheme_projects_more() {\n    $page  = max( 1, absint( $_POST['page'] ?? 1 ) );\n    $term  = absint( $_POST['term'] ?? 0 );\n    $query = new WP_Query( jdcpo_query_args( array(\n        'post_type'      =&gt; 'project',\n        'post_status'    =&gt; 'publish', \/\/ admin-ajax runs as \"admin\": always set the status.\n        'posts_per_page' =&gt; 9,\n        'paged'          =&gt; $page,\n        'tax_query'      =&gt; $term ? array( array( 'taxonomy' =&gt; 'project_category', 'terms' =&gt; $term ) ) : array(),\n    ), $term ? array( 'term' =&gt; $term ) : 'global' ) );\n    ob_start();\n    while ( $query-&gt;have_posts() ) { $query-&gt;the_post(); get_template_part( 'parts\/project-card' ); }\n    wp_reset_postdata();\n    wp_send_json_success( array( 'html' =&gt; ob_get_clean(), 'more' =&gt; $page &lt; $query-&gt;max_num_pages ) );\n}\n<\/code><\/pre>\n\n<p>Important: requests to <code>admin-ajax.php<\/code> run with <code>is_admin() === true<\/code>, where WordPress includes drafts in queries without an explicit status. Always set <code>post_status =&gt; 'publish'<\/code> in public AJAX handlers. That is standard WordPress practice, not something this plugin changes.<\/p>\n\n<h4>Debugging<\/h4>\n\n<ul>\n<li><strong>Check integration<\/strong> (on the Arrange screen) lists what is configured for the selected scope and common reasons an order may not apply.<\/li>\n<li>While logged in as an Editor or Administrator, open any listing on your site. The <strong>Post order<\/strong> toolbar menu reports each relevant query on that page and why it did or did not use a curated order, for example \"This listing sorts by title and has not opted into curated ordering.\"<\/li>\n<li>In code: <code>jdcpo_explain_query( $query )<\/code> returns the same plain-language reason.<\/li>\n<\/ul>\n\n<h4>Hooks for developers<\/h4>\n\n<ul>\n<li><code>jdcpo_loaded<\/code> (action): fires after the plugin has loaded. Add-ons hook in here.<\/li>\n<li><code>jdcpo_order_config<\/code> (filter): <code>( array $config, Scope $scope, WP_Query $query )<\/code>. Adjust pins or positions for one query just before ORDER BY is built. It cannot change which posts are returned.<\/li>\n<li><code>jdcpo_order_saved<\/code> (action): <code>( Scope $scope, array $config )<\/code>. Fires after an order is saved or undone on the Arrange screen.<\/li>\n<li><code>jdcpo_available_post_types<\/code> (filter): <code>( WP_Post_Type[] $types )<\/code>. The content types that can be enabled in Settings.<\/li>\n<li><code>jdcpo_scope_capability<\/code> (filter): <code>( string $cap, Scope $scope )<\/code>. Who can arrange a scope.<\/li>\n<\/ul>\n\n<h3>Known limitations<\/h3>\n\n<ul>\n<li>One order per term per content type: up to 5,000 arranged and 200 pinned posts per scope. The admin screen loads 50 cards at a time and can load up to 2,000 for \"Move to bottom\". Positions beyond the loaded range keep their saved order.<\/li>\n<li>Ordering is applied with a CASE expression on post IDs. That is fast for typical collections (hundreds to a few thousand arranged posts). Very large catalogues have not been benchmarked.<\/li>\n<li>Listings that mix several content types, or several terms without a named ordering term, keep their normal order by design.<\/li>\n<li>WordPress sticky posts are ignored on a curated blog index (pins replace them).<\/li>\n<li><code>get_posts()<\/code> skips query filters by default. Pass <code>suppress_filters =&gt; false<\/code>, or use <code>jdcpo_query_args()<\/code>.<\/li>\n<li>Page caches may serve an older copy of a listing after you save. Clear your cache.<\/li>\n<li>Theme template files that contain a Query Loop are not scanned by \"Where this applies\"; only content saved in the database is.<\/li>\n<li>Multilingual plugins (WPML, Polylang), WooCommerce product sorting and third-party page builders (Elementor, Divi, etc.) need separate compatibility verification. Translated terms are separate terms, so each language's term has its own order. Builder widgets that run their own queries need the <code>jdcpo<\/code> argument.<\/li>\n<\/ul>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>jdr-category-post-order<\/code> folder to <code>\/wp-content\/plugins\/<\/code> or install the ZIP via Plugins \u2192 Add New \u2192 Upload Plugin.<\/li>\n<li>Activate <strong>JDR Category Post Order &amp; Pinned Posts<\/strong>.<\/li>\n<li>Go to <strong>Tools \u2192 Category Post Order \u2192 Settings<\/strong> (administrators):\n\n<ul>\n<li>Tick the content types to arrange, and for each one the taxonomies that should have per-term orders. Posts + Categories are enabled by default.<\/li>\n<li>Optionally turn on <strong>native archives<\/strong>: the blog\/post-type archive uses the global order; selected taxonomy archives use the term's order.<\/li>\n<li>Choose what a term archive does when the term has no saved order: keep the theme order (default) or use the global order.<\/li>\n<\/ul><\/li>\n<li>Go to <strong>Tools \u2192 Category Post Order \u2192 Arrange<\/strong>, choose a content type and a term, arrange or pin, and click <strong>Save order<\/strong>.<\/li>\n<\/ol>\n\n<p>Who can arrange: users who can edit others' posts of that content type (Editors and Administrators by default). Change it with the <code>jdcpo_scope_capability<\/code> filter. Only Administrators see Settings.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20it%20use%20menu_order%3F\"><h3>Does it use menu_order?<\/h3><\/dt>\n<dd><p>No. <code>menu_order<\/code> is one number per post, so it cannot hold different positions in different categories. The plugin stores an ordered list per term and content type (term meta) and a global list per content type (an option).<\/p><\/dd>\n<dt id=\"what%20happens%20to%20new%20posts%3F\"><h3>What happens to new posts?<\/h3><\/dt>\n<dd><p>In Manual mode they appear first or last (your choice), newest first, until you arrange them. In Pinned-first mode they join the automatically sorted remainder.<\/p><\/dd>\n<dt id=\"what%20if%20a%20post%20leaves%20a%20category%20or%20is%20deleted%3F\"><h3>What if a post leaves a category or is deleted?<\/h3><\/dt>\n<dd><p>It simply stops appearing in that listing. Its stale entry is removed the next time the order is saved.<\/p><\/dd>\n<dt id=\"do%20drafts%20show%20up%20on%20my%20site%3F\"><h3>Do drafts show up on my site?<\/h3><\/dt>\n<dd><p>No. Drafts, scheduled and private posts are listed on the Arrange screen so you can position them in advance, but the plugin never changes which posts a visitor's query returns.<\/p><\/dd>\n<dt id=\"does%20it%20work%20with%20woocommerce%20products%3F\"><h3>Does it work with WooCommerce products?<\/h3><\/dt>\n<dd><p>Not yet. Products can be enabled in Settings, but WooCommerce shop and category pages use WooCommerce's own sorting, so they don't follow a saved order. WooCommerce support is being considered: if you need it, say so in the <a href=\"https:\/\/wordpress.org\/support\/plugin\/jdr-category-post-order\/\">support forum<\/a>.<\/p><\/dd>\n<dt id=\"is%20my%20data%20deleted%20when%20i%20deactivate%3F\"><h3>Is my data deleted when I deactivate?<\/h3><\/dt>\n<dd><p>No. Data is kept on deactivation and on deletion, unless you tick \"Delete all saved orders and settings when the plugin is deleted\" in Settings.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>New: developer hooks <code>jdcpo_loaded<\/code>, <code>jdcpo_order_config<\/code>, <code>jdcpo_order_saved<\/code> and <code>jdcpo_available_post_types<\/code>.<\/li>\n<li>New: \"Request a feature\" link on the Arrange screen and a support link on the Plugins screen.<\/li>\n<li>Readme: screenshots, a hooks reference and a WooCommerce FAQ.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>First release: per-term and global ordering for posts and custom post types; Manual and Pinned-first modes; visual cards with bulk actions, search, keyboard reordering and ordering preview; save, discard, undo and conflict detection; native archive, Query Loop and WP_Query integrations; Check integration panel and frontend toolbar inspector.<\/li>\n<\/ul>","raw_excerpt":"Order posts within each category, tag or custom taxonomy term. Pin important posts, auto-sort the rest, and see where each order applies.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/hr.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/394749","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hr.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/hr.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/hr.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=394749"}],"author":[{"embeddable":true,"href":"https:\/\/hr.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/jundarwinragodos"}],"wp:attachment":[{"href":"https:\/\/hr.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=394749"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/hr.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=394749"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/hr.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=394749"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/hr.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=394749"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/hr.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=394749"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/hr.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=394749"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}