Compare commits

...

88 Commits
0.13 ... master

Author SHA1 Message Date
abuyoyo
00ab82a35d 0.27
Some checks failed
Create Release / Create Release (push) Has been cancelled
2024-09-07 08:05:22 +03:00
abuyoyo
51cfce0071 0.27 - version bump + changelog 2024-09-07 08:04:22 +03:00
abuyoyo
416039449f composer.json - prepend-autoloader: false
Local vendor/autoload should not prepend if required locally.
Give precedence to main composer installation if available.
2024-09-07 00:44:37 +03:00
abuyoyo
e548158c15 0.26
Some checks failed
Create Release / Create Release (push) Has been cancelled
2024-09-01 08:04:08 +03:00
abuyoyo
41a42d3a2a 0.26 - version bump + changelog 2024-09-01 08:03:34 +03:00
abuyoyo
b6aca366b2 Mutation Observer replaces DOMNodeRemoved event 2024-09-01 07:48:58 +03:00
abuyoyo
7cc5b6de5a NoticeManager.bootstrap - refactor + modernize 2024-09-01 07:48:58 +03:00
abuyoyo
ca68f59a28 0.25
Some checks failed
Create Release / Create Release (push) Has been cancelled
2024-02-18 03:43:24 +02:00
abuyoyo
a3040b6c47 0.25 - version bump + changelog 2024-02-18 03:41:05 +02:00
abuyoyo
b99be56795 composer.lock + vendor/
- abuyoyo/metabox 0.8
- abuyoyo/adminmenupage 0.29
2024-02-18 03:34:30 +02:00
abuyoyo
3458b724be composer.json - update abuyoyo/adminmenupage ~0.29 2024-02-18 03:22:21 +02:00
abuyoyo
57fb24aa22 0.24
Some checks failed
Create Release / Create Release (push) Has been cancelled
2023-10-04 03:50:31 +03:00
abuyoyo
42db8f67df 0.24 - version bump + changelog 2023-10-04 02:40:21 +03:00
abuyoyo
1cd273bded composer.lock + vendor/ 2023-10-04 02:40:20 +03:00
abuyoyo
2c166a33a8 composer.json - update dependencies (PluginCore 0.27 supports plugin-update-checker v5|v4) 2023-10-04 02:40:20 +03:00
abuyoyo
1321d35bdf De-prioritize vendor/autoload.php 2023-10-04 02:40:20 +03:00
abuyoyo
7cf7b477bc PluginCore - set update_checker to true.
If Plugin-Update-Checker library (v4|v5) is available - enables updates from repo.
2023-10-04 01:29:04 +03:00
abuyoyo
42e26c9a4f 0.23
Some checks failed
Create Release / Create Release (push) Has been cancelled
2023-09-27 23:56:57 +03:00
abuyoyo
920dd0b683 0.23 - version bump + changelog 2023-09-27 23:21:52 +03:00
abuyoyo
fa4099a6dd composer.lock + vendor update 2023-09-27 23:21:50 +03:00
abuyoyo
2efc23c123 Update Description plugin header 2023-09-27 22:49:43 +03:00
abuyoyo
03473b957c Fix notices-panel removing all screen-meta-links panels when removing self 2023-09-27 22:32:57 +03:00
abuyoyo
ff4a8f542b Filter .hidden notices
Fix 0-count notices panel.
Fix wrong priority color plugin-count bullet on auto-collapsed panel.
2023-09-27 22:31:13 +03:00
abuyoyo
93afbe9b5b Fix plugin-count styling issue
.notice class was adding border to .plugin-count bullet
2023-09-27 22:24:20 +03:00
abuyoyo
bbec303755 0.22
Some checks failed
Create Release / Create Release (push) Has been cancelled
2023-08-11 22:37:46 +03:00
abuyoyo
24d4e54ca8 0.22 - version bump + changelog 2023-08-11 22:33:58 +03:00
abuyoyo
c0ca10a9c0 Fix - do not collect .theme-info notice 2023-08-11 22:33:58 +03:00
abuyoyo
0f13626dab Fix vendor/autoload.php include path 2023-08-11 22:33:56 +03:00
abuyoyo
320496722d 0.21
Some checks failed
Create Release / Create Release (push) Has been cancelled
2023-08-10 03:44:35 +03:00
abuyoyo
a856f85cb8 0.21 - version bump + changelog 2023-08-10 03:40:54 +03:00
abuyoyo
fcee298dab composer.lock + vendor update 2023-08-10 03:40:01 +03:00
abuyoyo
abedf94f24 composer.json - update abuyoyo/screen-meta-links ~0.13 2023-08-10 03:34:31 +03:00
abuyoyo
873b9abdd0 0.20
Some checks failed
Create Release / Create Release (push) Has been cancelled
2023-06-09 22:12:50 +03:00
abuyoyo
20eda54314 0.20 - version bump 2023-06-09 22:11:21 +03:00
abuyoyo
e9a5f8f71e 0.19
Some checks failed
Create Release / Create Release (push) Has been cancelled
2023-06-09 21:54:54 +03:00
abuyoyo
1d060b9a22 0.19 - version bump + changelog 2023-06-09 21:52:26 +03:00
abuyoyo
9d574615e8 composer.lock + vendor 2023-06-09 21:52:26 +03:00
abuyoyo
4229406ec8 composer.json - update dependencies 2023-06-09 21:46:48 +03:00
abuyoyo
acf477a0c0 vendor/autoload - check if file exists 2023-06-09 21:46:48 +03:00
abuyoyo
2bdd4b24b7 option: distraction_free 2023-06-09 21:46:48 +03:00
abuyoyo
bc1a864dd1 disable plugin-info meta box 2023-06-09 21:46:48 +03:00
abuyoyo
bffd39afe5 js - remove all semi-colons 2023-01-14 15:01:45 +02:00
abuyoyo
1cbb0a5aea 0.18
Some checks failed
Create Release / Create Release (push) Has been cancelled
2022-08-06 18:01:27 +03:00
abuyoyo
72684cf182 0.18 - version bump + changelog 2022-08-06 17:59:39 +03:00
abuyoyo
10b24ea86f composer.json + vendor - update dependencies 2022-08-06 17:59:39 +03:00
abuyoyo
d083cd3032 0.17
Some checks failed
Create Release / Create Release (push) Has been cancelled
2022-08-06 17:42:13 +03:00
abuyoyo
476d140f3f 0.17 - version bump + changelog 2022-08-06 17:41:43 +03:00
abuyoyo
4810fe9400 vendor - Remove 3rd party dependency + composer.lock
- remove yahnis-elsts/plugin-update-checker
2022-08-06 17:40:59 +03:00
abuyoyo
e42a6561a6 Remove plugin update checker 2022-08-06 16:46:06 +03:00
abuyoyo
0484250f45 composer.json - update dependencies 2022-08-06 16:32:17 +03:00
abuyoyo
ae634c10b1 0.16
Some checks failed
Create Release / Create Release (push) Has been cancelled
2022-08-05 21:12:18 +03:00
abuyoyo
650fd54176 0.16 - composer.lock + vendor updates + package-lock.json 2022-08-05 21:11:12 +03:00
abuyoyo
5035df9615 0.16 - version bump + changelog 2022-08-05 21:11:12 +03:00
abuyoyo
c72d8b3882 plugin action links 2022-08-05 21:07:17 +03:00
abuyoyo
d3e004a53e Plugin action links - add link to Settings page 2022-08-05 20:51:45 +03:00
abuyoyo
1980a2c749 composer.json - update dependencies (abuyoyo/plugincore ~0.21) 2022-08-05 20:50:46 +03:00
abuyoyo
03dfe6c442 0.15
Some checks failed
Create Release / Create Release (push) Has been cancelled
2022-08-02 06:31:24 +03:00
abuyoyo
4ce87fe808 0.15 - version bump + changelog 2022-08-02 06:24:30 +03:00
abuyoyo
95dac048fa vendor/autoload.php 2022-08-02 06:15:19 +03:00
abuyoyo
ff37df4180 add .notice-error to selectors 2022-08-02 06:15:19 +03:00
abuyoyo
cecbea8148 plugin_count - do not count hidden notices 2022-08-02 06:15:19 +03:00
abuyoyo
330fdd2430 js - expand DOMNodeRemoved/maybeRemoveNoticesPanel selector 2022-08-02 06:15:19 +03:00
abuyoyo
1355d280ea js - selector arrays 2022-08-02 06:15:19 +03:00
abuyoyo
e1f113ba7d Admin page: description_container - add notice wrapper
Use WPHelper/AdminPage 'description_container' option
2022-08-02 06:15:19 +03:00
abuyoyo
dfe9bdb23e css - restore css file as css/notice-manager.css 2022-08-02 06:15:19 +03:00
abuyoyo
55dbd0d1bf npm package.json - node-sass
Compile SCSS using node-sass
2022-08-02 06:15:19 +03:00
abuyoyo
6a96430da9 SCSS - notice-manager.scss - convert to scss format 2022-08-02 06:15:19 +03:00
abuyoyo
242dcdee2b SCSS - css/admin_notices.css => scss/notice-manager.scss 2022-08-02 06:15:19 +03:00
abuyoyo
4ab77a5a45 Admin page - expand field/option descriptions 2022-08-02 06:15:19 +03:00
abuyoyo
f01cdbf2b0 misc - cleanup + notes + typos + lint + modernize code 2022-08-02 06:13:14 +03:00
abuyoyo
1d813a48c9 Update URI header - avoid updating from wp.org plugin 'notice-manager' 2022-08-02 04:51:57 +03:00
abuyoyo
a7c43b60a4 composer.json - update dependencies 2022-08-02 04:51:57 +03:00
abuyoyo
f51ad078c6 Readme 2022-08-02 04:51:57 +03:00
abuyoyo
8bab6cc126 License - BSD 3-Clause License 2022-08-02 04:12:41 +03:00
abuoyoyo
23f2241dda 0.14
Some checks failed
Create Release / Create Release (push) Has been cancelled
2022-07-22 19:25:35 +03:00
abuoyoyo
36a79a5f5e 0.14 - version bump + changelog 2022-07-22 19:22:29 +03:00
abuoyoyo
296051fac1 Add ".plugin-count" counter to notice panel tag when closed 2022-07-22 19:22:29 +03:00
abuoyoyo
28ac8f941b Do not auto-collapse when error notice is shown 2022-07-22 19:07:12 +03:00
abuoyoyo
eabafb460f js - NoticeManager.getNoticesTopPriority() 2022-07-22 19:07:12 +03:00
abuoyoyo
d48631c4f6 FOUC - add body class .notices-auto-collect
Do not display notices before they are moved to panel.
2022-07-22 19:07:12 +03:00
abuoyoyo
2362cb37df FOUC - add body class .notices-above-title
Do not display notices before they are moved to new position.
2022-07-22 19:07:12 +03:00
abuoyoyo
a2c3e60f87 NoticeManager.moveAboveTitle + above_title option 2022-07-22 19:07:12 +03:00
abuoyoyo
57c54c756a js - re-factor/improve functionality 2022-07-22 19:07:12 +03:00
abuoyoyo
ba26708c9f css - override other plugins' custom notice padding 2022-07-22 19:07:12 +03:00
abuoyoyo
51a493926b css - Remove support/override of ngfb plugin 2022-07-22 19:07:12 +03:00
abuoyoyo
5c5788c87d misc - lint + add specificity to DOMNodeRemoved listener 2022-07-22 19:07:12 +03:00
abuoyoyo
770b2cc43d misc cleanup + notes 2022-07-22 19:07:12 +03:00
abuoyoyo
1d343e7dee .gitignore - node_modules/ 2022-07-22 19:07:12 +03:00
71 changed files with 8315 additions and 215 deletions

2
.gitignore vendored
View File

@ -1 +1 @@
vendor/
node_modules/

View File

@ -1,5 +1,115 @@
# Notice Manager Changelog
## 0.27
Release date: Sep 7 2024
### Dependencies
- Composer config: `prepend-autoloader: false` - Give precedence to other composer installations if present.
## 0.26
Release date: Sep 1 2024
### Changed
- Use Mutation Observer instead of deprecated `DOMNodeRemoved` event.
### Added
- Add method `NoticeManager.bootstrap()` to initialize Notice manager.
## 0.25
Release date: Feb 18 2024
### Fixed
- Fix fatal error `Class "WPHelper\MetaBox" not found` due to dependency `abuyoyo/adminmenupage` < 0.29 not requiring dependency `abuyoyo/metabox`.
### Dependencies
- Library WPHelper\AdminPage (`abuyoyo/adminmenupage`) updated to 0.29. Requires `abuyoyo/metabox`.
## 0.24
Release date: Oct 4 2023
### Fixed
- Set `WPHelper\PluginCore` option `update_checker` to true. If library Plugin-Update-Checker is installed, allows updates from repo.
### Dependencies
- Library WPHelper\PluginCore (`abuyoyo/plugincore`) updated to 0.27. Supports both Plugin-Update-Checker v5 and v4.
## 0.23
### Fixed
- Fix `.plugin-count` bullet styling issue.
- Fix 0-count panel caused by collecting `.hidden` notices.
- Fix wrong priority color `.plugin-count` bullet caused by collecting `.hidden` notices.
- Fix empty notices-panel removing all screen-meta-link panels.
## 0.22
### Fixed
- Do not collect `.theme-info` notices.
- Fix `vendor/autoload` include path.
## 0.21
### Fixed
- Fix PHP 8.2 deprecated optional parameter before required parameter. Fixed upstream in `abuyoyo/screen-meta-links ~0.13`.
### Dependencies
- Update library `abuyoyo/screen-meta-links` to version 0.13.
## 0.20
### Minor
- Just another version bump (composer.json as well).
## 0.19
### Added
- Add 'Distraction Free' option.
## 0.18
### Minor
- Version bump everywhere.
## 0.17
### Removed
- Remove plugin update checker.
- Remove 3rd party libraries.
## 0.16
### Added
- Add plugin action link to Notice Manager settings page.
## 0.15
### Added
- Add BSD 3-Clause License file.
- Add `vendor` directory and require `vendor/autoload.php`.
- Add `Update URI` header to plugin to avoid conflict with wp.org repo plugin of the same name.
### Changed
- Convert stylesheet to SCSS and use `node-sass` to render css file.
- Better option descriptions on settings page.
- Readme file - add detailed plugin description.
- Support `.notice-error` class.
- Do not count hidden notices in `.plugin-count` bullet.
## 0.14
### Added
- Added `above_title` setting - move all scripts above title.
- Added `.plugin_count` bullet to panel tab - showing number of notices in panel and priority.
### Changed
- Improve "jumpy" notices when page is loaded with certain setting combinations by selectively setting css `display: none` to notices not in their expected location.
- Improve integration between different options (eg. `above_title` with `auto-collect`).
- Option `auto-collapse` will not automatically collapse panel if an error notice is showing.
## 0.13
### Changed

29
LICENSE Normal file
View File

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2022, abuyoyo
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,3 +1,32 @@
# Notice Manager
Manage notices on WordPress admin pages. Adds 'Notices' screen-meta-link.
Collect WordPress admin notices into 'Notices' panel.
Notice Manager adds a 'Notices' panel alongside the 'Help' and 'Screen Option' collapsible panels. Notices are collected into the fold-out panel at the top of the page - making for a smoother and more accessible experience.
## Activation
Upon plugin activation, Notice Manager adds a setting page **Settings > Notice Manager** with various options. All options need to be enabled manually from that page.
## About
Notice Manager attempts to address some of the issues with WordPress's current notices implementation - the `admin_notices` hook system and accompanying scripts (I'm looking at you `common.js`!)
Notice Manager addresses the following issues:
- WordPress core's `common.js` script moves some of the notices below the title, while leaving other notices above the title (eg. `update-nag`). This is confusing and non-accessible. Notice Manager moves all notices together - either above the title or into the 'Notices' panel.
- WordPress core moves notices after they've already been printed on the page, Creating a jumpy experience. This is only partially addressed by Notice Manager as `common.js` functionality cannot be overridden/disabled currently. Notice Manager is less jumpy as it hides notices before they are in their final position.
- Opinionated: Notices should not appear below the title. Oftentimes the notices on a page are not related directly to the page being viewed. Having the title and the content uninterrupted by notices is arguably more accessible and makes for a smoother user experience.
### Minimalist Ethic
Notice Manager represents a minimalist approach to the issues and does not preclude other solutions being developed currently.
- Notice Manager does not invent any new UI on WordPress's admin side. It bootstraps WordPress's already existing and familiar `screen-meta-links` collapsing panels (The 'Help' and 'Screen Options' panels). More on that below.
- Notice Manager does not affect how plugins interact with WordPress core notice system. Notice Manager simply collects notices already printed to the page. It supports all plugins using the current `admin_notices` hook(s).
- Notice Manager does not change notices appearance. It simply moves them around in much the same way core's `common.js` does.
- Notice Manager works with what exists now.
- Notice Manager preserves backward compatibility.
### Screen Meta Links
WordPress does not currently allow plugin authors a way to add panels and content to the `screen-meta-links` - apart from interacting with the hardcoded Screen Meta 'Help' and 'Screen Options' panels.
Notice Manager uses a library (`abuyoyo/screen-meta-links`) that employs render-blocking JavaScript and PHP to generate new panels on page load. This could be developed as its own separate feature using PHP only and a hook system - to allow plugin authors to interface and add their own panels to WordPress's Screen Meta panels. However that is beyond the scope of this plugin.

View File

@ -2,6 +2,7 @@
"name": "abuyoyo/notice-manager",
"description": "Manage notices on WordPress admin pages. Adds 'Notices' screen-meta-link.",
"type": "wordpress-plugin",
"version": "0.27",
"repositories": [
{
"type": "vcs",
@ -9,7 +10,11 @@
}
],
"require":{
"abuyoyo/screen-meta-links": "~0.11",
"abuyoyo/plugincore": "~0.14"
"abuyoyo/screen-meta-links": "~0.13",
"abuyoyo/plugincore": "~0.27",
"abuyoyo/adminmenupage": "~0.29"
},
"config": {
"prepend-autoloader": false
}
}

153
composer.lock generated Normal file
View File

