Restore to commit 74e578279624c6045ca440a3459ebfa1f8d54191

This commit is contained in:
southseact-3d
2026-02-07 20:32:41 +00:00
commit ed67b7741b
252 changed files with 99814 additions and 0 deletions

View File

@@ -0,0 +1,104 @@
# FAQ Manager Plugin
A comprehensive WordPress plugin that allows administrators to easily create and manage FAQ pages with drag-and-drop reordering functionality.
## Features
- **Easy FAQ Management**: Add, edit, and delete FAQs from a user-friendly admin interface
- **Drag-and-Drop Reordering**: Intuitive FAQ ordering with visual feedback
- **Automatic Page Creation**: Automatically creates an FAQ page upon plugin activation
- **Responsive Design**: Mobile-friendly frontend FAQ display with modern styling
- **Accessibility**: WCAG 2.1 AA compliant with keyboard navigation and screen reader support
- **Dark Mode Support**: Automatic styling for dark mode preferences
- **Clean Uninstallation**: Complete data removal when plugin is uninstalled
## Installation
1. Upload the `pc-faq-manager-abc123` folder to your WordPress plugins directory
2. Activate the plugin through the "Plugins" menu in WordPress
3. Access "FAQ Manager" from your WordPress admin menu to start adding FAQs
4. The FAQ page will be automatically created at `/faq/`
## Usage
### For Administrators
1. **Adding FAQs**: Navigate to FAQ Manager → Add New FAQ, enter your question and answer, and click "Create FAQ"
2. **Editing FAQs**: From the main FAQ Manager page, click "Edit" on any FAQ to modify it inline
3. **Reordering FAQs**: Drag and drop FAQs to reorder them, then click "Save Order"
4. **Deleting FAQs**: Click "Delete" on any FAQ to remove it permanently
### For Website Visitors
- Visit `/faq/` on your website to view all FAQs
- Click on any question to expand/collapse the answer
- Use keyboard navigation (arrow keys) to browse FAQs
- Print-friendly format is available
## Files Structure
```
pc-faq-manager-abc123/
├── pc-faq-manager-abc123.php # Main plugin file
├── uninstall.php # Cleanup on uninstall
├── includes/
│ ├── class-pc-faq-manager-helper.php
│ └── class-pc-faq-manager-post-type.php
├── admin/
│ ├── class-pc-faq-manager-admin.php
│ ├── css/admin-style.css
│ ├── js/admin-script.js
│ └── templates/
│ ├── main-page.php
│ └── add-page.php
├── public/
│ ├── class-pc-faq-manager-public.php
│ ├── css/public-style.css
│ └── js/public-script.js
└── assets/ # For future media files
```
## Security Features
- Nonce verification for all AJAX requests
- Capability checks to ensure only authorized users can manage FAQs
- Input sanitization and output escaping
- SQL query protection using WordPress $wpdb prepare
- XSS prevention with proper data filtering
## Compatibility
- WordPress 5.0+
- PHP 7.4+
- Tested with popular themes and plugins
- Compatible with WordPress block editor (Gutenberg)
## Customization
### Styling
The plugin uses CSS custom properties that can be overridden in your theme:
```css
:root {
--pc-faq-primary-color: #your-color;
--pc-faq-secondary-color: #your-bg-color;
/* ... other variables */
}
```
### Shortcode
Use the `[pc_faq_page]` shortcode in any page or post to display FAQs:
```html
[pc_faq_page title="Custom Title" show_count="false"]
```
## Support
For support and feature requests, please visit: https://plugincompass.com/plugins/pc-faq-manager-abc123
## License
This plugin is licensed under the GPLv2 or later license.

View File

@@ -0,0 +1,221 @@
<?php
/**
* FAQ Manager Admin Interface
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
class PC_FAQ_Manager_Admin {
/**
* Constructor
*/
public function __construct() {
add_action('admin_menu', array($this, 'add_admin_menu'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
add_action('wp_ajax_pc_faq_reorder', array($this, 'handle_ajax_reorder'));
add_action('wp_ajax_pc_faq_add_new', array($this, 'handle_ajax_add_new'));
add_action('wp_ajax_pc_faq_edit', array($this, 'handle_ajax_edit'));
add_action('wp_ajax_pc_faq_delete', array($this, 'handle_ajax_delete'));
}
/**
* Add admin menu
*/
public function add_admin_menu() {
add_menu_page(
__('FAQ Manager', PC_FAQ_MANAGER_SLUG),
__('FAQ', PC_FAQ_MANAGER_SLUG),
'manage_options',
'pc-faq-manager',
array($this, 'render_main_page'),
'dashicons-editor-help',
25
);
add_submenu_page(
'pc-faq-manager',
__('All FAQs', PC_FAQ_MANAGER_SLUG),
__('All FAQs', PC_FAQ_MANAGER_SLUG),
'manage_options',
'pc-faq-manager',
array($this, 'render_main_page')
);
add_submenu_page(
'pc-faq-manager',
__('Add New FAQ', PC_FAQ_MANAGER_SLUG),
__('Add New', PC_FAQ_MANAGER_SLUG),
'manage_options',
'pc-faq-manager-add',
array($this, 'render_add_page')
);
}
/**
* Enqueue admin scripts and styles
*/
public function enqueue_admin_scripts($hook) {
if (strpos($hook, 'pc-faq-manager') !== false) {
wp_enqueue_style('pc-faq-manager-admin', PC_FAQ_MANAGER_PLUGIN_URL . 'admin/css/admin-style.css', array(), PC_FAQ_MANAGER_VERSION);
wp_enqueue_script('jquery-ui-sortable');
wp_enqueue_script('pc-faq-manager-admin', PC_FAQ_MANAGER_PLUGIN_URL . 'admin/js/admin-script.js', array('jquery', 'jquery-ui-sortable'), PC_FAQ_MANAGER_VERSION, true);
wp_localize_script('pc-faq-manager-admin', 'pc_faq_manager', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('pc_faq_manager_nonce'),
'confirm_delete' => __('Are you sure you want to delete this FAQ?', PC_FAQ_MANAGER_SLUG),
'saving' => __('Saving...', PC_FAQ_MANAGER_SLUG),
'saved' => __('Saved!', PC_FAQ_MANAGER_SLUG),
'error' => __('Error occurred. Please try again.', PC_FAQ_MANAGER_SLUG),
));
}
}
/**
* Render main admin page
*/
public function render_main_page() {
if (!PC_FAQ_Manager_Helper::can_manage_faqs()) {
wp_die(__('You do not have sufficient permissions to access this page.', PC_FAQ_MANAGER_SLUG));
}
$faqs = PC_FAQ_Manager_Helper::get_faqs();
$faq_count = PC_FAQ_Manager_Helper::get_faq_count();
include PC_FAQ_MANAGER_PLUGIN_DIR . 'admin/templates/main-page.php';
}
/**
* Render add new FAQ page
*/
public function render_add_page() {
if (!PC_FAQ_Manager_Helper::can_manage_faqs()) {
wp_die(__('You do not have sufficient permissions to access this page.', PC_FAQ_MANAGER_SLUG));
}
include PC_FAQ_MANAGER_PLUGIN_DIR . 'admin/templates/add-page.php';
}
/**
* Handle AJAX reorder request
*/
public function handle_ajax_reorder() {
check_ajax_referer('pc_faq_manager_nonce', 'nonce');
if (!PC_FAQ_Manager_Helper::can_manage_faqs()) {
wp_die(__('Permission denied.', PC_FAQ_MANAGER_SLUG));
}
if (isset($_POST['faq_ids']) && is_array($_POST['faq_ids'])) {
$result = PC_FAQ_Manager_Helper::reorder_faqs($_POST['faq_ids']);
if ($result) {
wp_send_json_success(array('message' => __('FAQs reordered successfully.', PC_FAQ_MANAGER_SLUG)));
} else {
wp_send_json_error(array('message' => __('Failed to reorder FAQs.', PC_FAQ_MANAGER_SLUG)));
}
}
wp_send_json_error(array('message' => __('Invalid request.', PC_FAQ_MANAGER_SLUG)));
}
/**
* Handle AJAX add new FAQ
*/
public function handle_ajax_add_new() {
check_ajax_referer('pc_faq_manager_nonce', 'nonce');
if (!PC_FAQ_Manager_Helper::can_manage_faqs()) {
wp_die(__('Permission denied.', PC_FAQ_MANAGER_SLUG));
}
$title = sanitize_text_field($_POST['title'] ?? '');
$content = wp_kses_post($_POST['content'] ?? '');
if (empty($title) || empty($content)) {
wp_send_json_error(array('message' => __('Title and content are required.', PC_FAQ_MANAGER_SLUG)));
}
$faq_data = array(
'post_title' => $title,
'post_content' => $content,
'post_status' => 'publish',
'post_type' => 'pc_faq',
);
$faq_id = wp_insert_post($faq_data);
if ($faq_id && !is_wp_error($faq_id)) {
$max_order = PC_FAQ_Manager_Helper::get_max_order();
update_post_meta($faq_id, '_pc_faq_order', $max_order + 1);
wp_send_json_success(array(
'message' => __('FAQ added successfully.', PC_FAQ_MANAGER_SLUG),
'faq_id' => $faq_id,
));
} else {
wp_send_json_error(array('message' => __('Failed to add FAQ.', PC_FAQ_MANAGER_SLUG)));
}
}
/**
* Handle AJAX edit FAQ
*/
public function handle_ajax_edit() {
check_ajax_referer('pc_faq_manager_nonce', 'nonce');
if (!PC_FAQ_Manager_Helper::can_manage_faqs()) {
wp_die(__('Permission denied.', PC_FAQ_MANAGER_SLUG));
}
$faq_id = intval($_POST['faq_id'] ?? 0);
$title = sanitize_text_field($_POST['title'] ?? '');
$content = wp_kses_post($_POST['content'] ?? '');
if (!$faq_id || empty($title) || empty($content)) {
wp_send_json_error(array('message' => __('Invalid data provided.', PC_FAQ_MANAGER_SLUG)));
}
$faq_data = array(
'ID' => $faq_id,
'post_title' => $title,
'post_content' => $content,
);
$result = wp_update_post($faq_data);
if ($result && !is_wp_error($result)) {
wp_send_json_success(array('message' => __('FAQ updated successfully.', PC_FAQ_MANAGER_SLUG)));
} else {
wp_send_json_error(array('message' => __('Failed to update FAQ.', PC_FAQ_MANAGER_SLUG)));
}
}
/**
* Handle AJAX delete FAQ
*/
public function handle_ajax_delete() {
check_ajax_referer('pc_faq_manager_nonce', 'nonce');
if (!PC_FAQ_Manager_Helper::can_manage_faqs()) {
wp_die(__('Permission denied.', PC_FAQ_MANAGER_SLUG));
}
$faq_id = intval($_POST['faq_id'] ?? 0);
if (!$faq_id) {
wp_send_json_error(array('message' => __('Invalid FAQ ID.', PC_FAQ_MANAGER_SLUG)));
}
$result = wp_delete_post($faq_id, true);
if ($result) {
wp_send_json_success(array('message' => __('FAQ deleted successfully.', PC_FAQ_MANAGER_SLUG)));
} else {
wp_send_json_error(array('message' => __('Failed to delete FAQ.', PC_FAQ_MANAGER_SLUG)));
}
}
}

