From e602749fe493ffe404cf50161d7d7a59246da302 Mon Sep 17 00:00:00 2001 From: ojopaul Date: Tue, 28 Nov 2023 11:59:09 +0100 Subject: [PATCH] Initial commit --- composer.lock | 567 ++++++------ defines.php | 2 +- lib/composer.json | 1 + lib/composer.lock | 2 +- lib/vendor/composer/autoload_psr4.php | 1 + lib/vendor/composer/autoload_static.php | 5 + lib/vendor/composer/installed.php | 12 +- src/assets/css/multiple-authors.css | 18 + src/assets/js/multiple-authors.js | 33 +- src/core/Classes/Legacy/LegacyPlugin.php | 2 + src/core/Classes/Post_Editor.php | 168 +++- src/core/Classes/Utils.php | 41 +- src/core/Plugin.php | 1 + .../assets/css/author-categories.css | 29 + .../assets/js/author-categories.js | 53 ++ .../assets/js/category-reorder.js | 114 +++ .../assets/js/inline-edit.js | 292 ++++++ .../author-categories/author-categories.php | 839 ++++++++++++++++++ .../classes/AuthorCategoriesSchema.php | 137 +++ .../classes/AuthorCategoriesTable.php | 471 ++++++++++ .../multiple-authors/multiple-authors.php | 8 + 21 files changed, 2454 insertions(+), 342 deletions(-) create mode 100644 src/modules/author-categories/assets/css/author-categories.css create mode 100644 src/modules/author-categories/assets/js/author-categories.js create mode 100644 src/modules/author-categories/assets/js/category-reorder.js create mode 100644 src/modules/author-categories/assets/js/inline-edit.js create mode 100644 src/modules/author-categories/author-categories.php create mode 100644 src/modules/author-categories/classes/AuthorCategoriesSchema.php create mode 100644 src/modules/author-categories/classes/AuthorCategoriesTable.php diff --git a/composer.lock b/composer.lock index 8e7062f2..a2f9cc28 100644 --- a/composer.lock +++ b/composer.lock @@ -1333,30 +1333,30 @@ }, { "name": "doctrine/instantiator", - "version": "1.5.0", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^11", + "doctrine/coding-standard": "^11", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.30 || ^5.4" + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" }, "type": "library", "autoload": { @@ -1383,7 +1383,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" }, "funding": [ { @@ -1399,7 +1399,7 @@ "type": "tidelift" } ], - "time": "2022-12-30T00:15:36+00:00" + "time": "2022-12-30T00:23:10+00:00" }, { "name": "eftec/bladeone", @@ -2571,31 +2571,31 @@ }, { "name": "illuminate/collections", - "version": "v9.52.16", + "version": "v10.33.0", "source": { "type": "git", "url": "https://github.com/illuminate/collections.git", - "reference": "d3710b0b244bfc62c288c1a87eaa62dd28352d1f" + "reference": "766a3b6c3e5c8011b037a147266dcf7f93b21223" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/collections/zipball/d3710b0b244bfc62c288c1a87eaa62dd28352d1f", - "reference": "d3710b0b244bfc62c288c1a87eaa62dd28352d1f", + "url": "https://api.github.com/repos/illuminate/collections/zipball/766a3b6c3e5c8011b037a147266dcf7f93b21223", + "reference": "766a3b6c3e5c8011b037a147266dcf7f93b21223", "shasum": "" }, "require": { - "illuminate/conditionable": "^9.0", - "illuminate/contracts": "^9.0", - "illuminate/macroable": "^9.0", - "php": "^8.0.2" + "illuminate/conditionable": "^10.0", + "illuminate/contracts": "^10.0", + "illuminate/macroable": "^10.0", + "php": "^8.1" }, "suggest": { - "symfony/var-dumper": "Required to use the dump method (^6.0)." + "symfony/var-dumper": "Required to use the dump method (^6.2)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "9.x-dev" + "dev-master": "10.x-dev" } }, "autoload": { @@ -2622,20 +2622,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2023-06-11T21:17:10+00:00" + "time": "2023-11-20T15:45:45+00:00" }, { "name": "illuminate/conditionable", - "version": "v9.52.16", + "version": "v10.33.0", "source": { "type": "git", "url": "https://github.com/illuminate/conditionable.git", - "reference": "bea24daa0fa84b7e7b0d5b84f62c71b7e2dc3364" + "reference": "d0958e4741fc9d6f516a552060fd1b829a85e009" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/conditionable/zipball/bea24daa0fa84b7e7b0d5b84f62c71b7e2dc3364", - "reference": "bea24daa0fa84b7e7b0d5b84f62c71b7e2dc3364", + "url": "https://api.github.com/repos/illuminate/conditionable/zipball/d0958e4741fc9d6f516a552060fd1b829a85e009", + "reference": "d0958e4741fc9d6f516a552060fd1b829a85e009", "shasum": "" }, "require": { @@ -2644,7 +2644,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.x-dev" + "dev-master": "10.x-dev" } }, "autoload": { @@ -2668,31 +2668,31 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2023-02-01T21:42:32+00:00" + "time": "2023-02-03T08:06:17+00:00" }, { "name": "illuminate/contracts", - "version": "v9.52.16", + "version": "v10.33.0", "source": { "type": "git", "url": "https://github.com/illuminate/contracts.git", - "reference": "44f65d723b13823baa02ff69751a5948bde60c22" + "reference": "f6bf37a272fda164f6c451407c99f820eb1eb95b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/contracts/zipball/44f65d723b13823baa02ff69751a5948bde60c22", - "reference": "44f65d723b13823baa02ff69751a5948bde60c22", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/f6bf37a272fda164f6c451407c99f820eb1eb95b", + "reference": "f6bf37a272fda164f6c451407c99f820eb1eb95b", "shasum": "" }, "require": { - "php": "^8.0.2", + "php": "^8.1", "psr/container": "^1.1.1|^2.0.1", "psr/simple-cache": "^1.0|^2.0|^3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "9.x-dev" + "dev-master": "10.x-dev" } }, "autoload": { @@ -2716,29 +2716,29 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2023-02-08T14:36:30+00:00" + "time": "2023-10-30T00:59:22+00:00" }, { "name": "illuminate/macroable", - "version": "v9.52.16", + "version": "v10.33.0", "source": { "type": "git", "url": "https://github.com/illuminate/macroable.git", - "reference": "e3bfaf6401742a9c6abca61b9b10e998e5b6449a" + "reference": "dff667a46ac37b634dcf68909d9d41e94dc97c27" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/macroable/zipball/e3bfaf6401742a9c6abca61b9b10e998e5b6449a", - "reference": "e3bfaf6401742a9c6abca61b9b10e998e5b6449a", + "url": "https://api.github.com/repos/illuminate/macroable/zipball/dff667a46ac37b634dcf68909d9d41e94dc97c27", + "reference": "dff667a46ac37b634dcf68909d9d41e94dc97c27", "shasum": "" }, "require": { - "php": "^8.0.2" + "php": "^8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "9.x-dev" + "dev-master": "10.x-dev" } }, "autoload": { @@ -2762,20 +2762,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2022-08-09T13:29:29+00:00" + "time": "2023-06-05T12:46:42+00:00" }, { "name": "illuminate/support", - "version": "v9.52.16", + "version": "v10.33.0", "source": { "type": "git", "url": "https://github.com/illuminate/support.git", - "reference": "223c608dbca27232df6213f776bfe7bdeec24874" + "reference": "f414b40d6149d6a4954f0abceacd1af2edf2d596" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/support/zipball/223c608dbca27232df6213f776bfe7bdeec24874", - "reference": "223c608dbca27232df6213f776bfe7bdeec24874", + "url": "https://api.github.com/repos/illuminate/support/zipball/f414b40d6149d6a4954f0abceacd1af2edf2d596", + "reference": "f414b40d6149d6a4954f0abceacd1af2edf2d596", "shasum": "" }, "require": { @@ -2783,30 +2783,30 @@ "ext-ctype": "*", "ext-filter": "*", "ext-mbstring": "*", - "illuminate/collections": "^9.0", - "illuminate/conditionable": "^9.0", - "illuminate/contracts": "^9.0", - "illuminate/macroable": "^9.0", - "nesbot/carbon": "^2.62.1", - "php": "^8.0.2", + "illuminate/collections": "^10.0", + "illuminate/conditionable": "^10.0", + "illuminate/contracts": "^10.0", + "illuminate/macroable": "^10.0", + "nesbot/carbon": "^2.67", + "php": "^8.1", "voku/portable-ascii": "^2.0" }, "conflict": { "tightenco/collect": "<5.5.33" }, "suggest": { - "illuminate/filesystem": "Required to use the composer class (^9.0).", + "illuminate/filesystem": "Required to use the composer class (^10.0).", "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.0.2).", "ramsey/uuid": "Required to use Str::uuid() (^4.7).", - "symfony/process": "Required to use the composer class (^6.0).", - "symfony/uid": "Required to use Str::ulid() (^6.0).", - "symfony/var-dumper": "Required to use the dd function (^6.0).", + "symfony/process": "Required to use the composer class (^6.2).", + "symfony/uid": "Required to use Str::ulid() (^6.2).", + "symfony/var-dumper": "Required to use the dd function (^6.2).", "vlucas/phpdotenv": "Required to use the Env class and env helper (^5.4.1)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "9.x-dev" + "dev-master": "10.x-dev" } }, "autoload": { @@ -2833,7 +2833,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2023-06-11T21:11:53+00:00" + "time": "2023-11-20T20:29:51+00:00" }, { "name": "justinrainbow/json-schema", @@ -2907,16 +2907,16 @@ }, { "name": "lucatume/wp-browser", - "version": "3.2.1", + "version": "3.2.2", "source": { "type": "git", "url": "https://github.com/lucatume/wp-browser.git", - "reference": "95a189fda634fd52cb44eebbbda3fabe0fdabdb2" + "reference": "88456c4d73ded85132c6bfa594f685a90952630c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lucatume/wp-browser/zipball/95a189fda634fd52cb44eebbbda3fabe0fdabdb2", - "reference": "95a189fda634fd52cb44eebbbda3fabe0fdabdb2", + "url": "https://api.github.com/repos/lucatume/wp-browser/zipball/88456c4d73ded85132c6bfa594f685a90952630c", + "reference": "88456c4d73ded85132c6bfa594f685a90952630c", "shasum": "" }, "require": { @@ -3006,7 +3006,7 @@ ], "support": { "issues": "https://github.com/lucatume/wp-browser/issues", - "source": "https://github.com/lucatume/wp-browser/tree/3.2.1" + "source": "https://github.com/lucatume/wp-browser/tree/3.2.2" }, "funding": [ { @@ -3014,7 +3014,7 @@ "type": "github" } ], - "time": "2023-09-21T06:36:09+00:00" + "time": "2023-11-20T15:05:37+00:00" }, { "name": "mck89/peast", @@ -3733,16 +3733,16 @@ }, { "name": "php-webdriver/webdriver", - "version": "1.15.0", + "version": "1.15.1", "source": { "type": "git", "url": "https://github.com/php-webdriver/php-webdriver.git", - "reference": "a1578689290055586f1ee51eaf0ec9d52895bb6d" + "reference": "cd52d9342c5aa738c2e75a67e47a1b6df97154e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/a1578689290055586f1ee51eaf0ec9d52895bb6d", - "reference": "a1578689290055586f1ee51eaf0ec9d52895bb6d", + "url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/cd52d9342c5aa738c2e75a67e47a1b6df97154e8", + "reference": "cd52d9342c5aa738c2e75a67e47a1b6df97154e8", "shasum": "" }, "require": { @@ -3751,7 +3751,7 @@ "ext-zip": "*", "php": "^7.3 || ^8.0", "symfony/polyfill-mbstring": "^1.12", - "symfony/process": "^5.0 || ^6.0" + "symfony/process": "^5.0 || ^6.0 || ^7.0" }, "replace": { "facebook/webdriver": "*" @@ -3793,9 +3793,9 @@ ], "support": { "issues": "https://github.com/php-webdriver/php-webdriver/issues", - "source": "https://github.com/php-webdriver/php-webdriver/tree/1.15.0" + "source": "https://github.com/php-webdriver/php-webdriver/tree/1.15.1" }, - "time": "2023-08-29T13:52:26+00:00" + "time": "2023-10-20T12:21:20+00:00" }, { "name": "phpmd/phpmd", @@ -5967,16 +5967,16 @@ }, { "name": "symfony/browser-kit", - "version": "v5.4.21", + "version": "v5.4.31", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "a866ca7e396f15d7efb6d74a8a7d364d4e05b704" + "reference": "0ed1f634a36606f2065eec221b3975e05016cbbe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/a866ca7e396f15d7efb6d74a8a7d364d4e05b704", - "reference": "a866ca7e396f15d7efb6d74a8a7d364d4e05b704", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/0ed1f634a36606f2065eec221b3975e05016cbbe", + "reference": "0ed1f634a36606f2065eec221b3975e05016cbbe", "shasum": "" }, "require": { @@ -6019,7 +6019,7 @@ "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/browser-kit/tree/v5.4.21" + "source": "https://github.com/symfony/browser-kit/tree/v5.4.31" }, "funding": [ { @@ -6035,42 +6035,39 @@ "type": "tidelift" } ], - "time": "2023-02-14T08:03:56+00:00" + "time": "2023-10-31T07:58:33+00:00" }, { "name": "symfony/config", - "version": "v6.0.19", + "version": "v6.3.8", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "db4fc45c24e0c3e2198e68ada9d7f90daa1f97e3" + "reference": "b7a63887960359e5b59b15826fa9f9be10acbe88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/db4fc45c24e0c3e2198e68ada9d7f90daa1f97e3", - "reference": "db4fc45c24e0c3e2198e68ada9d7f90daa1f97e3", + "url": "https://api.github.com/repos/symfony/config/zipball/b7a63887960359e5b59b15826fa9f9be10acbe88", + "reference": "b7a63887960359e5b59b15826fa9f9be10acbe88", "shasum": "" }, "require": { - "php": ">=8.0.2", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/filesystem": "^5.4|^6.0", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-php81": "^1.22" + "symfony/polyfill-ctype": "~1.8" }, "conflict": { - "symfony/finder": "<4.4" + "symfony/finder": "<5.4", + "symfony/service-contracts": "<2.5" }, "require-dev": { "symfony/event-dispatcher": "^5.4|^6.0", "symfony/finder": "^5.4|^6.0", "symfony/messenger": "^5.4|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", + "symfony/service-contracts": "^2.5|^3", "symfony/yaml": "^5.4|^6.0" }, - "suggest": { - "symfony/yaml": "To use the yaml reference dumper" - }, "type": "library", "autoload": { "psr-4": { @@ -6097,7 +6094,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v6.0.19" + "source": "https://github.com/symfony/config/tree/v6.3.8" }, "funding": [ { @@ -6113,20 +6110,20 @@ "type": "tidelift" } ], - "time": "2023-01-09T04:36:00+00:00" + "time": "2023-11-09T08:28:21+00:00" }, { "name": "symfony/console", - "version": "v5.4.28", + "version": "v5.4.31", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "f4f71842f24c2023b91237c72a365306f3c58827" + "reference": "11ac5f154e0e5c4c77af83ad11ead9165280b92a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/f4f71842f24c2023b91237c72a365306f3c58827", - "reference": "f4f71842f24c2023b91237c72a365306f3c58827", + "url": "https://api.github.com/repos/symfony/console/zipball/11ac5f154e0e5c4c77af83ad11ead9165280b92a", + "reference": "11ac5f154e0e5c4c77af83ad11ead9165280b92a", "shasum": "" }, "require": { @@ -6196,7 +6193,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.28" + "source": "https://github.com/symfony/console/tree/v5.4.31" }, "funding": [ { @@ -6212,7 +6209,7 @@ "type": "tidelift" } ], - "time": "2023-08-07T06:12:30+00:00" + "time": "2023-10-31T07:58:33+00:00" }, { "name": "symfony/css-selector", @@ -6282,30 +6279,30 @@ }, { "name": "symfony/dependency-injection", - "version": "v6.0.20", + "version": "v6.3.8", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "359806e1adebd1c43e18e5ea22acd14bef7fcf8c" + "reference": "1f30f545c4151f611148fc19e28d54d39e0a00bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/359806e1adebd1c43e18e5ea22acd14bef7fcf8c", - "reference": "359806e1adebd1c43e18e5ea22acd14bef7fcf8c", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/1f30f545c4151f611148fc19e28d54d39e0a00bc", + "reference": "1f30f545c4151f611148fc19e28d54d39e0a00bc", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php81": "^1.22", - "symfony/service-contracts": "^1.1.6|^2.0|^3.0" + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/service-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.2.10" }, "conflict": { "ext-psr": "<1.1|>=2", - "symfony/config": "<5.4", + "symfony/config": "<6.1", "symfony/finder": "<5.4", - "symfony/proxy-manager-bridge": "<5.4", + "symfony/proxy-manager-bridge": "<6.3", "symfony/yaml": "<5.4" }, "provide": { @@ -6313,17 +6310,10 @@ "symfony/service-implementation": "1.1|2.0|3.0" }, "require-dev": { - "symfony/config": "^5.4|^6.0", + "symfony/config": "^6.1", "symfony/expression-language": "^5.4|^6.0", "symfony/yaml": "^5.4|^6.0" }, - "suggest": { - "symfony/config": "", - "symfony/expression-language": "For using expressions in service container configuration", - "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", - "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", - "symfony/yaml": "" - }, "type": "library", "autoload": { "psr-4": { @@ -6350,7 +6340,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v6.0.20" + "source": "https://github.com/symfony/dependency-injection/tree/v6.3.8" }, "funding": [ { @@ -6366,29 +6356,29 @@ "type": "tidelift" } ], - "time": "2023-01-30T15:41:07+00:00" + "time": "2023-10-31T08:07:48+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.0.2", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", - "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.4-dev" }, "thanks": { "name": "symfony/contracts", @@ -6417,7 +6407,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.2" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0" }, "funding": [ { @@ -6433,7 +6423,7 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2023-05-23T14:45:45+00:00" }, { "name": "symfony/dom-crawler", @@ -6597,29 +6587,26 @@ }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.0.2", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "7bc61cc2db649b4637d331240c5346dcc7708051" + "reference": "a76aed96a42d2b521153fb382d418e30d18b59df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7bc61cc2db649b4637d331240c5346dcc7708051", - "reference": "7bc61cc2db649b4637d331240c5346dcc7708051", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/a76aed96a42d2b521153fb382d418e30d18b59df", + "reference": "a76aed96a42d2b521153fb382d418e30d18b59df", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "psr/event-dispatcher": "^1" }, - "suggest": { - "symfony/event-dispatcher-implementation": "" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.4-dev" }, "thanks": { "name": "symfony/contracts", @@ -6656,7 +6643,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.0.2" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.4.0" }, "funding": [ { @@ -6672,24 +6659,24 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2023-05-23T14:45:45+00:00" }, { "name": "symfony/filesystem", - "version": "v6.0.19", + "version": "v6.3.1", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "3d49eec03fda1f0fc19b7349fbbe55ebc1004214" + "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/3d49eec03fda1f0fc19b7349fbbe55ebc1004214", - "reference": "3d49eec03fda1f0fc19b7349fbbe55ebc1004214", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", + "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, @@ -6719,7 +6706,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.0.19" + "source": "https://github.com/symfony/filesystem/tree/v6.3.1" }, "funding": [ { @@ -6735,7 +6722,7 @@ "type": "tidelift" } ], - "time": "2023-01-20T17:44:14+00:00" + "time": "2023-06-01T08:30:39+00:00" }, { "name": "symfony/finder", @@ -7292,85 +7279,6 @@ ], "time": "2023-01-26T09:26:14+00:00" }, - { - "name": "symfony/polyfill-php81", - "version": "v1.28.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/7581cd600fa9fd681b797d00b02f068e2f13263b", - "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.28.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-01-26T09:26:14+00:00" - }, { "name": "symfony/process", "version": "v5.4.28", @@ -7435,32 +7343,29 @@ }, { "name": "symfony/service-contracts", - "version": "v3.0.2", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66" + "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d78d39c1599bd1188b8e26bb341da52c3c6d8a66", - "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b3313c2dbffaf71c8de2934e2ea56ed2291a3838", + "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "psr/container": "^2.0" }, "conflict": { "ext-psr": "<1.1|>=2" }, - "suggest": { - "symfony/service-implementation": "" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.4-dev" }, "thanks": { "name": "symfony/contracts", @@ -7470,7 +7375,10 @@ "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -7497,7 +7405,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.0.2" + "source": "https://github.com/symfony/service-contracts/tree/v3.4.0" }, "funding": [ { @@ -7513,36 +7421,37 @@ "type": "tidelift" } ], - "time": "2022-05-30T19:17:58+00:00" + "time": "2023-07-30T20:28:31+00:00" }, { "name": "symfony/string", - "version": "v6.0.19", + "version": "v6.3.8", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "d9e72497367c23e08bf94176d2be45b00a9d232a" + "reference": "13880a87790c76ef994c91e87efb96134522577a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/d9e72497367c23e08bf94176d2be45b00a9d232a", - "reference": "d9e72497367c23e08bf94176d2be45b00a9d232a", + "url": "https://api.github.com/repos/symfony/string/zipball/13880a87790c76ef994c91e87efb96134522577a", + "reference": "13880a87790c76ef994c91e87efb96134522577a", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/translation-contracts": "<2.0" + "symfony/translation-contracts": "<2.5" }, "require-dev": { "symfony/error-handler": "^5.4|^6.0", "symfony/http-client": "^5.4|^6.0", - "symfony/translation-contracts": "^2.0|^3.0", + "symfony/intl": "^6.2", + "symfony/translation-contracts": "^2.5|^3.0", "symfony/var-exporter": "^5.4|^6.0" }, "type": "library", @@ -7582,7 +7491,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.0.19" + "source": "https://github.com/symfony/string/tree/v6.3.8" }, "funding": [ { @@ -7598,32 +7507,35 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:36:10+00:00" + "time": "2023-11-09T08:28:21+00:00" }, { "name": "symfony/translation", - "version": "v6.0.19", + "version": "v6.3.7", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "9c24b3fdbbe9fb2ef3a6afd8bbaadfd72dad681f" + "reference": "30212e7c87dcb79c83f6362b00bde0e0b1213499" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/9c24b3fdbbe9fb2ef3a6afd8bbaadfd72dad681f", - "reference": "9c24b3fdbbe9fb2ef3a6afd8bbaadfd72dad681f", + "url": "https://api.github.com/repos/symfony/translation/zipball/30212e7c87dcb79c83f6362b00bde0e0b1213499", + "reference": "30212e7c87dcb79c83f6362b00bde0e0b1213499", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^2.3|^3.0" + "symfony/translation-contracts": "^2.5|^3.0" }, "conflict": { "symfony/config": "<5.4", "symfony/console": "<5.4", "symfony/dependency-injection": "<5.4", + "symfony/http-client-contracts": "<2.5", "symfony/http-kernel": "<5.4", + "symfony/service-contracts": "<2.5", "symfony/twig-bundle": "<5.4", "symfony/yaml": "<5.4" }, @@ -7631,23 +7543,20 @@ "symfony/translation-implementation": "2.3|3.0" }, "require-dev": { + "nikic/php-parser": "^4.13", "psr/log": "^1|^2|^3", "symfony/config": "^5.4|^6.0", "symfony/console": "^5.4|^6.0", "symfony/dependency-injection": "^5.4|^6.0", "symfony/finder": "^5.4|^6.0", - "symfony/http-client-contracts": "^1.1|^2.0|^3.0", + "symfony/http-client-contracts": "^2.5|^3.0", "symfony/http-kernel": "^5.4|^6.0", "symfony/intl": "^5.4|^6.0", "symfony/polyfill-intl-icu": "^1.21", - "symfony/service-contracts": "^1.1.2|^2|^3", + "symfony/routing": "^5.4|^6.0", + "symfony/service-contracts": "^2.5|^3", "symfony/yaml": "^5.4|^6.0" }, - "suggest": { - "psr/log-implementation": "To use logging capability in translator", - "symfony/config": "", - "symfony/yaml": "" - }, "type": "library", "autoload": { "files": [ @@ -7677,7 +7586,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.0.19" + "source": "https://github.com/symfony/translation/tree/v6.3.7" }, "funding": [ { @@ -7693,32 +7602,29 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:36:10+00:00" + "time": "2023-10-28T23:11:45+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.0.2", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "acbfbb274e730e5a0236f619b6168d9dedb3e282" + "reference": "dee0c6e5b4c07ce851b462530088e64b255ac9c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/acbfbb274e730e5a0236f619b6168d9dedb3e282", - "reference": "acbfbb274e730e5a0236f619b6168d9dedb3e282", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/dee0c6e5b4c07ce851b462530088e64b255ac9c5", + "reference": "dee0c6e5b4c07ce851b462530088e64b255ac9c5", "shasum": "" }, "require": { - "php": ">=8.0.2" - }, - "suggest": { - "symfony/translation-implementation": "" + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "3.4-dev" }, "thanks": { "name": "symfony/contracts", @@ -7728,7 +7634,10 @@ "autoload": { "psr-4": { "Symfony\\Contracts\\Translation\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -7755,7 +7664,81 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.0.2" + "source": "https://github.com/symfony/translation-contracts/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-25T15:08:44+00:00" + }, + { + "name": "symfony/var-exporter", + "version": "v6.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-exporter.git", + "reference": "374d289c13cb989027274c86206ddc63b16a2441" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/374d289c13cb989027274c86206ddc63b16a2441", + "reference": "374d289c13cb989027274c86206ddc63b16a2441", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/var-dumper": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\VarExporter\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "homepage": "https://symfony.com", + "keywords": [ + "clone", + "construct", + "export", + "hydrate", + "instantiate", + "lazy-loading", + "proxy", + "serialize" + ], + "support": { + "source": "https://github.com/symfony/var-exporter/tree/v6.3.6" }, "funding": [ { @@ -7771,20 +7754,20 @@ "type": "tidelift" } ], - "time": "2022-06-27T17:10:44+00:00" + "time": "2023-10-13T09:16:49+00:00" }, { "name": "symfony/yaml", - "version": "v5.4.23", + "version": "v5.4.31", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "4cd2e3ea301aadd76a4172756296fe552fb45b0b" + "reference": "f387675d7f5fc4231f7554baa70681f222f73563" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/4cd2e3ea301aadd76a4172756296fe552fb45b0b", - "reference": "4cd2e3ea301aadd76a4172756296fe552fb45b0b", + "url": "https://api.github.com/repos/symfony/yaml/zipball/f387675d7f5fc4231f7554baa70681f222f73563", + "reference": "f387675d7f5fc4231f7554baa70681f222f73563", "shasum": "" }, "require": { @@ -7830,7 +7813,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v5.4.23" + "source": "https://github.com/symfony/yaml/tree/v5.4.31" }, "funding": [ { @@ -7846,20 +7829,20 @@ "type": "tidelift" } ], - "time": "2023-04-23T19:33:36+00:00" + "time": "2023-11-03T14:41:28+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", "shasum": "" }, "require": { @@ -7888,7 +7871,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + "source": "https://github.com/theseer/tokenizer/tree/1.2.2" }, "funding": [ { @@ -7896,7 +7879,7 @@ "type": "github" } ], - "time": "2021-07-28T10:34:58+00:00" + "time": "2023-11-20T00:12:19+00:00" }, { "name": "voku/portable-ascii", @@ -8209,16 +8192,16 @@ }, { "name": "wp-cli/wp-cli", - "version": "v2.8.1", + "version": "v2.9.0", "source": { "type": "git", "url": "https://github.com/wp-cli/wp-cli.git", - "reference": "5dd2340b9a01c3cfdbaf5e93a140759fdd190eee" + "reference": "8a3befba2d947fbf5cc6d1941edf2dd99da4d4b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wp-cli/wp-cli/zipball/5dd2340b9a01c3cfdbaf5e93a140759fdd190eee", - "reference": "5dd2340b9a01c3cfdbaf5e93a140759fdd190eee", + "url": "https://api.github.com/repos/wp-cli/wp-cli/zipball/8a3befba2d947fbf5cc6d1941edf2dd99da4d4b7", + "reference": "8a3befba2d947fbf5cc6d1941edf2dd99da4d4b7", "shasum": "" }, "require": { @@ -8235,7 +8218,7 @@ "wp-cli/entity-command": "^1.2 || ^2", "wp-cli/extension-command": "^1.1 || ^2", "wp-cli/package-command": "^1 || ^2", - "wp-cli/wp-cli-tests": "^3.1.6" + "wp-cli/wp-cli-tests": "^4.0.1" }, "suggest": { "ext-readline": "Include for a better --prompt implementation", @@ -8275,7 +8258,7 @@ "issues": "https://github.com/wp-cli/wp-cli/issues", "source": "https://github.com/wp-cli/wp-cli" }, - "time": "2023-06-05T06:55:55+00:00" + "time": "2023-10-25T09:06:37+00:00" }, { "name": "wp-coding-standards/wpcs", @@ -8394,5 +8377,5 @@ "php": ">=7.2.5" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/defines.php b/defines.php index ed01eb41..d1f11237 100644 --- a/defines.php +++ b/defines.php @@ -12,7 +12,7 @@ defined('ABSPATH') or die('No direct script access allowed.'); if (!defined('PP_AUTHORS_LOADED')) { - define('PP_AUTHORS_VERSION', '4.2.1'); + define('PP_AUTHORS_VERSION', '4.2.1.14'); define('PP_AUTHORS_FILE', 'publishpress-authors/publishpress-authors.php'); define('PP_AUTHORS_BASE_PATH', plugin_dir_path(__DIR__ . '/publishpress-authors.php')); define('PP_AUTHORS_MODULES_PATH', PP_AUTHORS_BASE_PATH . 'src/modules/'); diff --git a/lib/composer.json b/lib/composer.json index 465cbff7..41e82422 100644 --- a/lib/composer.json +++ b/lib/composer.json @@ -25,6 +25,7 @@ "psr-4": { "MultipleAuthors\\": "../src/core/", "MultipleAuthorBoxes\\": "../src/modules/author-boxes/classes/", + "MultipleAuthorCategories\\": "../src/modules/author-categories/classes/", "PPAuthors\\YoastSEO\\": "../src/modules/yoast-seo-integration/src/" } } diff --git a/lib/composer.lock b/lib/composer.lock index 709d4763..70c8a2d5 100644 --- a/lib/composer.lock +++ b/lib/composer.lock @@ -486,5 +486,5 @@ "php": ">=7.2.5" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/lib/vendor/composer/autoload_psr4.php b/lib/vendor/composer/autoload_psr4.php index 432e067a..2bbad138 100644 --- a/lib/vendor/composer/autoload_psr4.php +++ b/lib/vendor/composer/autoload_psr4.php @@ -8,5 +8,6 @@ return array( 'PPAuthors\\YoastSEO\\' => array($baseDir . '/../src/modules/yoast-seo-integration/src'), 'MultipleAuthors\\' => array($baseDir . '/../src/core'), + 'MultipleAuthorCategories\\' => array($baseDir . '/../src/modules/author-categories/classes'), 'MultipleAuthorBoxes\\' => array($baseDir . '/../src/modules/author-boxes/classes'), ); diff --git a/lib/vendor/composer/autoload_static.php b/lib/vendor/composer/autoload_static.php index 30946363..e9688210 100644 --- a/lib/vendor/composer/autoload_static.php +++ b/lib/vendor/composer/autoload_static.php @@ -22,6 +22,7 @@ class ComposerStaticInitPPAuthors 'M' => array ( 'MultipleAuthors\\' => 16, + 'MultipleAuthorCategories\\' => 25, 'MultipleAuthorBoxes\\' => 20, ), ); @@ -35,6 +36,10 @@ class ComposerStaticInitPPAuthors array ( 0 => __DIR__ . '/../..' . '/../src/core', ), + 'MultipleAuthorCategories\\' => + array ( + 0 => __DIR__ . '/../..' . '/../src/modules/author-categories/classes', + ), 'MultipleAuthorBoxes\\' => array ( 0 => __DIR__ . '/../..' . '/../src/modules/author-boxes/classes', diff --git a/lib/vendor/composer/installed.php b/lib/vendor/composer/installed.php index efc4e4c3..4eca4a50 100644 --- a/lib/vendor/composer/installed.php +++ b/lib/vendor/composer/installed.php @@ -1,9 +1,9 @@ array( 'name' => '__root__', - 'pretty_version' => 'dev-master', - 'version' => 'dev-master', - 'reference' => 'a755d706d1b463e9794fde928f9812e369bc7412', + 'pretty_version' => '1.0.0+no-version-set', + 'version' => '1.0.0.0', + 'reference' => NULL, 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -11,9 +11,9 @@ ), 'versions' => array( '__root__' => array( - 'pretty_version' => 'dev-master', - 'version' => 'dev-master', - 'reference' => 'a755d706d1b463e9794fde928f9812e369bc7412', + 'pretty_version' => '1.0.0+no-version-set', + 'version' => '1.0.0.0', + 'reference' => NULL, 'type' => 'library', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), diff --git a/src/assets/css/multiple-authors.css b/src/assets/css/multiple-authors.css index 8b10f943..e9453f82 100644 --- a/src/assets/css/multiple-authors.css +++ b/src/assets/css/multiple-authors.css @@ -159,6 +159,24 @@ ul.authors-list li.ui-sortable-helper { cursor: move; } +ul.authors-list.no-select { + user-select: none; +} + +.author-category-title { + color: #3c434a; + font-size: 14px; + padding: 8px 0; + font-weight: 1.4; + font-weight: 400; +} + +ul.authors-list li.sortable-placeholder { + border-style: dashed; + pointer-events: none; + user-select: none; +} + ul.authors-list.authors-current-user-can-assign li:hover { cursor: move; } diff --git a/src/assets/js/multiple-authors.js b/src/assets/js/multiple-authors.js index e3b22139..e7af43df 100644 --- a/src/assets/js/multiple-authors.js +++ b/src/assets/js/multiple-authors.js @@ -104,11 +104,12 @@ jQuery(document).ready(function ($) { }); authorsSearch.on("ppma_select2:select", function (e) { var template = wp.template("authors-author-partial"); - $(".authors-list").append( + $(".authors-list:first").append( window.htmlEnDeCode.htmlDecode(template(e.params.data)) ); authorsSearch.val(null).trigger("change"); handleUsersAuthorField(); + handleAuthorCategory(); }); }); } @@ -139,6 +140,19 @@ jQuery(document).ready(function ($) { } } + function handleAuthorCategory() { + let $authorsCategoryId = ''; + let $authorsCategoryTerm = ''; + $('.authors-list').each(function () { + $authorsCategoryId = $(this).attr('data-category_id'); + $(this).find('.author_categories').each(function () { + $authorsCategoryTerm = $(this).closest('li').find('.author_term').val(); + $(this).attr('name', 'author_categories[' + $authorsCategoryTerm + ']'); + $(this).val($authorsCategoryId); + }); + }); + } + function authorsUserSelect2(selector) { selector.each(function () { var authorsSearch = $(this).ppma_select2({ @@ -343,7 +357,22 @@ jQuery(document).ready(function ($) { }); function sortedAuthorsList(selector) { - selector.sortable().on("click", ".author-remove", function () { + selector.sortable({ + connectWith: ".authors-list", + items: "> li:not(.no-drag)", + placeholder: "sortable-placeholder", + update: function (event, ui) { + handleAuthorCategory(); + }, + receive: function (event, ui) { + $(this).find('.sortable-placeholder').hide(); + }, + remove: function (event, ui) { + if ($(this).children().length === 1) { + $(this).find('.sortable-placeholder').show(); + } + }, + }).on("click", ".author-remove", function () { var el = $(this); el.closest("li").remove(); handleUsersAuthorField($(this).parent('.authors-list')); diff --git a/src/core/Classes/Legacy/LegacyPlugin.php b/src/core/Classes/Legacy/LegacyPlugin.php index 1450dfa8..292cbe57 100644 --- a/src/core/Classes/Legacy/LegacyPlugin.php +++ b/src/core/Classes/Legacy/LegacyPlugin.php @@ -38,6 +38,7 @@ class LegacyPlugin public $helpers; public $class_names; public $shortcode_authors_list; + public $author_categories; private $options_group = 'multiple_authors_'; @@ -173,6 +174,7 @@ private function getModulesDirs() $defaultDirs = [ 'modules-settings' => PP_AUTHORS_MODULES_PATH, 'author-boxes' => PP_AUTHORS_MODULES_PATH, + 'author-categories' => PP_AUTHORS_MODULES_PATH, 'author-custom-fields' => PP_AUTHORS_MODULES_PATH, 'settings' => PP_AUTHORS_MODULES_PATH, 'multiple-authors' => PP_AUTHORS_MODULES_PATH, diff --git a/src/core/Classes/Post_Editor.php b/src/core/Classes/Post_Editor.php index 8d9c0752..df081d62 100644 --- a/src/core/Classes/Post_Editor.php +++ b/src/core/Classes/Post_Editor.php @@ -240,54 +240,94 @@ public static function render_authors_metabox() echo self::get_rendered_authors_selection($authors, false); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } + /** + * Group author into categories + * + * @param array $author_categories + * @param array $author_relations + * @param array $authors + * + * @return array + */ + public static function group_category_authors($author_categories, $author_relations, $authors) { + + // group authors by category slug + $grouped_authors = array_reduce($author_relations, function ($result, $item) { + $result[$item['category_slug']][] = $item; + return $result; + }, []); + + // List all authors attached to the post + $remaining_authors = $authors; + + $authors_data = []; + foreach ($author_categories as $author_category) { + if (!empty($remaining_authors) && !empty($grouped_authors) && isset($grouped_authors[$author_category['slug']])) { + // get current category term ids + $category_author_ids = array_column($grouped_authors[$author_category['slug']], 'author_term_id'); + // get selected authors for the category terms + $selected_authors = array_filter($remaining_authors, function ($author) use ($category_author_ids) { + $term_id = $author->term_id; + return in_array($term_id, $category_author_ids); + }); + // update remaining authors + $remaining_authors = array_filter($remaining_authors, function ($author) use ($category_author_ids) { + $term_id = $author->term_id; + return !in_array($term_id, $category_author_ids); + }); + } else { + $selected_authors = []; + } + + $authors_data[] = [ + 'title' => $author_category['plural_name'], + 'description' => sprintf('Drag and Drop Author to add them to %s category.', $author_category['plural_name']), + 'slug' => $author_category['slug'], + 'id' => $author_category['id'], + 'authors' => $selected_authors + ]; + } + + // Add remaining author to first category + if (!empty($remaining_authors)) { + $authors_data[0]['authors'] = array_merge($authors_data[0]['authors'], $remaining_authors); + } + + + return $authors_data; + } + /** * Get rendered authors selection. */ public static function get_rendered_authors_selection($authors, $showAvatars = true, $bulkEdit = false) { + $post = get_post(); + $classes = [ 'authors-list', ]; if (current_user_can(get_taxonomy('author')->cap->assign_terms)) { $classes[] = 'authors-current-user-can-assign'; } - ?> - - cap->assign_terms)) { - ?> + $author_categories = \MA_Author_Categories::get_author_categories(['category_status' => 1]); + + if (!empty($author_categories)) { + $author_relations = \MA_Author_Categories::get_author_relations(['post_id' => $post->ID]); + $author_categories_data = self::group_category_authors($author_categories, $author_relations, $authors); + } else { + $author_categories_data = []; + $author_categories_data[] = [ + 'title' => '', + 'description' => '', + 'slug' => '', + 'id' => '', + 'authors' => $authors + ]; + } + ?> + cap->assign_terms)) : ?> + + 0) { update_post_meta($post_id, 'ppma_disable_author_box', $disableAuthorBox); } diff --git a/src/core/Classes/Utils.php b/src/core/Classes/Utils.php index e87b967e..06c1660c 100644 --- a/src/core/Classes/Utils.php +++ b/src/core/Classes/Utils.php @@ -164,8 +164,9 @@ public static function convert_post_coauthors($post_id) * @param array $authors Bylines to set on the post. * @param bool $syncPostAuthor * @param int $fallbackUserId User ID for using as the author in case no author or if only guests are selected + * @param array $categories for authors */ - public static function set_post_authors($postId, $authors, $syncPostAuthor = true, $fallbackUserId = null) + public static function set_post_authors($postId, $authors, $syncPostAuthor = true, $fallbackUserId = null, $author_categories = []) { static::set_post_authors_name_meta($postId, $authors); @@ -174,6 +175,7 @@ public static function set_post_authors($postId, $authors, $syncPostAuthor = tru } $authors = wp_list_pluck($authors, 'term_id'); + \MA_Author_Categories::updatePostAuthorCategory($postId, $authors, $author_categories); wp_set_object_terms($postId, $authors, 'author'); } @@ -1261,4 +1263,41 @@ public static function get_author_display_name_select($term_id = 0) return $output; } + + + /** + * Secondary admin notices function for use with admin_notices hook. + * + * Constructs admin notice HTML. + * + * @param string $message Message to use in admin notice. Optional. Default empty string. + * @param bool $success Whether or not a success. Optional. Default true. + * @return mixed + */ + public static function admin_notices_helper($message = '', $success = true) + { + + $class = []; + $class[] = $success ? 'updated' : 'error'; + $class[] = 'notice is-dismissible'; + + $messagewrapstart = '

'; + + $messagewrapend = '

'; + + $action = ''; + + /** + * Filters the custom admin notice for ppma. + * + * + * @param string $value Complete HTML output for notice. + * @param string $action Action whose message is being generated. + * @param string $message The message to be displayed. + * @param string $messagewrapstart Beginning wrap HTML. + * @param string $messagewrapend Ending wrap HTML. + */ + return apply_filters('ppma_admin_notice', $messagewrapstart . $message . $messagewrapend, $action, $message, + $messagewrapstart, $messagewrapend); + } } diff --git a/src/core/Plugin.php b/src/core/Plugin.php index d06087e8..9c534c38 100644 --- a/src/core/Plugin.php +++ b/src/core/Plugin.php @@ -1923,6 +1923,7 @@ public function filterCMECapabilities($capabilities) 'ppma_manage_custom_fields', 'ppma_edit_post_authors', 'ppma_edit_own_profile', + 'ppma_manage_author_categories', ] ); diff --git a/src/modules/author-categories/assets/css/author-categories.css b/src/modules/author-categories/assets/css/author-categories.css new file mode 100644 index 00000000..3dd5cc62 --- /dev/null +++ b/src/modules/author-categories/assets/css/author-categories.css @@ -0,0 +1,29 @@ +#addauthorcategory .spinner { + float: none; +} + +table.authorcategories tr { + cursor: move; +} + +table.authorcategories td.column-primary { + color: #2271b1; +} + +table.authorcategories tr.inline-edit-row td { + padding-left: 20px !important; + padding-right: 20px !important; +} + +table.authorcategories .inline-edit-row fieldset label span.title, +table.authorcategories .inline-edit-row fieldset.inline-edit-date legend { + width: 20% !important; +} + +table.authorcategories .inline-edit-row .input-text-wrap input[type=text] { + width: 70% !important; +} + +table.authorcategories .inline-editor .inline-edit-col label { + margin-bottom: 10px !important; +} \ No newline at end of file diff --git a/src/modules/author-categories/assets/js/author-categories.js b/src/modules/author-categories/assets/js/author-categories.js new file mode 100644 index 00000000..f653b75e --- /dev/null +++ b/src/modules/author-categories/assets/js/author-categories.js @@ -0,0 +1,53 @@ +(function ($) { + 'use strict'; + + jQuery(document).ready(function ($) { + + /** + * New author category form submission + */ + $('#addauthorcategory').submit(function (event) { + event.preventDefault(); + var form = $(this); + var role_table = $('table.authorcategories tbody'); + + $('#ajax-response').empty(); + form.find('.spinner').addClass('is-active'); + form.find('#submit').attr('disabled', true); + + var data = { + action: "save_ppma_author_category", + category_name: form.find('#category-name').val(), + plural_name: form.find('#category-plural-name').val(), + enabled_category: form.find('#category-enabled-category').is(':checked') ? 1 : 0, + nonce: authorCategories.nonce, + }; + $.post(ajaxurl, data, function (response) { + var status = response.status; + var content = response.content; + var message = response.message; + + $('#ajax-response').empty(); + form.find('.spinner').removeClass('is-active'); + form.find('#submit').attr('disabled', false); + $('#ajax-response').append(message); + + if (status === 'success') { + role_table.prepend(content); + role_table.find('.no-items').remove(); + $('input[type="text"]:visible', form).val(''); + } else { + + } + }).fail(function (xhr, textStatus, errorThrown) { + form.find('.spinner').removeClass('is-active'); + form.find('#submit').attr('disabled', false); + }); + + return false; + + }); + + }); + +})(jQuery); \ No newline at end of file diff --git a/src/modules/author-categories/assets/js/category-reorder.js b/src/modules/author-categories/assets/js/category-reorder.js new file mode 100644 index 00000000..795e1f47 --- /dev/null +++ b/src/modules/author-categories/assets/js/category-reorder.js @@ -0,0 +1,114 @@ +(function ($) { + 'use strict'; + + jQuery(document).ready(function ($) { + /* global inlineEditTax, ajaxurl */ + + var sortable_categories_table = jQuery('.wp-list-table.authorcategories tbody'); + + sortable_categories_table.sortable({ + + // Settings + items: '> tr:not(.no-items)', + cursor: 'move', + axis: 'y', + cancel: '.inline-edit-row', + distance: 2, + opacity: 0.9, + tolerance: 'pointer', + scroll: true, + + /** + * Sort start + * + * @param {event} e + * @param {element} ui + * @returns {void} + */ + start: function (e, ui) { + + if (typeof (inlineEditTax) !== 'undefined') { + inlineEditTax.revert(); + } + + ui.placeholder.height(ui.item.height()); + ui.item.parent().parent().addClass('dragging'); + }, + + /** + * Sort dragging + * + * @param {event} e + * @param {element} ui + * @returns {void} + */ + helper: function (e, ui) { + + ui.children().each(function () { + jQuery(this).width(jQuery(this).width()); + }); + + return ui; + }, + + /** + * Sort dragging stopped + * + * @param {event} e + * @param {element} ui + * @returns {void} + */ + stop: function (e, ui) { + ui.item.children('.row-actions').show(); + ui.item.parent().parent().removeClass('dragging'); + }, + + /** + * Update the data in the database based on UI changes + * + * @param {event} e + * @param {element} ui + * @returns {void} + */ + update: function (e, ui) { + sortable_categories_table.sortable('disable').addClass('to-updating'); + + ui.item.addClass('to-row-updating'); + + //get categories id + var categories = []; + jQuery(".wp-list-table tbody > tr:not(.no-items)").each(function (e, i) { + var category_id = jQuery(this).attr('id'); + categories.push(category_id.replace(/\D/g, '')); + }); + + // Go do the sorting stuff via ajax + jQuery.post(ajaxurl, { + action: 'reorder_ppma_author_category', + categories: categories, + nonce: authorCategoriesReorder.nonce + }, term_order_update_callback); + } + }); + + /** + * Update the term order based on the ajax response + * + * @param {type} response + * @returns {void} + */ + function term_order_update_callback(response) { + + setTimeout(function () { + jQuery('.to-row-updating').removeClass('to-row-updating'); + }, 500); + + sortable_categories_table.removeClass('to-updating').sortable('enable'); + + return; + } + + + }); + +})(jQuery); \ No newline at end of file diff --git a/src/modules/author-categories/assets/js/inline-edit.js b/src/modules/author-categories/assets/js/inline-edit.js new file mode 100644 index 00000000..ac9e2b8b --- /dev/null +++ b/src/modules/author-categories/assets/js/inline-edit.js @@ -0,0 +1,292 @@ +/** + * This file is copied from WordPress (wp-admin/js/inline-edit-tax.js) + */ + +/* global ajaxurl, inlineEditAuthorCategory */ + +window.wp = window.wp || {}; + +/** + * Consists of functions relevant to the inline taxonomy editor. + * + * @namespace inlineEditAuthorCategory + * + * @property {string} type The type of inline edit we are currently on. + * @property {string} what The type property with a hash prefixed and a dash + * suffixed. + */ +(function ($, wp) { + + window.inlineEditAuthorCategory = { + + /** + * Initializes the inline taxonomy editor by adding event handlers to be able to + * quick edit. + * + * @since 2.7.0 + * + * @this inlineEditAuthorCategory + * @memberof inlineEditAuthorCategory + * @return {void} + */ + init: function () { + var t = this, row = $('#inline-edit'); + + t.type = $('#the-list').attr('data-wp-lists').substr(5); + t.what = '#' + t.type + '-'; + + $('#the-list').on('click', '.editinline', function () { + $(this).attr('aria-expanded', 'true'); + inlineEditAuthorCategory.edit(this, $(this)); + }); + + /** + * Cancels inline editing when pressing Escape inside the inline editor. + * + * @param {Object} e The keyup event that has been triggered. + */ + row.on('keyup', function (e) { + // 27 = [Escape]. + if (e.which === 27) { + return inlineEditAuthorCategory.revert(); + } + }); + + /** + * Cancels inline editing when clicking the cancel button. + */ + $('.cancel', row).on('click', function () { + return inlineEditAuthorCategory.revert(); + }); + + /** + * Saves the inline edits when clicking the save button. + */ + $('.ppma-inline-category-save', row).on('click', function () { + return inlineEditAuthorCategory.save(this); + }); + + /** + * Saves the inline edits when pressing Enter inside the inline editor. + */ + $('input, select', row).on('keydown', function (e) { + // 13 = [Enter]. + if (e.which === 13) { + return inlineEditAuthorCategory.save(this); + } + }); + + /** + * Saves the inline edits on submitting the inline edit form. + */ + $('#posts-filter input[type="submit"]').on('mousedown', function () { + t.revert(); + }); + }, + + /** + * Toggles the quick edit based on if it is currently shown or hidden. + * + * @since 2.7.0 + * + * @this inlineEditAuthorCategory + * @memberof inlineEditAuthorCategory + * + * @param {HTMLElement} el An element within the table row or the table row + * itself that we want to quick edit. + * @return {void} + */ + toggle: function (el) { + var t = this; + + $(t.what + t.getId(el)).css('display') === 'none' ? t.revert() : t.edit(el); + }, + + /** + * Shows the quick editor + * + * @since 2.7.0 + * + * @this inlineEditAuthorCategory + * @memberof inlineEditAuthorCategory + * + * @param {string|HTMLElement} id The ID of the term we want to quick edit or an + * element within the table row or the + * table row itself. + * @return {boolean} Always returns false. + */ + edit: function (id, element) { + var editRow, rowData, val, + t = this; + t.revert(); + + // Makes sure we can pass an HTMLElement as the ID. + if (typeof (id) === 'object') { + id = t.getId(id); + } + + var category_id = element.attr('data-category_id'); + var category_name = element.attr('data-category_name'); + var plural_name = element.attr('data-plural_name'); + var category_status = Number(element.attr('data-category_status')); + var enabled_category = category_status > 0 ? true : false; + + editRow = $('#inline-edit').clone(true), rowData = $('#inline_' + id); + $('td', editRow).attr('colspan', $('th:visible, td:visible', '.wp-list-table.widefat:first thead').length); + + + $(t.what + id).hide().after(editRow).after(''); + + $(':input[name="singular_name"]', editRow).val(category_name); + $(':input[name="plural_name"]', editRow).val(plural_name); + $(':input[name="enabled_category"]', editRow).prop('checked', enabled_category); + + + $(editRow).attr('id', 'edit-' + id).addClass('inline-editor').show(); + $('.singular_name', editRow).eq(0).trigger('focus'); + + return false; + }, + + /** + * Saves the quick edit data. + * + * Saves the quick edit data to the server and replaces the table row with the + * HTML retrieved from the server. + * + * @since 2.7.0 + * + * @this inlineEditAuthorCategory + * @memberof inlineEditAuthorCategory + * + * @param {string|HTMLElement} id The ID of the term we want to quick edit or an + * element within the table row or the + * table row itself. + * @return {boolean} Always returns false. + */ + save: function (id) { + var params, fields; + + // Makes sure we can pass an HTMLElement as the ID. + if (typeof (id) === 'object') { + id = this.getId(id); + } + + $('table.widefat .spinner').addClass('is-active'); + + params = { + action: 'edit_ppma_author_category', + category_id: id, + }; + + fields = $('#edit-' + id).find(':input').serialize(); + params = fields + '&' + $.param(params); + + // Do the Ajax request to save the data to the server. + $.post(ajaxurl, params, + /** + * Handles the response from the server + * + * Handles the response from the server, replaces the table row with the response + * from the server. + * + * @param {string} r The string with which to replace the table row. + */ + function (r) { + var row, new_id, option_value, + $errorNotice = $('#edit-' + id + ' .inline-edit-save .notice-error'), + $error = $errorNotice.find('.error'); + + $('table.widefat .spinner').removeClass('is-active'); + + if (r) { + if (-1 !== r.indexOf('. + */ + +use MultipleAuthors\Classes\Legacy\Module; +use MultipleAuthorCategories\AuthorCategoriesSchema; +use MultipleAuthorCategories\AuthorCategoriesTable; +use MultipleAuthors\Factory; + +/** + * class MA_Author_Categories + */ +class MA_Author_Categories extends Module +{ + + /** + * Instance of the module + * + * @var stdClass + */ + public $module; + public $module_url; + public $author_categories_table; + + public $module_name = 'author_categories'; + + const MENU_SLUG = 'ppma-author-categories'; + + /** + * Construct the MA_Multiple_Authors class + */ + public function __construct() + { + + global $hook_suffix; + + if (!isset($hook_suffix)) { + $hook_suffix = ''; + } + + $this->module_url = $this->get_module_url(__FILE__); + + // Register the module with PublishPress + $args = [ + 'title' => __('Author Categories', 'publishpress-authors'), + 'short_description' => __( + 'Add support for author categories.', + 'publishpress-authors' + ), + 'extended_description' => __( + 'Add support for author categories.', + 'publishpress-authors' + ), + 'module_url' => $this->module_url, + 'icon_class' => 'dashicons dashicons-edit', + 'slug' => 'author-categories', + 'default_options' => [ + 'enabled' => 'on', + ], + 'options_page' => false, + 'autoload' => true, + ]; + + // Apply a filter to the default options + $args['default_options'] = apply_filters('MA_Author_Categories_default_options', $args['default_options']); + + $legacyPlugin = Factory::getLegacyPlugin(); + + $this->module = $legacyPlugin->register_module($this->module_name, $args); + + parent::__construct(); + } + + /** + * Initialize the module. Conditionally loads if the module is enabled + */ + public function init() + { + add_action('pp_authors_install', [$this, 'runInstallTasks']); + add_action('pp_authors_upgrade', [$this, 'runUpgradeTasks']); + add_action('multiple_authors_admin_submenu', [$this, 'adminSubmenu'], 50); + add_action('admin_enqueue_scripts', [$this, 'enqueueAdminScripts']); + add_action('wp_ajax_save_ppma_author_category', [$this, 'handleNewCategory']); + add_action('wp_ajax_edit_ppma_author_category', [$this, 'handleEditCategory']); + add_action('wp_ajax_reorder_ppma_author_category', [$this, 'handleReOrderCategory']); + add_filter('removable_query_args', [$this, 'removableQueryArgs']); + add_action('delete_post', [$this, 'deleteAuthorCategoryRelation']); + } + + /** + * Enqueue Admin Scripts + * + * @return void + */ + public function enqueueAdminScripts() + { + + if (!isset($_GET['page']) || $_GET['page'] !== self::MENU_SLUG) { + return; + } + + $moduleAssetsUrl = PP_AUTHORS_URL . 'src/modules/author-categories/assets'; + + // Load jquery and jquery ui for sortable + wp_enqueue_script('jquery'); + wp_enqueue_script('jquery-ui-sortable'); + + // Inline edit script + wp_enqueue_script('author-categories-inline-edit', $moduleAssetsUrl . '/js/inline-edit.js', ['jquery'], PP_AUTHORS_VERSION); + + // Drag and drop re-order script + wp_enqueue_script('author-category-reorder', $moduleAssetsUrl . '/js/category-reorder.js', ['jquery', 'jquery-ui-sortable'], PP_AUTHORS_VERSION); + wp_localize_script( + 'author-category-reorder', + 'authorCategoriesReorder', + [ + 'nonce' => wp_create_nonce('author-categories-reorder-nonce') + ] + ); + + // Author category general script + wp_enqueue_script('author-categories-js', $moduleAssetsUrl . '/js/author-categories.js', ['jquery'], PP_AUTHORS_VERSION); + wp_localize_script( + 'author-categories-js', + 'authorCategories', + [ + 'nonce' => wp_create_nonce('author-categories-save-nonce') + ] + ); + + // Author category styles + wp_enqueue_style( + 'author-categories-css', + $moduleAssetsUrl . '/css/author-categories.css', + [], + PP_AUTHORS_VERSION + ); + } + + public function removableQueryArgs($args) { + + if (!isset($_GET['page']) || $_GET['page'] !== self::MENU_SLUG) { + return $args; + } + + return array_merge( + $args, + [ + 'action', + 'author_categories', + '_wpnonce' + ] + ); + + } + + /** + * Add the admin submenu. + */ + public function adminSubmenu() + { + + // Add the submenu to the PublishPress menu. + $hook = add_submenu_page( + \MA_Multiple_Authors::MENU_SLUG, + esc_html__('Author Categories', 'publishpress-authors'), + esc_html__('Author Categories', 'publishpress-authors'), + apply_filters('pp_multiple_authors_manage_categories_cap', 'ppma_manage_author_categories'), + self::MENU_SLUG, + [$this, 'manageAuthorCategories'], + 11 + ); + + add_action("load-$hook", [$this, 'screenOption']); + } + + /** + * Screen options + */ + public function screenOption() + { + + $option = 'per_page'; + $args = [ + 'label' => esc_html__('Number of items per page', 'publishpress-authors'), + 'default' => 20, + 'option' => 'author_categories_per_page' + ]; + + add_screen_option($option, $args); + + $this->author_categories_table = new AuthorCategoriesTable(); + } + + /** + * Handle a request to save author category + * + * @return mixed + */ + public function handleNewCategory() { + + $response['status'] = 'error'; + $response['content'] = ''; + $response_message = esc_html__('An error occured.', 'publishpress-authors'); + + if (empty($_POST['nonce']) + || !wp_verify_nonce(sanitize_key($_POST['nonce']), 'author-categories-save-nonce') + ) { + $response_message = esc_html__( + 'Security error. Kindly reload this page and try again', + 'publishpress-authors' + ); + } elseif (!current_user_can(apply_filters('pp_multiple_authors_manage_categories_cap', 'ppma_manage_author_categories'))) { + $response_message = esc_html__( + 'You do not have permission to perform this action', + 'publishpress-authors' + ); + } else { + + $category_name = isset($_POST['category_name']) ? sanitize_text_field($_POST['category_name']) : ''; + $plural_name = isset($_POST['plural_name']) ? sanitize_text_field($_POST['plural_name']) : ''; + $enabled_category = isset($_POST['enabled_category']) ? intval($_POST['enabled_category']) : 0; + $slug = sanitize_title($category_name); + + if (empty($category_name)) { + $response_message = esc_html__( + 'Singular name is required.', + 'publishpress-authors' + ); + } elseif (empty($plural_name)) { + $response_message = esc_html__( + 'Plural name is required.', + 'publishpress-authors' + ); + } elseif(!empty(self::get_author_categories(['slug' => $slug]))) { + $response_message = esc_html__( + 'Author category with this name already exist.', + 'publishpress-authors' + ); + } else { + $category_args = [ + 'category_name' => $category_name, + 'plural_name' => $plural_name, + 'slug' => $slug, + 'category_order' => 0, + 'category_status' => $enabled_category, + 'created_at' => current_time('mysql', true) + ]; + $added_category = $this->addAuthorCategory($category_args); + if ($added_category && !empty($added_category)) { + $response_message = esc_html__( + 'Category added.', + 'publishpress-authors' + ); + $response['status'] = 'success'; + + ob_start(); + + $ajax_author_categories_table = new AuthorCategoriesTable(); + $ajax_author_categories_table->single_row($added_category); + $response['content'] = ob_get_clean(); + + } else { + $response_message = esc_html__( + 'Error inserting new author category.', + 'publishpress-authors' + ); + } + + } + + } + + $response['message'] = '

'. $response_message .'

'; + wp_send_json($response); + } + + /** + * Handle a request to save author category. + * Copied from WordPress wp_ajax_inline_save_tax + * to match the Quick Edit + * + * @return mixed + */ + public function handleEditCategory() { + + if (empty($_POST['nonce']) + || !wp_verify_nonce(sanitize_key($_POST['nonce']), 'ppma-category-inline-edit-nonce') + ) { + wp_die(esc_html__( + 'Security error. Kindly reload this page and try again', + 'publishpress-authors' + )); + } elseif (!current_user_can(apply_filters('pp_multiple_authors_manage_categories_cap', 'ppma_manage_author_categories'))) { + wp_die(esc_html__( + 'You do not have permission to perform this action', + 'publishpress-authors' + )); + } else { + + $category_name = isset($_POST['singular_name']) ? sanitize_text_field($_POST['singular_name']) : ''; + $plural_name = isset($_POST['plural_name']) ? sanitize_text_field($_POST['plural_name']) : ''; + $category_status = isset($_POST['enabled_category']) ? intval($_POST['enabled_category']) : 0; + $category_id = isset($_POST['category_id']) ? intval($_POST['category_id']) : 0; + $slug = sanitize_title($category_name); + $existing_category = self::get_author_categories(['slug' => $slug]); + if (empty($category_name) || empty($plural_name) || empty($category_id)) { + wp_die(esc_html__( + 'All fields are required.', + 'publishpress-authors' + )); + } elseif (!empty($existing_category) && (int)$existing_category['id'] !== (int)$category_id) { + wp_die(esc_html__( + 'Author category with this name already exist.', + 'publishpress-authors' + )); + } else { + + $category_args = [ + 'category_name' => $category_name, + 'plural_name' => $plural_name, + 'slug' => $slug, + 'category_status' => $category_status + ]; + $edited_category = $this->editAuthorCategory($category_args, $category_id); + if ($edited_category && !empty($edited_category)) { + ob_start(); + + $ajax_author_categories_table = new AuthorCategoriesTable(); + $ajax_author_categories_table->single_row($edited_category); + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo ob_get_clean(); + wp_die(); + } else { + wp_die(esc_html__( + 'Error updating category data.', + 'publishpress-authors' + )); + } + } + + } + wp_die(esc_html__('An error occured.', 'publishpress-authors')); + } + + public function handleReOrderCategory() { + + + $response['status'] = 'error'; + $response['content'] = ''; + $response_message = esc_html__('An error occured.', 'publishpress-authors'); + + if (empty($_POST['nonce']) + || !wp_verify_nonce(sanitize_key($_POST['nonce']), 'author-categories-reorder-nonce') + ) { + $response_message = esc_html__( + 'Security error. Kindly reload this page and try again.', + 'publishpress-authors' + ); + } elseif (empty($_POST['categories'])) { + $response_message = esc_html__( + 'Invalid form data.', + 'publishpress-authors' + ); + } else { + $categories = array_map('intval', $_POST['categories']); + + foreach($categories as $key => $category) { + $this->editAuthorCategory(['category_order' => ($key + 1)], $category); + } + + $response_message = esc_html__( + 'Category order updated.', + 'publishpress-authors' + ); + $response['status'] = 'success'; + } + + $response['message'] = '

'. $response_message .'

'; + wp_send_json($response); + } + + /** + * Add new author category + * + * @param array $insert_args + * + * @return array|bool + */ + private function addAuthorCategory($insert_args) { + global $wpdb; + + $table_name = AuthorCategoriesSchema::tableName(); + + $wpdb->insert( + $table_name, + $insert_args + ); + + $category_id = $wpdb->insert_id; + + if ((int) $category_id > 0) { + return self::get_author_categories(['id' => $category_id]); + } else { + return false; + } + } + + /** + * Edit author category + * + * @param array $edit_args + * @param integer $id + * + * @return array|bool + */ + private function editAuthorCategory($edit_args, $id) { + global $wpdb; + + if (!current_user_can(apply_filters('pp_multiple_authors_manage_categories_cap', 'ppma_manage_author_categories'))) { + return false; + } + + $table_name = AuthorCategoriesSchema::tableName(); + + $wpdb->update( + $table_name, + $edit_args, + [ + 'id' => $id + ] + ); + + return self::get_author_categories(['id' => $id]); + } + + /** + * Update post author category + * + * @param integer $post_id + * @param array $authors + * @param array $post_author_categories + * + * @return void + */ + public static function updatePostAuthorCategory($post_id, $authors, $post_author_categories) { + global $wpdb; + + if (!current_user_can(get_taxonomy('author')->cap->assign_terms) || empty(array_filter(array_values($post_author_categories)))) { + return; + } + + + $all_author_categories = \MA_Author_Categories::get_author_categories([]); + $all_author_category_ids = array_column($all_author_categories, 'id'); + + // Make 'id' the array index + $all_author_categories = array_combine($all_author_category_ids, $all_author_categories); + + $table_name = AuthorCategoriesSchema::relationTableName(); + + // Make sure there's no relationship for authors that could have been possibly removed + $wpdb->delete($table_name, ['post_id' => $post_id], ['%d']); + + if (!empty($authors)) { + foreach ($authors as $author) { + if (isset($post_author_categories[$author])) { + $category_id = $post_author_categories[$author]; + $wpdb->insert( + $table_name, + [ + 'category_id' => $all_author_categories[$category_id]['id'], + 'category_slug' => $all_author_categories[$category_id]['slug'], + 'post_id' => $post_id, + 'author_term_id' => $author, + ], + [ + '%d', + '%s', + '%d', + '%d', + ] + ); + } + } + } + } + + + /** + * Get author categories + * + * @param array $args + * + * @return array|integer + */ + public static function get_author_categories($args = []) { + global $wpdb; + + $default_args = [ + 'paged' => 1, + 'limit' => 20, + 'id' => 0, + 'slug' => '', + 'category_name' => '', + 'plural_name' => '', + 'search' => '', + 'category_status' => '', + 'orderby' => 'category_order', + 'order' => 'ASC', + 'count_only' => false, + ]; + + $args = wp_parse_args($args, $default_args); + + $table_name = AuthorCategoriesSchema::tableName(); + + $paged = intval($args['paged']); + $limit = intval($args['limit']); + $id = intval($args['id']); + $slug = sanitize_text_field($args['slug']); + $category_name = sanitize_text_field($args['category_name']); + $plural_name = sanitize_text_field($args['plural_name']); + $orderby = sanitize_text_field($args['orderby']); + $search = sanitize_text_field($args['search']); + $order = strtoupper(sanitize_text_field($args['order'])); + $category_status = sanitize_text_field($args['category_status']); + $count_only = boolval($args['count_only']); + + $field_search = $field_value = false; + if (!empty($id)) { + $field_search = 'id'; + $field_value = $id; + } elseif (!empty($slug)) { + $field_search = 'slug'; + $field_value = $slug; + } elseif (!empty($category_name)) { + $field_search = 'category_name'; + $field_value = $category_name; + } elseif (!empty($plural_name)) { + $field_search = 'plural_name'; + $field_value = $plural_name; + } + + $category_results = []; + if ($field_search) { + $query = $wpdb->prepare( + "SELECT * FROM {$table_name} WHERE {$field_search} = %s ORDER BY {$orderby} {$order} LIMIT 1", + $field_value + ); + $category_results = $wpdb->get_row($query, \ARRAY_A); + } else { + + $offset = ($paged - 1) * $limit; + + $query = "SELECT * FROM {$table_name} WHERE 1=1"; + + if (!empty($search)) { + $query .= $wpdb->prepare( + " AND (slug LIKE '%%%s%%' OR category_name LIKE '%%%s%%' OR plural_name LIKE '%%%s%%')", + $search, + $search, + $search + ); + } + + if ($category_status !== '') { + $query .= $wpdb->prepare( + " AND category_status = %d", + $category_status + ); + } + + if ($count_only) { + $query = str_replace("SELECT *", "SELECT COUNT(*)", $query); + return $wpdb->get_var($query); + } + + $query .= $wpdb->prepare( + " ORDER BY {$orderby} {$order} LIMIT %d OFFSET %d", + $limit, + $offset + ); + + $category_results = $wpdb->get_results($query, \ARRAY_A); + } + + return $category_results; + } + + /** + * Get author relationship + * + * @param array $args + * + * @return array|integer + */ + public static function get_author_relations($args = []) { + global $wpdb; + + $default_args = [ + 'post_id' => '', + ]; + + $args = wp_parse_args($args, $default_args); + + $post_id = intval($args['post_id']); + + $table_name = AuthorCategoriesSchema::relationTableName(); + + $sql = "SELECT * FROM $table_name WHERE 1=1"; + + // Add conditions based on provided arguments + if (!empty($post_id)) { + $sql .= $wpdb->prepare(" AND post_id = %d", $post_id); + } + + $results = $wpdb->get_results($sql, ARRAY_A); + + return $results; + } + + + + /** + * Author categories callback + * + * @return void + */ + public function manageAuthorCategories() { + + $this->author_categories_table->prepare_items(); + ?> + +
+ +

+ '; + printf( + /* translators: %s: Search query. */ + __( 'Search results for: %s' ), + '' . esc_html( wp_unslash( $_REQUEST['s'] ) ) . '' + ); + echo ''; + } + ?> +
+
+ + +
+ author_categories_table->search_box(esc_html__('Search Author Categories', 'publishpress-authors'), 'author-categories'); ?> +
+ +
+
+
+
+

+
+
+ + +

+
+
+ + +

+
+
+ +
+

+ + +

+
+
+
+
+ +
+
+
+ author_categories_table->display(); ?> +
+
+
+
+ +
+ author_categories_table->inline_edit(); ?> + esc_html__('Author', 'publishpress-authors'), + 'plural_name' => esc_html__('Authors', 'publishpress-authors'), + 'slug' => sanitize_title(esc_html__('Author', 'publishpress-authors')), + 'category_order' => 1, + 'category_status' => 1, + 'created_at' => current_time('mysql', true) + ]; + + $default_categories[] = [ + 'category_name' => esc_html__('Co Author', 'publishpress-authors'), + 'plural_name' => esc_html__('Co Authors', 'publishpress-authors'), + 'slug' => sanitize_title(esc_html__('Co Author', 'publishpress-authors')), + 'category_order' => 2, + 'category_status' => 1, + 'created_at' => current_time('mysql', true) + ]; + + $default_categories[] = [ + 'category_name' => esc_html__('Contributor', 'publishpress-authors'), + 'plural_name' => esc_html__('Contributors', 'publishpress-authors'), + 'slug' => sanitize_title(esc_html__('Contributor', 'publishpress-authors')), + 'category_order' => 3, + 'category_status' => 0, + 'created_at' => current_time('mysql', true) + ]; + + $default_categories[] = [ + 'category_name' => esc_html__('Written by', 'publishpress-authors'), + 'plural_name' => esc_html__('Written by', 'publishpress-authors'), + 'slug' => sanitize_title(esc_html__('Written by', 'publishpress-authors')), + 'category_order' => 4, + 'category_status' => 0, + 'created_at' => current_time('mysql', true) + ]; + + $default_categories[] = [ + 'category_name' => esc_html__('Reviewed by', 'publishpress-authors'), + 'plural_name' => esc_html__('Reviewed by', 'publishpress-authors'), + 'slug' => sanitize_title(esc_html__('Reviewed by', 'publishpress-authors')), + 'category_order' => 5, + 'category_status' => 0, + 'created_at' => current_time('mysql', true) + ]; + + $default_categories[] = [ + 'category_name' => esc_html__('Updated by', 'publishpress-authors'), + 'plural_name' => esc_html__('Updated by', 'publishpress-authors'), + 'slug' => sanitize_title(esc_html__('Updated by', 'publishpress-authors')), + 'category_order' => 6, + 'category_status' => 0, + 'created_at' => current_time('mysql', true) + ]; + + foreach ($default_categories as $default_category) { + $this->addAuthorCategory($default_category); + } + } + + /** + * Delete author category relation for post id + */ + public function deleteAuthorCategoryRelation($post_id) { + global $wpdb; + + $table_name = AuthorCategoriesSchema::relationTableName(); + + $wpdb->delete($table_name, ['post_id' => $post_id], ['%d']); + } + + /** + * Runs methods when the plugin is running for the first time. + * + * @param string $currentVersion + * + * @param void + */ + public function runInstallTasks($currentVersion) { + + // add new table + AuthorCategoriesSchema::createTableIfNotExists(); + AuthorCategoriesSchema::createRelationTableIfNotExists(); + + // add default author category + $this->insertDefaultCategories(); + + // add author category capability + $capability_roles = ['administrator', 'editor', 'author', 'contributor']; + foreach ($capability_roles as $capability_role) { + $role = get_role($capability_role); + if ($role instanceof \WP_Role) { + $role->add_cap('ppma_manage_author_categories'); + } + } + + } + + /** + * Runs methods when the plugin is being upgraded to a most recent version. + * + * @param string $currentVersion + * + * @param void + */ + public function runUpgradeTasks($currentVersion) { + if (version_compare($currentVersion, '4.3.0', '<')) { + $this->runInstallTasks($currentVersion); + } + } +} diff --git a/src/modules/author-categories/classes/AuthorCategoriesSchema.php b/src/modules/author-categories/classes/AuthorCategoriesSchema.php new file mode 100644 index 00000000..81e71d8a --- /dev/null +++ b/src/modules/author-categories/classes/AuthorCategoriesSchema.php @@ -0,0 +1,137 @@ + + * @copyright Copyright (C) 2018 PublishPress. All rights reserved. + * @license GPLv2 or later + * @since 4.3.0 + */ + +namespace MultipleAuthorCategories; + +/** + * AuthorCategoriesSchema + * + * @package MultipleAuthorCategories\Classes + * + */ +class AuthorCategoriesSchema +{ + /** + * Author categories table name + * + * @return string + */ + public static function tableName() + { + global $wpdb; + + return $wpdb->prefix . 'ppma_author_categories'; + } + + /** + * Author categories relationship table name + * + * @return string + */ + public static function relationTableName() + { + global $wpdb; + + return $wpdb->prefix . 'ppma_author_relationships'; + } + + /** + * Check if a table exists + * + * @param string $table_name + * @return bool + */ + public static function tableExists($table_name) + { + global $wpdb; + + return $wpdb->get_var("SHOW TABLES LIKE '{$table_name}'") === $table_name; + } + + /** + * Create author categories table if not exist + * + * @return void + */ + public static function createTableIfNotExists() + { + global $wpdb; + + $table_name = self::tableName(); + + if (!self::tableExists($table_name)) { + $charset_collate = $wpdb->get_charset_collate(); + + $sql = "CREATE TABLE {$table_name} ( + id bigint(20) unsigned NOT NULL auto_increment, + category_name varchar(200) NOT NULL default '', + plural_name varchar(200) NOT NULL default '', + slug varchar(200) NOT NULL default '', + category_order int(11) NOT NULL default 0, + category_status int(11) NOT NULL default 1, + created_at datetime NOT NULL, + meta_data longtext NOT NULL default '', + PRIMARY KEY (id), + KEY category_name (category_name), + KEY plural_name (plural_name), + KEY slug (slug) + ) $charset_collate;"; + + self::createTable($sql); + } + } + + /** + * Create author categories relationship table if not exist + * + * @return void + */ + public static function createRelationTableIfNotExists() + { + global $wpdb; + + $table_name = self::relationTableName(); + + if (!self::tableExists($table_name)) { + $charset_collate = $wpdb->get_charset_collate(); + + $sql = "CREATE TABLE {$table_name} ( + id bigint(20) unsigned NOT NULL auto_increment, + category_id bigint(20) unsigned NOT NULL, + category_slug varchar(200) NOT NULL default '', + post_id bigint(20) unsigned NOT NULL, + author_term_id bigint(20) unsigned NOT NULL default '0', + author_user_id bigint(20) unsigned NOT NULL default '0', + PRIMARY KEY (id), + KEY category_id (category_id), + KEY category_slug (category_slug), + KEY post_id (post_id), + KEY author_term_id (author_term_id), + KEY author_user_id (author_user_id) + ) $charset_collate;"; + + self::createTable($sql); + } + } + + /** + * Create new table + * + * @param string $sql + */ + private static function createTable($sql) + { + if (!function_exists('dbDelta')) { + require_once ABSPATH . 'wp-admin/includes/upgrade.php'; + } + + dbDelta($sql); + } +} diff --git a/src/modules/author-categories/classes/AuthorCategoriesTable.php b/src/modules/author-categories/classes/AuthorCategoriesTable.php new file mode 100644 index 00000000..077e90d5 --- /dev/null +++ b/src/modules/author-categories/classes/AuthorCategoriesTable.php @@ -0,0 +1,471 @@ + + * @copyright Copyright (C) 2018 PublishPress. All rights reserved. + * @license GPLv2 or later + * @since 4.3.0 + */ + +namespace MultipleAuthorCategories; + +use MultipleAuthorCategories\AuthorCategoriesSchema; +use MultipleAuthors\Classes\Utils; + +if (!class_exists('WP_List_Table')) { + require_once(ABSPATH . 'wp-admin/includes/class-wp-list-table.php'); +} + +class AuthorCategoriesTable extends \WP_List_Table +{ + + /** Class constructor */ + public function __construct() + { + + parent::__construct([ + 'singular' => esc_html__('Author Category', 'publishpress-authors'), + 'plural' => esc_html__('Author Categories', 'publishpress-authors'), + 'ajax' => true + ]); + } + + public function author_categories_data($count = false) + { + + $search = (!empty($_REQUEST['s'])) ? sanitize_text_field($_REQUEST['s']) : ''; + $orderby = (!empty($_REQUEST['orderby'])) ? sanitize_text_field($_REQUEST['orderby']) : 'category_order'; + $order = (!empty($_REQUEST['order'])) ? sanitize_text_field($_REQUEST['order']) : 'ASC'; + + $items_per_page = $this->get_items_per_page('author_categories_per_page', 20); + $page = $this->get_pagenum(); + + + if ($count) { + $author_categories = \MA_Author_Categories::get_author_categories(['count_only' => true]); + } else { + $author_categories = \MA_Author_Categories::get_author_categories(['paged' => $page, 'limit' => $items_per_page, 'search' => $search, 'orderby' => $orderby, 'order' => $order]); + } + + return $author_categories; + } + + /** + * Show single row item + * + * @param array $item + */ + public function single_row($item) + { + $class = ['ppma-category-tr']; + $id = 'authorcategory-' . $item['id'] . ''; + echo sprintf('', esc_attr($id), esc_attr(implode(' ', $class))); + $this->single_row_columns($item); + echo ''; + } + + /** + * Associative array of columns + * + * @return array + */ + function get_columns() + { + $columns = [ + 'cb' => '', + 'category_name' => esc_html__('Name', 'publishpress-authors'), + 'plural_name' => esc_html__('Plural Name', 'publishpress-authors'), + 'category_status' => esc_html__('Enable Category', 'publishpress-authors'), + 'created_at' => esc_html__('Date', 'publishpress-authors') + ]; + + return $columns; + } + + /** + * Columns to make sortable. + * + * @return array + */ + protected function get_sortable_columns() + { + $sortable_columns = [ + 'category_name' => ['category_name', true], + 'plural_name' => ['plural_name', true], + 'category_status' => ['category_status', true], + 'created_at' => ['created_at', true], + ]; + + return $sortable_columns; + } + + /** + * Render the bulk edit checkbox + * + * @param array $item + * + * @return string + */ + function column_cb($item) + { + return sprintf('', 'author_categories', $item['id']); + } + + /** + * Get the bulk actions to show in the top page dropdown + * + * @return array + */ + protected function get_bulk_actions() + { + $actions = [ + 'ppma-enable-author-categories' => esc_html__('Enable Categories', 'publishpress-authors'), + 'ppma-disable-author-categories' => esc_html__('Disable Categories', 'publishpress-authors'), + 'ppma-delete-author-categories' => esc_html__('Delete', 'publishpress-authors') + ]; + + return $actions; + } + + /** + * Process bulk actions + */ + public function process_bulk_action() + { + + $query_arg = '_wpnonce'; + $action = 'bulk-' . $this->_args['plural']; + $checked = isset($_REQUEST[$query_arg]) ? wp_verify_nonce(sanitize_key($_REQUEST[$query_arg]), $action) : false; + + if (!$checked || !current_user_can(apply_filters('pp_multiple_authors_manage_categories_cap', 'ppma_manage_author_categories'))) { + return; + } + + if ($this->current_action() === 'ppma-delete-author-categories' && !empty($_REQUEST['author_categories'])) { + $author_categories = array_map('sanitize_text_field', (array) $_REQUEST['author_categories']); + if (!empty($author_categories)) { + foreach ($author_categories as $author_category) { + $this->deleteAuthorCategory($author_category); + } + if (count($author_categories) > 1) { + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo Utils::admin_notices_helper(esc_html__('Author categories deleted successfully.', 'publishpress-authors'), false); + } else { + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo Utils::admin_notices_helper(esc_html__('Author category deleted successfully.', 'publishpress-authors'), false); + } + } + } elseif ($this->current_action() === 'ppma-enable-author-categories' && !empty($_REQUEST['author_categories'])) { + $author_categories = array_map('sanitize_text_field', (array) $_REQUEST['author_categories']); + if (!empty($author_categories)) { + foreach ($author_categories as $author_category) { + $this->editAuthorCategory(['category_status' => 1], $author_category); + } + if (count($author_categories) > 1) { + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo Utils::admin_notices_helper(esc_html__('Author categories enabled.', 'publishpress-authors'), true); + } else { + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo Utils::admin_notices_helper(esc_html__('Author category enabled.', 'publishpress-authors'), true); + } + } + } elseif ($this->current_action() === 'ppma-disable-author-categories' && !empty($_REQUEST['author_categories'])) { + $author_categories = array_map('sanitize_text_field', (array) $_REQUEST['author_categories']); + if (!empty($author_categories)) { + foreach ($author_categories as $author_category) { + $this->editAuthorCategory(['category_status' => 0], $author_category); + } + if (count($author_categories) > 1) { + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo Utils::admin_notices_helper(esc_html__('Author categories disabled.', 'publishpress-authors'), true); + } else { + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + echo Utils::admin_notices_helper(esc_html__('Author category disabled.', 'publishpress-authors'), true); + } + } + } + } + + /** + * Delete author category + * + * @param integer $category_id + * + * @return mixed + */ + private function deleteAuthorCategory($category_id) + { + global $wpdb; + + if (!current_user_can(apply_filters('pp_multiple_authors_manage_categories_cap', 'ppma_manage_author_categories'))) { + return false; + } + + $table_name = AuthorCategoriesSchema::tableName(); + + $delete = $wpdb->query( + $wpdb->prepare( + "DELETE FROM {$table_name} WHERE id = %d", + $category_id + ) + ); + + return $delete; + } + + /** + * Edit author category + * + * @param array $edit_args + * @param integer $id + * + * @return array|bool + */ + private function editAuthorCategory($edit_args, $id) + { + global $wpdb; + + if (!current_user_can(apply_filters('pp_multiple_authors_manage_categories_cap', 'ppma_manage_author_categories'))) { + return false; + } + + $table_name = AuthorCategoriesSchema::tableName(); + + $wpdb->update( + $table_name, + $edit_args, + [ + 'id' => $id + ] + ); + + return self::get_author_categories(['id' => $id]); + } + + /** + * Render a column when no column specific method exist. + * + * @param array $item + * @param string $column_name + * + * @return mixed + */ + public function column_default($item, $column_name) + { + return !empty($item[$column_name]) ? $item[$column_name] : '—'; + } + + /** Text displayed when no stterm data is available */ + public function no_items() + { + esc_html_e('No author categories.', 'publishpress-authors'); + } + + /** + * Displays the search box. + * + * @param string $text The 'submit' button label. + * @param string $input_id ID attribute value for the search input field. + * + * + */ + public function search_box($text, $input_id) + { + + $input_id = $input_id . '-search-input'; + + if (!empty($_REQUEST['orderby'])) { + echo ''; + } + if (!empty($_REQUEST['order'])) { + echo ''; + } + if (!empty($_REQUEST['page'])) { + echo ''; + } + ?> + _column_headers = $this->get_column_info(); + $this->process_bulk_action(); + + /** + * First, lets decide how many records per page to show + */ + $per_page = $this->get_items_per_page('author_categories_per_page', 20); + + /** + * Fetch the data + */ + $data = $this->author_categories_data(); + + $total_items = $this->author_categories_data(true); + + /** + * Now we can add the data to the items property, where it can be used by the rest of the class. + */ + $this->items = $data; + + /** + * We also have to register our pagination options & calculations. + */ + $this->set_pagination_args([ + 'total_items' => $total_items, //calculate the total number of items + 'per_page' => $per_page, //determine how many items to show on a page + 'total_pages' => ceil($total_items / $per_page) //calculate the total number of pages + ]); + } + + /** + * Generates and display row actions links for the list table. + * + * @param object $item The item being acted upon. + * @param string $column_name Current column name. + * @param string $primary Primary column name. + * + * @return string The row actions HTML, or an empty string if the current column is the primary column. + */ + protected function handle_row_actions($item, $column_name, $primary) + { + //Build row actions + $actions = []; + + if (current_user_can(apply_filters('pp_multiple_authors_manage_categories_cap', 'ppma_manage_author_categories'))) { + $actions['inline hide-if-no-js'] = sprintf( + '', + /* translators: %s: Taxonomy term name. */ + esc_attr(sprintf(esc_html__('Quick edit “%s” inline', 'publishpress-authors'), $item['category_name'])), + esc_html__('Quick Edit', 'publishpress-authors') + ); + + $actions['delete'] = sprintf( + '%s', + add_query_arg( + [ + 'page' => \MA_Author_Categories::MENU_SLUG, + 'action' => 'ppma-delete-author-categories', + 'author_categories' => esc_attr($item['id']), + '_wpnonce' => wp_create_nonce('bulk-' . $this->_args['plural']) + ], + admin_url('admin.php') + ), + esc_html__('Delete', 'publishpress-authors') + ); + } + + return $column_name === $primary ? $this->row_actions($actions, false) : ''; + } + + /** + * Method for category_status column + * + * @param array $item + * + * @return string + */ + protected function column_category_status($item) + { + if (!empty($item['category_status'])) { + return '
' . esc_html__('Enabled', 'publishpress-authors') . '
'; + } else { + return '
' . esc_html__('Disabled', 'publishpress-authors') . '
'; + } + } + + /** + * Method for category_name column + * + * @param array $item + * + * @return string + */ + protected function column_category_name($item) + { + + $title = sprintf( + '%1$s', + esc_html($item['category_name']) + ); + + return $title; + } + + /** + * The action column + * + * @param $item + * + * @return string + */ + protected function column_created_at($item) + { + return date_i18n('Y/m/d \a\t g:i a', strtotime($item['created_at'])); + } + + /** + * Outputs the hidden row displayed when inline editing + * + * @since 3.1.0 + */ + public function inline_edit() + { + ?> + +
+ + + + + + + + +
+
null, 'edit.php?post_type=ppma_boxes' => null, 'edit-tags.php?taxonomy=author' => null, 'edit.php?post_type=ppmacf_field' => null, @@ -422,6 +423,13 @@ public function filter_custom_menu_order($menu_ord) unset($currentSubmenu[$itemsToSort['edit-tags.php?taxonomy=author']]); } + // Author Categories + if (isset($itemsToSort['ppma-author-categories'])) { + $newSubmenu[] = $currentSubmenu[$itemsToSort['ppma-author-categories']]; + + unset($currentSubmenu[$itemsToSort['ppma-author-categories']]); + } + // Author Boxes if (isset($itemsToSort['edit.php?post_type=ppma_boxes'])) { $newSubmenu[] = $currentSubmenu[$itemsToSort['edit.php?post_type=ppma_boxes']];