Restore to commit 74e578279624c6045ca440a3459ebfa1f8d54191
This commit is contained in:
255
chat/templates/Community Suggestions/admin/class-admin.php
Normal file
255
chat/templates/Community Suggestions/admin/class-admin.php
Normal file
@@ -0,0 +1,255 @@
|
||||
<?php
|
||||
class PC_Community_Suggestions_Admin {
|
||||
|
||||
public function __construct() {
|
||||
add_action('admin_menu', array($this, 'add_admin_menu'));
|
||||
add_action('admin_init', array($this, 'register_settings'));
|
||||
|
||||
add_action('admin_post_pc_delete_suggestion', array($this, 'handle_suggestion_deletion'));
|
||||
}
|
||||
|
||||
public function add_admin_menu() {
|
||||
add_menu_page(
|
||||
__('Community Suggestions', 'pc-community-suggestions-7d3f'),
|
||||
__('Suggestions', 'pc-community-suggestions-7d3f'),
|
||||
'manage_options',
|
||||
'pc_community_suggestions',
|
||||
array($this, 'render_admin_page'),
|
||||
'dashicons-lightbulb',
|
||||
30
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
'pc_community_suggestions',
|
||||
__('All Suggestions', 'pc-community-suggestions-7d3f'),
|
||||
__('All Suggestions', 'pc-community-suggestions-7d3f'),
|
||||
'manage_options',
|
||||
'pc_community_suggestions',
|
||||
array($this, 'render_admin_page')
|
||||
);
|
||||
|
||||
|
||||
|
||||
add_submenu_page(
|
||||
'pc_community_suggestions',
|
||||
__('Settings', 'pc-community-suggestions-7d3f'),
|
||||
__('Settings', 'pc-community-suggestions-7d3f'),
|
||||
'manage_options',
|
||||
'pc_community_suggestions_settings',
|
||||
array($this, 'render_settings_page')
|
||||
);
|
||||
}
|
||||
|
||||
public function register_settings() {
|
||||
register_setting('pc_community_suggestions_settings', 'pc_community_suggestions_default_sort');
|
||||
|
||||
add_settings_section(
|
||||
'pc_community_suggestions_general',
|
||||
__('General Settings', 'pc-community-suggestions-7d3f'),
|
||||
array($this, 'render_settings_section'),
|
||||
'pc_community_suggestions_settings'
|
||||
);
|
||||
|
||||
|
||||
|
||||
add_settings_field(
|
||||
'pc_community_suggestions_default_sort',
|
||||
__('Default Sort Order', 'pc-community-suggestions-7d3f'),
|
||||
array($this, 'render_sort_field'),
|
||||
'pc_community_suggestions_settings',
|
||||
'pc_community_suggestions_general'
|
||||
);
|
||||
}
|
||||
|
||||
public function render_settings_section() {
|
||||
echo '<p>' . __('Configure how community suggestions work on your site.', 'pc-community-suggestions-7d3f') . '</p>';
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function render_sort_field() {
|
||||
$default_sort = get_option('pc_community_suggestions_default_sort', 'popular');
|
||||
?>
|
||||
<select name="pc_community_suggestions_default_sort">
|
||||
<option value="popular" <?php selected($default_sort, 'popular'); ?>><?php esc_html_e('Most Upvoted', 'pc-community-suggestions-7d3f'); ?></option>
|
||||
<option value="newest" <?php selected($default_sort, 'newest'); ?>><?php esc_html_e('Newest', 'pc-community-suggestions-7d3f'); ?></option>
|
||||
</select>
|
||||
<p class="description">
|
||||
<?php esc_html_e('Default sorting method for the suggestions page.', 'pc-community-suggestions-7d3f'); ?>
|
||||
</p>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function render_admin_page() {
|
||||
$current_tab = $_GET['tab'] ?? 'all';
|
||||
$sort = isset($_GET['sort']) && in_array($_GET['sort'], ['popular', 'newest']) ? $_GET['sort'] : 'newest';
|
||||
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php esc_html_e('Community Suggestions', 'pc-community-suggestions-7d3f'); ?></h1>
|
||||
|
||||
<div class="pc-admin-controls">
|
||||
<div class="pc-sort-dropdown">
|
||||
<label for="pc-admin-sort"><?php esc_html_e('Sort by:', 'pc-community-suggestions-7d3f'); ?></label>
|
||||
<select id="pc-admin-sort" onchange="window.location.href='?page=pc_community_suggestions&sort=' + this.value">
|
||||
<option value="newest" <?php selected($sort, 'newest'); ?>><?php esc_html_e('Newest', 'pc-community-suggestions-7d3f'); ?></option>
|
||||
<option value="popular" <?php selected($sort, 'popular'); ?>><?php esc_html_e('Most Upvoted', 'pc-community-suggestions-7d3f'); ?></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pc-admin-content">
|
||||
<?php $this->render_suggestions_table($sort); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function render_settings_page() {
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php esc_html_e('Community Suggestions Settings', 'pc-community-suggestions-7d3f'); ?></h1>
|
||||
|
||||
<form method="post" action="options.php">
|
||||
<?php
|
||||
settings_fields('pc_community_suggestions_settings');
|
||||
do_settings_sections('pc_community_suggestions_settings');
|
||||
submit_button();
|
||||
?>
|
||||
</form>
|
||||
|
||||
<div class="pc-settings-stats">
|
||||
<h2><?php esc_html_e('Statistics', 'pc-community-suggestions-7d3f'); ?></h2>
|
||||
<div class="pc-stats-grid">
|
||||
<div class="pc-stat-card">
|
||||
<h3><?php echo esc_html(PC_Community_Suggestions_Database::get_suggestion_count()); ?></h3>
|
||||
<p><?php esc_html_e('Total Suggestions', 'pc-community-suggestions-7d3f'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function render_suggestions_table($sort = 'newest') {
|
||||
global $wpdb;
|
||||
|
||||
$table_name = $wpdb->prefix . 'pc_community_suggestions';
|
||||
$order_by = $sort === 'newest' ? 'created_at DESC' : 'upvotes DESC, created_at DESC';
|
||||
|
||||
$suggestions = $wpdb->get_results(
|
||||
"SELECT s.*, u.user_login, u.display_name
|
||||
FROM $table_name s
|
||||
LEFT JOIN {$wpdb->users} u ON s.user_id = u.ID
|
||||
ORDER BY $order_by"
|
||||
);
|
||||
|
||||
?>
|
||||
<table class="wp-list-table widefat fixed striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php esc_html_e('Title', 'pc-community-suggestions-7d3f'); ?></th>
|
||||
<th><?php esc_html_e('Author', 'pc-community-suggestions-7d3f'); ?></th>
|
||||
<th><?php esc_html_e('Upvotes', 'pc-community-suggestions-7d3f'); ?></th>
|
||||
<th><?php esc_html_e('Date', 'pc-community-suggestions-7d3f'); ?></th>
|
||||
<th><?php esc_html_e('Actions', 'pc-community-suggestions-7d3f'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($suggestions)) : ?>
|
||||
<tr>
|
||||
<td colspan="5"><?php esc_html_e('No suggestions found.', 'pc-community-suggestions-7d3f'); ?></td>
|
||||
</tr>
|
||||
<?php else : ?>
|
||||
<?php foreach ($suggestions as $suggestion) : ?>
|
||||
<tr>
|
||||
<td>
|
||||
<strong><?php echo esc_html($suggestion->title); ?></strong>
|
||||
<div class="row-actions">
|
||||
<span class="view">
|
||||
<a href="#" onclick="pcPreviewSuggestion(<?php echo esc_attr($suggestion->id); ?>)">
|
||||
<?php esc_html_e('View Details', 'pc-community-suggestions-7d3f'); ?>
|
||||
</a> |
|
||||
</span>
|
||||
<span class="comment">
|
||||
<a href="#" onclick="pcAddComment(<?php echo esc_attr($suggestion->id); ?>)">
|
||||
<?php esc_html_e('Add Comment', 'pc-community-suggestions-7d3f'); ?>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td><?php echo esc_html($suggestion->display_name ?: $suggestion->user_login); ?></td>
|
||||
<td><?php echo esc_html($suggestion->upvotes); ?></td>
|
||||
<td><?php echo date_i18n(get_option('date_format'), strtotime($suggestion->created_at)); ?></td>
|
||||
<td>
|
||||
<a href="<?php echo wp_nonce_url(admin_url('admin-post.php?action=pc_delete_suggestion&id=' . $suggestion->id), 'pc_delete_suggestion_' . $suggestion->id); ?>"
|
||||
class="button button-small button-link-delete"
|
||||
onclick="return confirm('<?php esc_attr_e('Are you sure you want to delete this suggestion?', 'pc-community-suggestions-7d3f'); ?>')">
|
||||
<?php esc_html_e('Delete', 'pc-community-suggestions-7d3f'); ?>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="pc-comments-<?php echo esc_attr($suggestion->id); ?>" class="pc-comments-row" style="display: none;">
|
||||
<td colspan="5" class="pc-comments-cell">
|
||||
<div class="pc-comments-container">
|
||||
<h4><?php esc_html_e('Admin Comments', 'pc-community-suggestions-7d3f'); ?></h4>
|
||||
<div class="pc-comments-list" id="pc-comments-list-<?php echo esc_attr($suggestion->id); ?>">
|
||||
<?php
|
||||
$comments = PC_Community_Suggestions_Database::get_comments($suggestion->id);
|
||||
if (empty($comments)) {
|
||||
echo '<p class="pc-no-comments">' . esc_html__('No comments yet.', 'pc-community-suggestions-7d3f') . '</p>';
|
||||
} else {
|
||||
foreach ($comments as $comment) {
|
||||
echo '<div class="pc-comment-item" data-comment-id="' . esc_attr($comment->id) . '">';
|
||||
echo '<div class="pc-comment-header">';
|
||||
echo '<strong>' . esc_html($comment->display_name ?: $comment->user_login) . '</strong>';
|
||||
echo '<span class="pc-comment-date">' . esc_html(date_i18n(get_option('date_format'), strtotime($comment->created_at))) . '</span>';
|
||||
echo '</div>';
|
||||
echo '<div class="pc-comment-content">' . wp_kses_post(wpautop($comment->comment)) . '</div>';
|
||||
echo '<div class="pc-comment-actions">';
|
||||
echo '<button class="button button-small button-link-delete" onclick="pcDeleteComment(' . esc_attr($comment->id) . ')">' . esc_html__('Delete', 'pc-community-suggestions-7d3f') . '</button>';
|
||||
echo '</div>';
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
<div class="pc-add-comment">
|
||||
<h5><?php esc_html_e('Add Comment', 'pc-community-suggestions-7d3f'); ?></h5>
|
||||
<textarea id="pc-comment-<?php echo esc_attr($suggestion->id); ?>" rows="3" placeholder="<?php esc_attr_e('Enter your comment...', 'pc-community-suggestions-7d3f'); ?>"></textarea>
|
||||
<button type="button" class="button button-small" onclick="pcSubmitComment(<?php echo esc_attr($suggestion->id); ?>)">
|
||||
<?php esc_html_e('Add Comment', 'pc-community-suggestions-7d3f'); ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function handle_suggestion_deletion() {
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_die(__('You do not have permission to perform this action.', 'pc-community-suggestions-7d3f'));
|
||||
}
|
||||
|
||||
$suggestion_id = intval($_GET['id'] ?? 0);
|
||||
check_admin_referer('pc_delete_suggestion_' . $suggestion_id);
|
||||
|
||||
if ($suggestion_id > 0) {
|
||||
PC_Community_Suggestions_Database::delete_suggestion($suggestion_id);
|
||||
wp_redirect(admin_url('admin.php?page=pc_community_suggestions&deleted=1'));
|
||||
exit;
|
||||
}
|
||||
|
||||
wp_redirect(admin_url('admin.php?page=pc_community_suggestions&error=1'));
|
||||
exit;
|
||||
}
|
||||
}
|
||||
552
chat/templates/Community Suggestions/admin/css/admin-style.css
Normal file
552
chat/templates/Community Suggestions/admin/css/admin-style.css
Normal file
@@ -0,0 +1,552 @@
|
||||
/* Community Suggestions Admin Styles */
|
||||
.pc-admin-content {
|
||||
/* Admin-scoped palette to keep admin UI consistent */
|
||||
--pc-admin-primary: #2271b1;
|
||||
--pc-admin-primary-strong: #135e96;
|
||||
--pc-admin-muted: #646970;
|
||||
--pc-admin-bg: #fff;
|
||||
margin-top: 20px;
|
||||
padding: 18px 0;
|
||||
}
|
||||
|
||||
/* Admin button enforcement */
|
||||
.pc-admin-content .pc-button {
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.pc-admin-content .pc-button-primary {
|
||||
background: var(--pc-admin-primary) !important;
|
||||
border-color: var(--pc-admin-primary) !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.wp-list-table .row-actions .view,
|
||||
.wp-list-table .row-actions .comment {
|
||||
color: var(--pc-admin-primary);
|
||||
}
|
||||
|
||||
.pc-admin-controls {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.pc-sort-dropdown {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.pc-sort-dropdown label {
|
||||
font-weight: 600;
|
||||
color: #1d2327;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.pc-sort-dropdown select {
|
||||
padding: 6px 12px;
|
||||
border: 1px solid #8c8f94;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.pc-sort-dropdown select:focus {
|
||||
outline: none;
|
||||
border-color: #2271b1;
|
||||
box-shadow: 0 0 0 1px #2271b1;
|
||||
}
|
||||
|
||||
.pc-comment-item {
|
||||
background: #2c3338;
|
||||
border-color: #3c434a;
|
||||
border-left-color: #2271b1;
|
||||
}
|
||||
|
||||
.pc-add-comment textarea {
|
||||
background: #1d2327;
|
||||
border-color: #3c434a;
|
||||
color: #dcdcde;
|
||||
}
|
||||
|
||||
.pc-add-comment textarea:focus {
|
||||
border-color: #2271b1;
|
||||
box-shadow: 0 0 0 2px rgba(34, 113, 177, 0.2);
|
||||
}
|
||||
|
||||
.pc-no-comments {
|
||||
background: #2c3338;
|
||||
border-color: #3c434a;
|
||||
color: #9ca1a7;
|
||||
}
|
||||
|
||||
.pc-settings-stats {
|
||||
margin-top: 30px;
|
||||
border-top: 1px solid #ccd0d4;
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
.pc-stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 20px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.pc-stat-card {
|
||||
background: #fff;
|
||||
border: 1px solid #ccd0d4;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.pc-stat-card h3 {
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
color: #2271b1;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.pc-stat-card p {
|
||||
color: #646970;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Status badges */
|
||||
.pc-status {
|
||||
display: inline-block;
|
||||
padding: 4px 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.pc-status-pending {
|
||||
background: #fff0c2;
|
||||
color: #946c00;
|
||||
border: 1px solid #ffd54f;
|
||||
}
|
||||
|
||||
.pc-status-approved {
|
||||
background: #e7f5e7;
|
||||
color: #2a6b2a;
|
||||
border: 1px solid #8bc34a;
|
||||
}
|
||||
|
||||
.pc-status-rejected {
|
||||
background: #fbe7e7;
|
||||
color: #b32d2e;
|
||||
border: 1px solid #f44336;
|
||||
}
|
||||
|
||||
/* Table improvements */
|
||||
.wp-list-table .pc-status {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.wp-list-table .row-actions {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.wp-list-table .row-actions .view,
|
||||
.wp-list-table .row-actions .comment {
|
||||
color: #2271b1;
|
||||
}
|
||||
|
||||
.wp-list-table .row-actions .comment {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
/* Comments styles */
|
||||
.pc-comments-row {
|
||||
background: #f9f9f9 !important;
|
||||
}
|
||||
|
||||
.pc-comments-cell {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.pc-comments-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.pc-comments-container h4 {
|
||||
margin: 0 0 15px 0;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.pc-comments-list {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.pc-comment-item {
|
||||
background: #fff;
|
||||
border: 1px solid #dcdcde;
|
||||
border-radius: 6px;
|
||||
padding: 15px;
|
||||
margin-bottom: 15px;
|
||||
border-left: 3px solid #2271b1;
|
||||
}
|
||||
|
||||
/* Remove purple accents from admin comments: override public styles that use purple */
|
||||
.pc-comments-container .pc-comment-card,
|
||||
.pc-comments-container .pc-comment-item,
|
||||
.pc-comments-container .pc-comments-section {
|
||||
border-left-color: var(--pc-admin-primary) !important; /* use admin blue */
|
||||
border-color: #dcdcde !important;
|
||||
background: #fff !important;
|
||||
color: #111 !important;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.06) !important;
|
||||
}
|
||||
|
||||
/* Make comment headers readable */
|
||||
.pc-comments-container .pc-comment-header strong,
|
||||
.pc-comments-container .pc-comment-content {
|
||||
color: #111 !important;
|
||||
}
|
||||
|
||||
.pc-comment-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.pc-comment-header strong {
|
||||
font-weight: 600;
|
||||
color: #dcdcde;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.pc-sort-dropdown label {
|
||||
color: #dcdcde;
|
||||
}
|
||||
|
||||
.pc-comment-date {
|
||||
color: #646970;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.pc-comment-content {
|
||||
line-height: 1.5;
|
||||
color: #c3c4c7;
|
||||
font-size: 13px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.pc-comment-actions {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.pc-no-comments {
|
||||
text-align: center;
|
||||
color: #646970;
|
||||
font-style: italic;
|
||||
padding: 15px;
|
||||
background: #fff;
|
||||
border: 1px dashed #dcdcde;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.pc-add-comment {
|
||||
border-top: 1px solid #e0e0e0;
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.pc-add-comment h5 {
|
||||
margin: 0 0 10px 0;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #dcdcde;
|
||||
}
|
||||
|
||||
.pc-add-comment textarea {
|
||||
width: 100%;
|
||||
min-height: 80px;
|
||||
padding: 10px;
|
||||
border: 1px solid #dcdcde;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
font-family: inherit;
|
||||
resize: vertical;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.pc-add-comment textarea:focus {
|
||||
outline: none;
|
||||
border-color: #2271b1;
|
||||
box-shadow: 0 0 0 2px rgba(34, 113, 177, 0.1);
|
||||
}
|
||||
|
||||
/* Form styles */
|
||||
.pc-form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.pc-form-group label {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
margin-bottom: 5px;
|
||||
color: #1d2327;
|
||||
}
|
||||
|
||||
.pc-form-group input[type="text"],
|
||||
.pc-form-group textarea {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #8c8f94;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.pc-form-group input[type="text"]:focus,
|
||||
.pc-form-group textarea:focus {
|
||||
border-color: #2271b1;
|
||||
box-shadow: 0 0 0 1px #2271b1;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.pc-form-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
/* Button styles */
|
||||
.pc-button {
|
||||
display: inline-block;
|
||||
padding: 8px 16px;
|
||||
border: 1px solid #2271b1;
|
||||
border-radius: 4px;
|
||||
background: #2271b1;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
line-height: 1.4;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
.pc-button:hover {
|
||||
background: #135e96;
|
||||
border-color: #135e96;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.pc-button:active {
|
||||
background: #0a4b78;
|
||||
border-color: #0a4b78;
|
||||
}
|
||||
|
||||
.pc-button-primary {
|
||||
background: #2271b1;
|
||||
border-color: #2271b1;
|
||||
}
|
||||
|
||||
.pc-button-primary:hover {
|
||||
background: #135e96;
|
||||
border-color: #135e96;
|
||||
}
|
||||
|
||||
.pc-button-secondary {
|
||||
background: #f6f7f7;
|
||||
border-color: #dcdcde;
|
||||
color: #2c3338;
|
||||
}
|
||||
|
||||
.pc-button-secondary:hover {
|
||||
background: #dcdcde;
|
||||
border-color: #c3c4c7;
|
||||
}
|
||||
|
||||
/* Card styles */
|
||||
.pc-card {
|
||||
background: #fff;
|
||||
border: 1px solid #ccd0d4;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.pc-card h3 {
|
||||
margin: 0 0 15px 0;
|
||||
color: #1d2327;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Responsive design */
|
||||
@media screen and (max-width: 782px) {
|
||||
.pc-stats-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.pc-stat-card {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.pc-stat-card h3 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.pc-form-group input[type="text"],
|
||||
.pc-form-group textarea {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.pc-form-actions {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.wp-list-table {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.wp-list-table th,
|
||||
.wp-list-table td {
|
||||
padding: 8px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark mode support */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.pc-stat-card,
|
||||
.pc-card {
|
||||
background: #2c3338;
|
||||
border-color: #3c434a;
|
||||
}
|
||||
|
||||
.pc-stat-card p,
|
||||
.pc-form-group label {
|
||||
color: #dcdcde;
|
||||
}
|
||||
|
||||
.pc-form-group input[type="text"],
|
||||
.pc-form-group textarea {
|
||||
background: #1d2327;
|
||||
border-color: #3c434a;
|
||||
color: #dcdcde;
|
||||
}
|
||||
|
||||
.pc-form-group input[type="text"]:focus,
|
||||
.pc-form-group textarea:focus {
|
||||
border-color: #2271b1;
|
||||
box-shadow: 0 0 0 1px #2271b1;
|
||||
}
|
||||
|
||||
.pc-button-secondary {
|
||||
background: #3c434a;
|
||||
border-color: #4f5660;
|
||||
color: #dcdcde;
|
||||
}
|
||||
|
||||
.pc-button-secondary:hover {
|
||||
background: #4f5660;
|
||||
border-color: #646970;
|
||||
}
|
||||
}
|
||||
|
||||
/* Print styles */
|
||||
@media print {
|
||||
.pc-stats-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.pc-stat-card {
|
||||
border: 1px solid #000;
|
||||
background: #fff !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.pc-stat-card h3 {
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.pc-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Animation for status changes */
|
||||
@keyframes pc-fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.pc-stat-card,
|
||||
.pc-card {
|
||||
animation: pc-fade-in 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
/* Loading states */
|
||||
.pc-loading {
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.pc-loading::after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 2px solid #f3f3f3;
|
||||
border-top: 2px solid #2271b1;
|
||||
border-radius: 50%;
|
||||
animation: pc-spin 1s linear infinite;
|
||||
margin-left: 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@keyframes pc-spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Success/error messages */
|
||||
.pc-notice {
|
||||
padding: 12px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
border-left: 4px solid;
|
||||
}
|
||||
|
||||
.pc-notice-success {
|
||||
background: #edfaef;
|
||||
border-color: #00a32a;
|
||||
color: #2a6b2a;
|
||||
}
|
||||
|
||||
.pc-notice-error {
|
||||
background: #fcf0f1;
|
||||
border-color: #d63638;
|
||||
color: #b32d2e;
|
||||
}
|
||||
|
||||
.pc-notice-info {
|
||||
background: #f0f6ff;
|
||||
border-color: #2271b1;
|
||||
color: #135e96;
|
||||
}
|
||||
369
chat/templates/Community Suggestions/admin/js/admin-script.js
Normal file
369
chat/templates/Community Suggestions/admin/js/admin-script.js
Normal file
@@ -0,0 +1,369 @@
|
||||
jQuery(document).ready(function($) {
|
||||
|
||||
// View suggestion details modal
|
||||
window.pcPreviewSuggestion = function(suggestionId) {
|
||||
$.ajax({
|
||||
url: pcCommunitySuggestionsAdmin.ajax_url,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'pc_get_suggestion',
|
||||
suggestion_id: suggestionId,
|
||||
nonce: pcCommunitySuggestionsAdmin.nonce
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
pcShowSuggestionModal(response.data);
|
||||
} else {
|
||||
alert(pcCommunitySuggestionsAdmin.i18n.action_failed + ': ' + response.data.message);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alert(pcCommunitySuggestionsAdmin.i18n.action_failed);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function pcShowSuggestionModal(suggestion) {
|
||||
const modalHtml = `
|
||||
<div class="pc-modal" style="display: none;">
|
||||
<div class="pc-modal-overlay"></div>
|
||||
<div class="pc-modal-content">
|
||||
<div class="pc-modal-header">
|
||||
<h3>${suggestion.title}</h3>
|
||||
<button class="pc-modal-close" onclick="pcCloseModal()">×</button>
|
||||
</div>
|
||||
<div class="pc-modal-body">
|
||||
<div class="pc-suggestion-content">
|
||||
${suggestion.content}
|
||||
</div>
|
||||
<div class="pc-suggestion-meta">
|
||||
<p><strong>Author:</strong> ${suggestion.author}</p>
|
||||
<p><strong>Submitted:</strong> ${suggestion.date}</p>
|
||||
<p><strong>Upvotes:</strong> ${suggestion.upvotes}</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="pc-modal-footer">
|
||||
<button class="button button-primary" onclick="pcCloseModal()">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
$('body').append(modalHtml);
|
||||
$('.pc-modal').fadeIn(200);
|
||||
|
||||
// Prevent background scrolling
|
||||
$('body').css('overflow', 'hidden');
|
||||
}
|
||||
|
||||
window.pcCloseModal = function() {
|
||||
$('.pc-modal').fadeOut(200, function() {
|
||||
$(this).remove();
|
||||
});
|
||||
$('body').css('overflow', '');
|
||||
};
|
||||
|
||||
// Add comment functionality
|
||||
window.pcAddComment = function(suggestionId) {
|
||||
var $commentsRow = $('#pc-comments-' + suggestionId);
|
||||
$commentsRow.slideToggle();
|
||||
};
|
||||
|
||||
window.pcSubmitComment = function(suggestionId) {
|
||||
var $textarea = $('#pc-comment-' + suggestionId);
|
||||
var comment = $textarea.val().trim();
|
||||
|
||||
if (!comment) {
|
||||
alert('Please enter a comment.');
|
||||
return;
|
||||
}
|
||||
|
||||
var $button = $textarea.siblings('button');
|
||||
var originalText = $button.text();
|
||||
$button.prop('disabled', true).text('Adding...');
|
||||
|
||||
$.ajax({
|
||||
url: pcCommunitySuggestionsAdmin.ajax_url,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'pc_add_comment',
|
||||
suggestion_id: suggestionId,
|
||||
comment: comment,
|
||||
nonce: pcCommunitySuggestionsAdmin.nonce
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
var commentHtml = '<div class="pc-comment-item" data-comment-id="' + response.data.comment.id + '">' +
|
||||
'<div class="pc-comment-header">' +
|
||||
'<strong>' + response.data.comment.author + '</strong> ' +
|
||||
'<span class="pc-comment-date">' + response.data.comment.date + '</span>' +
|
||||
'</div>' +
|
||||
'<div class="pc-comment-content">' + response.data.comment.content + '</div>' +
|
||||
'<div class="pc-comment-actions">' +
|
||||
'<button class="button button-small button-link-delete" onclick="pcDeleteComment(' + response.data.comment.id + ')">Delete</button>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
|
||||
var $commentsList = $('#pc-comments-list-' + suggestionId);
|
||||
$commentsList.find('.pc-no-comments').remove();
|
||||
$commentsList.append(commentHtml);
|
||||
|
||||
$textarea.val('');
|
||||
alert(response.data.message);
|
||||
} else {
|
||||
alert(response.data.message || pcCommunitySuggestionsAdmin.i18n.action_failed);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alert(pcCommunitySuggestionsAdmin.i18n.action_failed);
|
||||
},
|
||||
complete: function() {
|
||||
$button.prop('disabled', false).text(originalText);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
window.pcDeleteComment = function(commentId) {
|
||||
if (!confirm('Are you sure you want to delete this comment?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: pcCommunitySuggestionsAdmin.ajax_url,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'pc_delete_comment',
|
||||
comment_id: commentId,
|
||||
nonce: pcCommunitySuggestionsAdmin.nonce
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
$('.pc-comment-item[data-comment-id="' + commentId + '"]').fadeOut(300, function() {
|
||||
$(this).remove();
|
||||
});
|
||||
alert(response.data.message);
|
||||
} else {
|
||||
alert(response.data.message || pcCommunitySuggestionsAdmin.i18n.action_failed);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alert(pcCommunitySuggestionsAdmin.i18n.action_failed);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Close modal on overlay click
|
||||
$(document).on('click', '.pc-modal-overlay', function(e) {
|
||||
if (e.target === this) {
|
||||
pcCloseModal();
|
||||
}
|
||||
});
|
||||
|
||||
// Close modal on ESC key
|
||||
$(document).on('keydown', function(e) {
|
||||
if (e.key === 'Escape' && $('.pc-modal').is(':visible')) {
|
||||
pcCloseModal();
|
||||
}
|
||||
});
|
||||
|
||||
// Add modal styles
|
||||
$('<style>')
|
||||
.text(`
|
||||
.pc-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 99999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pc-modal-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.pc-modal-content {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
|
||||
max-width: 600px;
|
||||
width: 90%;
|
||||
max-height: 80vh;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 100000;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pc-modal-header {
|
||||
padding: 20px 25px;
|
||||
border-bottom: 1px solid #dcdcde;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.pc-modal-header h3 {
|
||||
margin: 0;
|
||||
font-size: 1.3em;
|
||||
color: #1d2327;
|
||||
flex: 1;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.pc-modal-close {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
line-height: 1;
|
||||
color: #646970;
|
||||
}
|
||||
|
||||
.pc-modal-close:hover {
|
||||
color: #2271b1;
|
||||
}
|
||||
|
||||
.pc-modal-body {
|
||||
padding: 25px;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.pc-suggestion-content {
|
||||
margin-bottom: 20px;
|
||||
line-height: 1.6;
|
||||
color: #2c3338;
|
||||
}
|
||||
|
||||
.pc-suggestion-content p {
|
||||
margin: 0 0 15px 0;
|
||||
}
|
||||
|
||||
.pc-suggestion-content p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.pc-suggestion-meta {
|
||||
border-top: 1px solid #dcdcde;
|
||||
padding-top: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.pc-suggestion-meta p {
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
|
||||
.pc-suggestion-meta p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.pc-modal-footer {
|
||||
padding: 20px 25px;
|
||||
border-top: 1px solid #dcdcde;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 782px) {
|
||||
.pc-modal-content {
|
||||
width: 95%;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.pc-modal-header {
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.pc-modal-body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.pc-modal-footer {
|
||||
padding: 15px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark mode support */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.pc-modal-content {
|
||||
background: #2c3338;
|
||||
border: 1px solid #3c434a;
|
||||
}
|
||||
|
||||
.pc-modal-header,
|
||||
.pc-modal-footer {
|
||||
border-color: #3c434a;
|
||||
}
|
||||
|
||||
.pc-modal-header h3 {
|
||||
color: #dcdcde;
|
||||
}
|
||||
|
||||
.pc-modal-close {
|
||||
color: #9ca1a7;
|
||||
}
|
||||
|
||||
.pc-modal-close:hover {
|
||||
color: #2271b1;
|
||||
}
|
||||
|
||||
.pc-suggestion-content {
|
||||
color: #c3c4c7;
|
||||
}
|
||||
|
||||
.pc-suggestion-meta {
|
||||
border-color: #3c434a;
|
||||
}
|
||||
}
|
||||
`)
|
||||
.appendTo('head');
|
||||
|
||||
// Handle bulk actions
|
||||
$('.pc-bulk-action').on('change', function() {
|
||||
const action = $(this).val();
|
||||
if (action) {
|
||||
const checked = $('.pc-bulk-checkbox:checked');
|
||||
if (checked.length === 0) {
|
||||
alert(pcCommunitySuggestionsAdmin.i18n.no_items_selected);
|
||||
$(this).val('');
|
||||
return;
|
||||
}
|
||||
|
||||
const suggestionIds = checked.map(function() {
|
||||
return $(this).val();
|
||||
}).get();
|
||||
|
||||
if (confirm(pcCommunitySuggestionsAdmin.i18n.confirm_bulk_action.replace('{count}', checked.length).replace('{action}', action))) {
|
||||
$.ajax({
|
||||
url: pcCommunitySuggestionsAdmin.ajax_url,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'pc_get_stats',
|
||||
nonce: pcCommunitySuggestionsAdmin.nonce
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
$('#pc-stat-total').text(response.data.total);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Update stats every 30 seconds if on stats page
|
||||
if ($('.pc-settings-stats').length) {
|
||||
setInterval(pcUpdateStats, 30000);
|
||||
}
|
||||
});
|
||||
342
chat/templates/Community Suggestions/includes/class-database.php
Normal file
342
chat/templates/Community Suggestions/includes/class-database.php
Normal file
@@ -0,0 +1,342 @@
|
||||
<?php
|
||||
class PC_Community_Suggestions_Database {
|
||||
|
||||
public static function create_tables() {
|
||||
global $wpdb;
|
||||
|
||||
error_log('PC Community Suggestions: create_tables called');
|
||||
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
|
||||
$suggestions_table = $wpdb->prefix . 'pc_community_suggestions';
|
||||
$votes_table = $wpdb->prefix . 'pc_community_votes';
|
||||
$comments_table = $wpdb->prefix . 'pc_community_comments';
|
||||
|
||||
error_log('PC Community Suggestions: Creating tables - ' . $suggestions_table . ', ' . $votes_table . ', ' . $comments_table);
|
||||
|
||||
// Suggestions table
|
||||
$sql_suggestions = "CREATE TABLE IF NOT EXISTS $suggestions_table (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
upvotes INT DEFAULT 0,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
KEY user_id (user_id),
|
||||
KEY created_at (created_at),
|
||||
KEY upvotes (upvotes)
|
||||
) $charset_collate;";
|
||||
|
||||
// Votes table
|
||||
$sql_votes = "CREATE TABLE IF NOT EXISTS $votes_table (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
suggestion_id BIGINT UNSIGNED NOT NULL,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY user_suggestion (user_id, suggestion_id),
|
||||
KEY suggestion_id (suggestion_id)
|
||||
) $charset_collate;";
|
||||
|
||||
// Comments table
|
||||
$sql_comments = "CREATE TABLE IF NOT EXISTS $comments_table (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
suggestion_id BIGINT UNSIGNED NOT NULL,
|
||||
admin_id BIGINT UNSIGNED NOT NULL,
|
||||
comment TEXT NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id),
|
||||
KEY suggestion_id (suggestion_id),
|
||||
KEY admin_id (admin_id)
|
||||
) $charset_collate;";
|
||||
|
||||
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
||||
|
||||
// Clear any previous errors
|
||||
$wpdb->last_error = '';
|
||||
|
||||
// Create tables directly using dbDelta
|
||||
dbDelta($sql_suggestions);
|
||||
dbDelta($sql_votes);
|
||||
dbDelta($sql_comments);
|
||||
|
||||
// Log any errors for debugging
|
||||
if (!empty($wpdb->last_error)) {
|
||||
error_log('PC Community Suggestions Table Creation Error: ' . $wpdb->last_error);
|
||||
} else {
|
||||
error_log('PC Community Suggestions: Tables created without database errors');
|
||||
}
|
||||
|
||||
// Verify tables were created
|
||||
$tables_created = true;
|
||||
$tables_to_check = array($suggestions_table, $votes_table, $comments_table);
|
||||
|
||||
foreach ($tables_to_check as $table) {
|
||||
$exists = $wpdb->get_var("SHOW TABLES LIKE '$table'");
|
||||
if (!$exists) {
|
||||
error_log('PC Community Suggestions: Failed to create table ' . $table);
|
||||
$tables_created = false;
|
||||
} else {
|
||||
error_log('PC Community Suggestions: Table exists - ' . $table);
|
||||
}
|
||||
}
|
||||
|
||||
return $tables_created;
|
||||
}
|
||||
|
||||
public static function add_suggestion($user_id, $title, $content) {
|
||||
global $wpdb;
|
||||
|
||||
$table_name = $wpdb->prefix . 'pc_community_suggestions';
|
||||
|
||||
error_log('PC Community Suggestions: add_suggestion called, table: ' . $table_name);
|
||||
error_log('PC Community Suggestions: user_id=' . $user_id . ', title=' . substr($title, 0, 50));
|
||||
|
||||
// Check if table exists
|
||||
$table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'");
|
||||
if (!$table_exists) {
|
||||
error_log('PC Community Suggestions: Table does not exist - ' . $table_name);
|
||||
error_log('PC Community Suggestions: Creating tables now...');
|
||||
self::create_tables();
|
||||
|
||||
// Check again
|
||||
$table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'");
|
||||
if (!$table_exists) {
|
||||
error_log('PC Community Suggestions: Still cannot find table after creation attempt');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$result = $wpdb->insert(
|
||||
$table_name,
|
||||
array(
|
||||
'user_id' => $user_id,
|
||||
'title' => sanitize_text_field($title),
|
||||
'content' => wp_kses_post($content)
|
||||
),
|
||||
array('%d', '%s', '%s')
|
||||
);
|
||||
|
||||
if ($result === false) {
|
||||
error_log('PC Community Suggestions: Failed to insert suggestion - ' . $wpdb->last_error);
|
||||
return false;
|
||||
}
|
||||
|
||||
$insert_id = $wpdb->insert_id;
|
||||
error_log('PC Community Suggestions: Suggestion inserted with ID: ' . $insert_id);
|
||||
return $insert_id;
|
||||
}
|
||||
|
||||
public static function get_suggestions($page = 1, $per_page = 10, $sort = 'popular') {
|
||||
global $wpdb;
|
||||
|
||||
$table_name = $wpdb->prefix . 'pc_community_suggestions';
|
||||
$offset = ($page - 1) * $per_page;
|
||||
|
||||
$order_by = $sort === 'newest' ? 'created_at DESC' : 'upvotes DESC, created_at DESC';
|
||||
|
||||
$query = $wpdb->prepare(
|
||||
"SELECT s.*, u.user_login, u.display_name
|
||||
FROM $table_name s
|
||||
LEFT JOIN {$wpdb->users} u ON s.user_id = u.ID
|
||||
ORDER BY $order_by
|
||||
LIMIT %d OFFSET %d",
|
||||
$per_page,
|
||||
$offset
|
||||
);
|
||||
|
||||
return $wpdb->get_results($query);
|
||||
}
|
||||
|
||||
public static function get_suggestion_count() {
|
||||
global $wpdb;
|
||||
|
||||
$table_name = $wpdb->prefix . 'pc_community_suggestions';
|
||||
|
||||
return $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
|
||||
}
|
||||
|
||||
public static function get_suggestion_by_id($suggestion_id) {
|
||||
global $wpdb;
|
||||
|
||||
$table_name = $wpdb->prefix . 'pc_community_suggestions';
|
||||
|
||||
return $wpdb->get_row($wpdb->prepare(
|
||||
"SELECT s.*, u.user_login, u.display_name
|
||||
FROM $table_name s
|
||||
LEFT JOIN {$wpdb->users} u ON s.user_id = u.ID
|
||||
WHERE s.id = %d",
|
||||
$suggestion_id
|
||||
));
|
||||
}
|
||||
|
||||
public static function add_vote($suggestion_id, $user_id) {
|
||||
global $wpdb;
|
||||
|
||||
$votes_table = $wpdb->prefix . 'pc_community_votes';
|
||||
$suggestions_table = $wpdb->prefix . 'pc_community_suggestions';
|
||||
|
||||
// Check if user already voted
|
||||
$existing_vote = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM $votes_table WHERE suggestion_id = %d AND user_id = %d",
|
||||
$suggestion_id,
|
||||
$user_id
|
||||
)
|
||||
);
|
||||
|
||||
if ($existing_vote > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$wpdb->query('START TRANSACTION');
|
||||
|
||||
// Add vote
|
||||
$vote_result = $wpdb->insert(
|
||||
$votes_table,
|
||||
array(
|
||||
'suggestion_id' => $suggestion_id,
|
||||
'user_id' => $user_id
|
||||
),
|
||||
array('%d', '%d')
|
||||
);
|
||||
|
||||
if (!$vote_result) {
|
||||
$wpdb->query('ROLLBACK');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update suggestion upvote count
|
||||
$update_result = $wpdb->query(
|
||||
$wpdb->prepare(
|
||||
"UPDATE $suggestions_table SET upvotes = upvotes + 1 WHERE id = %d",
|
||||
$suggestion_id
|
||||
)
|
||||
);
|
||||
|
||||
if (!$update_result) {
|
||||
$wpdb->query('ROLLBACK');
|
||||
return false;
|
||||
}
|
||||
|
||||
$wpdb->query('COMMIT');
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function get_user_vote_count($user_id) {
|
||||
global $wpdb;
|
||||
|
||||
$votes_table = $wpdb->prefix . 'pc_community_votes';
|
||||
|
||||
return $wpdb->get_var(
|
||||
$wpdb->prepare("SELECT COUNT(*) FROM $votes_table WHERE user_id = %d", $user_id)
|
||||
);
|
||||
}
|
||||
|
||||
public static function has_user_voted($suggestion_id, $user_id) {
|
||||
global $wpdb;
|
||||
|
||||
$votes_table = $wpdb->prefix . 'pc_community_votes';
|
||||
|
||||
return (bool) $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM $votes_table WHERE suggestion_id = %d AND user_id = %d",
|
||||
$suggestion_id,
|
||||
$user_id
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static function add_comment($suggestion_id, $admin_id, $comment) {
|
||||
global $wpdb;
|
||||
|
||||
$comments_table = $wpdb->prefix . 'pc_community_comments';
|
||||
|
||||
return $wpdb->insert(
|
||||
$comments_table,
|
||||
array(
|
||||
'suggestion_id' => $suggestion_id,
|
||||
'admin_id' => $admin_id,
|
||||
'comment' => wp_kses_post($comment)
|
||||
),
|
||||
array('%d', '%d', '%s')
|
||||
);
|
||||
}
|
||||
|
||||
public static function get_comments($suggestion_id) {
|
||||
global $wpdb;
|
||||
|
||||
$comments_table = $wpdb->prefix . 'pc_community_comments';
|
||||
|
||||
return $wpdb->get_results($wpdb->prepare(
|
||||
"SELECT c.*, u.user_login, u.display_name
|
||||
FROM $comments_table c
|
||||
LEFT JOIN {$wpdb->users} u ON c.admin_id = u.ID
|
||||
WHERE c.suggestion_id = %d
|
||||
ORDER BY c.created_at ASC",
|
||||
$suggestion_id
|
||||
));
|
||||
}
|
||||
|
||||
public static function delete_comment($comment_id) {
|
||||
global $wpdb;
|
||||
|
||||
$comments_table = $wpdb->prefix . 'pc_community_comments';
|
||||
|
||||
return $wpdb->delete(
|
||||
$comments_table,
|
||||
array('id' => $comment_id),
|
||||
array('%d')
|
||||
);
|
||||
}
|
||||
|
||||
public static function delete_suggestion($suggestion_id) {
|
||||
global $wpdb;
|
||||
|
||||
$suggestions_table = $wpdb->prefix . 'pc_community_suggestions';
|
||||
$votes_table = $wpdb->prefix . 'pc_community_votes';
|
||||
$comments_table = $wpdb->prefix . 'pc_community_comments';
|
||||
|
||||
$wpdb->query('START TRANSACTION');
|
||||
|
||||
// Delete votes first
|
||||
$wpdb->delete($votes_table, array('suggestion_id' => $suggestion_id), array('%d'));
|
||||
|
||||
// Delete comments
|
||||
$wpdb->delete($comments_table, array('suggestion_id' => $suggestion_id), array('%d'));
|
||||
|
||||
// Delete suggestion
|
||||
$result = $wpdb->delete($suggestions_table, array('id' => $suggestion_id), array('%d'));
|
||||
|
||||
if ($result === false) {
|
||||
$wpdb->query('ROLLBACK');
|
||||
return false;
|
||||
}
|
||||
|
||||
$wpdb->query('COMMIT');
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function verify_tables() {
|
||||
global $wpdb;
|
||||
|
||||
$suggestions_table = $wpdb->prefix . 'pc_community_suggestions';
|
||||
$votes_table = $wpdb->prefix . 'pc_community_votes';
|
||||
$comments_table = $wpdb->prefix . 'pc_community_comments';
|
||||
|
||||
$tables_to_check = array($suggestions_table, $votes_table, $comments_table);
|
||||
$all_exist = true;
|
||||
|
||||
foreach ($tables_to_check as $table) {
|
||||
$exists = $wpdb->get_var("SHOW TABLES LIKE '$table'");
|
||||
if (!$exists) {
|
||||
error_log('PC Community Suggestions: Missing table ' . $table);
|
||||
$all_exist = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $all_exist;
|
||||
}
|
||||
}
|
||||
401
chat/templates/Community Suggestions/includes/class-rest-api.php
Normal file
401
chat/templates/Community Suggestions/includes/class-rest-api.php
Normal file
@@ -0,0 +1,401 @@
|
||||
<?php
|
||||
class PC_Community_Suggestions_REST_API {
|
||||
|
||||
public function __construct() {
|
||||
add_action('rest_api_init', array($this, 'register_routes'));
|
||||
add_action('wp_ajax_pc_submit_suggestion', array($this, 'handle_suggestion_submission'));
|
||||
add_action('wp_ajax_pc_vote_suggestion', array($this, 'handle_vote'));
|
||||
add_action('wp_ajax_pc_get_suggestion', array($this, 'handle_get_suggestion'));
|
||||
add_action('wp_ajax_pc_get_stats', array($this, 'handle_get_stats'));
|
||||
add_action('wp_ajax_pc_add_comment', array($this, 'handle_add_comment'));
|
||||
add_action('wp_ajax_pc_delete_comment', array($this, 'handle_delete_comment'));
|
||||
add_action('wp_ajax_nopriv_pc_submit_suggestion', array($this, 'handle_not_logged_in'));
|
||||
add_action('wp_ajax_nopriv_pc_vote_suggestion', array($this, 'handle_not_logged_in'));
|
||||
}
|
||||
|
||||
public function register_routes() {
|
||||
register_rest_route('pc-community-suggestions/v1', '/suggestions', array(
|
||||
'methods' => 'POST',
|
||||
'callback' => array($this, 'create_suggestion'),
|
||||
'permission_callback' => array($this, 'check_user_permission'),
|
||||
'args' => array(
|
||||
'title' => array(
|
||||
'required' => true,
|
||||
'validate_callback' => array($this, 'validate_title'),
|
||||
'sanitize_callback' => 'sanitize_text_field'
|
||||
),
|
||||
'content' => array(
|
||||
'required' => true,
|
||||
'validate_callback' => array($this, 'validate_content'),
|
||||
'sanitize_callback' => 'wp_kses_post'
|
||||
)
|
||||
)
|
||||
));
|
||||
|
||||
register_rest_route('pc-community-suggestions/v1', '/suggestions/(?P<id>\d+)/vote', array(
|
||||
'methods' => 'POST',
|
||||
'callback' => array($this, 'vote_suggestion'),
|
||||
'permission_callback' => array($this, 'check_user_permission'),
|
||||
'args' => array(
|
||||
'id' => array(
|
||||
'required' => true,
|
||||
'validate_callback' => array($this, 'validate_suggestion_id')
|
||||
)
|
||||
)
|
||||
));
|
||||
|
||||
register_rest_route('pc-community-suggestions/v1', '/suggestions', array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array($this, 'get_suggestions'),
|
||||
'permission_callback' => '__return_true',
|
||||
'args' => array(
|
||||
'page' => array(
|
||||
'default' => 1,
|
||||
'validate_callback' => array($this, 'validate_page')
|
||||
),
|
||||
'per_page' => array(
|
||||
'default' => 10,
|
||||
'validate_callback' => array($this, 'validate_per_page')
|
||||
),
|
||||
'sort' => array(
|
||||
'default' => 'popular',
|
||||
'validate_callback' => array($this, 'validate_sort')
|
||||
)
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
public function check_user_permission() {
|
||||
return is_user_logged_in();
|
||||
}
|
||||
|
||||
public function validate_title($title) {
|
||||
$title = sanitize_text_field($title);
|
||||
return !empty($title) && strlen($title) <= 255;
|
||||
}
|
||||
|
||||
public function validate_content($content) {
|
||||
$content = wp_kses_post($content);
|
||||
return !empty($content) && strlen($content) >= 10;
|
||||
}
|
||||
|
||||
public function validate_suggestion_id($id) {
|
||||
return is_numeric($id) && $id > 0;
|
||||
}
|
||||
|
||||
public function validate_page($page) {
|
||||
return is_numeric($page) && $page > 0;
|
||||
}
|
||||
|
||||
public function validate_per_page($per_page) {
|
||||
return is_numeric($per_page) && $per_page > 0 && $per_page <= 50;
|
||||
}
|
||||
|
||||
public function validate_sort($sort) {
|
||||
return in_array($sort, array('popular', 'newest'));
|
||||
}
|
||||
|
||||
public function create_suggestion(WP_REST_Request $request) {
|
||||
$title = $request->get_param('title');
|
||||
$content = $request->get_param('content');
|
||||
$user_id = get_current_user_id();
|
||||
|
||||
// Check for duplicate submissions
|
||||
$existing = $this->check_duplicate_suggestion($user_id, $title);
|
||||
if ($existing) {
|
||||
return new WP_Error('duplicate', __('You have already submitted a similar suggestion.', 'pc-community-suggestions-7d3f'), array('status' => 400));
|
||||
}
|
||||
|
||||
$suggestion_id = PC_Community_Suggestions_Database::add_suggestion($user_id, $title, $content);
|
||||
|
||||
if (!$suggestion_id) {
|
||||
return new WP_Error('database_error', __('Failed to create suggestion.', 'pc-community-suggestions-7d3f'), array('status' => 500));
|
||||
}
|
||||
|
||||
return rest_ensure_response(array(
|
||||
'success' => true,
|
||||
'suggestion_id' => $suggestion_id,
|
||||
'message' => __('Your suggestion has been published successfully.', 'pc-community-suggestions-7d3f')
|
||||
));
|
||||
}
|
||||
|
||||
private function check_duplicate_suggestion($user_id, $title) {
|
||||
global $wpdb;
|
||||
|
||||
$table_name = $wpdb->prefix . 'pc_community_suggestions';
|
||||
$similar_title = '%' . $wpdb->esc_like($title) . '%';
|
||||
|
||||
$count = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM $table_name WHERE user_id = %d AND title LIKE %s AND created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)",
|
||||
$user_id,
|
||||
$similar_title
|
||||
)
|
||||
);
|
||||
|
||||
return $count > 0;
|
||||
}
|
||||
|
||||
public function vote_suggestion(WP_REST_Request $request) {
|
||||
$suggestion_id = $request->get_param('id');
|
||||
$user_id = get_current_user_id();
|
||||
|
||||
$result = PC_Community_Suggestions_Database::add_vote($suggestion_id, $user_id);
|
||||
|
||||
if (!$result) {
|
||||
return new WP_Error('already_voted', __('You have already voted for this suggestion.', 'pc-community-suggestions-7d3f'), array('status' => 400));
|
||||
}
|
||||
|
||||
return rest_ensure_response(array(
|
||||
'success' => true,
|
||||
'message' => __('Vote recorded successfully.', 'pc-community-suggestions-7d3f')
|
||||
));
|
||||
}
|
||||
|
||||
public function get_suggestions(WP_REST_Request $request) {
|
||||
$page = $request->get_param('page');
|
||||
$per_page = $request->get_param('per_page');
|
||||
$sort = $request->get_param('sort');
|
||||
|
||||
$suggestions = PC_Community_Suggestions_Database::get_suggestions($page, $per_page, $sort);
|
||||
$total = PC_Community_Suggestions_Database::get_suggestion_count();
|
||||
|
||||
$formatted_suggestions = array();
|
||||
foreach ($suggestions as $suggestion) {
|
||||
$formatted_suggestions[] = array(
|
||||
'id' => $suggestion->id,
|
||||
'title' => $suggestion->title,
|
||||
'content' => $suggestion->content,
|
||||
'upvotes' => $suggestion->upvotes,
|
||||
'author' => $suggestion->display_name ?: $suggestion->user_login,
|
||||
'created_at' => $suggestion->created_at,
|
||||
'human_date' => human_time_diff(strtotime($suggestion->created_at), current_time('timestamp'))
|
||||
);
|
||||
}
|
||||
|
||||
return rest_ensure_response(array(
|
||||
'suggestions' => $formatted_suggestions,
|
||||
'pagination' => array(
|
||||
'current_page' => $page,
|
||||
'per_page' => $per_page,
|
||||
'total' => $total,
|
||||
'total_pages' => ceil($total / $per_page)
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
public function handle_suggestion_submission() {
|
||||
error_log('PC Community Suggestions: handle_suggestion_submission called');
|
||||
|
||||
$nonce = isset($_POST['nonce']) ? $_POST['nonce'] : '';
|
||||
error_log('PC Community Suggestions: Nonce value: ' . substr($nonce, 0, 20) . '...');
|
||||
|
||||
if (!check_ajax_referer('pc_community_suggestions_nonce', 'nonce', false)) {
|
||||
error_log('PC Community Suggestions: Nonce check failed');
|
||||
wp_send_json_error(array('message' => __('Security check failed. Please try again.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
if (!is_user_logged_in()) {
|
||||
error_log('PC Community Suggestions: User not logged in');
|
||||
wp_send_json_error(array('message' => __('You must be logged in to submit suggestions.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
$title = isset($_POST['title']) ? sanitize_text_field($_POST['title']) : '';
|
||||
$content = isset($_POST['content']) ? wp_kses_post($_POST['content']) : '';
|
||||
|
||||
error_log('PC Community Suggestions: Title length: ' . strlen($title) . ', Content length: ' . strlen($content));
|
||||
|
||||
if (empty($title) || empty($content)) {
|
||||
wp_send_json_error(array('message' => __('Please fill in all required fields.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
if (strlen($title) > 255) {
|
||||
wp_send_json_error(array('message' => __('Title must be less than 255 characters.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
if (strlen($content) < 10) {
|
||||
wp_send_json_error(array('message' => __('Content must be at least 10 characters long.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
$user_id = get_current_user_id();
|
||||
error_log('PC Community Suggestions: User ID: ' . $user_id);
|
||||
|
||||
try {
|
||||
$existing = $this->check_duplicate_suggestion($user_id, $title);
|
||||
if ($existing) {
|
||||
error_log('PC Community Suggestions: Duplicate suggestion detected');
|
||||
wp_send_json_error(array('message' => __('You have already submitted a similar suggestion recently.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
if (!PC_Community_Suggestions_Database::verify_tables()) {
|
||||
error_log('PC Community Suggestions: Tables do not exist, creating...');
|
||||
PC_Community_Suggestions_Database::create_tables();
|
||||
}
|
||||
|
||||
$suggestion_id = PC_Community_Suggestions_Database::add_suggestion($user_id, $title, $content);
|
||||
|
||||
if (!$suggestion_id) {
|
||||
global $wpdb;
|
||||
$last_error = $wpdb->last_error;
|
||||
error_log('PC Community Suggestions DB Error: ' . $last_error);
|
||||
wp_send_json_error(array('message' => __('Failed to submit suggestion. Database error.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
error_log('PC Community Suggestions: Suggestion created successfully with ID: ' . $suggestion_id);
|
||||
wp_send_json_success(array(
|
||||
'message' => __('Your suggestion has been published successfully.', 'pc-community-suggestions-7d3f')
|
||||
));
|
||||
} catch (Exception $e) {
|
||||
error_log('PC Community Suggestions Exception: ' . $e->getMessage());
|
||||
wp_send_json_error(array('message' => __('An error occurred: ', 'pc-community-suggestions-7d3f') . $e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
public function handle_vote() {
|
||||
if (!check_ajax_referer('pc_community_suggestions_nonce', 'nonce', false)) {
|
||||
wp_send_json_error(array('message' => __('Security check failed. Please try again.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
if (!is_user_logged_in()) {
|
||||
wp_send_json_error(array('message' => __('You must be logged in to vote.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
$suggestion_id = isset($_POST['suggestion_id']) ? intval($_POST['suggestion_id']) : 0;
|
||||
$user_id = get_current_user_id();
|
||||
|
||||
if ($suggestion_id <= 0) {
|
||||
wp_send_json_error(array('message' => __('Invalid suggestion.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
$result = PC_Community_Suggestions_Database::add_vote($suggestion_id, $user_id);
|
||||
|
||||
if (!$result) {
|
||||
wp_send_json_error(array('message' => __('You have already voted for this suggestion.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
wp_send_json_success(array('message' => __('Vote recorded successfully.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
public function handle_not_logged_in() {
|
||||
wp_send_json_error(array('message' => __('You must be logged in to perform this action.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
public function handle_get_suggestion() {
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error(array('message' => __('You do not have permission to view this suggestion.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
if (!check_ajax_referer('pc_admin_nonce', 'nonce', false)) {
|
||||
wp_send_json_error(array('message' => __('Security check failed. Please try again.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
$suggestion_id = isset($_POST['suggestion_id']) ? intval($_POST['suggestion_id']) : 0;
|
||||
|
||||
if ($suggestion_id <= 0) {
|
||||
wp_send_json_error(array('message' => __('Invalid suggestion ID.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
$table_name = $wpdb->prefix . 'pc_community_suggestions';
|
||||
|
||||
$suggestion = $wpdb->get_row(
|
||||
$wpdb->prepare(
|
||||
"SELECT s.*, u.user_login, u.display_name
|
||||
FROM $table_name s
|
||||
LEFT JOIN {$wpdb->users} u ON s.user_id = u.ID
|
||||
WHERE s.id = %d",
|
||||
$suggestion_id
|
||||
)
|
||||
);
|
||||
|
||||
if (!$suggestion) {
|
||||
wp_send_json_error(array('message' => __('Suggestion not found.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
wp_send_json_success(array(
|
||||
'id' => $suggestion->id,
|
||||
'title' => $suggestion->title,
|
||||
'content' => wpautop($suggestion->content),
|
||||
'author' => $suggestion->display_name ?: $suggestion->user_login,
|
||||
'date' => date_i18n(get_option('date_format'), strtotime($suggestion->created_at)),
|
||||
'upvotes' => $suggestion->upvotes,
|
||||
));
|
||||
}
|
||||
|
||||
public function handle_get_stats() {
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error(array('message' => __('You do not have permission to view statistics.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
if (!check_ajax_referer('pc_admin_nonce', 'nonce', false)) {
|
||||
wp_send_json_error(array('message' => __('Security check failed. Please try again.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
wp_send_json_success(array(
|
||||
'total' => PC_Community_Suggestions_Database::get_suggestion_count()
|
||||
));
|
||||
}
|
||||
|
||||
public function handle_add_comment() {
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error(array('message' => __('You do not have permission to add comments.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
if (!check_ajax_referer('pc_admin_nonce', 'nonce', false)) {
|
||||
wp_send_json_error(array('message' => __('Security check failed. Please try again.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
$suggestion_id = isset($_POST['suggestion_id']) ? intval($_POST['suggestion_id']) : 0;
|
||||
$comment = isset($_POST['comment']) ? wp_kses_post($_POST['comment']) : '';
|
||||
$admin_id = get_current_user_id();
|
||||
|
||||
if ($suggestion_id <= 0 || empty($comment)) {
|
||||
wp_send_json_error(array('message' => __('Invalid data provided.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
$comment_id = PC_Community_Suggestions_Database::add_comment($suggestion_id, $admin_id, $comment);
|
||||
|
||||
if (!$comment_id) {
|
||||
wp_send_json_error(array('message' => __('Failed to add comment.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
$admin = get_userdata($admin_id);
|
||||
$admin_name = $admin->display_name ?: $admin->user_login;
|
||||
|
||||
wp_send_json_success(array(
|
||||
'message' => __('Comment added successfully.', 'pc-community-suggestions-7d3f'),
|
||||
'comment' => array(
|
||||
'id' => $comment_id,
|
||||
'content' => wpautop($comment),
|
||||
'author' => $admin_name,
|
||||
'date' => date_i18n(get_option('date_format'), current_time('timestamp'))
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
public function handle_delete_comment() {
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error(array('message' => __('You do not have permission to delete comments.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
if (!check_ajax_referer('pc_admin_nonce', 'nonce', false)) {
|
||||
wp_send_json_error(array('message' => __('Security check failed. Please try again.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
$comment_id = isset($_POST['comment_id']) ? intval($_POST['comment_id']) : 0;
|
||||
|
||||
if ($comment_id <= 0) {
|
||||
wp_send_json_error(array('message' => __('Invalid comment ID.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
$result = PC_Community_Suggestions_Database::delete_comment($comment_id);
|
||||
|
||||
if (!$result) {
|
||||
wp_send_json_error(array('message' => __('Failed to delete comment.', 'pc-community-suggestions-7d3f')));
|
||||
}
|
||||
|
||||
wp_send_json_success(array(
|
||||
'message' => __('Comment deleted successfully.', 'pc-community-suggestions-7d3f')
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
class PC_Community_Suggestions_Shortcodes {
|
||||
|
||||
public function __construct() {
|
||||
add_shortcode('community_suggestions', array($this, 'render_suggestions_page'));
|
||||
}
|
||||
|
||||
public function render_suggestions_page($atts) {
|
||||
$current_suggestion = isset($_GET['suggestion']) ? intval($_GET['suggestion']) : 0;
|
||||
|
||||
ob_start();
|
||||
|
||||
if ($current_suggestion > 0) {
|
||||
$this->render_suggestion_detail($current_suggestion);
|
||||
} else {
|
||||
$this->render_suggestions_interface();
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
private function render_suggestion_detail($suggestion_id) {
|
||||
$suggestion = PC_Community_Suggestions_Database::get_suggestion_by_id($suggestion_id);
|
||||
$comments = $suggestion ? PC_Community_Suggestions_Database::get_comments($suggestion_id) : array();
|
||||
|
||||
if (!$suggestion) {
|
||||
echo '<div class="pc-suggestions-card">';
|
||||
echo '<p>' . esc_html__('Suggestion not found.', 'pc-community-suggestions-7d3f') . '</p>';
|
||||
echo '<a href="' . esc_url(remove_query_arg('suggestion')) . '" class="pc-button">' . esc_html__('Back to Suggestions', 'pc-community-suggestions-7d3f') . '</a>';
|
||||
echo '</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="pc-suggestions-container">
|
||||
<div class="pc-suggestions-header">
|
||||
<a href="<?php echo esc_url(remove_query_arg('suggestion')); ?>" class="pc-back-link">
|
||||
← <?php esc_html_e('Back to Suggestions', 'pc-community-suggestions-7d3f'); ?>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="pc-suggestion-detail">
|
||||
<?php $this->render_suggestion_card($suggestion, true); ?>
|
||||
|
||||
<div class="pc-comments-section">
|
||||
<h3><?php esc_html_e('Admin Comments', 'pc-community-suggestions-7d3f'); ?></h3>
|
||||
|
||||
<?php if (empty($comments)) : ?>
|
||||
<p class="pc-no-comments"><?php esc_html_e('No admin comments yet.', 'pc-community-suggestions-7d3f'); ?></p>
|
||||
<?php else : ?>
|
||||
<?php foreach ($comments as $comment) : ?>
|
||||
<div class="pc-comment-card">
|
||||
<div class="pc-comment-header">
|
||||
<span class="pc-comment-author"><?php echo esc_html($comment->display_name ?: $comment->user_login); ?></span>
|
||||
<span class="pc-comment-date">
|
||||
<?php echo date_i18n(get_option('date_format'), strtotime($comment->created_at)); ?>
|
||||
</span>
|
||||
</div>
|
||||
<div class="pc-comment-content">
|
||||
<?php echo wp_kses_post(wpautop($comment->comment)); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function render_suggestions_interface() {
|
||||
$current_page = max(1, get_query_var('paged', 1));
|
||||
$sort = isset($_GET['sort']) && in_array($_GET['sort'], ['popular', 'newest']) ? $_GET['sort'] : 'popular';
|
||||
$per_page = 10;
|
||||
|
||||
$suggestions = PC_Community_Suggestions_Database::get_suggestions($current_page, $per_page, $sort);
|
||||
$total_suggestions = PC_Community_Suggestions_Database::get_suggestion_count();
|
||||
$total_pages = ceil($total_suggestions / $per_page);
|
||||
|
||||
?>
|
||||
<div class="pc-community-suggestions-container">
|
||||
<div class="pc-suggestions-header">
|
||||
<div class="pc-suggestions-controls">
|
||||
<?php if (is_user_logged_in()) : ?>
|
||||
<button class="pc-button pc-button-primary" onclick="pcToggleSuggestionForm()">
|
||||
<?php esc_html_e('Add Suggestion', 'pc-community-suggestions-7d3f'); ?>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
<div class="pc-sort-dropdown">
|
||||
<select onchange="pcChangeSort(this.value)">
|
||||
<option value="popular" <?php selected($sort, 'popular'); ?>><?php esc_html_e('Most Upvoted', 'pc-community-suggestions-7d3f'); ?></option>
|
||||
<option value="newest" <?php selected($sort, 'newest'); ?>><?php esc_html_e('Newest', 'pc-community-suggestions-7d3f'); ?></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (is_user_logged_in()) : ?>
|
||||
<div id="pc-suggestion-form" class="pc-suggestion-form" style="display: none;">
|
||||
<div class="pc-form-card">
|
||||
<h3><?php esc_html_e('Add Your Suggestion', 'pc-community-suggestions-7d3f'); ?></h3>
|
||||
<form id="pc-new-suggestion-form">
|
||||
<div class="pc-form-group">
|
||||
<label for="pc-suggestion-title"><?php esc_html_e('Title', 'pc-community-suggestions-7d3f'); ?></label>
|
||||
<input type="text" id="pc-suggestion-title" name="title" required maxlength="255">
|
||||
</div>
|
||||
<div class="pc-form-group">
|
||||
<label for="pc-suggestion-content"><?php esc_html_e('Details', 'pc-community-suggestions-7d3f'); ?></label>
|
||||
<textarea id="pc-suggestion-content" name="content" rows="5" required></textarea>
|
||||
</div>
|
||||
<div class="pc-form-actions">
|
||||
<button type="button" class="pc-button" onclick="pcToggleSuggestionForm()">
|
||||
<?php esc_html_e('Cancel', 'pc-community-suggestions-7d3f'); ?>
|
||||
</button>
|
||||
<button type="submit" class="pc-button pc-button-primary">
|
||||
<?php esc_html_e('Submit Suggestion', 'pc-community-suggestions-7d3f'); ?>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="pc-suggestions-list">
|
||||
<?php if (empty($suggestions)) : ?>
|
||||
<div class="pc-no-suggestions">
|
||||
<p><?php esc_html_e('No suggestions yet. Be the first to share your idea!', 'pc-community-suggestions-7d3f'); ?></p>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<?php foreach ($suggestions as $suggestion) : ?>
|
||||
<?php $this->render_suggestion_card($suggestion); ?>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if ($total_pages > 1) : ?>
|
||||
<div class="pc-suggestions-pagination">
|
||||
<?php
|
||||
echo paginate_links(array(
|
||||
'base' => get_pagenum_link(1) . '%_%',
|
||||
'format' => 'page/%#%/',
|
||||
'current' => $current_page,
|
||||
'total' => $total_pages,
|
||||
'prev_text' => __('« Previous', 'pc-community-suggestions-7d3f'),
|
||||
'next_text' => __('Next »', 'pc-community-suggestions-7d3f'),
|
||||
'add_args' => array('sort' => $sort)
|
||||
));
|
||||
?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
private function render_suggestion_card($suggestion, $detail_view = false) {
|
||||
$user_has_voted = is_user_logged_in() ? PC_Community_Suggestions_Database::has_user_voted($suggestion->id, get_current_user_id()) : false;
|
||||
$vote_class = $user_has_voted ? 'pc-voted' : '';
|
||||
|
||||
if ($detail_view) {
|
||||
$vote_button_html = '';
|
||||
} else {
|
||||
$vote_button_html = 'onclick="pcVoteSuggestion(' . esc_attr($suggestion->id) . ')"';
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="pc-suggestion-card <?php echo $detail_view ? 'pc-suggestion-card-detail' : ''; ?>" data-suggestion-id="<?php echo esc_attr($suggestion->id); ?>">
|
||||
<div class="pc-suggestion-vote">
|
||||
<?php if (is_user_logged_in()) : ?>
|
||||
<button class="pc-vote-button <?php echo esc_attr($vote_class); ?>"
|
||||
<?php echo $vote_button_html; ?>
|
||||
<?php echo $user_has_voted || $detail_view ? 'disabled' : ''; ?>>
|
||||
<span class="pc-vote-arrow">↑</span>
|
||||
<span class="pc-vote-count"><?php echo esc_html($suggestion->upvotes); ?></span>
|
||||
</button>
|
||||
<?php else : ?>
|
||||
<div class="pc-vote-button pc-vote-disabled">
|
||||
<span class="pc-vote-arrow">↑</span>
|
||||
<span class="pc-vote-count"><?php echo esc_html($suggestion->upvotes); ?></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="pc-suggestion-content">
|
||||
<h3 class="pc-suggestion-title">
|
||||
<?php if (!$detail_view) : ?>
|
||||
<a href="<?php echo esc_url(add_query_arg('suggestion', $suggestion->id, get_permalink())); ?>">
|
||||
<?php echo esc_html($suggestion->title); ?>
|
||||
</a>
|
||||
<?php else : ?>
|
||||
<?php echo esc_html($suggestion->title); ?>
|
||||
<?php endif; ?>
|
||||
</h3>
|
||||
<div class="pc-suggestion-body">
|
||||
<?php
|
||||
if ($detail_view) {
|
||||
echo wp_kses_post(wpautop($suggestion->content));
|
||||
} else {
|
||||
echo wp_kses_post(wpautop(wp_trim_words($suggestion->content, 30, '...')));
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
|
||||
<?php if (!$detail_view) : ?>
|
||||
<div class="pc-suggestion-actions">
|
||||
<a href="<?php echo esc_url(add_query_arg('suggestion', $suggestion->id, get_permalink())); ?>"
|
||||
class="pc-button pc-button-small">
|
||||
<?php esc_html_e('View Details', 'pc-community-suggestions-7d3f'); ?>
|
||||
</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="pc-suggestion-meta">
|
||||
<span class="pc-suggestion-author">
|
||||
<?php
|
||||
printf(
|
||||
__('By %s', 'pc-community-suggestions-7d3f'),
|
||||
esc_html($suggestion->display_name ?: $suggestion->user_login)
|
||||
);
|
||||
?>
|
||||
</span>
|
||||
<span class="pc-suggestion-date">
|
||||
<?php echo human_time_diff(strtotime($suggestion->created_at), current_time('timestamp')) . ' ' . __('ago', 'pc-community-suggestions-7d3f'); ?>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: Plugin Compass Community suggestions
|
||||
* Plugin URI: https://plugincompass.com/plugins/pc-community-suggestions-7d3f
|
||||
* Description: A community suggestions system where logged-in users can create and upvote improvement suggestions.
|
||||
* Version: 1.0.0
|
||||
* Author: Plugin Compass
|
||||
* Author URI: https://plugincompass.com
|
||||
* Text Domain: pc-community-suggestions-7d3f
|
||||
* Domain Path: /languages
|
||||
* Update URI: https://plugincompass.com/plugins/pc-community-suggestions-7d3f
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Define plugin constants
|
||||
define('PC_COMMUNITY_SUGGESTIONS_VERSION', '1.0.0');
|
||||
define('PC_COMMUNITY_SUGGESTIONS_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
||||
define('PC_COMMUNITY_SUGGESTIONS_PLUGIN_URL', plugin_dir_url(__FILE__));
|
||||
define('PC_COMMUNITY_SUGGESTIONS_PLUGIN_BASENAME', plugin_basename(__FILE__));
|
||||
|
||||
// Prevent WordPress.org update checks
|
||||
add_filter('site_transient_update_plugins', function($value) {
|
||||
$plugin_file = PC_COMMUNITY_SUGGESTIONS_PLUGIN_BASENAME;
|
||||
if (isset($value->response[$plugin_file])) {
|
||||
unset($value->response[$plugin_file]);
|
||||
}
|
||||
return $value;
|
||||
});
|
||||
|
||||
// Activation hook
|
||||
register_activation_hook(__FILE__, 'pc_community_suggestions_activate');
|
||||
function pc_community_suggestions_activate() {
|
||||
require_once PC_COMMUNITY_SUGGESTIONS_PLUGIN_DIR . 'includes/class-database.php';
|
||||
PC_Community_Suggestions_Database::create_tables();
|
||||
|
||||
// Set default options
|
||||
add_option('pc_community_suggestions_default_sort', 'popular');
|
||||
add_option('pc_community_suggestions_page_id', '0');
|
||||
add_option('pc_community_suggestions_page_created', false);
|
||||
|
||||
// Create default page immediately
|
||||
pc_community_suggestions_create_default_page();
|
||||
|
||||
// Flush rewrite rules to ensure URL routing works
|
||||
flush_rewrite_rules();
|
||||
}
|
||||
|
||||
// Deactivation hook
|
||||
register_deactivation_hook(__FILE__, 'pc_community_suggestions_deactivate');
|
||||
function pc_community_suggestions_deactivate() {
|
||||
// Cleanup on deactivation if needed
|
||||
}
|
||||
|
||||
// Create default suggestions page
|
||||
function pc_community_suggestions_create_default_page() {
|
||||
// Check if page already exists
|
||||
$existing_page = get_page_by_path('suggestions');
|
||||
|
||||
if ($existing_page) {
|
||||
update_option('pc_community_suggestions_page_id', $existing_page->ID);
|
||||
return;
|
||||
}
|
||||
|
||||
$page_content = '<!-- wp:shortcode -->[community_suggestions]<!-- /wp:shortcode -->';
|
||||
|
||||
$page_data = array(
|
||||
'post_title' => __('Community Suggestions', 'pc-community-suggestions-7d3f'),
|
||||
'post_content' => $page_content,
|
||||
'post_status' => 'publish',
|
||||
'post_type' => 'page',
|
||||
'post_name' => 'suggestions',
|
||||
'post_author' => 1 // Set to admin user
|
||||
);
|
||||
|
||||
$page_id = wp_insert_post($page_data);
|
||||
|
||||
if ($page_id && !is_wp_error($page_id)) {
|
||||
update_option('pc_community_suggestions_page_id', $page_id);
|
||||
|
||||
// Flush rewrite rules to ensure the URL works immediately
|
||||
flush_rewrite_rules();
|
||||
|
||||
// Also set this page as the option for our plugin
|
||||
update_option('pc_community_suggestions_page_created', true);
|
||||
}
|
||||
}
|
||||
|
||||
// Add rewrite rule for /suggestions URL
|
||||
function pc_community_suggestions_add_rewrite_rule() {
|
||||
add_rewrite_rule(
|
||||
'^suggestions/?$',
|
||||
'index.php?pagename=suggestions',
|
||||
'top'
|
||||
);
|
||||
}
|
||||
add_action('init', 'pc_community_suggestions_add_rewrite_rule');
|
||||
|
||||
// Handle template redirect for suggestions page
|
||||
function pc_community_suggestions_template_include($template) {
|
||||
if (get_query_var('pagename') === 'suggestions') {
|
||||
// Check if the page exists, if not create it
|
||||
$page_id = get_option('pc_community_suggestions_page_id');
|
||||
if (!$page_id || !get_post($page_id)) {
|
||||
pc_community_suggestions_create_default_page();
|
||||
}
|
||||
|
||||
// Use the page template if it exists
|
||||
$page_template = locate_template(array('page-suggestions.php', 'page.php', 'singular.php'));
|
||||
if ($page_template) {
|
||||
return $page_template;
|
||||
}
|
||||
}
|
||||
return $template;
|
||||
}
|
||||
add_filter('template_include', 'pc_community_suggestions_template_include');
|
||||
|
||||
// Query var for suggestions page
|
||||
function pc_community_suggestions_query_vars($query_vars) {
|
||||
$query_vars[] = 'pagename';
|
||||
return $query_vars;
|
||||
}
|
||||
add_filter('query_vars', 'pc_community_suggestions_query_vars');
|
||||
|
||||
// Handle 404 for suggestions page and create it if needed
|
||||
function pc_community_suggestions_handle_404() {
|
||||
if (is_404()) {
|
||||
$requested_url = $_SERVER['REQUEST_URI'];
|
||||
|
||||
// Check if this is a request for /suggestions/
|
||||
if (preg_match('/^\/suggestions\/?$/', $requested_url) || strpos($requested_url, '/suggestions') !== false) {
|
||||
// Check if page exists
|
||||
$page_id = get_option('pc_community_suggestions_page_id');
|
||||
if (!$page_id || !get_post($page_id)) {
|
||||
// Create the page
|
||||
pc_community_suggestions_create_default_page();
|
||||
|
||||
// Redirect to the newly created page
|
||||
$suggestions_url = home_url('/suggestions/');
|
||||
wp_redirect($suggestions_url);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
add_action('template_redirect', 'pc_community_suggestions_handle_404', 1);
|
||||
|
||||
// Fallback handler - if no page is found for /suggestions, render our content
|
||||
function pc_community_suggestions_fallback_handler() {
|
||||
if (is_404()) {
|
||||
$requested_path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
||||
|
||||
if ($requested_path === '/suggestions/' || $requested_path === '/suggestions') {
|
||||
status_header(200);
|
||||
|
||||
// Render the suggestions content directly
|
||||
$shortcode = new PC_Community_Suggestions_Shortcodes();
|
||||
echo $shortcode->render_suggestions_page();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
add_action('template_redirect', 'pc_community_suggestions_fallback_handler', 999);
|
||||
|
||||
// Load plugin classes
|
||||
function pc_community_suggestions_init() {
|
||||
// Include required files
|
||||
require_once PC_COMMUNITY_SUGGESTIONS_PLUGIN_DIR . 'includes/class-database.php';
|
||||
require_once PC_COMMUNITY_SUGGESTIONS_PLUGIN_DIR . 'includes/class-shortcodes.php';
|
||||
require_once PC_COMMUNITY_SUGGESTIONS_PLUGIN_DIR . 'includes/class-rest-api.php';
|
||||
|
||||
// Initialize REST API early to ensure AJAX handlers are registered
|
||||
$rest_api = new PC_Community_Suggestions_REST_API();
|
||||
|
||||
// Initialize shortcodes
|
||||
new PC_Community_Suggestions_Shortcodes();
|
||||
|
||||
if (is_admin()) {
|
||||
require_once PC_COMMUNITY_SUGGESTIONS_PLUGIN_DIR . 'admin/class-admin.php';
|
||||
new PC_Community_Suggestions_Admin();
|
||||
}
|
||||
|
||||
// Ensure suggestions page exists on every load (safety check)
|
||||
$page_id = get_option('pc_community_suggestions_page_id');
|
||||
if (!$page_id || !get_post($page_id)) {
|
||||
pc_community_suggestions_create_default_page();
|
||||
}
|
||||
}
|
||||
add_action('plugins_loaded', 'pc_community_suggestions_init', 5);
|
||||
|
||||
// Enqueue scripts and styles
|
||||
function pc_community_suggestions_enqueue_scripts() {
|
||||
// Frontend styles
|
||||
wp_enqueue_style(
|
||||
'pc-community-suggestions-frontend',
|
||||
PC_COMMUNITY_SUGGESTIONS_PLUGIN_URL . 'public/css/public-style.css',
|
||||
array(),
|
||||
PC_COMMUNITY_SUGGESTIONS_VERSION
|
||||
);
|
||||
|
||||
// Frontend scripts
|
||||
wp_enqueue_script(
|
||||
'pc-community-suggestions-frontend',
|
||||
PC_COMMUNITY_SUGGESTIONS_PLUGIN_URL . 'public/js/public-script.js',
|
||||
array('jquery'),
|
||||
PC_COMMUNITY_SUGGESTIONS_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
// Localize script for AJAX
|
||||
wp_localize_script('pc-community-suggestions-frontend', 'pcCommunitySuggestions', array(
|
||||
'ajax_url' => admin_url('admin-ajax.php'),
|
||||
'nonce' => wp_create_nonce('pc_community_suggestions_nonce'),
|
||||
'is_user_logged_in' => is_user_logged_in(),
|
||||
'i18n' => array(
|
||||
'fill_all_fields' => __('Please fill in all required fields.', 'pc-community-suggestions-7d3f'),
|
||||
'title_too_long' => __('Title must be less than 255 characters.', 'pc-community-suggestions-7d3f'),
|
||||
'login_to_vote' => __('You must be logged in to vote.', 'pc-community-suggestions-7d3f'),
|
||||
'login_to_submit' => __('You must be logged in to submit suggestions.', 'pc-community-suggestions-7d3f'),
|
||||
'ajax_error' => __('An error occurred. Please try again.', 'pc-community-suggestions-7d3f')
|
||||
)
|
||||
));
|
||||
}
|
||||
add_action('wp_enqueue_scripts', 'pc_community_suggestions_enqueue_scripts');
|
||||
|
||||
// Admin enqueue
|
||||
function pc_community_suggestions_admin_enqueue_scripts($hook) {
|
||||
if (strpos($hook, 'pc_community_suggestions') !== false) {
|
||||
wp_enqueue_style(
|
||||
'pc-community-suggestions-admin',
|
||||
PC_COMMUNITY_SUGGESTIONS_PLUGIN_URL . 'admin/css/admin-style.css',
|
||||
array(),
|
||||
PC_COMMUNITY_SUGGESTIONS_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'pc-community-suggestions-admin',
|
||||
PC_COMMUNITY_SUGGESTIONS_PLUGIN_URL . 'admin/js/admin-script.js',
|
||||
array('jquery'),
|
||||
PC_COMMUNITY_SUGGESTIONS_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
// Localize admin script
|
||||
wp_localize_script('pc-community-suggestions-admin', 'pcCommunitySuggestionsAdmin', array(
|
||||
'nonce' => wp_create_nonce('pc_admin_nonce'),
|
||||
'ajax_url' => admin_url('admin-ajax.php'),
|
||||
'i18n' => array(
|
||||
'confirm_bulk_action' => __('Are you sure you want to perform this action on the selected suggestions?', 'pc-community-suggestions-7d3f'),
|
||||
'no_items_selected' => __('Please select at least one suggestion.', 'pc-community-suggestions-7d3f'),
|
||||
'action_failed' => __('The action failed. Please try again.', 'pc-community-suggestions-7d3f'),
|
||||
'loading' => __('Loading...', 'pc-community-suggestions-7d3f'),
|
||||
'processing' => __('Processing...', 'pc-community-suggestions-7d3f')
|
||||
)
|
||||
));
|
||||
}
|
||||
}
|
||||
add_action('admin_enqueue_scripts', 'pc_community_suggestions_admin_enqueue_scripts');
|
||||
|
||||
// Add plugin action links
|
||||
function pc_community_suggestions_action_links($links) {
|
||||
$settings_link = '<a href="' . admin_url('admin.php?page=pc_community_suggestions_settings') . '">' . __('Settings', 'pc-community-suggestions-7d3f') . '</a>';
|
||||
array_unshift($links, $settings_link);
|
||||
return $links;
|
||||
}
|
||||
add_filter('plugin_action_links_' . PC_COMMUNITY_SUGGESTIONS_PLUGIN_BASENAME, 'pc_community_suggestions_action_links');
|
||||
|
||||
// Load textdomain
|
||||
function pc_community_suggestions_load_textdomain() {
|
||||
load_plugin_textdomain('pc-community-suggestions-7d3f', false, dirname(PC_COMMUNITY_SUGGESTIONS_PLUGIN_BASENAME) . '/languages');
|
||||
}
|
||||
add_action('plugins_loaded', 'pc_community_suggestions_load_textdomain');
|
||||
|
||||
// Manual page creation trigger (for debugging)
|
||||
function pc_community_suggestions_manual_create_page() {
|
||||
if (isset($_GET['pc_create_suggestions_page']) && current_user_can('manage_options')) {
|
||||
check_admin_referer('pc_create_page_nonce');
|
||||
|
||||
pc_community_suggestions_create_default_page();
|
||||
flush_rewrite_rules();
|
||||
|
||||
$page_id = get_option('pc_community_suggestions_page_id');
|
||||
$page_url = get_permalink($page_id);
|
||||
|
||||
wp_redirect(admin_url('admin.php?page=pc_community_suggestions_settings&created=1&url=' . urlencode($page_url)));
|
||||
exit;
|
||||
}
|
||||
}
|
||||
add_action('admin_init', 'pc_community_suggestions_manual_create_page');
|
||||
|
||||
// Note: Do not require the uninstall file here. WordPress will include `uninstall.php` when the plugin is uninstalled.
|
||||
// The uninstall logic is self-contained in `uninstall.php` and should not run during normal plugin load.
|
||||
752
chat/templates/Community Suggestions/public/css/public-style.css
Normal file
752
chat/templates/Community Suggestions/public/css/public-style.css
Normal file
@@ -0,0 +1,752 @@
|
||||
/* Community Suggestions Frontend Styles */
|
||||
.pc-community-suggestions-container {
|
||||
/* Palette (used throughout the plugin to avoid inheriting theme styles) */
|
||||
--pc-primary: #2563eb; /* main blue */
|
||||
--pc-primary-strong: #1e40af;
|
||||
--pc-accent: #06b6d4;
|
||||
--pc-success: #059669;
|
||||
--pc-muted: #64748b;
|
||||
--pc-text: #000000; /* make body text pure black to stand out */
|
||||
--pc-heading: #000000; /* headings black for emphasis */
|
||||
--pc-card-bg: #ffffff; /* keep cards white */
|
||||
--pc-card-border: #e6f2fb;
|
||||
--pc-input-bg: #ffffff;
|
||||
--pc-card-shadow: 0 6px 20px rgba(15, 23, 42, 0.06);
|
||||
--pc-radius: 12px;
|
||||
|
||||
max-width: 880px;
|
||||
margin: 0 auto;
|
||||
padding: 30px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Enforce plugin palette with higher specificity so theme defaults don't leak in */
|
||||
.pc-community-suggestions-container .pc-button-primary {
|
||||
background: var(--pc-primary) !important;
|
||||
border-color: var(--pc-primary) !important;
|
||||
color: #fff !important;
|
||||
box-shadow: 0 6px 18px rgba(37,99,235,0.12);
|
||||
}
|
||||
|
||||
.pc-community-suggestions-container .pc-button-primary:hover {
|
||||
background: var(--pc-primary-strong) !important;
|
||||
border-color: var(--pc-primary-strong) !important;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.pc-community-suggestions-container .pc-button-secondary {
|
||||
background: #f1f5f9;
|
||||
color: var(--pc-text);
|
||||
border-color: #e2e8f0;
|
||||
}
|
||||
|
||||
.pc-community-suggestions-container .pc-suggestion-card {
|
||||
background: var(--pc-card-bg);
|
||||
border: 1px solid var(--pc-card-border);
|
||||
box-shadow: var(--pc-card-shadow);
|
||||
border-radius: var(--pc-radius);
|
||||
padding: 22px;
|
||||
}
|
||||
|
||||
.pc-community-suggestions-container .pc-form-card {
|
||||
background: var(--pc-card-bg) !important;
|
||||
border: 1px solid var(--pc-card-border);
|
||||
box-shadow: var(--pc-card-shadow);
|
||||
border-radius: calc(var(--pc-radius) - 2px);
|
||||
}
|
||||
|
||||
/* Inputs */
|
||||
.pc-community-suggestions-container .pc-form-group input,
|
||||
.pc-community-suggestions-container .pc-form-group textarea,
|
||||
.pc-community-suggestions-container .pc-sort-dropdown select {
|
||||
background: var(--pc-input-bg);
|
||||
border-color: #e6eef8;
|
||||
color: var(--pc-text);
|
||||
}
|
||||
|
||||
/* Vote button */
|
||||
.pc-community-suggestions-container .pc-vote-button {
|
||||
border-color: var(--pc-success);
|
||||
background: linear-gradient(180deg, #d1fae5 0%, #a7f3d0 100%);
|
||||
box-shadow: 0 6px 12px rgba(5,150,105,0.12);
|
||||
border-radius: 10px;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
}
|
||||
|
||||
.pc-community-suggestions-container .pc-vote-button:hover:not(:disabled) {
|
||||
transform: scale(1.04);
|
||||
}
|
||||
|
||||
/* Headings and text */
|
||||
.pc-community-suggestions-container .pc-suggestions-header h2 { color: var(--pc-heading); }
|
||||
.pc-community-suggestions-container .pc-suggestion-title { color: var(--pc-heading); }
|
||||
.pc-community-suggestions-container .pc-suggestion-body { color: var(--pc-text); }
|
||||
|
||||
/* Links */
|
||||
.pc-community-suggestions-container .pc-back-link { color: var(--pc-primary); }
|
||||
.pc-community-suggestions-container .pc-back-link:hover { color: var(--pc-primary-strong); }
|
||||
|
||||
|
||||
/* Apply box-sizing to all elements */
|
||||
.pc-community-suggestions-container *,
|
||||
.pc-community-suggestions-container *::before,
|
||||
.pc-community-suggestions-container *::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.pc-suggestions-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.pc-suggestions-header h2 {
|
||||
font-size: 2em;
|
||||
font-weight: 700;
|
||||
color: #1e3a5f;
|
||||
margin: 0;
|
||||
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.pc-suggestions-controls {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pc-sort-dropdown select {
|
||||
padding: 10px 15px;
|
||||
border: 2px solid #8b5cf6;
|
||||
border-radius: 8px;
|
||||
background: #faf5ff;
|
||||
font-size: 14px;
|
||||
color: #1e0533;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.pc-sort-dropdown select:focus {
|
||||
outline: none;
|
||||
border-color: #7c3aed;
|
||||
box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.3);
|
||||
}
|
||||
|
||||
/* Button styles */
|
||||
.pc-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 12px 24px;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease-in-out;
|
||||
min-height: 44px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.pc-button-primary {
|
||||
background: #3b82f6;
|
||||
color: #fff;
|
||||
border-color: #3b82f6;
|
||||
}
|
||||
|
||||
.pc-button-primary:hover {
|
||||
background: #2563eb;
|
||||
border-color: #2563eb;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
.pc-button-primary:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: 0 2px 6px rgba(34, 113, 177, 0.2);
|
||||
}
|
||||
|
||||
/* Suggestion form */
|
||||
.pc-suggestion-form {
|
||||
margin-bottom: 30px;
|
||||
animation: pc-slide-down 0.3s ease-out;
|
||||
}
|
||||
|
||||
.pc-form-card {
|
||||
background: linear-gradient(135deg, #ffffff 0%, #fef3c7 100%);
|
||||
border: 2px solid #f59e0b;
|
||||
border-radius: 12px;
|
||||
padding: 25px;
|
||||
box-shadow: 0 4px 15px rgba(245, 158, 11, 0.15);
|
||||
}
|
||||
|
||||
.pc-form-card h3 {
|
||||
margin: 0 0 20px 0;
|
||||
font-size: 1.3em;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.pc-form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.pc-form-group label {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
color: #0f172a;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.pc-form-group input,
|
||||
.pc-form-group textarea {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
transition: all 0.3s ease-in-out;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.pc-form-group input:focus,
|
||||
.pc-form-group textarea:focus {
|
||||
outline: none;
|
||||
border-color: #3b82f6;
|
||||
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.15);
|
||||
background: linear-gradient(135deg, #ffffff 0%, #eff6ff 100%);
|
||||
}
|
||||
|
||||
.pc-form-group textarea {
|
||||
min-height: 120px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.pc-form-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* Suggestions list */
|
||||
.pc-suggestions-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.pc-no-suggestions {
|
||||
text-align: center;
|
||||
padding: 40px 20px;
|
||||
color: #ef4444;
|
||||
font-style: italic;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
/* Suggestion card */
|
||||
.pc-suggestion-card {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
background: var(--pc-card-bg) !important;
|
||||
border: 1px solid var(--pc-card-border) !important;
|
||||
border-radius: 16px;
|
||||
padding: 20px;
|
||||
box-shadow: var(--pc-card-shadow);
|
||||
transition: all 0.3s ease-in-out;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pc-suggestion-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 18px 40px rgba(15, 23, 42, 0.12);
|
||||
border-color: var(--pc-card-border);
|
||||
background: var(--pc-card-bg) !important; /* keep background white */
|
||||
}
|
||||
|
||||
.pc-suggestion-card.pc-suggestion-hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
||||
border-color: #c3c4c7;
|
||||
}
|
||||
|
||||
.pc-suggestion-vote {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.pc-vote-button {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 2px solid #10b981;
|
||||
border-radius: 10px;
|
||||
background: linear-gradient(135deg, #d1fae5 0%, #a7f3d0 100%);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease-in-out;
|
||||
padding: 0;
|
||||
box-shadow: 0 2px 8px rgba(16, 185, 129, 0.2);
|
||||
}
|
||||
|
||||
.pc-vote-button:hover:not(:disabled) {
|
||||
border-color: #059669;
|
||||
background: linear-gradient(135deg, #a7f3d0 0%, #6ee7b7 100%);
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);
|
||||
}
|
||||
|
||||
.pc-vote-button.pc-voted {
|
||||
background: linear-gradient(135deg, #059669 0%, #047857 100%);
|
||||
border-color: #047857;
|
||||
color: #ffffff;
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 4px 12px rgba(5, 150, 105, 0.4);
|
||||
}
|
||||
|
||||
.pc-vote-button:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.pc-vote-button.pc-vote-disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.pc-vote-arrow {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.pc-vote-count {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #2c3338;
|
||||
}
|
||||
|
||||
.pc-vote-button.pc-voted .pc-vote-count {
|
||||
color: #2a6b2a;
|
||||
}
|
||||
|
||||
.pc-suggestion-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.pc-suggestion-title {
|
||||
font-size: 1.2em;
|
||||
font-weight: 600;
|
||||
margin: 0 0 12px 0;
|
||||
color: #000000 !important; /* enforce black heading */
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.pc-suggestion-body {
|
||||
margin-bottom: 16px;
|
||||
color: #000000 !important; /* enforce black body text */
|
||||
line-height: 1.6;
|
||||
font-size: 1.05em;
|
||||
}
|
||||
|
||||
.pc-suggestion-body p {
|
||||
margin: 0 0 12px 0;
|
||||
}
|
||||
|
||||
.pc-suggestion-body p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.pc-suggestion-meta {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.pc-suggestion-author {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.pc-suggestion-date {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.pc-suggestion-actions {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.pc-button-small {
|
||||
padding: 8px 16px;
|
||||
font-size: 13px;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.pc-back-link {
|
||||
color: #3b82f6;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.pc-back-link:hover {
|
||||
color: #2563eb;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.pc-suggestion-detail {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.pc-suggestion-card.pc-suggestion-card-detail {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.pc-comments-section {
|
||||
background: linear-gradient(135deg, #ffffff 0%, #faf5ff 100%);
|
||||
border: 2px solid #a855f7;
|
||||
border-radius: 16px;
|
||||
padding: 25px;
|
||||
margin-top: 30px;
|
||||
box-shadow: 0 4px 15px rgba(168, 85, 247, 0.15);
|
||||
}
|
||||
|
||||
.pc-comments-section h3 {
|
||||
margin: 0 0 20px 0;
|
||||
font-size: 1.2em;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.pc-comment-card {
|
||||
border-left: 4px solid #a855f7;
|
||||
padding-left: 15px;
|
||||
margin-bottom: 20px;
|
||||
background: linear-gradient(135deg, #faf5ff 0%, #f3e8ff 100%);
|
||||
border-radius: 8px;
|
||||
padding: 15px 15px 15px 20px;
|
||||
box-shadow: 0 2px 8px rgba(168, 85, 247, 0.1);
|
||||
}
|
||||
|
||||
.pc-comment-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.pc-comment-author {
|
||||
font-weight: 600;
|
||||
color: #2c3338;
|
||||
}
|
||||
|
||||
.pc-comment-date {
|
||||
color: #646970;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.pc-comment-content {
|
||||
line-height: 1.6;
|
||||
color: #2c3338;
|
||||
}
|
||||
|
||||
.pc-comment-content p {
|
||||
margin: 0 0 12px 0;
|
||||
}
|
||||
|
||||
.pc-comment-content p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.pc-no-comments {
|
||||
text-align: center;
|
||||
color: #7c3aed;
|
||||
font-style: italic;
|
||||
padding: 20px;
|
||||
background: linear-gradient(135deg, #faf5ff 0%, #f3e8ff 100%);
|
||||
border-radius: 8px;
|
||||
border: 2px solid #c4b5fd;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.pc-char-counter {
|
||||
font-size: 12px;
|
||||
color: #646970;
|
||||
margin-top: 5px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.pc-counter-exceeded {
|
||||
color: #d63638;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Login required */
|
||||
.pc-community-suggestions-login-required {
|
||||
max-width: 500px;
|
||||
margin: 40px auto;
|
||||
}
|
||||
|
||||
.pc-suggestions-card {
|
||||
background: #fff;
|
||||
border: 1px solid #dcdcde;
|
||||
border-radius: 12px;
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.pc-suggestions-card h3 {
|
||||
font-size: 1.5em;
|
||||
font-weight: 600;
|
||||
margin: 0 0 15px 0;
|
||||
color: #1d2327;
|
||||
}
|
||||
|
||||
.pc-suggestions-card p {
|
||||
margin: 0 0 25px 0;
|
||||
color: #646970;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.pc-suggestions-actions {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.pc-suggestions-pagination {
|
||||
margin-top: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pc-suggestions-pagination .page-numbers {
|
||||
display: inline-flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
@keyframes pc-slide-down {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pc-fade-in-up {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.pc-suggestion-card {
|
||||
animation: pc-fade-in-up 0.4s ease-out;
|
||||
}
|
||||
|
||||
/* Responsive design */
|
||||
@media screen and (max-width: 768px) {
|
||||
.pc-community-suggestions-container {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.pc-suggestions-header {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pc-suggestions-header h2 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.pc-suggestions-controls {
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.pc-suggestion-card {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.pc-suggestion-vote {
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pc-vote-button {
|
||||
width: 60px;
|
||||
height: 40px;
|
||||
flex-direction: row;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.pc-form-card {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.pc-form-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.pc-suggestions-actions {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pc-suggestion-meta {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
.pc-suggestions-header h2 {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.pc-button {
|
||||
padding: 10px 20px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.pc-form-group input,
|
||||
.pc-form-group textarea {
|
||||
font-size: 14px;
|
||||
padding: 10px 14px;
|
||||
}
|
||||
|
||||
.pc-suggestion-title {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark mode support using the same variable palette (keeps styles consistent across themes) */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.pc-community-suggestions-container {
|
||||
--pc-primary: #60a5fa;
|
||||
--pc-primary-strong: #3b82f6;
|
||||
--pc-accent: #0891b2;
|
||||
--pc-success: #10b981;
|
||||
--pc-muted: #9ca3af;
|
||||
--pc-text: #e6eef8;
|
||||
--pc-heading: #ffffff;
|
||||
--pc-card-bg: #0b1220;
|
||||
--pc-card-border: #374151;
|
||||
--pc-input-bg: #0b1220;
|
||||
--pc-card-shadow: 0 6px 20px rgba(2,6,23,0.6);
|
||||
}
|
||||
|
||||
.pc-community-suggestions-container .pc-form-group input,
|
||||
.pc-community-suggestions-container .pc-form-group textarea,
|
||||
.pc-community-suggestions-container .pc-sort-dropdown select {
|
||||
background: var(--pc-input-bg);
|
||||
border-color: var(--pc-card-border);
|
||||
color: var(--pc-text);
|
||||
}
|
||||
|
||||
/* Force white cards and black text even in dark mode as requested */
|
||||
.pc-community-suggestions-container .pc-form-card,
|
||||
.pc-community-suggestions-container .pc-suggestion-card {
|
||||
background: #ffffff !important;
|
||||
color: #000000 !important;
|
||||
border-color: #e6f2fb !important;
|
||||
box-shadow: 0 6px 20px rgba(15, 23, 42, 0.06) !important;
|
||||
}
|
||||
|
||||
.pc-community-suggestions-container .pc-suggestion-card:hover {
|
||||
background: #ffffff !important;
|
||||
border-color: #e6f2fb !important;
|
||||
box-shadow: 0 18px 40px rgba(15, 23, 42, 0.12) !important;
|
||||
}
|
||||
|
||||
.pc-community-suggestions-container .pc-suggestion-title,
|
||||
.pc-community-suggestions-container .pc-suggestion-body {
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
.pc-community-suggestions-container .pc-form-group input,
|
||||
.pc-community-suggestions-container .pc-form-group textarea,
|
||||
.pc-community-suggestions-container .pc-sort-dropdown select {
|
||||
background: #ffffff !important;
|
||||
color: #000000 !important;
|
||||
border-color: #e6eef8 !important;
|
||||
}
|
||||
|
||||
.pc-community-suggestions-container .pc-comment-card { border-left-color: var(--pc-accent); }
|
||||
}
|
||||
|
||||
/* Accessibility improvements */
|
||||
.pc-vote-button:focus {
|
||||
outline: 2px solid #2271b1;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.pc-button:focus,
|
||||
.pc-form-group input:focus,
|
||||
.pc-form-group textarea:focus,
|
||||
.pc-sort-dropdown select:focus {
|
||||
outline: 2px solid #2271b1;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* High contrast mode */
|
||||
@media (prefers-contrast: high) {
|
||||
.pc-suggestion-card {
|
||||
border: 2px solid #000;
|
||||
}
|
||||
|
||||
.pc-vote-button {
|
||||
border: 2px solid #000;
|
||||
}
|
||||
|
||||
.pc-button {
|
||||
border: 2px solid #000;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reduced motion */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.pc-suggestion-card,
|
||||
.pc-suggestion-form,
|
||||
.pc-button,
|
||||
.pc-vote-button {
|
||||
transition: none;
|
||||
animation: none;
|
||||
}
|
||||
|
||||
.pc-button:hover,
|
||||
.pc-vote-button:hover {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
265
chat/templates/Community Suggestions/public/js/public-script.js
Normal file
265
chat/templates/Community Suggestions/public/js/public-script.js
Normal file
@@ -0,0 +1,265 @@
|
||||
jQuery(document).ready(function($) {
|
||||
|
||||
// Toggle suggestion form
|
||||
window.pcToggleSuggestionForm = function() {
|
||||
const form = document.getElementById('pc-suggestion-form');
|
||||
if (form.style.display === 'none') {
|
||||
form.style.display = 'block';
|
||||
form.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
} else {
|
||||
form.style.display = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
// Change sort method
|
||||
window.pcChangeSort = function(sort) {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('sort', sort);
|
||||
window.location.href = url.toString();
|
||||
};
|
||||
|
||||
// Handle suggestion submission
|
||||
$('#pc-new-suggestion-form').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const form = $(this);
|
||||
const submitBtn = form.find('button[type="submit"]');
|
||||
const title = $('#pc-suggestion-title').val().trim();
|
||||
const content = $('#pc-suggestion-content').val().trim();
|
||||
|
||||
// Basic validation
|
||||
if (!title || !content) {
|
||||
alert(pcCommunitySuggestions.i18n.fill_all_fields);
|
||||
return;
|
||||
}
|
||||
|
||||
if (title.length > 255) {
|
||||
alert(pcCommunitySuggestions.i18n.title_too_long);
|
||||
return;
|
||||
}
|
||||
|
||||
submitBtn.prop('disabled', true).addClass('pc-loading');
|
||||
|
||||
$.ajax({
|
||||
url: pcCommunitySuggestions.ajax_url,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'pc_submit_suggestion',
|
||||
title: title,
|
||||
content: content,
|
||||
nonce: pcCommunitySuggestions.nonce
|
||||
},
|
||||
success: function(response) {
|
||||
console.log('AJAX Response:', response);
|
||||
if (response.success) {
|
||||
// Clear form and show success message
|
||||
form[0].reset();
|
||||
pcToggleSuggestionForm();
|
||||
|
||||
// Show success message
|
||||
pcShowMessage(response.data.message, 'success');
|
||||
|
||||
// Reload page to show new suggestion
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 2000);
|
||||
} else {
|
||||
pcShowMessage(response.data.message, 'error');
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('AJAX Error:', status, error);
|
||||
console.error('Response Text:', xhr.responseText);
|
||||
console.error('Status Code:', xhr.status);
|
||||
if (xhr.responseText) {
|
||||
pcShowMessage('Error: ' + xhr.responseText.substring(0, 200) + '...', 'error');
|
||||
} else {
|
||||
pcShowMessage(pcCommunitySuggestions.i18n.ajax_error + ' (Status: ' + xhr.status + ')', 'error');
|
||||
}
|
||||
},
|
||||
complete: function() {
|
||||
submitBtn.prop('disabled', false).removeClass('pc-loading');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Handle voting
|
||||
window.pcVoteSuggestion = function(suggestionId) {
|
||||
if (!pcCommunitySuggestions.is_user_logged_in) {
|
||||
alert(pcCommunitySuggestions.i18n.login_to_vote);
|
||||
return;
|
||||
}
|
||||
|
||||
const voteBtn = $(`.pc-vote-button[onclick*="${suggestionId}"]`);
|
||||
if (voteBtn.prop('disabled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
voteBtn.prop('disabled', true).addClass('pc-loading');
|
||||
|
||||
$.ajax({
|
||||
url: pcCommunitySuggestions.ajax_url,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'pc_vote_suggestion',
|
||||
suggestion_id: suggestionId,
|
||||
nonce: pcCommunitySuggestions.nonce
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
// Update UI
|
||||
const voteCount = voteBtn.find('.pc-vote-count');
|
||||
const currentVotes = parseInt(voteCount.text());
|
||||
voteCount.text(currentVotes + 1);
|
||||
|
||||
voteBtn.addClass('pc-voted');
|
||||
voteBtn.find('.pc-vote-arrow').css('color', '#2a6b2a');
|
||||
|
||||
pcShowMessage(response.data.message, 'success');
|
||||
} else {
|
||||
pcShowMessage(response.data.message, 'error');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
pcShowMessage(pcCommunitySuggestions.i18n.ajax_error, 'error');
|
||||
},
|
||||
complete: function() {
|
||||
voteBtn.removeClass('pc-loading');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Show message function
|
||||
function pcShowMessage(message, type = 'info') {
|
||||
// Remove existing messages
|
||||
$('.pc-message').remove();
|
||||
|
||||
const messageClass = `pc-message pc-message-${type}`;
|
||||
const messageHtml = `
|
||||
<div class="${messageClass}">
|
||||
<p>${message}</p>
|
||||
<button class="pc-message-close" onclick="$(this).parent().fadeOut(300, function() { $(this).remove(); })">
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
$('.pc-suggestions-header').after(messageHtml);
|
||||
|
||||
// Auto-hide after 5 seconds
|
||||
setTimeout(() => {
|
||||
$('.pc-message').fadeOut(300, function() {
|
||||
$(this).remove();
|
||||
});
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Add message styles dynamically
|
||||
$('<style>')
|
||||
.text(`
|
||||
.pc-message {
|
||||
padding: 15px 20px;
|
||||
margin: 20px 0;
|
||||
border-radius: 6px;
|
||||
border-left: 4px solid;
|
||||
position: relative;
|
||||
animation: pc-slide-in 0.3s ease-out;
|
||||
}
|
||||
|
||||
.pc-message-success {
|
||||
background: #edfaef;
|
||||
border-color: #00a32a;
|
||||
color: #2a6b2a;
|
||||
}
|
||||
|
||||
.pc-message-error {
|
||||
background: #fcf0f1;
|
||||
border-color: #d63638;
|
||||
color: #b32d2e;
|
||||
}
|
||||
|
||||
.pc-message-info {
|
||||
background: #f0f6ff;
|
||||
border-color: #2271b1;
|
||||
color: #135e96;
|
||||
}
|
||||
|
||||
.pc-message-close {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
@keyframes pc-slide-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
`)
|
||||
.appendTo('head');
|
||||
|
||||
// Add loading class styles
|
||||
$('<style>')
|
||||
.text(`
|
||||
.pc-loading {
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pc-loading::after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 2px solid #f3f3f3;
|
||||
border-top: 2px solid #2271b1;
|
||||
border-radius: 50%;
|
||||
animation: pc-spin 1s linear infinite;
|
||||
margin-left: 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@keyframes pc-spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
`)
|
||||
.appendTo('head');
|
||||
|
||||
|
||||
|
||||
// Handle enter key in form
|
||||
$('#pc-suggestion-title, #pc-suggestion-content').on('keydown', function(e) {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
$('#pc-new-suggestion-form').submit();
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-resize textarea
|
||||
$('#pc-suggestion-content').on('input', function() {
|
||||
this.style.height = 'auto';
|
||||
this.style.height = (this.scrollHeight) + 'px';
|
||||
}).trigger('input');
|
||||
|
||||
// Focus on title field when form opens
|
||||
$(document).on('click', '#pc-suggestion-form', function(e) {
|
||||
if (e.target === this) {
|
||||
$('#pc-suggestion-title').focus();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,176 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/**
|
||||
* Check for duplicate class declarations in WordPress plugin
|
||||
* Detects duplicate classes, interfaces, traits, functions, and constants
|
||||
*/
|
||||
|
||||
$plugin_dir = $argv[1] ?? __DIR__;
|
||||
|
||||
echo "=== Checking for Duplicates ===\n\n";
|
||||
|
||||
function scan_directory($dir, $extensions = ['php']) {
|
||||
$files = [];
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS)
|
||||
);
|
||||
|
||||
foreach ($iterator as $file) {
|
||||
if (in_array(strtolower($file->getExtension()), $extensions)) {
|
||||
$files[] = $file->getPathname();
|
||||
}
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
function extract_declarations($content) {
|
||||
$declarations = [];
|
||||
|
||||
// Extract classes
|
||||
if (preg_match_all('/^\s*(?:abstract\s+)?class\s+(\w+)/m', $content, $matches)) {
|
||||
foreach ($matches[1] as $class) {
|
||||
$declarations['classes'][$class][] = 'class';
|
||||
}
|
||||
}
|
||||
|
||||
// Extract interfaces
|
||||
if (preg_match_all('/^\s*interface\s+(\w+)/m', $content, $matches)) {
|
||||
foreach ($matches[1] as $interface) {
|
||||
$declarations['interfaces'][$interface][] = 'interface';
|
||||
}
|
||||
}
|
||||
|
||||
// Extract traits
|
||||
if (preg_match_all('/^\s*trait\s+(\w+)/m', $content, $matches)) {
|
||||
foreach ($matches[1] as $trait) {
|
||||
$declarations['traits'][$trait][] = 'trait';
|
||||
}
|
||||
}
|
||||
|
||||
// Extract functions (non-namespaced)
|
||||
if (preg_match_all('/^\s*function\s+(\w+)/m', $content, $matches)) {
|
||||
foreach ($matches[1] as $function) {
|
||||
if (!isset($declarations['functions'][$function])) {
|
||||
$declarations['functions'][$function] = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $declarations;
|
||||
}
|
||||
|
||||
function check_duplicate_classes($files) {
|
||||
$all_classes = [];
|
||||
$duplicates = [];
|
||||
|
||||
foreach ($files as $file) {
|
||||
$content = file_get_contents($file);
|
||||
|
||||
// Check for classes
|
||||
if (preg_match_all('/^\s*(?:abstract\s+)?class\s+(\w+)/m', $content, $matches)) {
|
||||
foreach ($matches[1] as $class) {
|
||||
if (!isset($all_classes[$class])) {
|
||||
$all_classes[$class] = [];
|
||||
}
|
||||
$all_classes[$class][] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for interfaces
|
||||
if (preg_match_all('/^\s*interface\s+(\w+)/m', $content, $matches)) {
|
||||
foreach ($matches[1] as $interface) {
|
||||
$key = strtolower($interface);
|
||||
if (!isset($all_classes[$key])) {
|
||||
$all_classes[$key] = [];
|
||||
}
|
||||
$all_classes[$key][] = $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($all_classes as $name => $files_list) {
|
||||
if (count($files_list) > 1) {
|
||||
$duplicates['classes'][$name] = $files_list;
|
||||
}
|
||||
}
|
||||
|
||||
return $duplicates;
|
||||
}
|
||||
|
||||
function check_duplicate_functions($files) {
|
||||
$all_functions = [];
|
||||
$duplicates = [];
|
||||
|
||||
foreach ($files as $file) {
|
||||
$content = file_get_contents($file);
|
||||
|
||||
// Skip content inside classes, interfaces, and traits
|
||||
$content = preg_replace('/class\s+\w+\s*\{[^}]*\}/s', '', $content);
|
||||
$content = preg_replace('/interface\s+\w+\s*\{[^}]*\}/s', '', $content);
|
||||
$content = preg_replace('/trait\s+\w+\s*\{[^}]*\}/s', '', $content);
|
||||
|
||||
if (preg_match_all('/^\s*function\s+(\w+)\s*\(/m', $content, $matches)) {
|
||||
foreach ($matches[1] as $function) {
|
||||
if (!isset($all_functions[$function])) {
|
||||
$all_functions[$function] = [];
|
||||
}
|
||||
$all_functions[$function][] = $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($all_functions as $name => $files_list) {
|
||||
if (count($files_list) > 1) {
|
||||
$duplicates['functions'][$name] = $files_list;
|
||||
}
|
||||
}
|
||||
|
||||
return $duplicates;
|
||||
}
|
||||
|
||||
echo "Scanning directory: $plugin_dir\n";
|
||||
$files = scan_directory($plugin_dir);
|
||||
echo "Found " . count($files) . " PHP files\n\n";
|
||||
|
||||
$class_duplicates = check_duplicate_classes($files);
|
||||
$function_duplicates = check_duplicate_functions($files);
|
||||
|
||||
$has_issues = false;
|
||||
|
||||
if (!empty($class_duplicates['classes'])) {
|
||||
$has_issues = true;
|
||||
echo "❌ Duplicate class/interface declarations found:\n";
|
||||
foreach ($class_duplicates['classes'] as $name => $files_list) {
|
||||
echo " - $name found in:\n";
|
||||
foreach ($files_list as $file) {
|
||||
echo " $file\n";
|
||||
}
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
if (!empty($function_duplicates['functions'])) {
|
||||
$has_issues = true;
|
||||
echo "⚠️ Potential duplicate function declarations:\n";
|
||||
foreach ($function_duplicates['functions'] as $name => $files_list) {
|
||||
echo " - $name() found in:\n";
|
||||
foreach ($files_list as $file) {
|
||||
echo " $file\n";
|
||||
}
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
if (!$has_issues) {
|
||||
echo "✅ No duplicate declarations found\n";
|
||||
} else {
|
||||
echo "⚠️ Please review the duplicates above and fix them.\n";
|
||||
echo " Solutions:\n";
|
||||
echo " - Use class_exists() guards around class definitions\n";
|
||||
echo " - Consolidate duplicate declarations into one file\n";
|
||||
echo " - Namespace conflicting classes differently\n";
|
||||
}
|
||||
|
||||
echo "\n=== Check Complete ===\n";
|
||||
exit($has_issues ? 1 : 0);
|
||||
@@ -0,0 +1,195 @@
|
||||
#!/bin/bash
|
||||
|
||||
# WordPress Plugin Validation Script
|
||||
# Validates plugin structure, syntax, and WordPress coding standards
|
||||
|
||||
PLUGIN_DIR="$(pwd)"
|
||||
PLUGIN_SLUG="pc-community-suggestions-7d3f"
|
||||
MAIN_FILE="$PLUGIN_SLUG.php"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${BLUE}=== WordPress Plugin Validation ===${NC}"
|
||||
echo -e "Plugin: $PLUGIN_SLUG"
|
||||
echo -e "Directory: $PLUGIN_DIR"
|
||||
echo
|
||||
|
||||
# Check if main plugin file exists
|
||||
if [[ ! -f "$MAIN_FILE" ]]; then
|
||||
echo -e "${RED}❌ ERROR: Main plugin file $MAIN_FILE not found${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✅ Main plugin file found${NC}"
|
||||
|
||||
# Check plugin header
|
||||
echo -e "${BLUE}Checking plugin header...${NC}"
|
||||
if ! grep -q "Plugin Name:" "$MAIN_FILE"; then
|
||||
echo -e "${RED}❌ ERROR: Plugin header missing 'Plugin Name:'${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -q "Plugin URI:" "$MAIN_FILE"; then
|
||||
echo -e "${YELLOW}⚠️ WARNING: Plugin header missing 'Plugin URI:'${NC}"
|
||||
fi
|
||||
|
||||
if ! grep -q "Text Domain:" "$MAIN_FILE"; then
|
||||
echo -e "${RED}❌ ERROR: Plugin header missing 'Text Domain:'${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! grep -q "Update URI:" "$MAIN_FILE"; then
|
||||
echo -e "${YELLOW}⚠️ WARNING: Plugin header missing 'Update URI:'${NC}"
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✅ Plugin header check passed${NC}"
|
||||
|
||||
# Check required directories
|
||||
echo -e "${BLUE}Checking directory structure...${NC}"
|
||||
REQUIRED_DIRS=("includes" "admin" "admin/css" "admin/js" "public" "public/css" "public/js" "assets")
|
||||
|
||||
for dir in "${REQUIRED_DIRS[@]}"; do
|
||||
if [[ ! -d "$dir" ]]; then
|
||||
echo -e "${YELLOW}⚠️ WARNING: Directory $dir not found${NC}"
|
||||
else
|
||||
echo -e "${GREEN}✅ Directory $dir found${NC}"
|
||||
fi
|
||||
done
|
||||
|
||||
# Check required files
|
||||
echo -e "${BLUE}Checking required files...${NC}"
|
||||
REQUIRED_FILES=(
|
||||
"includes/class-database.php"
|
||||
"includes/class-shortcodes.php"
|
||||
"includes/class-rest-api.php"
|
||||
"admin/class-admin.php"
|
||||
"admin/css/admin-style.css"
|
||||
"public/css/public-style.css"
|
||||
"public/js/public-script.js"
|
||||
"admin/js/admin-script.js"
|
||||
"uninstall.php"
|
||||
)
|
||||
|
||||
for file in "${REQUIRED_FILES[@]}"; do
|
||||
if [[ ! -f "$file" ]]; then
|
||||
echo -e "${YELLOW}⚠️ WARNING: File $file not found${NC}"
|
||||
else
|
||||
echo -e "${GREEN}✅ File $file found${NC}"
|
||||
fi
|
||||
done
|
||||
|
||||
# PHP syntax check
|
||||
echo -e "${BLUE}Running PHP syntax checks...${NC}"
|
||||
PHP_FILES=$(find . -name "*.php" -type f)
|
||||
SYNTAX_ERRORS=0
|
||||
|
||||
for php_file in $PHP_FILES; do
|
||||
if php -l "$php_file" > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✅ Syntax OK: $php_file${NC}"
|
||||
else
|
||||
echo -e "${RED}❌ Syntax ERROR: $php_file${NC}"
|
||||
php -l "$php_file"
|
||||
SYNTAX_ERRORS=$((SYNTAX_ERRORS + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $SYNTAX_ERRORS -gt 0 ]]; then
|
||||
echo -e "${RED}❌ Found $SYNTAX_ERRORS PHP syntax errors${NC}"
|
||||
exit 1
|
||||
else
|
||||
echo -e "${GREEN}✅ All PHP files passed syntax check${NC}"
|
||||
fi
|
||||
|
||||
# Check for WordPress functions
|
||||
echo -e "${BLUE}Checking WordPress function usage...${NC}"
|
||||
if ! grep -q "add_action\|add_filter" "$MAIN_FILE"; then
|
||||
echo -e "${YELLOW}⚠️ WARNING: No WordPress hooks found in main file${NC}"
|
||||
fi
|
||||
|
||||
# Check for proper enqueueing
|
||||
echo -e "${BLUE}Checking script enqueueing...${NC}"
|
||||
if grep -q "wp_enqueue_style\|wp_enqueue_script" "$MAIN_FILE"; then
|
||||
echo -e "${GREEN}✅ Script enqueueing functions found${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ WARNING: No script enqueueing functions found${NC}"
|
||||
fi
|
||||
|
||||
# Check for security measures
|
||||
echo -e "${BLUE}Checking security measures...${NC}"
|
||||
if grep -q "check_admin_referer\|wp_verify_nonce" "$MAIN_FILE" || grep -q "check_admin_referer\|wp_verify_nonce" includes/*.php; then
|
||||
echo -e "${GREEN}✅ Security nonce checks found${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ WARNING: No security nonce checks found${NC}"
|
||||
fi
|
||||
|
||||
if grep -q "current_user_can\|is_user_logged_in" "$MAIN_FILE" || grep -q "current_user_can\|is_user_logged_in" includes/*.php; then
|
||||
echo -e "${GREEN}✅ User capability checks found${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ WARNING: No user capability checks found${NC}"
|
||||
fi
|
||||
|
||||
# Check for database operations
|
||||
echo -e "${BLUE}Checking database operations...${NC}"
|
||||
if grep -q "\$wpdb->" includes/class-database.php; then
|
||||
echo -e "${GREEN}✅ Database operations found${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ WARNING: No database operations found${NC}"
|
||||
fi
|
||||
|
||||
# Check for activation/deactivation hooks
|
||||
echo -e "${BLUE}Checking activation hooks...${NC}"
|
||||
if grep -q "register_activation_hook\|register_deactivation_hook" "$MAIN_FILE"; then
|
||||
echo -e "${GREEN}✅ Activation/deactivation hooks found${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ WARNING: No activation/deactivation hooks found${NC}"
|
||||
fi
|
||||
|
||||
# Check for shortcodes
|
||||
echo -e "${BLUE}Checking shortcodes...${NC}"
|
||||
if grep -q "add_shortcode" includes/class-shortcodes.php; then
|
||||
echo -e "${GREEN}✅ Shortcode registration found${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ WARNING: No shortcode registration found${NC}"
|
||||
fi
|
||||
|
||||
# Check for REST API
|
||||
echo -e "${BLUE}Checking REST API...${NC}"
|
||||
if grep -q "register_rest_route" includes/class-rest-api.php; then
|
||||
echo -e "${GREEN}✅ REST API routes found${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ WARNING: No REST API routes found${NC}"
|
||||
fi
|
||||
|
||||
# Check for admin menu
|
||||
echo -e "${BLUE}Checking admin menu...${NC}"
|
||||
if grep -q "add_menu_page\|add_submenu_page" admin/class-admin.php; then
|
||||
echo -e "${GREEN}✅ Admin menu registration found${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ WARNING: No admin menu registration found${NC}"
|
||||
fi
|
||||
|
||||
# Final summary
|
||||
echo
|
||||
echo -e "${BLUE}=== Validation Summary ===${NC}"
|
||||
echo -e "${GREEN}✅ Plugin structure looks good${NC}"
|
||||
echo -e "${GREEN}✅ All PHP files passed syntax check${NC}"
|
||||
echo -e "${GREEN}✅ WordPress integration features found${NC}"
|
||||
echo -e "${GREEN}✅ Security measures implemented${NC}"
|
||||
echo
|
||||
echo -e "${GREEN}🎉 Plugin validation completed successfully!${NC}"
|
||||
echo -e "${BLUE}The plugin is ready for WordPress installation.${NC}"
|
||||
|
||||
# Display plugin information
|
||||
echo
|
||||
echo -e "${BLUE}=== Plugin Information ===${NC}"
|
||||
grep "Plugin Name:" "$MAIN_FILE" | head -1
|
||||
grep "Version:" "$MAIN_FILE" | head -1
|
||||
grep "Author:" "$MAIN_FILE" | head -1
|
||||
grep "Text Domain:" "$MAIN_FILE" | head -1
|
||||
|
||||
exit 0
|
||||
44
chat/templates/Community Suggestions/uninstall.php
Normal file
44
chat/templates/Community Suggestions/uninstall.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/**
|
||||
* Uninstall Community Suggestions plugin
|
||||
*
|
||||
* @package Community Suggestions
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('WP_UNINSTALL_PLUGIN')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Remove database tables
|
||||
function pc_community_suggestions_uninstall() {
|
||||
global $wpdb;
|
||||
|
||||
$suggestions_table = $wpdb->prefix . 'pc_community_suggestions';
|
||||
$votes_table = $wpdb->prefix . 'pc_community_votes';
|
||||
|
||||
// Drop tables
|
||||
$wpdb->query("DROP TABLE IF EXISTS $suggestions_table");
|
||||
$wpdb->query("DROP TABLE IF EXISTS $votes_table");
|
||||
|
||||
// Remove options
|
||||
delete_option('pc_community_suggestions_moderation');
|
||||
delete_option('pc_community_suggestions_default_sort');
|
||||
delete_option('pc_community_suggestions_page_id');
|
||||
delete_option('pc_community_suggestions_version');
|
||||
|
||||
// Remove any transients
|
||||
$wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE '%pc_community_suggestions_%'");
|
||||
|
||||
// Remove scheduled events
|
||||
wp_clear_scheduled_hook('pc_community_suggestions_daily_cleanup');
|
||||
|
||||
// Remove the default page if it exists
|
||||
$page_id = get_option('pc_community_suggestions_page_id', 0);
|
||||
if ($page_id && get_post_type($page_id) === 'page') {
|
||||
wp_delete_post($page_id, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Run uninstall function
|
||||
pc_community_suggestions_uninstall();
|
||||
Reference in New Issue
Block a user