Enjoying these plugins? ☕ Buy me a coffee to support ongoing development.

Category Sticky Posts

A lightweight WordPress plugin that allows you to mark one post per category as "sticky" to appear first in category archives. Unlike WordPress's native sticky posts feature (which only works on the main blog page), this plugin provides per-category sticky functionality.

Single File v1.0.0 Updated 1 month ago

YT Category Sticky Posts

A lightweight WordPress plugin that allows you to mark one post per category as "sticky" to appear first in category archives. Unlike WordPress's native sticky posts feature (which only works on the main blog page), this plugin provides per-category sticky functionality.

Features

  • Per-Category Sticky Posts: Mark different posts as sticky in different categories
  • One Sticky Per Category: Automatically replaces previous sticky post when selecting a new one
  • Visual Admin Interface: Intuitive checkbox interface in the post editor
  • Admin Column: See sticky status at a glance in the posts list
  • Quick Edit Support: Manage sticky status from the quick edit panel
  • Bulk Edit Support: Update multiple posts at once
  • Post State Indicator: Visual badge showing sticky status
  • Auto Query Modification: Automatically prioritizes sticky posts in category archives
  • Translation Ready: Full i18n/l10n support
  • WPCS Compliant: Follows WordPress Coding Standards
  • Secure: Proper sanitization, validation, and escaping

Installation

Manual Installation

  1. Download the plugin files
  2. Upload the yt-category-sticky-posts folder to /wp-content/plugins/
  3. Activate the plugin through the 'Plugins' menu in WordPress

Via Composer

composer require krasenslavov/yt-category-sticky-posts

Usage

Making a Post Sticky in Categories

  1. Edit any post that has categories assigned
  2. Find the Category Sticky meta box in the sidebar (below the Publish box)
  3. Check the categories where you want this post to be sticky
  4. Click Update or Publish

Note: Only one post can be sticky per category. Selecting a category will replace any existing sticky post for that category.

From Posts List (Quick Edit)

  1. Go to PostsAll Posts
  2. Hover over a post and click Quick Edit
  3. Check/uncheck Category Sticky
  4. Click Update

Bulk Edit

  1. Go to PostsAll Posts
  2. Select multiple posts using checkboxes
  3. Select Edit from the Bulk Actions dropdown
  4. Click Apply
  5. Choose Make Sticky or Remove Sticky from the Category Sticky dropdown
  6. Click Update

Viewing Sticky Posts

Sticky posts automatically appear first in their respective category archives. No additional configuration needed!

File Structure

yt-category-sticky-posts/
├── class-yt-category-sticky-posts.php    # Main plugin file
├── assets/
│   ├── css/
│   │   └── yt-category-sticky-posts-admin.css    # Admin styles
│   └── js/
│       └── yt-category-sticky-posts-admin.js     # Admin scripts
├── languages/                             # Translation files (pot/po/mo)
└── README.md                              # This file

Plugin Architecture

Constants Defined

YT_CATEGORY_STICKY_POSTS_VERSION  // Plugin version: 1.0.0
YT_CATEGORY_STICKY_POSTS_BASENAME // Plugin base name
YT_CATEGORY_STICKY_POSTS_PATH     // Plugin directory path
YT_CATEGORY_STICKY_POSTS_URL      // Plugin directory URL

Main Class: YT_Category_Sticky_Posts

Core Methods

  • get_instance() - Singleton instance retrieval
  • __construct() - Initialize plugin
  • csp_init_hooks() - Register WordPress hooks
  • csp_load_textdomain() - Load translations

Meta Box Methods

  • csp_add_meta_box() - Add meta box to post editor
  • csp_render_meta_box() - Render meta box content
  • csp_save_post_meta() - Save sticky category selections

Query Modification Methods

  • csp_modify_query() - Detect category archives and inject sticky logic
  • csp_orderby_sticky() - Modify SQL ORDER BY clause
  • csp_fields_sticky() - Placeholder for field modifications

