Compare commits

..

No commits in common. "master" and "0.13" have entirely different histories.
master ... 0.13

71 changed files with 210 additions and 8310 deletions

2
.gitignore vendored
View File

@ -1 +1 @@
node_modules/ vendor/

View File

@ -1,115 +1,5 @@
# Notice Manager Changelog # 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 ## 0.13
### Changed ### Changed

29
LICENSE
View File

@ -1,29 +0,0 @@
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,32 +1,3 @@
# Notice Manager # Notice Manager
Collect WordPress admin notices into 'Notices' panel. Manage notices on WordPress admin pages. Adds 'Notices' screen-meta-link.
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,7 +2,6 @@
"name": "abuyoyo/notice-manager", "name": "abuyoyo/notice-manager",
"description": "Manage notices on WordPress admin pages. Adds 'Notices' screen-meta-link.", "description": "Manage notices on WordPress admin pages. Adds 'Notices' screen-meta-link.",
"type": "wordpress-plugin", "type": "wordpress-plugin",
"version": "0.27",
"repositories": [ "repositories": [
{ {
"type": "vcs", "type": "vcs",
@ -10,11 +9,7 @@
} }
], ],
"require":{ "require":{
"abuyoyo/screen-meta-links": "~0.13", "abuyoyo/screen-meta-links": "~0.11",
"abuyoyo/plugincore": "~0.27", "abuyoyo/plugincore": "~0.14"
"abuyoyo/adminmenupage": "~0.29"
},
"config": {
"prepend-autoloader": false
} }
} }

153
composer.lock generated
View File

@ -1,153 +0,0 @@
{
"_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"
}

60
css/admin_notices.css Normal file
View File

@ -0,0 +1,60 @@
: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;
}

View File