View File

@@ -0,0 +1,439 @@
/* FAQ Manager Admin Styles */
.pc-faq-manager-admin {
max-width: 1200px;
margin: 0 auto;
}
/* Header Section */
.pc-faq-manager-header {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 20px;
margin: 20px 0;
padding: 20px;
background: #f9f9f9;
border-radius: 8px;
border: 1px solid #e5e5e5;
}
.pc-faq-manager-stats .stat-card {
background: #fff;
padding: 20px;
border-radius: 6px;
border: 1px solid #ddd;
text-align: center;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.stat-number {
font-size: 2.5em;
font-weight: 600;
color: #0073aa;
line-height: 1;
margin-bottom: 5px;
}
.stat-label {
color: #666;
font-size: 0.9em;
}
.pc-faq-manager-instructions h3 {
margin-top: 0;
margin-bottom: 10px;
color: #23282d;
}
.pc-faq-manager-instructions ul {
margin: 0;
padding-left: 20px;
}
.pc-faq-manager-instructions li {
margin-bottom: 5px;
color: #444;
}
/* FAQ List */
.pc-faq-manager-list {
background: #fff;
border: 1px solid #e5e5e5;
border-radius: 6px;
overflow: hidden;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
}
.pc-faq-manager-list-header {
display: grid;
grid-template-columns: 40px 1fr 1fr 150px;
background: #fcfcfc;
border-bottom: 1px solid #e5e5e5;
padding: 12px 16px;
font-weight: 600;
color: #23282d;
}
.pc-faq-manager-items {
max-height: 600px;
overflow-y: auto;
}
.pc-faq-item {
display: grid;
grid-template-columns: 40px 1fr 1fr 150px;
padding: 16px;
border-bottom: 1px solid #f0f0f0;
align-items: start;
transition: background-color 0.2s ease;
}
.pc-faq-item:hover {
background-color: #f9f9f9;
}
.pc-faq-item:last-child {
border-bottom: none;
}
.sort-handle {
display: flex;
align-items: center;
justify-content: center;
cursor: move;
color: #666;
font-size: 14px;
}
.sort-handle:hover {
color: #0073aa;
}
.faq-content h4 {
margin: 0 0 5px 0;
font-size: 14px;
font-weight: 600;
color: #23282d;
line-height: 1.3;
}
.faq-excerpt {
font-size: 12px;
color: #666;
line-height: 1.4;
}
.faq-content {
color: #444;
font-size: 13px;
line-height: 1.4;
}
.faq-actions {
display: flex;
flex-direction: column;
gap: 8px;
}
.faq-actions .button {
padding: 6px 12px;
font-size: 12px;
line-height: 1.4;
height: auto;
white-space: nowrap;
}
.faq-actions .dashicons {
font-size: 14px;
margin-right: 4px;
vertical-align: middle;
}
/* Draggable state */
.pc-faq-manager-items.ui-sortable .pc-faq-item {
cursor: move;
}
.pc-faq-manager-items.ui-sortable-helper {
background: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
opacity: 0.9;
}
.pc-faq-manager-items.ui-sortable-placeholder {
background: #f0f6fc;
border: 2px dashed #0073aa;
margin: 4px 16px;
border-radius: 4px;
}
/* Footer */
.pc-faq-manager-footer {
display: flex;
align-items: center;
gap: 15px;
margin-top: 20px;
padding: 15px 0;
}
.pc-faq-save-status {
font-size: 13px;
color: #666;
}
.pc-faq-save-status.saving {
color: #f0ad4e;
}
.pc-faq-save-status.saved {
color: #5cb85c;
}
.pc-faq-save-status.error {
color: #d9534f;
}
/* Empty State */
.pc-faq-manager-empty {
text-align: center;
padding: 60px 20px;
background: #fff;
border: 1px solid #e5e5e5;
border-radius: 6px;
}
.pc-faq-manager-empty-icon {
font-size: 48px;
color: #666;
margin-bottom: 20px;
}
.pc-faq-manager-empty h2 {
margin: 0 0 15px 0;
color: #23282d;
}
.pc-faq-manager-empty p {
margin: 0 0 20px 0;
color: #666;
font-size: 14px;
}
/* Add Form */
.pc-faq-manager-add-form {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 30px;
}
.pc-faq-form-container {
background: #fff;
padding: 25px;
border: 1px solid #e5e5e5;
border-radius: 6px;
}
.pc-faq-form-container h2 {
margin-top: 0;
margin-bottom: 25px;
color: #23282d;
}
.form-field {
margin-bottom: 20px;
}
.form-field label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #23282d;
}
.form-field input[type="text"],
.form-field textarea {
width: 100%;
}
.form-field .description {
margin-top: 8px;
font-size: 12px;
color: #666;
font-style: italic;
}
.wp-editor-container {
border: 1px solid #ddd;
}
/* Help Section */
.pc-faq-manager-help {
display: flex;
flex-direction: column;
gap: 20px;
}
.help-card {
background: #fff;
padding: 20px;
border: 1px solid #e5e5e5;
border-radius: 6px;
}
.help-card h3 {
margin-top: 0;
margin-bottom: 15px;
color: #23282d;
}
.help-card ul {
margin: 0;
padding-left: 20px;
}
.help-card li {
margin-bottom: 8px;
color: #444;
font-size: 14px;
}
.help-card code {
background: #f5f5f5;
padding: 4px 8px;
border-radius: 3px;
font-family: 'Courier New', monospace;
font-size: 13px;
}
/* Modal */
.pc-faq-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 100000;
}
.pc-faq-modal-backdrop {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 99999;
}
.pc-faq-modal-content {
position: relative;
background: #fff;
margin: 50px auto;
max-width: 700px;
max-height: 80vh;
overflow-y: auto;
border-radius: 8px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
z-index: 100001;
}
.pc-faq-modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 25px;
border-bottom: 1px solid #e5e5e5;
background: #fcfcfc;
border-radius: 8px 8px 0 0;
}
.pc-faq-modal-header h2 {
margin: 0;
color: #23282d;
}
.pc-faq-modal-close {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #666;
padding: 0;
line-height: 1;
}
.pc-faq-modal-close:hover {
color: #d9534f;
}
.pc-faq-modal-body {
padding: 25px;
}
.pc-faq-modal-footer {
display: flex;
justify-content: flex-end;
gap: 15px;
padding: 20px 25px;
border-top: 1px solid #e5e5e5;
background: #fcfcfc;
border-radius: 0 0 8px 8px;
}
/* Notice styling */
.notice.is-dismissible {
margin-top: 20px;
}
/* Responsive Design */
@media screen and (max-width: 1200px) {
.pc-faq-manager-admin {
max-width: 100%;
}
}
@media screen and (max-width: 980px) {
.pc-faq-manager-header {
grid-template-columns: 1fr;
}
.pc-faq-manager-add-form {
grid-template-columns: 1fr;
}
.pc-faq-manager-list-header,
.pc-faq-item {
grid-template-columns: 40px 1fr 80px;
}
.faq-answer {
display: none;
}
}
@media screen and (max-width: 600px) {
.pc-faq-manager-list-header,
.pc-faq-item {
grid-template-columns: 30px 1fr;
}
.faq-actions {
grid-column: 1 / -1;
flex-direction: row;
margin-top: 10px;
}
.sort-handle {
font-size: 12px;
}
.pc-faq-modal-content {
margin: 10px;
max-height: 90vh;
}
.pc-faq-modal-header,
.pc-faq-modal-body,
.pc-faq-modal-footer {
padding: 15px 20px;
}
}