Admin UI Methods

  • csp_add_admin_column() - Add sticky column to posts list
  • csp_render_admin_column() - Display sticky status in column
  • csp_quick_edit_box() - Add quick edit field
  • csp_quick_edit_script() - JavaScript for quick edit
  • csp_bulk_edit_box() - Add bulk edit field
  • csp_display_post_state() - Show sticky badge on posts

Utility Methods

  • csp_get_sticky_categories() - Get sticky categories for a post
  • csp_get_sticky_post_for_category() - Get sticky post for a category
  • csp_update_sticky_categories() - Update sticky status for post
  • csp_enqueue_scripts() - Load admin CSS/JS
  • csp_add_action_links() - Add documentation link to plugins page

Lifecycle Methods

  • activate() - Run on plugin activation
  • deactivate() - Run on plugin deactivation
  • yt_category_sticky_posts_uninstall() - Run on plugin deletion

How It Works

Data Storage

The plugin stores sticky post information in two places:

  1. Post Meta: Each sticky post stores its sticky category IDs in _yt_category_sticky meta key
  2. WordPress Option: A global mapping of category IDs to sticky post IDs in yt_category_sticky_posts option

This dual storage ensures:

  • Fast lookups when rendering category archives
  • Easy management when editing posts
  • Clean data removal on uninstall

Query Modification

When a user visits a category archive:

  1. csp_modify_query() detects the category being viewed
  2. Retrieves the sticky post ID for that category
  3. Injects custom SQL ORDER BY clause via csp_orderby_sticky()
  4. Uses MySQL FIELD() function to prioritize the sticky post
// Simplified SQL logic
ORDER BY FIELD(wp_posts.ID, [sticky_post_id]) DESC, [original_orderby]

This ensures the sticky post appears first while maintaining the original sort order for other posts.

Security

  • ✅ Nonce verification on save
  • ✅ Capability checks (edit_post)
  • ✅ Input sanitization (intval() for category IDs)
  • ✅ Output escaping (esc_html(), esc_attr())
  • ✅ Direct file access prevention
  • ✅ SQL injection prevention (uses WP Query API)

Developer Hooks

Actions

// Before plugin initialization (none currently)

Filters

// Filter sticky post IDs before display (future)
apply_filters( 'yt_category_sticky_posts', $all_sticky_posts );

Programmatic Access

// Get plugin instance
$plugin = YT_Category_Sticky_Posts::get_instance();

// Get sticky categories for a post
$sticky_cats = get_post_meta( $post_id, '_yt_category_sticky', true );

// Get all sticky posts
$all_sticky_posts = get_option( 'yt_category_sticky_posts', array() );

// Get sticky post for specific category
$sticky_post_id = isset( $all_sticky_posts[ $category_id ] )
    ? $all_sticky_posts[ $category_id ]
    : false;

JavaScript API

The plugin provides a global CSP_Admin object with AJAX methods (placeholders for future enhancements):

// Update sticky status
window.CSP_Admin.updateSticky(postId, [categoryIds], function (success, response) {
    if (success) {
        console.log("Updated successfully");
    }
});

// Get sticky posts for a category
window.CSP_Admin.getStickyPosts(categoryId, function (success, response) {
    if (success) {
        console.log("Sticky posts:", response);
    }
});

Styling

The plugin includes comprehensive admin styles:

  • Modern, clean WordPress admin UI design
  • Hover effects and smooth transitions
  • Responsive design for mobile devices
  • Dark mode support
  • Accessibility improvements (focus states, keyboard navigation)
  • Loading state animations

Custom Styling

Override plugin styles by targeting these CSS classes:

/* Meta box */
.csp-meta-box {
}
.csp-category-item {
}
.csp-category-name {
}
.csp-current-sticky {
}

/* Admin column */
.csp-sticky-indicator {
}
.csp-no-sticky {
}

/* Post state */
.post-state-category_sticky {
}