@ -1,59 +0,0 @@
: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,232 +2,147 @@
* NoticeManager class/module * NoticeManager class/module
* *
*/ */
const NoticeManager = function ($) { var NoticeManager = (function ($, document) {
let options = window.notice_manager_options;
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 // wait function used with autoCollapse
const wait = function (ms) { let wait = function (ms) {
var dfd = $.Deferred() var dfd = $.Deferred();
setTimeout(dfd.resolve, ms) //callback, timeout till callback setTimeout(dfd.resolve, ms); //callback, timeout till callback
return dfd.promise() return dfd.promise();
} };
const options = window.notice_manager_options let notices;
let notices let button;
let panel;
let haveClosed;
let dismissNoticesButton;
let button // bootstrap
let panel // some of these need to run BEFORE document.ready - don't know why really
let dismissNoticesButton button = $("#meta-link-notices");
panel = $("#meta-link-notices-wrap");
haveClosed = false;
dismissNoticesButton = $("#meta-link-notices-wrap button.notice-dismiss");
let haveClosed // set to true on first close/collect dismissNoticesButton.on("click", () => {
let panelObserver screenMeta.close(panel, button);
NoticeManager.collectNotices();
});
return { //original wp focus on click function
bootstrap: () => { button.on("focus.scroll-into-view", (e) => {
if (e.target.scrollIntoView) e.target.scrollIntoView(false);
});
// Init selectors // scroll page to top when closing notice panel
button = $("#meta-link-notices") // cannot convert to arrow function - uses this
panel = $("#meta-link-notices-wrap") // could use event.target instead
dismissNoticesButton = $("#meta-link-notices-wrap button.notice-dismiss") 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);
// });
}
});
// bootstrap notices /**
// get all notices that are not explicitly marked as `.inline` or `.below-h2` * document.on.ready
// we add .update-nag.inline for WordPress Update notice */
notices = $(selectors_all.join(', ')) $(() => {
.not(selectors_skip.join(', '))
.add("div.update-nag")
// Set state console.log("NoticeManager.on.ready");
haveClosed = false console.log("options");
console.log(options);
dismissNoticesButton.on("click", () => { // bootstrap notices
screenMeta.close(panel, button) // get all notices that are not explicily marked as `.inline` or `.below-h2`
if (!haveClosed) { // we add .update-nag.inline for WordPress Update notice
NoticeManager.collectNotices() notices = $("div.updated, div.error, div.notice")
} .not(".inline, .below-h2")
NoticeManager.addCounter() .add("div.update-nag");
})
//original wp focus on click function
button.on("focus.scroll-into-view", (e) => {
if (e.target.scrollIntoView) e.target.scrollIntoView(false)
})
// scroll page to top when closing notice panel
// 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'
//}
/**
* 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
)
)
/**
* Remove panel if there are no notices on this page
*/
if (options.screen_panel) {
NoticeManager.maybeRemoveNoticesPanel()
}
if (options.screen_panel && options.auto_collect) {
NoticeManager.collectNotices()
} else {
if (options.above_title) {
NoticeManager.moveAboveTitle()
}
}
/**
* auto-open notices panel
*/
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 collected notices previously
* only auto-close if no error messages
*/
if (options.auto_collapse && !options.distraction_free) {
wait(4000).then(() => {
if (haveClosed && NoticeManager.getNoticesTopPriority() != 'error') {
screenMeta.close(panel, button)
NoticeManager.addCounter()
}
})
}
if (options.distraction_free) {
NoticeManager.addCounterWhenClosed()
}
},
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 * Remove panel if there are no notices on this page
*
* @returns {string} top priority
*/ */
getNoticesTopPriorityWhenClosed: () => { NoticeManager.maybeRemoveNoticesPanel();
if (notices.filter(selectors_error.join(", ")).length)
return 'error' if (options.auto_collect) {
if (notices.filter(selectors_warning.join(", ")).length) NoticeManager.collectNotices();
return 'warning' } else {
return 'info' /**
}, * 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");
}
/**
* auto-open notices panel
*/
if (button.length) {
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
*/
if (options.auto_collapse) {
wait(4000).then(() => {
if (!haveClosed) {
screenMeta.close(panel, button);
}
});
}
}); // 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
);
});
return {
getNotices: () => notices,
/** /**
* Collect notices into panel. * Collect notices into panel.
* Remove dismiss-notices button. * Remove dismiss-notices button.
*/ */
collectNotices: () => { collectNotices: () => {
notices.appendTo(".notice_container").eq(0) notices.appendTo(".notice_container").eq(0);
$(".notice_container").removeClass("empty") // .empty removes padding $(".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. * When dismissible notices are dismissed, check if any notices are left on page.
* If no notices are left - remove Notice Panel entirely * If no notices are left - remove Notice Panel entirely
*/ */
panelObserver = new MutationObserver(() => { $(document).on("DOMNodeRemoved", ".notice.is-dismissible", (e) => {
notices = panel notices = panel
.find(selectors_all.join(", ")) .find("div.updated, div.error, div.notice, div.update-nag")
.filter(":visible") .filter(":visible");
NoticeManager.maybeRemoveNoticesPanel() 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()
}, },
/** /**
@ -236,26 +151,14 @@ const NoticeManager = function ($) {
*/ */
maybeRemoveNoticesPanel: () => { maybeRemoveNoticesPanel: () => {
if (!notices.length) { if (!notices.length) {
screenMeta.close(panel, button) screenMeta.close(panel, button);
$("#meta-link-notices-link-wrap").detach() $("#meta-link-notices-link-wrap").detach();
$("#meta-link-notices-wrap").detach() $("#meta-link-notices-wrap").detach();
if ($("#screen-meta-links").children().length == 0) { if (!$("#screen-meta-links").children().length)
$("#screen-meta-links").detach() $("#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,98 +1,64 @@
<?php <?php
/** /**
* Plugin Name: Notice Manager * Plugin Name: Notice Manager
* Description: Manage notices on WordPress admin pages. Adds 'Notices' screen-meta-link panel to collect notices from page. * Description: Manage notices on WordPress admin pages. Adds 'Notices' screen-meta-link.
* Version: 0.27 * Version: 0.13
* Author: abuyoyo * Author: abuyoyo
* Author URI: https://github.com/abuyoyo/ * Author URI: https://github.com/abuyoyo/
* Plugin URI: https://github.com/abuyoyo/notice-manager * Plugin URI: https://github.com/abuyoyo/notice-manager
* Update URI: https://github.com/abuyoyo/notice-manager
*/ */
defined( 'ABSPATH' ) || die( 'No soup for you!' ); 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; use WPHelper\PluginCore;
/** /**
* Bootstrap plugin and admin page (Tools > Notice Manager) * Print setting page
*/ */
new PluginCore( new PluginCore(
__FILE__, __FILE__,
[ [
'action_links' => [ 'update_checker' => true,
'settings' => [
'text' => 'Settings',
'href' => 'menu_page' // reserved option_name
],
],
'admin_page' => [ 'admin_page' => [
'parent' => 'options', 'parent' => 'options',
'render' => 'settings-page', // built-in settings page 'render' => 'settings-page', // built-in settings page
// 'plugin_info' => true, // disable on public repo 'plugin_info' => true,
'settings' => [ 'settings' => [
'option_name' => 'notice_manager', // option_name used in wp_options table 'option_name' => 'notice_manager', // option_name used in wp_options table
'sections' => [ 'sections' => [
[ [
'id' => 'notice_manager', 'id' => 'notice_manager',
'description' => 'Setup Notice Manager options.', // 'title' => 'N',
'description_container' => 'notice-info', 'description' => 'Setup How notice manager functions.',
'fields' => [ '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', 'id' => 'screen_panel',
'title' => 'Notices Panel', 'title' => 'Notices Panel',
'type' => 'checkbox', 'type' => 'checkbox',
'description' => 'Enable Screen Meta \'Notices\' panel. User can collect notices into collapsible panel.', 'description' => 'Enable\disable screen-meta-links \'Notices\' panel.',
], ],
[ [
'id' => 'auto_collect', 'id' => 'auto_collect',
'title' => 'Auto-Collect Notices', 'title' => 'Auto-Collect Notices',
'type' => 'checkbox', 'type' => 'checkbox',
'description' => 'If Notices panel is enabled - auto-collect notices into panel on page load.', 'description' => 'Automatic collection of notices into panel.',
], ],
[ [
'id' => 'auto_collapse', 'id' => 'auto_collapse',
'title' => 'Auto-Collapse Panel', 'title' => 'Auto-collapse Panel',
'type' => 'checkbox', 'type' => 'checkbox',
'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.', 'description' => 'Notices panel will stay open for a few seconds on page load, and then close automatically.',
], ],
[ ]
'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'; require_once 'src/NoticeManager.php';
add_action( 'plugins_loaded', fn() => new NoticeManager() ); add_action('plugins_loaded', function(){
new NoticeManager();
});

1671
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +0,0 @@
{
"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"
}
}

View File

@ -1,71 +0,0 @@
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,23 +22,17 @@ class NoticeManager{
function __construct(){ function __construct(){
// exit early if not admin page // exit early if not admin page
if ( ! is_admin() ) if ( !is_admin() )
return; return;
$this->options = get_option( 'notice_manager' ); $this->options = get_option( 'notice_manager');
add_action( 'admin_enqueue_scripts' , [ $this , 'admin_enqueues' ] ); add_action( 'admin_enqueue_scripts' , [ $this , 'admin_enqueues' ] );
if ( ! empty( $this->options['screen_panel'] ) ){ if ( ! empty( $this->options['screen_panel'] ) ){
add_action( 'admin_init' , [ $this , 'register_notice_manager_panel' ] ); add_action( 'admin_init' , [ $this , 'register_notice_manager_panel' ] );
}else{
if ( ! empty( $this->options['auto_collect'] ) ){ array_walk($this->options,function(&$item){$item=0;});
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' );
} }
} }
@ -46,7 +40,7 @@ class NoticeManager{
function admin_enqueues(){ function admin_enqueues(){
wp_enqueue_script( 'notice_manager_panel', NOTICE_MANAGER_URL . 'js/notice_manager_panel.js' , null, false , true ); 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_localize_script( 'notice_manager_panel', 'notice_manager_options', $this->options );
wp_enqueue_style( 'admin_notices', NOTICE_MANAGER_URL . 'css/notice-manager.css' ); wp_enqueue_style( 'admin_notices', NOTICE_MANAGER_URL . 'css/admin_notices.css' );
} }

View File

@ -1,225 +0,0 @@
# 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

View File

@ -1,29 +0,0 @@
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.

View File

@ -1,53 +0,0 @@
# 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

@ -1,23 +0,0 @@
{
"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

@ -1,19 +0,0 @@
<?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

@ -1,268 +0,0 @@
<?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

@ -1,34 +0,0 @@
<?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

@ -1,56 +0,0 @@
<?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

@ -1,116 +0,0 @@
<?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

@ -1,377 +0,0 @@
<?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

@ -1,15 +0,0 @@
<?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

@ -1,15 +0,0 @@
<?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

@ -1,37 +0,0 @@
<?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

@ -1,84 +0,0 @@
<?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

@ -1,12 +0,0 @@
<?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

@ -1,41 +0,0 @@
<?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

@ -1,17 +0,0 @@
<?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

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

View File

@ -1,9 +0,0 @@
<?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

@ -1,42 +0,0 @@
<?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

@ -1,13 +0,0 @@
<?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

@ -1,9 +0,0 @@
<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

@ -1,24 +0,0 @@
<?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

@ -1,29 +0,0 @@
<?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

@ -1,12 +0,0 @@
<?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

@ -1,34 +0,0 @@
<?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

@ -1,38 +0,0 @@
# 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

@ -1,26 +0,0 @@
# 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.

View File

@ -1,220 +0,0 @@
<?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;
}
}
}

View File

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

View File

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

View File

@ -1,150 +0,0 @@
# 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.

View File

@ -1,29 +0,0 @@
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.

View File

@ -1,698 +0,0 @@
<?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;

View File

@ -1,95 +0,0 @@
# 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'
```

View File

@ -1,17 +0,0 @@
{
"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

@ -1,38 +0,0 @@
# 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

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

View File

@ -1,48 +0,0 @@
# 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

@ -1,60 +0,0 @@
# 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

@ -1,8 +0,0 @@
{
"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

@ -1,16 +0,0 @@
.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

@ -1,51 +0,0 @@
(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

@ -1,373 +0,0 @@
<?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
View File

@ -1,25 +0,0 @@
<?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();

View File

@ -1,579 +0,0 @@
<?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);
}
}

View File

@ -1,359 +0,0 @@
<?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;
}
}

View File

@ -1,21 +0,0 @@
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.

View File

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

View File

@ -1,11 +0,0 @@
<?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

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

View File

@ -1,10 +0,0 @@
<?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'),
);

View File

@ -1,48 +0,0 @@
<?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;
}
}

View File

@ -1,43 +0,0 @@
<?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);
}
}

View File

@ -1,152 +0,0 @@
{
"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": []
}

View File

@ -1,59 +0,0 @@
<?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,
),
),
);