View File

@@ -0,0 +1,250 @@
jQuery(document).ready(function($) {
// Sortable functionality
$('#pc-faq-sortable').sortable({
handle: '.sort-handle',
placeholder: 'pc-faq-sortable-placeholder',
helper: 'clone',
opacity: 0.9,
cursor: 'move',
tolerance: 'pointer',
start: function(event, ui) {
ui.placeholder.height(ui.item.outerHeight());
},
update: function(event, ui) {
$('#pc-faq-save-order').addClass('highlight');
}
});
// Save order
$('#pc-faq-save-order').on('click', function() {
var $button = $(this);
var $status = $('.pc-faq-save-status');
if ($button.prop('disabled')) {
return;
}
var faqIds = [];
$('#pc-faq-sortable .pc-faq-item').each(function() {
faqIds.push($(this).data('faq-id'));
});
$button.prop('disabled', true);
$status.text(pc_faq_manager.saving).removeClass('saved error').addClass('saving');
$.post(pc_faq_manager.ajax_url, {
action: 'pc_faq_reorder',
nonce: pc_faq_manager.nonce,
faq_ids: faqIds
})
.done(function(response) {
if (response.success) {
$status.text(pc_faq_manager.saved).removeClass('saving error').addClass('saved');
$button.removeClass('highlight');
} else {
$status.text(pc_faq_manager.error).removeClass('saving saved').addClass('error');
}
})
.fail(function() {
$status.text(pc_faq_manager.error).removeClass('saving saved').addClass('error');
})
.always(function() {
$button.prop('disabled', false);
setTimeout(function() {
$status.text('').removeClass('saving saved error');
}, 3000);
});
});
// Edit FAQ modal
var $modal = $('#pc-faq-edit-modal');
var $backdrop = $('.pc-faq-modal-backdrop');
var currentEditingId = null;
function openEditModal(faqId) {
var $item = $('.pc-faq-item[data-faq-id="' + faqId + '"]');
var title = $item.find('.faq-question h4').text();
var content = $item.find('.faq-content').text();
$('#pc-faq-edit-title').val(title);
$('#pc-faq-edit-content').val(content);
currentEditingId = faqId;
$modal.show();
$backdrop.show();
$('body').css('overflow', 'hidden');
}
function closeEditModal() {
$modal.hide();
$backdrop.hide();
$('body').css('overflow', '');
currentEditingId = null;
}
$('.pc-faq-edit').on('click', function() {
var faqId = $(this).data('faq-id');
openEditModal(faqId);
});
$('.pc-faq-modal-close, .pc-faq-modal-cancel, .pc-faq-modal-backdrop').on('click', function() {
closeEditModal();
});
// Save edited FAQ
$('.pc-faq-modal-save').on('click', function() {
if (!currentEditingId) {
return;
}
var $button = $(this);
var title = $('#pc-faq-edit-title').val().trim();
var content = $('#pc-faq-edit-content').val();
if (!title || !content) {
alert('Please fill in both the question and answer.');
return;
}
if ($button.prop('disabled')) {
return;
}
$button.prop('disabled', true);
$.post(pc_faq_manager.ajax_url, {
action: 'pc_faq_edit',
nonce: pc_faq_manager.nonce,
faq_id: currentEditingId,
title: title,
content: content
})
.done(function(response) {
if (response.success) {
// Update the item in the list
var $item = $('.pc-faq-item[data-faq-id="' + currentEditingId + '"]');
$item.find('.faq-question h4').text(title);
$item.find('.faq-content').text(content.replace(/<[^>]*>/g, '').substring(0, 100) + '...');
$item.find('.faq-excerpt').text(content.replace(/<[^>]*>/g, '').substring(0, 50) + '...');
closeEditModal();
} else {
alert('Failed to update FAQ: ' + (response.data.message || 'Unknown error'));
}
})
.fail(function() {
alert('Failed to update FAQ. Please try again.');
})
.always(function() {
$button.prop('disabled', false);
});
});
// Delete FAQ
$('.pc-faq-delete').on('click', function() {
var $button = $(this);
var faqId = $button.data('faq-id');
var $item = $button.closest('.pc-faq-item');
if (!confirm(pc_faq_manager.confirm_delete)) {
return;
}
if ($button.prop('disabled')) {
return;
}
$button.prop('disabled', true);
$.post(pc_faq_manager.ajax_url, {
action: 'pc_faq_delete',
nonce: pc_faq_manager.nonce,
faq_id: faqId
})
.done(function(response) {
if (response.success) {
$item.fadeOut(300, function() {
$(this).remove();
// Check if we need to show empty state
var remainingItems = $('#pc-faq-sortable .pc-faq-item').length;
if (remainingItems === 0) {
location.reload();
}
});
} else {
alert('Failed to delete FAQ: ' + (response.data.message || 'Unknown error'));
}
})
.fail(function() {
alert('Failed to delete FAQ. Please try again.');
})
.always(function() {
$button.prop('disabled', false);
});
});
// Add new FAQ form
$('#pc-faq-add-form').on('submit', function(e) {
e.preventDefault();
var $form = $(this);
var $button = $form.find('button[type="submit"]');
var title = $('#pc-faq-title').val().trim();
var content = $('#pc-faq-content').val();
if (!title || !content) {
showMessage('Please fill in both the question and answer.', 'error');
return;
}
if ($button.prop('disabled')) {
return;
}
$button.prop('disabled', true);
$.post(pc_faq_manager.ajax_url, {
action: 'pc_faq_add_new',
nonce: pc_faq_manager.nonce,
title: title,
content: content
})
.done(function(response) {
if (response.success) {
showMessage('FAQ created successfully! Redirecting...', 'success');
setTimeout(function() {
window.location.href = 'admin.php?page=pc-faq-manager';
}, 1500);
} else {
showMessage('Failed to create FAQ: ' + (response.data.message || 'Unknown error'), 'error');
}
})
.fail(function() {
showMessage('Failed to create FAQ. Please try again.', 'error');
})
.always(function() {
$button.prop('disabled', false);
});
});
function showMessage(message, type) {
var $message = $('#pc-faq-add-message');
$message.removeClass('notice-success notice-error')
.addClass('notice-' + type)
.html('<p>' + message + '</p>')
.show();
setTimeout(function() {
$message.fadeOut();
}, 5000);
}
// ESC key to close modal
$(document).on('keydown', function(e) {
if (e.keyCode === 27 && $modal.is(':visible')) {
closeEditModal();
}
});
});