Compatibility

  • WordPress: 5.8 or higher
  • PHP: 7.4 or higher
  • Tested up to: WordPress 6.7
  • Compatible with: Classic Editor, Gutenberg, Quick Edit, Bulk Edit
  • Works with: Any post type that supports categories

Translation

The plugin is translation-ready with text domain yt-category-sticky-posts.

Creating Translations

  1. Use the .pot file in /languages directory
  2. Create .po and .mo files for your language
  3. Place them in /wp-content/languages/plugins/

Available Translations

  • English (default)

Frequently Asked Questions

Can I make a post sticky in multiple categories?

Yes! Check multiple categories in the meta box to make the post sticky in all of them.

What happens if I select a category that already has a sticky post?

The plugin will replace the previous sticky post. You'll see a warning message showing which post will be replaced.

Does this work with custom taxonomies?

Currently, the plugin only supports the default category taxonomy. Custom taxonomy support may be added in future versions.

Does this affect the main blog page?

No, this plugin only affects category archives. Use WordPress's native sticky posts feature for the main blog page.

What happens to sticky posts when I deactivate the plugin?

The sticky status data remains in your database. When you reactivate the plugin, all sticky posts will work again.

What happens when I uninstall the plugin?

All plugin data (post meta and options) is automatically removed from your database during uninstall.

Can I use this with WooCommerce product categories?

This plugin is designed for standard WordPress posts and categories. For WooCommerce products, you would need a modified version.

Troubleshooting

Sticky posts not appearing first

  1. Ensure the post is Published (not draft or pending)
  2. Verify the post is assigned to the category
  3. Check that the sticky status is saved (look for the sticky indicator in the posts list)
  4. Clear any caching plugins (W3 Total Cache, WP Super Cache, etc.)
  5. Check if theme is using custom queries that bypass the plugin

Meta box not showing

  1. Verify the post type supports categories
  2. Check Screen Options (top right) - make sure "Category Sticky" is checked
  3. Ensure the post is assigned to at least one category

Quick edit not working

  1. Clear browser cache
  2. Deactivate other plugins that modify quick edit
  3. Check browser console for JavaScript errors

Development

Code Standards

This plugin follows WordPress Coding Standards (WPCS).

Run PHP_CodeSniffer:

phpcs --standard=WordPress class-yt-category-sticky-posts.php

JavaScript Linting

The JavaScript code follows WordPress JavaScript coding standards.

Run ESLint:

eslint assets/js/yt-category-sticky-posts-admin.js

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Follow WordPress coding standards
  4. Test thoroughly
  5. Submit a pull request

Testing Checklist

  • [x] Plugin activates without errors
  • [x] Meta box displays correctly
  • [x] Sticky status saves properly
  • [x] Category archives show sticky posts first
  • [x] Admin column displays correctly
  • [x] Quick edit works
  • [x] Bulk edit works
  • [x] Post state indicator appears
  • [x] Plugin deactivates cleanly
  • [x] Plugin uninstalls and removes all data
  • [x] No PHP warnings or notices
  • [x] Compatible with WordPress 5.8+
  • [x] Compatible with PHP 7.4+

Performance

The plugin is optimized for performance:

  • Minimal database queries: Uses efficient lookups and caching
  • Only loads on necessary pages: Scripts/styles only load on post edit and list screens
  • Lightweight: ~640 lines of PHP, ~3.7KB CSS, ~7.8KB JS
  • No frontend JavaScript: Zero impact on visitor page load times
  • Smart query modification: Only runs on category archive pages

Changelog

1.0.0 (2024)

  • Initial release
  • Per-category sticky post functionality
  • Admin meta box interface
  • Quick edit and bulk edit support
  • Admin column display
  • Post state indicator
  • Automatic query modification
  • Translation ready
  • Full admin assets (CSS/JS)

License

GPL v2 or later

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

Credits

Built following WordPress Plugin Handbook and WPCS guidelines.

Support

For issues, questions, or feature requests:

Related Resources