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.
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
- Download the plugin files
- Upload the
yt-category-sticky-postsfolder to/wp-content/plugins/ - 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
- Edit any post that has categories assigned
- Find the Category Sticky meta box in the sidebar (below the Publish box)
- Check the categories where you want this post to be sticky
- 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)
- Go to Posts → All Posts
- Hover over a post and click Quick Edit
- Check/uncheck Category Sticky
- Click Update
Bulk Edit
- Go to Posts → All Posts
- Select multiple posts using checkboxes
- Select Edit from the Bulk Actions dropdown
- Click Apply
- Choose Make Sticky or Remove Sticky from the Category Sticky dropdown
- 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 plugincsp_init_hooks()- Register WordPress hookscsp_load_textdomain()- Load translations
Meta Box Methods
csp_add_meta_box()- Add meta box to post editorcsp_render_meta_box()- Render meta box contentcsp_save_post_meta()- Save sticky category selections
Query Modification Methods
csp_modify_query()- Detect category archives and inject sticky logiccsp_orderby_sticky()- Modify SQL ORDER BY clausecsp_fields_sticky()- Placeholder for field modifications
Admin UI Methods
csp_add_admin_column()- Add sticky column to posts listcsp_render_admin_column()- Display sticky status in columncsp_quick_edit_box()- Add quick edit fieldcsp_quick_edit_script()- JavaScript for quick editcsp_bulk_edit_box()- Add bulk edit fieldcsp_display_post_state()- Show sticky badge on posts
Utility Methods
csp_get_sticky_categories()- Get sticky categories for a postcsp_get_sticky_post_for_category()- Get sticky post for a categorycsp_update_sticky_categories()- Update sticky status for postcsp_enqueue_scripts()- Load admin CSS/JScsp_add_action_links()- Add documentation link to plugins page
Lifecycle Methods
activate()- Run on plugin activationdeactivate()- Run on plugin deactivationyt_category_sticky_posts_uninstall()- Run on plugin deletion
How It Works
Data Storage
The plugin stores sticky post information in two places:
- Post Meta: Each sticky post stores its sticky category IDs in
_yt_category_stickymeta key - WordPress Option: A global mapping of category IDs to sticky post IDs in
yt_category_sticky_postsoption
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:
csp_modify_query()detects the category being viewed- Retrieves the sticky post ID for that category
- Injects custom SQL ORDER BY clause via
csp_orderby_sticky() - 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
- Use the
.potfile in/languagesdirectory - Create
.poand.mofiles for your language - 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
- Ensure the post is Published (not draft or pending)
- Verify the post is assigned to the category
- Check that the sticky status is saved (look for the sticky indicator in the posts list)
- Clear any caching plugins (W3 Total Cache, WP Super Cache, etc.)
- Check if theme is using custom queries that bypass the plugin
Meta box not showing
- Verify the post type supports categories
- Check Screen Options (top right) - make sure "Category Sticky" is checked
- Ensure the post is assigned to at least one category
Quick edit not working
- Clear browser cache
- Deactivate other plugins that modify quick edit
- 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:
- Fork the repository
- Create a feature branch
- Follow WordPress coding standards
- Test thoroughly
- 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
- Author: Krasen Slavov
- Author URI: https://krasenslavov.com
- Plugin URI: https://github.com/krasenslavov/yt-category-sticky-posts
Built following WordPress Plugin Handbook and WPCS guidelines.
Support
For issues, questions, or feature requests:
- GitHub Issues: https://github.com/krasenslavov/yt-category-sticky-posts/issues
- Documentation: https://github.com/krasenslavov/yt-category-sticky-posts