View File

@@ -0,0 +1,74 @@
<?php
/**
* Add New FAQ admin page template
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
?>
<div class="wrap pc-faq-manager-admin">
<h1 class="wp-heading-inline">
<?php esc_html_e('Add New FAQ', PC_FAQ_MANAGER_SLUG); ?>
</h1>
<a href="<?php echo admin_url('admin.php?page=pc-faq-manager'); ?>" class="page-title-action">
<?php esc_html_e('View All FAQs', PC_FAQ_MANAGER_SLUG); ?>
</a>
<hr class="wp-header-end">
<div class="pc-faq-manager-add-form">
<div class="pc-faq-form-container">
<h2><?php esc_html_e('Create New FAQ', PC_FAQ_MANAGER_SLUG); ?></h2>
<form id="pc-faq-add-form">
<div class="form-field">
<label for="pc-faq-title"><?php esc_html_e('Question', PC_FAQ_MANAGER_SLUG); ?></label>
<input type="text" id="pc-faq-title" class="widefat" placeholder="<?php esc_attr_e('Enter your question here...', PC_FAQ_MANAGER_SLUG); ?>" required>
<p class="description"><?php esc_html_e('This will be displayed as the question on your FAQ page.', PC_FAQ_MANAGER_SLUG); ?></p>
</div>
<div class="form-field">
<label for="pc-faq-content"><?php esc_html_e('Answer', PC_FAQ_MANAGER_SLUG); ?></label>
<textarea id="pc-faq-content" name="content" class="widefat" rows="10" placeholder="<?php esc_attr_e('Enter your answer here...', PC_FAQ_MANAGER_SLUG); ?>" required></textarea>
<p class="description"><?php esc_html_e('Provide a detailed answer to the question above.', PC_FAQ_MANAGER_SLUG); ?></p>
</div>
<div class="form-field">
<button type="submit" class="button button-primary button-large">
<span class="dashicons dashicons-plus-alt"></span>
<?php esc_html_e('Create FAQ', PC_FAQ_MANAGER_SLUG); ?>
</button>
<a href="<?php echo admin_url('admin.php?page=pc-faq-manager'); ?>" class="button button-large">
<?php esc_html_e('Cancel', PC_FAQ_MANAGER_SLUG); ?>
</a>
</div>
</form>
</div>
<div class="pc-faq-manager-help">
<div class="help-card">
<h3><?php esc_html_e('Pro Tips', PC_FAQ_MANAGER_SLUG); ?></h3>
<ul>
<li><?php esc_html_e('Keep questions clear and concise', PC_FAQ_MANAGER_SLUG); ?></li>
<li><?php esc_html_e('Provide comprehensive answers', PC_FAQ_MANAGER_SLUG); ?></li>
<li><?php esc_html_e('Use formatting to improve readability', PC_FAQ_MANAGER_SLUG); ?></li>
<li><?php esc_html_e('Consider adding images or links if helpful', PC_FAQ_MANAGER_SLUG); ?></li>
</ul>
</div>
<div class="help-card">
<h3><?php esc_html_e('FAQ Page', PC_FAQ_MANAGER_SLUG); ?></h3>
<p><?php esc_html_e('Your FAQs will automatically appear on the FAQ page. You can find it at:', PC_FAQ_MANAGER_SLUG); ?></p>
<p><code><?php echo home_url('/faq/'); ?></code></p>
<p><a href="<?php echo home_url('/faq/'); ?>" target="_blank" class="button"><?php esc_html_e('View FAQ Page', PC_FAQ_MANAGER_SLUG); ?></a></p>
</div>
</div>
</div>
<!-- Success/Error Messages -->
<div id="pc-faq-add-message" class="notice is-dismissible" style="display: none;"></div>
</div>

View File

@@ -0,0 +1,140 @@
<?php
/**
* Main FAQ Manager admin page template
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
?>
<div class="wrap pc-faq-manager-admin">
<h1 class="wp-heading-inline">
<?php esc_html_e('FAQ Manager', PC_FAQ_MANAGER_SLUG); ?>
</h1>
<a href="<?php echo admin_url('admin.php?page=pc-faq-manager-add'); ?>" class="page-title-action">
<?php esc_html_e('Add New FAQ', PC_FAQ_MANAGER_SLUG); ?>
</a>
<hr class="wp-header-end">
<?php if ($faq_count > 0): ?>
<div class="pc-faq-manager-header">
<div class="pc-faq-manager-stats">
<div class="stat-card">
<div class="stat-number"><?php echo esc_html($faq_count); ?></div>
<div class="stat-label"><?php esc_html_e('Total FAQs', PC_FAQ_MANAGER_SLUG); ?></div>
</div>
</div>
<div class="pc-faq-manager-instructions">
<h3><?php esc_html_e('How to use', PC_FAQ_MANAGER_SLUG); ?></h3>
<ul>
<li><?php esc_html_e('Drag and drop FAQs to reorder them', PC_FAQ_MANAGER_SLUG); ?></li>
<li><?php esc_html_e('Click on any FAQ to edit it inline', PC_FAQ_MANAGER_SLUG); ?></li>
<li><?php esc_html_e('Use the delete button to remove an FAQ', PC_FAQ_MANAGER_SLUG); ?></li>
<li><?php esc_html_e('The FAQ page will automatically update on your website', PC_FAQ_MANAGER_SLUG); ?></li>
</ul>
</div>
</div>
<div class="pc-faq-manager-list">
<div class="pc-faq-manager-list-header">
<div class="sort-handle"></div>
<div class="faq-question"><?php esc_html_e('Question', PC_FAQ_MANAGER_SLUG); ?></div>
<div class="faq-answer"><?php esc_html_e('Answer', PC_FAQ_MANAGER_SLUG); ?></div>
<div class="faq-actions"><?php esc_html_e('Actions', PC_FAQ_MANAGER_SLUG); ?></div>
</div>
<div class="pc-faq-manager-items" id="pc-faq-sortable">
<?php foreach ($faqs as $faq): ?>
<div class="pc-faq-item" data-faq-id="<?php echo esc_attr($faq->ID); ?>">
<div class="sort-handle">
<span class="dashicons dashicons-menu"></span>
</div>
<div class="faq-question">
<div class="faq-content">
<h4><?php echo esc_html($faq->post_title); ?></h4>
<div class="faq-excerpt">
<?php echo wp_trim_words($faq->post_content, 15, '...'); ?>
</div>
</div>
</div>
<div class="faq-answer">
<div class="faq-content">
<?php echo wp_trim_words($faq->post_content, 20, '...'); ?>
</div>
</div>
<div class="faq-actions">
<button type="button" class="button button-secondary pc-faq-edit" data-faq-id="<?php echo esc_attr($faq->ID); ?>">
<span class="dashicons dashicons-edit"></span>
<?php esc_html_e('Edit', PC_FAQ_MANAGER_SLUG); ?>
</button>
<button type="button" class="button pc-faq-delete" data-faq-id="<?php echo esc_attr($faq->ID); ?>">
<span class="dashicons dashicons-trash"></span>
<?php esc_html_e('Delete', PC_FAQ_MANAGER_SLUG); ?>
</button>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<div class="pc-faq-manager-footer">
<button type="button" class="button button-primary" id="pc-faq-save-order">
<span class="dashicons dashicons-yes"></span>
<?php esc_html_e('Save Order', PC_FAQ_MANAGER_SLUG); ?>
</button>
<span class="pc-faq-save-status"></span>
</div>
<?php else: ?>
<div class="pc-faq-manager-empty">
<div class="pc-faq-manager-empty-icon">
<span class="dashicons dashicons-editor-help"></span>
</div>
<h2><?php esc_html_e('No FAQs yet', PC_FAQ_MANAGER_SLUG); ?></h2>
<p><?php esc_html_e('Create your first FAQ to get started with the FAQ Manager.', PC_FAQ_MANAGER_SLUG); ?></p>
<p>
<a href="<?php echo admin_url('admin.php?page=pc-faq-manager-add'); ?>" class="button button-primary">
<?php esc_html_e('Create Your First FAQ', PC_FAQ_MANAGER_SLUG); ?>
</a>
</p>
</div>
<?php endif; ?>
</div>
<!-- Edit Modal -->
<div id="pc-faq-edit-modal" class="pc-faq-modal" style="display: none;">
<div class="pc-faq-modal-content">
<div class="pc-faq-modal-header">
<h2><?php esc_html_e('Edit FAQ', PC_FAQ_MANAGER_SLUG); ?></h2>
<button type="button" class="pc-faq-modal-close">&times;</button>
</div>
<div class="pc-faq-modal-body">
<div class="form-field">
<label for="pc-faq-edit-title"><?php esc_html_e('Question', PC_FAQ_MANAGER_SLUG); ?></label>
<input type="text" id="pc-faq-edit-title" class="widefat" placeholder="<?php esc_attr_e('Enter your question here...', PC_FAQ_MANAGER_SLUG); ?>">
</div>
<div class="form-field">
<label for="pc-faq-edit-content"><?php esc_html_e('Answer', PC_FAQ_MANAGER_SLUG); ?></label>
<?php
wp_editor('', 'pc-faq-edit-content', array(
'textarea_name' => 'content',
'textarea_rows' => 10,
'media_buttons' => false,
'teeny' => true,
));
?>
</div>
</div>
<div class="pc-faq-modal-footer">
<button type="button" class="button button-secondary pc-faq-modal-cancel"><?php esc_html_e('Cancel', PC_FAQ_MANAGER_SLUG); ?></button>
<button type="button" class="button button-primary pc-faq-modal-save"><?php esc_html_e('Update FAQ', PC_FAQ_MANAGER_SLUG); ?></button>
</div>
</div>
</div>
<div class="pc-faq-modal-backdrop" style="display: none;"></div>

View File

@@ -0,0 +1,106 @@
<?php
/**
* FAQ Manager Helper Class
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
class PC_FAQ_Manager_Helper {
/**
* Get all FAQs ordered by order meta
*/
public static function get_faqs($posts_per_page = -1) {
$args = array(
'post_type' => 'pc_faq',
'post_status' => 'publish',
'posts_per_page' => $posts_per_page,
'orderby' => 'meta_value_num',
'meta_key' => '_pc_faq_order',
'order' => 'ASC',
);
return get_posts($args);
}
/**
* Get FAQ count
*/
public static function get_faq_count() {
$args = array(
'post_type' => 'pc_faq',
'post_status' => 'publish',
'posts_per_page' => 1,
);
$query = new WP_Query($args);
return $query->found_posts;
}
/**
* Reorder FAQs
*/
public static function reorder_faqs($faq_ids) {
if (!is_array($faq_ids) || empty($faq_ids)) {
return false;
}
foreach ($faq_ids as $order => $faq_id) {
$faq_id = intval($faq_id);
$order = intval($order);
if ($faq_id > 0) {
update_post_meta($faq_id, '_pc_faq_order', $order);
}
}
return true;
}
/**
* Get max order value
*/
public static function get_max_order() {
global $wpdb;
$max_order = $wpdb->get_var($wpdb->prepare(
"SELECT MAX(CAST(meta_value AS SIGNED))
FROM {$wpdb->postmeta}
WHERE meta_key = %s",
'_pc_faq_order'
));
return $max_order ? intval($max_order) : 0;
}
/**
* Sanitize and validate FAQ data
*/
public static function sanitize_faq_data($data) {
$sanitized = array();
if (isset($data['title'])) {
$sanitized['title'] = sanitize_text_field($data['title']);
}
if (isset($data['content'])) {
$sanitized['content'] = wp_kses_post($data['content']);
}
if (isset($data['order'])) {
$sanitized['order'] = intval($data['order']);
}
return $sanitized;
}
/**
* Check user capabilities
*/
public static function can_manage_faqs() {
return current_user_can('manage_options') || current_user_can('edit_posts');
}
}

