Restore to commit 74e578279624c6045ca440a3459ebfa1f8d54191
This commit is contained in:
125
chat/templates/Announcements/README.md
Normal file
125
chat/templates/Announcements/README.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# PC Announcements 274
|
||||
|
||||
A comprehensive WordPress plugin for creating and managing announcements that display at the top of public pages with scheduling capabilities.
|
||||
|
||||
## Features
|
||||
|
||||
- **Admin Management**: Create, edit, delete, and schedule announcements through an intuitive admin interface
|
||||
- **Scheduling**: Set start and end dates for announcements with automatic activation/deactivation
|
||||
- **Modern Frontend Display**: Responsive, accessible announcement banners with smooth animations
|
||||
- **Security**: Built with WordPress security best practices including nonce verification and capability checks
|
||||
- **Customizable**: Easily styled with CSS custom properties and modern design patterns
|
||||
- **Mobile Responsive**: Optimized for all screen sizes with mobile-first approach
|
||||
|
||||
## Installation
|
||||
|
||||
1. Upload the `pc-announcements-274` folder to your WordPress `/wp-content/plugins/` directory
|
||||
2. Activate the plugin through the WordPress admin "Plugins" menu
|
||||
3. Navigate to "Announcements" in the WordPress admin to create your first announcement
|
||||
|
||||
## Usage
|
||||
|
||||
### Creating Announcements
|
||||
|
||||
1. Go to **Announcements → Add New** in the WordPress admin
|
||||
2. Enter a title and message for your announcement
|
||||
3. Optionally add an image URL
|
||||
4. Set scheduling dates (start/end) if needed
|
||||
5. Set the status (active, inactive, or scheduled)
|
||||
6. Click "Create Announcement"
|
||||
|
||||
### Managing Announcements
|
||||
|
||||
- View all announcements at **Announcements → All Announcements**
|
||||
- Edit existing announcements by clicking the "Edit" button
|
||||
- Delete announcements using the "Delete" button with confirmation
|
||||
- See real-time status indicators for active announcements
|
||||
|
||||
### Frontend Display
|
||||
|
||||
Announcements automatically appear at the top of all public pages when:
|
||||
- The announcement status is set to "Active"
|
||||
- The current time is within the scheduled start and end dates
|
||||
- The announcement hasn't been dismissed by the user
|
||||
|
||||
## Customization
|
||||
|
||||
### CSS Custom Properties
|
||||
|
||||
You can customize the appearance using these CSS variables:
|
||||
|
||||
```css
|
||||
:root {
|
||||
--pc-announcements-274-bg-primary: #0d47a1;
|
||||
--pc-announcements-274-bg-secondary: #1565c0;
|
||||
--pc-announcements-274-text-primary: #ffffff;
|
||||
--pc-announcements-274-text-secondary: rgba(255, 255, 255, 0.9);
|
||||
--pc-announcements-274-border-radius: 8px;
|
||||
--pc-announcements-274-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto;
|
||||
}
|
||||
```
|
||||
|
||||
### Theme Variations
|
||||
|
||||
Add these classes to customize appearance:
|
||||
- `.pc-announcements-274-success` - Green theme
|
||||
- `.pc-announcements-274-warning` - Orange theme
|
||||
- `.pc-announcements-274-error` - Red theme
|
||||
- `.pc-announcements-274-info` - Light blue theme
|
||||
- `.pc-announcements-274-compact` - Smaller, compact version
|
||||
- `.pc-announcements-274-no-image` - Hide image and optimize layout
|
||||
|
||||
## Security Features
|
||||
|
||||
- **Capability Checks**: Only administrators can manage announcements
|
||||
- **Nonce Verification**: All AJAX requests protected with WordPress nonces
|
||||
- **Input Sanitization**: All user inputs properly sanitized and escaped
|
||||
- **Database Security**: Prepared statements used for all database operations
|
||||
- **CSRF Protection**: Built-in CSRF protection for form submissions
|
||||
|
||||
## Accessibility
|
||||
|
||||
- WCAG 2.1 AA compliant design
|
||||
- Keyboard navigation support
|
||||
- Screen reader compatibility
|
||||
- High contrast mode support
|
||||
- Reduced motion preferences respected
|
||||
- Focus management for dynamic content
|
||||
|
||||
## Browser Support
|
||||
|
||||
- Chrome 60+
|
||||
- Firefox 55+
|
||||
- Safari 12+
|
||||
- Edge 79+
|
||||
- Mobile browsers (iOS Safari 12+, Android Chrome 60+)
|
||||
|
||||
## Technical Details
|
||||
|
||||
- **PHP Version**: 7.4+
|
||||
- **WordPress Version**: 5.0+
|
||||
- **Database**: Uses custom table with proper indexing
|
||||
- **Performance**: Optimized queries with caching considerations
|
||||
- **Memory**: Minimal memory footprint
|
||||
- **Standards**: Follows WordPress coding standards and best practices
|
||||
|
||||
## Changelog
|
||||
|
||||
### Version 1.0.0
|
||||
- Initial release
|
||||
- Core announcement management functionality
|
||||
- Admin interface with CRUD operations
|
||||
- Frontend display with responsive design
|
||||
- Scheduling and status management
|
||||
- Security and accessibility features
|
||||
|
||||
## Support
|
||||
|
||||
For support, documentation, and updates:
|
||||
- Plugin URL: https://plugincompass.com/plugins/pc-announcements-274
|
||||
- Author: Plugin Compass
|
||||
- Author URI: https://plugincompass.com
|
||||
|
||||
## License
|
||||
|
||||
This plugin is licensed under the GPL-2.0-or-later license.
|
||||
238
chat/templates/Announcements/admin/class-admin.php
Normal file
238
chat/templates/Announcements/admin/class-admin.php
Normal file
@@ -0,0 +1,238 @@
|
||||
<?php
|
||||
/**
|
||||
* Admin class for managing announcements in WordPress admin
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class PC_Announcements_274_Admin {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action('admin_menu', array($this, 'add_admin_menu'));
|
||||
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
|
||||
add_action('wp_ajax_pc_announcements_274_action', array($this, 'handle_ajax_requests'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add admin menu items
|
||||
*/
|
||||
public function add_admin_menu() {
|
||||
add_menu_page(
|
||||
__('Announcements', 'pc-announcements-274'),
|
||||
__('Announcements', 'pc-announcements-274'),
|
||||
'manage_options',
|
||||
'pc-announcements-274',
|
||||
array($this, 'render_announcements_page'),
|
||||
'dashicons-megaphone',
|
||||
25
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
'pc-announcements-274',
|
||||
__('All Announcements', 'pc-announcements-274'),
|
||||
__('All Announcements', 'pc-announcements-274'),
|
||||
'manage_options',
|
||||
'pc-announcements-274',
|
||||
array($this, 'render_announcements_page')
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
'pc-announcements-274',
|
||||
__('Add New', 'pc-announcements-274'),
|
||||
__('Add New', 'pc-announcements-274'),
|
||||
'manage_options',
|
||||
'pc-announcements-274-add',
|
||||
array($this, 'render_add_announcement_page')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue admin scripts and styles
|
||||
*/
|
||||
public function enqueue_admin_scripts($hook) {
|
||||
if (strpos($hook, 'pc-announcements-274') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_style('pc-announcements-274-admin-style', PC_ANNOUNCEMENTS_274_PLUGIN_URL . 'admin/css/admin-style.css', array('wp-admin'), PC_ANNOUNCEMENTS_274_VERSION);
|
||||
wp_enqueue_media();
|
||||
wp_enqueue_script('pc-announcements-274-admin-script', PC_ANNOUNCEMENTS_274_PLUGIN_URL . 'admin/js/admin-script.js', array('jquery'), PC_ANNOUNCEMENTS_274_VERSION, true);
|
||||
wp_localize_script('pc-announcements-274-admin-script', 'pc_announcements_274_ajax', array(
|
||||
'ajax_url' => admin_url('admin-ajax.php'),
|
||||
'nonce' => wp_create_nonce('pc_announcements_274_nonce'),
|
||||
'i18n' => array(
|
||||
'error_occurred' => __('An error occurred. Please try again.', 'pc-announcements-274'),
|
||||
'choose_image' => __('Choose Image', 'pc-announcements-274'),
|
||||
'preview' => __('Preview', 'pc-announcements-274'),
|
||||
'end_date_warning' => __('End date should be after start date.', 'pc-announcements-274')
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Render main announcements page
|
||||
*/
|
||||
public function render_announcements_page() {
|
||||
$current_page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1;
|
||||
$per_page = 20;
|
||||
$offset = ($current_page - 1) * $per_page;
|
||||
|
||||
global $wpdb;
|
||||
$table_name = PC_Announcements_274_Install::get_table_name();
|
||||
|
||||
if (empty($table_name)) {
|
||||
include PC_ANNOUNCEMENTS_274_PLUGIN_DIR . 'admin/templates/error-page.php';
|
||||
return;
|
||||
}
|
||||
|
||||
// Get total count
|
||||
$total = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
|
||||
if ($total === null) {
|
||||
$total = 0;
|
||||
}
|
||||
$total_pages = ceil($total / $per_page);
|
||||
|
||||
// Get announcements
|
||||
$announcements = $wpdb->get_results($wpdb->prepare(
|
||||
"SELECT * FROM $table_name ORDER BY created_at DESC LIMIT %d OFFSET %d",
|
||||
$per_page, $offset
|
||||
));
|
||||
|
||||
if ($announcements === null) {
|
||||
$announcements = array();
|
||||
}
|
||||
|
||||
include PC_ANNOUNCEMENTS_274_PLUGIN_DIR . 'admin/templates/list-page.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render add new announcement page
|
||||
*/
|
||||
public function render_add_announcement_page() {
|
||||
$announcement_id = isset($_GET['id']) ? intval($_GET['id']) : 0;
|
||||
$announcement = null;
|
||||
|
||||
if ($announcement_id > 0) {
|
||||
global $wpdb;
|
||||
$table_name = PC_Announcements_274_Install::get_table_name();
|
||||
|
||||
if (!empty($table_name)) {
|
||||
$announcement = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table_name WHERE id = %d", $announcement_id));
|
||||
}
|
||||
}
|
||||
|
||||
include PC_ANNOUNCEMENTS_274_PLUGIN_DIR . 'admin/templates/edit-page.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle AJAX requests
|
||||
*/
|
||||
public function handle_ajax_requests() {
|
||||
check_ajax_referer('pc_announcements_274_nonce', 'nonce');
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_die(__('You do not have sufficient permissions.', 'pc-announcements-274'));
|
||||
}
|
||||
|
||||
$action = isset($_POST['sub_action']) ? sanitize_text_field($_POST['sub_action']) : '';
|
||||
|
||||
switch ($action) {
|
||||
case 'save_announcement':
|
||||
$this->save_announcement();
|
||||
break;
|
||||
case 'delete_announcement':
|
||||
$this->delete_announcement();
|
||||
break;
|
||||
default:
|
||||
wp_send_json_error(array('message' => __('Invalid action.', 'pc-announcements-274')));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save announcement
|
||||
*/
|
||||
private function save_announcement() {
|
||||
global $wpdb;
|
||||
$table_name = PC_Announcements_274_Install::get_table_name();
|
||||
|
||||
$id = isset($_POST['id']) ? intval($_POST['id']) : 0;
|
||||
$title = sanitize_text_field($_POST['title']);
|
||||
$message = wp_kses_post($_POST['message']);
|
||||
$banner_color = sanitize_hex_color($_POST['banner_color']);
|
||||
$link_url = esc_url_raw($_POST['link_url']);
|
||||
$image_url = esc_url_raw($_POST['image_url']);
|
||||
$start_date = !empty($_POST['start_date']) ? sanitize_text_field($_POST['start_date']) : null;
|
||||
$end_date = !empty($_POST['end_date']) ? sanitize_text_field($_POST['end_date']) : null;
|
||||
$status = sanitize_text_field($_POST['status']);
|
||||
|
||||
if (empty($title)) {
|
||||
wp_send_json_error(array('message' => __('Title is required.', 'pc-announcements-274')));
|
||||
}
|
||||
|
||||
if (empty($banner_color)) {
|
||||
$banner_color = '#0d47a1';
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'title' => $title,
|
||||
'message' => $message,
|
||||
'banner_color' => $banner_color,
|
||||
'link_url' => $link_url,
|
||||
'image_url' => $image_url,
|
||||
'start_date' => $start_date,
|
||||
'end_date' => $end_date,
|
||||
'status' => $status,
|
||||
'updated_at' => current_time('mysql')
|
||||
);
|
||||
|
||||
$format = array('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s');
|
||||
|
||||
if ($id > 0) {
|
||||
$result = $wpdb->update($table_name, $data, array('id' => $id), $format, array('%d'));
|
||||
} else {
|
||||
$data['created_at'] = current_time('mysql');
|
||||
$data['created_by'] = get_current_user_id();
|
||||
$format = array('%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s');
|
||||
|
||||
$result = $wpdb->insert($table_name, $data, $format);
|
||||
$id = $wpdb->insert_id;
|
||||
}
|
||||
|
||||
if ($result === false) {
|
||||
wp_send_json_error(array('message' => __('Failed to save announcement.', 'pc-announcements-274')));
|
||||
}
|
||||
|
||||
wp_send_json_success(array(
|
||||
'message' => $id > 0 && isset($_POST['id']) ? __('Announcement updated successfully!', 'pc-announcements-274') : __('Announcement created successfully!', 'pc-announcements-274'),
|
||||
'id' => $id
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete announcement
|
||||
*/
|
||||
private function delete_announcement() {
|
||||
global $wpdb;
|
||||
$table_name = PC_Announcements_274_Install::get_table_name();
|
||||
|
||||
$id = intval($_POST['id']);
|
||||
|
||||
if ($id <= 0) {
|
||||
wp_send_json_error(array('message' => __('Invalid announcement ID.', 'pc-announcements-274')));
|
||||
}
|
||||
|
||||
$result = $wpdb->delete($table_name, array('id' => $id), array('%d'));
|
||||
|
||||
if ($result === false) {
|
||||
wp_send_json_error(array('message' => __('Failed to delete announcement.', 'pc-announcements-274')));
|
||||
}
|
||||
|
||||
wp_send_json_success(array('message' => __('Announcement deleted successfully!', 'pc-announcements-274')));
|
||||
}
|
||||
}
|
||||
1017
chat/templates/Announcements/admin/css/admin-style.css
Normal file
1017
chat/templates/Announcements/admin/css/admin-style.css
Normal file
File diff suppressed because it is too large
Load Diff
199
chat/templates/Announcements/admin/js/admin-script.js
Normal file
199
chat/templates/Announcements/admin/js/admin-script.js
Normal file
@@ -0,0 +1,199 @@
|
||||
jQuery(document).ready(function($) {
|
||||
'use strict';
|
||||
|
||||
// Form submission
|
||||
$('#pc-announcements-274-form').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var $form = $(this);
|
||||
var $submitBtn = $form.find('button[type="submit"]');
|
||||
var originalText = $submitBtn.text();
|
||||
|
||||
// Show loading state
|
||||
$submitBtn.prop('disabled', true).text('Saving...');
|
||||
|
||||
$.ajax({
|
||||
url: pc_announcements_274_ajax.ajax_url,
|
||||
type: 'POST',
|
||||
data: $form.serialize(),
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
// Show success message
|
||||
$('<div class="notice notice-success is-dismissible"><p>' + response.data.message + '</p></div>')
|
||||
.insertAfter('.wp-header-end')
|
||||
.delay(3000)
|
||||
.fadeOut(function() {
|
||||
$(this).remove();
|
||||
});
|
||||
|
||||
// Redirect to list page
|
||||
setTimeout(function() {
|
||||
window.location.href = 'admin.php?page=pc-announcements-274&message=success';
|
||||
}, 1000);
|
||||
} else {
|
||||
// Show error message
|
||||
$('<div class="notice notice-error is-dismissible"><p>' + response.data.message + '</p></div>')
|
||||
.insertAfter('.wp-header-end');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
$('<div class="notice notice-error is-dismissible"><p>' + pc_announcements_274_ajax.i18n.error_occurred + '</p></div>')
|
||||
.insertAfter('.wp-header-end');
|
||||
},
|
||||
complete: function() {
|
||||
// Restore button state
|
||||
$submitBtn.prop('disabled', false).text(originalText);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Delete confirmation
|
||||
$('.pc-announcements-274-delete-btn').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var announcementId = $(this).data('id');
|
||||
var $modal = $('#pc-announcements-274-delete-modal');
|
||||
|
||||
// Show modal
|
||||
$modal.show();
|
||||
|
||||
// Handle delete confirmation
|
||||
$modal.find('.pc-announcements-274-confirm-delete').off('click').on('click', function() {
|
||||
$.ajax({
|
||||
url: pc_announcements_274_ajax.ajax_url,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'pc_announcements_274_action',
|
||||
sub_action: 'delete_announcement',
|
||||
id: announcementId,
|
||||
nonce: pc_announcements_274_ajax.nonce
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
// Remove the row from table
|
||||
$('button[data-id="' + announcementId + '"]').closest('tr').fadeOut(function() {
|
||||
$(this).remove();
|
||||
|
||||
// Show empty state if no items left
|
||||
if ($('.pc-announcements-274-wrap .wp-list-table tbody tr').length === 0) {
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
|
||||
// Show success message
|
||||
$('<div class="notice notice-success is-dismissible"><p>' + response.data.message + '</p></div>')
|
||||
.insertAfter('.wp-header-end')
|
||||
.delay(3000)
|
||||
.fadeOut(function() {
|
||||
$(this).remove();
|
||||
});
|
||||
} else {
|
||||
// Show error message
|
||||
$('<div class="notice notice-error is-dismissible"><p>' + response.data.message + '</p></div>')
|
||||
.insertAfter('.wp-header-end');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
$('<div class="notice notice-error is-dismissible"><p>' + pc_announcements_274_ajax.i18n.error_occurred + '</p></div>')
|
||||
.insertAfter('.wp-header-end');
|
||||
},
|
||||
complete: function() {
|
||||
// Hide modal
|
||||
$modal.hide();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Close modal handlers
|
||||
$('#pc-announcements-274-delete-modal').on('click', '.pc-announcements-274-cancel-delete, .pc-announcements-274-modal-backdrop', function() {
|
||||
$('#pc-announcements-274-delete-modal').hide();
|
||||
});
|
||||
|
||||
// Close modal with Escape key
|
||||
$(document).on('keydown', function(e) {
|
||||
if (e.keyCode === 27) { // Escape key
|
||||
$('#pc-announcements-274-delete-modal').hide();
|
||||
}
|
||||
});
|
||||
|
||||
// Media upload functionality
|
||||
var customUploader;
|
||||
|
||||
$('.pc-announcements-274-upload-image-btn').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var $button = $(this);
|
||||
var $inputField = $button.siblings('input[type="url"]');
|
||||
|
||||
// If the uploader object has already been created, reopen the dialog
|
||||
if (customUploader) {
|
||||
customUploader.open();
|
||||
return;
|
||||
}
|
||||
|
||||
// Extend the wp.media object
|
||||
customUploader = wp.media.frames.file_frame = wp.media({
|
||||
title: pc_announcements_274_ajax.i18n.choose_image,
|
||||
button: {
|
||||
text: pc_announcements_274_ajax.i18n.choose_image
|
||||
},
|
||||
multiple: false
|
||||
});
|
||||
|
||||
// When a file is selected, grab the URL and set it as the text field's value
|
||||
customUploader.on('select', function() {
|
||||
var attachment = customUploader.state().get('selection').first().toJSON();
|
||||
$inputField.val(attachment.url);
|
||||
|
||||
// Update preview if exists
|
||||
var $preview = $inputField.siblings('.pc-announcements-274-image-preview');
|
||||
if ($preview.length === 0) {
|
||||
$preview = $('<div class="pc-announcements-274-image-preview"></div>').insertAfter($inputField.parent());
|
||||
}
|
||||
$preview.html('<img src="' + attachment.url + '" alt="' + pc_announcements_274_ajax.i18n.preview + '" style="max-width: 200px; height: auto;">');
|
||||
});
|
||||
|
||||
// Open the uploader dialog
|
||||
customUploader.open();
|
||||
});
|
||||
|
||||
// Auto-hide notices
|
||||
$('.notice.is-dismissible').on('click', '.notice-dismiss', function() {
|
||||
$(this).closest('.notice').fadeOut(function() {
|
||||
$(this).remove();
|
||||
});
|
||||
});
|
||||
|
||||
// Image URL field change handler
|
||||
$('input[name="image_url"]').on('input', function() {
|
||||
var url = $(this).val();
|
||||
var $preview = $(this).siblings('.pc-announcements-274-image-preview');
|
||||
|
||||
if (url) {
|
||||
if ($preview.length === 0) {
|
||||
$preview = $('<div class="pc-announcements-274-image-preview"></div>').insertAfter($(this).parent());
|
||||
}
|
||||
$preview.html('<img src="' + url + '" alt="' + pc_announcements_274_ajax.i18n.preview + '" style="max-width: 200px; height: auto;" onerror="this.style.display=\'none\'">');
|
||||
} else if ($preview.length > 0) {
|
||||
$preview.empty();
|
||||
}
|
||||
});
|
||||
|
||||
// Date/time validation
|
||||
$('#start_date, #end_date').on('change', function() {
|
||||
var startDate = $('#start_date').val();
|
||||
var endDate = $('#end_date').val();
|
||||
|
||||
if (startDate && endDate && new Date(startDate) >= new Date(endDate)) {
|
||||
$('<div class="notice notice-warning is-dismissible"><p>' + pc_announcements_274_ajax.i18n.end_date_warning + '</p></div>')
|
||||
.insertAfter('.wp-header-end')
|
||||
.delay(5000)
|
||||
.fadeOut(function() {
|
||||
$(this).remove();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
197
chat/templates/Announcements/admin/templates/edit-page.php
Normal file
197
chat/templates/Announcements/admin/templates/edit-page.php
Normal file
@@ -0,0 +1,197 @@
|
||||
<?php
|
||||
/**
|
||||
* Edit/Add announcement page template
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$is_edit = !empty($announcement) && isset($announcement->id) && $announcement->id > 0;
|
||||
$page_title = $is_edit ? __('Edit Announcement', 'pc-announcements-274') : __('Add New Announcement', 'pc-announcements-274');
|
||||
?>
|
||||
|
||||
<div class="wrap pc-announcements-274-wrap">
|
||||
<h1 class="wp-heading-inline">
|
||||
<?php echo esc_html($page_title); ?>
|
||||
</h1>
|
||||
<a href="<?php echo admin_url('admin.php?page=pc-announcements-274'); ?>" class="page-title-action">
|
||||
<?php _e('Back to List', 'pc-announcements-274'); ?>
|
||||
</a>
|
||||
<hr class="wp-header-end">
|
||||
|
||||
<form id="pc-announcements-274-form" class="pc-announcements-274-form">
|
||||
<div class="pc-announcements-274-main-content">
|
||||
<div class="pc-announcements-274-card">
|
||||
<div class="pc-announcements-274-card-header">
|
||||
<h2><?php _e('Announcement Details', 'pc-announcements-274'); ?></h2>
|
||||
</div>
|
||||
<div class="pc-announcements-274-card-body">
|
||||
<div class="pc-announcements-274-form-row">
|
||||
<div class="pc-announcements-274-form-group">
|
||||
<label for="title" class="pc-announcements-274-label">
|
||||
<?php _e('Title', 'pc-announcements-274'); ?> <span class="required">*</span>
|
||||
</label>
|
||||
<input type="text" id="title" name="title" class="regular-text" required
|
||||
value="<?php echo $is_edit ? esc_attr($announcement->title) : ''; ?>"
|
||||
placeholder="<?php _e('Enter announcement title', 'pc-announcements-274'); ?>">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pc-announcements-274-form-row">
|
||||
<div class="pc-announcements-274-form-group">
|
||||
<label for="banner_color" class="pc-announcements-274-label">
|
||||
<?php _e('Banner Color', 'pc-announcements-274'); ?>
|
||||
</label>
|
||||
<div class="pc-announcements-274-color-picker">
|
||||
<input type="color" id="banner_color" name="banner_color"
|
||||
value="<?php echo $is_edit ? esc_attr($announcement->banner_color) : '#0d47a1'; ?>"
|
||||
style="width: 60px; height: 40px; padding: 2px; cursor: pointer;">
|
||||
<input type="text" id="banner_color_text" class="regular-text"
|
||||
value="<?php echo $is_edit ? esc_attr($announcement->banner_color) : '#0d47a1'; ?>"
|
||||
placeholder="#0d47a1">
|
||||
</div>
|
||||
<p class="description">
|
||||
<?php _e('Choose the background color for the announcement banner', 'pc-announcements-274'); ?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pc-announcements-274-form-row">
|
||||
<div class="pc-announcements-274-form-group">
|
||||
<label for="link_url" class="pc-announcements-274-label">
|
||||
<?php _e('Link URL', 'pc-announcements-274'); ?>
|
||||
</label>
|
||||
<input type="url" id="link_url" name="link_url" class="regular-text"
|
||||
value="<?php echo $is_edit ? esc_url($announcement->link_url) : ''; ?>"
|
||||
placeholder="<?php _e('https://example.com', 'pc-announcements-274'); ?>">
|
||||
<p class="description">
|
||||
<?php _e('Enter a URL to make the announcement banner clickable', 'pc-announcements-274'); ?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pc-announcements-274-form-row">
|
||||
<div class="pc-announcements-274-form-group">
|
||||
<label for="image_url" class="pc-announcements-274-label">
|
||||
<?php _e('Image URL', 'pc-announcements-274'); ?>
|
||||
</label>
|
||||
<div class="pc-announcements-274-media-upload">
|
||||
<input type="url" id="image_url" name="image_url" class="regular-text"
|
||||
value="<?php echo $is_edit ? esc_url($announcement->image_url) : ''; ?>"
|
||||
placeholder="<?php _e('https://example.com/image.jpg', 'pc-announcements-274'); ?>">
|
||||
<button type="button" class="button pc-announcements-274-upload-image-btn">
|
||||
<?php _e('Upload Image', 'pc-announcements-274'); ?>
|
||||
</button>
|
||||
</div>
|
||||
<?php if ($is_edit && $announcement->image_url): ?>
|
||||
<div class="pc-announcements-274-image-preview">
|
||||
<img src="<?php echo esc_url($announcement->image_url); ?>" alt="<?php _e('Preview', 'pc-announcements-274'); ?>" style="max-width: 200px; height: auto;">
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pc-announcements-274-card">
|
||||
<div class="pc-announcements-274-card-header">
|
||||
<h2><?php _e('Scheduling', 'pc-announcements-274'); ?></h2>
|
||||
</div>
|
||||
<div class="pc-announcements-274-card-body">
|
||||
<div class="pc-announcements-274-form-row">
|
||||
<div class="pc-announcements-274-form-group">
|
||||
<label for="status" class="pc-announcements-274-label">
|
||||
<?php _e('Status', 'pc-announcements-274'); ?>
|
||||
</label>
|
||||
<select id="status" name="status" class="regular-text">
|
||||
<option value="active" <?php echo ($is_edit && $announcement->status === 'active') ? 'selected' : ''; ?>>
|
||||
<?php _e('Active', 'pc-announcements-274'); ?>
|
||||
</option>
|
||||
<option value="inactive" <?php echo ($is_edit && $announcement->status === 'inactive') ? 'selected' : ''; ?>>
|
||||
<?php _e('Inactive', 'pc-announcements-274'); ?>
|
||||
</option>
|
||||
<option value="scheduled" <?php echo ($is_edit && $announcement->status === 'scheduled') ? 'selected' : ''; ?>>
|
||||
<?php _e('Scheduled', 'pc-announcements-274'); ?>
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pc-announcements-274-form-row">
|
||||
<div class="pc-announcements-274-form-group">
|
||||
<label for="start_date" class="pc-announcements-274-label">
|
||||
<?php _e('Start Date', 'pc-announcements-274'); ?>
|
||||
</label>
|
||||
<input type="datetime-local" id="start_date" name="start_date"
|
||||
value="<?php echo $is_edit && $announcement->start_date ? date('Y-m-d\TH:i', strtotime($announcement->start_date)) : ''; ?>">
|
||||
<p class="description">
|
||||
<?php _e('Leave empty to start immediately', 'pc-announcements-274'); ?>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="pc-announcements-274-form-group">
|
||||
<label for="end_date" class="pc-announcements-274-label">
|
||||
<?php _e('End Date', 'pc-announcements-274'); ?>
|
||||
</label>
|
||||
<input type="datetime-local" id="end_date" name="end_date"
|
||||
value="<?php echo $is_edit && $announcement->end_date ? date('Y-m-d\TH:i', strtotime($announcement->end_date)) : ''; ?>">
|
||||
<p class="description">
|
||||
<?php _e('Leave empty to show indefinitely', 'pc-announcements-274'); ?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pc-announcements-274-sidebar">
|
||||
<div class="pc-announcements-274-card">
|
||||
<div class="pc-announcements-274-card-header">
|
||||
<h2><?php _e('Publish', 'pc-announcements-274'); ?></h2>
|
||||
</div>
|
||||
<div class="pc-announcements-274-card-body">
|
||||
<input type="hidden" name="id" value="<?php echo $is_edit ? $announcement->id : 0; ?>">
|
||||
<input type="hidden" name="action" value="pc_announcements_274_action">
|
||||
<input type="hidden" name="sub_action" value="save_announcement">
|
||||
<?php wp_nonce_field('pc_announcements_274_nonce', 'nonce'); ?>
|
||||
|
||||
<div class="pc-announcements-274-publish-actions">
|
||||
<button type="submit" class="button button-primary button-large">
|
||||
<?php echo $is_edit ? __('Update Announcement', 'pc-announcements-274') : __('Create Announcement', 'pc-announcements-274'); ?>
|
||||
</button>
|
||||
<a href="<?php echo admin_url('admin.php?page=pc-announcements-274'); ?>" class="button">
|
||||
<?php _e('Cancel', 'pc-announcements-274'); ?>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<?php if ($is_edit): ?>
|
||||
<div class="pc-announcements-274-form-info">
|
||||
<p><strong><?php _e('Created:', 'pc-announcements-274'); ?></strong> <?php echo date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($announcement->created_at)); ?></p>
|
||||
<?php if ($announcement->updated_at !== $announcement->created_at): ?>
|
||||
<p><strong><?php _e('Last Updated:', 'pc-announcements-274'); ?></strong> <?php echo date_i18n(get_option('date_format') . ' ' . get_option('time_format'), strtotime($announcement->updated_at)); ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="pc-announcements-274-delete-modal" class="pc-announcements-274-modal" style="display: none;">
|
||||
<div class="pc-announcements-274-modal-backdrop"></div>
|
||||
<div class="pc-announcements-274-modal-content">
|
||||
<div class="pc-announcements-274-modal-header">
|
||||
<h3><?php _e('Delete Announcement', 'pc-announcements-274'); ?></h3>
|
||||
</div>
|
||||
<div class="pc-announcements-274-modal-body">
|
||||
<p><?php _e('Are you sure you want to delete this announcement? This action cannot be undone.', 'pc-announcements-274'); ?></p>
|
||||
</div>
|
||||
<div class="pc-announcements-274-modal-footer">
|
||||
<button type="button" class="button pc-announcements-274-cancel-delete"><?php _e('Cancel', 'pc-announcements-274'); ?></button>
|
||||
<button type="button" class="button button-danger pc-announcements-274-confirm-delete"><?php _e('Delete', 'pc-announcements-274'); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
30
chat/templates/Announcements/admin/templates/error-page.php
Normal file
30
chat/templates/Announcements/admin/templates/error-page.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/**
|
||||
* Error page template for database table issues
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="wrap pc-announcements-274-wrap">
|
||||
<h1 class="wp-heading-inline">
|
||||
<?php _e('Announcements', 'pc-announcements-274'); ?>
|
||||
</h1>
|
||||
<hr class="wp-header-end">
|
||||
|
||||
<div class="pc-announcements-274-card">
|
||||
<div class="pc-announcements-274-card-header">
|
||||
<h2><?php _e('Database Error', 'pc-announcements-274'); ?></h2>
|
||||
</div>
|
||||
<div class="pc-announcements-274-card-body">
|
||||
<div class="pc-announcements-274-empty-state">
|
||||
<div class="pc-announcements-274-empty-icon">⚠️</div>
|
||||
<h3><?php _e('Database table not found', 'pc-announcements-274'); ?></h3>
|
||||
<p><?php _e('The announcements database table has not been created yet. Please try deactivating and reactivating the plugin.', 'pc-announcements-274'); ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
133
chat/templates/Announcements/admin/templates/list-page.php
Normal file
133
chat/templates/Announcements/admin/templates/list-page.php
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
/**
|
||||
* List page template for announcements
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="wrap pc-announcements-274-wrap">
|
||||
<h1 class="wp-heading-inline">
|
||||
<?php _e('Announcements', 'pc-announcements-274'); ?>
|
||||
</h1>
|
||||
<a href="<?php echo admin_url('admin.php?page=pc-announcements-274-add'); ?>" class="page-title-action">
|
||||
<?php _e('Add New', 'pc-announcements-274'); ?>
|
||||
</a>
|
||||
<hr class="wp-header-end">
|
||||
|
||||
<?php if (isset($_GET['message']) && $_GET['message'] === 'success'): ?>
|
||||
<div class="notice notice-success is-dismissible">
|
||||
<p><?php _e('Announcement saved successfully!', 'pc-announcements-274'); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="pc-announcements-274-card">
|
||||
<div class="pc-announcements-274-card-header">
|
||||
<h2><?php _e('All Announcements', 'pc-announcements-274'); ?></h2>
|
||||
</div>
|
||||
<div class="pc-announcements-274-card-body">
|
||||
<?php if (!empty($announcements) && is_array($announcements)): ?>
|
||||
<table class="wp-list-table widefat fixed striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="manage-column column-title"><?php _e('Title', 'pc-announcements-274'); ?></th>
|
||||
<th scope="col" class="manage-column column-status"><?php _e('Status', 'pc-announcements-274'); ?></th>
|
||||
<th scope="col" class="manage-column column-date"><?php _e('Schedule', 'pc-announcements-274'); ?></th>
|
||||
<th scope="col" class="manage-column column-date"><?php _e('Created', 'pc-announcements-274'); ?></th>
|
||||
<th scope="col" class="manage-column column-actions"><?php _e('Actions', 'pc-announcements-274'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($announcements as $announcement): ?>
|
||||
<?php
|
||||
$current_time = current_time('timestamp');
|
||||
$start_timestamp = $announcement->start_date ? strtotime($announcement->start_date) : 0;
|
||||
$end_timestamp = $announcement->end_date ? strtotime($announcement->end_date) : 9999999999;
|
||||
|
||||
$is_active = $announcement->status === 'active' &&
|
||||
(!$start_timestamp || $current_time >= $start_timestamp) &&
|
||||
(!$end_timestamp || $current_time <= $end_timestamp);
|
||||
?>
|
||||
<tr>
|
||||
<td class="column-title">
|
||||
<strong>
|
||||
<a href="<?php echo admin_url('admin.php?page=pc-announcements-274-add&id=' . $announcement->id); ?>">
|
||||
<?php echo esc_html($announcement->title); ?>
|
||||
</a>
|
||||
</strong>
|
||||
<?php if ($is_active): ?>
|
||||
<span class="pc-announcements-274-active-badge"><?php _e('Active', 'pc-announcements-274'); ?></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="column-status">
|
||||
<span class="pc-announcements-274-status pc-announcements-274-status-<?php echo esc_attr($announcement->status); ?>">
|
||||
<?php
|
||||
$status_labels = array(
|
||||
'active' => __('Active', 'pc-announcements-274'),
|
||||
'inactive' => __('Inactive', 'pc-announcements-274'),
|
||||
'scheduled' => __('Scheduled', 'pc-announcements-274')
|
||||
);
|
||||
echo esc_html($status_labels[$announcement->status] ?? $announcement->status);
|
||||
?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="column-date">
|
||||
<?php if ($announcement->start_date): ?>
|
||||
<?php _e('From', 'pc-announcements-274'); ?> <?php echo date_i18n(get_option('date_format'), strtotime($announcement->start_date)); ?><br>
|
||||
<?php endif; ?>
|
||||
<?php if ($announcement->end_date): ?>
|
||||
<?php _e('Until', 'pc-announcements-274'); ?> <?php echo date_i18n(get_option('date_format'), strtotime($announcement->end_date)); ?>
|
||||
<?php endif; ?>
|
||||
<?php if (!$announcement->start_date && !$announcement->end_date): ?>
|
||||
<?php _e('Always', 'pc-announcements-274'); ?>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="column-date">
|
||||
<?php echo date_i18n(get_option('date_format'), strtotime($announcement->created_at)); ?>
|
||||
</td>
|
||||
<td class="column-actions">
|
||||
<a href="<?php echo admin_url('admin.php?page=pc-announcements-274-add&id=' . $announcement->id); ?>" class="button">
|
||||
<?php _e('Edit', 'pc-announcements-274'); ?>
|
||||
</a>
|
||||
<button class="button button-danger pc-announcements-274-delete-btn" data-id="<?php echo $announcement->id; ?>">
|
||||
<?php _e('Delete', 'pc-announcements-274'); ?>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php if (!empty($total_pages) && $total_pages > 1): ?>
|
||||
<div class="tablenav bottom">
|
||||
<div class="tablenav-pages">
|
||||
<?php
|
||||
$current_url = admin_url('admin.php?page=pc-announcements-274');
|
||||
echo paginate_links(array(
|
||||
'base' => add_query_arg('paged', '%#%', $current_url),
|
||||
'format' => '',
|
||||
'prev_text' => __('« Previous', 'pc-announcements-274'),
|
||||
'next_text' => __('Next »', 'pc-announcements-274'),
|
||||
'total' => $total_pages,
|
||||
'current' => $current_page,
|
||||
));
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php else: ?>
|
||||
<div class="pc-announcements-274-empty-state">
|
||||
<div class="pc-announcements-274-empty-icon">📢</div>
|
||||
<h3><?php _e('No announcements found', 'pc-announcements-274'); ?></h3>
|
||||
<p><?php _e('Create your first announcement to get started.', 'pc-announcements-274'); ?></p>
|
||||
<a href="<?php echo admin_url('admin.php?page=pc-announcements-274-add'); ?>" class="button button-primary">
|
||||
<?php _e('Add New Announcement', 'pc-announcements-274'); ?>
|
||||
</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
74
chat/templates/Announcements/includes/class-install.php
Normal file
74
chat/templates/Announcements/includes/class-install.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/**
|
||||
* Installation class for database setup and cleanup
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class PC_Announcements_274_Install {
|
||||
|
||||
/**
|
||||
* Install plugin - create database tables and set default options
|
||||
*/
|
||||
public static function install() {
|
||||
global $wpdb;
|
||||
|
||||
// Ensure plugin constants are defined
|
||||
if (!defined('PC_ANNOUNCEMENTS_274_VERSION')) {
|
||||
define('PC_ANNOUNCEMENTS_274_VERSION', '1.0.0');
|
||||
}
|
||||
|
||||
if (!function_exists('dbDelta')) {
|
||||
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
||||
}
|
||||
|
||||
$table_name = $wpdb->prefix . 'pc_announcements_274';
|
||||
|
||||
$charset_collate = '';
|
||||
if (method_exists($wpdb, 'get_charset_collate')) {
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
}
|
||||
|
||||
if (empty($charset_collate)) {
|
||||
$charset_collate = 'DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci';
|
||||
}
|
||||
|
||||
$sql = "CREATE TABLE $table_name (
|
||||
id int(11) NOT NULL AUTO_INCREMENT,
|
||||
title varchar(255) NOT NULL,
|
||||
message text NOT NULL,
|
||||
image_url varchar(500) DEFAULT NULL,
|
||||
banner_color varchar(7) DEFAULT '#0d47a1',
|
||||
link_url varchar(500) DEFAULT NULL,
|
||||
start_date datetime DEFAULT NULL,
|
||||
end_date datetime DEFAULT NULL,
|
||||
status varchar(20) DEFAULT 'active',
|
||||
created_at datetime DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
created_by int(11) NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY status (status),
|
||||
KEY start_date (start_date),
|
||||
KEY end_date (end_date)
|
||||
) $charset_collate;";
|
||||
|
||||
dbDelta($sql);
|
||||
|
||||
add_option('pc_announcements_274_version', PC_ANNOUNCEMENTS_274_VERSION);
|
||||
add_option('pc_announcements_274_db_version', '1.0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get table name
|
||||
*/
|
||||
public static function get_table_name() {
|
||||
global $wpdb;
|
||||
if (!isset($wpdb)) {
|
||||
return null;
|
||||
}
|
||||
return $wpdb->prefix . 'pc_announcements_274';
|
||||
}
|
||||
}
|
||||
126
chat/templates/Announcements/pc-announcements-274.php
Normal file
126
chat/templates/Announcements/pc-announcements-274.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: Announcements Manager
|
||||
* Plugin URI: https://plugincompass.com/plugins/pc-announcements-274
|
||||
* Description: Create and manage announcements that display at the top of public pages with scheduling capabilities.
|
||||
* Version: 1.0.0
|
||||
* Author: Plugin Compass
|
||||
* Author URI: https://plugincompass.com
|
||||
* License: GPL-2.0-or-later
|
||||
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
||||
* Text Domain: pc-announcements-274
|
||||
* Domain Path: /languages
|
||||
* Requires at least: 5.0
|
||||
* Requires PHP: 7.4
|
||||
* Update URI: false
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Define plugin constants
|
||||
define('PC_ANNOUNCEMENTS_274_VERSION', '1.0.0');
|
||||
define('PC_ANNOUNCEMENTS_274_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
||||
define('PC_ANNOUNCEMENTS_274_PLUGIN_URL', plugin_dir_url(__FILE__));
|
||||
define('PC_ANNOUNCEMENTS_274_PLUGIN_BASENAME', plugin_basename(__FILE__));
|
||||
|
||||
// Prevent WordPress.org update checks
|
||||
add_filter('site_transient_update_plugins', function($value) {
|
||||
$plugin_file = plugin_basename(__FILE__);
|
||||
if (isset($value->response[$plugin_file])) {
|
||||
unset($value->response[$plugin_file]);
|
||||
}
|
||||
return $value;
|
||||
});
|
||||
|
||||
/**
|
||||
* Main plugin class
|
||||
*/
|
||||
class PC_Announcements_274 {
|
||||
|
||||
/**
|
||||
* Single instance of the class
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Get single instance
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if (null === self::$instance) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
private function __construct() {
|
||||
add_action('plugins_loaded', array($this, 'init'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize plugin
|
||||
*/
|
||||
public function init() {
|
||||
// Load text domain
|
||||
load_plugin_textdomain('pc-announcements-274', false, dirname(PC_ANNOUNCEMENTS_274_PLUGIN_BASENAME) . '/languages');
|
||||
|
||||
// Include required files
|
||||
$this->includes();
|
||||
|
||||
// Initialize admin
|
||||
if (is_admin()) {
|
||||
new PC_Announcements_274_Admin();
|
||||
}
|
||||
|
||||
// Initialize frontend
|
||||
new PC_Announcements_274_Frontend();
|
||||
}
|
||||
|
||||
/**
|
||||
* Include required files
|
||||
*/
|
||||
private function includes() {
|
||||
$files = array(
|
||||
PC_ANNOUNCEMENTS_274_PLUGIN_DIR . 'includes/class-install.php',
|
||||
PC_ANNOUNCEMENTS_274_PLUGIN_DIR . 'admin/class-admin.php',
|
||||
PC_ANNOUNCEMENTS_274_PLUGIN_DIR . 'public/class-frontend.php'
|
||||
);
|
||||
|
||||
foreach ($files as $file_path) {
|
||||
if (file_exists($file_path)) {
|
||||
require_once $file_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate plugin
|
||||
*/
|
||||
public static function activate() {
|
||||
// Ensure install class is available during activation (plugins_loaded hasn't run yet)
|
||||
if (!class_exists('PC_Announcements_274_Install')) {
|
||||
require_once PC_ANNOUNCEMENTS_274_PLUGIN_DIR . 'includes/class-install.php';
|
||||
}
|
||||
PC_Announcements_274_Install::install();
|
||||
flush_rewrite_rules();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate plugin
|
||||
*/
|
||||
public static function deactivate() {
|
||||
flush_rewrite_rules();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize plugin
|
||||
PC_Announcements_274::get_instance();
|
||||
|
||||
// Register activation and deactivation hooks
|
||||
register_activation_hook(__FILE__, array('PC_Announcements_274', 'activate'));
|
||||
register_deactivation_hook(__FILE__, array('PC_Announcements_274', 'deactivate'));
|
||||
239
chat/templates/Announcements/public/class-frontend.php
Normal file
239
chat/templates/Announcements/public/class-frontend.php
Normal file
@@ -0,0 +1,239 @@
|
||||
<?php
|
||||
/**
|
||||
* Frontend class for displaying announcements on public pages
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class PC_Announcements_274_Frontend {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_scripts'));
|
||||
add_action('wp_head', array($this, 'display_announcements'), 1);
|
||||
add_shortcode('pc_announcements_274', array($this, 'get_announcements_for_shortcode'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue frontend scripts and styles
|
||||
*/
|
||||
public function enqueue_frontend_scripts() {
|
||||
wp_enqueue_style('pc-announcements-274-public-style', PC_ANNOUNCEMENTS_274_PLUGIN_URL . 'public/css/public-style.css', array(), PC_ANNOUNCEMENTS_274_VERSION);
|
||||
wp_enqueue_script('pc-announcements-274-public-script', PC_ANNOUNCEMENTS_274_PLUGIN_URL . 'public/js/public-script.js', array('jquery'), PC_ANNOUNCEMENTS_274_VERSION, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get active announcements
|
||||
*/
|
||||
private function get_active_announcements() {
|
||||
global $wpdb;
|
||||
$table_name = PC_Announcements_274_Install::get_table_name();
|
||||
|
||||
if (empty($table_name)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$current_time = current_time('mysql');
|
||||
|
||||
$announcements = $wpdb->get_results($wpdb->prepare("
|
||||
SELECT * FROM $table_name
|
||||
WHERE status = %s
|
||||
AND (start_date IS NULL OR start_date <= %s)
|
||||
AND (end_date IS NULL OR end_date >= %s)
|
||||
ORDER BY created_at DESC
|
||||
", 'active', $current_time, $current_time));
|
||||
|
||||
if ($announcements === null) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return $announcements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display announcements at the top of pages
|
||||
*/
|
||||
public function display_announcements() {
|
||||
// Don't show on admin pages or in WordPress admin
|
||||
if (is_admin()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$announcements = $this->get_active_announcements();
|
||||
|
||||
if (empty($announcements)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Display only the most recent active announcement
|
||||
$announcement = $announcements[0];
|
||||
|
||||
$this->render_announcement($announcement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render single announcement
|
||||
*/
|
||||
private function render_announcement($announcement) {
|
||||
$title = esc_html($announcement->title);
|
||||
$message = wpautop(wp_kses_post($announcement->message));
|
||||
$image_url = esc_url($announcement->image_url);
|
||||
$banner_color = esc_attr($announcement->banner_color);
|
||||
$link_url = esc_url($announcement->link_url);
|
||||
$close_text = __('Close', 'pc-announcements-274');
|
||||
|
||||
$banner_style = '';
|
||||
if (!empty($banner_color) && $banner_color !== '#0d47a1') {
|
||||
$banner_style = 'background: ' . $banner_color . ';';
|
||||
}
|
||||
|
||||
$has_link = !empty($link_url);
|
||||
$link_attrs = $has_link ? 'href="' . $link_url . '" target="_blank" rel="noopener noreferrer"' : '';
|
||||
$close_button = $has_link ? '' : '<button class="pc-announcements-274-close" aria-label="' . esc_attr($close_text) . '">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18 6L6 18M6 6L18 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</button>';
|
||||
|
||||
?>
|
||||
<div class="pc-announcements-274-announcement" data-announcement-id="<?php echo $announcement->id; ?>" style="<?php echo $banner_style; ?>">
|
||||
<div class="pc-announcements-274-container">
|
||||
<div class="pc-announcements-274-content">
|
||||
<?php if ($has_link): ?>
|
||||
<a class="pc-announcements-274-link" <?php echo $link_attrs; ?>>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($image_url)): ?>
|
||||
<div class="pc-announcements-274-image">
|
||||
<img src="<?php echo $image_url; ?>" alt="<?php echo esc_attr($title); ?>" loading="lazy">
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="pc-announcements-274-text">
|
||||
<?php if (!empty($title)): ?>
|
||||
<h3 class="pc-announcements-274-title"><?php echo $title; ?></h3>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty(trim($message))): ?>
|
||||
<div class="pc-announcements-274-message">
|
||||
<?php echo $message; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if ($has_link): ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php echo $close_button; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Get announcements for shortcode
|
||||
*/
|
||||
public function get_announcements_for_shortcode($atts) {
|
||||
$atts = shortcode_atts(array(
|
||||
'count' => 1,
|
||||
'show_image' => true,
|
||||
'show_close' => true,
|
||||
'class' => ''
|
||||
), $atts, 'pc_announcements_274');
|
||||
|
||||
$announcements = $this->get_active_announcements();
|
||||
|
||||
if (empty($announcements)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$output = '';
|
||||
$count = min(intval($atts['count']), count($announcements));
|
||||
$show_image = filter_var($atts['show_image'], FILTER_VALIDATE_BOOLEAN);
|
||||
$show_close = filter_var($atts['show_close'], FILTER_VALIDATE_BOOLEAN);
|
||||
$custom_class = sanitize_html_class($atts['class']);
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$announcement = $announcements[$i];
|
||||
$output .= $this->render_announcement_html($announcement, $show_image, $show_close, $custom_class);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render announcement as HTML string
|
||||
*/
|
||||
private function render_announcement_html($announcement, $show_image = true, $show_close = true, $custom_class = '') {
|
||||
$title = esc_html($announcement->title);
|
||||
$message = wpautop(wp_kses_post($announcement->message));
|
||||
$image_url = esc_url($announcement->image_url);
|
||||
$banner_color = esc_attr($announcement->banner_color);
|
||||
$link_url = esc_url($announcement->link_url);
|
||||
$close_text = __('Close', 'pc-announcements-274');
|
||||
$class_names = array('pc-announcements-274-announcement');
|
||||
|
||||
if (!empty($custom_class)) {
|
||||
$class_names[] = $custom_class;
|
||||
}
|
||||
|
||||
$banner_style = '';
|
||||
if (!empty($banner_color) && $banner_color !== '#0d47a1') {
|
||||
$banner_style = 'style="background: ' . $banner_color . ';"';
|
||||
}
|
||||
|
||||
$has_link = !empty($link_url);
|
||||
$link_attrs = $has_link ? 'href="' . $link_url . '" target="_blank" rel="noopener noreferrer"' : '';
|
||||
|
||||
$html = '<div class="' . implode(' ', $class_names) . '" data-announcement-id="' . $announcement->id . '" ' . $banner_style . '>';
|
||||
$html .= '<div class="pc-announcements-274-container">';
|
||||
$html .= '<div class="pc-announcements-274-content">';
|
||||
|
||||
if ($has_link) {
|
||||
$html .= '<a class="pc-announcements-274-link" ' . $link_attrs . '>';
|
||||
}
|
||||
|
||||
if ($show_image && !empty($image_url)) {
|
||||
$html .= '<div class="pc-announcements-274-image">';
|
||||
$html .= '<img src="' . $image_url . '" alt="' . esc_attr($title) . '" loading="lazy">';
|
||||
$html .= '</div>';
|
||||
}
|
||||
|
||||
$html .= '<div class="pc-announcements-274-text">';
|
||||
|
||||
if (!empty($title)) {
|
||||
$html .= '<h3 class="pc-announcements-274-title">' . $title . '</h3>';
|
||||
}
|
||||
|
||||
if (!empty(trim($message))) {
|
||||
$html .= '<div class="pc-announcements-274-message">' . $message . '</div>';
|
||||
}
|
||||
|
||||
$html .= '</div>';
|
||||
|
||||
if ($has_link) {
|
||||
$html .= '</a>';
|
||||
}
|
||||
|
||||
$html .= '</div>';
|
||||
|
||||
if ($show_close && !$has_link) {
|
||||
$html .= '<button class="pc-announcements-274-close" aria-label="' . esc_attr($close_text) . '">';
|
||||
$html .= '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">';
|
||||
$html .= '<path d="M18 6L6 18M6 6L18 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>';
|
||||
$html .= '</svg>';
|
||||
$html .= '</button>';
|
||||
}
|
||||
|
||||
$html .= '</div>';
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
424
chat/templates/Announcements/public/css/public-style.css
Normal file
424
chat/templates/Announcements/public/css/public-style.css
Normal file
@@ -0,0 +1,424 @@
|
||||
/* PC Announcements 274 - Public Styles */
|
||||
|
||||
/* CSS Custom Properties */
|
||||
:root {
|
||||
--pc-announcements-274-bg-primary: #0d47a1;
|
||||
--pc-announcements-274-bg-secondary: #1565c0;
|
||||
--pc-announcements-274-text-primary: #ffffff;
|
||||
--pc-announcements-274-text-secondary: rgba(255, 255, 255, 0.9);
|
||||
--pc-announcements-274-border-color: rgba(255, 255, 255, 0.2);
|
||||
--pc-announcements-274-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
--pc-announcements-274-shadow-hover: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||
--pc-announcements-274-close-color: rgba(255, 255, 255, 0.8);
|
||||
--pc-announcements-274-close-hover: #ffffff;
|
||||
--pc-announcements-274-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
||||
--pc-announcements-274-font-size: 16px;
|
||||
--pc-announcements-274-line-height: 1.5;
|
||||
--pc-announcements-274-border-radius: 8px;
|
||||
--pc-announcements-274-z-index: 9999;
|
||||
--pc-announcements-274-transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
/* Main Announcement Container */
|
||||
.pc-announcements-274-announcement {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
background: linear-gradient(135deg, var(--pc-announcements-274-bg-primary), var(--pc-announcements-274-bg-secondary));
|
||||
border-bottom: 1px solid var(--pc-announcements-274-border-color);
|
||||
box-shadow: var(--pc-announcements-274-shadow);
|
||||
font-family: var(--pc-announcements-274-font-family);
|
||||
font-size: var(--pc-announcements-274-font-size);
|
||||
line-height: var(--pc-announcements-274-line-height);
|
||||
color: var(--pc-announcements-274-text-primary);
|
||||
z-index: var(--pc-announcements-274-z-index);
|
||||
transition: var(--pc-announcements-274-transition);
|
||||
}
|
||||
|
||||
.pc-announcements-274-announcement:hover {
|
||||
box-shadow: var(--pc-announcements-274-shadow-hover);
|
||||
}
|
||||
|
||||
/* Content Container */
|
||||
.pc-announcements-274-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 16px 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Content Layout */
|
||||
.pc-announcements-274-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* Image Styles */
|
||||
.pc-announcements-274-image {
|
||||
flex-shrink: 0;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: var(--pc-announcements-274-border-radius);
|
||||
overflow: hidden;
|
||||
border: 2px solid var(--pc-announcements-274-border-color);
|
||||
box-shadow: var(--pc-announcements-274-shadow);
|
||||
}
|
||||
|
||||
.pc-announcements-274-image img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Text Content */
|
||||
.pc-announcements-274-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.pc-announcements-274-title {
|
||||
margin: 0 0 4px 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
line-height: 1.3;
|
||||
color: var(--pc-announcements-274-text-primary);
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.pc-announcements-274-message {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
color: var(--pc-announcements-274-text-secondary);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.pc-announcements-274-message p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.pc-announcements-274-message p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Link Styles */
|
||||
.pc-announcements-274-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
transition: var(--pc-announcements-274-transition);
|
||||
}
|
||||
|
||||
.pc-announcements-274-link:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.pc-announcements-274-link:focus {
|
||||
outline: 2px solid var(--pc-announcements-274-close-hover);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* Close Button */
|
||||
.pc-announcements-274-close {
|
||||
flex-shrink: 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: none;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 50%;
|
||||
color: var(--pc-announcements-274-close-color);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: var(--pc-announcements-274-transition);
|
||||
padding: 0;
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.pc-announcements-274-close:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
color: var(--pc-announcements-274-close-hover);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.pc-announcements-274-close:focus {
|
||||
outline: 2px solid var(--pc-announcements-274-close-hover);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.pc-announcements-274-close svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
.pc-announcements-274-announcement.pc-announcements-274-hidden {
|
||||
animation: pc-announcements-274-slide-up 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
||||
}
|
||||
|
||||
.pc-announcements-274-announcement.pc-announcements-274-show {
|
||||
animation: pc-announcements-274-slide-down 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
||||
}
|
||||
|
||||
@keyframes pc-announcements-274-slide-up {
|
||||
0% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
max-height: 200px;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(-100%);
|
||||
opacity: 0;
|
||||
max-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pc-announcements-274-slide-down {
|
||||
0% {
|
||||
transform: translateY(-100%);
|
||||
opacity: 0;
|
||||
max-height: 0;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
max-height: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.pc-announcements-274-container {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.pc-announcements-274-content {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pc-announcements-274-image {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.pc-announcements-274-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.pc-announcements-274-message {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.pc-announcements-274-close {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.pc-announcements-274-container {
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.pc-announcements-274-image {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.pc-announcements-274-title {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.pc-announcements-274-message {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.pc-announcements-274-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.pc-announcements-274-close svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
/* High Contrast Mode Support */
|
||||
@media (prefers-contrast: high) {
|
||||
.pc-announcements-274-announcement {
|
||||
border-bottom: 2px solid #000;
|
||||
}
|
||||
|
||||
.pc-announcements-274-image {
|
||||
border: 2px solid #000;
|
||||
}
|
||||
|
||||
.pc-announcements-274-close {
|
||||
border: 2px solid var(--pc-announcements-274-text-primary);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reduced Motion Support */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.pc-announcements-274-announcement,
|
||||
.pc-announcements-274-close,
|
||||
.pc-announcements-274-announcement:hover {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.pc-announcements-274-close:hover {
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.pc-announcements-274-announcement.pc-announcements-274-hidden,
|
||||
.pc-announcements-274-announcement.pc-announcements-274-show {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
.pc-announcements-274-announcement.pc-announcements-274-hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dark Mode Support */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--pc-announcements-274-bg-primary: #1a237e;
|
||||
--pc-announcements-274-bg-secondary: #283593;
|
||||
--pc-announcements-274-text-primary: #ffffff;
|
||||
--pc-announcements-274-text-secondary: rgba(255, 255, 255, 0.85);
|
||||
--pc-announcements-274-border-color: rgba(255, 255, 255, 0.1);
|
||||
--pc-announcements-274-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
--pc-announcements-274-shadow-hover: 0 4px 8px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
/* Print Styles */
|
||||
@media print {
|
||||
.pc-announcements-274-announcement {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* RTL Support */
|
||||
[dir="rtl"] .pc-announcements-274-content {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
[dir="rtl"] .pc-announcements-274-close {
|
||||
margin-left: 0;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
[dir="rtl"] .pc-announcements-274-close:only-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
/* Custom Theme Variations */
|
||||
.pc-announcements-274-announcement.pc-announcements-274-success {
|
||||
--pc-announcements-274-bg-primary: #2e7d32;
|
||||
--pc-announcements-274-bg-secondary: #388e3c;
|
||||
}
|
||||
|
||||
.pc-announcements-274-announcement.pc-announcements-274-warning {
|
||||
--pc-announcements-274-bg-primary: #f57c00;
|
||||
--pc-announcements-274-bg-secondary: #fb8c00;
|
||||
}
|
||||
|
||||
.pc-announcements-274-announcement.pc-announcements-274-error {
|
||||
--pc-announcements-274-bg-primary: #c62828;
|
||||
--pc-announcements-274-bg-secondary: #d32f2f;
|
||||
}
|
||||
|
||||
.pc-announcements-274-announcement.pc-announcements-274-info {
|
||||
--pc-announcements-274-bg-primary: #0277bd;
|
||||
--pc-announcements-274-bg-secondary: #0288d1;
|
||||
}
|
||||
|
||||
/* Compact Version */
|
||||
.pc-announcements-274-announcement.pc-announcements-274-compact {
|
||||
--pc-announcements-274-font-size: 14px;
|
||||
}
|
||||
|
||||
.pc-announcements-274-announcement.pc-announcements-274-compact .pc-announcements-274-container {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.pc-announcements-274-announcement.pc-announcements-274-compact .pc-announcements-274-image {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.pc-announcements-274-announcement.pc-announcements-274-compact .pc-announcements-274-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.pc-announcements-274-announcement.pc-announcements-274-compact .pc-announcements-274-message {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* No Image Variant */
|
||||
.pc-announcements-274-announcement.pc-announcements-274-no-image .pc-announcements-274-content {
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.pc-announcements-274-announcement.pc-announcements-274-no-image .pc-announcements-274-text {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
/* Hidden State - Animation */
|
||||
.pc-announcements-274-announcement[aria-hidden="true"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Focus Management */
|
||||
.pc-announcements-274-announcement:focus-within {
|
||||
outline: 2px solid var(--pc-announcements-274-close-hover);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
/* Accessibility Improvements */
|
||||
.pc-announcements-274-announcement[role="banner"] {
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.pc-announcements-274-announcement[role="banner"].pc-announcements-274-hidden {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Browser-specific fixes */
|
||||
@supports (-webkit-appearance: none) {
|
||||
.pc-announcements-274-close {
|
||||
-webkit-appearance: none;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@supports not (display: grid) {
|
||||
.pc-announcements-274-content {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.pc-announcements-274-text {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
219
chat/templates/Announcements/public/js/public-script.js
Normal file
219
chat/templates/Announcements/public/js/public-script.js
Normal file
@@ -0,0 +1,219 @@
|
||||
jQuery(document).ready(function($) {
|
||||
'use strict';
|
||||
|
||||
// Close announcement functionality
|
||||
$('.pc-announcements-274-close').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
var $announcement = $(this).closest('.pc-announcements-274-announcement');
|
||||
var announcementId = $announcement.data('announcement-id');
|
||||
|
||||
// Add hiding class for animation
|
||||
$announcement.addClass('pc-announcements-274-hidden');
|
||||
|
||||
// Store dismissal in localStorage for this session
|
||||
if (typeof(Storage) !== "undefined" && announcementId) {
|
||||
var dismissed = localStorage.getItem('pc_announcements_274_dismissed') || '[]';
|
||||
var dismissedArray = JSON.parse(dismissed);
|
||||
|
||||
if (dismissedArray.indexOf(announcementId) === -1) {
|
||||
dismissedArray.push(announcementId);
|
||||
localStorage.setItem('pc_announcements_274_dismissed', JSON.stringify(dismissedArray));
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from DOM after animation
|
||||
setTimeout(function() {
|
||||
$announcement.attr('aria-hidden', 'true').hide();
|
||||
|
||||
// Adjust body padding if needed
|
||||
adjustBodyPadding();
|
||||
}, 400);
|
||||
});
|
||||
|
||||
// Adjust body padding to prevent content jump when announcement is hidden
|
||||
function adjustBodyPadding() {
|
||||
var $announcement = $('.pc-announcements-274-announcement');
|
||||
if ($announcement.length === 0) {
|
||||
$('body').css('padding-top', '');
|
||||
return;
|
||||
}
|
||||
|
||||
if ($announcement.is(':visible')) {
|
||||
var announcementHeight = $announcement.outerHeight();
|
||||
var currentPadding = parseInt($('body').css('padding-top')) || 0;
|
||||
|
||||
if (currentPadding < announcementHeight) {
|
||||
$('body').css('padding-top', announcementHeight + 'px');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize padding adjustment
|
||||
$(window).on('load', function() {
|
||||
adjustBodyPadding();
|
||||
});
|
||||
|
||||
// Handle window resize
|
||||
$(window).on('resize', function() {
|
||||
adjustBodyPadding();
|
||||
});
|
||||
|
||||
// Check for dismissed announcements on page load
|
||||
function checkDismissedAnnouncements() {
|
||||
if (typeof(Storage) !== "undefined") {
|
||||
var dismissed = localStorage.getItem('pc_announcements_274_dismissed') || '[]';
|
||||
var dismissedArray = JSON.parse(dismissed);
|
||||
|
||||
$('.pc-announcements-274-announcement').each(function() {
|
||||
var announcementId = $(this).data('announcement-id');
|
||||
if (announcementId && dismissedArray.indexOf(announcementId) !== -1) {
|
||||
$(this).attr('aria-hidden', 'true').hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
checkDismissedAnnouncements();
|
||||
|
||||
// Re-check padding after checking dismissed announcements
|
||||
setTimeout(function() {
|
||||
adjustBodyPadding();
|
||||
}, 100);
|
||||
|
||||
// Keyboard navigation
|
||||
$('.pc-announcements-274-close').on('keydown', function(e) {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
$(this).click();
|
||||
}
|
||||
});
|
||||
|
||||
// Escape key to close announcement
|
||||
$(document).on('keydown', function(e) {
|
||||
if (e.key === 'Escape') {
|
||||
var $visibleAnnouncement = $('.pc-announcements-274-announcement:visible');
|
||||
if ($visibleAnnouncement.length > 0) {
|
||||
$visibleAnnouncement.find('.pc-announcements-274-close').first().focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-hide functionality (optional - could be enabled with a data attribute)
|
||||
function initAutoHide() {
|
||||
$('.pc-announcements-274-announcement[data-auto-hide]').each(function() {
|
||||
var $announcement = $(this);
|
||||
var autoHideTime = parseInt($announcement.data('auto-hide')) * 1000;
|
||||
|
||||
if (autoHideTime > 0) {
|
||||
setTimeout(function() {
|
||||
if ($announcement.is(':visible')) {
|
||||
$announcement.find('.pc-announcements-274-close').click();
|
||||
}
|
||||
}, autoHideTime);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
initAutoHide();
|
||||
|
||||
// Add animation classes on initial load
|
||||
$('.pc-announcements-274-announcement:visible').addClass('pc-announcements-274-show');
|
||||
|
||||
// Handle dynamic content loading (if announcements are loaded via AJAX)
|
||||
function reinitializeAnnouncements() {
|
||||
adjustBodyPadding();
|
||||
checkDismissedAnnouncements();
|
||||
initAutoHide();
|
||||
|
||||
$('.pc-announcements-274-announcement:visible').addClass('pc-announcements-274-show');
|
||||
}
|
||||
|
||||
// Expose reinitialize function for global use
|
||||
window.pcAnnouncements274Reinitialize = reinitializeAnnouncements;
|
||||
|
||||
// Smooth scroll to top when announcement appears (optional)
|
||||
function smoothScrollToTop() {
|
||||
if ($('.pc-announcements-274-announcement:visible').length > 0) {
|
||||
$('html, body').animate({
|
||||
scrollTop: 0
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
// Only scroll to top on initial page load if announcement is present
|
||||
if (performance.navigation.type === 0) { // First page load
|
||||
setTimeout(function() {
|
||||
if ($('.pc-announcements-274-announcement:visible').length > 0) {
|
||||
var $announcement = $('.pc-announcements-274-announcement:visible');
|
||||
var announcementId = $announcement.data('announcement-id');
|
||||
|
||||
// Don't scroll if it was just dismissed
|
||||
if (typeof(Storage) !== "undefined" && announcementId) {
|
||||
var dismissed = localStorage.getItem('pc_announcements_274_dismissed') || '[]';
|
||||
var dismissedArray = JSON.parse(dismissed);
|
||||
|
||||
if (dismissedArray.indexOf(announcementId) === -1) {
|
||||
smoothScrollToTop();
|
||||
}
|
||||
} else {
|
||||
smoothScrollToTop();
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// Handle announcement stacking if multiple are shown
|
||||
function handleStacking() {
|
||||
var $announcements = $('.pc-announcements-274-announcement:visible');
|
||||
var offset = 0;
|
||||
|
||||
$announcements.each(function(index) {
|
||||
$(this).css('top', offset + 'px');
|
||||
offset += $(this).outerHeight();
|
||||
});
|
||||
}
|
||||
|
||||
handleStacking();
|
||||
|
||||
// Re-handle stacking on window resize
|
||||
$(window).on('resize', function() {
|
||||
handleStacking();
|
||||
});
|
||||
|
||||
// Accessibility: Focus management
|
||||
function manageFocus() {
|
||||
$('.pc-announcements-274-announcement').attr('role', 'banner');
|
||||
$('.pc-announcements-274-close').attr('tabindex', '0');
|
||||
}
|
||||
|
||||
manageFocus();
|
||||
|
||||
// Performance: Debounce resize events
|
||||
function debounce(func, wait) {
|
||||
var timeout;
|
||||
return function executedFunction() {
|
||||
var context = this;
|
||||
var args = arguments;
|
||||
var later = function() {
|
||||
timeout = null;
|
||||
func.apply(context, args);
|
||||
};
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
};
|
||||
}
|
||||
|
||||
var debouncedResize = debounce(function() {
|
||||
adjustBodyPadding();
|
||||
handleStacking();
|
||||
}, 250);
|
||||
|
||||
$(window).on('resize', debouncedResize);
|
||||
|
||||
// Log for debugging (remove in production)
|
||||
if (window.console && window.console.log && false) { // Set to true for debugging
|
||||
console.log('PC Announcements 274: Initialized');
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,162 @@
|
||||
#!/bin/bash
|
||||
|
||||
# WordPress Plugin Validation Script
|
||||
# This script validates the PHP syntax and structure of the PC Announcements plugin
|
||||
|
||||
echo "🔍 Validating PC Announcements Plugin..."
|
||||
echo "========================================"
|
||||
|
||||
PLUGIN_DIR="/home/web/data/apps/c7f9e5c6-e7c2-4258-a583-ccffcf9791c8/announcements-v274"
|
||||
|
||||
# Check if plugin directory exists
|
||||
if [ ! -d "$PLUGIN_DIR" ]; then
|
||||
echo "❌ Plugin directory not found: $PLUGIN_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Navigate to plugin directory
|
||||
cd "$PLUGIN_DIR" || exit 1
|
||||
|
||||
echo "✅ Plugin directory found: $PLUGIN_DIR"
|
||||
|
||||
# Check main plugin file
|
||||
echo ""
|
||||
echo "📝 Checking main plugin file..."
|
||||
MAIN_FILE="pc-announcements-274.php"
|
||||
if [ ! -f "$MAIN_FILE" ]; then
|
||||
echo "❌ Main plugin file not found: $MAIN_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Main plugin file found: $MAIN_FILE"
|
||||
|
||||
# Check PHP syntax for main file
|
||||
echo ""
|
||||
echo "🔍 Checking PHP syntax for main file..."
|
||||
if php -l "$MAIN_FILE"; then
|
||||
echo "✅ Main plugin file has valid PHP syntax"
|
||||
else
|
||||
echo "❌ Main plugin file has PHP syntax errors"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check all PHP files for syntax errors
|
||||
echo ""
|
||||
echo "🔍 Checking all PHP files for syntax errors..."
|
||||
PHP_FILES=$(find . -name "*.php" -type f)
|
||||
|
||||
if [ -z "$PHP_FILES" ]; then
|
||||
echo "❌ No PHP files found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Found PHP files:"
|
||||
echo "$PHP_FILES"
|
||||
echo ""
|
||||
|
||||
SYNTAX_ERRORS=0
|
||||
for file in $PHP_FILES; do
|
||||
echo "Checking: $file"
|
||||
if php -l "$file"; then
|
||||
echo "✅ Valid syntax"
|
||||
else
|
||||
echo "❌ Syntax errors found"
|
||||
SYNTAX_ERRORS=$((SYNTAX_ERRORS + 1))
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
|
||||
if [ $SYNTAX_ERRORS -eq 0 ]; then
|
||||
echo "✅ All PHP files have valid syntax"
|
||||
else
|
||||
echo "❌ Found $SYNTAX_ERRORS PHP files with syntax errors"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check plugin header
|
||||
echo ""
|
||||
echo "🔍 Checking plugin header..."
|
||||
HEADER_CHECK=$(grep -q "Plugin Name:" "$MAIN_FILE" && grep -q "Plugin URI:" "$MAIN_FILE" && grep -q "Version:" "$MAIN_FILE" && grep -q "Requires PHP:" "$MAIN_FILE" && echo "✅ Plugin header found" || echo "❌ Plugin header incomplete")
|
||||
|
||||
if [[ "$HEADER_CHECK" == *"❌"* ]]; then
|
||||
echo "❌ Plugin header is incomplete"
|
||||
exit 1
|
||||
else
|
||||
echo "$HEADER_CHECK"
|
||||
fi
|
||||
|
||||
# Check required files and directories
|
||||
echo ""
|
||||
echo "🔍 Checking required files and directories..."
|
||||
REQUIRED_FILES=(
|
||||
"includes/class-install.php"
|
||||
"admin/class-admin.php"
|
||||
"public/class-frontend.php"
|
||||
"uninstall.php"
|
||||
"admin/templates/list-page.php"
|
||||
"admin/templates/edit-page.php"
|
||||
"admin/templates/error-page.php"
|
||||
)
|
||||
|
||||
MISSING_FILES=0
|
||||
for file in "${REQUIRED_FILES[@]}"; do
|
||||
if [ -f "$file" ]; then
|
||||
echo "✅ Found: $file"
|
||||
else
|
||||
echo "❌ Missing: $file"
|
||||
MISSING_FILES=$((MISSING_FILES + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $MISSING_FILES -eq 0 ]; then
|
||||
echo "✅ All required files found"
|
||||
else
|
||||
echo "❌ Missing $MISSING_FILES required files"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for WordPress coding standards compliance
|
||||
echo ""
|
||||
echo "🔍 Checking WordPress coding standards..."
|
||||
STANDARD_CHECK=$(grep -q "defined('ABSPATH')" "$MAIN_FILE" && echo "✅ ABSPATH check found" || echo "❌ ABSPATH check missing")
|
||||
|
||||
if [[ "$STANDARD_CHECK" == *"❌"* ]]; then
|
||||
echo "❌ WordPress coding standards not met"
|
||||
exit 1
|
||||
else
|
||||
echo "$STANDARD_CHECK"
|
||||
fi
|
||||
|
||||
# Check for security measures
|
||||
echo ""
|
||||
echo "🔍 Checking security measures..."
|
||||
NONCE_CHECK=$(grep -r "wp_verify_nonce\|check_ajax_referer" . --include="*.php" | grep -v "grep" | head -1)
|
||||
|
||||
if [ -n "$NONCE_CHECK" ]; then
|
||||
echo "✅ Nonce validation found in plugin files"
|
||||
else
|
||||
echo "❌ Nonce validation missing"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for proper file permissions
|
||||
echo ""
|
||||
echo "🔍 Checking file permissions..."
|
||||
PERMISSION_CHECK=$(ls -la "$MAIN_FILE" | grep -q "\-rw\-" && echo "✅ Main file has proper permissions" || echo "❌ Main file has incorrect permissions")
|
||||
|
||||
if [[ "$PERMISSION_CHECK" == *"❌"* ]]; then
|
||||
echo "⚠️ Warning: File permissions may need adjustment"
|
||||
else
|
||||
echo "$PERMISSION_CHECK"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🎉 Plugin validation completed successfully!"
|
||||
echo "============================================="
|
||||
echo "✅ All PHP files have valid syntax"
|
||||
echo "✅ Plugin header is complete"
|
||||
echo "✅ All required files are present"
|
||||
echo "✅ WordPress coding standards are met"
|
||||
echo "✅ Security measures are in place"
|
||||
echo ""
|
||||
echo "The PC Announcements 274 plugin is ready for installation!"
|
||||
31
chat/templates/Announcements/uninstall.php
Normal file
31
chat/templates/Announcements/uninstall.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* Uninstall script for PC Announcements 274
|
||||
* This script runs when the plugin is uninstalled from WordPress
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('WP_UNINSTALL_PLUGIN')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Plugin options to delete
|
||||
$options_to_delete = array(
|
||||
'pc_announcements_274_version',
|
||||
'pc_announcements_274_db_version'
|
||||
);
|
||||
|
||||
// Delete options
|
||||
foreach ($options_to_delete as $option) {
|
||||
delete_option($option);
|
||||
}
|
||||
|
||||
// Delete database table
|
||||
global $wpdb;
|
||||
if (isset($wpdb)) {
|
||||
$table_name = $wpdb->prefix . 'pc_announcements_274';
|
||||
$wpdb->query("DROP TABLE IF EXISTS $table_name");
|
||||
}
|
||||
|
||||
// Clear any cached data
|
||||
wp_cache_flush();
|
||||
Reference in New Issue
Block a user