diff --git a/.gitignore b/.gitignore index e535af8..0e05c11 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ .idea/ *.kdev4 nbproject/ +.vscode/ diff --git a/Announce.php b/Announce.php index fed6c3f..9725e2b 100644 --- a/Announce.php +++ b/Announce.php @@ -32,6 +32,8 @@ function register() { function config() { return array( "manage_threshold" => MANAGER, + "display_all" => NO, + "default_dismissable" => YES, ); } @@ -40,8 +42,9 @@ function hooks() { "EVENT_CORE_READY" => "api", "EVENT_LAYOUT_RESOURCES" => "resources", "EVENT_MENU_MANAGE" => "menu_manage", - + "EVENT_LAYOUT_BODY_BEGIN" => "body_begin", + "EVENT_LAYOUT_BODY_END" => "body_end", 'EVENT_REST_API_ROUTES' => 'routes', ); @@ -79,9 +82,22 @@ function menu_manage($event, $user_id) { } function body_begin() { - Announce::display("header", null, "announcement-header"); + if (plugin_config_get( "display_all" )) { + Announce::display_all("header", null, "announcement-header"); + } else { + Announce::display("header", null, "announcement-header"); + } + } + + function body_end() { + if (plugin_config_get( "display_all" )) { + Announce::display_all("footer", null, "announcement-footer"); + } else { + Announce::display("footer", null, "announcement-footer"); + } } + function schema() { require_once("api/install.php"); diff --git a/api/base.php b/api/base.php index db3abec..7c4f0db 100644 --- a/api/base.php +++ b/api/base.php @@ -13,6 +13,7 @@ class Announce { */ protected static $locations = array( 'header' => null, + 'footer' => null, ); /** @@ -31,7 +32,7 @@ public static function display($location, $project_id=null, $css_class="") { $project_id = helper_get_current_project(); } - $message = AnnounceMessage::load_random(auth_get_current_user_id(), "header", $project_id); + $message = AnnounceMessage::load_random(auth_get_current_user_id(), $location, $project_id); if ($message !== null) { $css_class = string_attribute($css_class); @@ -63,6 +64,59 @@ public static function display($location, $project_id=null, $css_class="") { } } + /** + * Generate the HTML for displaying all visible announcements. + * A div element is created with the CSS class "announcement" for each announcement. + * Dismissable announcements will include an image to hook to an AJAX call for dismissal. + * + * @param string $location Location name + * @param int|null $project_id Project ID (optional) + * @param string $css_class Additional CSS classes for styling (optional) + */ + public static function display_all($location, $project_id = null, $css_class = "") { + if (auth_is_user_authenticated()) { + if ($project_id === null) { + $project_id = helper_get_current_project(); + } + + // Load all visible announcements for the user + $messages = AnnounceMessage::load_visible(auth_get_current_user_id(), $location, $project_id); + + if (!empty($messages)) { + $css_class = string_attribute($css_class); + + foreach ($messages as $message) { + $message = AnnounceMessage::clean($message, AnnounceMessage::TARGET_VIEW); + $context = array_shift($message->contexts); + + $html = sprintf( + '%s
%s' . "\n", + $message->title, + $message->message + ); + + // Include dismiss button if dismissable + if ($context->dismissable) { + $html = sprintf( + 'Dismiss Announcement', + plugin_file("dismiss.png") + ) + . "\n" . $html; + } + + // Print the complete announcement div + printf( + '
%s
', + $css_class, + $context->id, $context->ttl, + $html + ); + echo "\n"; + } + } + } + } + /** * Initialize the locations' names (display values). */ @@ -127,9 +181,10 @@ public static function print_location_option_list($value=null) { self::initLocations(); if ($value === null) { - if( count(self::$locations) == 1 ) { - $value = reset( self::$locations ); - } else { + // The first location is automatically selected for backward compatibility reasons. + reset( self::$locations ); + $value = key( self::$locations ); + if( count(self::$locations) !== 1 ) { echo '\n"; diff --git a/files/announce.css b/files/announce.css index 32370a0..81c5c36 100644 --- a/files/announce.css +++ b/files/announce.css @@ -26,6 +26,11 @@ display: none; } +/* Mobile-first: Ensures text wraps normally on small screens for better readability. */ +.category .small.wrap { + white-space: normal; +} + input.ttl { width: 5em; } diff --git a/files/announce.js b/files/announce.js index 1592179..dd67ae4 100644 --- a/files/announce.js +++ b/files/announce.js @@ -29,14 +29,18 @@ jQuery(document).ready(function($) { $('#threshold_warning').hide(); } }); - - // Move announcement to the page's top, between navbar and breadcrumbs + var main_div = $('div.main-content'); if (!main_div.length) { // Admin pages don't have a main-content div main_div = $('div.main-container'); } - main_div.prepend($(announcement)); + // Move header announcement to the page's top, between navbar and breadcrumbs + var announcement_header = announcement.filter('.announcement-header'); + main_div.prepend($(announcement_header)); + // Move footer announcement to the page's bottom, between page content and footer + var announcement_footer = announcement.filter('.announcement-footer'); + main_div.append($(announcement_footer)); // Manual dismissal of announcement (user click) $('img.announcement-dismiss').click(dismiss); diff --git a/lang/strings_english.txt b/lang/strings_english.txt index 0d2577e..b53f1a1 100644 --- a/lang/strings_english.txt +++ b/lang/strings_english.txt @@ -32,14 +32,17 @@ $s_plugin_Announce_config = 'Configuration'; $s_plugin_Announce_config_title = 'Announcement Configuration'; $s_plugin_Announce_config_manage_threshold = 'Manage Announcements'; $s_plugin_Announce_threshold_warning = 'WARNING: access level is lower than $g_manage_site_threshold ; users may not be able to access the management pages.'; +$s_plugin_Announce_config_display_all = 'Display all announcements'; +$s_plugin_Announce_config_default_dismissable = 'Dismissable is enabled by default'; $s_plugin_Announce_action_create = 'Create'; $s_plugin_Announce_action_edit = 'Edit'; $s_plugin_Announce_action_delete = 'Delete'; -$s_plugin_Announce_action_delete_confirm = 'Do you really want to delete the following announcements?'; +$s_plugin_Announce_action_delete_confirm = 'Do you really want to delete the following announcement?'; $s_plugin_Announce_action_update = 'Update'; $s_plugin_Announce_location_header = 'Page Header'; +$s_plugin_Announce_location_footer = 'Page Footer'; $s_plugin_Announce_error_duplicate_context = 'A "%1$s" context is already defined for project "%2$s".'; $s_plugin_Announce_error_unknown_location = 'Unknown context location "%1$s".'; diff --git a/lang/strings_hungarian.txt b/lang/strings_hungarian.txt index 29578c3..bfcb6ff 100644 --- a/lang/strings_hungarian.txt +++ b/lang/strings_hungarian.txt @@ -33,11 +33,13 @@ $s_plugin_Announce_config = 'Beállítások'; $s_plugin_Announce_config_title = 'Közlemény konfiguráció'; $s_plugin_Announce_config_manage_threshold = 'Közlemények kezelése'; $s_plugin_Announce_threshold_warning = 'FIGYELEM: a hozzáférési szint alacsonyabb, mint az $g_manage_site_threshold; a felhasználók nem biztos, hogy hozzáférnek a kezelőfelületekhez.'; +$s_plugin_Announce_config_display_all = 'Összes közlemény megjelenítése'; +$s_plugin_Announce_config_default_dismissable = 'Elvethető alapértelmezésként'; $s_plugin_Announce_action_create = 'Létrehozás'; $s_plugin_Announce_action_edit = 'Szerkesztés'; $s_plugin_Announce_action_delete = 'Törlés'; -$s_plugin_Announce_action_delete_confirm = 'Valóban törölni szeretné a következő közleményeket?'; +$s_plugin_Announce_action_delete_confirm = 'Valóban törölni szeretné a következő közleményt?'; $s_plugin_Announce_action_update = 'Frissítés'; $s_plugin_Announce_location_header = 'Oldal fejléc'; diff --git a/pages/config.php b/pages/config.php index bc7dccc..b3cb3df 100644 --- a/pages/config.php +++ b/pages/config.php @@ -15,6 +15,10 @@ function maybe_set_option( $name, $value ) { maybe_set_option("manage_threshold", gpc_get_int("manage_threshold")); +maybe_set_option("display_all", gpc_get_bool("display_all")); + +maybe_set_option("default_dismissable", gpc_get_bool("default_dismissable")); + form_security_purge("plugin_Announce_config"); print_header_redirect(plugin_page("config_page", true)); diff --git a/pages/config_page.php b/pages/config_page.php index d1f645c..7d15620 100644 --- a/pages/config_page.php +++ b/pages/config_page.php @@ -49,6 +49,26 @@ + + + + + + + /> + + + + + + + + + /> + + diff --git a/pages/list.php b/pages/list.php index e7d2395..8540c18 100644 --- a/pages/list.php +++ b/pages/list.php @@ -196,7 +196,7 @@ class="btn btn-primary btn-white btn-round">
- + - + - + /> diff --git a/pages/list_action_update.php b/pages/list_action_update.php index 23894fa..c931161 100644 --- a/pages/list_action_update.php +++ b/pages/list_action_update.php @@ -46,12 +46,10 @@ } } - if( !is_blank( $t_new_title ) ) { - $t_message->title = $t_new_title; - } - if( !is_blank( $t_new_message ) ) { - $t_message->message = $t_new_message; - } + // Allow saving empty values for title and message to support scenarios where + // clearing these fields is intentional (e.g., resetting or removing content). + $t_message->title = $t_new_title; + $t_message->message = $t_new_message; $t_message->save(); }