View File

@@ -0,0 +1,137 @@
<?php
/**
* FAQ Post Type Handler
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
class PC_FAQ_Manager_Post_Type {
/**
* Constructor
*/
public function __construct() {
add_action('init', array($this, 'register_post_type'));
add_action('add_meta_boxes', array($this, 'add_meta_boxes'));
add_action('save_post_pc_faq', array($this, 'save_faq_meta'));
add_action('manage_pc_faq_posts_custom_column', array($this, 'custom_columns'), 10, 2);
add_filter('manage_pc_faq_posts_columns', array($this, 'set_custom_columns'));
}
/**
* Register FAQ post type
*/
public function register_post_type() {
$labels = array(
'name' => __('FAQs', PC_FAQ_MANAGER_SLUG),
'singular_name' => __('FAQ', PC_FAQ_MANAGER_SLUG),
'menu_name' => __('FAQ Manager', PC_FAQ_MANAGER_SLUG),
'add_new' => __('Add New FAQ', PC_FAQ_MANAGER_SLUG),
'add_new_item' => __('Add New FAQ', PC_FAQ_MANAGER_SLUG),
'edit_item' => __('Edit FAQ', PC_FAQ_MANAGER_SLUG),
'new_item' => __('New FAQ', PC_FAQ_MANAGER_SLUG),
'view_item' => __('View FAQ', PC_FAQ_MANAGER_SLUG),
'search_items' => __('Search FAQs', PC_FAQ_MANAGER_SLUG),
'not_found' => __('No FAQs found', PC_FAQ_MANAGER_SLUG),
'not_found_in_trash' => __('No FAQs found in Trash', PC_FAQ_MANAGER_SLUG),
'all_items' => __('All FAQs', PC_FAQ_MANAGER_SLUG),
);
$args = array(
'labels' => $labels,
'public' => false,
'has_archive' => false,
'publicly_queryable' => false,
'query_var' => false,
'rewrite' => false,
'capability_type' => 'post',
'hierarchical' => false,
'menu_position' => 25,
'menu_icon' => 'dashicons-editor-help',
'supports' => array('title', 'editor'),
'show_in_rest' => false,
'show_ui' => true,
'show_in_menu' => false,
'exclude_from_search' => true,
);
register_post_type('pc_faq', $args);
}
/**
* Add meta boxes
*/
public function add_meta_boxes() {
add_meta_box(
'pc_faq_order_meta',
__('FAQ Order', PC_FAQ_MANAGER_SLUG),
array($this, 'order_meta_box'),
'pc_faq',
'side',
'default'
);
}
/**
* Order meta box callback
*/
public function order_meta_box($post) {
wp_nonce_field('pc_faq_save_meta', 'pc_faq_nonce');
$order = get_post_meta($post->ID, '_pc_faq_order', true) ?: 0;
echo '<label for="pc_faq_order">' . __('Display Order:', PC_FAQ_MANAGER_SLUG) . '</label>';
echo '<input type="number" id="pc_faq_order" name="pc_faq_order" value="' . esc_attr($order) . '" min="0" style="width: 100%; margin-top: 5px;" />';
echo '<p class="description">' . __('Lower numbers appear first. Leave at 0 for automatic ordering.', PC_FAQ_MANAGER_SLUG) . '</p>';
}
/**
* Save FAQ meta
*/
public function save_faq_meta($post_id) {
if (!isset($_POST['pc_faq_nonce']) || !wp_verify_nonce($_POST['pc_faq_nonce'], 'pc_faq_save_meta')) {
return;
}
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
if (!current_user_can('edit_post', $post_id)) {
return;
}
if (isset($_POST['pc_faq_order'])) {
$order = sanitize_text_field($_POST['pc_faq_order']);
update_post_meta($post_id, '_pc_faq_order', $order);
}
}
/**
* Set custom columns
*/
public function set_custom_columns($columns) {
$new_columns = array();
$new_columns['cb'] = $columns['cb'];
$new_columns['title'] = $columns['title'];
$new_columns['order'] = __('Order', PC_FAQ_MANAGER_SLUG);
$new_columns['date'] = $columns['date'];
return $new_columns;
}
/**
* Display custom columns
*/
public function custom_columns($column, $post_id) {
switch ($column) {
case 'order':
$order = get_post_meta($post_id, '_pc_faq_order', true) ?: 0;
echo esc_html($order);
break;
}
}
}

View File

@@ -0,0 +1,10 @@
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"external_directory": {
"*": "deny",
"*/faq-fzzb/*": "allow",
"apps/c7f9e5c6-e7c2-4258-a583-ccffcf9791c8/faq-fzzb/*": "allow"
}
}
}

View File