@ -0,0 +1,153 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "b1a74cf1316f5dd317f70efeb60d9f4d",
"packages": [
{
"name": "abuyoyo/adminmenupage",
"version": "0.29",
"source": {
"type": "git",
"url": "https://github.com/abuyoyo/AdminMenuPage.git",
"reference": "4cd47d5217ca25ed17af76f5fdbab3cab3b37ef8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/abuyoyo/AdminMenuPage/zipball/4cd47d5217ca25ed17af76f5fdbab3cab3b37ef8",
"reference": "4cd47d5217ca25ed17af76f5fdbab3cab3b37ef8",
"shasum": ""
},
"require": {
"abuyoyo/metabox": "~0.8"
},
"suggest": {
"abuyoyo/plugincore": "~0.26",
"cmb2/cmb2": "~2.9"
},
"type": "library",
"autoload": {
"files": [
"wph_admin_page.php"
],
"psr-4": {
"WPHelper\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"description": "WordPress admin menu page helper class",
"support": {
"issues": "https://github.com/abuyoyo/AdminMenuPage/issues",
"source": "https://github.com/abuyoyo/AdminMenuPage/tree/0.29"
},
"time": "2023-10-05T00:00:00+00:00"
},
{
"name": "abuyoyo/metabox",
"version": "0.8",
"source": {
"type": "git",
"url": "https://github.com/abuyoyo/MetaBox.git",
"reference": "98cb4c30db4c366c0d273985eb9c31ffa1cd78f9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/abuyoyo/MetaBox/zipball/98cb4c30db4c366c0d273985eb9c31ffa1cd78f9",
"reference": "98cb4c30db4c366c0d273985eb9c31ffa1cd78f9",
"shasum": ""
},
"type": "library",
"autoload": {
"psr-4": {
"WPHelper\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"description": "WordPress metabox helper class",
"support": {
"issues": "https://github.com/abuyoyo/MetaBox/issues",
"source": "https://github.com/abuyoyo/MetaBox/tree/0.8"
},
"time": "2023-07-18T19:14:03+00:00"
},
{
"name": "abuyoyo/plugincore",
"version": "0.27",
"source": {
"type": "git",
"url": "https://github.com/abuyoyo/PluginCore.git",
"reference": "d730a674cbe2dc92e60ace8e25a2e0e3fdeee3e6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/abuyoyo/PluginCore/zipball/d730a674cbe2dc92e60ace8e25a2e0e3fdeee3e6",
"reference": "d730a674cbe2dc92e60ace8e25a2e0e3fdeee3e6",
"shasum": ""
},
"suggest": {
"abuyoyo/adminmenupage": "~0.27",
"yahnis-elsts/plugin-update-checker": "~5.2"
},
"type": "library",
"autoload": {
"psr-4": {
"WPHelper\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"description": "WordPress plugin core helper class",
"support": {
"issues": "https://github.com/abuyoyo/PluginCore/issues",
"source": "https://github.com/abuyoyo/PluginCore/tree/0.27"
},
"time": "2022-10-03T00:00:00+00:00"
},
{
"name": "abuyoyo/screen-meta-links",
"version": "0.13",
"source": {
"type": "git",
"url": "https://github.com/abuyoyo/screen-meta-links.git",
"reference": "b324cef9eb5825d04ffa17f771237b7deca5cd01"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/abuyoyo/screen-meta-links/zipball/b324cef9eb5825d04ffa17f771237b7deca5cd01",
"reference": "b324cef9eb5825d04ffa17f771237b7deca5cd01",
"shasum": ""
},
"type": "library",
"autoload": {
"files": [
"screen-meta-links.php"
]
},
"description": "API for adding custom screen-meta-links alongside the 'Screen Options' and 'Help' links.",
"support": {
"source": "https://github.com/abuyoyo/screen-meta-links/tree/0.13",
"issues": "https://github.com/abuyoyo/screen-meta-links/issues"
},
"time": "2023-08-08T22:37:03+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": true,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.3.0"
}

View File

@ -1,60 +0,0 @@
:not(.wrap) ~ div.updated,
:not(.wrap) ~ div.error,
:not(.wrap) ~ div.notice,
:not(.wrap) ~ div.update-nag{
margin: 5px 20px 15px 2px;
}
#meta-link-notices-wrap {
margin: 0;
/* padding: 8px 20px 12px; */
position: relative;
}
#meta-link-notices-wrap > button.notice-dismiss {
position: relative;
display: flex;
width: 100%;
}
.notice_container{
padding: 9px 0px 1px 0px;
background-color: gainsboro;
}
.notice_container.empty{
padding: 0;
}
.notice_container > div.updated,
.notice_container > div.error,
.notice_container > div.notice,
.notice_container > div.update-nag{
margin: 5px 12px 15px 12px;
}
/* ngfb update-nag override */
div.ngfb-notice.update-nag {
display: none !important; /* not working - need to remove with js */
/* restore normal formatting */
/* margin-top: 25px; */
border: 0px;
/* border-left: 4px solid #ffba00; */
border-left: 4px solid #00a0d2;
}
.ngfb-notice.update-nag .notice-message {
max-width: 100%;
padding: 0;
}
.ngfb-notice.update-nag p, .ngfb-notice.update-nag ul, .ngfb-notice.update-nag ol {
text-align: left !important;
font-size: 13px !important;
line-height: 1.5;
margin: 1em 0 !important;
opacity: 0.5;
}

59
css/notice-manager.css Normal file
View File

@ -0,0 +1,59 @@
:not(.wrap) ~ div.notice, :not(.wrap) ~ div.updated,
:not(.wrap) ~ div.error,
:not(.wrap) ~ div.notice-error,
:not(.wrap) ~ div.update-nag {
margin: 5px 20px 15px 2px; }
.notices-auto-collect #wpbody-content > div.notice, .notices-auto-collect #wpbody-content > div.updated,
.notices-auto-collect #wpbody-content > div.error,
.notices-auto-collect #wpbody-content > div.notice-error,
.notices-auto-collect #wpbody-content > div.update-nag,
.notices-auto-collect .wrap > div.notice,
.notices-auto-collect .wrap > div.updated,
.notices-auto-collect .wrap > div.error,
.notices-auto-collect .wrap > div.notice-error,
.notices-auto-collect .wrap > div.update-nag,
.notices-above-title .wrap > div.notice,
.notices-above-title .wrap > div.updated,
.notices-above-title .wrap > div.error,
.notices-above-title .wrap > div.notice-error,
.notices-above-title .wrap > div.update-nag {
display: none; }
#meta-link-notices-wrap {
margin: 0;
position: relative; }
#meta-link-notices-wrap > button.notice-dismiss {
position: relative;
display: flex;
width: 100%; }
.notice_container {
padding: 9px 0px 1px 0px;
background-color: gainsboro; }
.notice_container.empty {
padding: 0; }
.notice_container > div.notice, .notice_container > div.updated,
.notice_container > div.error,
.notice_container > div.notice-error,
.notice_container > div.update-nag {
margin: 5px 12px 15px 12px !important; }
#meta-link-notices .plugin-count {
display: inline-block;
box-sizing: border-box;
margin: 1px 0 -1px 2px;
padding: 0 5px;
min-width: 18px;
height: 18px;
border-radius: 9px;
background-color: #72aee6;
color: #fff;
font-size: 11px;
line-height: 1.6;
text-align: center;
z-index: 26; }
#meta-link-notices .plugin-count.warning {
background-color: #dba617; }
#meta-link-notices .plugin-count.error {
background-color: #d63638; }

View File

@ -2,147 +2,232 @@
* NoticeManager class/module
*
*/
var NoticeManager = (function ($, document) {
let options = window.notice_manager_options;
const NoticeManager = function ($) {
const selectors_notice = [
"div.notice",
"div.updated",
]
const selectors_warning = [
"div.notice-warning",
"div.update-nag",
]
const selectors_error = [
"div.error",
"div.notice-error",
]
const selectors_all = selectors_notice.concat(selectors_warning, selectors_error)
const selectors_skip = [
".inline",
".below-h2",
".theme-info .notice",
".hidden",
]
// wait function used with autoCollapse
let wait = function (ms) {
var dfd = $.Deferred();
setTimeout(dfd.resolve, ms); //callback, timeout till callback
return dfd.promise();
};
const wait = function (ms) {
var dfd = $.Deferred()
setTimeout(dfd.resolve, ms) //callback, timeout till callback
return dfd.promise()
}
let notices;
const options = window.notice_manager_options
let button;
let panel;
let haveClosed;
let dismissNoticesButton;
let notices
// bootstrap
// some of these need to run BEFORE document.ready - don't know why really
button = $("#meta-link-notices");
panel = $("#meta-link-notices-wrap");
haveClosed = false;
dismissNoticesButton = $("#meta-link-notices-wrap button.notice-dismiss");
let button
let panel
let dismissNoticesButton
let haveClosed // set to true on first close/collect
let panelObserver
return {
bootstrap: () => {
// Init selectors
button = $("#meta-link-notices")
panel = $("#meta-link-notices-wrap")
dismissNoticesButton = $("#meta-link-notices-wrap button.notice-dismiss")
// bootstrap notices
// get all notices that are not explicitly marked as `.inline` or `.below-h2`
// we add .update-nag.inline for WordPress Update notice
notices = $(selectors_all.join(', '))
.not(selectors_skip.join(', '))
.add("div.update-nag")
// Set state
haveClosed = false
dismissNoticesButton.on("click", () => {
screenMeta.close(panel, button);
NoticeManager.collectNotices();
});
screenMeta.close(panel, button)
if (!haveClosed) {
NoticeManager.collectNotices()
}
NoticeManager.addCounter()
})
//original wp focus on click function
button.on("focus.scroll-into-view", (e) => {
if (e.target.scrollIntoView) e.target.scrollIntoView(false);
});
if (e.target.scrollIntoView) e.target.scrollIntoView(false)
})
// scroll page to top when closing notice panel
// cannot convert to arrow function - uses this
// could use event.target instead
button.on("click", function () {
haveClosed = true;
if ($(this).hasClass("screen-meta-active")) {
// $(window).scrollTop(true);
} else {
// wait (500).then(function(){ //still jumpy sometimes - but scrolls to correct position 400 ~ 600
// $(window).scrollTop(true);
// });
// function used to work with $(this)
// using e.target instead
// not sure if this should perhaps be e.currentTarget
button.on("click", (e) => {
if ($(e.target).hasClass("screen-meta-active")) {
if (haveClosed) {
NoticeManager.addCounter()
}
});
// $(window).scrollTop(true)
} else {
NoticeManager.removeCounter()
// wait (500).then(function(){ //still jumpy sometimes - but scrolls to correct position 400 ~ 600
// $(window).scrollTop(true)
// })
}
})
// prevent jumpy scrollRestoration on reload page
// fixed below on 'beforeunload'
// if (history.scrollRestoration) {
// history.scrollRestoration = 'manual'
//}
/**
* document.on.ready
* Set history.scrollTop to prevent jump on page refresh when scrollRestoration = auto
*/
$(() => {
console.log("NoticeManager.on.ready");
console.log("options");
console.log(options);
// bootstrap notices
// get all notices that are not explicily marked as `.inline` or `.below-h2`
// we add .update-nag.inline for WordPress Update notice
notices = $("div.updated, div.error, div.notice")
.not(".inline, .below-h2")
.add("div.update-nag");
$(window).on('beforeunload', () => history.pushState(
{ scrollTop: document.body.scrollTop },
document.title,
document.location.pathname
)
)
/**
* Remove panel if there are no notices on this page
*/
NoticeManager.maybeRemoveNoticesPanel();
if (options.screen_panel) {
NoticeManager.maybeRemoveNoticesPanel()
}
if (options.auto_collect) {
NoticeManager.collectNotices();
if (options.screen_panel && options.auto_collect) {
NoticeManager.collectNotices()
} else {
/**
* Move ALL notices above page title.
* Default no-panel action - override WordPress moving notices BELOW title.
* I HATE it when WordPress moves notices below title.
*
* comment this line out to completely restore WordPress functionality when auto_collect is off
*/
notices.insertBefore(".wrap:first");
if (options.above_title) {
NoticeManager.moveAboveTitle()
}
}
/**
* auto-open notices panel
*/
if (button.length) {
panel.toggle();
button.addClass("screen-meta-active");
screenMeta.open(panel, button);
if (button.length && !options.distraction_free) {
panel.toggle()
button.addClass("screen-meta-active")
screenMeta.open(panel, button)
}
/**
* auto-close notices panel after short delay
* only auto-close if we have not interacted (opened/closed) with panel previously
* only auto-close if we have collected notices previously
* only auto-close if no error messages
*/
if (options.auto_collapse) {
if (options.auto_collapse && !options.distraction_free) {
wait(4000).then(() => {
if (!haveClosed) {
screenMeta.close(panel, button);
if (haveClosed && NoticeManager.getNoticesTopPriority() != 'error') {
screenMeta.close(panel, button)
NoticeManager.addCounter()
}
});
})
}
}); // end document.on.ready
// prevent jumpy scrollRestoration on reload page
// fixed below on 'beforeunload'
// if (history.scrollRestoration) {
// history.scrollRestoration = 'manual';
//}
/**
* Set history.scrollTop to prevent jump on page refresh when scrollRestoration = auto
*/
$(window).on("beforeunload", () => {
history.pushState(
{ scrollTop: document.body.scrollTop },
document.title,
document.location.pathname
);
});
if (options.distraction_free) {
NoticeManager.addCounterWhenClosed()
}
},
return {
getNotices: () => notices,
getNoticesTopPriority: () => {
if (notices.filter(":visible").filter(selectors_error.join(", ")).length)
return 'error'
if (notices.filter(":visible").filter(selectors_warning.join(", ")).length)
return 'warning'
return 'info'
},
/**
* .filter(":visible") unreliable when closed
*
* @returns {string} top priority
*/
getNoticesTopPriorityWhenClosed: () => {
if (notices.filter(selectors_error.join(", ")).length)
return 'error'
if (notices.filter(selectors_warning.join(", ")).length)
return 'warning'
return 'info'
},
/**
* Collect notices into panel.
* Remove dismiss-notices button.
*/
collectNotices: () => {
notices.appendTo(".notice_container").eq(0);
$(".notice_container").removeClass("empty"); // .empty removes padding
notices.appendTo(".notice_container").eq(0)
$(".notice_container").removeClass("empty") // .empty removes padding
haveClosed = true // initial collection has occurred.
/**
* When dismissible notices are dismissed, check if any notices are left on page.
* If no notices are left - remove Notice Panel entirely
*/
$(document).on("DOMNodeRemoved", ".notice.is-dismissible", (e) => {
panelObserver = new MutationObserver(() => {
notices = panel
.find("div.updated, div.error, div.notice, div.update-nag")
.filter(":visible");
NoticeManager.maybeRemoveNoticesPanel();
.find(selectors_all.join(", "))
.filter(":visible")
NoticeManager.maybeRemoveNoticesPanel()
});
panelObserver.observe(panel.get(0), { childList: true, subtree: true }); // only run once
},
addCounter: () => {
if (!button.children('.plugin-count').length) {
button.append(
$("<span/>").text(notices.filter(":visible").length).attr({
class: "plugin-count",
}).addClass(NoticeManager.getNoticesTopPriority())
)
}
},
/**
* cannot rely on filter(:visible)
*/
addCounterWhenClosed: () => {
if (!button.children('.plugin-count').length) {
button.append(
$("<span/>").text(notices.length).attr({
class: "plugin-count",
}).addClass(NoticeManager.getNoticesTopPriorityWhenClosed())
)
}
},
removeCounter: () => {
button.children(".plugin-count").remove()
},
/**
@ -151,14 +236,26 @@ var NoticeManager = (function ($, document) {
*/
maybeRemoveNoticesPanel: () => {
if (!notices.length) {
screenMeta.close(panel, button);
screenMeta.close(panel, button)
$("#meta-link-notices-link-wrap").detach();
$("#meta-link-notices-wrap").detach();
$("#meta-link-notices-link-wrap").detach()
$("#meta-link-notices-wrap").detach()
if (!$("#screen-meta-links").children().length)
$("#screen-meta-links").detach();
if ($("#screen-meta-links").children().length == 0) {
$("#screen-meta-links").detach()
}
}
},
};
}(jQuery,document) )
/**
* Move ALL notices above page title.
* Default no-panel action - override WordPress moving notices BELOW title.
* I HATE it when WordPress moves notices below title.
*/
moveAboveTitle: () => {
notices.insertBefore(".wrap:first")
},
}
}(jQuery);
jQuery(NoticeManager.bootstrap);

View File

@ -1,64 +1,98 @@
<?php
/**
* Plugin Name: Notice Manager
* Description: Manage notices on WordPress admin pages. Adds 'Notices' screen-meta-link.
* Version: 0.13
* Description: Manage notices on WordPress admin pages. Adds 'Notices' screen-meta-link panel to collect notices from page.
* Version: 0.27
* Author: abuyoyo
* Author URI: https://github.com/abuyoyo/
* Plugin URI: https://github.com/abuyoyo/notice-manager
* Update URI: https://github.com/abuyoyo/notice-manager
*/
defined( 'ABSPATH' ) || die( 'No soup for you!' );
/**
* Dependencies
* Allow all other auto-loaders to fail before including our own.
*/
if (
! class_exists( 'WPHelper\PluginCore' )
||
! class_exists( 'WPHelper\AdminPage' )
||
! function_exists( 'wph_add_screen_meta_panel' )
)
{
if ( file_exists( __DIR__ . '/vendor/autoload.php' ) ){
require_once __DIR__ . '/vendor/autoload.php';
}
}
use WPHelper\PluginCore;
/**
* Print setting page
* Bootstrap plugin and admin page (Tools > Notice Manager)
*/
new PluginCore(
__FILE__,
[
'update_checker' => true,
'action_links' => [
'settings' => [
'text' => 'Settings',
'href' => 'menu_page' // reserved option_name
],
],
'admin_page' => [
'parent' => 'options',
'render' => 'settings-page', // built-in settings page
'plugin_info' => true,
// 'plugin_info' => true, // disable on public repo
'settings' => [
'option_name' => 'notice_manager', // option_name used in wp_options table
'sections' => [
[
'id' => 'notice_manager',
// 'title' => 'N',
'description' => 'Setup How notice manager functions.',
'description' => 'Setup Notice Manager options.',
'description_container' => 'notice-info',
'fields' => [
[
'id' => 'above_title',
'title' => 'Above Title',
'type' => 'checkbox',
'description' => 'Simply move all notices above title. WordPress core moves notices below title using script. This script moves them back over the title. This option does not move notices into panel.',
],
[
'id' => 'screen_panel',
'title' => 'Notices Panel',
'type' => 'checkbox',
'description' => 'Enable\disable screen-meta-links \'Notices\' panel.',
'description' => 'Enable Screen Meta \'Notices\' panel. User can collect notices into collapsible panel.',
],
[
'id' => 'auto_collect',
'title' => 'Auto-Collect Notices',
'type' => 'checkbox',
'description' => 'Automatic collection of notices into panel.',
'description' => 'If Notices panel is enabled - auto-collect notices into panel on page load.',
],
[
'id' => 'auto_collapse',
'title' => 'Auto-collapse Panel',
'title' => 'Auto-Collapse Panel',
'type' => 'checkbox',
'description' => 'Notices panel will stay open for a few seconds on page load, and then close automatically.',
'description' => 'If auto-collect is enabled - Notices panel will stay open for a few seconds on page load, and then close automatically. Panel will not auto-collapse if it contains `error` level notices.',
],
[
'id' => 'distraction_free',
'title' => 'Distraction Free',
'type' => 'checkbox',
'description' => 'Notice Panel is closed on page load. Requires auto_collect.'
],
],
],
],
],
],
'update_checker' => true, // If Plugin Update Checker library is available - allow updates/auto-updates.
],
]
]
]
]
]
]
);
require_once 'src/NoticeManager.php';
add_action('plugins_loaded', function(){
new NoticeManager();
});
add_action( 'plugins_loaded', fn() => new NoticeManager() );

