Skip to content

Commit e95e6d2

Browse files
Refactor: use namespacing in JS and persistence (#1)
1 parent 4150935 commit e95e6d2

File tree

11 files changed

+212
-77
lines changed

11 files changed

+212
-77
lines changed

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,3 +393,38 @@ $notice = AdminNotices::show('my_notice', 'This is a notice')
393393
$notice = AdminNotices::show('my_notice', 'This is a notice')
394394
->notDismissible();
395395
```
396+
397+
## Resetting dismissed notices
398+
399+
For dismissible notices, when the user dismisses the notice, it is permanently dismissed. If you
400+
want
401+
to reset the dismissed notice(s), there are a couple methods available.
402+
403+
### `resetNoticeForUser($notificationId, $userId)`
404+
405+
Reset a specific notification for a user.
406+
407+
Parameters:
408+
409+
1. `string $notificationId` - The unique identifier for the notice
410+
2. `int $userId` - The user ID to reset the notice for
411+
412+
```php
413+
use StellarWP\AdminNotices\AdminNotices;
414+
415+
AdminNotices::resetNoticeForUser('my_notice', get_current_user_id());
416+
```
417+
418+
### `resetAllNoticesForUser($userId)`
419+
420+
Reset all dismissed notices for a user.
421+
422+
Parameters:
423+
424+
1. `int $userId` - The user ID to reset all notices for
425+
426+
```php
427+
use StellarWP\AdminNotices\AdminNotices;
428+
429+
AdminNotices::resetAllNoticesForUser(get_current_user_id());
430+
```

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "stellarwp/admin-notices",
3-
"version": "1.0.2",
3+
"version": "1.1.0",
44
"description": "A handy package for easily displaying admin notices in WordPress with simple to complex visibility conditions",
55
"minimum-stability": "stable",
66
"license": "MIT",

src/Actions/DisplayNoticesInAdmin.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,20 @@
88
use DateTimeImmutable;
99
use DateTimeZone;
1010
use StellarWP\AdminNotices\AdminNotice;
11+
use StellarWP\AdminNotices\Traits\HasNamespace;
1112

1213
/**
1314
* Displays the provided notices in the admin based on the conditions set in the notice.
1415
*
16+
* @since 1.1.0 added namespacing
1517
* @since 1.0.0
1618
*/
1719
class DisplayNoticesInAdmin
1820
{
21+
use HasNamespace;
22+
1923
/**
24+
* @since 1.1.0 passed the namespace to RenderAdminNotice
2025
* @since 1.0.0
2126
*/
2227
public function __invoke(AdminNotice ...$notices)
@@ -27,7 +32,7 @@ public function __invoke(AdminNotice ...$notices)
2732

2833
foreach ($notices as $notice) {
2934
if ($this->shouldDisplayNotice($notice)) {
30-
echo (new RenderAdminNotice($notice))();
35+
echo (new RenderAdminNotice($this->namespace))($notice);
3136
}
3237
}
3338
}
@@ -152,11 +157,17 @@ private function passesScreenConditions(AdminNotice $notice): bool
152157
return false;
153158
}
154159

160+
/**
161+
* Checks whether the notice has been dismissed by the user.
162+
*
163+
* @since 1.1.0 added namespacing to the preferences key
164+
* @since 1.0.0
165+
*/
155166
private function passesDismissedConditions(AdminNotice $notice): bool
156167
{
157168
$userPreferences = get_user_meta(get_current_user_id(), 'wp_persisted_preferences', true);
158169

159-
$key = "stellarwp/admin-notices";
170+
$key = "stellarwp/admin-notices/$this->namespace";
160171
if (!is_array($userPreferences) || empty($userPreferences[$key])) {
161172
return true;
162173
}

src/Actions/RenderAdminNotice.php

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,49 +6,50 @@
66
namespace StellarWP\AdminNotices\Actions;
77

88
use StellarWP\AdminNotices\AdminNotice;
9+
use StellarWP\AdminNotices\Traits\HasNamespace;
910

1011
/**
1112
* Renders the admin notice based on the configuration of the notice.
1213
*
14+
* @since 1.1.0 refactored to use namespace and notice is passed to the __invoke method
1315
* @since 1.0.0
1416
*/
1517
class RenderAdminNotice
1618
{
19+
use HasNamespace;
20+
1721
/**
18-
* @var AdminNotice
22+
* Renders the admin notice
23+
*
24+
* @since 1.1.0 added namespacing and notice is passed to the __invoke method
25+
* @since 1.0.0
1926
*/
20-
private $notice;
21-
22-
public function __construct(AdminNotice $notice)
23-
{
24-
$this->notice = $notice;
25-
}
26-
27-
public function __invoke(): string
27+
public function __invoke(AdminNotice $notice): string
2828
{
29-
if (!$this->notice->usesWrapper()) {
30-
return $this->notice->getRenderedContent();
29+
if (!$notice->usesWrapper()) {
30+
return $notice->getRenderedContent();
3131
}
3232

3333
return sprintf(
34-
"<div class='%s' data-notice-id='%s'>%s</div>",
35-
esc_attr($this->getWrapperClasses()),
36-
$this->notice->getId(),
37-
$this->notice->getRenderedContent()
34+
"<div class='%s' data-stellarwp-$this->namespace-notice-id='%s'>%s</div>",
35+
esc_attr($this->getWrapperClasses($notice)),
36+
$notice->getId(),
37+
$notice->getRenderedContent()
3838
);
3939
}
4040

4141
/**
4242
* Generates the classes for the standard WordPress notice wrapper.
4343
*
44+
* @since 1.1.0 notice is passed instead of accessed as a property
4445
* @since 1.0.0
4546
*/
46-
private function getWrapperClasses(): string
47+
private function getWrapperClasses(AdminNotice $notice): string
4748
{
48-
$classes = ['notice', 'notice-' . $this->notice->getUrgency()];
49+
$classes = ['notice', 'notice-' . $notice->getUrgency()];
4950

50-
if ($this->notice->isDismissible()) {
51-
$classes[] = 'is-dismissible';
51+
if ($notice->isDismissible()) {
52+
$classes[] = "is-dismissible";
5253
}
5354

5455
return implode(' ', $classes);

src/AdminNotices.php

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,13 @@ class AdminNotices
3535
* Registers a notice to be conditionally displayed in the admin
3636
*
3737
* @since 1.0.0
38+
* @since 1.1.0 no longer include namespace in AdminNotice id
3839
*
3940
* @param string|callable $render
4041
*/
4142
public static function show(string $notificationId, $render): AdminNotice
4243
{
43-
$notice = new AdminNotice(self::$namespace . '/' . $notificationId, $render);
44+
$notice = new AdminNotice($notificationId, $render);
4445

4546
self::getRegistrar()->registerNotice($notice);
4647

@@ -59,7 +60,7 @@ public static function show(string $notificationId, $render): AdminNotice
5960
public static function render(AdminNotice $notice, bool $echo = true): ?string
6061
{
6162
ob_start();
62-
(new DisplayNoticesInAdmin())($notice);
63+
(new DisplayNoticesInAdmin(self::$namespace))($notice);
6364
$output = ob_get_clean();
6465

6566
if ($echo) {
@@ -108,12 +109,15 @@ public static function removeContainer(): void
108109
*
109110
* This should be called at the beginning of the plugin file along with other configuration.
110111
*
112+
* @since 1.1.0 added namespace validation
111113
* @since 1.0.0
112114
*/
113115
public static function initialize(string $namespace, string $pluginUrl): void
114116
{
115117
if (empty($namespace)) {
116118
throw new RuntimeException('Namespace must be provided');
119+
} elseif (preg_match('/[^a-zA-Z0-9_-]/', $namespace)) {
120+
throw new RuntimeException('Namespace must only contain letters, numbers, hyphens, and underscores');
117121
}
118122

119123
self::$packageUrl = $pluginUrl;
@@ -138,6 +142,7 @@ public static function getNotices(): array
138142
/**
139143
* Rests a dismissed notice for a given user so the notice will be shown again
140144
*
145+
* @since 1.1.0 uses namespacing
141146
* @since 1.0.0
142147
*/
143148
public static function resetNoticeForUser(string $notificationId, int $userId): void
@@ -146,17 +151,18 @@ public static function resetNoticeForUser(string $notificationId, int $userId):
146151

147152
$preferencesKey = $wpdb->get_blog_prefix() . 'persisted_preferences';
148153
$preferences = get_user_meta($userId, $preferencesKey, true);
154+
$packageKey = 'stellarwp/admin-notices/' . self::$namespace;
149155

150-
$notificationKey = self::$namespace . '/' . $notificationId;
151-
if (isset($preferences['stellarwp/admin-notices'][$notificationKey])) {
152-
unset($preferences['stellarwp/admin-notices'][$notificationKey]);
156+
if (isset($preferences[$packageKey][$notificationId])) {
157+
unset($preferences[$packageKey][$notificationId]);
153158
update_user_meta($userId, $preferencesKey, $preferences);
154159
}
155160
}
156161

157162
/**
158163
* Resets all dismissed notices for a given user so all notices will be shown again
159164
*
165+
* @since 1.1.0 uses namespacing and simplified the method
160166
* @since 1.0.0
161167
*/
162168
public static function resetAllNoticesForUser(int $userId): void
@@ -165,44 +171,52 @@ public static function resetAllNoticesForUser(int $userId): void
165171

166172
$preferencesKey = $wpdb->get_blog_prefix() . 'persisted_preferences';
167173
$preferences = get_user_meta($userId, $preferencesKey, true);
174+
$packageKey = 'stellarwp/admin-notices/' . self::$namespace;
168175

169-
if (isset($preferences['stellarwp/admin-notices'])) {
170-
$preferenceRemoved = false;
171-
foreach ($preferences['stellarwp/admin-notices'] as $key => $value) {
172-
if (strpos($key, self::$namespace . '/') === 0) {
173-
unset($preferences['stellarwp/admin-notices'][$key]);
174-
$preferenceRemoved = true;
175-
}
176-
}
177-
178-
if ($preferenceRemoved) {
179-
update_user_meta($userId, $preferencesKey, $preferences);
180-
}
176+
if (isset($preferences[$packageKey])) {
177+
unset($preferences[$packageKey]);
178+
update_user_meta($userId, $preferencesKey, $preferences);
181179
}
182180
}
183181

184182
/**
185183
* Hook action to display the notices in the admin
186184
*
185+
* @since 1.1.0 passes the namespace to the display notices class
187186
* @since 1.0.0
188187
*/
189188
public static function setUpNotices(): void
190189
{
191-
(new DisplayNoticesInAdmin())(...self::getNotices());
190+
(new DisplayNoticesInAdmin(self::$namespace))(...self::getNotices());
192191
}
193192

194193
/**
195194
* Hook action to enqueue the scripts needed for dismissing notices
196195
*
196+
* @since 1.1.0 added the namespacing attribute to the script tag
197197
* @since 1.0.2 use filetime for versioning, which will bust the cache when the library is updated
198198
* @since 1.0.0
199199
*/
200200
public static function enqueueScripts(): void
201201
{
202+
$namespace = self::$namespace;
203+
$handle = "$namespace-admin-notices";
202204
$version = filemtime(__DIR__ . '/resources/admin-notices.js');
203205

206+
add_filter('script_loader_tag', static function ($tag, $tagHandle) use ($handle, $namespace) {
207+
if ($handle !== $tagHandle) {
208+
return $tag;
209+
}
210+
211+
$tag = str_replace(' src', ' defer src', $tag);
212+
213+
$replacement = "<script data-stellarwp-namespace='$namespace'";
214+
215+
return str_replace('<script', $replacement, $tag);
216+
}, 10, 2);
217+
204218
wp_enqueue_script(
205-
'stellarwp-admin-notices',
219+
$handle,
206220
self::$packageUrl . '/src/resources/admin-notices.js',
207221
['jquery', 'wp-data', 'wp-preferences'],
208222
$version,

src/Traits/HasNamespace.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace StellarWP\AdminNotices\Traits;
6+
7+
trait HasNamespace
8+
{
9+
/**
10+
* The namespace for the plugin.
11+
*
12+
* @var string
13+
*/
14+
protected $namespace;
15+
16+
public function __construct(string $namespace)
17+
{
18+
$this->namespace = $namespace;
19+
}
20+
}

src/resources/admin-notices.js

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,36 @@
1-
jQuery(document).ready(function ($) {
2-
const {dispatch} = wp.data;
3-
const dismissButtons = $('.notice-dismiss');
1+
(function ($, dispatch, document) {
2+
// Set up the package functions using the namespace provided by the script tag.
3+
const currentScript = typeof document.currentScript !== 'undefined' ? document.currentScript : document.scripts[document.scripts.length - 1];
4+
const namespace = currentScript.getAttribute('data-stellarwp-namespace');
45

5-
dismissButtons.on('click', function (event) {
6-
const $this = $(this);
7-
const container = $this.closest('.notice');
8-
const noticeId = container.data('notice-id');
6+
if (!namespace) {
7+
console.error('The stellarwp/admin-notices library failed to load because the namespace attribute is missing.');
8+
return;
9+
}
910

10-
const now = Math.floor(Date.now() / 1000);
11-
dispatch('core/preferences').set('stellarwp/admin-notices', noticeId, now);
11+
window.stellarwp = window.stellarwp || {};
12+
window.stellarwp.adminNotices = window.stellarwp.adminNotices || {};
13+
window.stellarwp.adminNotices[namespace] = {
14+
/**
15+
* Dismisses a notice with the given ID.
16+
*
17+
* @since 1.1.0
18+
*
19+
* @param {string} noticeId
20+
*/
21+
dismissNotice: function (noticeId) {
22+
const now = Math.floor(Date.now() / 1000);
23+
dispatch('core/preferences').set(`stellarwp/admin-notices/${namespace}`, noticeId, now);
24+
},
25+
};
26+
27+
// Begin notice dismissal code
28+
const noticeIdAttribute = `data-stellarwp-${namespace}-notice-id`;
29+
const $notices = $(`[${noticeIdAttribute}]`);
30+
31+
$notices.on('click', '.notice-dismiss', function (event) {
32+
const noticeId = $(this).closest(`[${noticeIdAttribute}]`).data(`stellarwp-${namespace}-notice-id`);
33+
34+
window.stellarwp.adminNotices[namespace].dismissNotice(noticeId);
1235
});
13-
});
36+
})(window.jQuery, window.wp.data.dispatch, document);

0 commit comments

Comments
 (0)