@@ -0,0 +1,147 @@
<?php
/**
* Plugin Name: FAQ Manager
* Plugin URI: https://plugincompass.com/plugins/pc-faq-manager-abc123
* Description: Easily create and manage FAQ pages with drag-and-drop reordering functionality.
* Version: 1.0.0
* Author: Plugin Compass
* Author URI: https://plugincompass.com
* License: GPLv2 or later
* Text Domain: pc-faq-manager-abc123
* Domain Path: /languages
* Update URI: false
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Plugin constants
define('PC_FAQ_MANAGER_VERSION', '1.0.0');
define('PC_FAQ_MANAGER_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('PC_FAQ_MANAGER_PLUGIN_URL', plugin_dir_url(__FILE__));
define('PC_FAQ_MANAGER_SLUG', 'pc-faq-manager-abc123');
// Prevent WordPress.org update checks
add_filter('site_transient_update_plugins', function($value) {
$plugin_file = plugin_basename(__FILE__);
if (isset($value->response[$plugin_file])) {
unset($value->response[$plugin_file]);
}
return $value;
});
/**
* Main plugin class
*/
class PC_FAQ_Manager {
private static $instance = null;
/**
* Get singleton instance
*/
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
private function __construct() {
add_action('init', array($this, 'init'));
register_activation_hook(__FILE__, array($this, 'activate'));
register_deactivation_hook(__FILE__, array($this, 'deactivate'));
}
/**
* Initialize plugin
*/
public function init() {
// Load text domain
load_plugin_textdomain(PC_FAQ_MANAGER_SLUG, false, dirname(plugin_basename(__FILE__)) . '/languages');
// Include required files
$this->include_files();
// Initialize admin functionality
if (is_admin()) {
new PC_FAQ_Manager_Admin();
}
// Initialize frontend functionality
new PC_FAQ_Manager_Public();
}
/**
* Include required files
*/
private function include_files() {
require_once PC_FAQ_MANAGER_PLUGIN_DIR . 'includes/class-pc-faq-manager-post-type.php';
require_once PC_FAQ_MANAGER_PLUGIN_DIR . 'includes/class-pc-faq-manager-helper.php';
if (is_admin()) {
require_once PC_FAQ_MANAGER_PLUGIN_DIR . 'admin/class-pc-faq-manager-admin.php';
}
require_once PC_FAQ_MANAGER_PLUGIN_DIR . 'public/class-pc-faq-manager-public.php';
}
/**
* Plugin activation
*/
public function activate() {
// Flush rewrite rules
flush_rewrite_rules();
// Create FAQ page if it doesn't exist
$this->create_faq_page();
// Set default options
add_option('pc_faq_manager_version', PC_FAQ_MANAGER_VERSION);
}
/**
* Plugin deactivation
*/
public function deactivate() {
// Flush rewrite rules
flush_rewrite_rules();
}
/**
* Create FAQ page automatically
*/
private function create_faq_page() {
$page_title = __('FAQ', PC_FAQ_MANAGER_SLUG);
$page_content = '[pc_faq_page]';
$page_template = '';
// Check if page already exists
$existing_page = get_page_by_title($page_title);
if (!$existing_page) {
$page_data = array(
'post_title' => $page_title,
'post_content' => $page_content,
'post_status' => 'publish',
'post_author' => get_current_user_id(),
'post_type' => 'page',
'post_name' => 'faq'
);
$page_id = wp_insert_post($page_data);
if ($page_id && !is_wp_error($page_id)) {
update_post_meta($page_id, '_wp_page_template', $page_template);
}
}
}
}
// Initialize the plugin
PC_FAQ_Manager::get_instance();

View File

@@ -0,0 +1,79 @@
<?php
/**
* FAQ Manager Public Interface
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
class PC_FAQ_Manager_Public {
/**
* Constructor
*/
public function __construct() {
add_action('wp_enqueue_scripts', array($this, 'enqueue_public_scripts'));
add_shortcode('pc_faq_page', array($this, 'render_faq_page'));
}
/**
* Enqueue public scripts and styles
*/
public function enqueue_public_scripts() {
wp_enqueue_style('pc-faq-manager-public', PC_FAQ_MANAGER_PLUGIN_URL . 'public/css/public-style.css', array(), PC_FAQ_MANAGER_VERSION);
wp_enqueue_script('pc-faq-manager-public', PC_FAQ_MANAGER_PLUGIN_URL . 'public/js/public-script.js', array('jquery'), PC_FAQ_MANAGER_VERSION, true);
}
/**
* Render FAQ page shortcode
*/
public function render_faq_page($atts) {
$atts = shortcode_atts(array(
'title' => __('Frequently Asked Questions', PC_FAQ_MANAGER_SLUG),
'show_count' => true,
), $atts);
$faqs = PC_FAQ_Manager_Helper::get_faqs();
if (empty($faqs)) {
return '<div class="pc-faq-empty">' . esc_html__('No FAQs available at this time.', PC_FAQ_MANAGER_SLUG) . '</div>';
}
ob_start();
if ($atts['show_count']) {
echo '<div class="pc-faq-header">';
echo '<h1 class="pc-faq-title">' . esc_html($atts['title']) . '</h1>';
echo '<div class="pc-faq-count">' . sprintf(esc_html__('%d FAQs available', PC_FAQ_MANAGER_SLUG), count($faqs)) . '</div>';
echo '</div>';
}
echo '<div class="pc-faq-container">';
echo '<div class="pc-faq-items">';
foreach ($faqs as $index => $faq) {
echo '<div class="pc-faq-item" data-faq-id="' . esc_attr($faq->ID) . '">';
echo '<div class="pc-faq-question">';
echo '<button class="pc-faq-toggle" aria-expanded="false" aria-controls="pc-faq-answer-' . esc_attr($faq->ID) . '">';
echo '<span class="pc-faq-question-text">' . esc_html($faq->post_title) . '</span>';
echo '<span class="pc-faq-toggle-icon"><span class="pc-faq-plus"></span><span class="pc-faq-minus"></span></span>';
echo '</button>';
echo '</div>';
echo '<div class="pc-faq-answer" id="pc-faq-answer-' . esc_attr($faq->ID) . '" style="display: none;">';
echo '<div class="pc-faq-answer-content">';
echo wp_kses_post($faq->post_content);
echo '</div>';
echo '</div>';
echo '</div>';
}
echo '</div>';
echo '</div>';
return ob_get_clean();
}
}

View File