1671
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

24
package.json Normal file
View File

@ -0,0 +1,24 @@
{
"name": "notice-manager",
"version": "0.18.0",
"description": "Manage notices on WordPress admin pages. Adds 'Notices' screen-meta-link.",
"author": "abuyoyo",
"repository": {
"type": "git",
"url": "git+https://github.com/abuyoyo/notice-manager.git"
},
"bugs": {
"url": "https://github.com/abuyoyo/notice-manager/issues"
},
"homepage": "https://github.com/abuyoyo/notice-manager#readme",
"scripts": {
"start": "node-sass scss/ -o css/",
"build": "node-sass scss/ -o css/",
"watch": "node-sass -w scss/ -o css/",
"sass": "node-sass -w scss/ -o css/",
"test": "echo \"Error: no test specified\" && exit 1"
},
"devDependencies": {
"node-sass": "^4.9.3"
}
}

71
scss/notice-manager.scss Normal file
View File

@ -0,0 +1,71 @@
div.updated,
div.error,
div.notice-error,
div.update-nag {
@extend div.notice;
}
div.notice {
:not(.wrap) ~ & {
margin: 5px 20px 15px 2px;
}
// This should only be used if auto-collect/above-title is enabled
// classes .notices-auto-collect, .notices-above-title added to body tag by plugin
.notices-auto-collect #wpbody-content > &,
.notices-auto-collect .wrap > &,
.notices-above-title .wrap > & {
display: none;
}
}
#meta-link-notices-wrap {
margin: 0;
// padding: 8px 20px 12px;
position: relative;
> button.notice-dismiss {
position: relative;
display: flex;
width: 100%;
}
}
.notice_container {
padding: 9px 0px 1px 0px;
background-color: gainsboro;
&.empty {
padding: 0;
}
> div.notice {
margin: 5px 12px 15px 12px !important; // Override plugins custom css
}
}
#meta-link-notices .plugin-count {
display: inline-block;
// vertical-align: top;
box-sizing: border-box;
margin: 1px 0 -1px 2px;
padding: 0 5px;
min-width: 18px;
height: 18px;
border-radius: 9px;
background-color: #72aee6;
color: #fff;
font-size: 11px;
line-height: 1.6;
text-align: center;
z-index: 26;
&.warning {
background-color: #dba617;
}
&.error {
background-color: #d63638;
}
}

View File