@@ -0,0 +1,442 @@
/* FAQ Manager Public Styles */
/* Custom CSS Variables */
:root {
--pc-faq-primary-color: #0073aa;
--pc-faq-secondary-color: #f1f1f1;
--pc-faq-text-color: #333;
--pc-faq-text-light: #666;
--pc-faq-border-color: #ddd;
--pc-faq-hover-color: #005a87;
--pc-faq-success-color: #46b450;
--pc-faq-background: #fff;
--pc-faq-spacing: 20px;
--pc-faq-border-radius: 6px;
--pc-faq-transition: all 0.3s ease;
--pc-faq-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
--pc-faq-shadow-hover: 0 4px 12px rgba(0, 0, 0, 0.15);
}
/* Dark Mode Support */
@media (prefers-color-scheme: dark) {
:root {
--pc-faq-primary-color: #4fb3d9;
--pc-faq-secondary-color: #2c2c2c;
--pc-faq-text-color: #f0f0f0;
--pc-faq-text-light: #ccc;
--pc-faq-border-color: #555;
--pc-faq-hover-color: #6cc0e3;
--pc-faq-background: #1a1a1a;
--pc-faq-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
--pc-faq-shadow-hover: 0 4px 12px rgba(0, 0, 0, 0.5);
}
}
/* FAQ Container */
.pc-faq-container {
max-width: 900px;
margin: 0 auto;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;
line-height: 1.6;
color: var(--pc-faq-text-color);
}
/* FAQ Header */
.pc-faq-header {
text-align: center;
margin-bottom: calc(var(--pc-faq-spacing) * 2);
padding: calc(var(--pc-faq-spacing) * 1.5);
background: linear-gradient(135deg, var(--pc-faq-primary-color) 0%, var(--pc-faq-hover-color) 100%);
color: white;
border-radius: var(--pc-faq-border-radius);
box-shadow: var(--pc-faq-shadow);
}
.pc-faq-title {
font-size: 2.5rem;
font-weight: 700;
margin: 0 0 10px 0;
line-height: 1.2;
}
.pc-faq-count {
font-size: 1.1rem;
opacity: 0.9;
font-weight: 300;
}
/* FAQ Items */
.pc-faq-items {
display: flex;
flex-direction: column;
gap: 15px;
}
.pc-faq-item {
background: var(--pc-faq-background);
border: 1px solid var(--pc-faq-border-color);
border-radius: var(--pc-faq-border-radius);
overflow: hidden;
transition: var(--pc-faq-transition);
box-shadow: var(--pc-faq-shadow);
}
.pc-faq-item:hover {
transform: translateY(-2px);
box-shadow: var(--pc-faq-shadow-hover);
border-color: var(--pc-faq-primary-color);
}
/* FAQ Question */
.pc-faq-question {
margin: 0;
}
.pc-faq-toggle {
width: 100%;
padding: 20px 25px;
background: none;
border: none;
text-align: left;
cursor: pointer;
font-size: 1.1rem;
font-weight: 600;
color: var(--pc-faq-text-color);
display: flex;
justify-content: space-between;
align-items: center;
transition: var(--pc-faq-transition);
position: relative;
}
.pc-faq-toggle:hover {
background: var(--pc-faq-secondary-color);
color: var(--pc-faq-primary-color);
}
.pc-faq-toggle:focus {
outline: 2px solid var(--pc-faq-primary-color);
outline-offset: -2px;
}
.pc-faq-question-text {
flex: 1;
padding-right: 15px;
line-height: 1.4;
}
/* Toggle Icon */
.pc-faq-toggle-icon {
position: relative;
width: 24px;
height: 24px;
flex-shrink: 0;
}
.pc-faq-plus,
.pc-faq-minus {
position: absolute;
background: var(--pc-faq-primary-color);
transition: var(--pc-faq-transition);
}
.pc-faq-plus {
width: 100%;
height: 2px;
top: 50%;
left: 0;
transform: translateY(-50%);
}
.pc-faq-minus {
width: 2px;
height: 100%;
top: 0;
left: 50%;
transform: translateX(-50%);
}
.pc-faq-item.is-open .pc-faq-minus {
opacity: 0;
transform: translateX(-50%) rotate(90deg);
}
.pc-faq-item.is-open .pc-faq-plus {
transform: translateY(-50%) rotate(90deg);
}
/* FAQ Answer */
.pc-faq-answer {
max-height: 0;
overflow: hidden;
transition: max-height 0.4s ease, padding 0.4s ease;
background: var(--pc-faq-secondary-color);
border-top: 1px solid var(--pc-faq-border-color);
}
.pc-faq-item.is-open .pc-faq-answer {
max-height: 1000px;
padding: 25px;
}
.pc-faq-answer-content {
color: var(--pc-faq-text-light);
font-size: 1rem;
line-height: 1.7;
}
.pc-faq-answer-content p:last-child {
margin-bottom: 0;
}
.pc-faq-answer-content h1,
.pc-faq-answer-content h2,
.pc-faq-answer-content h3,
.pc-faq-answer-content h4,
.pc-faq-answer-content h5,
.pc-faq-answer-content h6 {
color: var(--pc-faq-text-color);
margin-top: 20px;
margin-bottom: 15px;
}
.pc-faq-answer-content h1 { font-size: 1.8em; }
.pc-faq-answer-content h2 { font-size: 1.6em; }
.pc-faq-answer-content h3 { font-size: 1.4em; }
.pc-faq-answer-content h4 { font-size: 1.2em; }
.pc-faq-answer-content h5 { font-size: 1.1em; }
.pc-faq-answer-content h6 { font-size: 1em; }
.pc-faq-answer-content ul,
.pc-faq-answer-content ol {
margin: 15px 0;
padding-left: 25px;
}
.pc-faq-answer-content li {
margin-bottom: 8px;
}
.pc-faq-answer-content a {
color: var(--pc-faq-primary-color);
text-decoration: none;
border-bottom: 1px solid transparent;
transition: border-bottom-color 0.2s ease;
}
.pc-faq-answer-content a:hover {
border-bottom-color: var(--pc-faq-primary-color);
}
.pc-faq-answer-content blockquote {
margin: 20px 0;
padding: 15px 20px;
background: var(--pc-faq-background);
border-left: 4px solid var(--pc-faq-primary-color);
font-style: italic;
border-radius: 0 var(--pc-faq-border-radius) var(--pc-faq-border-radius) 0;
}
.pc-faq-answer-content code {
background: var(--pc-faq-background);
padding: 3px 6px;
border-radius: 3px;
font-family: 'Courier New', Courier, monospace;
font-size: 0.9em;
border: 1px solid var(--pc-faq-border-color);
}
.pc-faq-answer-content pre {
background: var(--pc-faq-background);
padding: 15px;
border-radius: var(--pc-faq-border-radius);
overflow-x: auto;
margin: 20px 0;
border: 1px solid var(--pc-faq-border-color);
}
.pc-faq-answer-content pre code {
background: none;
padding: 0;
border: none;
font-size: 0.9em;
}
.pc-faq-answer-content img {
max-width: 100%;
height: auto;
border-radius: var(--pc-faq-border-radius);
margin: 15px 0;
box-shadow: var(--pc-faq-shadow);
}
.pc-faq-answer-content table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
background: var(--pc-faq-background);
border-radius: var(--pc-faq-border-radius);
overflow: hidden;
}
.pc-faq-answer-content th,
.pc-faq-answer-content td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid var(--pc-faq-border-color);
}
.pc-faq-answer-content th {
background: var(--pc-faq-primary-color);
color: white;
font-weight: 600;
}
.pc-faq-answer-content tr:last-child td {
border-bottom: none;
}
/* Empty State */
.pc-faq-empty {
text-align: center;
padding: 60px 20px;
color: var(--pc-faq-text-light);
font-size: 1.1rem;
font-style: italic;
}
/* Accessibility */
.pc-faq-toggle[aria-expanded="true"] {
color: var(--pc-faq-primary-color);
font-weight: 700;
}
/* Focus visible for keyboard navigation */
.pc-faq-toggle:focus-visible {
outline: 3px solid var(--pc-faq-primary-color);
outline-offset: 2px;
}
/* Print Styles */
@media print {
.pc-faq-container {
max-width: none;
}
.pc-faq-item {
break-inside: avoid;
box-shadow: none;
border: 1px solid #333;
}
.pc-faq-answer {
display: block !important;
max-height: none !important;
padding: 20px !important;
}
.pc-faq-toggle-icon {
display: none;
}
}
/* Responsive Design */
@media screen and (max-width: 768px) {
.pc-faq-container {
margin: 0 15px;
}
.pc-faq-title {
font-size: 2rem;
}
.pc-faq-toggle {
padding: 15px 20px;
font-size: 1rem;
}
.pc-faq-item.is-open .pc-faq-answer {
padding: 20px;
}
.pc-faq-answer-content {
font-size: 0.95rem;
}
}
@media screen and (max-width: 480px) {
.pc-faq-header {
padding: 20px 15px;
margin-bottom: 30px;
}
.pc-faq-title {
font-size: 1.75rem;
}
.pc-faq-toggle {
padding: 12px 15px;
font-size: 0.95rem;
}
.pc-faq-question-text {
padding-right: 10px;
}
.pc-faq-toggle-icon {
width: 20px;
height: 20px;
}
.pc-faq-item.is-open .pc-faq-answer {
padding: 15px;
}
.pc-faq-answer-content {
font-size: 0.9rem;
}
}
/* Animation Enhancements */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.pc-faq-item {
animation: fadeIn 0.4s ease-out;
}
.pc-faq-item:nth-child(1) { animation-delay: 0.1s; }
.pc-faq-item:nth-child(2) { animation-delay: 0.2s; }
.pc-faq-item:nth-child(3) { animation-delay: 0.3s; }
.pc-faq-item:nth-child(4) { animation-delay: 0.4s; }
.pc-faq-item:nth-child(5) { animation-delay: 0.5s; }
/* RTL Support */
[dir="rtl"] .pc-faq-toggle {
text-align: right;
}
[dir="rtl"] .pc-faq-question-text {
padding-right: 0;
padding-left: 15px;
}
[dir="rtl"] .pc-faq-answer-content ul,
[dir="rtl"] .pc-faq-answer-content ol {
padding-left: 0;
padding-right: 25px;
}
[dir="rtl"] .pc-faq-answer-content blockquote {
border-left: none;
border-right: 4px solid var(--pc-faq-primary-color);
border-radius: var(--pc-faq-border-radius) 0 0 var(--pc-faq-border-radius);
}

View File

@@ -0,0 +1,250 @@
jQuery(document).ready(function($) {
// FAQ Toggle functionality
$('.pc-faq-toggle').on('click', function(e) {
e.preventDefault();
var $button = $(this);
var $faqItem = $button.closest('.pc-faq-item');
var $answer = $faqItem.find('.pc-faq-answer');
var isExpanded = $button.attr('aria-expanded') === 'true';
// Close all other FAQs (optional accordion behavior)
$('.pc-faq-item').each(function() {
if ($(this)[0] !== $faqItem[0]) {
$(this).removeClass('is-open');
$(this).find('.pc-faq-answer').slideUp(300);
$(this).find('.pc-faq-toggle').attr('aria-expanded', 'false');
}
});
// Toggle current FAQ
if (isExpanded) {
$faqItem.removeClass('is-open');
$answer.slideUp(300);
$button.attr('aria-expanded', 'false');
} else {
$faqItem.addClass('is-open');
$answer.slideDown(300);
$button.attr('aria-expanded', 'true');
// Smooth scroll to the FAQ
setTimeout(function() {
$('html, body').animate({
scrollTop: $faqItem.offset().top - 20
}, 300);
}, 100);
}
});
// Keyboard navigation
$('.pc-faq-toggle').on('keydown', function(e) {
var $button = $(this);
var $faqItem = $button.closest('.pc-faq-item');
var $allItems = $('.pc-faq-item');
var currentIndex = $allItems.index($faqItem);
switch (e.keyCode) {
case 38: // Up arrow
e.preventDefault();
if (currentIndex > 0) {
$allItems.eq(currentIndex - 1).find('.pc-faq-toggle').focus();
}
break;
case 40: // Down arrow
e.preventDefault();
if (currentIndex < $allItems.length - 1) {
$allItems.eq(currentIndex + 1).find('.pc-faq-toggle').focus();
}
break;
case 36: // Home
e.preventDefault();
$allItems.first().find('.pc-faq-toggle').focus();
break;
case 35: // End
e.preventDefault();
$allItems.last().find('.pc-faq-toggle').focus();
break;
}
});
// Search functionality (if search box is added)
function initSearch() {
var $searchBox = $('#pc-faq-search');
if ($searchBox.length === 0) return;
$searchBox.on('input', function() {
var searchTerm = $(this).val().toLowerCase();
var $faqs = $('.pc-faq-item');
var visibleCount = 0;
$faqs.each(function() {
var $faqItem = $(this);
var question = $faqItem.find('.pc-faq-question-text').text().toLowerCase();
var answer = $faqItem.find('.pc-faq-answer-content').text().toLowerCase();
if (question.includes(searchTerm) || answer.includes(searchTerm)) {
$faqItem.show();
visibleCount++;
} else {
$faqItem.hide();
}
});
// Show no results message
var $noResults = $('.pc-faq-no-results');
if (visibleCount === 0 && searchTerm !== '') {
if ($noResults.length === 0) {
$noResults = $('<div class="pc-faq-no-results" style="text-align: center; padding: 40px 20px; color: #666; font-style: italic;"></div>');
$('.pc-faq-items').append($noResults);
}
$noResults.text('No FAQs found matching "' + searchTerm + '"');
} else {
$noResults.remove();
}
// Update count
var $count = $('.pc-faq-count');
if ($count.length > 0) {
if (searchTerm === '') {
$count.text($faqs.length + ' FAQs available');
} else {
$count.text(visibleCount + ' of ' + $faqs.length + ' FAQs matching "' + searchTerm + '"');
}
}
});
}
// Initialize search if present
initSearch();
// Expand/Collapse All functionality
function initExpandCollapseAll() {
var $expandAll = $('#pc-faq-expand-all');
var $collapseAll = $('#pc-faq-collapse-all');
if ($expandAll.length === 0 && $collapseAll.length === 0) return;
$expandAll.on('click', function() {
$('.pc-faq-item').addClass('is-open');
$('.pc-faq-answer').slideDown(300);
$('.pc-faq-toggle').attr('aria-expanded', 'true');
});
$collapseAll.on('click', function() {
$('.pc-faq-item').removeClass('is-open');
$('.pc-faq-answer').slideUp(300);
$('.pc-faq-toggle').attr('aria-expanded', 'false');
});
}
initExpandCollapseAll();
// Print functionality
function initPrintButton() {
var $printButton = $('#pc-faq-print');
if ($printButton.length === 0) return;
$printButton.on('click', function(e) {
e.preventDefault();
window.print();
});
}
initPrintButton();
// URL hash functionality
function initHashNavigation() {
if (window.location.hash) {
var hash = window.location.hash.substring(1);
var $targetFaq = $('#pc-faq-' + hash);
if ($targetFaq.length > 0) {
// Close all FAQs first
$('.pc-faq-item').removeClass('is-open');
$('.pc-faq-answer').slideUp(300);
$('.pc-faq-toggle').attr('aria-expanded', 'false');
// Open the target FAQ
setTimeout(function() {
$targetFaq.find('.pc-faq-toggle').trigger('click');
}, 500);
}
}
}
initHashNavigation();
// Update hash when FAQ is opened
$('.pc-faq-toggle').on('click', function() {
var $faqItem = $(this).closest('.pc-faq-item');
var faqId = $faqItem.attr('id');
if (faqId && $faqItem.hasClass('is-open')) {
history.pushState(null, null, '#' + faqId.replace('pc-faq-', ''));
} else {
if (window.location.hash) {
history.pushState(null, null, window.location.pathname);
}
}
});
// Analytics tracking (if available)
function trackFAQInteraction(action, faqId) {
if (typeof gtag !== 'undefined') {
gtag('event', 'faq_' + action, {
'faq_id': faqId
});
} else if (typeof ga !== 'undefined') {
ga('send', 'event', 'FAQ', action, faqId);
}
}
// Track FAQ opens
$('.pc-faq-toggle').on('click', function() {
var $faqItem = $(this).closest('.pc-faq-item');
var faqId = $faqItem.data('faq-id') || $faqItem.attr('id');
var question = $faqItem.find('.pc-faq-question-text').text();
trackFAQInteraction('open', faqId);
trackFAQInteraction('open_question', question);
});
// Performance optimization: Lazy load images in answers
function lazyLoadImages() {
if ('IntersectionObserver' in window) {
var imageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
var img = entry.target;
if (img.dataset.src) {
img.src = img.dataset.src;
img.removeAttribute('data-src');
observer.unobserve(img);
}
}
});
});
$('.pc-faq-answer-content img[data-src]').each(function() {
imageObserver.observe(this);
});
}
}
lazyLoadImages();
// Smooth scroll behavior for anchor links within answers
$('.pc-faq-answer-content a[href^="#"]').on('click', function(e) {
var target = $(this.getAttribute('href'));
if (target.length) {
e.preventDefault();
$('html, body').animate({
scrollTop: target.offset().top - 20
}, 300);
}
});
});

View File

@@ -0,0 +1,69 @@
<?php
/**
* FAQ Manager Uninstall Script
*
* This script runs when the plugin is uninstalled and cleans up all plugin data.
*/
// Prevent direct access
if (!defined('WP_UNINSTALL_PLUGIN')) {
exit;
}
// Plugin constants
define('PC_FAQ_MANAGER_SLUG', 'pc-faq-manager-abc123');
// Delete all FAQ posts
function pc_faq_manager_delete_faqs() {
$args = array(
'post_type' => 'pc_faq',
'post_status' => 'any',
'posts_per_page' => -1,
'fields' => 'ids',
);
$faq_ids = get_posts($args);
if (!empty($faq_ids)) {
foreach ($faq_ids as $faq_id) {
// Delete post permanently
wp_delete_post($faq_id, true);
}
}
}
// Delete FAQ page created by plugin
function pc_faq_manager_delete_faq_page() {
$page = get_page_by_title('FAQ');
if ($page) {
// Check if page contains our shortcode before deleting
$content = $page->post_content;
if (strpos($content, '[pc_faq_page]') !== false) {
wp_delete_post($page->ID, true);
}
}
}
// Delete plugin options
function pc_faq_manager_delete_options() {
delete_option('pc_faq_manager_version');
}
// Clean up post meta
function pc_faq_manager_cleanup_post_meta() {
global $wpdb;
// Delete FAQ order meta
$wpdb->delete(
$wpdb->postmeta,
array('meta_key' => '_pc_faq_order'),
array('%s')
);
}
// Run cleanup functions
pc_faq_manager_delete_faqs();
pc_faq_manager_delete_faq_page();
pc_faq_manager_delete_options();
pc_faq_manager_cleanup_post_meta();