@ -22,17 +22,23 @@ class NoticeManager{
function __construct(){
// exit early if not admin page
if ( !is_admin() )
if ( ! is_admin() )
return;
$this->options = get_option( 'notice_manager');
$this->options = get_option( 'notice_manager' );
add_action( 'admin_enqueue_scripts' , [ $this , 'admin_enqueues' ] );
if ( ! empty( $this->options['screen_panel'] ) ){
add_action( 'admin_init' , [ $this , 'register_notice_manager_panel' ] );
}else{
array_walk($this->options,function(&$item){$item=0;});
if ( ! empty( $this->options['auto_collect'] ) ){
add_filter( 'admin_body_class', fn($classes) => $classes . ' notices-auto-collect' );
}
}
if ( ! empty( $this->options['above_title'] ) ){
add_filter( 'admin_body_class', fn($classes) => $classes . ' notices-above-title' );
}
}
@ -40,7 +46,7 @@ class NoticeManager{
function admin_enqueues(){
wp_enqueue_script( 'notice_manager_panel', NOTICE_MANAGER_URL . 'js/notice_manager_panel.js' , null, false , true );
wp_localize_script( 'notice_manager_panel', 'notice_manager_options', $this->options );
wp_enqueue_style( 'admin_notices', NOTICE_MANAGER_URL . 'css/admin_notices.css' );
wp_enqueue_style( 'admin_notices', NOTICE_MANAGER_URL . 'css/notice-manager.css' );
}

View File

@ -0,0 +1,225 @@
# Changelog
WPHelper\AdminMenuPage
## 0.29
Release date: Oct 5 2023
### Fixed
- `composer.json` - Require `abuyoyo/metabox`. WPHelper\Metabox has been a required library since 0.25.
### Dependencies
- lib: WPHelper\Metabox (`abuyoyo/metabox`) ~0.8.
## 0.28
Release date: Oct 4 2023
### Added
- Option `parent` accepts `tools` as shorthand for `tools.php`.
- Add link to Install Plugin page in "CMB2 plugin missing" template.
## 0.27
Release date: Sep 10 2023
### Fixed
- Fix nav-tabs appearing on pages without `tab_group`.
## 0.26
Release date: Jun 20 2023
### Added
- Add `allow_on_front` setting to CMB2 pages. Hooks metabox on `cmb2_init` instead of `cmb2_admin_init`.
- If defined `WPH_DEBUG` add WPHelper classes debug information to plugin info meta box.
## 0.25
Release date: Jun 9 2023
### Added
- Non-CMB2 pages can be added to CMB2 tab groups. New options `tab_group` and `tab_title`
### Changed
- New method `render_plugin_info_meta_box`. Deprecate `render_plugin_info_box`.
- Plugin info meta box rendered using `WPHelper\MetaBox`.
### Fixed
- Fix several PHP undefined variable warnings.
### Internal
- Setting pages/wrap template uses WordPress Core `do_meta_boxes` to render `side` meta boxes div.
- Add variables to `AdminPage::options()` array.
- Multiple code refactoring and template restructuring.
## 0.24
Release date: Jan 28 2023
### Fixed
- Fix plugin info meta box when no PluginCore is available.
- Fix PHP deprecated notice.
## 0.23
Release date: Jan 15 2023
### Added
- Add action hook `wphelper/plugin_info_meta_box/{$slug}` to modify and render plugin info meta box.
- Add support for `textarea` input field in SettingsPage.
- Add `sanitize_callback` option - allow plugins to supply their own sanitize function.
- Add `render` to fields - allow plugins to supply their own render callback for fields.
- Add `placeholder` to fields - allow plugins to supply placeholder values for fields.
### Fixed
- Fix default value handling for fields.
### Internal
- Rename `tpl/` template parts.
- Minor changes and fixes.
## 0.22
Release date: Jan 1 2023
### Fixed
- Fix error when `plugin_info = true` but `plugin_core` is not set.
## 0.21
### Fixed
- Minor fixes.
## 0.20
### Added
- Add SettingsPage section option `description-container`. Accepts `card` div, `notice`, `notice-info` and `none`.
- Sanitize SettingsPage text, url and email fields.
## 0.19
### Added
- SettingsPage supports `text`, `url`, `email` fields.
- CMB2_OptionsPage supports all admin menu top-level slugs.
### Fixed
- Fix PHP fatal error: cannot redeclare function `wph_extra_plugin_headers()`.
### Changed
- If CMB2 plugin is not activated - show missing plugin card on `cmb2` and `cmb2-tabs` pages.
## 0.18
### Added
- Add `wrap` parameter to output WordPress admin `.wrap` template. Accepts `simple` and `sidebar`.
- Accept `plugin_info = true` to output default plugin info meta box and wrap.
- Add `Last Update` and `Release Date` optional headers to WordPress theme headers (Used in plugin info-box).
### Changed
- All classes are pluggable.
- Prevent direct access if not withing WordPress environment.
## 0.17
### Changed
- Various improvements to CMB2 settings pages.
- Make use of CMB2 2.9.0's `options_page_tab_nav_output()` to render tabs on non-CMB2 pages.
- Plugins can provide their own plugin info-box render callback.
- Parent item's first sub-menu page (itself) uses item's `tab_title` instead of `menu_title`
### Added
- Add action `wphelper/adminpage/plugin_info_box/$slug` to render plugin info-box.
- Add `Last Update` and `Release Date` optional headers to WordPress plugin headers (Used in plugin info-box).
## 0.16
### Fixed
- Fix CMB2 "multi" options page to actually override fields.
### Changed
- Add CMB2 fields directly in options array instead of using `add_field` method.
## 0.15
### Changed
- Restore deprecated param to SettingsPage constructor and add `_deprecated_argument` message.
## 0.14
### Added
- Add CMB2 Options-page delegation. Allows adding CMB2 options page.
- Add CMB2 Options "multi" page. Allows CMB2 options page that saves each field to its own row in options table.
- Supports CMB2 tabs in CMB2 option-pages.
- Add Plugin Info metabox to CMB2 tables.
### Changed
- Deprecate `AdminPage->setup` - add `_doing_it_wrong` message.
- Admin Page method `bootstrap()` runs on `init` hook instead of constructor. Allows setter functions to have effect.
## 0.13
### Added
- Add `methods` option to load functions on `load-{hook_suffix}` hook.
- Add `get_hook_suffix()` getter method (`hook_suffix` variable is no longer public).
## v0.12
### Changed
- New `AdminPage` class.
- Deprecate class `AdminMenuPage` in favor of `AdminPage`.
- Restructure source files.
## v0.11
### Added
- Setting Page - class and template for registering WordPress settings page.
- Options_Menu - use WordPress core `add_options_page` to register page.
### Changed
- No longer require call to `setup()` method. Bootstrap into WordPress from constructor method.
## v0.10
### Added
- Styles - enqueue styles to registered admin page
## v0.9
### Changed
- Don't use extract() in constructor
- Use setter methods for all variables
### Fixed
- Fix PHP notices: undefined property
## v0.8
### Fixed
- Removed calls to AdminNotice causing errors.
## v0.7
### Fixed
- Fixed error when no scripts are added
### Changed
- Accept `render_cb` and `render_tpl` args. Use `render` method instead of `template`
- Print default template if no callback or template provided
## v0.6
### Added
- Initial public release
- Register and print top-level or submenu pages to WordPress admin menu
- Enqueue scripts to registered admin page

29
vendor/abuyoyo/adminmenupage/LICENSE vendored Normal file
View File

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2019, abuyoyo
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

53
vendor/abuyoyo/adminmenupage/README.md vendored Normal file
View File

@ -0,0 +1,53 @@
# WPHelper \ AdminMenuPage
> Helper class for simple admin menu page registration in WordPress.
## Requirements
* PHP >= 7.4
* [Composer](https://getcomposer.org/)
* [WordPress](https://wordpress.org)
## Installation
Install with [Composer](https://getcomposer.org/) as a library.
```PHP
// Require the Composer autoloader anywhere in your code.
require __DIR__ . '/vendor/autoload.php';
```
WPHelper\AdminMenuPage uses [PSR-4](https://www.php-fig.org/psr/psr-4/) to autoload.
OR
Install as WordPress plugin and activate.
## Basic Usage
```PHP
// Import AdminPage.
use WPHelper\AdminPage;
// Register the admin menu page.
$args = [
'title' => 'The Tile of My Page', // title - passed to add_menu_page
'menu_title' => 'Page Title', // menu_title - passed to add_menu_page (optional - will use title if none provided)
'capability' => 'manage_options', // capability - passed to add_menu_page (optional - will default to 'manage_options')
'slug' => 'my_page', // slug - passed to add_menu_page
'template' => 'tpl/my_admin_page.php', // template - include file to print the page. wrapped in callback and passed to add_menu_page
'parent' => 'parent_page_slug'; // optional - slug of parent page if creating submenu
'icon_url' => $icon_url; // optional - icon url passed to add_menu_page/add_submenu_page
'position' => 4; // optional - passed to add_menu_page
'scripts' => [ // optional - script parameters passed to enqueue_scripts. Will only enqueue scripts on admin page
[ 'script_handle', 'js/my_script.js', ['jquery'], false, true ],
[ 'another_script', 'js/my_other_script.js', ['jquery', 'script_handle'], false, true ]
];
];
// Register the admin menu page.
$admin_menu_page = new AdminPage( $args );
// That's it. We're done.
// This function can be called from anywhere. No need to wrap in any hook.
```

View File

@ -0,0 +1,23 @@
{
"name": "abuyoyo/adminmenupage",
"description": "WordPress admin menu page helper class",
"type": "library",
"version": "0.29",
"time": "2023-10-05",
"license": "BSD-3-Clause",
"require": {
"abuyoyo/metabox": "~0.8"
},
"suggest": {
"abuyoyo/plugincore": "~0.26",
"cmb2/cmb2": "~2.9"
},
"autoload": {
"psr-4": {
"WPHelper\\" : "src/"
},
"files": [
"wph_admin_page.php"
]
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace WPHelper;
defined( 'ABSPATH' ) || die( 'No soup for you!' );
if ( ! class_exists( AdminMenuPage::class ) ):
/**
* AdminMenuPage
*
* Deprecated class. Use AdminPage instead.
*
* @author abuyoyo
* @since 0.12
*
*/
class AdminMenuPage extends AdminPage{
}
endif;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,268 @@
<?php
namespace WPHelper;
defined( 'ABSPATH' ) || die( 'No soup for you!' );
use CMB2;
use CMB2_Options_Hookup;
if ( ! class_exists( CMB2_OptionsPage::class ) ):
/**
* CMB2_OptionsPage
*
* Helper class
* Create WordPress Setting page using CMB2 Options Hookup.
*
* @author abuyoyo
*
* @see CMB2_Options_Hookup::options_page_output and 'display_cb' - to manipulate tabs
*
* @todo add 'submenu' field and functionality (rename first submenu item) to WPHelper\AdminPage
*/
class CMB2_OptionsPage{
/**
* @var AdminPage $admin_page
*/
public $admin_page;
/**
* @var array $fields
*/
protected $fields;
/**
* @var CMB2 $cmb
*/
private $cmb;
/**
* @var array $cmb2_options
*/
protected $cmb2_options;
/**
* @param AdminPage $admin_page
*/
function __construct( $admin_page ){
$this->admin_page = $admin_page;
$admin_options = $this->admin_page->options();
$settings = $admin_options['settings'];
$settings['object_types'] = [ 'options-page' ];
$settings['display_cb'] ??= [ $this, 'options_page_output' ];
$settings['option_key'] ??= ( $settings['option_name'] ?? ( $settings['id'] ?? $admin_options['slug'] ) );
$settings['title'] ??= $admin_options['title'];
$settings['menu_title'] ??= $admin_options['menu_title'];
// @todo Only if cmb2-tabs
$settings['tab_title'] ??= $admin_options['tab_title'] ?? $settings['submenu_title'] ?? $settings['menu_title'];
$settings['parent_slug'] ??= $admin_options['parent'];
$settings['position'] ??= $admin_options['position'];
$settings['icon_url'] ??= $admin_options['icon_url'];
$settings['capability'] ??= $admin_options['capability'];
/**
* CMB2 must have admin menu page slug same as option key :(
*/
$settings['id'] = $settings['option_key'];
unset( $settings['option_name'] );
/**
* CMB2 only accepts url slug
*
* @todo export parent_slug conversion to dedicated method
* @todo perhaps move this to AdminPage::parent() method
*/
switch ( $settings['parent_slug'] ) {
case 'dashboard':
$settings['parent_slug'] = 'index.php';
break;
case 'posts':
$settings['parent_slug'] = 'edit.php';
break;
case 'media':
$settings['parent_slug'] = 'upload.php';
break;
case 'pages':
$settings['parent_slug'] = 'edit.php?post_type=page';
break;
case 'comments':
$settings['parent_slug'] = 'edit-comments.php';
break;
case 'themes':
case 'appearance': // Official WordPress designation
$settings['parent_slug'] = 'themes.php';
break;
case 'plugins':
$settings['parent_slug'] = 'plugins.php';
break;
case 'users':
$settings['parent_slug'] = 'users.php';
break;
case 'options':
case 'settings': // Official WordPress designation
$settings['parent_slug'] = 'options-general.php';
break;
case 'tools':
$settings['parent_slug'] = 'tools.php';
break;
case 'network':
case 'network_settings':
$settings['parent_slug'] = 'settings.php';
break;
case null:
break;
default:
if ( post_type_exists( $settings['parent_slug'] ) ){
$settings['parent_slug'] = "edit.php?post_type={$settings['parent_slug']}";
}
break;
}
if ( $admin_options['render'] == 'cmb2-tabs' ){
$settings['tab_group'] ??= $settings['parent_slug'] ?? $settings['id'];
$settings['tab_title'] ??= $settings['menu_title'];
}
$this->fields = $settings['fields'] ?? [];
/**
* @todo revisit this - might not need to unset fields
*/
if ( isset( $settings['fields'] ) ){
unset( $settings['fields'] );
}
/**
* If args are formatted for SettingsPage we convert to CMB2 options format
* Convert nested sections=>fields to straight title, fields, title, fields.
*
* @todo export this to dedicated method
*/
if ( isset( $settings['sections'] ) ){
$this->fields = [];
foreach ( $settings['sections'] as $section ){
$title_field = [];
if ( $id = $section['id'] ?? $section['slug'] ){
$title_field['id'] = $id;
}
if ( $name = $section['name'] ?? $section['title'] ){
$title_field['name'] = $name;
}
if ( $desc = $section['desc'] ?? $section['description'] ){
$title_field['desc'] = $desc;
}
if ( ! empty($title_field)){
$title_field['type'] = 'title';
$this->fields[] = $title_field;
}
foreach ($section['fields'] as $field){
$field = $this->convert_field_to_cmb2_field($field);
$this->fields[] = $field;
}
}
unset( $settings['sections'] );
}
/**
* Special provision for cmb2-switch
*/
if ( ! class_exists( 'CMB2_Switch_Button' ) ){
array_walk(
$this->fields,
function( &$field ){
if ( $field['type'] == 'switch'){
$field['type'] = 'checkbox';
}
}
);
}
// re-insert fields back into settings
$settings['fields'] = $this->fields;
$this->cmb2_options = $settings;
// register parent pages before sub-menu pages
$priority = empty( $settings['parent_slug'] ) ? 9 : 10;
if ( $settings['allow_on_front'] ?? false ){
add_action( 'cmb2_init', [ $this, 'register_metabox' ], $priority );
} else {
add_action( 'cmb2_admin_init', [ $this, 'register_metabox' ], $priority );
}
/**
* @todo add 'submenu' field and functionality to WPHelper\AdminPage
* @todo reverse control/flow - so 'tab title' inherits/defaults to AdminPage 'submenu' field if exists.
*/
if ( empty( $settings['parent_slug'] ) && $settings['menu_title'] != $settings['tab_title'] ){
add_action('admin_menu', [ $this, 'replace_submenu_title'], 11 );
}
}
public function register_metabox(){
$this->cmb = new CMB2( $this->cmb2_options );
}
/**
* Display options-page output. To override, set 'display_cb' box property.
*
* @param CMB2_Options_Hookup $hookup - instance of Options Page Hookup class (caller of this function)
*
* @see CMB2_Options_Hookup
*/
public function options_page_output( $hookup ) {
$options = $this->admin_page->options();
if ( ! empty( $options['plugin_core'] ) || ! empty( $options['plugin_info'] ) ){
include __DIR__ . '/tpl/wrap-cmb2-sidebar.php';
} else {
include __DIR__ . '/tpl/wrap-cmb2-simple.php';
}
}
private function convert_field_to_cmb2_field( $field ){
$field['id'] ??= $field['slug'] ?? null;
$field['name'] ??= $field['title'] ?? null;
$field['desc'] ??= $field['description'] ?? null;
unset( $field['slug'] );
unset( $field['title'] );
unset( $field['description'] );
return array_filter($field);
}
/**
* Replace submenu title of parent item with tab title
*
* @todo add 'submenu' field and functionality to WPHelper\PluginCore
*/
public function replace_submenu_title(){
remove_submenu_page( $this->cmb2_options['id'], $this->cmb2_options['id'] );// Remove the default submenu so we can add our customized version.
add_submenu_page(
$this->cmb2_options['id'],
$this->cmb2_options['title'],
$this->cmb2_options['tab_title'],
$this->cmb2_options['capability'],
$this->cmb2_options['id'],
'',
0,
);
}
}
endif;

View File

@ -0,0 +1,34 @@
<?php
namespace WPHelper;
defined( 'ABSPATH' ) || die( 'No soup for you!' );
if ( ! class_exists( CMB2_OptionsPage_Multi::class ) ):
// Require dependency CMB2_Override_Meta
if ( ! trait_exists( CMB2_Override_Meta::class ) ) {
require_once __DIR__ . '/CMB2_Override_Meta.php';
}
/**
* CMB2_OptionsPage - MULTI
*
* Helper class
* Custom CMB2 Options page - saves each field as separate option in Options table.
* Create WordPress Setting page using CMB2 Options Hookup.
*
* @author abuyoyo
*
* @todo - Rename class from MULTI to something more descriptive
*/
class CMB2_OptionsPage_Multi extends CMB2_OptionsPage{
use CMB2_Override_Meta;
function __construct( $admin_page )
{
parent::__construct( $admin_page );
$this->cmb2_override_fields( $this->fields );
}
}
endif;

View File

@ -0,0 +1,56 @@
<?php
namespace WPHelper;
defined( 'ABSPATH' ) || die( 'No soup for you!' );
if ( ! trait_exists( CMB2_Override_Meta::class ) ):
/**
* CMB2 Options page override meta
*
* Override default cmb2 meta.
* Saves each field as separate option in wp_options table
*/
trait CMB2_Override_Meta{
function cmb2_override_fields( $fields ){
foreach( $fields as $field ){
add_filter( "cmb2_override_{$field['id']}_meta_value", [ $this, 'cmb2_override_get' ], 10, 4 );
add_filter( "cmb2_override_{$field['id']}_meta_save", [ $this, 'cmb2_override_save' ], 10, 4 );
add_filter( "cmb2_override_{$field['id']}_meta_remove", [ $this, 'cmb2_override_delete' ], 10, 4 );
}
}
/**
* cmb2_override_meta_value
*
*/
function cmb2_override_get( $override, $args, $field_args, $field ) {
return get_option( $field_args['field_id'], '' );
}
/**
* cmb2_override_meta_save
*
*/
function cmb2_override_save( $override, $args, $field_args, $field ) {
// Here, we're storing the data to the options table, but you can store to any data source here.
// If to a custom table, you can use the $args['id'] as the reference id.
$updated = update_option( $field_args['id'], $args['value'], false );
return !! $updated;
}
/**
* cmb2_override_meta_remove
*
*/
function cmb2_override_delete( $override, $args, $field_args, $field ) {
// Here, we're removing from the options table, but you can query to remove from any data source here.
// If from a custom table, you can use the $args['id'] to query against.
// (If we do "delete_option", then our default value will be re-applied, which isn't desired.)
$updated = update_option( $field_args['id'], '' );
// $updated = update_option( $field_args['field_id'], '' );
return !! $updated;
}
}
endif;

View File

@ -0,0 +1,116 @@
<?php
namespace WPHelper;
use DateTime;
if ( ! class_exists( PluginInfoMetaBox::class ) ):
/**
* Plugin Info Metabox
*
* Get instance of PluginCore
* Render default plugin info box template.
*
* @since 0.14
*/
class PluginInfoMetaBox{
private $tpl = '/tpl/plugin_info_meta_box.php';
private $tpl_inside = '/tpl/plugin_info_meta_box-inside.php';
private $tpl_debug = '/tpl/plugin_info_meta_box-wph_debug.php';
/**
* @var PluginCore
*/
public $plugin_core;
function __construct( PluginCore $plugin_core )
{
$this->plugin_core = $plugin_core;
/**
* Allow plugins to render or modify plugin info box
*
* Call: do_action('wphelper/plugin_info_meta_box/{$slug}')
* action used in AdminPage::render_plugin_info_meta_box()
*
* @since 0.23
*/
add_action( "wphelper/plugin_info_meta_box/{$this->plugin_core->slug()}", [ $this, 'plugin_info_box' ] );
add_action( "wphelper/plugin_info_meta_box/inside/{$this->plugin_core->slug()}", [ $this, 'inside' ] );
}
/**
* Setup args used in template.
*
* @todo move 'repo' setup to method from template
*/
function setup_template_args() {
$plugin_data = $this->plugin_core->plugin_data();
$last_update = $plugin_data['Last Update'] ?: $plugin_data['Release Date'];
$last_update = DateTime::createFromFormat('Y_m_d', $last_update);
if ($last_update) {
$diff = (int) abs( time() - $last_update->format('U') );
if ( $diff < (DAY_IN_SECONDS) ){
$update_message = 'Today';
}elseif ($diff < (2 * DAY_IN_SECONDS)){
$update_message = 'Yesterday';
}else{
$update_message = human_time_diff($last_update->format('U')) . ' ago';
}
} else {
$update_message = '';
}
return compact('plugin_data','update_message');
}
/**
* PLUGIN INFO BOX
*
* Display plugin info meta-box on admin pages
*
* @since iac_engine 1.1.0
* @since iac_engine 1.2.0 plugin_info_box now a function
* @since iac_engine 1.3.0 use 'Last Update' header
* @since 0.14 PluginInfoMetaBox::plugin_info_box()
*
* @todo rename method render()
*/
function plugin_info_box(){
$args = $this->setup_template_args();
extract($args);
include __DIR__ . $this->tpl;
}
/**
* Only print meta-box .inside
* No header.
*/
function inside(){
$args = $this->setup_template_args();
extract($args);
include __DIR__ . $this->tpl_inside;
}
/**
* WPHelper classes debug info
*
* Prints inside Plugin Info Meta Box
*
* @since 0.26
*/
function wph_debug() {
include __DIR__ . $this->tpl_debug;
}
}
endif;

View File

@ -0,0 +1,377 @@
<?php
namespace WPHelper;
defined( 'ABSPATH' ) || die( 'No soup for you!' );
use function register_setting;
use function add_settings_section;
use function add_settings_field;
use function checked;
use function get_option;
if ( ! class_exists( SettingsPage::class ) ):
/**
* SettingsPage
*
* Helper class
* Create WordPress Setting page.
*
* @author abuyoyo
*
* @since 0.11
*/
class SettingsPage{
/**
* AdminPage instance that called this class
*
* @var AdminPage $admin_page instance
*/
protected $admin_page;
/**
* Page slug to display sections
*
* @var string $page
*/
public $page;
/**
* option_name key used in wp_options table
*
* @var string $option_name
*/
protected $option_name;
/**
* option_group used by register_setting() and settings_fields()
*
* @var string $option_group
*/
public $option_group;
/**
* Sections
*
* @var array[] $sections
*/
public $sections = [];
/**
* Fields
*
* @var array[] $fields
*/
public $fields = [];
/**
* Sanitize Callback
*
* @var Callable $sanitize_callback
*/
public $sanitize_callback;
/**
* Constructor.
*
* @param AdminPage $admin_page instance
* @param null $settings deprecated
*/
public function __construct( $admin_page, $settings = null )
{
if ( ! empty( $settings ) ) {
_deprecated_argument( __FUNCTION__, '3.0.0' );
}
// save reference to caller instance
$this->admin_page = $admin_page;
$admin_options = $admin_page->options();
$settings = $admin_options['settings'];
$this->page = $admin_options['slug'];
$this->option_name = $settings['option_name'] ?? str_replace( '-', '_' , strtolower( $this->page ) );
$this->option_group = $settings['option_group'] ?? $this->page . '_option_group';
$this->sanitize_callback = $settings['sanitize_callback'] ?? null;
// PHP Warning: foreach() argument must be of type array|object
foreach ( $settings['sections'] ?? [] as $section ) {
// extract fields
foreach ( $section['fields'] as $field ){
$field['section_id'] = $section['id']; // create back-reference in field to section. ( @see add_settings_field() )
$field['name'] = $this->option_name . '[' . $field['id'] . ']';
$this->fields[] = $field;
}
unset( $section['fields'] );
$this->sections[] = $section; // save without fields
}
add_action( 'admin_init', [ $this, 'register_settings' ] );
}
public function register_settings() {
register_setting(
$this->option_group, // $option_group - A settings group name. Must exist prior to the register_setting call. This must match the group name in settings_fields()
$this->option_name, // $option_name - The name of an option to sanitize and save.
$this->sanitize_callback ?? [ $this,'sanitize_settings' ] // callback ?? fallback // $sanitize_callback - A callback function that sanitizes the option's value. (see also: built-in php callbacks)
);
foreach ( $this->sections as $section ){
add_settings_section(
$section['id'], // $id - Slug-name to identify the section. Used in the 'id' attribute of tags.
$section['title'] ?? null, // $title - Formatted title of the section. Shown as the heading for the section.
$this->section_description_cb( $section ), // $callback - Function that echos out any content at the top of the section (between heading and fields).
$this->page // $page - The slug-name of the settings page on which to show the section.
//Built-in pages include 'general', 'reading', 'writing', 'discussion', 'media', etc.
);
}
foreach ( $this->fields as $field ) {
add_settings_field(
$field['id'],
$field['title'],
$field['render'] ?? [ $this, "print_{$field['type']}" ],
$this->page, // can built-in pages: (general, reading, writing, ...)
$field['section_id'],
$field //send setting array as $args for print function
);
}
}
/**
* Print checkbox input field
* Support field type 'checkbox'
*
* @since 0.11
*/
function print_checkbox( $field ) {
extract($field);
$options = get_option( $this->option_name );
$input_tag = sprintf(
'<label for="%1$s">
<input name="%2$s" type="checkbox" id="%1$s" aria-describedby="%1$s-description" value="1" %4$s />
%3$s
</label>',
$id,
$name,
$description,
checked( ( $options[$id] ?? false ), '1', false)
);
/**
* Allow plugins to directly manipulate field HTML
*/
$input_tag = apply_filters( 'wphelper/settings_page/input_checkbox', $input_tag, $field, $this->option_name, $options );
echo $input_tag;
}
/**
* Print text input field
* Support field type 'text'
*
* @since 0.19
*/
function print_text( $field ) {
extract($field);
$options = get_option( $this->option_name );
$input_tag = sprintf(
'<input name="%2$s" type="text" id="%1$s" aria-describedby="%1$s-description" value="%3$s" placeholder="%4$s" class="regular-text">',
$id,
$name,
$options[$id] ?: $default ?? '',
$placeholder ?? ''
);
if ( ! empty( $description ) ) {
$input_tag .= sprintf(
'<p class="description" id="%1$s-description">%2$s</p>',
$id,
$description
);
}
/**
* Allow plugins to directly manipulate field HTML
*/
$input_tag = apply_filters( 'wphelper/settings_page/input_text', $input_tag, $field, $this->option_name, $options );
echo $input_tag;
}
/**
* Print url input field
* Support field type 'url'
*
* @since 0.19
*/
function print_url( $field ){
extract($field);
$options = get_option( $this->option_name );
$input_tag = sprintf(
'<input name="%2$s" type="url" id="%1$s" aria-describedby="%1$s-description" placeholder="%4$s" value="%3$s" class="regular-text code ">',
$id,
$name,
$options[$id] ?: $default ?? '',
$placeholder ?? ''
);
if ( ! empty( $description ) ) {
$input_tag .= sprintf(
'<p class="description" id="%1$s-description">%2$s</p>',
$id,
$description
);
}
/**
* Allow plugins to directly manipulate field HTML
*/
$input_tag = apply_filters( 'wphelper/settings_page/input_url', $input_tag, $field, $this->option_name, $options );
echo $input_tag;
}
/**
* Print email input field
* Support field type 'email'
*
* @since 0.19
*/
function print_email( $field ){
extract($field);
$options = get_option( $this->option_name );
$input_tag = sprintf(
'<input name="%2$s" type="email" id="%1$s" aria-describedby="%1$s-description" placeholder="%4$s" value="%3$s" class="regular-text ltr">',
$id,
$name,
$options[$id] ?: $default ?? '',
$placeholder ?? ''
);
if ( ! empty( $description ) ) {
$input_tag .= sprintf(
'<p class="description" id="%1$s-description">%2$s</p>',
$id,
$description
);
}
/**
* Allow plugins to directly manipulate field HTML
*/
$input_tag = apply_filters( 'wphelper/settings_page/input_email', $input_tag, $field, $this->option_name, $options );
echo $input_tag;
}
/**
* Print email input field
* Support field type 'email'
*
* @since 0.23
*/
function print_textarea( $field ){
extract($field);
$options = get_option( $this->option_name );
$textarea = sprintf(
'<textarea class="regular-text" rows="5" id="%1$s-description" name="%2$s" placeholder="%4$s">%3$s</textarea>',
$id,
$name,
$options[$id] ?: $default ?? '',
$placeholder ?? ''
);
if ( ! empty( $description ) ) {
$textarea .= sprintf(
'<p class="description" id="%1$s-description">%2$s</p>',
$id,
$description
);
}
/**
* Allow plugins to directly manipulate field HTML
*/
$textarea = apply_filters( 'wphelper/settings_page/textarea', $textarea, $field, $this->option_name, $options );
echo $textarea;
}
/**
* Sanitizes entire $options array.
*/
function sanitize_settings( $options ) {
$new_options = [];
foreach( $options as $id => $option ) {
$field = current(
array_filter(
$this->fields,
fn($item) => $item['id'] == $id
)
);
switch ( $field['type'] ) {
case 'checkbox':
$new_options[$id] = $option == 1 ? 1 : 0;
break;
case 'text':
case 'textarea':
$new_options[$id] = sanitize_text_field( $option );
break;
case 'email':
$new_options[$id] = sanitize_email( $option );
break;
case 'url':
$new_options[$id] = esc_url_raw( $option );
break;
default:
break;
}
}
return $new_options;
}
function section_description_cb( $section ) {
if ( ! empty( $section['description'] ) ) {
switch ( $section[ 'description_container' ] ?? '' ){
case 'card':
$container = '<div class="card">%s</div>';
break;
case 'notice':
case 'notice-info':
$container = '<div class="notice notice-info inline"><p>%s</p></div>';
break;
case 'none':
$container = '%s';
break;
default:
$container = '<p>%s</p>';
break;
}
return fn() => printf( $container, $section['description'] );
}
}
}
endif;

View File

@ -0,0 +1,15 @@
<?php
/**
* Settings-Page settings form
*
* Print WordPress settings form and submit button.
*/
?>
<form method="post" action="options.php">
<?php
/** @var WPHelper\AdminPage $this */
settings_fields( $this->settings_page->option_group );// Print hidden setting fields
do_settings_sections( $this->settings_page->page );// Print title, info callback and form-table
submit_button();
?>
</form>

View File

@ -0,0 +1,15 @@
<?php
/**
* Template form.cmb-form
*
* Print form tag used by CMB2 options page.
*
* @var WPHelper\CMB2_Options_Page $this
* @var CMB2_Options_Hook $hookup
*/
?>
<form class="cmb-form" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="POST" id="<?php echo $this->cmb->cmb_id; ?>" enctype="multipart/form-data" encoding="multipart/form-data">
<input type="hidden" name="action" value="<?php echo esc_attr( $hookup->option_key ); ?>">
<?php $hookup->options_page_metabox(); ?>
<?php submit_button( esc_attr( $this->cmb->prop( 'save_button' ) ), 'primary', 'submit-cmb' ); ?>
</form>

View File

@ -0,0 +1,37 @@
<?php
/**
* Plugin Info Metabox - .inside
*/
?>
<h3 style="font-weight: 100; font-size: 1.5em;"><?php echo $plugin_data['Name']; ?></h3>
<p>
<?php if ( ! empty( $plugin_data['Version'] ) ): ?>
Version: <?php echo $plugin_data['Version']; ?><br/>
<?php endif; ?>
<?php if ( ! empty( $plugin_data['Author'] ) ): ?>
Author:
<?php if ( ! empty( $plugin_data['AuthorURI'] ) ): ?>
<a href="<?php echo $plugin_data['AuthorURI'] ?>"><?php echo $plugin_data['Author']; ?></a><br/>
<?php else: ?>
<?php echo $plugin_data['Author']; ?><br/>
<?php endif; ?>
<?php endif; ?>
<?php if ( ! empty( $plugin_data['UpdateURI'] ) || ! empty( $plugin_data['PluginURI'] ) ): ?>
Repo: <a href="<?php echo $plugin_data['UpdateURI'] ?: $plugin_data['PluginURI']; ?>">
<?php echo $plugin_data['TextDomain'] ?? $this->$plugin_core->slug(); ?>
</a><br/>
<?php endif; ?>
<?php if ( ! empty( $update_message ) ): ?>
Last Updated: <?php echo $update_message; ?>
<?php endif; ?>
</p>
<?php
/**
* Print WPHelper debug info in plugin info meta box
*
* @since 0.26
*/
if ( defined('WPH_DEBUG') && WPH_DEBUG ) {
/** @var WPHelper\PluginInfoMetaBox $this */
$this->wph_debug();
}

View File

@ -0,0 +1,84 @@
<?php
/**
* Plugin Info Metabox - .wph-debug
*
* @since 0.26
*/
use WPHelper\AdminPage;
use WPHelper\MetaBox;
use WPHelper\PluginCore;
use WPHelper\Utility\Singleton;
use WPHelper\DatabaseTable;
if (class_exists(AdminPage::class)){
$wph_admin_ref = new ReflectionClass(AdminPage::class);
$wph_admin_file = $wph_admin_ref->getFileName();
$wph_admin_composer = json_decode(file_get_contents( dirname( dirname( $wph_admin_file ) ) . '/composer.json' )) ;
}
if (class_exists(PluginCore::class)){
$wph_pc_ref = new ReflectionClass(PluginCore::class);
$wph_pc_file = $wph_pc_ref->getFileName();
$wph_pc_composer = json_decode(file_get_contents( dirname( $wph_pc_file ) . '/composer.json' )) ;
}
if (class_exists(MetaBox::class)){
$wph_mb_ref = new ReflectionClass(MetaBox::class);
$wph_mb_file = $wph_mb_ref->getFileName();
$wph_mb_composer = json_decode(file_get_contents( dirname( $wph_mb_file ) . '/composer.json' )) ;
}
if (trait_exists(Singleton::class)){
$wph_util_ref = new ReflectionClass(Singleton::class);
$wph_util_file = $wph_util_ref->getFileName();
$wph_util_composer = json_decode(file_get_contents( dirname( dirname( $wph_util_file ) ) . '/composer.json' )) ;
}
if (class_exists(DatabaseTable::class)){
$wph_db_ref = new ReflectionClass(DatabaseTable::class);
$wph_db_file = $wph_db_ref->getFileName();
$wph_db_composer = json_decode(file_get_contents( dirname( $wph_db_file ) . '/composer.json' )) ;
}
?>
<style>
.inside {
word-wrap: break-word;
}
</style>
<?php if ( ! empty( $wph_admin_composer ) ): ?>
<hr>
<p>
AdminPage: <?php echo $wph_admin_composer->version; ?><br/>
Location: <?php echo $wph_admin_file; ?><br/>
<?php endif; ?>
<?php if ( ! empty( $wph_pc_composer ) ): ?>
<hr>
<p>
PluginCore: <?php echo $wph_pc_composer->version; ?><br/>
Location: <?php echo $wph_pc_file; ?><br/>
<?php endif; ?>
<?php if ( ! empty( $wph_mb_composer ) ): ?>
<hr>
<p>
MetaBox: <?php echo $wph_mb_composer->version; ?><br/>
Location: <?php echo $wph_mb_file; ?><br/>
<?php endif; ?>
<?php if ( ! empty( $wph_util_composer ) ): ?>
<hr>
<p>
Utility: <?php echo $wph_util_composer->version; ?><br/>
Location: <?php echo $wph_util_file; ?><br/>
<?php endif; ?>
<?php if ( ! empty( $wph_db_composer ) ): ?>
<hr>
<p>
DatabaseTable: <?php echo $wph_db_composer->version; ?><br/>
Location: <?php echo $wph_db_file; ?><br/>
<?php endif; ?>
</p>

View File

@ -0,0 +1,12 @@
<?php
/**
* Plugin Info Metabox
*/
?>
<div id="plugin_info" class="postbox">
<h2 style="border-bottom: 1px solid #eee;"><span>Plugin Info</span></h2>
<div class="inside">
<?php include __DIR__ . '/plugin_info_meta_box-inside.php'; ?>
</div><!-- .inside -->
</div><!-- .postbox -->

View File

@ -0,0 +1,41 @@
<?php
/**
* Bootstrap WordPress core meta-boxes to generate metaboxes
*
* @var WPHelper\AdminPage $this
*/
?>
<style>
/*
we don't actually have draggable/movable metaboxes
.hide-if-no-js / hidden classes would help
*/
.handle-actions {
display: none;
}
.postbox .postbox-header .hndle {
cursor: unset;
}
</style>
<?php
/**
* Allow meta-boxes to hook to this page ('side' context).
*
*
*/
do_action('add_meta_boxes', $this->get_hook_suffix(), 'side' );
/**
* Remove 'Featured Image' meta-box added by core.
*
* @see register_and_do_post_meta_boxes() (wp-admin/includes/meta-boxes.php)
* @todo Investigate why $thumbnail_support returns true for our pages.
*/
remove_meta_box( 'postimagediv', $this->get_hook_suffix(), 'side' );
/**
* Render meta-boxes
*
* Renders div.meta-box-sortables
*/
do_meta_boxes( $this->get_hook_suffix(), 'side', null );

View File

@ -0,0 +1,17 @@
<?php
/**
* Template - Tabs navigation row
*
* Print tabs navigation row.
*
* @var CMB2_Options_Hookup $hookup
*/
if ( ! isset( $hookup ) ){
return;
}
$tabs = $hookup->get_tab_group_tabs();
if ( count( $tabs ) > 1 ){
$hookup->options_page_tab_nav_output();
}

View File

@ -0,0 +1,9 @@
<?php
/**
* Template - Tabs navigation row
*
* Print tabs navigation row.
*
* @var WPHelper\AdminPage $this
*/
do_action( "wphelper/adminpage/tab_nav/{$this->tab_group}" );

View File

@ -0,0 +1,9 @@
<?php
/**
* Template for CMB2 page title
*
* @var WPHelper\CMB2_OptionsPage $this
*/
if ( $this->cmb->prop( 'title' ) ) {
echo '<h2>' . wp_kses_post( $this->cmb->prop( 'title' ) ) . '</h2>';
}

View File

@ -0,0 +1,42 @@
<?php
/**
* Template for CMB2 Options Page
*
* @var WPHelper\CMB2_OptionsPage $this
* @var CMB2_Options_Hookup $hookup
*/
?>
<style>
/*
* fix top container alignment issues when using 2 columns
* This assumes first item is a title item
*/
.cmb2-options-page .cmb2-wrap .cmb-type-title:first-of-type {
margin-top: 0;
}
</style>
<div class="wrap cmb2-options-page option-<?php echo esc_attr( sanitize_html_class( $hookup->option_key ) ); ?>">
<?php include 'title-cmb2.php' ?>
<?php include 'tab-nav-cmb2.php' ?>
<div id="poststuff">
<div id="post-body" class="metabox-holder columns-2">
<!-- main content -->
<div id="post-body-content">
<div class="meta-box-sortables ui-sortable tabs-content">
<?php include 'form-cmb2.php' ?>
</div><!-- .meta-box-sortables -->
</div><!-- #post-body-content -->
<!-- sidebar -->
<div id="postbox-container-1" class="postbox-container">
<div class="meta-box-sortables">
<?php do_action("wphelper/adminpage/plugin_info_box/{$this->admin_page->get_slug()}"); ?>
</div><!-- .meta-box-sortables -->
</div><!-- #postbox-container-1 .postbox-container -->
</div><!-- #post-body -->
<div class="clear"></div>
</div><!-- #poststuff -->
</div><!-- .wrap -->

View File

@ -0,0 +1,13 @@
<?php
/**
* Template for CMB2 Options Page
*
* @var WPHelper\CMB2_OptionsPage $this
* @var CMB2_Options_Hookup $hookup
*/
?>
<div class="wrap cmb2-options-page option-<?php echo esc_attr( sanitize_html_class( $hookup->option_key ) ); ?>">
<?php include 'title-cmb2.php' ?>
<?php include 'tab-nav-cmb2.php' ?>
<?php include 'form-cmb2.php' ?>
</div>

View File

@ -0,0 +1,9 @@
<div class="wrap">
<h1><?= get_admin_page_title() ?></h1>
<div class="card">
<h3>CMB2 Plugin Missing</h3>
<p>CMB2 plugin is required to display this page.
<br>Please install and activate <a href="<?php echo admin_url('plugin-install.php?s=cmb2&tab=search') ?>">CMB2 plugin</a>.
</div>
</div>

View File

@ -0,0 +1,24 @@
<?php
/**
* WordPress admin 'wrap' div
*
* @var WPHelper\AdminPage $this
*/
?>
<div class="wrap">
<h1><?= get_admin_page_title() ?></h1>
<div class="card">
<h3>WPHelper\AdminPage</h3>
<p>Please provide a template file or callback function to render this page
<br />Like so:
</p>
<pre><code style="display: block;">new WPHelper\AdminPage(
[
'slug' => '<?=$this->slug?>',
'title' => '<?=$this->title?>',
<strong><em>'render' => 'callback_or_tpl_file',</em></strong>
]
);</code></pre>
</div>
</div>

View File

@ -0,0 +1,29 @@
<?php
/**
* Template for CMB2 Options Page
*
* @var string $ob_content - Render template or callback
* @var WPHelper\AdminPage $this
*/
?>
<div class="wrap">
<h1><?= get_admin_page_title() ?></h1>
<?php include 'tab-nav-simple.php' ?>
<div id="poststuff">
<div id="post-body" class="metabox-holder columns-2">
<!-- main content -->
<div id="post-body-content">
<div class="meta-box-sortables ui-sortable tabs-content">
<?php echo $ob_content ?>
</div><!-- .meta-box-sortables -->
</div><!-- #post-body-content -->
<!-- sidebar -->
<div id="postbox-container-1" class="postbox-container">
<?php include 'sidebar-add-meta-boxes.php'; ?>
</div><!-- #postbox-container-1 .postbox-container -->
</div><!-- #post-body -->
<div class="clear"></div>
</div><!-- #poststuff -->
</div><!-- .wrap -->

View File

@ -0,0 +1,12 @@
<?php
/**
* Simple wrap
*
* @var string $ob_content - Render template or callback
*/
?>
<div class="wrap">
<h1><?= get_admin_page_title() ?></h1>
<?php include 'tab-nav-simple.php' ?>
<?php echo $ob_content; ?>
</div>

View File

@ -0,0 +1,34 @@
<?php
/**
* Plugin Name: WPHelper/Admin-Page
*/
defined( 'ABSPATH' ) || die( 'No soup for you!' );
if ( ! function_exists( 'wph_extra_plugin_headers' ) ):
/**
* WPHelper Extra Plugin Headers
*
* Adds 'Last Update' and 'Release Date' header option to plugins
* Used in plugin info-box
* Can be used by all plugins
*/
function wph_extra_plugin_headers( $headers ){
if ( empty( $headers ) ){
$headers = [];
}
if ( ! in_array( 'Last Update', $headers ) ){
$headers[] = 'Last Update';
}
if ( ! in_array( 'Release Date', $headers ) ){
$headers[] = 'Release Date';
}
return $headers;
}
add_filter( 'extra_plugin_headers', 'wph_extra_plugin_headers' );
add_filter( 'extra_theme_headers', 'wph_extra_plugin_headers' );
endif;

View File

@ -0,0 +1,38 @@
# Create Github Release
# v1.0
# Create Github release on tag push
# - Use tag name as release title
# - Use CHANGELOG.md log entry as body
on:
push:
# Sequence of patterns matched against refs/tags
tags:
- '*' # Match any tag
name: Create Release
jobs:
build:
name: Create Release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Get Changelog Entry
id: changelog_reader
uses: mindsers/changelog-reader-action@v1.1.0
with:
version: ${{ github.ref }}
path: ./CHANGELOG.md
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
body: ${{ steps.changelog_reader.outputs.log_entry }} # This pulls from the GET CHANGELOG ENTRY step above, referencing it's ID to get its outputs object, which include a `log_entry`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
draft: false
prerelease: false

26
vendor/abuyoyo/metabox/CHANGELOG.md vendored Normal file
View File

@ -0,0 +1,26 @@
# Changelog
WPHelper\MetaBox
## 0.8
### Fixed
- Fix `Metabox::render()` callback arguments. Callback is passed `$data_object` and `$box`.
## 0.7
### Fixed
- Validate `is_callable(render_cb)` before `call_user_func` call.
## 0.6
### Added
- Accept callable `render_cb` as well as readable `render_tpl` as render template.
## 0.5
### Added
- Basic Metabox API.

220
vendor/abuyoyo/metabox/MetaBox.php vendored Normal file
View File

@ -0,0 +1,220 @@
<?php
namespace WPHelper;
use function wp_parse_args;
use function add_action;
use function add_meta_box;
/**
* MetaBox
*
* Object-Oriented WordPress meta box creator.
*
* @author abuyoyo
* @version 0.8
*/
class MetaBox
{
/**
* Screen context where the meta box should display.
*
* @var string
*/
private $context;
/**
* The ID of the meta box.
*
* @var string
*/
private $id;
/**
* The display priority of the meta box.
*
* @var string
*/
private $priority;
/**
* Screens where this meta box will appear.
*
* @var string[]
*/
private $screens;
/**
* Path to the template used to display the content of the meta box.
*
* @var string filename
*/
private $render_tpl;
/**
* Path to the template used to display the content of the meta box.
*
* @var callable
*/
private $render_cb;
/**
* The title of the meta box.
*
* @var string
*/
private $title;
/**
* Hook where this meta box will be added.
*
* @var string
*/
private $hook;
/**
* Array of $args to be sent to callback function's second parameter
*
* @var array
*/
private $args;
/**
* Constructor.
*
* @param string $id
* @param string $template
* @param string $title
* @param string $context
* @param string $priority
* @param string[] $screens
*/
public function __construct($options)
{
// should throw error if required fields (id, title) not given
// template is actually optional
$defaults = [
'context' => 'advanced',
'priority' => 'default',
'screens' => [],
'args' => null,
'hook' => 'add_meta_boxes',
];
$options = wp_parse_args( $options, $defaults );
extract($options);
$this->context = $context;
$this->id = $id;
$this->priority = $priority;
$this->screens = $screens;
$this->render_tpl = isset( $template ) ? rtrim( $template, '/' ) : '';
$this->render_cb = $render ?? '';
$this->title = $title;
$this->hook = $hook;
$this->args = $args;
}
/**
* Add metabox at given hook.
*
* @return void
*/
public function add()
{
add_action( $this->hook, [ $this, 'wp_add_metabox' ] );
}
public function wp_add_metabox(){
add_meta_box(
$this->id,
$this->title,
[ $this, 'render' ], // $this->render_tpl | $this->render_cb
$this->screens,
$this->context,
$this->priority,
$this->args
);
}
/**
* Get the callable that will render the content of the meta box.
*
* @return callable
*/
public function get_callback()
{
return [ $this, 'render' ];
}
/**
* Get the screen context where the meta box should display.
*
* @return string
*/
public function get_context()
{
return $this->context;
}
/**
* Get the ID of the meta box.
*
* @return string
*/
public function get_id()
{
return $this->id;
}
/**
* Get the display priority of the meta box.
*
* @return string
*/
public function get_priority()
{
return $this->priority;
}
/**
* Get the screen(s) where the meta box will appear.
*
* @return array|string|WP_Screen
*/
public function get_screens()
{
return $this->screens;
}
/**
* Get the title of the meta box.
*
* @return string
*/
public function get_title()
{
return $this->title;
}
/**
* Render the content of the meta box using a PHP template.
* Callback passed to to add_meta_box()
*
* @see do_meta_boxes()
*
* @param mixed $data_object Object that's the focus of the current screen. eg. WP_Post|WP_Comment
* @param array $box Meta-box data [id, title, callback, args] (@see global $wp_meta_boxes)
*/
public function render( $data_object, $box )
{
if ( ! is_readable( $this->render_tpl ) && ! is_callable( $this->render_cb ) ){
return;
}
if ( is_callable( $this->render_cb ) ){
call_user_func( $this->render_cb, $data_object, $box );
} else if ( isset( $this->render_tpl ) ){
include $this->render_tpl;
}
}
}

1
vendor/abuyoyo/metabox/README.md vendored Normal file
View File

@ -0,0 +1 @@
# WPHelper\MetaBox

12
vendor/abuyoyo/metabox/composer.json vendored Normal file
View File

@ -0,0 +1,12 @@
{
"name": "abuyoyo/metabox",
"description": "WordPress metabox helper class",
"type": "library",
"version": "0.8",
"license": "BSD-3-Clause",
"autoload": {
"psr-4": {
"WPHelper\\" : ""
}
}
}

150
vendor/abuyoyo/plugincore/CHANGELOG.md vendored Normal file
View File

@ -0,0 +1,150 @@
# WPHelper\PluginCore Changelog
## 0.27
Release Date: Oct 3, 2023
### Added
- Support Plugin Update Checker v5 as well as v4. Create class alias `WPHelper\PucFactory` for `YahnisElsts\PluginUpdateChecker\v5\PucFactory` if available. Fallback to `Puc_v4_Factory`.
## 0.26
Release Date: Jun 9, 2023
### Fixed
- Fix passing PluginCore instance to AdminPage constructor in `admin_page()` method.
## 0.25
Release Date: Feb 3, 2023
### Added
- Property `token` and method `token`. Lowercase underscore token to be used as variable name.
## 0.24
Release Date: Jan 13, 2023
### Added
- Static `get_by_file` method. Get PluginCore instance by plugin filename.
## 0.23
Release Date: Aug 8, 2022
### Fixed
- Minor fixes.
## 0.22
Release Date: Aug 8, 2022
### Removed
- Remove all dependencies. No require.
## 0.21
Release Date: Aug 5, 2022
### Added
- Add `action_links` option. Accepts standard `plugin_action_links_` callback filter function. Alternatively accepts array of links. links can be HTML tag strings (`'<a href="/link">Link</a>'`) or arrays with keys `href` and `text`. Special use case `'href' => 'menu_page'` available for quick Settings link generation.
### Changed
- Plugin updater - prefer plugin header `Update URI` for plugin update checker, if no URI provided in options.
- Validate class `WPHelper\AdminPage` exists - required for `admin_page` option/settings.
- Significant code cleanup, notes, doc blocks and reorganizing of PluginCore class.
## 0.20
Release Date: Jul 29, 2022
### Changed
- Update `composer.json` dependencies - `abuyoyo/adminmenupage ~0.20`.
## 0.19
Release Date: Jul 27, 2022
### Changed
- Update `composer.json` dependencies.
- Require PHP >= 7.4
## 0.18
Release Date: May 22, 2022
### Changed
- Class `PluginCore` is pluggable.
- Prevent direct PHP script execution if not accessed within the WordPress environment.
### Fixed
- Include `plugin.php` if function `get_plugin_data` does not exist. This could case critical failure.
## 0.17
Release Date: Feb 7, 2021
### Added
- Pass instance of `PluginCore` to `AdminPage` if current version supports it (used in Plugin Info Metabox generation).
## 0.16
### Fixed
- Upgrade callback `upgrade_cb` will execute when only single plugin is updated.
## 0.15
### Changed
- Use `new WPHelper\AdminPage()` (WPHelper\AdminMenuPage >= 0.12) instead of deprecated `AdminMenuPage`.
- Do not hook `Puc_v4_Factory::buildUpdateChecker` on `admin_init`. Run in plugin's global scope.
## 0.14
### Added
- Add `admin_page` option to create a WPHelper\AdminMenuPage instance.
- Add `plugin_data` variable with WordPress core `get_plugin_data()` object. Use header data if no slug or title provided.
### Fixed
- Fix PHP defines when `const` not provided.
## 0.13.3
- Fix `upgrade_cb` function handling.
## 0.13.2
- Fix `upgrade_cb_wrapper` function.
## 0.13.1
- Update `composer.json` version.
## 0.13
- Fix `upgrade_cb_wrapper` function.
## 0.12
- Add upgrade_cb wrapper function that conducts sanity-checks before calling `upgrade_cb` callback provided.
- Add `plugin_basename()` getter/setter function and `plugin_basename` variable.
- Add changelog.
## 0.11
- Add `upgrade_cb` option - callable function to run on WordPress `upgrader_process_complete` hook.
## 0.10
- Fix undefined index PHP notices introduced in version 0.9
## 0.9
- Add automatic plugin update checker using `yahnis-elsts/plugin-update-checker` library.
## 0.8
- Fix wrong `plugin_basename` constant.
## 0.7
- Don't use `extract` in constructor
- Add sanity checks and normalize getter/setter functions
- Add `file()` getter function.
## 0.6
- Add `path()`, `url()` getter/setter functions.
- Add `name()` getter function.
## 0.5
- Initial release.
- Defines `PLUGINNAME_URL`, `_PATH`, `_DIR`, `_BASENAME`, `_FILE` constants for plugin.
- Registers plugin activation, deactivation and uninstall hook if callbacks provided.
- Static function `PluginCore::get($slug)` will return instance of PluginCore registered with `$slug`. Thus PluginCore can be initiated without polluting global scope.

29
vendor/abuyoyo/plugincore/LICENSE vendored Normal file
View File

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2019, abuyoyo
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

698
vendor/abuyoyo/plugincore/PluginCore.php vendored Normal file
View File

@ -0,0 +1,698 @@
<?php
namespace WPHelper;
defined( 'ABSPATH' ) || die( 'No soup for you!' );
if ( ! class_exists( 'WPHelper/PluginCore' ) ):
// require dependency get_plugin_data()
if( ! function_exists( 'get_plugin_data' ) ) {
require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
}
/**
* PluginCore
*
* Helper Class for creating WordPress Plugins
*
* (@see README.md)
*
* @version 0.27
*/
class PluginCore {
/**
* @var string Plugin filename
*/
private $plugin_file;
/**
* @var string
*/
private $title;
/**
* @var string
*/
private $slug;
/**
* @var string
*/
private $const;
/**
* @var string
*/
private $token;
/**
* @var string
*/
private $path;
/**
* @var string
*/
private $url;
/**
* @var string
*/
private $plugin_basename;
/**
* @var array plugin header metadata
*/
private $plugin_data;
/**
* @var callable
*/
public $activate_cb;
/**
* @var callable
*/
public $deactivate_cb;
/**
* @var callable
*/
public $uninstall_cb;
/**
* @var callable
*/
public $upgrade_cb;
/**
* @var array|callable
*/
public $action_links;
/**
* @var AdminPage
*/
public $admin_page;
/**
* @var \Puc_v4p10_Plugin_UpdateChecker
*/
private $update_checker;
/**
* @var string Repo uri
*/
private $update_repo_uri;
/**
* @var string Repo authentication key
*/
private $update_auth;
/**
* @var string Repo branch
*/
private $update_branch;
/**
* Static array of all PluginCore instances
* Used in PluginCore::get($slug)
*
* @var array[PluginCore] Instances of PluginCore
*/
private static $cores = [];
/**
* Retrieve instance of PluginCore by plugin slug.
*
* @since 0.5
*
* @param string $slug - Plugin slug
* @return PluginCore - Instance of specific plugin.
*/
static public function get( $slug ) {
return self::$cores[ $slug ] ?? null;
}
/**
* Retrieve instance of PluginCore by plugin __FILE__.
*
* @since 0.24
*
* @param string $filename - Plugin filename
* @return PluginCore - Instance of specific plugin.
*/
static public function get_by_file( $filename ) {
return current(
array_filter(
self::$cores,
fn($core) => $core->file() == $filename
)
) ?: null;
}
/**
* Constructor
*
* @since 0.1
* @since 0.2 Accept filename as first param and options array as optional param
*/
function __construct( $plugin_file, $options = null ) {
$this->plugin_file( $plugin_file );
if ( is_array( $options ) && ! empty( $options ) ) {
$options = (object) $options;
$this->title( $options->title ?? null ); // fallback: get title from header plugin_data
$this->slug( $options->slug ?? null ); // fallback: guess slug from plugin basename
$this->const( $options->const ?? null ); // fallback: generate const from slug
$this->token( $options->token ?? null ); // fallback: generate token from slug
if ( isset( $options->activate_cb ) )
$this->activate_cb( $options->activate_cb );
if ( isset( $options->deactivate_cb ) )
$this->deactivate_cb( $options->deactivate_cb );
if ( isset( $options->uninstall_cb ) )
$this->uninstall_cb( $options->uninstall_cb );
if ( isset( $options->upgrade_cb ) )
$this->upgrade_cb( $options->upgrade_cb );
if ( isset( $options->action_links ) )
$this->action_links( $options->action_links );
if ( isset( $options->admin_page ) )
$this->admin_page( $options->admin_page ); // creates AdminPage instance
if ( isset( $options->update_checker ) )
$this->update_checker( $options->update_checker );
}
$this->bootstrap();
}
/**
* Bootstrap
*
* Setup url, path, plugin_basename variables
* Add PluginCore instance to static $cores
* Define plugin constants (_PATH, _URL, _BASENAME, _FILE etc.)
* Register activation, deactivation, uninstall, upgrade hooks.
* Init PUC update checker.
*
* @since 0.1 setup()
* @since 0.21 bootstrap()
*
* @todo set plugin_dir_path, plugin_basename as accessible public variables (available thru methods atm)
*/
private function bootstrap() {
// validate basic variables (in case no options array were given)
$this->title();
$this->slug();
$this->const();
// set variables
$this->path();
$this->url();
$this->plugin_basename();
/**
* Add this PluginCore instance to static list of PluginCore instances (key = slug).
* @see static function get()
*/
self::$cores[ $this->slug ] = $this;
// define constants
define( $this->const . '_PATH', $this->path );
define( $this->const . '_DIR', $this->path );
define( $this->const . '_URL', $this->url );
define( $this->const . '_BASENAME', $this->plugin_basename );
define( $this->const . '_PLUGIN_FILE', $this->plugin_file );
define( $this->const . '_FILE', $this->plugin_file );
$this->register_hooks();
$this->add_plugin_action_links();
if ( $this->update_checker === true ) {
$this->build_update_checker();
}
}
/**
* Register activation/deactivation/uninstall/upgrade hooks
*
* @since 0.5
*/
private function register_hooks() {
if ( ! empty( $this->activate_cb ) ) // && is_callable() ?
register_activation_hook( $this->plugin_file, $this->activate_cb );
if ( ! empty( $this->deactivate_cb ) )
register_deactivation_hook( $this->plugin_file, $this->deactivate_cb );
if ( ! empty( $this->uninstall_cb ) )
register_uninstall_hook( $this->plugin_file, $this->uninstall_cb );
if ( ! empty( $this->upgrade_cb ) )
add_action( 'upgrader_process_complete', [ $this, 'upgrade_cb_wrapper' ], 10, 2 );
}
/**
* Getter/Setter - title
* Plugin title.
* If none provided - plugin header Title will be used.
*
* @since 0.1
*
* @param string|null $title
* @return string $this->title
*/
public function title( $title = null ) {
return $this->title ??= esc_html( $title ) ?: $this->plugin_data()['Title'];
}
/**
* Wrapper function for $this->title()
*
* @since 0.6
*
* @deprecated
*/
public function name( $title = null ) {
_doing_it_wrong( __METHOD__, 'Use PluginCore::title instead.', '0.21' );
return $this->title( $title );
}
/**
* Getter/Setter - slug
* Plugin slug.
* If none provided - plugin file basename will be used
*
* @since 0.1
*
* @param string|null $slug
* @return string $this->slug
*/
public function slug( $slug = null ) {
return $this->slug ??= $slug ?: basename( $this->plugin_file, '.php' );
}
/**
* Setter - plugin_file (also Getter - kinda)
* Plugin file fully qualified path.
*
* @since 0.1
*
* @param string $plugin_file - Path to plugin file
* @return string $this->plugin_file
*/
public function plugin_file( $plugin_file ) {
return $this->plugin_file ??= $plugin_file;
}
/**
* GETTER function. NOT a wrapper
* Might have to rethink this
* used by test-plugin update_checker
*
* @since 0.7
*
* @todo revisit this
*/
public function file() {
return $this->plugin_file;
}
/**
* Getter/Setter - plugin data array
*
* @since 0.14
*/
public function plugin_data() {
return $this->plugin_data ??= get_plugin_data( $this->plugin_file, false ); // false = no markup (i think)
}
/**
* Getter/Setter - const
* Prefix of plugin specific defines (PLUGIN_NAME_PATH etc.)
* If not provided - plugin slug will be uppercase.
*
* @since 0.4
*
* @param string|null $const (string should be uppercase)
* @return string $this->const
*/
public function const( $const = null ) {
return $this->const ??= $const ?: str_replace( '-', '_' , strtoupper( $this->slug() ) );
}
/**
* Getter/Setter - token
* Create a single-token slug (convert to underscore + lowercase).
*
* @since 0.25
*
* @param string|null $token (string will be normalized)
* @return string $this->token
*/
public function token( $token = null ) {
return $this->token ??= str_replace( '-', '_' , strtolower( $token ?: $this->slug() ) );
}
/**
* Getter/setter
*
* @since 0.6
*/
public function path() {
return $this->path ??= plugin_dir_path( $this->plugin_file );
}
/**
* Getter/Setter
*
* @since 0.6
*/
public function url() {
return $this->url ??= plugin_dir_url( $this->plugin_file );
}
/**
* Getter/Setter
*
* @since 0.12
*/
public function plugin_basename() {
return $this->plugin_basename ??= plugin_basename( $this->plugin_file );
}
/**
* Setter - Activation callback
* Callback runs on 'register_activation_hook'
* PluginCore does not validate. Authors must ensure valid callback.
*
* @since 0.4
*
* @param callable $activate_cb - Activation callback
*
* @access private
*/
private function activate_cb( $activate_cb ) {
$this->activate_cb = $activate_cb;
}
/**
* Setter - Deactivation callback
* Callback runs on 'register_deactivation_hook'
* PluginCore does not validate. Authors must ensure valid callback.
*
* @since 0.4
*
* @param callable $deactivate_cb - Deactivation callback.
*
* @access private
*/
private function deactivate_cb( $deactivate_cb ) {
$this->deactivate_cb = $deactivate_cb;
}
/**
* Setter - Uninstall callback
* Callback runs on 'register_uninstall_hook'
* PluginCore does not validate. Authors must ensure valid callback.
*
* @since 0.4
*
* @param callable $uninstall_cb - Uninstall callback.
*
* @access private
*/
private function uninstall_cb( $uninstall_cb ) {
$this->uninstall_cb = $uninstall_cb;
}
/**
* Setter - Upgrade callback
* Callback runs on 'upgrader_process_complete' hook - only for our plugin.
* Runs inside wrapper function that ensures our plugin was updated.
* (@see upgrade_cb_wrapper() below)
*
* PluginCore does not validate. Authors must ensure valid callback.
*
* @since 0.11
*
* @param callable $upgrade_cb - Upgrade callback.
*
* @access private
*/
private function upgrade_cb( $upgrade_cb ) {
$this->upgrade_cb = $upgrade_cb;
}
/**
* Setter - Plugin action links
*
* Add links to plugin action links on Plugins page.
* Accepts callable hooked to 'plugin_action_links_{$plugin}'
* Alternatively accepts array of key => string/HTML tag (eg. [ 'settings' => '<a href="foo" />' ] )
* Alternatively accepts array of key => [ 'text' => 'My Link', 'href' => 'foo' ]
* Special case: Settings Page
* [ 'settings' => [ 'href' => 'menu_page', 'text' => 'Settings' ] ] will generate link to plugin menu page url (@see menu_page_url() )
* (@see add_plugin_action_links() below)
*
* @since 0.21
*
* @param callable|array $action_links - filter function or custom action links array
*
* @todo perhaps have separate action_links_array + action_links_cb variables
*/
private function action_links( $action_links ) {
$this->action_links = $action_links;
}
/**
* Getter/Setter - AdminPage
*
* Construct AdminPage instance for plugin.
*
* @since 0.14
* @since 0.17 - Pass instance of PluginCore to AdminPage (~0.14)
*
* @param array $admin_page - AdminPage settings array
* @return AdminPage
*/
public function admin_page( $admin_page ) {
if ( ! class_exists( AdminPage::class ) )
return;
if ( ! isset( $this->admin_page ) ){
// validate
$admin_page['slug'] ??= $this->slug();
$admin_page['title'] ??= $this->title();
$admin_page['plugin_core'] ??= $this;
$this->admin_page = new AdminPage( $admin_page );
}
return $this->admin_page;
}
/**
* Setter
*
* Setup info used by PucFactory
*
* set $update_checker (bool)
* set $update_repo_uri (string)
* set $update_auth (optional)
* set $update_branch (optional)
*
* @since 0.9
*
* @param bool|string|array $update_checker
*/
private function update_checker( $update_checker ) {
if ( empty( $update_checker ) ) {
$this->update_checker = false;
}
if ( is_bool( $update_checker ) ) {
$this->update_checker = $update_checker;
}
// option 'update_checker' accepts string - repo uri
if ( is_string( $update_checker ) ) {
$this->update_checker = true;
$this->update_repo_uri = $update_checker;
}
// option 'update_checker' accepts array: ['uri'=> , 'auth'=>, 'branch'=> ]
if ( is_array( $update_checker ) ) {
$this->update_checker = true;
if ( isset( $update_checker['uri'] ) ) {
$this->update_repo_uri = $update_checker['uri'];
}
if ( isset( $update_checker['auth'] ) ) {
$this->update_auth = $update_checker['auth'];
}
if ( isset( $update_checker['branch'] ) ) {
$this->update_branch = $update_checker['branch'];
}
}
// Use plugin header 'UpdateURI' or fallback to 'PluginURI'
// call plugin_data() to init var plugin_data
$this->update_repo_uri ??= $this->plugin_data()['UpdateURI'] ?: $this->plugin_data['PluginURI'] ?: null;
// validate
// If no repo uri - update checker is disabled.
if ( empty( $this->update_repo_uri ) ) {
$this->update_checker = false;
}
}
/**
* Init Puc update checker instance
*
* @since 0.9 init_update_checker()
* @since 0.21 build_update_checker()
* @since 0.27 Create class alias WPHelper\PucFactory - support plugin-update-checker v4 & v5
*
* @uses PucFactory::buildUpdateChecker
*/
private function build_update_checker() {
/**
* Create class alias WPHelper\PucFactory
* Support YahnisElsts\PluginUpdateChecker v4 | v5
*
* @since 0.27
*/
if ( ! class_exists( 'WPHelper\PucFactory' ) ) {
if ( class_exists( 'YahnisElsts\PluginUpdateChecker\v5\PucFactory' ) ) {
$actual_puc = 'YahnisElsts\PluginUpdateChecker\v5\PucFactory';
} else if ( class_exists( 'Puc_v4_Factory' ) ) {
$actual_puc = 'Puc_v4_Factory';
}
if ( ! empty( $actual_puc ) ) {
class_alias( $actual_puc, 'WPHelper\PucFactory' );
}
}
if ( ! class_exists( 'WPHelper\PucFactory' ) )
return;
$update_checker = PucFactory::buildUpdateChecker(
$this->update_repo_uri,
$this->plugin_file,
$this->slug() // using slug()
);
//Optional: If you're using a private repository, specify the access token like this:
if ( isset( $this->update_auth ) )
$update_checker->setAuthentication( $this->update_auth );
//Optional: Set the branch that contains the stable release.
if ( isset( $this->update_branch ) )
$update_checker->setBranch( $this->update_branch );
}
/**
* upgrade_cb_wrapper
*
* This function only called if upgrade_cb is set (@see register_hooks())
* This function called on upgrader_process_complete
* sanity-checks if our plugin was upgraded
* if so - calls upgrade_cb provided by our plugin
*
* @since 0.12
*/
public function upgrade_cb_wrapper( $upgrader_object, $options ) {
if(
$options['action'] == 'update' // has upgrade taken place
&&
$options['type'] == 'plugin' // is it a plugin upgrade
&&
(
(
isset( $options['plugins'] ) // is list of plugins upgraded
&&
in_array( $this->plugin_basename(), $options['plugins']) // is our plugin in that list
)
||
( // single plugin updated
isset( $options['plugin'] )
&&
$this->plugin_basename() == $options['plugin']
)
)
) {
call_user_func( $this->upgrade_cb, $upgrader_object, $options );
}
}
/**
* Add plugin_action_links
*
* Parse action_links (callable or array).
* Generate callback if action_links provided as array.
* Add callback to 'plugin_action_links_{$plugin}' hook.
*
* @since 0.21
*
* @access private
*/
private function add_plugin_action_links() {
if ( empty( $this->action_links ) )
return;
if ( is_callable( $this->action_links ) ) { // default - pass a filter method
$action_links_cb = $this->action_links;
} else if ( is_array( $this->action_links ) ) { // array of links - PluginCore will do the heavy lifting
$action_links_cb = function( $links ) {
foreach( $this->action_links as $key => $link ) {
if ( is_string( $link ) ) { // we assume a straight HTML tag string
$links[ $key ] = $link; // just print it
} else if ( is_array( $link ) ) { // accepts ['href'=>'/my-href', 'text'=>'My Action Link']
$links[ $key ] = sprintf(
'<a href="%s">%s</a>',
$link['href'] == 'menu_page' // reserved parameter value
? esc_url( menu_page_url( $this->slug, false ) )
: $link['href'],
$link['text'],
);
}
}
return $links;
};
}
add_filter( 'plugin_action_links_' . $this->plugin_basename(), $action_links_cb );
}
}
endif;

95
vendor/abuyoyo/plugincore/README.md vendored Normal file
View File

@ -0,0 +1,95 @@
# WPHelper \ PluginCore
> Helper class for registering WordPress plugins.
Plugin Boilerplates and boilerplate generator are a hassle. The file structure they impose is way too cumbersome (and redundant) to push into every single plugin. WPHelper\PluginCore replaces boilerplates with one simple class (usually hidden away somewhere in your ``vendor/`` dir).
[WPHelper\AdminMenuPage](https://github.com/abuyoyo/AdminMenuPage) can be used to register and generate admin menus if your plugin requires that.
## Requirements
* PHP >= 7.4
* [Composer](https://getcomposer.org/)
* [WordPress](https://wordpress.org)
## Installation
Install with [Composer](https://getcomposer.org/) or just drop PluginCore.php into your plugin folder and require it.
```PHP
// Require the Composer autoloader anywhere in your code.
require __DIR__ . '/vendor/autoload.php';
```
OR
```PHP
// Require the class file directly from your plugin.
require_once __DIR__ . 'PluginCore.php';
```
WPHelper\PluginCore uses [PSR-4](https://www.php-fig.org/psr/psr-4/) to autoload.
## Basic Usage
WpHelper/PluginCore replaces the many plugin core skeleton generators out there. Just add these lines of code at the top of your plugin file and you're good to go.
WpHelper/PluginCore will define %PLUGIN%_BASENAME, %PLUGIN%_PATH, %PLUGIN%_URL, %PLUGIN%_FILE constants available to your code.
```PHP
/*
* Plugin Name: My Awesome Plugin
* Description: Plugin's description.
* Version: 1.0.0
*/
// Import PluginCore.
use WPHelper\PluginCore;
// Register the plugin
$args = [
'title' => 'My Awesome Plugin', // Optional - will fallback to plugin header Plugin Name.
'slug' => 'my-awesome-plugin', // Optional - will generate slug based on plugin header Plugin Name
'const' => 'MYPLUGIN' // Optional - slug used to define constants: MYPLUGIN_DIR, MYPLUGIN_URL etc. (if not provided will use 'slug' in ALLCAPS)
'activate_cb' => 'activate_callback' // Optional - Provide a callable function to run on activation
'deactivate_cb' => 'deactivate_callback' // Optional - Provide a callable function to run on deactivation
'uninstall_cb' => 'uninstall_callback' // Optional - (@todo) Consider using uninstall.php and not this plugin. This plugin can run in the global scope and cause problems
];
// Setup plugin constants and activation/deactivation hooks
new PluginCore( __FILE__, $args );
// Start writing your code here..
include '/foo.php';
add_action( 'plugins_loaded' function() {
// whatever..
});
```
### Constants
WPHelper\PluginCore defines constants for use in your code. Where ``__FILE__`` is the filename provided to the class and ``%PLUGIN%`` is the ``'const'`` option.
Like so:
```PHP
define( '%PLUGIN%_URL', plugin_dir_url( __FILE__ ) );
define( '%PLUGIN%_FILE', __FILE__ );
```
These are the constants defined by WPHelper\PluginCore. There are some redundancies to account for different conventions.
* %PLUGIN%_PATH: ``plugin_dir_path( __FILE__ ) )``
* %PLUGIN%_DIR: ``plugin_dir_path( __FILE__ ) )``
* %PLUGIN%_URL: ``plugin_dir_url( __FILE__ ) )``
* %PLUGIN%_BASENAME: ``plugin_basename( __FILE__ ) )``
* %PLUGIN%_FILE: ``__FILE__``
* %PLUGIN%_PLUGIN_FILE: ``__FILE__``
### Get Instance
All PluginCore instances can be referenced anywhere in your code using static method `get()` and the plugin slug. Available on `plugins_loaded` hook or later.
```PHP
PluginCore::get('my-awesome-plugin'); // returns PluginCore instance constructed with slug 'my-awesome-plugin'
```

17
vendor/abuyoyo/plugincore/composer.json vendored Normal file
View File

@ -0,0 +1,17 @@
{
"name": "abuyoyo/plugincore",
"description": "WordPress plugin core helper class",
"type": "library",
"version": "0.27",
"time": "2022-10-03",
"license": "BSD-3-Clause",
"suggest": {
"yahnis-elsts/plugin-update-checker": "~5.2",
"abuyoyo/adminmenupage": "~0.27"
},
"autoload": {
"psr-4": {
"WPHelper\\" : ""
}
}
}

View File

@ -0,0 +1,38 @@
# Create Github Release
# v1.0
# Create Github release on tag push
# - Use tag name as release title
# - Use CHANGELOG.md log entry as body
on:
push:
# Sequence of patterns matched against refs/tags
tags:
- '*' # Match any tag
name: Create Release
jobs:
build:
name: Create Release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Get Changelog Entry
id: changelog_reader
uses: mindsers/changelog-reader-action@v1.1.0
with:
version: ${{ github.ref }}
path: ./CHANGELOG.md
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
body: ${{ steps.changelog_reader.outputs.log_entry }} # This pulls from the GET CHANGELOG ENTRY step above, referencing it's ID to get its outputs object, which include a `log_entry`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
draft: false
prerelease: false

View File

@ -0,0 +1,2 @@
demo/*
demo/

View File

@ -0,0 +1,48 @@
# Screen Meta Links
API for adding custom `screen-meta-links` links and panels alongside the 'Screen Options' and 'Help' links on the WordPress admin page.
## [0.13](https://github.com/abuyoyo/screen-meta-links/releases/tag/0.13)
### Fixed
- Fix PHP 8.2 depreacted: Optional parameter declared before required parameter.
## [0.12](https://github.com/abuyoyo/screen-meta-links/releases/tag/0.12)
### Fixed
- Fix PHP notice: Constant already defined.
## [0.11](https://github.com/abuyoyo/screen-meta-links/releases/tag/0.11)
### Removed
- Drop support for `add_screen_meta_link`. This is a backward-incopatible change!
### Added
- New API function `wph_add_screen_meta_panel` replaces `add_screen_meta_link`.
### Changed
- Style is printed in inline style tag.
### Fixed
- Fix conflict with other plugins importing `add_screen_meta_link` such as `broken-link-checker` (issue #1).
## [0.10](https://github.com/abuyoyo/screen-meta-links/releases/tag/0.10)
### Added
- Add `composer.json` file. Library can be imported as composer library.
- Add `CHANGELOG.md` file
- Github action - create release on push tag.
## [0.9](https://github.com/abuyoyo/screen-meta-links/releases/tag/0.9)
- Initial release.
- Modified fork of Janis Elsts' library function `add_screen_meta_link`.
### Added
- Library can be imported as WordPress plugin.
- Ability to insert panels to screen-meta-links.
- Inline (render-blocking) javascript adds meta-links panel and button\link at run-time. Before `screenMeta.init()` script in WordPress's `common.js`. Allowing for full integration.
### Changed
- Add optional `panel` parameter to function `add_screen_meta_link`. This is backward-compatible with Janis Elsts' function of the same name, and any plugins using the original function.
- Rewrite screen-meta-link registration process. Optimized for performance.

View File

@ -0,0 +1,60 @@
# Screen Meta Links API
> Easily add screen-meta-links panels to WordPress admin pages
## Description
API for adding custom screen-meta-links alongside the "Screen Options" and "Help" links on WordPress admin pages.
This library uses render-blocking javascript to get get around WordPress's lack of API for adding tabs to the screen-meta-links.
## Installation
### WordPress Plugin
Screen-Meta-Links API can be installed as a WordPress plugin by dropping this directory into the `plugins` directory and activating from the Plugins page.
### Library
Screen-Meta-Links can also be used as library by using Composer
```bash
composer install abuyoyo\screen-meta-links
```
## Compatibility with original Screen-Meta-Links classes
- $page parameter accepts single string or array of strings. Either file string `index.php` or name `dashboard`. Use `*` to display panel on all pages. Empty string will disable panel on all pages.
- If only `$href` is provided without corresponding `$panel` - a simple link will be added.
- If both `$href` and `$panel` are provided - a button and panel are added.
## Usage
```php
/**
* Add a new link to the screen meta area.
*
* This function can be called on current_screen hook (priority < 100) or earlier (admin_init is fine)
* Plugin begins heavy-lifting (filtering and processing) on current_screen priority 100
*
* @param string $id - Link ID. Should be unique and a valid HTML ID attribute.
* @param string $text - Link text. The text appearing on the tab.
* @param string $href - Optional. Link URL to be used if no panel is provided
* Support for `add_screen_meta_link` original usage.
* @param string|string[] $page - The page(s) where you want to add the link.
* @param array $attributes - Optional. Additional attributes for the link tag.
* Add 'aria-controls' => "{$id}-wrap" to toggle panel
* @param callback $panel - Optional. Callback should echo screen-meta panel HTML content.
*
* @return void
*/
wph_add_screen_meta_panel( $id, $text, $href, $page, $attributes, $panel );
```
### The `$page` Parameter
The `$page` parameter accepts a string or array of strings.
Accepts `page`, `post`, `dashboard` etc.
Or actual file name: `post.php`, `index.php` etc. (`index.php` and `dashboard` will resolve to the same page).
Accepts custom page id's: `toplevel_page_my-plugin` etc.
Accepts wildcard: `*` - This will add the meta-screen-panel to all admin pages.

View File

@ -0,0 +1,8 @@
{
"name": "abuyoyo/screen-meta-links",
"description": "API for adding custom screen-meta-links alongside the 'Screen Options' and 'Help' links.",
"type": "library",
"autoload": {
"files": ["screen-meta-links.php"]
}
}

View File

@ -0,0 +1,16 @@
.custom-screen-meta-link-wrap {
float: right;
height: 28px;
margin: 0 0 0 6px;
border: 1px solid #ddd;
border-top: none;
background: #fff;
box-shadow: 0 1px 1px -1px rgba(0,0,0,.1);
}
.site-health #screen-meta{
margin: 0;
}

View File

@ -0,0 +1,51 @@
(function($, links, panels){
'use strict'
$(document).on('ready', function(){
var container = $('#screen-meta-links');
var container_panels = $('#screen-meta');
var linkTag; //if we have a panel it's a button - otherwise it's a link anchor
debugger;
if (!container.length){
container = $('<div />')
.attr({
'id' : 'screen-meta-links'
});
container.insertAfter('#screen-meta');
}
$.each( links, function( i, element ) {
if (panels[i]){
container_panels.append(
$('<div />')
.attr({
'id' : element.id + '-wrap',
'class' : 'hidden',
'tabindex' : '-1',
'aria-label' : element.text + ' Tab'
})
.html(panels[i])
);
linkTag = '<button />';
}else{
linkTag = '<a />';
}
container.append(
$('<div />')
.attr({
'id' : element.id + '-link-wrap',
'class' : 'hide-if-no-js screen-meta-toggle custom-screen-meta-link-wrap'
})
.append( $( linkTag, element) )
);
});
});
})( jQuery, sml.links, sml.panels );

View File

@ -0,0 +1,373 @@
<?php
/**
* Plugin Name: abuyoyo / Screen Meta Links
* Description: API for adding custom screen-meta-links alongside the "Screen Options" and "Help" links.
* Version: 0.13
* Author: abuyoyo
* Author URI: https://github.com/abuyoyo
* Plugin URI: https://github.com/abuyoyo/screen-meta-links
*/
/**
* TODO:
*
* if panel exists add 'aria-controls' attribute if none supplied
* if no panel exists render anchor tag instead of button
*
*
*/
! defined( 'SML_FILE' ) && define ( 'SML_FILE', __FILE__ );
! defined( 'SML_URL' ) && define ( 'SML_URL' , plugin_dir_url( __FILE__ ) );
! defined( 'SML_PATH' ) && define ( 'SML_PATH', plugin_dir_path( __FILE__ ) );
if ( ! class_exists('Screen_Meta_Links') ):
class Screen_Meta_Links {
protected static $instance = null;
static $registered_requests;
static $links;
static $panels;
static $counter;
static $debug = false;
/**
* Constructor.
*
* @return void
*/
function __construct(){
global $iac_eng_settings;
self::$registered_requests = array();
self::$links = array();
self::$panels = array();
self::$counter = -1;
if( defined( 'DEBUG_SCREEN_META_LINKS' ) ){
self::$debug = true;
}
// inline solution
add_action( 'current_screen' , [ $this, 'setup_current_screen_meta_links' ], 100 );
add_action( 'admin_notices', [ $this, 'append_meta_links' ] ); // print inline sml script
add_action( 'admin_head', [ $this, 'print_inline_style'] );
// external load solution
// add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
// add_action( 'admin_print_styles', [ $this, 'add_link_styles' ] ); //admin_enqueue_styles too early
}
/**
* Get Instance
*
* @return self::$instance
*/
public static function get_instance() {
if (null == self::$instance) {
self::$instance = new self;
}
return self::$instance;
}
/**
* Register a new link (and optional panel) to the screen-meta area.
*
* Do not call this method directly. Instead, use the global add_screen_meta_link() function.
*
* @param string $id - Link ID. Should be unique and a valid value for a HTML ID attribute.
* @param string $text - Link text.
* @param string|string[] $page - The page(s) where you want to add the link.
* @param array $attributes - Optional. Additional attributes for the link tag. Add 'aria-controls' => "{$id}-wrap" to toggle panel
* @param callback $panel - Optional. Callback should print out screen-meta panel contents
*
* @return void
*/
public function register_request($id, $text, $href='', $page='', $attributes = null, $panel=''){
self::$counter++;
self::$registered_requests[self::$counter] = compact( 'id', 'text', 'href', 'page', 'attributes', 'panel');
}
/**
* Setup all requests hooked to this screen
*
* @hook current_screen
*/
public function setup_current_screen_meta_links( $screen ){
foreach(self::$registered_requests as $request_index => $args){
$this->process_request($request_index, $args['id'], $args['text'], $args['href'], $args['page'], $args['attributes'], $args['panel']);
}
}
/**
* process_request
*
* @todo sanitize user input!
* @todo maybe don't use compact()
*/
private function process_request($request_index, $id, $text, $href, $page='', $attributes = null, $panel=''){
if ( ! $this->show_on_this_screen($id, $page) ){
return;
}
if ( is_null($attributes) )
$attributes = array();
if ( !is_array($attributes) )
$attributes = array($attributes);
if ($panel)
$link = compact('id', 'text' );
else
$link = compact('id', 'text', 'href' );
$link = array_merge($link, $attributes);
if ( empty($link['class']) )
$link['class'] = '';
$link['class'] = 'show-settings custom-screen-meta-link ' . $link['class'];
if ($panel)
$link['class'] = 'button ' . $link['class'];
self::$links[$request_index] = $link;
if ($panel){
ob_start();
call_user_func($panel);
$panel = ob_get_clean();
self::$panels[$request_index] = $panel;
}
}
/**
* Test if registered link should be displayed on this screen
*
* @param $id
* @param string|string[] $pages - list of hook_suffix or screen id to show screen-meta-link on
*
* @return boolean
*/
private function show_on_this_screen($id, $pages){
global $hook_suffix;
if ( ! is_array( $pages ) ){
$pages = [ $pages ];
}
$screen = convert_to_screen($hook_suffix);
$add_to_current_page = false;
foreach( $pages as $k => $page ){
if ( ! $page )//ignore empty string. otherwise - will return same as '*' (on screen->id test)
continue;
$page_as_screen = convert_to_screen($page);
if ( $page == $hook_suffix || $page_as_screen->id == $screen->id || $page == '*' ){
$add_to_current_page = true;
break;
}
}
return $add_to_current_page;
}
/**
* DISABLED/UNUSED
*
* Enqueueing does not work.
* Instead we add our script and data inline using append_meta_links().
*
* @see append_meta_links()
*/
public function enqueue_scripts(){
if ( empty(self::$links) ){
return;
}
wp_enqueue_script( 'screen_meta_links', SML_URL . 'js/screen-meta-links.js', 'jquery' );
wp_localize_script( 'screen_meta_links', 'sml', [ 'links' => self::$links, 'panels' => self::$panels ] );
}
/**
* Output the JS that appends the custom meta links to the page.
* Hooked on 'admin_notices' action.
*
* This is very much a render-blocking script
* Runs here before the wp script that inits screen-meta-links runs
* (enqueueing external script w/ localized variables failed to add listener to button)
*
* @access public
* @return void
*/
public function append_meta_links(){
if ( empty(self::$links) ){
return;
}
// ---------------------[meta-screen-links script]-----------------------
?>
<script type="text/javascript">
(function($, links, panels){
var container = $('#screen-meta-links');
var container_panels = $('#screen-meta');
var linkTag; //if we have a panel it's a button - otherwise it's a link anchor
if (!container.length){
container = $('<div />')
.attr({
'id' : 'screen-meta-links'
});
container.insertAfter('#screen-meta');
}
$.each( links, function( i, element ) {
if (panels[i]){
container_panels.append(
$('<div />')
.attr({
'id' : element.id + '-wrap',
'class' : 'hidden',
'tabindex' : '-1',
'aria-label' : element.text + ' Tab'
})
.html(panels[i])
);
linkTag = '<button />';
}else{
linkTag = '<a />';
}
container.append(
$('<div />')
.attr({
'id' : element.id + '-link-wrap',
'class' : 'hide-if-no-js screen-meta-toggle custom-screen-meta-link-wrap'
})
.append( $( linkTag, element) )
);
});
})(
jQuery,
<?php echo json_encode(self::$links); ?>,
<?php echo json_encode(self::$panels); ?> );
</script>
<?php
}
/**
* DISABLED/UNUSED
*
* Output the CSS code for custom screen meta links. Required because WP only
* has styles for specific meta links (by #id), not meta links in general.
*
* Callback for 'admin_print_styles'.
*
* This function is unused because we cannot reliably obtain URL of css file.
* Instead we add inline style using print_inline_style().
*
* @see print_inline_style
*
* @access public
* @return void
*/
function add_link_styles(){
//Don't output the CSS if there are no custom meta links for this page.
if ( empty(self::$links) )
return;
wp_enqueue_style( 'screen-meta-links' , SML_URL . 'css/screen_meta_links.css');
}
/**
* Output inline style tag.
*
* Output the CSS code for custom screen meta links. Required because WP only
* has styles for specific meta links (by #id), not meta links in general.
*
* @hook admin_head
*/
public function print_inline_style(){
//Don't output the CSS if there are no custom meta links for this page.
if ( empty(self::$links) )
return;
ob_start();
include SML_PATH . 'css/screen_meta_links.css';
$css = ob_get_clean();
echo '<style>';
echo $css;
echo '</style>';
}
}
endif;
/**
* DEMO
*
* A separate editable demo file
* For debugging purposes
*
*/
if( defined('DEMO_SCREEN_META_LINKS') ){
include plugin_dir_path( __FILE__ ) . 'demo/demo_screen_meta_links.php' ;
}
if ( ! function_exists( 'wph_add_screen_meta_panel' ) ):
/**
* Add a new link+panel to the screen meta area.
*
* This function can be called on current_screen hook (priority < 100) or earlier (admin_init is fine)
* Plugin begins heavy-lifting (filtering and processing) on current_screen priority 100
*
* @param string $id - Link ID. Should be unique and a valid value for a HTML ID attribute.
* @param string $text - Link text.
* @param string $href - Optional. Link URL to be used if no panel is provided
* @param string|array $page - Optional. The page(s) where you want to add the link. Accepts wildcard '*'. If left empty will not add to any page.
* @param array $attributes - Optional. Additional attributes for the link tag. Add 'aria-controls' => "{$id}-wrap" to toggle panel
* @param callback $panel - Optional. Callback should print out screen-meta panel contents
* @return void
*
* @todo Remove $href parameter and functionailty
* @todo $page should not be optional. We need to remove $href first.
*/
function wph_add_screen_meta_panel( $id, $text, $href = '', $page = '', $attributes = null, $panel = '' ){
static $sml_instance = null;
if ( null === $sml_instance){
$sml_instance = Screen_Meta_Links::get_instance();
}
$sml_instance->register_request($id, $text, $href, $page, $attributes, $panel);
}
endif;

25
vendor/autoload.php vendored Normal file
View File

@ -0,0 +1,25 @@
<?php
// autoload.php @generated by Composer
if (PHP_VERSION_ID < 50600) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, $err);
} elseif (!headers_sent()) {
echo $err;
}
}
trigger_error(
$err,
E_USER_ERROR
);
}
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInita13a895834453aad32a897cc456c73ff::getLoader();

579
vendor/composer/ClassLoader.php vendored Normal file
View File

@ -0,0 +1,579 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
/** @var \Closure(string):void */
private static $includeFile;
/** @var string|null */
private $vendorDir;
// PSR-4
/**
* @var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array<string, list<string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var list<string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* List of PSR-0 prefixes
*
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
*
* @var array<string, array<string, list<string>>>
*/
private $prefixesPsr0 = array();
/**
* @var list<string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var array<string, bool>
*/
private $missingClasses = array();
/** @var string|null */
private $apcuPrefix;
/**
* @var array<string, self>
*/
private static $registeredLoaders = array();
/**
* @param string|null $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
self::initializeIncludeClosure();
}
/**
* @return array<string, list<string>>
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
/**
* @return array<string, list<string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return list<string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return list<string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return array<string, string> Array of classname => path
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array<string, string> $classMap Class to filename map
*
* @return void
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
$paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
$paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
$paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
$paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
$paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
$paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
$includeFile = self::$includeFile;
$includeFile($file);
return true;
}
return null;
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
/**
* Returns the currently registered loaders keyed by their corresponding vendor directories.
*
* @return array<string, self>
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
/**
* @return void
*/
private static function initializeIncludeClosure()
{
if (self::$includeFile !== null) {
return;
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
*/
self::$includeFile = \Closure::bind(static function($file) {
include $file;
}, null, null);
}
}

359
vendor/composer/InstalledVersions.php vendored Normal file
View File

@ -0,0 +1,359 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer;
use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;
/**
* This class is copied in every Composer installed project and available to all
*
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*
* @final
*/
class InstalledVersions
{
/**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
*/
private static $installed;
/**
* @var bool|null
*/
private static $canGetVendors;
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static $installedByVendor = array();
/**
* Returns a list of all package names which are present, either by being installed, replaced or provided
*
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackages()
{
$packages = array();
foreach (self::getInstalled() as $installed) {
$packages[] = array_keys($installed['versions']);
}
if (1 === \count($packages)) {
return $packages[0];
}
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
}
/**
* Returns a list of all package names with a specific type e.g. 'library'
*
* @param string $type
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackagesByType($type)
{
$packagesByType = array();
foreach (self::getInstalled() as $installed) {
foreach ($installed['versions'] as $name => $package) {
if (isset($package['type']) && $package['type'] === $type) {
$packagesByType[] = $name;
}
}
}
return $packagesByType;
}
/**
* Checks whether the given package is installed
*
* This also returns true if the package name is provided or replaced by another package
*
* @param string $packageName
* @param bool $includeDevRequirements
* @return bool
*/
public static function isInstalled($packageName, $includeDevRequirements = true)
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
}
}
return false;
}
/**
* Checks whether the given package satisfies a version constraint
*
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
*
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
*
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
* @param string $packageName
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
* @return bool
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints((string) $constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
/**
* Returns a version constraint representing all the range(s) which are installed for a given package
*
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
* whether a given version of a package is installed, and not just whether it exists
*
* @param string $packageName
* @return string Version constraint usable with composer/semver
*/
public static function getVersionRanges($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
$ranges = array();
if (isset($installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['version'])) {
return null;
}
return $installed['versions'][$packageName]['version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getPrettyVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return $installed['versions'][$packageName]['pretty_version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
*/
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['reference'])) {
return null;
}
return $installed['versions'][$packageName]['reference'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
*/
public static function getInstallPath($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @return array
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
*/
public static function getRootPackage()
{
$installed = self::getInstalled();
return $installed[0]['root'];
}
/**
* Returns the raw installed.php data for custom implementations
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
*/
public static function getRawData()
{
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = include __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
return self::$installed;
}
/**
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
public static function getAllRawData()
{
return self::getInstalled();
}
/**
* Lets you reload the static array from another file
*
* This is only useful for complex integrations in which a project needs to use
* this class but then also needs to execute another project's autoloader in process,
* and wants to ensure both projects have access to their version of installed.php.
*
* A typical case would be PHPUnit, where it would need to make sure it reads all
* the data it needs from this class, then call reload() with
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
* the project in which it runs can then also use this class safely, without
* interference between PHPUnit's dependencies and the project's dependencies.
*
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
*/
public static function reload($data)
{
self::$installed = $data;
self::$installedByVendor = array();
}
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static function getInstalled()
{
if (null === self::$canGetVendors) {
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
}
$installed = array();
if (self::$canGetVendors) {
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require $vendorDir.'/composer/installed.php';
$installed[] = self::$installedByVendor[$vendorDir] = $required;
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
}
}
}
}
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require __DIR__ . '/installed.php';
self::$installed = $required;
} else {
self::$installed = array();
}
}
if (self::$installed !== array()) {
$installed[] = self::$installed;
}
return $installed;
}
}

21
vendor/composer/LICENSE vendored Normal file
View File

@ -0,0 +1,21 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

10
vendor/composer/autoload_classmap.php vendored Normal file
View File

@ -0,0 +1,10 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
);

11
vendor/composer/autoload_files.php vendored Normal file
View File

@ -0,0 +1,11 @@
<?php
// autoload_files.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'8653524d908cf23a56335c6d210d6627' => $vendorDir . '/abuyoyo/adminmenupage/wph_admin_page.php',
'd8f9115e9a479bb7bf2fea8e74fb82f0' => $vendorDir . '/abuyoyo/screen-meta-links/screen-meta-links.php',
);

View File

@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
);

10
vendor/composer/autoload_psr4.php vendored Normal file
View File

@ -0,0 +1,10 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'WPHelper\\' => array($vendorDir . '/abuyoyo/adminmenupage/src', $vendorDir . '/abuyoyo/metabox', $vendorDir . '/abuyoyo/plugincore'),
);

48
vendor/composer/autoload_real.php vendored Normal file
View File

@ -0,0 +1,48 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInita13a895834453aad32a897cc456c73ff
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInita13a895834453aad32a897cc456c73ff', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInita13a895834453aad32a897cc456c73ff', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInita13a895834453aad32a897cc456c73ff::getInitializer($loader));
$loader->register(true);
$filesToLoad = \Composer\Autoload\ComposerStaticInita13a895834453aad32a897cc456c73ff::$files;
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}, null, null);
foreach ($filesToLoad as $fileIdentifier => $file) {
$requireFile($fileIdentifier, $file);
}
return $loader;
}
}

43
vendor/composer/autoload_static.php vendored Normal file
View File

@ -0,0 +1,43 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInita13a895834453aad32a897cc456c73ff
{
public static $files = array (
'8653524d908cf23a56335c6d210d6627' => __DIR__ . '/..' . '/abuyoyo/adminmenupage/wph_admin_page.php',
'd8f9115e9a479bb7bf2fea8e74fb82f0' => __DIR__ . '/..' . '/abuyoyo/screen-meta-links/screen-meta-links.php',
);
public static $prefixLengthsPsr4 = array (
'W' =>
array (
'WPHelper\\' => 9,
),
);
public static $prefixDirsPsr4 = array (
'WPHelper\\' =>
array (
0 => __DIR__ . '/..' . '/abuyoyo/adminmenupage/src',
1 => __DIR__ . '/..' . '/abuyoyo/metabox',
2 => __DIR__ . '/..' . '/abuyoyo/plugincore',
),
);
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInita13a895834453aad32a897cc456c73ff::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInita13a895834453aad32a897cc456c73ff::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInita13a895834453aad32a897cc456c73ff::$classMap;
}, null, ClassLoader::class);
}
}

152
vendor/composer/installed.json vendored Normal file
View File

@ -0,0 +1,152 @@
{
"packages": [
{
"name": "abuyoyo/adminmenupage",
"version": "0.29",
"version_normalized": "0.29.0.0",
"source": {
"type": "git",
"url": "https://github.com/abuyoyo/AdminMenuPage.git",
"reference": "4cd47d5217ca25ed17af76f5fdbab3cab3b37ef8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/abuyoyo/AdminMenuPage/zipball/4cd47d5217ca25ed17af76f5fdbab3cab3b37ef8",
"reference": "4cd47d5217ca25ed17af76f5fdbab3cab3b37ef8",
"shasum": ""
},
"require": {
"abuyoyo/metabox": "~0.8"
},
"suggest": {
"abuyoyo/plugincore": "~0.26",
"cmb2/cmb2": "~2.9"
},
"time": "2023-10-05T00:00:00+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"files": [
"wph_admin_page.php"
],
"psr-4": {
"WPHelper\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"description": "WordPress admin menu page helper class",
"support": {
"issues": "https://github.com/abuyoyo/AdminMenuPage/issues",
"source": "https://github.com/abuyoyo/AdminMenuPage/tree/0.29"
},
"install-path": "../abuyoyo/adminmenupage"
},
{
"name": "abuyoyo/metabox",
"version": "0.8",
"version_normalized": "0.8.0.0",
"source": {
"type": "git",
"url": "https://github.com/abuyoyo/MetaBox.git",
"reference": "98cb4c30db4c366c0d273985eb9c31ffa1cd78f9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/abuyoyo/MetaBox/zipball/98cb4c30db4c366c0d273985eb9c31ffa1cd78f9",
"reference": "98cb4c30db4c366c0d273985eb9c31ffa1cd78f9",
"shasum": ""
},
"time": "2023-07-18T19:14:03+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"WPHelper\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"description": "WordPress metabox helper class",
"support": {
"issues": "https://github.com/abuyoyo/MetaBox/issues",
"source": "https://github.com/abuyoyo/MetaBox/tree/0.8"
},
"install-path": "../abuyoyo/metabox"
},
{
"name": "abuyoyo/plugincore",
"version": "0.27",
"version_normalized": "0.27.0.0",
"source": {
"type": "git",
"url": "https://github.com/abuyoyo/PluginCore.git",
"reference": "d730a674cbe2dc92e60ace8e25a2e0e3fdeee3e6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/abuyoyo/PluginCore/zipball/d730a674cbe2dc92e60ace8e25a2e0e3fdeee3e6",
"reference": "d730a674cbe2dc92e60ace8e25a2e0e3fdeee3e6",
"shasum": ""
},
"suggest": {
"abuyoyo/adminmenupage": "~0.27",
"yahnis-elsts/plugin-update-checker": "~5.2"
},
"time": "2022-10-03T00:00:00+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"WPHelper\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"description": "WordPress plugin core helper class",
"support": {
"issues": "https://github.com/abuyoyo/PluginCore/issues",
"source": "https://github.com/abuyoyo/PluginCore/tree/0.27"
},
"install-path": "../abuyoyo/plugincore"
},
{
"name": "abuyoyo/screen-meta-links",
"version": "0.13",
"version_normalized": "0.13.0.0",
"source": {
"type": "git",
"url": "https://github.com/abuyoyo/screen-meta-links.git",
"reference": "b324cef9eb5825d04ffa17f771237b7deca5cd01"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/abuyoyo/screen-meta-links/zipball/b324cef9eb5825d04ffa17f771237b7deca5cd01",
"reference": "b324cef9eb5825d04ffa17f771237b7deca5cd01",
"shasum": ""
},
"time": "2023-08-08T22:37:03+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"files": [
"screen-meta-links.php"
]
},
"description": "API for adding custom screen-meta-links alongside the 'Screen Options' and 'Help' links.",
"support": {
"source": "https://github.com/abuyoyo/screen-meta-links/tree/0.13",
"issues": "https://github.com/abuyoyo/screen-meta-links/issues"
},
"install-path": "../abuyoyo/screen-meta-links"
}
],
"dev": true,
"dev-package-names": []
}

59
vendor/composer/installed.php vendored Normal file
View File

@ -0,0 +1,59 @@
<?php return array(
'root' => array(
'name' => 'abuyoyo/notice-manager',
'pretty_version' => '0.25',
'version' => '0.25.0.0',
'reference' => NULL,
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev' => true,
),
'versions' => array(
'abuyoyo/adminmenupage' => array(
'pretty_version' => '0.29',
'version' => '0.29.0.0',
'reference' => '4cd47d5217ca25ed17af76f5fdbab3cab3b37ef8',
'type' => 'library',
'install_path' => __DIR__ . '/../abuyoyo/adminmenupage',
'aliases' => array(),
'dev_requirement' => false,
),
'abuyoyo/metabox' => array(
'pretty_version' => '0.8',
'version' => '0.8.0.0',
'reference' => '98cb4c30db4c366c0d273985eb9c31ffa1cd78f9',
'type' => 'library',
'install_path' => __DIR__ . '/../abuyoyo/metabox',
'aliases' => array(),
'dev_requirement' => false,
),
'abuyoyo/notice-manager' => array(
'pretty_version' => '0.25',
'version' => '0.25.0.0',
'reference' => NULL,
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev_requirement' => false,
),
'abuyoyo/plugincore' => array(
'pretty_version' => '0.27',
'version' => '0.27.0.0',
'reference' => 'd730a674cbe2dc92e60ace8e25a2e0e3fdeee3e6',
'type' => 'library',
'install_path' => __DIR__ . '/../abuyoyo/plugincore',
'aliases' => array(),
'dev_requirement' => false,
),
'abuyoyo/screen-meta-links' => array(
'pretty_version' => '0.13',
'version' => '0.13.0.0',
'reference' => 'b324cef9eb5825d04ffa17f771237b7deca5cd01',
'type' => 'library',
'install_path' => __DIR__ . '/../abuyoyo/screen-meta-links',
'aliases' => array(),
'dev_requirement' => false,
),
),
);