From 6abeb7f4a82ee474fda4e05c5a2a53d04b100d43 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Wed, 10 Sep 2025 17:43:11 +0530 Subject: [PATCH 01/25] Remove wp-hookdoc with other deps. --- package-lock.json | 315 +--------------------------------------------- package.json | 5 +- 2 files changed, 2 insertions(+), 318 deletions(-) diff --git a/package-lock.json b/package-lock.json index 73c16074c..4357ab8f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,11 +19,8 @@ "cypress": "^13.1.0", "cypress-mochawesome-reporter": "^3.5.1", "eslint-plugin-cypress": "^2.12.1", - "jsdoc": "^4.0.2", "mochawesome-json-to-md": "^0.7.2", - "node-wp-i18n": "^1.2.6", - "taffydb": "^2.7.3", - "wp-hookdoc": "^0.2.0" + "node-wp-i18n": "^1.2.6" }, "engines": { "node": ">=12.13.0" @@ -3327,18 +3324,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@jsdoc/salty": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.7.tgz", - "integrity": "sha512-mh8LbS9d4Jq84KLw8pzho7XC2q2/IJGiJss3xwRoLD1A+EE16SjN4PfaG4jRCzKegTFLlN0Zd8SdUPE6XdoPFg==", - "dev": true, - "dependencies": { - "lodash": "^4.17.21" - }, - "engines": { - "node": ">=v12.0.0" - } - }, "node_modules/@kwsites/file-exists": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", @@ -4918,28 +4903,6 @@ "@types/node": "*" } }, - "node_modules/@types/linkify-it": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", - "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", - "dev": true - }, - "node_modules/@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", - "dev": true, - "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" - } - }, - "node_modules/@types/mdurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", - "dev": true - }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -7601,18 +7564,6 @@ "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true }, - "node_modules/catharsis": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", - "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", - "dev": true, - "dependencies": { - "lodash": "^4.17.15" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -14274,50 +14225,12 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/js2xmlparser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", - "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", - "dev": true, - "dependencies": { - "xmlcreate": "^2.0.4" - } - }, "node_modules/jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true }, - "node_modules/jsdoc": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", - "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.15", - "@jsdoc/salty": "^0.2.1", - "@types/markdown-it": "^12.2.3", - "bluebird": "^3.7.2", - "catharsis": "^0.9.0", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", - "klaw": "^3.0.0", - "markdown-it": "^12.3.2", - "markdown-it-anchor": "^8.4.1", - "marked": "^4.0.10", - "mkdirp": "^1.0.4", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.1.0", - "underscore": "~1.13.2" - }, - "bin": { - "jsdoc": "jsdoc.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/jsdoc-type-pratt-parser": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz", @@ -14327,27 +14240,6 @@ "node": ">=12.0.0" } }, - "node_modules/jsdoc/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jsdoc/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/jsdom": { "version": "20.0.3", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", @@ -14542,15 +14434,6 @@ "node": ">=0.10.0" } }, - "node_modules/klaw": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", - "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.9" - } - }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -15235,16 +15118,6 @@ "markdown-it": "bin/markdown-it.js" } }, - "node_modules/markdown-it-anchor": { - "version": "8.6.5", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.5.tgz", - "integrity": "sha512-PI1qEHHkTNWT+X6Ip9w+paonfIQ+QZP9sCeMYi47oqhH+EsW8CrJ8J7CzV19QVOj6il8ATGbK2nTECj22ZHGvQ==", - "dev": true, - "peerDependencies": { - "@types/markdown-it": "*", - "markdown-it": "*" - } - }, "node_modules/markdown-it/node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -15341,18 +15214,6 @@ "integrity": "sha512-oEacRUVeTJ5D5hW1UYd2qExYI0oELdYK72k1TKGvIeYJIbqQWAz476NAc7LNixSySUhcNl++d02DvX0ccDk9/w==", "dev": true }, - "node_modules/marked": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.0.tgz", - "integrity": "sha512-+Z6KDjSPa6/723PQYyc1axYZpYYpDnECDaU6hkaf5gqBieBkMKYReL5hteF2QizhlMbgbo8umXl/clZ67+GlsA==", - "dev": true, - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 12" - } - }, "node_modules/marky": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", @@ -18907,15 +18768,6 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, - "node_modules/requizzle": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", - "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", - "dev": true, - "dependencies": { - "lodash": "^4.17.14" - } - }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -20614,12 +20466,6 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/taffydb": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.7.3.tgz", - "integrity": "sha512-GQ3gtYFSOAxSMN/apGtDKKkbJf+8izz5YfbGqIsUc7AMiQOapARZ76dhilRY2h39cynYxBFdafQo5HUL5vgkrg==", - "dev": true - }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -21254,12 +21100,6 @@ "through": "^2.3.8" } }, - "node_modules/underscore": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz", - "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==", - "dev": true - }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -22298,12 +22138,6 @@ "dev": true, "peer": true }, - "node_modules/wp-hookdoc": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/wp-hookdoc/-/wp-hookdoc-0.2.0.tgz", - "integrity": "sha512-BDVfqSA+L7Ho99McFu0ATwJRddaSVbUH/lgoe4RFP7Ktg3kiRtp7VHGIIdn+QhQN7ZtLmCBMpqtEChDvaGDppw==", - "dev": true - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -22406,12 +22240,6 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, - "node_modules/xmlcreate": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", - "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", - "dev": true - }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -24904,15 +24732,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "@jsdoc/salty": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.7.tgz", - "integrity": "sha512-mh8LbS9d4Jq84KLw8pzho7XC2q2/IJGiJss3xwRoLD1A+EE16SjN4PfaG4jRCzKegTFLlN0Zd8SdUPE6XdoPFg==", - "dev": true, - "requires": { - "lodash": "^4.17.21" - } - }, "@kwsites/file-exists": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", @@ -26020,28 +25839,6 @@ "@types/node": "*" } }, - "@types/linkify-it": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", - "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", - "dev": true - }, - "@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", - "dev": true, - "requires": { - "@types/linkify-it": "*", - "@types/mdurl": "*" - } - }, - "@types/mdurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", - "dev": true - }, "@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -28025,15 +27822,6 @@ "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", "dev": true }, - "catharsis": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", - "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", - "dev": true, - "requires": { - "lodash": "^4.17.15" - } - }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -32935,58 +32723,12 @@ "esprima": "^4.0.0" } }, - "js2xmlparser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", - "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", - "dev": true, - "requires": { - "xmlcreate": "^2.0.4" - } - }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", "dev": true }, - "jsdoc": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", - "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", - "dev": true, - "requires": { - "@babel/parser": "^7.20.15", - "@jsdoc/salty": "^0.2.1", - "@types/markdown-it": "^12.2.3", - "bluebird": "^3.7.2", - "catharsis": "^0.9.0", - "escape-string-regexp": "^2.0.0", - "js2xmlparser": "^4.0.2", - "klaw": "^3.0.0", - "markdown-it": "^12.3.2", - "markdown-it-anchor": "^8.4.1", - "marked": "^4.0.10", - "mkdirp": "^1.0.4", - "requizzle": "^0.2.3", - "strip-json-comments": "^3.1.0", - "underscore": "~1.13.2" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - } - } - }, "jsdoc-type-pratt-parser": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz", @@ -33152,15 +32894,6 @@ "is-buffer": "^1.1.5" } }, - "klaw": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", - "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.9" - } - }, "kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -33705,13 +33438,6 @@ } } }, - "markdown-it-anchor": { - "version": "8.6.5", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.5.tgz", - "integrity": "sha512-PI1qEHHkTNWT+X6Ip9w+paonfIQ+QZP9sCeMYi47oqhH+EsW8CrJ8J7CzV19QVOj6il8ATGbK2nTECj22ZHGvQ==", - "dev": true, - "requires": {} - }, "markdownlint": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.25.1.tgz", @@ -33777,12 +33503,6 @@ "integrity": "sha512-oEacRUVeTJ5D5hW1UYd2qExYI0oELdYK72k1TKGvIeYJIbqQWAz476NAc7LNixSySUhcNl++d02DvX0ccDk9/w==", "dev": true }, - "marked": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.0.tgz", - "integrity": "sha512-+Z6KDjSPa6/723PQYyc1axYZpYYpDnECDaU6hkaf5gqBieBkMKYReL5hteF2QizhlMbgbo8umXl/clZ67+GlsA==", - "dev": true - }, "marky": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", @@ -36371,15 +36091,6 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, - "requizzle": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", - "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } - }, "resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -37709,12 +37420,6 @@ } } }, - "taffydb": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.7.3.tgz", - "integrity": "sha512-GQ3gtYFSOAxSMN/apGtDKKkbJf+8izz5YfbGqIsUc7AMiQOapARZ76dhilRY2h39cynYxBFdafQo5HUL5vgkrg==", - "dev": true - }, "tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -38206,12 +37911,6 @@ "through": "^2.3.8" } }, - "underscore": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz", - "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==", - "dev": true - }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -38975,12 +38674,6 @@ "dev": true, "peer": true }, - "wp-hookdoc": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/wp-hookdoc/-/wp-hookdoc-0.2.0.tgz", - "integrity": "sha512-BDVfqSA+L7Ho99McFu0ATwJRddaSVbUH/lgoe4RFP7Ktg3kiRtp7VHGIIdn+QhQN7ZtLmCBMpqtEChDvaGDppw==", - "dev": true - }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -39044,12 +38737,6 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, - "xmlcreate": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", - "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", - "dev": true - }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index 8859eeeb0..d4c7fc380 100644 --- a/package.json +++ b/package.json @@ -32,11 +32,8 @@ "cypress": "^13.1.0", "cypress-mochawesome-reporter": "^3.5.1", "eslint-plugin-cypress": "^2.12.1", - "jsdoc": "^4.0.2", "mochawesome-json-to-md": "^0.7.2", - "node-wp-i18n": "^1.2.6", - "taffydb": "^2.7.3", - "wp-hookdoc": "^0.2.0" + "node-wp-i18n": "^1.2.6" }, "scripts": { "build": "wp-scripts build", From f8d017c8212addec519ee483ad32fd7aa0af756c Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Wed, 10 Sep 2025 19:36:03 +0530 Subject: [PATCH 02/25] Doc blocks updates for the hooks. --- includes/auto-distribute.php | 36 ++-- includes/bootstrap.php | 5 +- .../classes/API/SubscriptionsController.php | 5 +- includes/classes/Authentication.php | 18 +- .../Authentications/WordPressBasicAuth.php | 9 +- .../WordPressDotcomOauth2Authentication.php | 2 - includes/classes/DistributorPost.php | 24 +-- includes/classes/ExternalConnection.php | 7 +- .../WordPressExternalConnection.php | 80 ++++---- .../NetworkSiteConnection.php | 158 +++++++--------- includes/classes/PullListTable.php | 29 +-- includes/classes/RegisteredDataHandler.php | 50 +++-- includes/debug-info.php | 11 +- includes/external-connection-cpt.php | 10 +- includes/global-functions.php | 1 - includes/pull-ui.php | 11 +- includes/push-ui.php | 27 +-- includes/rest-api.php | 15 +- includes/subscriptions.php | 17 +- includes/syndicated-post-ui.php | 17 +- includes/template-tags.php | 6 +- includes/utils.php | 174 ++++++++---------- templates/show-connections-amp.php | 10 +- templates/show-connections.php | 10 +- 24 files changed, 306 insertions(+), 426 deletions(-) diff --git a/includes/auto-distribute.php b/includes/auto-distribute.php index 5ae8fcb5d..92613314b 100644 --- a/includes/auto-distribute.php +++ b/includes/auto-distribute.php @@ -50,11 +50,10 @@ function enabled() { * to. These posts will be distributed as published posts, not drafts. * * @since 2.2.0 - * @hook dt_auto_distribution_enabled * - * @param {bool} $enabled Whether the auto-distribution feature is enabled. Default false. + * @param bool $enabled Whether the auto-distribution feature is enabled. Default false. * - * @return {bool} Whether the auto-distribution feature is enabled. + * @return bool Whether the auto-distribution feature is enabled. */ return apply_filters( 'dt_auto_distribution_enabled', false ); } @@ -77,12 +76,11 @@ function default_post_status( $post ) { * Filter the default status for auto-distributed posts. * * @since 2.2.0 - * @hook dt_auto_distribution_default_status * - * @param {string} $status Default status for auto-distributed posts. Default 'publish'. - * @param {WP_Post} $post Post object for the post being pushed. + * @param string $status Default status for auto-distributed posts. Default 'publish'. + * @param WP_Post $post Post object for the post being pushed. * - * @return {string} Default status for auto-distributed posts. + * @return string Default status for auto-distributed posts. */ return apply_filters( 'dt_auto_distribution_default_status', 'publish', $post ); } @@ -111,15 +109,14 @@ function auto_distribute_post( $post, $user_id, $connection_type, $connection_id * Filter to determine if a post should be auto-distributed. * * @since 2.2.0 - * @hook dt_auto_distribute_post * - * @param {bool} $should_distribute Whether the post should be auto-distributed. - * @param {WP_Post} $post WP_Post object for the post being pushed. - * @param {int} $user_id User ID of the user pushing the post. - * @param {string} $connection_type Type of connection ('external' or 'internal'). - * @param {int} $connection_id Connection ID. + * @param bool $should_distribute Whether the post should be auto-distributed. + * @param WP_Post $post WP_Post object for the post being pushed. + * @param int $user_id User ID of the user pushing the post. + * @param string $connection_type Type of connection ('external' or 'internal'). + * @param int $connection_id Connection ID. * - * @return {bool} Whether the post should be auto-distributed. + * @return bool Whether the post should be auto-distributed. */ return apply_filters( 'dt_auto_distribute_post', true, $post, $user_id, $connection_type, $connection_id ); } @@ -140,11 +137,10 @@ function auto_distribute_supported_post_types() { * Filter the post types that are auto-distributable. * * @since 2.2.0 - * @hook auto_distribute_supported_post_types * - * @param {string[]} $post_types Array of post types that can be auto-distributed. + * @param string[] $post_types Array of post types that can be auto-distributed. * Default is array( 'post', 'page' ). - * @return {string[]} Array of post types that can be auto-distributed. + * @return string[] Array of post types that can be auto-distributed. */ return apply_filters( 'auto_distribute_supported_post_types', $post_types ); } @@ -318,12 +314,10 @@ function get_external_connections( $post_id = 0, $user_id = 0 ) { * Modify the maximum number of external connection post types are * queried with requesting the post type. * - * @hook dt_external_connections_per_page - * * @since 2.2.0 * - * @param {int} $max_connections The maximum number of external connections to load. - * @return {int} The maximum number of external connections to load. + * @param int $max_connections The maximum number of external connections to load. + * @return int The maximum number of external connections to load. */ 'posts_per_page' => apply_filters( 'dt_external_connections_per_page', 200 ), // @codingStandardsIgnoreLine This high pagination limit is purposeful ] diff --git a/includes/bootstrap.php b/includes/bootstrap.php index 7dd296ee0..466220f79 100644 --- a/includes/bootstrap.php +++ b/includes/bootstrap.php @@ -209,11 +209,10 @@ function() { * Filter whether the network connection type is enabled. Enabled by default, return false to disable. * * @since 1.0.0 - * @hook dt_network_site_connection_enabled * - * @param {bool} true Whether the network connection should be enabled. + * @param bool true Whether the network connection should be enabled. * - * @return {bool} Whether the network connection should be enabled. + * @return bool Whether the network connection should be enabled. */ apply_filters( 'dt_network_site_connection_enabled', true ) ) { diff --git a/includes/classes/API/SubscriptionsController.php b/includes/classes/API/SubscriptionsController.php index 7ce8e8bea..6fc07758c 100644 --- a/includes/classes/API/SubscriptionsController.php +++ b/includes/classes/API/SubscriptionsController.php @@ -314,10 +314,9 @@ public function receive_item( $request ) { * Action fired after receiving a subscription update from Distributor * * @since 1.3.8 - * @hook dt_process_subscription_attributes * - * @param {WP_Post} $post Updated post object. - * @param {WP_REST_Request} $request Request object. + * @param WP_Post $post Updated post object. + * @param WP_REST_Request $request Request object. */ do_action( 'dt_process_subscription_attributes', $post, $request ); diff --git a/includes/classes/Authentication.php b/includes/classes/Authentication.php index 3fd9f5c9f..b81600408 100644 --- a/includes/classes/Authentication.php +++ b/includes/classes/Authentication.php @@ -56,13 +56,12 @@ public function format_get_args( $args = array(), $context = array() ) { * Format request args for a GET request so auth occurs. * * @since 0.8 - * @hook dt_auth_format_get_args * - * @param {array} $args Array of request arguments. - * @param {array} $context Optional array of information about the request. - * @param {object} $this The authentication class. + * @param array $args Array of request arguments. + * @param array $context Optional array of information about the request. + * @param object $this The authentication class. * - * @return {array} Array of request arguments. + * @return array Array of request arguments. */ return apply_filters( 'dt_auth_format_get_args', $args, $context, $this ); } @@ -85,13 +84,12 @@ public function format_post_args( $args, $context = array() ) { * Format request args for a POST request so auth occurs * * @since 0.8 - * @hook dt_auth_format_post_args * - * @param {array} $args Array of request arguments. - * @param {array} $context Optional array of information about the request. - * @param {object} $this The authentication class. + * @param array $args Array of request arguments. + * @param array $context Optional array of information about the request. + * @param object $this The authentication class. * - * @return {array} Array of request arguments. + * @return array Array of request arguments. */ return apply_filters( 'dt_auth_format_post_args', $args, $context, $this ); } diff --git a/includes/classes/Authentications/WordPressBasicAuth.php b/includes/classes/Authentications/WordPressBasicAuth.php index 8dfff2613..65ed352bd 100644 --- a/includes/classes/Authentications/WordPressBasicAuth.php +++ b/includes/classes/Authentications/WordPressBasicAuth.php @@ -211,13 +211,12 @@ public static function prepare_credentials( $args ) { * Filter the authorization credentials prepared before saving. * * @since 1.0 - * @hook dt_auth_prepare_credentials * - * @param {array} $auth The credentials to be saved. - * @param {array} $args The arguments originally passed to `prepare_credentials`. - * @param {string} $slug The authorization handler type slug. + * @param array $auth The credentials to be saved. + * @param array $args The arguments originally passed to `prepare_credentials`. + * @param string $slug The authorization handler type slug. * - * @return {array} The authorization credentials to be saved. + * @return array The authorization credentials to be saved. */ return apply_filters( 'dt_auth_prepare_credentials', $auth, $args, self::$slug ); } diff --git a/includes/classes/Authentications/WordPressDotcomOauth2Authentication.php b/includes/classes/Authentications/WordPressDotcomOauth2Authentication.php index db4dd44ae..5052fd338 100644 --- a/includes/classes/Authentications/WordPressDotcomOauth2Authentication.php +++ b/includes/classes/Authentications/WordPressDotcomOauth2Authentication.php @@ -90,8 +90,6 @@ public static function credentials_form( $args = array() ) { /** * Display any authorization or token errors. - * - * @hook dt_oauth_admin_notices */ do_action( 'dt_oauth_admin_notices' ); diff --git a/includes/classes/DistributorPost.php b/includes/classes/DistributorPost.php index d16c83938..945f278e6 100644 --- a/includes/classes/DistributorPost.php +++ b/includes/classes/DistributorPost.php @@ -695,11 +695,10 @@ protected function get_extra_data() { * Filters whether to process extra data for the post. * * @since 2.2.0 - * @hook dt_process_extra_data * - * @param {bool} $process_extra_data Whether to process extra data. - * @param {array} $post_data The post data. - * @return {bool} Whether to process extra data. + * @param bool $process_extra_data Whether to process extra data. + * @param array $post_data The post data. + * @return bool Whether to process extra data. */ if ( ! apply_filters( 'dt_process_extra_data', true, $post_data ) ) { return array(); @@ -798,11 +797,10 @@ protected function get_extra_data() { * Filters the extra data for the post to be processed on the target site. * * @since 2.2.0 - * @hook dt_extra_data * - * @param {array} $extra_data Extra data for the post. - * @param {DistributorPost} $this The DistributorPost object. - * @return {array} Extra data for the post. + * @param array $extra_data Extra data for the post. + * @param DistributorPost $this The DistributorPost object. + * @return array Extra data for the post. */ $extra_data = apply_filters( 'dt_extra_data', $extra_data, $this ); @@ -1009,11 +1007,10 @@ protected function parse_blocks_for_attachment_id( $block ) { * } * * @since 2.0.0 - * @hook dt_parse_media_blocks * - * @param {array} $media_blocks Array of media blocks. + * @param array $media_blocks Array of media blocks. * - * @return {array} Modified array of media blocks. + * @return array Modified array of media blocks. */ $media_blocks = apply_filters( 'dt_parse_media_blocks', $media_blocks ); @@ -1213,11 +1210,10 @@ protected function to_pull_list( $args = array() ) { * Filters the post data for when they are being formatted for a pull * * @since 2.0.3 - * @hook dt_post_to_pull * - * @param {array} $display_data The post data. + * @param array $display_data The post data. * - * @return {array} Modified post data. + * @return array Modified post data. */ return apply_filters( 'dt_post_to_pull', $display_data ); } diff --git a/includes/classes/ExternalConnection.php b/includes/classes/ExternalConnection.php index ba86bd9fd..ed8e50924 100644 --- a/includes/classes/ExternalConnection.php +++ b/includes/classes/ExternalConnection.php @@ -97,11 +97,10 @@ public function log_sync( array $item_id_mappings, $connection_id = 0, $overwrit * Action fired when a sync is being logged. * * @since 1.0 - * @hook dt_log_sync * - * @param {array} $item_id_mappings Item ID mappings. - * @param {array} $sync_log The sync log - * @param {object} $this The current connection class. + * @param array $item_id_mappings Item ID mappings. + * @param array $sync_log The sync log + * @param object $this The current connection class. */ do_action( 'dt_log_sync', $item_id_mappings, $sync_log, $this ); } diff --git a/includes/classes/ExternalConnections/WordPressExternalConnection.php b/includes/classes/ExternalConnections/WordPressExternalConnection.php index 75f3a7f0c..b6cb7cfd3 100644 --- a/includes/classes/ExternalConnections/WordPressExternalConnection.php +++ b/includes/classes/ExternalConnections/WordPressExternalConnection.php @@ -127,12 +127,11 @@ public function remote_get( $args = array() ) { * Filter the remote_get request. * * @since 1.0 - * @hook dt_remote_get * - * @param {array} $args The arguments originally passed to `remote_get`. - * @param {object} $this The authentication class. + * @param array $args The arguments originally passed to `remote_get`. + * @param object $this The authentication class. * - * @return {array} The arguments originally passed to `remote_get`. + * @return array The arguments originally passed to `remote_get`. */ return apply_filters( 'dt_remote_get', @@ -213,12 +212,11 @@ public function remote_post( $url = '', $args = array() ) { * Filter the remote_post query arguments * * @since 1.6.7 - * @hook dt_remote_post_query_args * - * @param {array} $args The request arguments. - * @param {WordPressExternalConnection} $this The current connection object. + * @param array $args The request arguments. + * @param WordPressExternalConnection $this The current connection object. * - * @return {array} The query arguments. + * @return array The query arguments. */ $body = apply_filters( 'dt_remote_post_query_args', $args, $this ); @@ -233,12 +231,11 @@ public function remote_post( $url = '', $args = array() ) { * Filter the timeout used when calling `remote_post` * * @since 1.6.7 - * @hook dt_remote_post_timeout * - * @param {int} $timeout The timeout to use for the remote post. Default `45`. - * @param {array} $args The request arguments. + * @param int $timeout The timeout to use for the remote post. Default `45`. + * @param array $args The request arguments. * - * @return {int} The timeout to use for the remote_post call. + * @return int The timeout to use for the remote_post call. */ 'timeout' => apply_filters( 'dt_remote_post_timeout', 45, $args ), 'body' => $body, @@ -307,13 +304,12 @@ public function remote_post( $url = '', $args = array() ) { * Filter the items returned when using `WordPressExternalConnection::remote_post` * * @since 1.6.7 - * @hook dt_remote_post * - * @param {array} $items The items returned from the POST request. - * @param {array} $args The arguments used in the POST request. - * @param {WordPressExternalConnection} $this The current connection object. + * @param array $items The items returned from the POST request. + * @param array $args The arguments used in the POST request. + * @param WordPressExternalConnection $this The current connection object. * - * @return {array} The items returned from a remote POST request. + * @return array The items returned from a remote POST request. */ return apply_filters( 'dt_remote_post', @@ -407,14 +403,13 @@ public function pull( $items ) { * Filter the arguments passed into wp_insert_post during a pull. * * @since 1.0 - * @hook dt_pull_post_args * - * @param {array} $post_array The post data to be inserted. - * @param {array} $remote_post_id The remote post ID. - * @param {object} $post The request that got the post. - * @param {ExternalConnection} $this The Distributor connection pulling the post. + * @param array $post_array The post data to be inserted. + * @param array $remote_post_id The remote post ID. + * @param object $post The request that got the post. + * @param ExternalConnection $this The Distributor connection pulling the post. * - * @return {array} The post data to be inserted. + * @return array The post data to be inserted. */ $new_post_args = Utils\post_args_allow_list( apply_filters( 'dt_pull_post_args', $post_array, $item_array['remote_post_id'], $post, $this ) ); if ( $update ) { @@ -563,27 +558,26 @@ public function push( $post, $args = array() ) { * Filter the timeout used when calling `WordPressExternalConnection::push`. * * @since 1.0 - * @hook dt_push_post_timeout * - * @param {int} $timeout The timeout to use for the remote post. Default `5`. - * @param {object} $post The post object + * @param int $timeout The timeout to use for the remote post. Default `5`. + * @param object $post The post object * - * @return {int} The timeout to use for the remote post. + * @return int The timeout to use for the remote post. */ 'timeout' => apply_filters( 'dt_push_post_timeout', 45, $post ), /** * Filter the arguments sent to the remote server during a push. * * @since 1.0 - * @hook dt_push_post_args + * * @tutorial snippets * - * @param {array} $post_body The request body to send. - * @param {object} $post The WP_Post that is being pushed. - * @param {array} $args Post args to push. - * @param {ExternalConnection} $this The distributor connection being pushed to. + * @param array $post_body The request body to send. + * @param object $post The WP_Post that is being pushed. + * @param array $args Post args to push. + * @param ExternalConnection $this The distributor connection being pushed to. * - * @return {array} The request body to send. + * @return array The request body to send. */ 'body' => apply_filters( 'dt_push_post_args', $post_body, $post, $args, $this ), ) @@ -602,14 +596,13 @@ public function push( $post, $args = array() ) { * Fires the action after a post is pushed via Distributor before remote request validation. * * @since 2.0.0 - * @hook dt_push_external_post * - * @param {array|WP_Error} $response The response from the remote request. - * @param {array} $post_body The Post data formatted for the REST API endpoint. - * @param {string} $type_url The Post type api endpoint. - * @param {int} $post_id The Post id. - * @param {array} $args The arguments passed into wp_insert_post. - * @param {WordPressExternalConnection} $this The Distributor connection being pushed to. + * @param array|WP_Error $response The response from the remote request. + * @param array $post_body The Post data formatted for the REST API endpoint. + * @param string $type_url The Post type api endpoint. + * @param int $post_id The Post id. + * @param array $args The arguments passed into wp_insert_post. + * @param WordPressExternalConnection $this The Distributor connection being pushed to. */ do_action( 'dt_push_external_post', $response, $post_body, $type_url, $post_id, $args, $this ); @@ -842,12 +835,11 @@ private function to_wp_post( $post ) { * Filter the post item. * * @since 1.0 - * @hook dt_item_mapping * - * @param {WP_Post} $obj The WP_Post that is being pushed. - * @param {ExternalConnection} $this The external connection the post concerns. + * @param WP_Post $obj The WP_Post that is being pushed. + * @param ExternalConnection $this The external connection the post concerns. * - * @return {WP_Post} The WP_Post that is being pushed. + * @return WP_Post The WP_Post that is being pushed. */ $post_object = apply_filters( 'dt_item_mapping', new \WP_Post( $obj ), $post, $this ); diff --git a/includes/classes/InternalConnections/NetworkSiteConnection.php b/includes/classes/InternalConnections/NetworkSiteConnection.php index e2bbd9dbc..ee5034565 100644 --- a/includes/classes/InternalConnections/NetworkSiteConnection.php +++ b/includes/classes/InternalConnections/NetworkSiteConnection.php @@ -139,12 +139,11 @@ public function push( $post, $args = array() ) { * * @since 1.2.2 * @deprecated 2.0.0 The dt_push_post action has been deprecated. Please use dt_push_network_post or dt_push_external_post instead. - * @hook dt_push_post * - * @param {int} $new_post_id The newly created post. - * @param {int} $post_id The original post. - * @param {array} $args The arguments passed into wp_insert_post. - * @param {Connection} $this The Distributor connection being pushed to. + * @param int $new_post_id The newly created post. + * @param int $post_id The original post. + * @param array $args The arguments passed into wp_insert_post. + * @param Connection $this The Distributor connection being pushed to. */ do_action_deprecated( 'dt_push_post', @@ -157,12 +156,11 @@ public function push( $post, $args = array() ) { * Fires the action after a post is pushed via Distributor before `restore_current_blog()`. * * @since 2.0.0 - * @hook dt_push_network_post * - * @param {int} $new_post_id The newly created post. - * @param {int} $post_id The original post. - * @param {array} $args The arguments passed into wp_insert_post. - * @param {NetworkSiteConnection} $this The Distributor connection being pushed to. + * @param int $new_post_id The newly created post. + * @param int $post_id The original post. + * @param array $args The arguments passed into wp_insert_post. + * @param NetworkSiteConnection $this The Distributor connection being pushed to. */ do_action( 'dt_push_network_post', $new_post_id, $post_id, $args, $this ); @@ -180,16 +178,14 @@ public function push( $post, $args = array() ) { /** * Allow bypassing of all media processing. * - * @hook dt_push_post_media + * @param bool true If Distributor should push the post media. + * @param int $new_post_id The newly created post ID. + * @param array $post_media List of media items attached to the post, formatted by {@link \Distributor\Utils\prepare_media()}. + * @param int $post_id The original post ID. + * @param array $args The arguments passed into wp_insert_post. + * @param Connection $this The distributor connection being pushed to. * - * @param {bool} true If Distributor should push the post media. - * @param {int} $new_post_id The newly created post ID. - * @param {array} $post_media List of media items attached to the post, formatted by {@link \Distributor\Utils\prepare_media()}. - * @param {int} $post_id The original post ID. - * @param {array} $args The arguments passed into wp_insert_post. - * @param {Connection} $this The distributor connection being pushed to. - * - * @return {bool} If Distributor should push the post media. + * @return bool If Distributor should push the post media. */ if ( apply_filters( 'dt_push_post_media', true, $new_post_id, $post_media, $post_id, $args, $this ) ) { Utils\set_media( $new_post_id, $post_media, [ 'use_filesystem' => true ] ); @@ -205,16 +201,14 @@ public function push( $post, $args = array() ) { /** * Allow bypassing of all term processing. * - * @hook dt_push_post_terms - * - * @param {bool} true If Distributor should push the post terms. - * @param {int} $new_post_id The newly created post ID. - * @param {array} $post_terms Terms attached to the post, formatted by {@link \Distributor\Utils\prepare_taxonomy_terms()}. - * @param {int} $post_id The original post ID. - * @param {array} $args The arguments passed into wp_insert_post. - * @param {Connection} $this The distributor connection being pushed to. + * @param bool true If Distributor should push the post terms. + * @param int $new_post_id The newly created post ID. + * @param array $post_terms Terms attached to the post, formatted by {@link \Distributor\Utils\prepare_taxonomy_terms()}. + * @param int $post_id The original post ID. + * @param array $args The arguments passed into wp_insert_post. + * @param Connection $this The distributor connection being pushed to. * - * @return {bool} If Distributor should push the post terms. + * @return bool If Distributor should push the post terms. */ if ( apply_filters( 'dt_push_post_terms', true, $new_post_id, $post_terms, $post_id, $args, $this ) ) { Utils\set_taxonomy_terms( $new_post_id, $post_terms ); @@ -223,16 +217,14 @@ public function push( $post, $args = array() ) { /** * Allow bypassing of all meta processing. * - * @hook dt_push_post_meta + * @param bool true If Distributor should push the post meta. + * @param int $new_post_id The newly created post ID. + * @param array $post_meta Meta attached to the post, formatted by {@link \Distributor\Utils\prepare_meta()}. + * @param int $post_id The original post ID. + * @param array $args The arguments passed into wp_insert_post. + * @param Connection $this The distributor connection being pushed to. * - * @param {bool} true If Distributor should push the post meta. - * @param {int} $new_post_id The newly created post ID. - * @param {array} $post_meta Meta attached to the post, formatted by {@link \Distributor\Utils\prepare_meta()}. - * @param {int} $post_id The original post ID. - * @param {array} $args The arguments passed into wp_insert_post. - * @param {Connection} $this The distributor connection being pushed to. - * - * @return {bool} If Distributor should push the post meta. + * @return bool If Distributor should push the post meta. */ if ( apply_filters( 'dt_push_post_meta', true, $new_post_id, $post_meta, $post_id, $args, $this ) ) { $post_meta = $this->exclude_additional_meta_data( $post_meta ); @@ -335,16 +327,14 @@ public function pull( $items ) { /** * Allow bypassing of all media processing. * - * @hook dt_pull_post_media - * - * @param {bool} true If Distributor should set the post media. - * @param {int} $new_post_id The newly created post ID. - * @param {array} $post_media List of media items attached to the post, formatted by {@link \Distributor\Utils\prepare_media()}. - * @param {int} $remote_post_id The original post ID. - * @param {array} $post_array The arguments passed into wp_insert_post. - * @param {NetworkSiteConnection} $this The Distributor connection being pulled from. + * @param bool true If Distributor should set the post media. + * @param int $new_post_id The newly created post ID. + * @param array $post_media List of media items attached to the post, formatted by {@link \Distributor\Utils\prepare_media()}. + * @param int $remote_post_id The original post ID. + * @param array $post_array The arguments passed into wp_insert_post. + * @param NetworkSiteConnection $this The Distributor connection being pulled from. * - * @return {bool} If Distributor should set the post media. + * @return bool If Distributor should set the post media. */ if ( apply_filters( 'dt_pull_post_media', true, $new_post_id, $post['media'], $item_array['remote_post_id'], $post_array, $this ) ) { \Distributor\Utils\set_media( $new_post_id, $post['media'], [ 'use_filesystem' => true ] ); @@ -353,16 +343,14 @@ public function pull( $items ) { /** * Allow bypassing of all terms processing. * - * @hook dt_pull_post_terms + * @param bool true If Distributor should set the post terms. + * @param int $new_post_id The newly created post ID. + * @param array $post_terms List of terms items attached to the post, formatted by {@link \Distributor\Utils\prepare_taxonomy_terms()}. + * @param int $remote_post_id The original post ID. + * @param array $post_array The arguments passed into wp_insert_post. + * @param NetworkSiteConnection $this The Distributor connection being pulled from. * - * @param {bool} true If Distributor should set the post terms. - * @param {int} $new_post_id The newly created post ID. - * @param {array} $post_terms List of terms items attached to the post, formatted by {@link \Distributor\Utils\prepare_taxonomy_terms()}. - * @param {int} $remote_post_id The original post ID. - * @param {array} $post_array The arguments passed into wp_insert_post. - * @param {NetworkSiteConnection} $this The Distributor connection being pulled from. - * - * @return {bool} If Distributor should set the post terms. + * @return bool If Distributor should set the post terms. */ if ( apply_filters( 'dt_pull_post_terms', true, $new_post_id, $post['terms'], $item_array['remote_post_id'], $post_array, $this ) ) { \Distributor\Utils\set_taxonomy_terms( $new_post_id, $post['terms'] ); @@ -371,16 +359,14 @@ public function pull( $items ) { /** * Allow bypassing of all meta processing. * - * @hook dt_pull_post_meta - * - * @param {bool} true If Distributor should set the post meta. - * @param {int} $new_post_id The newly created post ID. - * @param {array} $post_meta List of meta items attached to the post, formatted by {@link \Distributor\Utils\prepare_meta()}. - * @param {int} $remote_post_id The original post ID. - * @param {array} $post_array The arguments passed into wp_insert_post. - * @param {NetworkSiteConnection} $this The Distributor connection being pulled from. + * @param bool true If Distributor should set the post meta. + * @param int $new_post_id The newly created post ID. + * @param array $post_meta List of meta items attached to the post, formatted by {@link \Distributor\Utils\prepare_meta()}. + * @param int $remote_post_id The original post ID. + * @param array $post_array The arguments passed into wp_insert_post. + * @param NetworkSiteConnection $this The Distributor connection being pulled from. * - * @return {bool} If Distributor should set the post meta. + * @return bool If Distributor should set the post meta. */ if ( apply_filters( 'dt_pull_post_meta', true, $new_post_id, $post['meta'], $item_array['remote_post_id'], $post_array, $this ) ) { $post_meta = $this->exclude_additional_meta_data( $post['meta'] ); @@ -415,14 +401,12 @@ public function pull( $items ) { /** * Allow the sync'ed post to be updated via a REST request get the rendered content. * - * @hook dt_pull_post_apply_rendered_content + * @param bool false Apply rendered content after a pull? Defaults to false. + * @param int $new_post_id The new post ID. + * @param Connection $this The Distributor connection pulling the post. + * @param array $post_array The post array used to create the new post. * - * @param {bool} false Apply rendered content after a pull? Defaults to false. - * @param {int} $new_post_id The new post ID. - * @param {Connection} $this The Distributor connection pulling the post. - * @param {array} $post_array The post array used to create the new post. - * - * @return {bool} Whether to apply rendered content after a pull. + * @return bool Whether to apply rendered content after a pull. */ if ( apply_filters( 'dt_pull_post_apply_rendered_content', false, $new_post_id, $this, $post_array ) ) { $this->update_content_via_rest( $new_post_id ); @@ -433,11 +417,10 @@ public function pull( $items ) { * Fires after a post is pulled via Distributor and after `restore_current_blog()`. * * @since 1.0 - * @hook dt_pull_post * - * @param {int} $new_post_id The new post ID that was pulled. - * @param {Connection} $this The Distributor connection pulling the post. - * @param {array} $post_array The original post data retrieved via the connection. + * @param int $new_post_id The new post ID that was pulled. + * @param Connection $this The Distributor connection pulling the post. + * @param array $post_array The original post data retrieved via the connection. */ do_action( 'dt_pull_post', $new_post_id, $this, $post_array ); @@ -838,14 +821,13 @@ public static function get_available_authorized_sites( $context = null ) { * * @since 1.2 * @since 1.3.7 Added the `$context` parameter. - * @hook dt_pre_get_authorized_sites * * @see \Distributor\InternalConnections\NetworkSiteConnection::get_available_authorized_sites() * - * @param {array} $authorized_sites Array of `WP_Site` object and post type objects the user can edit. - * @param {string} $context The context of the authorization. + * @param array $authorized_sites Array of `WP_Site` object and post type objects the user can edit. + * @param string $context The context of the authorization. * - * @return {array} Array of `WP_Site` object and post type objects. + * @return array Array of `WP_Site` object and post type objects. */ $authorized_sites = apply_filters( 'dt_pre_get_authorized_sites', array(), $context ); if ( ! empty( $authorized_sites ) ) { @@ -859,13 +841,13 @@ public static function get_available_authorized_sites( $context = null ) { * * @since 1.2 * @since 1.3.7 Added the `$context` parameter. - * @hook dt_authorized_sites + * * @tutorial snippets * - * @param {array} $authorized_sites An array of `WP_Site` objects and the post type objects the user can edit. - * @param {string} $context The context of the authorization. + * @param array $authorized_sites An array of `WP_Site` objects and the post type objects the user can edit. + * @param string $context The context of the authorization. * - * @return {array} An array of `WP_Site` objects and the post type objects. + * @return array An array of `WP_Site` objects and the post type objects. */ return apply_filters( 'dt_authorized_sites', $authorized_sites, $context ); } @@ -1099,13 +1081,11 @@ public function update_content_via_rest( $new_post_id ) { * Allow filtering of the HTTP request args before updating content * via a REST API call. * - * @hook dt_update_content_via_request_args - * - * @param {array} list List of request args. - * @param {int} $new_post_id The new post ID. - * @param {NetworkSiteConnection} $this The distributor connection being pulled from. + * @param array $list List of request args. + * @param int $new_post_id The new post ID. + * @param NetworkSiteConnection $this The distributor connection being pulled from. * - * @return {array} List of filtered request args. + * @return array List of filtered request args. */ $request = apply_filters( 'dt_update_content_via_request_args', [], $new_post_id, $this ); diff --git a/includes/classes/PullListTable.php b/includes/classes/PullListTable.php index 8c1041a88..d8bbf5107 100644 --- a/includes/classes/PullListTable.php +++ b/includes/classes/PullListTable.php @@ -63,11 +63,9 @@ public function get_columns() { /** * Filters the columns displayed in the pull list table. * - * @hook dt_pull_list_table_columns + * @param array $columns An associative array of column headings. * - * @param {array} $columns An associative array of column headings. - * - * @return {array} An associative array of column headings. + * @return array An associative array of column headings. */ return apply_filters( 'dt_pull_list_table_columns', $columns ); } @@ -268,10 +266,8 @@ public function column_default( $item, $column_name ) { /** * Fires for each column in the pull list table. * - * @hook dt_pull_list_table_custom_column - * - * @param {string} $column_name The name of the column to display. - * @param {WP_Post} $item The post/item to output in the column. + * @param string $column_name The name of the column to display. + * @param WP_Post $item The post/item to output in the column. */ do_action( 'dt_pull_list_table_custom_column', $column_name, $item ); @@ -323,12 +319,10 @@ public function column_name( $item ) { /** * Filter the default value of the 'Pull as draft' option in the pull ui * - * @hook dt_pull_as_draft + * @param bool $as_draft Whether the 'Pull as draft' option should be checked. + * @param object $connection The connection being used to pull from. * - * @param {bool} $as_draft Whether the 'Pull as draft' option should be checked. - * @param {object} $connection The connection being used to pull from. - * - * @return {bool} Whether the 'Pull as draft' option should be checked. + * @return bool Whether the 'Pull as draft' option should be checked. */ $as_draft = apply_filters( 'dt_pull_as_draft', true, $connection_now ); @@ -403,12 +397,10 @@ public function single_row( $item ) { /** * Filters the class used on the table row on the pull list table. * - * @hook dt_pull_list_table_tr_class - * - * @param {string} $class The class name. - * @param {WP_Post} $item The current post object. + * @param string $class The class name. + * @param WP_Post $item The current post object. * - * @return {string} The class name. + * @return string The class name. */ $class = sanitize_html_class( apply_filters( 'dt_pull_list_table_tr_class', 'dt-table-row', $item ) ); @@ -652,7 +644,6 @@ public function extra_tablenav( $which ) { * Action fired when extra table nav is generated. * * @since 1.0 - * @hook dt_pull_filters */ do_action( 'dt_pull_filters' ); } diff --git a/includes/classes/RegisteredDataHandler.php b/includes/classes/RegisteredDataHandler.php index 19be71226..073c59b74 100644 --- a/includes/classes/RegisteredDataHandler.php +++ b/includes/classes/RegisteredDataHandler.php @@ -249,13 +249,12 @@ public function process_registered_data( $post_data, $is_rest = false ) { * Filter the post data after processing the registered data. * * @since 2.2.0 - * @hook dt_after_registered_data_processed * - * @param {array} $post_data The post data after processing the registered data. - * @param {array} $registered_data The distributor registered data. - * @param {array} $extra_data The extra data for the given registered data. - * @param {array} $unprocessed_post_data The post data before processing the registered data. - * @return {array} $post_data The updated post data. + * @param array $post_data The post data after processing the registered data. + * @param array $registered_data The distributor registered data. + * @param array $extra_data The extra data for the given registered data. + * @param array $unprocessed_post_data The post data before processing the registered data. + * @return array $post_data The updated post data. */ $post_data = apply_filters( 'dt_after_registered_data_processed', $post_data, $registered_data, $post_data['distributor_extra_data'] ?? array(), $unprocessed_post_data ); @@ -329,14 +328,13 @@ public function process_registered_post_meta_data( $post_meta, $registered_data, * Filter the post meta data after processing the registered data. * * @since 2.2.0 - * @hook dt_after_registered_post_meta_processed * - * @param {array} $post_meta The post meta data. - * @param {array} $registered_data The distributor registered data. - * @param {array} $extra_data The extra data for the given registered data. - * @param {array} $post_data The post data. - * @param {array} $unprocessed_post_meta The post meta data before processing the registered data. - * @return {array} $post_meta The updated post meta data. + * @param array $post_meta The post meta data. + * @param array $registered_data The distributor registered data. + * @param array $extra_data The extra data for the given registered data. + * @param array $post_data The post data. + * @param array $unprocessed_post_meta The post meta data before processing the registered data. + * @return array $post_meta The updated post meta data. */ return apply_filters( 'dt_after_registered_post_meta_processed', $post_meta, $registered_data, $extra_data, $post_data, $unprocessed_post_meta ); } @@ -371,14 +369,13 @@ public function process_registered_block_data( $post_content, $registered_data, * Filter the post content blocks after processing the registered data. * * @since 2.2.0 - * @hook dt_after_registered_block_data_processed * - * @param {array} $post_content The post content. - * @param {array} $registered_data The distributor registered data. - * @param {array} $extra_data The extra data for the given registered data. - * @param {array} $post_data The post data. - * @param {array} $unprocessed_post_content The post content before processing the registered data. - * @return {array} $post_content The updated post content. + * @param array $post_content The post content. + * @param array $registered_data The distributor registered data. + * @param array $extra_data The extra data for the given registered data. + * @param array $post_data The post data. + * @param array $unprocessed_post_content The post content before processing the registered data. + * @return array $post_content The updated post content. */ return apply_filters( 'dt_after_registered_block_data_processed', $post_content, $registered_data, $extra_data, $post_data, $unprocessed_post_content ); } @@ -459,14 +456,13 @@ function ( $matches ) use ( &$index, $shortcode, $shortcode_attribute, $callback * Filter the post content shortcodes after processing the registered data. * * @since 2.2.0 - * @hook dt_after_registered_shortcode_data_processed * - * @param {array} $post_content The post content. - * @param {array} $registered_data The distributor registered data. - * @param {array} $extra_data The extra data for the given registered data. - * @param {array} $post_data The post data. - * @param {array} $unprocessed_post_content The post content before processing the registered data. - * @return {array} $post_content The updated post content. + * @param array $post_content The post content. + * @param array $registered_data The distributor registered data. + * @param array $extra_data The extra data for the given registered data. + * @param array $post_data The post data. + * @param array $unprocessed_post_content The post content before processing the registered data. + * @return array $post_content The updated post content. */ return apply_filters( 'dt_after_registered_shortcode_data_processed', $post_content, $registered_data, $extra_data, $post_data, $unprocessed_post_content ); } diff --git a/includes/debug-info.php b/includes/debug-info.php index 8b88da68f..01d620553 100644 --- a/includes/debug-info.php +++ b/includes/debug-info.php @@ -25,11 +25,10 @@ function() { * Filter whether the debug info is enabled. Enabled by default, return false to disable. * * @since 2.0.0 - * @hook dt_debug_info_enabled * - * @param {bool} true Whether the debug info should be enabled. + * @param bool true Whether the debug info should be enabled. * - * @return {bool} Whether the debug info should be enabled. + * @return bool Whether the debug info should be enabled. */ if ( ! apply_filters( 'dt_debug_info_enabled', true ) ) { return; @@ -213,12 +212,10 @@ function get_formatted_external_connections() { * Modify the maximum number of external connection post types are * queried with requesting the post type. * - * @hook dt_external_connections_per_page - * * @since 2.2.0 * - * @param {int} $max_connections The maximum number of external connections to load. - * @return {int} The maximum number of external connections to load. + * @param int $max_connections The maximum number of external connections to load. + * @return int The maximum number of external connections to load. */ 'posts_per_page' => apply_filters( 'dt_external_connections_per_page', 200 ), // @codingStandardsIgnoreLine This high pagination limit is purposeful ) diff --git a/includes/external-connection-cpt.php b/includes/external-connection-cpt.php index 2a854b399..613abd669 100644 --- a/includes/external-connection-cpt.php +++ b/includes/external-connection-cpt.php @@ -653,11 +653,10 @@ function add_menu_item() { * Filter Distributor capabilities allowed to view external connections. * * @since 1.0.0 - * @hook dt_capabilities * - * @param {string} 'manage_options' The capability allowed to view external connections. + * @param string 'manage_options' The capability allowed to view external connections. * - * @return {string} The capability allowed to view external connections. + * @return string The capability allowed to view external connections. */ apply_filters( 'dt_capabilities', 'manage_options' ), 'distributor', @@ -684,11 +683,10 @@ function add_submenu_item() { * Filter Distributor capabilities allowed to manage external connections. * * @since 1.0.0 - * @hook dt_external_capabilities * - * @param {string} 'manage_options' The capability allowed to manage external connections. + * @param string 'manage_options' The capability allowed to manage external connections. * - * @return {string} The capability allowed to manage external connections. + * @return string The capability allowed to manage external connections. */ apply_filters( 'dt_external_capabilities', 'manage_options' ), 'distributor' diff --git a/includes/global-functions.php b/includes/global-functions.php index d60cae928..f51715fe6 100644 --- a/includes/global-functions.php +++ b/includes/global-functions.php @@ -544,7 +544,6 @@ function distributor_term_pre_distribute_callback( $term_id, $source_post_id ) { * If set to true, the term will be distributed with its parents. * * @since 2.2.0 - * @hook dt_registered_data_distribute_term_parent * * @param bool $with_parents Whether to distribute term with parents. Default false. * diff --git a/includes/pull-ui.php b/includes/pull-ui.php index b506d0add..3f2febad0 100644 --- a/includes/pull-ui.php +++ b/includes/pull-ui.php @@ -66,12 +66,10 @@ function setup_list_table() { * Modify the maximum number of external connection post types are * queried with requesting the post type. * - * @hook dt_external_connections_per_page - * * @since 2.2.0 * - * @param {int} $max_connections The maximum number of external connections to load. - * @return {int} The maximum number of external connections to load. + * @param int $max_connections The maximum number of external connections to load. + * @return int The maximum number of external connections to load. */ 'posts_per_page' => apply_filters( 'dt_external_connections_per_page', 200 ), // @codingStandardsIgnoreLine This high pagination limit is purposeful ) @@ -161,11 +159,10 @@ function action_admin_menu() { * Filter Distributor capabilities allowed to pull content. * * @since 1.0.0 - * @hook dt_pull_capabilities * - * @param {string} 'manage_options' The capability allowed to pull content. + * @param string 'manage_options' The capability allowed to pull content. * - * @return {string} The capability allowed to pull content. + * @return string The capability allowed to pull content. */ apply_filters( 'dt_pull_capabilities', 'manage_options' ), 'pull', diff --git a/includes/push-ui.php b/includes/push-ui.php index a62bf853d..f4b294a64 100644 --- a/includes/push-ui.php +++ b/includes/push-ui.php @@ -48,12 +48,11 @@ function syndicatable() { /** * Filter Distributor capabilities allowed to syndicate content. * - * @hook dt_syndicatable_capabilities * @tutorial snippets * - * @param {string} edit_posts The capability allowed to syndicate content. + * @param string edit_posts The capability allowed to syndicate content. * - * @return {string} The capability allowed to syndicate content. + * @return string The capability allowed to syndicate content. */ if ( ! is_user_logged_in() || ! current_user_can( apply_filters( 'dt_syndicatable_capabilities', 'edit_posts' ) ) ) { return false; @@ -66,11 +65,9 @@ function syndicatable() { * * Helpful for sites that want to push custom post type content to another site. * - * @hook dt_available_push_post_types + * @param array Post types that are distributable. * - * @param {array} Post types that are distributable. - * - * @return {array} Post types available for push. + * @return array Post types available for push. */ $distributable_post_types = apply_filters( 'dt_available_push_post_types', $distributable_post_types ); @@ -164,12 +161,10 @@ function get_connections() { * Modify the maximum number of external connection post types are * queried with requesting the post type. * - * @hook dt_external_connections_per_page - * * @since 2.2.0 * - * @param {int} $max_connections The maximum number of external connections to load. - * @return {int} The maximum number of external connections to load. + * @param int $max_connections The maximum number of external connections to load. + * @return int The maximum number of external connections to load. */ 'posts_per_page' => apply_filters( 'dt_external_connections_per_page', 200 ), // @codingStandardsIgnoreLine This high pagination limit is purposeful 'no_found_rows' => true, @@ -209,11 +204,10 @@ function get_connections() { * Filter Distributor capabilities allowed to push content. * * @since 1.0.0 - * @hook dt_push_capabilities * - * @param {string} 'manage_options' The capability allowed to push content. + * @param string 'manage_options' The capability allowed to push content. * - * @return {string} The capability allowed to push content. + * @return string The capability allowed to push content. */ if ( ! current_user_can( apply_filters( 'dt_push_capabilities', 'manage_options' ) ) ) { $current_user_roles = (array) wp_get_current_user()->roles; @@ -438,11 +432,10 @@ function enqueue_scripts( $hook ) { * See {@link https://vip.wordpress.com/documentation/handling-frontend-file-uploads/#handling-ajax-requests} * * @since 1.0.0 - * @hook dt_ajax_requires_with_credentials * - * @param {bool} false Whether front end ajax requests should use xhrFields credentials:true. + * @param bool false Whether front end ajax requests should use xhrFields credentials:true. * - * @return {bool} Whether front end ajax requests should use xhrFields credentials:true. + * @return bool Whether front end ajax requests should use xhrFields credentials:true. */ 'usexhr' => apply_filters( 'dt_ajax_requires_with_credentials', false ), ); diff --git a/includes/rest-api.php b/includes/rest-api.php index 8d60a4f7c..8c6112acf 100644 --- a/includes/rest-api.php +++ b/includes/rest-api.php @@ -151,11 +151,10 @@ function process_distributor_attributes( $post, $request, $update ) { * Fires after an API push is handled by Distributor. * * @since 1.0 - * @hook dt_process_distributor_attributes * - * @param {WP_Post} $post Inserted or updated post object. - * @param {WP_REST_Request} $request Request object. - * @param {bool} $update True when creating a post, false when updating. + * @param WP_Post $post Inserted or updated post object. + * @param WP_REST_Request $request Request object. + * @param bool $update True when creating a post, false when updating. */ do_action( 'dt_process_distributor_attributes', $post, $request, $update ); } @@ -712,12 +711,10 @@ function get_pull_content_list( $request ) { * * Enables adding extra arguments or setting defaults for a post collection request. * - * @hook dt_get_pull_content_rest_query_args + * @param array $args Array of arguments for WP_Query. + * @param WP_REST_Request $request The REST API request. * - * @param {array} $args Array of arguments for WP_Query. - * @param {WP_REST_Request} $request The REST API request. - * - * @return {array} The array of arguments for WP_Query. + * @return array The array of arguments for WP_Query. */ $args = apply_filters( 'dt_get_pull_content_rest_query_args', $args, $request ); diff --git a/includes/subscriptions.php b/includes/subscriptions.php index 06ec0730d..c77f3f6ef 100644 --- a/includes/subscriptions.php +++ b/includes/subscriptions.php @@ -291,12 +291,10 @@ function send_notifications( $post ) { /** * Filter the timeout used when calling `\Distributor\Subscriptions\send_notifications` * - * @hook dt_subscription_post_timeout + * @param int $timeout The timeout to use for the remote post. Default `5`. + * @param WP_Post $post The post object * - * @param {int} $timeout The timeout to use for the remote post. Default `5`. - * @param {WP_Post} $post The post object - * - * @return {int} The timeout to use for the remote post. + * @return int The timeout to use for the remote post. */ $request_timeout = apply_filters( 'dt_subscription_post_timeout', 5, $post ); @@ -304,12 +302,11 @@ function send_notifications( $post ) { * Filter the arguments sent to the remote server during a subscription update. * * @since 1.3.0 - * @hook dt_subscription_post_args * - * @param {array} $post_body The request body to send. - * @param {WP_Post} $post The WP_Post that is being pushed. + * @param array $post_body The request body to send. + * @param WP_Post $post The WP_Post that is being pushed. * - * @return {array} The request body to send. + * @return array The request body to send. */ $post_body = apply_filters( 'dt_subscription_post_args', $post_body, $post ); @@ -370,5 +367,3 @@ function register_cpt() { register_post_type( 'dt_subscription', $args ); } - - diff --git a/includes/syndicated-post-ui.php b/includes/syndicated-post-ui.php index f0bf76b4a..d2d4a64b2 100644 --- a/includes/syndicated-post-ui.php +++ b/includes/syndicated-post-ui.php @@ -90,12 +90,12 @@ function output_distributor_column( $column_name, $post_id ) { } else { $unlinked = (bool) get_post_meta( $post_id, 'dt_unlinked', true ); $post_url = get_post_meta( $post_id, 'dt_original_post_url', true ); - + if ( $unlinked ) { echo ''; } else { echo ''; - } + } } } } @@ -307,12 +307,11 @@ function unlink() { * Filters whether the post can be unlinked. * * @since 1.0 - * @hook dt_allow_post_unlink * - * @param {bool} true Whether the post is allowed to be unlinked. Default true. - * @param {int} $post_id The ID of the post attempting to be unlinked. + * @param bool true Whether the post is allowed to be unlinked. Default true. + * @param int $post_id The ID of the post attempting to be unlinked. * - * @return {bool} Whether the post is allowed to be unlinked. + * @return bool Whether the post is allowed to be unlinked. */ ! apply_filters( 'dt_allow_post_unlink', true, $post_id ) ) { return; @@ -327,9 +326,8 @@ function unlink() { * Fires when a post is unlinked. * * @since 1.0 - * @hook dt_unlink_post * - * @param {int} $post_id ID of the post being unlinked. + * @param int $post_id ID of the post being unlinked. */ do_action( 'dt_unlink_post', $post_id ); @@ -404,9 +402,8 @@ function link() { * Fires when a post is linked. * * @since 1.0 - * @hook dt_link_post * - * @param {int} $post_id ID of the post being linked. + * @param int $post_id ID of the post being linked. */ do_action( 'dt_link_post', $post_id ); diff --git a/includes/template-tags.php b/includes/template-tags.php index 95c408318..2dacd70af 100644 --- a/includes/template-tags.php +++ b/includes/template-tags.php @@ -171,11 +171,10 @@ function distributor_get_original_site_link( $post_id = null ) { * Filter the original site link for a distributed post. * * @since 1.0.0 - * @hook distributor_get_original_site_link * - * @param {string} $link A formatted version of the original site link. + * @param string $link A formatted version of the original site link. * - * @return {string} A formatted version of the original site link. + * @return string A formatted version of the original site link. */ /* translators: %1$s: site url, %2$s; site name*/ return apply_filters( 'distributor_get_original_site_link', sprintf( __( 'By %2$s', 'distributor' ), esc_url( $site_url ), esc_html( $site_name ) ) ); @@ -239,4 +238,3 @@ function distributor_the_connection_source( $post_id = null, $preface = null ) { printf( '%s %s', esc_html( $preface ), esc_url( $url ), esc_html( $site_name ) ); } } - diff --git a/includes/utils.php b/includes/utils.php index c89860658..2e4fd3029 100644 --- a/includes/utils.php +++ b/includes/utils.php @@ -167,10 +167,9 @@ function set_meta( $post_id, $meta ) { * from the saved meta data. * * @since 2.0.0 - * @hook dt_before_set_meta * - * @param {array} $meta All received meta for the post - * @param {int} $post_id Post ID + * @param array $meta All received meta for the post + * @param int $post_id Post ID */ $meta = apply_filters( 'dt_before_set_meta', $meta, $post_id ); @@ -210,12 +209,11 @@ function set_meta( $post_id, $meta ) { * Take care to continue to filter out excluded keys in any further meta setting. * * @since 1.3.8 - * @hook dt_after_set_meta * @tutorial snippets * - * @param {array} $meta All received meta for the post - * @param {array} $existing_meta Existing meta for the post - * @param {int} $post_id Post ID + * @param array $meta All received meta for the post + * @param array $existing_meta Existing meta for the post + * @param int $post_id Post ID */ do_action( 'dt_after_set_meta', $meta, $existing_meta, $post_id ); } @@ -261,15 +259,14 @@ function available_pull_post_types( $connection, $type ) { * Helpful for sites that want to pull custom post type content from another site into a different existing post type on the receiving end. * * @since 1.3.5 - * @hook dt_available_pull_post_types * - * @param {array} $post_types Post types available for pull with name and slug. - * @param {array} $remote_post_types Post types available from the remote connection. - * @param {array} $local_post_types Post types registered as public on the local site. - * @param {Connection} $connection Distributor connection object. - * @param {string} $type Distributor connection type. + * @param array $post_types Post types available for pull with name and slug. + * @param array $remote_post_types Post types available from the remote connection. + * @param array $local_post_types Post types registered as public on the local site. + * @param Connection $connection Distributor connection object. + * @param string $type Distributor connection type. * - * @return {array} Post types available for pull with name and slug. + * @return array Post types available for pull with name and slug. */ $pull_post_types = apply_filters( 'dt_available_pull_post_types', $post_types, $remote_post_types, $local_post_types, $connection, $type ); @@ -312,12 +309,11 @@ function distributable_post_types( $output = 'names' ) { * Filter post types that are distributable. * * @since 1.0.0 - * @hook distributable_post_types * @tutorial snippets * - * @param {array} Post types that are distributable. + * @param array Post types that are distributable. * - * @return {array} Post types that are distributable. + * @return array Post types that are distributable. */ $post_types = apply_filters( 'distributable_post_types', $post_types ); @@ -345,11 +341,9 @@ function distributable_post_statuses() { * * By default only published posts can be distributed. * - * @hook dt_distributable_post_statuses + * @param array $statuses Post statuses that are distributable. Default `publish`. * - * @param {array} $statuses Post statuses that are distributable. Default `publish`. - * - * @return {array} Post statuses that are distributable. + * @return array Post statuses that are distributable. */ return apply_filters( 'dt_distributable_post_statuses', array( 'publish' ) ); } @@ -417,12 +411,11 @@ function excluded_meta() { * Filter meta keys that are excluded from distribution. * * @since 1.9.0 - * @hook dt_excluded_meta * @tutorial snippets * - * @param {array} $meta_keys Excluded meta keys. Default `dt_unlinked, dt_connection_map, dt_subscription_update, dt_subscriptions, dt_subscription_signature, dt_original_post_id, dt_original_post_url, dt_original_blog_id, dt_syndicate_time, _wp_attached_file, _wp_attachment_metadata, _edit_lock, _edit_last, _wp_old_slug, _wp_old_date`. + * @param array $meta_keys Excluded meta keys. Default `dt_unlinked, dt_connection_map, dt_subscription_update, dt_subscriptions, dt_subscription_signature, dt_original_post_id, dt_original_post_url, dt_original_blog_id, dt_syndicate_time, _wp_attached_file, _wp_attachment_metadata, _edit_lock, _edit_last, _wp_old_slug, _wp_old_date`. * - * @return {array} Excluded meta keys. + * @return array Excluded meta keys. */ return apply_filters( 'dt_excluded_meta', $excluded_meta ); } @@ -454,14 +447,12 @@ function prepare_meta( $post_id ) { /** * Filter whether to sync meta. * - * @hook dt_sync_meta - * - * @param {bool} $sync_meta Whether to sync meta. Default `true`. - * @param {string} $meta_key The meta key. - * @param {mixed} $meta_value The meta value. - * @param {int} $post_id The post ID. + * @param bool $sync_meta Whether to sync meta. Default `true`. + * @param string $meta_key The meta key. + * @param mixed $meta_value The meta value. + * @param int $post_id The post ID. * - * @return {bool} Whether to sync meta. + * @return bool Whether to sync meta. */ if ( false === apply_filters( 'dt_sync_meta', true, $meta_key, $meta_value, $post_id ) ) { continue; @@ -479,12 +470,11 @@ function prepare_meta( $post_id ) { * see `excluded_meta()`. * * @since 2.0.0 - * @hook dt_prepared_meta * - * @param {array} $prepared_meta Prepared meta. - * @param {int} $post_id Post ID. + * @param array $prepared_meta Prepared meta. + * @param int $post_id Post ID. * - * @return {array} Prepared meta. + * @return array Prepared meta. */ $prepared_meta = apply_filters( 'dt_prepared_meta', $prepared_meta, $post_id ); @@ -537,12 +527,11 @@ function prepare_taxonomy_terms( $post_id, $args = array() ) { * Filters the taxonomies that should be synced. * * @since 1.0 - * @hook dt_syncable_taxonomies * - * @param {array} $taxonomies Associative array list of taxonomies supported by current post in the format of `$taxonomy => $terms`. - * @param {WP_Post} $post The post object. + * @param array $taxonomies Associative array list of taxonomies supported by current post in the format of `$taxonomy => $terms`. + * @param WP_Post $post The post object. * - * @return {array} Associative array list of taxonomies supported by current post in the format of `$taxonomy => $terms`. + * @return array Associative array list of taxonomies supported by current post in the format of `$taxonomy => $terms`. */ $taxonomies = apply_filters( 'dt_syncable_taxonomies', $taxonomies, $post ); @@ -558,12 +547,11 @@ function prepare_taxonomy_terms( $post_id, $args = array() ) { * taxonomies permitted for distribution. See the `dt_syncable_taxonomies` hook. * * @since 2.0.0 - * @hook dt_prepared_taxonomy_terms * - * @param {array} $taxonomy_terms Associative array of terms keyed by taxonomy. - * @param {int} $post_id Post ID. + * @param array $taxonomy_terms Associative array of terms keyed by taxonomy. + * @param int $post_id Post ID. * - * @param {array} $args Modified array of terms keyed by taxonomy. + * @param array $args Modified array of terms keyed by taxonomy. */ $taxonomy_terms = apply_filters( 'dt_prepared_taxonomy_terms', $taxonomy_terms, $post_id ); @@ -617,14 +605,13 @@ function set_taxonomy_terms( $post_id, $taxonomy_terms ) { * Filter whether missing terms should be created. * * @since 1.0.0 - * @hook dt_create_missing_terms * - * @param {bool} true Whether missing terms should be created. Default `true`. - * @param {string} $taxonomy The taxonomy name. - * @param {array} $term_array Term data. - * @param {WP_Term|array|false} $term `WP_Term` object or `array` if found, `false` if not. + * @param bool true Whether missing terms should be created. Default `true`. + * @param string $taxonomy The taxonomy name. + * @param array $term_array Term data. + * @param WP_Term|array|false} $term `WP_Term` object or `array` if found, `false` if not. * - * @return {bool} Whether missing terms should be created. + * @return bool Whether missing terms should be created. */ $create_missing_terms = apply_filters( 'dt_create_missing_terms', true, $taxonomy, $term_array, $term ); @@ -659,12 +646,11 @@ function set_taxonomy_terms( $post_id, $taxonomy_terms ) { * Filter whether term hierarchy should be updated. * * @since 1.0.0 - * @hook dt_update_term_hierarchy * - * @param {bool} true Whether term hierarchy should be updated. Default `true`. - * @param {string} $taxonomy The taxonomy slug for the current term. + * @param bool true Whether term hierarchy should be updated. Default `true`. + * @param string $taxonomy The taxonomy slug for the current term. * - * @return {bool} Whether term hierarchy should be updated. + * @return bool Whether term hierarchy should be updated. */ $update_term_hierarchy = apply_filters( 'dt_update_term_hierarchy', true, $taxonomy ); @@ -724,13 +710,12 @@ function set_media( $post_id, $media, $args = [] ) { * Allow filtering of the set_media args. * * @since 1.6.0 - * @hook dt_set_media_args * - * @param {array} $args List of args. - * @param {int} $post_id Post ID. - * @param {array} $media Array of media posts. + * @param array $args List of args. + * @param int $post_id Post ID. + * @param array $media Array of media posts. * - * @return {array} set_media args. + * @return array set_media args. */ $args = apply_filters( 'dt_set_media_args', $args, $post_id, $media ); @@ -762,12 +747,11 @@ function set_media( $post_id, $media, $args = [] ) { * Filter whether media should be deleted and replaced if it already exists. * * @since 1.0.0 - * @hook dt_sync_media_delete_and_replace * - * @param {bool} true Whether pre-existing media should be deleted and replaced. Default `false`. - * @param {int} $post_id The post ID. + * @param bool true Whether pre-existing media should be deleted and replaced. Default `false`. + * @param int $post_id The post ID. * - * @return {bool} Whether pre-existing media should be deleted and replaced. + * @return bool Whether pre-existing media should be deleted and replaced. */ if ( apply_filters( 'dt_sync_media_delete_and_replace', false, $post_id ) ) { if ( ! empty( $current_media[ $media_item['source_url'] ] ) ) { @@ -869,12 +853,10 @@ function format_media_post( $media_post, $post_id = 0 ) { /** * Filter media details retrieved by `wp_get_attachment_metadata()`. * - * @hook dt_get_media_details + * @param array|false $metadata Array of media metadata. `false` on failure. + * @param int $media_post->ID The media post ID. * - * @param {array|false} $metadata Array of media metadata. `false` on failure. - * @param {int} $media_post->ID The media post ID. - * - * @return {array} Array of media metadata. + * @return array Array of media metadata. */ $media_item['media_details'] = apply_filters( 'dt_get_media_details', wp_get_attachment_metadata( $media_post->ID ), $media_post->ID ); $media_item['post'] = $media_post->post_parent; @@ -885,12 +867,10 @@ function format_media_post( $media_post, $post_id = 0 ) { /** * Filter formatted media item. * - * @hook dt_media_item_formatted - * - * @param {array} $media_item Array of media item details. - * @param {int} $media_post->ID The media post ID. + * @param array $media_item Array of media item details. + * @param int $media_post->ID The media post ID. * - * @return {array} Array of media item details. + * @return array Array of media item details. */ return apply_filters( 'dt_media_item_formatted', $media_item, $media_post->ID ); } @@ -919,7 +899,6 @@ function process_media( $url, $post_id, $args = [] ) { * Allow filtering of the process_media args. * * @since 1.6.0 - * @hook dt_process_media_args * * @param array $args List of args. * @param string $url URL of media. @@ -933,13 +912,12 @@ function process_media( $url, $post_id, $args = [] ) { * Filter allowed media extensions to be processed * * @since 1.3.7 - * @hook dt_allowed_media_extensions * - * @param {array} $allowed_extensions Allowed extensions array. - * @param {string} $url Media url. - * @param {int} $post_id Post ID. + * @param array $allowed_extensions Allowed extensions array. + * @param string $url Media url. + * @param int $post_id Post ID. * - * @return {array} Media extensions to be processed. + * @return array Media extensions to be processed. */ $allowed_extensions = apply_filters( 'dt_allowed_media_extensions', array( 'jpg', 'jpeg', 'jpe', 'gif', 'png' ), $url, $post_id ); preg_match( '/[^\?]+\.(' . implode( '|', $allowed_extensions ) . ')\b/i', $url, $matches ); @@ -953,13 +931,12 @@ function process_media( $url, $post_id, $args = [] ) { * Filter name of the processing media. * * @since 1.3.7 - * @hook dt_media_processing_filename * - * @param {string} $media_name Filename of the media being processed. - * @param {string} $url Media url. - * @param {int} $post_id Post ID. + * @param string $media_name Filename of the media being processed. + * @param string $url Media url. + * @param int $post_id Post ID. * - * @return {string} Filename of the media being processed. + * @return string Filename of the media being processed. */ $media_name = apply_filters( 'dt_media_processing_filename', $media_name, $url, $post_id ); @@ -999,11 +976,10 @@ function process_media( $url, $post_id, $args = [] ) { * Allow filtering whether to save the source file path. * * @since 1.6.0 - * @hook dt_process_media_save_source_file_path * - * @param {boolean} $save_file Whether to save the source file path. Default `false`. + * @param boolean $save_file Whether to save the source file path. Default `false`. * - * @return {boolean} Whether to save the source file path or not. + * @return boolean Whether to save the source file path or not. */ $save_source_file_path = apply_filters( 'dt_process_media_save_source_file_path', false ); @@ -1195,13 +1171,12 @@ function update_content_image_urls( int $post_id, array $images ) { * Filter whether image URLS should be updated in the content. * * @since 2.1.0 - * @hook dt_update_content_image_urls * - * @param {bool} true Whether image URLs should be updated. Default `true`. - * @param {int} $post_id The post ID. - * @param {array} $images The old image details. + * @param bool true Whether image URLs should be updated. Default `true`. + * @param int $post_id The post ID. + * @param array $images The old image details. * - * @return {bool} Whether image URLs should be updated. + * @return bool Whether image URLs should be updated. */ if ( ! apply_filters( 'dt_update_content_image_urls', true, $post_id, $images ) ) { return; @@ -1315,12 +1290,11 @@ function dt_use_block_editor_for_post_type( $post_type ) { * Filters whether an item is able to be edited in the block editor. * * @since 1.6.9 - * @hook dt_use_block_editor_for_post_type * - * @param {bool} $use_block_editor Whether the post type uses the block editor. Default true. - * @param {string} $post_type The post type being checked. + * @param bool $use_block_editor Whether the post type uses the block editor. Default true. + * @param string $post_type The post type being checked. * - * @return {bool} Whether the post type uses the block editor. + * @return bool Whether the post type uses the block editor. */ return apply_filters( 'dt_use_block_editor_for_post_type', true, $post_type ); } @@ -1379,13 +1353,11 @@ function get_rest_url( $blog_id, $post_id ) { /** * Allow filtering of the REST API URL used for pulling post content. * - * @hook dt_get_rest_url - * - * @param {string} $rest_url The default REST URL to the post. - * @param {int} $blog_id The blog ID. - * @param {int} $post_id The post ID being retrieved. + * @param string $rest_url The default REST URL to the post. + * @param int $blog_id The blog ID. + * @param int $post_id The post ID being retrieved. * - * @return {string} REST API URL for pulling post content. + * @return string REST API URL for pulling post content. */ return apply_filters( 'dt_get_rest_url', $rest_url, $blog_id, $post_id ); } diff --git a/templates/show-connections-amp.php b/templates/show-connections-amp.php index 397e6d543..86a54f309 100644 --- a/templates/show-connections-amp.php +++ b/templates/show-connections-amp.php @@ -60,13 +60,11 @@ class="add-connection{{#syndicated}} syndicated{{/syndicated}}" /** * Filter whether the 'As Draft' option appears in the push ui. * - * @hook dt_allow_as_draft_distribute + * @param bool $as_draft Whether the 'As Draft' option should appear. + * @param object $connection The connection being used to push. + * @param WP_Post $post The post being pushed. * - * @param {bool} $as_draft Whether the 'As Draft' option should appear. - * @param {object} $connection The connection being used to push. - * @param {WP_Post} $post The post being pushed. - * - * @return {bool} Whether the 'As Draft' option should appear. + * @return bool Whether the 'As Draft' option should appear. */ $as_draft = apply_filters( 'dt_allow_as_draft_distribute', $as_draft, $connection = null, $post ); ?> diff --git a/templates/show-connections.php b/templates/show-connections.php index cdfeb7c2f..b89487e36 100644 --- a/templates/show-connections.php +++ b/templates/show-connections.php @@ -58,13 +58,11 @@ class="add-connection<# if ( ! _.isEmpty( connections[ key ]['syndicated'] ) ) { /** * Filter whether the 'As Draft' option appears in the push ui. * - * @hook dt_allow_as_draft_distribute + * @param bool $as_draft Whether the 'As Draft' option should appear. + * @param object $connection The connection being used to push. + * @param WP_Post $post The post being pushed. * - * @param {bool} $as_draft Whether the 'As Draft' option should appear. - * @param {object} $connection The connection being used to push. - * @param {WP_Post} $post The post being pushed. - * - * @return {bool} Whether the 'As Draft' option should appear. + * @return bool Whether the 'As Draft' option should appear. */ $as_draft = apply_filters( 'dt_allow_as_draft_distribute', $as_draft, $connection = null, $post ); ?> From abac425df8a36b83d4a23fb2504a755fc2414498 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Fri, 12 Sep 2025 13:27:42 +0530 Subject: [PATCH 03/25] Add initial docs. --- .../docs/01.get-started/01.installation.md | 25 ++ .../02.how-to-distribute-content.md | 43 +++ .../01.get-started/03.known-caveats-issues.md | 67 ++++ wp-hooks-docs/docs/01.get-started/index.md | 35 ++ .../01.connect-to-an-existing-post.md | 112 +++++++ ....exclude-meta-from-yoast-duplicate-post.md | 31 ++ ....migration-guide-version-1-to-version-2.md | 63 ++++ .../docs/02.advanced-docs/04.post-meta.md | 81 +++++ .../docs/02.advanced-docs/05.snippets.md | 206 ++++++++++++ .../06.demo-with-playground.md | 17 + .../02.advanced-docs/07.stored-id-handling.md | 310 ++++++++++++++++++ .../02.advanced-docs/08.auto-distribution.md | 74 +++++ wp-hooks-docs/docs/02.advanced-docs/index.md | 33 ++ 13 files changed, 1097 insertions(+) create mode 100644 wp-hooks-docs/docs/01.get-started/01.installation.md create mode 100644 wp-hooks-docs/docs/01.get-started/02.how-to-distribute-content.md create mode 100644 wp-hooks-docs/docs/01.get-started/03.known-caveats-issues.md create mode 100644 wp-hooks-docs/docs/01.get-started/index.md create mode 100644 wp-hooks-docs/docs/02.advanced-docs/01.connect-to-an-existing-post.md create mode 100644 wp-hooks-docs/docs/02.advanced-docs/02.exclude-meta-from-yoast-duplicate-post.md create mode 100644 wp-hooks-docs/docs/02.advanced-docs/03.migration-guide-version-1-to-version-2.md create mode 100644 wp-hooks-docs/docs/02.advanced-docs/04.post-meta.md create mode 100644 wp-hooks-docs/docs/02.advanced-docs/05.snippets.md create mode 100644 wp-hooks-docs/docs/02.advanced-docs/06.demo-with-playground.md create mode 100644 wp-hooks-docs/docs/02.advanced-docs/07.stored-id-handling.md create mode 100644 wp-hooks-docs/docs/02.advanced-docs/08.auto-distribution.md create mode 100644 wp-hooks-docs/docs/02.advanced-docs/index.md diff --git a/wp-hooks-docs/docs/01.get-started/01.installation.md b/wp-hooks-docs/docs/01.get-started/01.installation.md new file mode 100644 index 000000000..97fdeb6ca --- /dev/null +++ b/wp-hooks-docs/docs/01.get-started/01.installation.md @@ -0,0 +1,25 @@ +--- +id: installation +title: "Distributor Plugin Installation" +sidebar_label: "Installation" +sidebar_position: 1 +--- + +For Production use, we recommend [registering and downloading the plugin from DistributorPlugin.com](https://distributorplugin.com/#cta) – it's 100% free. You will be emailed a direct link to download the latest, production-ready build. Alternatively, you can [download the latest release from GitHub](https://github.com/10up/distributor/archive/trunk.zip). + +You can upload and install the archived (zip) plugin via the WordPress dashboard (`Plugins` > `Add New` -> `Upload Plugin`) or manually inside of the `wp-content/plugins` directory, and activate on the Plugins dashboard. + +### Registration + +To help inform our roadmap, keep adopters apprised of major updates and changes that could impact their websites, and solicit opportunities for beta testing and feedback, we’re asking for a little bit of information in exchange for a free key that unlocks update notifications and 1-click upgrades inside the WordPress dashboard. Your information is kept confidential. You can [register here](https://distributorplugin.com/#cta) and input your key in Distributor settings in the dashboard (network dashboard for multisite users). Note that you need to input the email address you used to register Distributor (included in the email with your registration key) as that is linked to the registration key. + +### Set up External Connections + +1. Ensure that the current version of Distributor is active on BOTH sites being connected. We'll refer to these as mainsite.com and remotesite.com. +1. On mainsite.com, navigate to `Distributor` > `External Connections` and click `Add New`. +1. Enter a label for the connection (e.g., `remotesite`). +1. Enter the URL (e.g. `https://remotesite.com`) for your remote site below the External Site URL and press the `Authorize Connection` button. +1. You will be prompted to enter the user name and password of an administrative role of the `remotesite.com` if you are not already logged into `remotesite.com` and then redirected to the Authorize Application screen. +1. At the Authorize Application screen, enter the name of the main site and press the 'Yes, I approve of this connection' button +1. Review the roles selected in `Roles Allowed to Push` are the ones you want to support, update if necessary, then press the `Update Connection` button. + diff --git a/wp-hooks-docs/docs/01.get-started/02.how-to-distribute-content.md b/wp-hooks-docs/docs/01.get-started/02.how-to-distribute-content.md new file mode 100644 index 000000000..84ad68062 --- /dev/null +++ b/wp-hooks-docs/docs/01.get-started/02.how-to-distribute-content.md @@ -0,0 +1,43 @@ +--- +id: how-to-distribute-content +title: "How to Distribute Content" +sidebar_label: "How to Distribute Content" +--- + +There are two methods for distributing content between multiple WordPress sites, Push and Pull. Pushing allows you to share content from your site to one or more connected sites while Pulling allows you to bring content into your site from one of your connected sites. In either method, once content has been distributed it will stay in sync with any changes made to the origin post (when Pushing the origin is the site being Pushed from, when Pulling the origin is the site being Pulled from). + +### Pushing Content + +The `Distributor` menu in the WP Admin Bar will appear after a piece of content has been published. Hovering over that menu item will expose the Push menu that displays the list of connected sites on the left, the list of sites that have been selected for push distribution on the right, and a button to `Distribute` the content to those selected sites. + +!["Push menu exposed when viewing published content on the front-end"](https://raw.githubusercontent.com/10up/distributor/refs/heads/trunk/.github/screenshots/screenshot-1.png "Push menu exposed when viewing published content on the front-end") + +The same Push menu and set of Distributor options are also available after publishing a piece of content within the WordPress Block Editor. + +!["Push menu exposed when viewing published content in the Block Editor"](https://raw.githubusercontent.com/10up/distributor/refs/heads/trunk/.github/screenshots/screenshot-2.png "Push menu exposed when viewing published content in the Block Editor") + +After you click the `Distribute` button, Distributor will push the content to the selected connected sites, showing a `View` link when those pieces of content have been distributed, and noting `Post successfully distributed.` once the content has completed distributing to selected sites. + +!["Push menu showing details after content distribution"](https://raw.githubusercontent.com/10up/distributor/refs/heads/trunk/.github/screenshots/screenshot-3.png "Push menu showing details after content distribution") + +When viewing that piece of content in the Block Editor, there will be a Distributor notice in the `Status & visibility` section noting how many sites the content has been distributed to. + +!["Block Editor sidebar showing Distributor count of sites that content has been distributed to (via Push and Pull)"](https://raw.githubusercontent.com/10up/distributor/refs/heads/trunk/.github/screenshots/screenshot-4.png "Block Editor sidebar showing Distributor count of sites that content has been distributed to (via Push and Pull)") + +The same Push menu is available in the WP Admin Bar if you are using the Classic Editor. The Distributor notice is also available in the `Publish` metabox noting how many sites the content has been distributed to. + +!["Classic Editor showing the Push menu and metabox showing Distributor count of sites that content has been distributed to (via Push and Pull)"](https://raw.githubusercontent.com/10up/distributor/refs/heads/trunk/.github/screenshots/screenshot-5.png "Classic Editor showing the Push menu and metabox showing Distributor count of sites that content has been distributed to (via Push and Pull)") + +### Pulling Content + +Navigating to the `Distributor` > `Pull Content` screen in the WP Admin will present you with a dropdown to select any of the sites you are connected to and will display all available pieces of content that can be Pulled into the current site. You can select Posts, Pages, and other Custom Post Types to filter on this screen; you can use the Bulk Edit menu to Pull or Skip more than one piece of content at a time; and you can use individual row actions on each piece of content pull, view, or skip each piece of content. + +!["Pull Content screen showing row actions and a single post selected for Pulling"](https://raw.githubusercontent.com/10up/distributor/refs/heads/trunk/.github/screenshots/screenshot-6.png "Pull Content screen showing row actions and a single post selected for Pulling") + +After you have Bulk Pulled several pieces of content or used the row actions to Pull a single piece of content, the Pull Content screen will show a confirmation message that the Pull action was successful and redirect you to the `Pulled` list view to see all the items that have been pulled into the current site. The same process will happen if you opt to Skip specific piece(s) of content. + +!["Pull Content screen showing confirmation on content being pulled"](https://raw.githubusercontent.com/10up/distributor/refs/heads/trunk/.github/screenshots/screenshot-7.png "Pull Content screen showing confirmation on content being pulled") + +You can navigate to the `Posts` > `All Posts` table list view to see all content that has been pushed or pulled to the current site via the Distributor column denoted with the Distributor icon. Rows that include the Distributor icon will link off that icon to the origin site and post where that content was either pushed or pulled from. + +!["All Posts screen showing Distributor links for pushed and pulled content"](https://raw.githubusercontent.com/10up/distributor/refs/heads/trunk/.github/screenshots/screenshot-8.png "All Posts screen showing Distributor links for pushed and pulled content") diff --git a/wp-hooks-docs/docs/01.get-started/03.known-caveats-issues.md b/wp-hooks-docs/docs/01.get-started/03.known-caveats-issues.md new file mode 100644 index 000000000..2e32987e2 --- /dev/null +++ b/wp-hooks-docs/docs/01.get-started/03.known-caveats-issues.md @@ -0,0 +1,67 @@ +--- +id: known-caveats-issues +title: "Known Caveats/Issues" +sidebar_label: "Known Caveats/Issues" +--- + +### Remote Request Timeouts + +With external connections, HTTP requests are sent back and forth - creating posts, transferring images, syncing post updates, etc. In certain situations, mostly commonly when distributing posts with a large number of images (or very large file sizes), using poorly configured or saturated servers / hosts, or using plugins that add significant weight to post creation, Distributor requests can fail. Although we do some error handling, there are certain cases in which post distribution can fail silently. If distribution requests are taking a long time to load and/or failing, consider adjusting the timeout; you can filter the timeout for pushing external posts using the [`dt_push_post_timeout` filter](https://10up.github.io/distributor/dt_push_post_timeout.html). More advanced handling of large content requests, and improved error handling is on the road map for a future update. + +### Post Meta Associations + +A distributed post includes all of the post meta from the original version. Sometimes arbitrary post meta references an ID for another piece of content on the original site. Distributor _does not_ "bring along" the referenced content or update references for arbitrary post meta (it will take care of updating references in the case of core WordPress features, such as the featured image ID). This issue is very common when using field management plugins like Advanced Custom Fields (ACF). This can be addressed on a case-by-case basis by extending the plugin. Distributor provides the `distributor_register_data` helper function for [stored ID handling](https://10up.github.io/distributor/tutorial-stored-id-handling.html). Additionally, for external connections, you can manually handle post meta associations using [the `dt_push_post` hook](https://github.com/10up/distributor/blob/f7b60740e679bce4671ccd69a670abadce4f2f93/includes/classes/ExternalConnections/WordPressExternalConnection.php#L646). For internal connections, use the [`dt_push_post` hook](https://10up.github.io/distributor/dt_push_post.html). Note that while named the same, these hooks accept different parameters. + +### Stored ID Handling + +Distributor often deals with data stored as ID references. This is common in WordPress, where IDs are used in block attributes, shortcodes, or post meta by popular plugins like ACF, Elementor, and Gravity Forms. However, when content is distributed to another site, these stored IDs may not match, leading to data loss or unexpected content. + +To address this, Distributor provides a helper function: `distributor_register_data`. This function allows developers to register data references, specify where the data is located, define how to extract the ID, and register callbacks to process the data on both the source and target sites. More information can be found [here](https://10up.github.io/distributor/tutorial-stored-id-handling.html). + +### Deleting Distributed Posts + +When a post that has been distributed is deleted, the distributed copies will become unlinked ("forked") from the original and thus become editable. Similarly, when a distributed post is unpublished, distributed copies will not be unpublished. More sophisticated "removal" workflow is on the road map for a future update. + +### Gutenberg Block Mismatch + +When distributing a Gutenberg post to another site that supports Gutenberg, if a block in the post does not exist on the receiving site, the block will be converted to a "Classic" HTML block. + +### Parent Posts + +Distributor does not "bring along" parent (or child posts). If your post (or custom post type) has a parent or a child, it will distribute it as if it's an orphan. + +### Custom Post Type Support + +Internal Connections (multisite) support multiple post types. In order for distribution to work with External Connections that have custom post type content, that post type needs to be registered with the argument `show_in_rest => true` on the external site. + +### Unable to Push to New Custom Post Types + +If new Custom Post Types are created after establishing an External Connection, you will only be able to `Pull` those from an External Connection. To ensure you are able to `Push` new Custom Post Types to an External Connection, you will need to update the External Connection by editing it and then clicking the `Update connection` button. + +### Backwards Compatibility + +While we strive to be mindful of backwards compatibility much the same way WordPress itself is, we do not currently guarantee continued interoperability between different versions of Distributor. We assume the current userbase for this plugin has a high degree of control over any site that has been set up as an external connection and urge you to keep Distributor up to date. + +### Distributing Post content + +By default, post content is rendered before being copied. This means that shortcodes are expanded before being distributed and remote posts will not have the shortcode, but rather the expanded HTML content. + +### Distributing Authors + +By default, distributed posts reference the original site as the "author" with a link to it. This can be altered by extending Distributor with custom code to make it sync authors. + +### Distributing Post Date + +By default, the "post date" on distributed posts is the date its published on the remote site, not the date published on the origin site. This can be overridden by extending Distributor with custom code to make it preserve the post date. + +### Distributing Canonical URL + +By default, canonical URL of distributed post will point to original content, which corresponds to SEO best practices. This can be overridden by extending Distributor with custom code and removing Distributor's default front end canonical URL filtering (look for `'get_canonical_url'` and `'wpseo_canonical'`). + +### Drafts as Preferred Status + +By default, drafts are the preferred status and can't be changed at the source site. + +### Conflicts with Security plugins + +Oftentimes the communication Distributor attempts to make across sites using the REST API will be flagged by various security plugins and surreptitiously blocked. If you run into an issue like this, please reach out to the support for your security plugin and ask about getting Distributor unblocked ([here is an example for doing so with Wordfence](https://wordpress.org/support/topic/distributor-plugin-being-blocked/)). diff --git a/wp-hooks-docs/docs/01.get-started/index.md b/wp-hooks-docs/docs/01.get-started/index.md new file mode 100644 index 000000000..159ce27a1 --- /dev/null +++ b/wp-hooks-docs/docs/01.get-started/index.md @@ -0,0 +1,35 @@ +--- +id: get-started +title: "ClassifAI Docs - Get Started" +sidebar_label: "Get Started" +--- + +# Get Started + +## Overview + +Distributor supports safe, SEO-friendly content reuse and sharing via "pushing" and "pulling". + +While logged in and editing or viewing any single post (or custom post type) that can be distributed, a `Distributor` admin bar item will appear, that will facilitate sharing ("pushing") that content to any `connection`. + +![Push the content you’re editing or viewing to any of your other sites from the admin bar](https://raw.githubusercontent.com/10up/distributor/refs/heads/trunk/.github/screenshots/screenshot-2.png "Push the content you’re editing or viewing to any of your other sites from the admin bar") + +In the admin dashboard, a top level Distributor menu item links to the "pull" screen. Here, editors can share ("pull") content from any `connection` into the current site. + +![Pull content from another site from the Distributor admin menu](https://raw.githubusercontent.com/10up/distributor/refs/heads/trunk/.github/screenshots/screenshot-6.png "Pull content from another site from the Distributor admin menu") + +Content this is distributed (via Push or Pull) is connected to the original. Reposted content receives updates from the original, canonical source automatically. + +![Distributor intuitively presents the origin and status of any reused content](https://raw.githubusercontent.com/10up/distributor/refs/heads/trunk/.github/screenshots/screenshot-9.jpg "Distributor intuitively presents the origin and status of any reused content") + +There are two connection types: `internal` and `external`. +* Internal connections are other sites inside of the same multisite network. Any user logged into the network can distribute any content in the network to any other sites in the network where that user has permission to publish posts (assuming the site supports the same post type). +* External connections are external websites, connected by the JSON REST API using the Authorization Setup Wizard for External Connections leveraging Application Passwords. External connections can be added in the WordPress admin dashboard under `Distributor` > `External Connections`. Administrators can decide which user roles are allowed to distribute content to and from that connection (Editors and Administrators by default). All users with those roles will inherit the permissions of the user account used to establish the remote connection. + +## Requirements + +* PHP 7.4+ +* [WordPress](http://wordpress.org) 6.5+ +* External connections require HTTP Basic Authentication or [WordPress.com OAuth2](https://developer.wordpress.com/docs/oauth2/) (must be on [WordPress VIP](https://wpvip.com/)) be set up on the remote website. For Basic Auth, we recommend using [Application Passwords](https://make.wordpress.org/core/2020/11/05/application-passwords-integration-guide/#Getting-Credentials) built in to WordPress. +* For external connections, Distributor needs to be installed on BOTH sides of the connection. +* Version 2.0.0 of Distributor requires version 2.0.0 on BOTH sides of all connections. For other version 2.0.0 specific changes, please see our [migration guide](https://10up.github.io/distributor/tutorial-migration-guide-version-1-to-version-2.html). diff --git a/wp-hooks-docs/docs/02.advanced-docs/01.connect-to-an-existing-post.md b/wp-hooks-docs/docs/02.advanced-docs/01.connect-to-an-existing-post.md new file mode 100644 index 000000000..6f0c8631f --- /dev/null +++ b/wp-hooks-docs/docs/02.advanced-docs/01.connect-to-an-existing-post.md @@ -0,0 +1,112 @@ +--- +id: connect-to-an-existing-post +title: "Connect to an Existing Post" +--- + +In a scenario where you have existing sites that share content and that content was in place prior to the addition of Distributor, it is useful to be able to connect that content together without having to delete one and push/pull the other one. Behind the scenes, Distributor stores a handful of details in post meta, so these details will need to be manually added for the links to work. + +At it's simplest, you'll need to connect the two sites together and then add the proper meta (as detailed below) for the items that should be linked together, both on the source site and the receiving site. + +## Network Connections + +There are different pieces of data that will need to be set on each side of a connection, what we'll call as the source site and the receiving site. + +The only piece of data needed on the source site to connect two items in Network Connections is the `dt_connection_map`. This is a serialized array of data that contains the mapping from the site ID to the post ID, along with a timestamp of when the item was linked. Note that the site ID and post ID should correspond to the data on the receiving site, not source site. + +This data structure will look like the following: + +```php +$data = [ + [ + 'external' => [], + 'internal' => [ + 2 => [ + 'post_id' => 674, + 'time' => 1693494494, + ], + ], + ], +]; +``` + +In the above example, the `external` array will always be empty, unless an item is also linked to an external site. For the `internal` array, each item in the array will have a key that corresponds to the site ID (2 in the example above) and then the `post_id` should be the destination post ID. + +As a further example, if I have a post with an ID of 100 that lives on a site with an ID of 1 and I want that post to be linked to an existing post with an ID of 50 on the site with an ID of 2, the `dt_connection_map` data that is stored with the original item (ID of 100, site ID 1) would look like the following: + +```php +$data = [ + [ + 'external' => [], + 'internal' => [ + 2 => [ + 'post_id' => 50, + 'time' => 1693494494, + ], + ], + ], +]; +update_post_meta( 100, 'dt_connection_map', $data ); +``` + +And then on the receiving site, the following data is needed: `dt_original_post_id`, `dt_original_post_url`, `dt_original_blog_id` and `dt_syndicate_time`. These should all be fairly self explanatory but to use the same example from above: + +- `dt_original_post_id` would be set to 100 +- `dt_original_post_url` would be the full URL of that post with an ID of 100 +- `dt_original_blog_id` would be set to 1 +- `dt_syndicate_time` should match the same timestamp set in the connection map, in this case 1693494494 + +## External Connections + +External Connections share a similar data structure as detailed above but they also contain a Subscriptions piece, which is more complicated to manually replicate (as such, full details on how to replicate the Subscription is not outlined here). + +Similar to the above, there's a data mapping that is needed on the source site: `dt_connection_map`. This is a serialized array of data that contains the mapping from the external connection ID to the post ID, along with a timestamp of when the item was linked. Note that the post ID should correspond to the data on the receiving site, not source site. + +This data structure will look like the following: + +```php +$data = [ + [ + 'external' => [ + 2 => [ + 'post_id' => 50, + 'time' => 1693494494, + ], + ], + 'internal' => [], + ], +]; +``` + +In the above example, the `internal` array will always be empty, unless an item is also linked to an internal site. For the `external` array, each item in the array will have a key that corresponds to the connection ID (2 in the example above) and then the `post_id` should be the destination post ID. + +As a further example, if I have a post with an ID of 100 that lives on a site with an ID of 1 and I want that post to be linked to an existing post with an ID of 50 on the site with a connection ID of 2, the `dt_connection_map` data that is stored with the original item (ID of 100, site ID 1) would look like the following: + +```php +$data = [ + [ + 'external' => [ + 2 => [ + 'post_id' => 50, + 'time' => 1693494494, + ], + ], + 'internal' => [], + ], +]; +update_post_meta( 100, 'dt_connection_map', $data ); +``` + +In addition, there needs to be a piece of data on the source site that contains the subscription information: `dt_subscriptions`. This contains a serialized array of data that links a hashed signature to the subscription post ID. This isn't something that is easily manually reproduced. Suggestion is to look at using the existing `Distributor\Subscriptions\create_subscription` function to replicate this data. + +And then on the receiving site, the following data is needed: `dt_original_post_id`, `dt_original_post_url`, `dt_original_source_id`, `dt_original_site_name`, `dt_original_site_url`, `dt_subscription_signature`, `dt_full_connection` and `dt_syndicate_time`. + +From the example above: + +- `dt_original_post_id` would be set to 100 +- `dt_original_post_url` would be the full URL of that post with an ID of 100 +- `dt_original_source_id` would be set to 2 (the source connection ID) +- `dt_original_site_name` would be the name of the source site +- `dt_original_site_url` would be the URL of the source site +- `dt_subscription_signature` would be the signature of the subscription mentioned above +- `dt_full_connection` would be set to `true` +- `dt_syndicate_time` should match the same timestamp set in the connection map, in this case 1693494494 diff --git a/wp-hooks-docs/docs/02.advanced-docs/02.exclude-meta-from-yoast-duplicate-post.md b/wp-hooks-docs/docs/02.advanced-docs/02.exclude-meta-from-yoast-duplicate-post.md new file mode 100644 index 000000000..3805328d1 --- /dev/null +++ b/wp-hooks-docs/docs/02.advanced-docs/02.exclude-meta-from-yoast-duplicate-post.md @@ -0,0 +1,31 @@ +--- +id: exclude-meta-from-yoast-duplicate-post +title: "Exclude Meta from Yoast Duplicate Post" +--- + +When duplicating a source post using Yoast Duplicate Post, a distributed post can be connected to two sources if the subscription meta is not excluded. + +[External reference](https://developer.yoast.com/duplicate-post/filters-actions/#duplicate_post_excludelist_filter) + +```php +/** + * Filters out custom fields from being duplicated in addition to the defaults. + * + * @param array $meta_excludelist The default exclusion list, based on the “Do not copy these fields” setting, plus some other field names. + * + * @return array The custom fields to exclude. + */ +function client_prefix__custom_fields_filter( $meta_excludelist ) { + // Merges the defaults array with our own array of custom fields. + return array_merge( + $meta_excludelist, [ + 'dt_connection_map', + 'dt_subscription_update', + 'dt_subscriptions', + 'dt_subscription_signature', + ] + ); +} + +add_filter( 'duplicate_post_excludelist_filter', 'client_prefix__custom_fields_filter' ); +``` diff --git a/wp-hooks-docs/docs/02.advanced-docs/03.migration-guide-version-1-to-version-2.md b/wp-hooks-docs/docs/02.advanced-docs/03.migration-guide-version-1-to-version-2.md new file mode 100644 index 000000000..6fcd2a687 --- /dev/null +++ b/wp-hooks-docs/docs/02.advanced-docs/03.migration-guide-version-1-to-version-2.md @@ -0,0 +1,63 @@ +--- +id: migration-guide-version-1-to-version-2 +title: "Migration Guide Version 1 to Version 2" +--- + +Version 2.0.0 of Distributor includes a number of breaking changes that will require updates to custom code you may have written for distributor. + +## External connections require a minimum of version 2.0.0 + +It is recommended that both ends of an external connection run the same version of Distributor. + +Version 2.0.0 of Distributor will prevent the pulling of posts from sites running Version 1.9.x or lower of Distributor. + +### Remove canonical links for both Internal and External Connections + +The code snippet required to prevent sites from displaying the source post as canonical URLs for distributed posts has changed. + +If you have implemented this using the code snippet from our tutorial file, please update your code to the following: + +```php +/** + * Stop Distributor from changing the canonical links. + * + * This removes Distributor's canonical functionality from both Internal and + * External Connections. + * + * This accounts for sites using either WordPress or Yoast SEO to generate the + * canonical URL. + */ +add_action( 'plugins_loaded', function() { + remove_filter( 'get_canonical_url', '\\Distributor\\Hooks\\get_canonical_url', 10, 2 ); + remove_filter( 'wpseo_canonical', '\\Distributor\\Hooks\\wpseo_canonical', 10, 2 ); +} ); +``` + +### REST API Changes + +The distributor REST API endpoint at `/wp/v2/distributor/list-pull-content` has been modified substantially and will now reject connections from 1.x versions of Distributor. + +The fields returned by the endpoint have been modified to match the names used by `wp_insert_post` and `wp_update_post`. + +#### Additional parameters + +* `include` (Array|Int): Ensure result set includes specific Post IDs. Default empty. +* `order` (`asc`|`desc`): Specify order of returned data. Default `desc`. +* `orderby` (`author`|`date`|`id`|`include`|`modified`|`parent`|`relevance`|`slug`|`title`): Field to order results by. Default `date`, `relevance` for search queries. + +#### Modified parameters + +* `post_type` (String|String[]): Modified to accept multiple post types. Post types are limited to posts the connected account can edit, are public post types and visible in the WordPress REST API's standard endpoints. Default `post`. +* `post_status` (String|String[]): Modified to accept multiple post statuses. Statuses are limited to public statuses only. Default `publish`. + +### `dt_push_post` action deprecated + +The `dt_push_post` action has been deprecated in favor of two actions: `dt_push_external_post` and `dt_push_network_post`. + +Extenders using the old action are advised to switch to the new actions as a matter of priority. The deprecated action had conflicting arguments so can not be relied upon to pass data consistently. + +### Internationalization improvements + +The generation of translation files has been updated to include strings included in JavaScript files. + +Version 2.0.0 of Distributor has also improved pluralization of strings and combined similar strings to reduce the burden on translators. diff --git a/wp-hooks-docs/docs/02.advanced-docs/04.post-meta.md b/wp-hooks-docs/docs/02.advanced-docs/04.post-meta.md new file mode 100644 index 000000000..9cbab34fa --- /dev/null +++ b/wp-hooks-docs/docs/02.advanced-docs/04.post-meta.md @@ -0,0 +1,81 @@ +--- +id: post-meta +title: "Post Meta" +--- + +### Table of Contents +- [Source post meta](#source-post-meta) + - [Network connections](#network-connections) +- [Distributed post meta](#distributed-post-meta) + - [Connects a Source Post to a Subscription](#connects-a-source-post-to-a-subscription) + - [Network connections](#network-connections-1) + - [External connections](#external-connections) +- [Distributed post meta](#distributed-post-meta-1) +- [Connections post type post meta](#connections-post-type-post-meta) + - [External connections](#external-connections-1) + +--- + +### Source post meta + +- `dt_unlinked` Whether this post is linked to the original version. For the original post this is set to true. + +#### Network connections + +- `dt_connection_map` + +--- + +### Distributed post meta + +- `dt_original_post_id` +- `dt_original_media_url` +- `dt_original_media_id` +- `dt_original_source_id` +- `dt_original_post_deleted` +- `dt_original_post_parent` +- `dt_original_site_name` +- `dt_original_site_url` +- `dt_original_post_url` +- `dt_original_deleted` Whether the original post has been deleted. +- `dt_unlinked` Whether this post is linked to the original version. +- `dt_original_file_path` Only saved if filter `dt_process_media_save_source_file_path` is used. +- `dt_syndicate_time` + +#### Connects a Source Post to a Subscription +- `dt_subscriptions` +- `dt_subscription_update` + +#### Network connections + +- `dt_original_blog_id` + +#### External connections + +- `dt_full_connection` + +--- + +### Distributed post meta + +For non-public `dt_subscription` post type. + +- `dt_subscription_remote_post_id` +- `dt_subscription_signature` +- `dt_subscription_remote_post_id` +- `dt_subscription_target_url` +- `dt_subscription_post_id` + +--- + +### Connections post type post meta + +#### External connections + +- `dt_sync_log` +- `dt_external_connection_type` +- `dt_external_connection_allowed_roles` +- `dt_external_connection_check_time` +- `dt_external_connection_url` +- `dt_external_connection_auth` +- `dt_external_connections` Stores what we can do with a given external connection (push or pull). diff --git a/wp-hooks-docs/docs/02.advanced-docs/05.snippets.md b/wp-hooks-docs/docs/02.advanced-docs/05.snippets.md new file mode 100644 index 000000000..ec91adf02 --- /dev/null +++ b/wp-hooks-docs/docs/02.advanced-docs/05.snippets.md @@ -0,0 +1,206 @@ +--- +id: snippets +title: "Snippets" +--- + + +### Table of Contents +- [Limit to certain post types](#limit-to-certain-post-types) +- [Limit to certain user capabilities](#limit-to-certain-user-capabilities) +- [Limit to certain sites on the network](#limit-to-certain-sites-on-the-network) +- [Remove canonical links for both Internal and External Connections](#remove-canonical-links-for-both-internal-and-external-connections) +- [Push original publication date](#push-original-publication-date) +- [Automatically unlink posts](#automatically-unlink-posts) +- [Modify custom meta data](#modify-custom-meta-data) +- [Exclude meta key from distribution](#exclude-meta-key-from-distribution) + +--- + +### Limit to certain post types + +```php +add_filter( 'distributable_post_types', 'client_prefix_filter_post_types' ); +/** + * Filter the post types we can distribute. + * + * @see https://10up.github.io/distributor/distributable_post_types.html + * + * @return array + */ +function client_prefix_filter_post_types() : array { + return array( 'post', 'page' ); +} +``` + +### Limit to certain user capabilities + +```php +add_filter( 'dt_syndicatable_capabilities', 'client_prefix_filter_user_capabilities' ); +/** + * Filter the user capabilities that are allowed to distribute content. + * + * @see https://10up.github.io/distributor/dt_syndicatable_capabilities.html + * + * @return string + */ +function client_prefix_filter_user_capabilities() : string { + return 'manage_options'; +} +``` + +### Limit to certain sites on the network + +```php +add_filter( 'dt_authorized_sites', 'client_prefix_filter_authorized_sites', 10, 2 ); +/** + * Filter certain sites from the authorized sites list. + * + * @see https://10up.github.io/distributor/dt_authorized_sites.html + * + * @param array $authorized_sites Authorized sites. + * @param string $context Push or pull. + * + * @return array + */ +function client_prefix_filter_authorized_sites( array $authorized_sites, string $context ) : array { + return array_filter( + $authorized_sites, + function( $site ) { + return '/' === $site['site']->path; + } + ); +} +``` + +### Remove canonical links for both Internal and External Connections + +```php +/** + * Stop Distributor from changing the canonical links. + * + * This removes Distributor's canonical functionality from both Internal and + * External Connections. + * + * This accounts for sites using either WordPress or Yoast SEO to generate the + * canonical URL. + */ +add_action( 'plugins_loaded', function() { + remove_filter( 'get_canonical_url', '\\Distributor\\Hooks\\get_canonical_url', 10, 2 ); + remove_filter( 'wpseo_canonical', '\\Distributor\\Hooks\\wpseo_canonical', 10, 2 ); +} ); +``` + +### Push original publication date + +```php +/** + * Keep the publication date on the new pushed post. + * + * This filter is used to filter the arguments sent to the remote server during a push. The below code snippet passes the original published date to the new pushed post and sets the same published date instead of setting it as per the current time. + */ +add_filter( 'dt_push_post_args', function( $post_body, $post, $args, $connection ) { + + // When pushing to an external connection, we use the REST API, so the name of the field is `date`. + // But when pushing to an internal connection, the attributes are sent to wp_insert_post, which expects `post_date`. + $field_prefix =( $connection instanceof \Distributor\ExternalConnections\WordPressExternalConnection ) ? '' : 'post_'; + + $post_body[ $field_prefix . 'date'] = $post->post_date; + $post_body[ $field_prefix . 'date_gmt'] = $post->post_date_gmt; + + return $post_body; +}, 10, 4 ); + +/** + * This filters the the arguments passed into wp_insert_post during a pull + */ +add_filter( 'dt_pull_post_args', function( $post_array, $remote_id, $post ) { + $post_array['post_date'] = $post->post_date; + $post_array['post_date_gmt'] = $post->post_date_gmt; + + return $post_array; +}, 10, 3 ); +``` + +### Automatically unlink posts +```php +/** + * Auto unlink distributor posts automatically. + * + * Runs on the `dt_after_set_meta` hook. + * + * @param mixed $meta All received meta for the post + * @param mixed $existing_meta Existing meta for the post + * @param mixed $post_id Post ID + * @return void + */ +function client_prefix_auto_unlink_distributed_posts( $meta, $existing_meta, $post_id ) { + $post = get_post( $post_id ); + + if ( ! $post ) { + return; + } + + $is_distributed = get_post_meta( $post->ID, 'dt_original_post_id', true ) ? true : false; + + if ( ! $is_distributed ) { + return; + } + + update_post_meta( $post->ID, 'dt_unlinked', true ); +} +add_action( 'dt_after_set_meta', 'client_prefix_auto_unlink_distributed_posts', 10, 3 ); +``` + +### Modify custom meta data + +```php +/** + * Set default post meta if not set in the original. + * + * @param {array} $meta All received meta for the post + * @param {array} $existing_meta Existing meta for the post + * @param {int} $post_id Post ID + */ +function client_prefix_modify_meta( $meta, $existing_meta, $post_id ) { + // Set post meta if not set. + if ( ! isset( $existing_meta['my_meta_key'] ) ) { + add_post_meta( $post_id, 'my_meta_key', 'my meta value' ); + } +} +add_action( 'dt_after_set_meta', 'client_prefix_modify_meta', 10, 3 ); +``` + +### Exclude meta key from distribution + +```php +/** + * Denylist a meta key from distribution. + */ +add_filter( 'dt_excluded_meta', function( $meta_keys ) { + $meta_keys[] = 'my_meta_key'; + return $meta_keys; +} ); +``` + +### Turn off automatic updates for distributed content + +```php +/** + * Prevent auto-updates from happening for network connections. + */ +add_action( + 'init', + function() { + remove_action( 'wp_after_insert_post', [ '\Distributor\InternalConnections\NetworkSiteConnection', 'update_syndicated' ], 99 ); + } +); + +/** + * Prevent auto-updates from happening for external connections. + */ +add_action( + 'init', + function() { + remove_action( 'wp_after_insert_post', 'Distributor\Subscriptions\send_notifications', 99 ); + } +); diff --git a/wp-hooks-docs/docs/02.advanced-docs/06.demo-with-playground.md b/wp-hooks-docs/docs/02.advanced-docs/06.demo-with-playground.md new file mode 100644 index 000000000..9b32125f3 --- /dev/null +++ b/wp-hooks-docs/docs/02.advanced-docs/06.demo-with-playground.md @@ -0,0 +1,17 @@ +--- +id: demo-with-playground +title: "Demo with Playground" +--- + +Use WordPress Playground to quickly spin up a project to demonstrate the plugin's features. + +## Example + +This is an example URL you can use to run the project: [https://playground.wordpress.net/?blueprint-url=https://raw.githubusercontent.com/10up/distributor/refs/heads/develop/.github/blueprints/blueprint.json](https://playground.wordpress.net/?blueprint-url=https://raw.githubusercontent.com/10up/distributor/refs/heads/develop/.github/blueprints/blueprint.json) + +## Features + +The project has the following features: +- it is a multisite installation +- the Distributor plugin is installed and activated network-wide +- the network contains two websites diff --git a/wp-hooks-docs/docs/02.advanced-docs/07.stored-id-handling.md b/wp-hooks-docs/docs/02.advanced-docs/07.stored-id-handling.md new file mode 100644 index 000000000..59eb811c0 --- /dev/null +++ b/wp-hooks-docs/docs/02.advanced-docs/07.stored-id-handling.md @@ -0,0 +1,310 @@ +--- +id: stored-id-handling +title: "Stored ID Handling" +--- + +Distributor often deals with data stored as ID references. This is common in WordPress, where IDs are used in block attributes, shortcodes, or post meta by popular plugins like ACF, Elementor, and Gravity Forms. However, when content is distributed to another site, these stored IDs may not match, leading to data loss or unexpected content. + +To address this, Distributor provides a helper function: `distributor_register_data`. This function allows developers to register data references, specify where the data is located, define how to extract the ID, and register callbacks to process the data on both the source and target sites. + +## Overview + +The `distributor_register_data` function streamlines the process of updating stored ID references by: + +- **Identifying the data location:** Whether in post meta or within post content (shortcodes or blocks). +- **Extracting the reference:** Defining how to locate the specific attribute or meta key holding the ID. +- **Processing the data:** Using pre-distribution and post-distribution callbacks to prepare and update the reference IDs, ensuring they point to the correct data on the target site. + +#### **Important:** Ensure that this data registration code is added to both the source and target sites. Also, verify that your Distributor plugin version is the same on both sites and equal to or greater than the required version (2.2.0). + +## Function Definition + +### `distributor_register_data` + +This function registers a data piece by providing details about its location and the callbacks needed to process it during distribution. + +#### Parameters + +- **`$data_name`** (`string`): + The unique identifier for the data. + +- **`$args`** (`array`): + An array of parameters that describe: + + - **`location`** (`string`): + Where the data is stored. Accepted values are `'post_meta'` or `'post_content'`. + + - **`attributes`** (`array`): + Additional details depending on the location: + - **For post meta:** + - `meta_key` (`string`): The post meta key. **(Required)** + - **For shortcodes:** + - `shortcode` (`string`): The shortcode tag. **(Required)** + - `shortcode_attribute` (`string|array`): The attribute(s) containing the data. **(Required)** + - **For blocks:** + - `block_name` (`string`): The block name. **(Required)** + - `block_attribute` (`string|array`): The attribute(s) containing the data. **(Required)** + + - **`type`** (`string`): + Type of data. Accepted values are `'media'`, `'post'`, or `'term'`. If set, the default callbacks will be used. + **Note:** If this parameter is used in conjunction with custom callbacks, the custom callbacks will be used instead. + + - **`pre_distribute_cb`** (`callable`): + A callback function that prepares the data on the source site before distribution. It should return extra data that is required for processing on the target site. + + - **`post_distribute_cb`** (`callable`): + A callback function that processes the extra data on the target site and returns the updated ID to replace the source reference. + +--- + +## Examples + +Below are several examples demonstrating how to use the `distributor_register_data` function in different scenarios. + +### 1. Handling an Image ID Stored in Post Meta + +In this example, we register an image ID stored in post meta under the key `example_image_id`. + +#### With Custom Callback Functions + +```php +distributor_register_data( 'example_post_meta_data', array( + 'location' => 'post_meta', + 'attributes' => array( + 'meta_key' => 'example_image_id', + ), + // Callback function to prepare the data before sending it from the source site. + // $image_id is the ID stored in post meta (example_image_id). + // $source_post_id is the ID of the post being distributed. + 'pre_distribute_cb' => function ( $image_id, $source_post_id ) { + if ( ! $image_id ) { + return array(); + } + + $image_url = wp_get_attachment_image_url( $image_id, 'full' ); + + // Return extra data required for processing on the target site. + return array( + 'image_url' => $image_url, + ); + }, + // Callback function to process the data before saving it on the target site. + // $extra_data is the data returned from pre_distribute_cb. + // $source_data is the original data stored in post meta. + // $post_data is the distributed post's data. + 'post_distribute_cb' => function ( $extra_data, $source_data, $post_data ) { + if ( ! isset( $extra_data['image_url'] ) ) { + return; + } + + $source_image_url = $extra_data['image_url']; + $source_image_id = $source_data; + + // Check if the media already exists on the target site. If yes, return its ID. + $image_id = Utils\get_attachment_id_by_original_data( $source_image_id, $source_image_url ); + + // If the media is not found, process and save it. + if ( ! $image_id ) { + $image_id = Utils\process_media( $source_image_url, 0, [] ); + update_post_meta( $image_id, 'dt_original_media_id', wp_slash( $source_image_id ) ); + update_post_meta( $image_id, 'dt_original_media_url', wp_slash( $source_image_url ) ); + } + + // Return the media ID to replace the source reference. + return $image_id; + }, +) ); +``` + +**Explanation** + +1. **Pre-Distribution Callback (`pre_distribute_cb`):** + - Retrieves the URL of the image using the stored ID. + - Returns an array with the image URL to be used on the target site. + +2. **Post-Distribution Callback (`post_distribute_cb`):** + - Checks whether the media already exists on the target site by comparing the source image data. + - If not found, processes (uploads) the media and updates the relevant post meta with the original source data. + - Returns the new image ID for the distributed post. + +#### Using the `type` Parameter to Utilize the Default Callback + +```php +distributor_register_data( 'example_post_meta_data', array( + 'location' => 'post_meta', + 'attributes' => array( + 'meta_key' => 'example_image_id', + ), + 'type' => 'media' +) ); +``` + +### 2. Handling Term ID in a Block Attribute + +This example demonstrates how to handle a term ID stored in a block attribute. + +#### With Custom Callback Functions + +```php +distributor_register_data( + 'example_block_data', + array( + 'location' => 'post_content', + 'attributes' => array( + 'block_name' => 'example/block-name', + 'block_attribute' => 'id', + ), + 'pre_distribute_cb' => function( $source_term_id ) { + if ( ! $source_term_id ) { + return array(); + } + + $term = get_term_by( 'id', $source_term_id, 'category' ); + if ( ! $term ) { + return array(); + } + + // Return extra data required for processing on the target site. + return array( + 'name' => $term->name, + 'slug' => $term->slug, + 'description' => $term->description, + 'taxonomy' => $term->taxonomy, + ); + }, + 'post_distribute_cb' => function( $extra_data, $source_data, $post_data ) { + if ( ! isset( $extra_data['name'] ) && ! isset( $extra_data['taxonomy'] ) ) { + return; + } + + $existing_term = get_term_by( 'name', $extra_data['name'], $extra_data['taxonomy'] ); + if ( $existing_term ) { + return $existing_term->term_id; + } + + // Create the term on the target site. + $term = wp_insert_term( + $extra_data['name'], + $extra_data['taxonomy'], + array( + 'description' => $extra_data['description'], + 'slug' => $extra_data['slug'], + ) + ); + + if ( is_wp_error( $term ) ) { + return; + } + + return $term['term_id']; + }, + ) +); +``` + +#### Using the `type` Parameter to Utilize the Default Callback + +```php +distributor_register_data( + 'example_block_data', + array( + 'location' => 'post_content', + 'attributes' => array( + 'block_name' => 'example/block-name', + 'block_attribute' => 'id', + ), + 'type' => 'term' + ) +); +``` + + +### 3. Handling Post ID in a Shortcode Attribute + +This example shows how to manage a post ID stored within a shortcode attribute. + +#### With Custom Callback Functions +```php +// Distributor data registration for the post ID stored in shortcode attribute. +distributor_register_data( 'example_post_data', array( + 'location' => 'post_content', + 'attributes' => array( + 'shortcode' => 'extra_shortcode', + 'shortcode_attribute' => 'example_post_id', + ), + 'pre_distribute_cb' => function( $post_id ) { + if ( ! $post_id ) { + return array(); + } + + $post = get_post( $post_id ); + if ( ! $post ) { + return array(); + } + + return array( + 'post_type' => $post->post_type, + 'post_title' => $post->post_title, + 'post_status' => $post->post_status, + 'post_content' => $post->post_content, + ); + }, + 'post_distribute_cb' => function( $extra_data, $source_data, $post_data ) { + if ( ! isset( $extra_data['post_type'] ) && ! isset( $extra_data['post_title'] ) ) { + return $source_data; + } + + $posts = get_posts( + array( + 'post_type' => $extra_data['post_type'], + 'meta_query' => array( + array( + 'key' => 'dt_original_post_id', + 'value' => $source_data, + 'compare' => '=', + ), + ), + 'post_status' => 'any', + 'fields' => 'ids', + 'no_found_rows' => true, + 'update_post_meta_cache' => false, + 'update_post_term_cache' => false, + 'numberposts' => 1, + 'orderby' => 'post_date ID', + 'order' => 'ASC', + ) + ); + + if ( ! empty( $posts ) ) { + return $posts[0]; + } + + // Create the post. + $post_id = wp_insert_post( $extra_data ); + + if ( is_wp_error( $post_id ) ) { + return; + } + + // Store the original post ID in post meta to check for existing posts on subsequent distributions. + update_post_meta( $post_id, 'dt_original_post_id', wp_slash( $source_data ) ); + + return $post_id; + }, +) ); +``` + +#### Using the `type` Parameter to Utilize the Default Callback +```php +// Distributor data registration for the post ID stored in shortcode attribute. +distributor_register_data( 'example_post_data', array( + 'location' => 'post_content', + 'attributes' => array( + 'shortcode' => 'extra_shortcode', + 'shortcode_attribute' => 'example_post_id', + ), + 'type' => 'post' +) ); +``` + +If you have any doubts, encounter any different scenarios to handle, or need further assistance, please feel free to report an issue on our [GitHub repository](https://github.com/10up/distributor) and we would be happy to help. diff --git a/wp-hooks-docs/docs/02.advanced-docs/08.auto-distribution.md b/wp-hooks-docs/docs/02.advanced-docs/08.auto-distribution.md new file mode 100644 index 000000000..d47d59e05 --- /dev/null +++ b/wp-hooks-docs/docs/02.advanced-docs/08.auto-distribution.md @@ -0,0 +1,74 @@ +--- +id: auto-distribution +title: "Enabling Auto Distribution" +--- + +You can enable the automatic distribution of posts upon publication by togging on the "auto-distribute" feature. + +To enable auto-distribution, you can include this code in your site's feature plugin. + +```php + add_filter( 'dt_auto_distribution_enabled', '__return_true' ); +``` + +This code must run prior to or on the `plugins_loaded` hook. If running on the `plugins_loaded` hook, it must do so at priority 19 or lower. + +With auto-distribution enabled, the following will occur: + +* upon publication, posts and pages will be pushed to all internal and external connections, +* the default status for pushing posts is `publish`, and, +* auto distribution is runs via cron jobs to avoid slowing down the publication process for users. + +## Filters + +The auto-distribution feature comes with a number of filters in additional to the one above. + +### Auto-distributed post types + +By default only posts and pages are auto-distributed. To enable auto-distribution of custom post +types requires the `auto_distribute_supported_post_types` filter be used. + +To add a custom post type to supported post types would require the code: + +```php +add_filter( 'auto_distribute_supported_post_types', function( $post_types ) { + $post_types[] = 'my_cpt'; + return $post_types; +} ); +``` + +### Default post status. + +The `dt_auto_distribution_default_status` filter allows you to filter the default post status for +distribution. To modify the default post status for one post type but not others, you can use the code: + +```php +function ad_demo_modify_page_default_status( $default_status, $post ) { + if ( 'page' !== get_post_type() ) { + return $default_status; + } + + return 'draft'; +} +add_filter( 'dt_auto_distribution_default_status', 'ad_demo_modify_page_default_status', 10, 2 ); +``` + +### Whether to auto-distribute a post + +The `dt_auto_distribute_post` filter allows you to filter whether an individual post will be +auto-distributed. The filter accepts a number of arguments providing the context of the post, +see the [filter's docs](./dt_auto_distribute_post.html) for further information.' + +This filter is only run for post types that are supported, see above. + +To prevent auto-distribution to a certain connection type: + +```php +function ad_demo_no_external( $should_distribute, $post, $user_id, $connection_type ) { + if ( $connection_type === 'external' ) { + return false; + } + return $should_distribute; +} +add_filter( 'dt_auto_distribute_post', 'ad_demo_no_external', 10, 4 ); +``` diff --git a/wp-hooks-docs/docs/02.advanced-docs/index.md b/wp-hooks-docs/docs/02.advanced-docs/index.md new file mode 100644 index 000000000..bc689a138 --- /dev/null +++ b/wp-hooks-docs/docs/02.advanced-docs/index.md @@ -0,0 +1,33 @@ +--- +id: advanced-docs +title: "Advanced Documentation" +sidebar_label: "Advanced Documentation" +--- + +Welcome to the Distributor Advanced Documentation. This section provides in-depth technical documentation, guides, and examples for developers looking to extend and customize the Distributor plugin. + +## Quick Start & Integration + +- **[Connect to an Existing Post](./01.connect-to-an-existing-post.md)** - Link existing content between sites that was created before Distributor was installed +- **[Exclude Meta from Yoast Duplicate Post](./02.exclude-meta-from-yoast-duplicate-post.md)** - Prevent connection conflicts when using Yoast Duplicate Post plugin + +## Migration & Upgrades + +- **[Migration Guide (v1 to v2)](./03.migration-guide-version-1-to-version-2.md)** - Complete guide for upgrading from Distributor v1 to v2 with breaking changes + +## Developer Reference + +- **[Post Meta Documentation](./04.post-meta.md)** - Technical reference for post meta fields used by Distributor to track content relationships +- **[Code Snippets](./05.snippets.md)** - Ready-to-use code examples for common Distributor customizations and extensions +- **[Stored ID Handling](./07.stored-id-handling.md)** - Advanced guide for handling ID references in distributed content (ACF fields, Elementor, Gravity Forms, etc.) + +## Automation Features + +- **[Auto Distribution](./08.auto-distribution.md)** - Configure automatic distribution of posts upon publication + +## Testing & Development + +- **[Demo with WordPress Playground](./06.demo-with-playground.md)** - Quickly spin up a test environment to demonstrate Distributor features + + +Each guide provides step-by-step instructions, code examples, and best practices. Whether you're setting up content distribution for the first time, migrating between versions, or building custom integrations, these resources will help you implement Distributor effectively in your WordPress ecosystem. From d23822d51b6154aad3f9424a0f2d1811582fcbb5 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Fri, 12 Sep 2025 16:18:34 +0530 Subject: [PATCH 04/25] Install new wp hooks documentor. --- package-lock.json | 148 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 4 +- 2 files changed, 151 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 4357ab8f5..30914e913 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ }, "devDependencies": { "@10up/cypress-wp-utils": "^0.6.0", + "@10up/wp-hooks-documentor": "^1.0.0", "@wordpress/env": "^10.29.0", "@wordpress/scripts": "^29.0.0", "compare-versions": "^4.1.3", @@ -35,6 +36,49 @@ "node": ">=12.0" } }, + "node_modules/@10up/wp-hooks-documentor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@10up/wp-hooks-documentor/-/wp-hooks-documentor-1.0.0.tgz", + "integrity": "sha512-fwAcLzoyliJQUqvlVQNiq8U84/ZzYL832XSFonEoaw9hpLKa4UdHFy84o3Uvg5NQqPX44fJgJin5DPxS116IFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^14.0.0", + "fs-extra": "^11.3.0", + "sanitize-html": "^2.17.0" + }, + "bin": { + "wp-hooks-documentor": "dist/cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@10up/wp-hooks-documentor/node_modules/commander": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.1.tgz", + "integrity": "sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/@10up/wp-hooks-documentor/node_modules/fs-extra": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -12320,6 +12364,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -16947,6 +17011,13 @@ "node": ">=0.10.0" } }, + "node_modules/parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==", + "dev": true, + "license": "MIT" + }, "node_modules/parse5": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", @@ -19042,6 +19113,21 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "node_modules/sanitize-html": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.17.0.tgz", + "integrity": "sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "deepmerge": "^4.2.2", + "escape-string-regexp": "^4.0.0", + "htmlparser2": "^8.0.0", + "is-plain-object": "^5.0.0", + "parse-srcset": "^1.0.2", + "postcss": "^8.3.11" + } + }, "node_modules/sass": { "version": "1.63.6", "resolved": "https://registry.npmjs.org/sass/-/sass-1.63.6.tgz", @@ -22426,6 +22512,36 @@ "integrity": "sha512-LI4IpWm9QKT8SgSmMM3k641WhUMj0fWF2KaHrtmZLVoEeqTpeYEV0hG/9FiZddHFX4+R5KPgvb9a9AFkblCsfA==", "dev": true }, + "@10up/wp-hooks-documentor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@10up/wp-hooks-documentor/-/wp-hooks-documentor-1.0.0.tgz", + "integrity": "sha512-fwAcLzoyliJQUqvlVQNiq8U84/ZzYL832XSFonEoaw9hpLKa4UdHFy84o3Uvg5NQqPX44fJgJin5DPxS116IFA==", + "dev": true, + "requires": { + "commander": "^14.0.0", + "fs-extra": "^11.3.0", + "sanitize-html": "^2.17.0" + }, + "dependencies": { + "commander": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.1.tgz", + "integrity": "sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==", + "dev": true + }, + "fs-extra": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + } + } + }, "@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -31347,6 +31463,18 @@ "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", "dev": true }, + "htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "dev": true, + "requires": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, "http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -34806,6 +34934,12 @@ "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", "dev": true }, + "parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==", + "dev": true + }, "parse5": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", @@ -36294,6 +36428,20 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "sanitize-html": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.17.0.tgz", + "integrity": "sha512-dLAADUSS8rBwhaevT12yCezvioCA+bmUTPH/u57xKPT8d++voeYE6HeluA/bPbQ15TwDBG2ii+QZIEmYx8VdxA==", + "dev": true, + "requires": { + "deepmerge": "^4.2.2", + "escape-string-regexp": "^4.0.0", + "htmlparser2": "^8.0.0", + "is-plain-object": "^5.0.0", + "parse-srcset": "^1.0.2", + "postcss": "^8.3.11" + } + }, "sass": { "version": "1.63.6", "resolved": "https://registry.npmjs.org/sass/-/sass-1.63.6.tgz", diff --git a/package.json b/package.json index d4c7fc380..aee47ed6a 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ }, "devDependencies": { "@10up/cypress-wp-utils": "^0.6.0", + "@10up/wp-hooks-documentor": "^1.0.0", "@wordpress/env": "^10.29.0", "@wordpress/scripts": "^29.0.0", "compare-versions": "^4.1.3", @@ -37,7 +38,8 @@ }, "scripts": { "build": "wp-scripts build", - "build:docs": "rm -rf docs-built && jsdoc -c hookdoc-conf.json distributor.php includes", + "prebuild:docs": "cp ./CHANGELOG.md ./wp-hooks-docs/docs/01.get-started/04.CHANGELOG.md", + "build:docs": "rm -rf docs-built && wp-hooks-documentor generate", "check-engines": "wp-scripts check-engines", "check-licenses": "wp-scripts check-licenses", "format": "npm run format:css; npm run format:js", From e838f1a47025e915edfa2c6a95a4ba2c3ece3b00 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Fri, 12 Sep 2025 16:18:49 +0530 Subject: [PATCH 05/25] Add config file. --- wp-hooks-doc.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 wp-hooks-doc.json diff --git a/wp-hooks-doc.json b/wp-hooks-doc.json new file mode 100644 index 000000000..fc8abb858 --- /dev/null +++ b/wp-hooks-doc.json @@ -0,0 +1,20 @@ +{ + "input": ".", + "outputDir": "./docs-built", + "ignoreFiles": [ + "/tests/", + "/vendor/", + "/node_modules/" + ], + "templatesDir": "./wp-hooks-docs", + "ignoreHooks": [], + "title": "Distributor Developer Documentation", + "tagline": "Documentation for actions, filters, and WP-CLI commands found in the Distributor plugin", + "url": "https://distributorplugin.com", + "baseUrl": "/distributor/", + "repoUrl": "https://github.com/10up/distributor", + "githubSourceCodeUrl": "https://github.com/10up/distributor/blob/trunk", + "organizationName": "10up", + "projectName": "distributor", + "footerStyle": "dark" +} From 367cfc20858415b608480db02d3504a18260ba88 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Fri, 12 Sep 2025 16:21:08 +0530 Subject: [PATCH 06/25] Add index page with content, update logo, favicon. --- wp-hooks-docs/docusaurus.config.js | 121 ++++ wp-hooks-docs/src/css/custom.css | 111 ++++ wp-hooks-docs/src/pages/index.js | 206 +++++++ wp-hooks-docs/src/pages/index.module.css | 557 ++++++++++++++++++ .../src/theme/Footer/footer.module.css | 207 +++++++ .../src/theme/Footer/img/footer-bg.jpg | Bin 0 -> 64816 bytes wp-hooks-docs/src/theme/Footer/index.js | 162 +++++ wp-hooks-docs/static/img/favicon.png | Bin 0 -> 21991 bytes wp-hooks-docs/static/img/logo.svg | 30 + 9 files changed, 1394 insertions(+) create mode 100644 wp-hooks-docs/docusaurus.config.js create mode 100644 wp-hooks-docs/src/css/custom.css create mode 100644 wp-hooks-docs/src/pages/index.js create mode 100644 wp-hooks-docs/src/pages/index.module.css create mode 100644 wp-hooks-docs/src/theme/Footer/footer.module.css create mode 100644 wp-hooks-docs/src/theme/Footer/img/footer-bg.jpg create mode 100644 wp-hooks-docs/src/theme/Footer/index.js create mode 100644 wp-hooks-docs/static/img/favicon.png create mode 100644 wp-hooks-docs/static/img/logo.svg diff --git a/wp-hooks-docs/docusaurus.config.js b/wp-hooks-docs/docusaurus.config.js new file mode 100644 index 000000000..4b1ca9150 --- /dev/null +++ b/wp-hooks-docs/docusaurus.config.js @@ -0,0 +1,121 @@ +// `@type` JSDoc annotations allow editor autocompletion and type checking +// (when paired with `@ts-check`). +// There are various equivalent ways to declare your Docusaurus config. +// See: https://docusaurus.io/docs/api/docusaurus-config + +import { themes as prismThemes } from 'prism-react-renderer'; + +// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) + +/** @type {import('@docusaurus/types').Config} */ +const config = { + title: 'Distributor Developer Documentation', + tagline: + 'Documentation for action and filter hooks found in the Distributor plugin', + favicon: 'img/favicon.png', + + // Future flags, see https://docusaurus.io/docs/api/docusaurus-config#future + future: { + v4: true, // Improve compatibility with the upcoming Docusaurus v4 + }, + + url: 'https://distributorplugin.com', + baseUrl: '/distributor/', + + organizationName: '10up', + projectName: 'distributor', + + onBrokenLinks: 'throw', + onBrokenMarkdownLinks: 'warn', + + presets: [ + [ + 'classic', + /** @type {import('@docusaurus/preset-classic').Options} */ + { + docs: { + sidebarPath: './sidebars.js', + routeBasePath: '/', + breadcrumbs: true, + }, + blog: false, + theme: { + customCss: './src/css/custom.css', + }, + }, + ], + ], + + themes: [ + [ + '@easyops-cn/docusaurus-search-local', + { + indexDocs: true, + docsRouteBasePath: '/', + docsDir: 'docs', + hashed: true, + highlightSearchTermsOnTargetPage: true, + searchBarPosition: 'right', + }, + ], + ], + + themeConfig: { + navbar: { + title: 'Distributor Documentation', + logo: { + alt: 'Distributor Logo', + src: 'img/favicon.png', + }, + items: [ + { + type: 'docSidebar', + sidebarId: 'hooksSidebar', + position: 'left', + label: 'Get Started', + href: '/get-started/', + sidebarCollapsed: false, + }, + { + type: 'docSidebar', + sidebarId: 'hooksSidebar', + position: 'left', + label: 'Hooks', + href: '/hooks', + sidebarCollapsed: false, + }, + + { + type: 'docSidebar', + sidebarId: 'hooksSidebar', + position: 'left', + label: 'Live Demo', + href: 'https://playground.wordpress.net/?blueprint-url=https://raw.githubusercontent.com/10up/distributor/refs/heads/develop/.github/blueprints/blueprint.json', + sidebarCollapsed: false, + }, + { + href: 'https://github.com/10up/distributor', + label: 'GitHub', + position: 'right', + }, + ], + }, + colorMode: { + defaultMode: 'dark', + disableSwitch: false, + respectPrefersColorScheme: true, + }, + footer: { + style: 'dark', + copyright: + 'Copyright © 2025 Distributor Developer Documentation. Built with WP Hooks Documentor.', + }, + prism: { + theme: prismThemes.github, + darkTheme: prismThemes.dracula, + additionalLanguages: [ 'php' ], + }, + }, +}; + +export default config; diff --git a/wp-hooks-docs/src/css/custom.css b/wp-hooks-docs/src/css/custom.css new file mode 100644 index 000000000..86ef0ec3b --- /dev/null +++ b/wp-hooks-docs/src/css/custom.css @@ -0,0 +1,111 @@ +/** + * Custom CSS for WordPress Hooks Documentation + */ + +:root { + --ifm-color-primary: #ffc619; + --ifm-color-primary-dark: #ffc619; + --ifm-color-primary-darker: #ffc619; + --ifm-color-primary-darkest: #ffc619; + --ifm-color-primary-light: #ffc619; + --ifm-color-primary-lighter: #ffc619; + --ifm-color-primary-lightest: #ffc619; + --ifm-code-font-size: 95%; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); +} + +[data-theme='dark'] { + --ifm-color-primary: #fee450; + --ifm-color-primary-dark: #fedf2f; + --ifm-color-primary-darker: #fedc1e; + --ifm-color-primary-darkest: #e8c601; + --ifm-color-primary-light: #fee971; + --ifm-color-primary-lighter: #feec82; + --ifm-color-primary-lightest: #fff3b4; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); +} + +.hook-type { + display: inline-block; + padding: 0.2rem 0.5rem; + border-radius: 4px; + font-size: 0.8rem; + font-weight: 600; + margin-bottom: 1rem; +} + +.hook-type.action { + background-color: var(--ifm-color-primary-lightest); + color: var(--ifm-color-primary-darkest); +} + +.hook-type.filter { + background-color: var(--ifm-color-primary-darker); + color: white; +} + +.hook-parameters { + margin-top: 1rem; + padding: 1rem; + background-color: var(--ifm-color-emphasis-100); + border-radius: 8px; +} + +.hook-source { + font-family: var(--ifm-font-family-monospace); + font-size: var(--ifm-code-font-size); + padding: 0.2rem 0.4rem; + background-color: var(--ifm-color-emphasis-200); + border-radius: 4px; +} + +.navbar__logo img { + height: 2rem; +} + +.navbar__search > * { + max-width: 47.5rem; + width: 100%; + margin: 0 auto; +} + +.main-wrapper .navbar__search > button { + right: calc(50% - 47.5rem / 2 + 4rem); + width: auto; + background: var(--ifm-navbar-search-input-background-color); + border-radius: .25rem; + width: 2rem; + height: 2rem; + transition: background-color .2s; +} + +.main-wrapper .navbar__search span { + width: 100%; +} + +.main-wrapper .navbar__search input::placeholder { + color: var(--ifm-color-content); +} + +.navbar__search [class^=searchHintContainer_] { + display: none !important; +} + +.main-wrapper .navbar__search input { + background-color: #fff; + border-radius: 0.75em; + height: 4rem; + display: block; + width: 100%; + border: 3px solid var(--ifm-color-content); + padding: 1rem 1.5rem; + background-position: right 1.5rem center; + margin-right: auto; + margin-left: auto; + font-size: 1.125rem; + color: var(--ifm-color-content); +} + +[data-theme='light'] .markdown > ul > li > a { + font-weight: 700; +} diff --git a/wp-hooks-docs/src/pages/index.js b/wp-hooks-docs/src/pages/index.js new file mode 100644 index 000000000..af224ce74 --- /dev/null +++ b/wp-hooks-docs/src/pages/index.js @@ -0,0 +1,206 @@ +/* eslint-disable @typescript-eslint/explicit-function-return-type */ +import React from 'react'; +import clsx from 'clsx'; +import Link from '@docusaurus/Link'; +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import Layout from '@theme/Layout'; + +import styles from './index.module.css'; + +/** + * Homepage header component + * + * @return {JSX.Element} The header component + */ +function HomepageHeader() { + return ( +
+
+
+

+ Developer Documentation +

+

Distributor

+

Documentation

+

+ Your complete guide to Distributor's hooks and + APIs. Build and extend content syndication the WordPress + way. +

+
+ + Get Started + + + Hooks Reference + +
+
+
+
+ ); +} + +/** + * Developer Features section component + * + * @return {JSX.Element} The developer features section + */ +function DeveloperFeaturesSection() { + const features = [ + { + title: 'Distribution Guide', + description: + 'Learn the fundamentals of content distribution, setting up connections, and managing content syndication workflows.', + link: '/get-started/how-to-distribute-content/', + buttonText: 'View Guide', + }, + { + title: 'Code Snippets', + description: + 'Ready-to-use code snippets and practical examples for common Distributor customizations and integrations.', + link: '/advanced-docs/snippets/', + buttonText: 'Browse Snippets', + }, + { + title: 'Stored ID Handling', + description: + 'Understand how Distributor manages content relationships and handles stored IDs across distributed content.', + link: '/advanced-docs/stored-id-handling/', + buttonText: 'Learn More', + }, + { + title: 'Auto Distribution', + description: + 'Set up automated content distribution workflows using hooks and filters to streamline your content syndication.', + link: '/advanced-docs/auto-distribution/', + buttonText: 'Setup Guide', + }, + { + title: 'Migration Guide', + description: + 'Complete migration guide for upgrading from Distributor version 1 to version 2 with breaking changes and updates.', + link: '/advanced-docs/migration-guide-version-1-to-version-2/', + buttonText: 'Migrate Now', + }, + { + title: 'Hooks & Filters', + description: + 'Comprehensive reference of all available hooks, actions, and filters with parameters and usage examples.', + link: '/hooks/', + buttonText: 'View Reference', + }, + ]; + + return ( +
+
+

Build & Extend

+

+ Everything you need to customize, integrate, and extend + Distributor. From quick start guides to advanced hooks and + migration resources. +

+
+ { features.map( ( feature, idx ) => ( +
+

{ feature.title }

+

{ feature.description }

+ + { feature.buttonText } + +
+ ) ) } +
+
+
+ ); +} + +/** + * Contribution Guide section component + * + * @return {JSX.Element} The contribution guide section + */ +function ContributionGuideSection() { + const contributions = [ + { + title: 'Report Issues', + description: + 'Found a bug, have a feature request, or discovered a security vulnerability? Help us improve by reporting issues.', + action: 'Report an Issue', + link: 'https://github.com/10up/distributor/issues', + }, + { + title: 'Contribute Code', + description: + 'Submit pull requests with bug fixes, new features, or improvements. All skill levels welcome.', + action: 'View Contributing Guide', + link: 'https://github.com/10up/distributor/blob/develop/CONTRIBUTING.md', + }, + { + title: 'Share Feedback', + description: + 'Share your experience, suggest improvements, or discuss ideas with the development team and community.', + action: 'Start a Discussion', + link: 'https://github.com/10up/distributor/issues', + } + ]; + + return ( +
+
+

Get Involved

+

+ Join our community and help make Distributor better for + everyone. There are many ways to contribute, regardless of + your skill level. +

+
+ { contributions.map( ( item, idx ) => ( +
+

{ item.title }

+

{ item.description }

+
+ + { item.action } + +
+
+ ) ) } +
+
+
+ ); +} + +/** + * Homepage component + * + * @return {JSX.Element} The homepage component + */ +export default function Home() { + const { siteConfig } = useDocusaurusContext(); + + return ( + + +
+ + +
+
+ ); +} diff --git a/wp-hooks-docs/src/pages/index.module.css b/wp-hooks-docs/src/pages/index.module.css new file mode 100644 index 000000000..ed4f83d54 --- /dev/null +++ b/wp-hooks-docs/src/pages/index.module.css @@ -0,0 +1,557 @@ +/** + * CSS files with the .module.css suffix will be treated as CSS modules + * and scoped locally. + */ + +.heroBanner { + padding: 6rem 0; + text-align: center; + position: relative; +} + +.headerContent { + max-width: 800px; + margin: 0 auto; +} + +.wpPluginLabel { + color: var(--ifm-color-primary); + font-size: 0.9rem; + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: 1rem; +} + +.heroTitle { + font-size: 4rem; + font-weight: 800; + margin-bottom: 0.5rem; + line-height: 1.1; +} + +.heroSubtitle { + font-size: 4rem; + font-weight: 800; + color: var(--ifm-color-primary); + margin-bottom: 1rem; + line-height: 1.1; +} + +.heroDescription { + font-size: 1.25rem; + line-height: 1.6; + margin-bottom: 2rem; + max-width: 600px; + margin-left: auto; + margin-right: auto; + +} + +[data-theme='light'] .heroDescription { + color: #666; +} + +[data-theme='dark'] .heroDescription { + color: #ccc; +} + + +.buttons { + display: flex; + align-items: center; + justify-content: center; + gap: 1rem; + margin-bottom: 3rem; +} + +.buttons a { + position: relative; + overflow: hidden; + transition: all 0.3s ease; + transform: translateY(0); +} + +/* Primary button animation */ +.buttons a:global(.button--primary) { + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); +} + +.buttons a:global(.button--primary):hover { + transform: translateY(-2px); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); +} + +.buttons a:global(.button--primary):active { + transform: translateY(0); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +/* Secondary button animation */ +.buttons a:global(.button--secondary) { + background: linear-gradient(120deg, transparent 0%, transparent 50%, var(--ifm-color-primary) 50%); + background-size: 250% 100%; + transition: all 0.3s ease; +} + +.buttons a:global(.button--secondary):hover { + background-position: -100% 0; +} + +/* Outline button animation */ +.buttons a:global(.button--outline), +.featureActions a:global(.button--outline) { + border: 2px solid var(--ifm-color-primary); + transition: all 0.3s ease; +} + +.buttons a:global(.button--outline):hover, +.featureActions a:global(.button--outline):hover { + background: var(--ifm-color-primary); + transform: translateY(-2px); +} + +.buttons a:global(.button--outline):active, +.featureActions a:global(.button--outline):active { + transform: translateY(0); +} + +/* Add ripple effect to all buttons */ +.buttons a::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 5px; + height: 5px; + background: rgba(255, 255, 255, 0.5); + opacity: 0; + border-radius: 100%; + transform: scale(1, 1) translate(-50%); + transform-origin: 50% 50%; +} + +.buttons a:focus:not(:active)::after { + animation: ripple 1s ease-out; +} + +@keyframes ripple { + 0% { + transform: scale(0, 0); + opacity: 0.5; + } + 20% { + transform: scale(25, 25); + opacity: 0.3; + } + 100% { + opacity: 0; + transform: scale(40, 40); + } +} + +.featureSection, +.platformSection, +.getStartedSection { + padding: 3rem 0; +} + +[data-theme='dark'] .featureSection { + background: var(--ifm-color-bg-secondary); +} + +[data-theme='light'] .featureSection { + background: #f8f9fa; +} + +.sectionTitle { + font-size: 2.5rem; + text-align: center; + margin-bottom: 1rem; +} + +[data-theme='light'] .sectionTitle { + color: #000; +} + +[data-theme='dark'] .sectionTitle { + color: #fff; +} + +.sectionDescription { + text-align: center; + max-width: 700px; + margin: 0 auto 1rem; + font-size: 1.1rem; + line-height: 1.6; +} + + +[data-theme='light'] .sectionDescription { + color: #666; +} + +[data-theme='dark'] .sectionDescription { + color: #ccc; +} + +.featureGrid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 2rem; + margin-top: 3rem; +} + +.featureCard { + background: var(--ifm-color-gray-800); + border-radius: 8px; + padding: 2rem; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + transition: all 0.3s ease; + position: relative; + top: 0; +} + +[data-theme='light'] .featureCard { + background: #fff; +} + +.featureCard:hover { + top: -5px; + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1); +} + +.featureCard:hover h3 { + color: var(--ifm-color-primary); +} + +.featureCard .featureActions { + opacity: 0.8; + transform: translateY(10px); + transition: all 0.3s ease; +} + +.featureCard:hover .featureActions { + opacity: 1; + transform: translateY(0); +} + +.featureCard h3 { + font-size: 1.5rem; + margin-bottom: 1rem; +} + +[data-theme='light'] .featureCard h3 { + color: #000; +} + +[data-theme='dark'] .featureCard h3 { + color: #fff; +} + +.featureCard p { + margin-bottom: 1.5rem; + line-height: 1.6; +} + +[data-theme='light'] .featureCard p { + color: #666; +} + +[data-theme='dark'] .featureCard p { + color: #ccc; +} + +.featureTags { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin-bottom: 1.5rem; +} + +.tag { + background: #f0f0f0; + padding: 0.25rem 0.75rem; + border-radius: 20px; + font-size: 0.875rem; + color: #666; + transition: all 0.2s ease; + cursor: default; + user-select: none; +} + +[data-theme='dark'] .tag { + background: #2a2a2a; + color: #ccc; +} + +.tag:hover { + background: var(--ifm-color-primary); + color: white; + transform: translateY(-1px); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.tag:active { + transform: translateY(0); + box-shadow: none; +} + +.featureActions { + display: flex; + gap: 1rem; +} + +.platformGrid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 2rem; + margin-top: 3rem; +} + +.platformCard { + background: #fff; + border-radius: 8px; + padding: 2rem; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + transition: all 0.3s ease; + transform: scale(1); + cursor: pointer; +} + +[data-theme='dark'] .platformCard { + background: var(--ifm-color-gray-800); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); +} + +.platformCard:hover { + transform: scale(1.02); + box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15); +} + +.platformCard:hover h3 { + color: var(--ifm-color-primary); +} + +.platformCard .featureList { + transition: all 0.3s ease; +} + +.platformCard:hover .featureList { + color: #333; +} + +.platformCard .capabilityTags { + transition: all 0.3s ease; + transform: translateY(0); +} + +.platformCard:hover .capabilityTags { + transform: translateY(-5px); +} + +.platformCard h3 { + font-size: 1.5rem; + margin-bottom: 1rem; + color: #000; +} + +[data-theme='dark'] .platformCard h3 { + color: #fff; +} + +.featureList { + list-style: none; + padding: 0; + margin: 0 0 1.5rem; +} + +.featureList li { + margin-bottom: 0.5rem; + color: #666; +} + +[data-theme='dark'] .featureList li { + color: #ccc; +} + +.capabilityTags { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} + +.platformAction { + text-align: center; + margin-top: 3rem; +} + +.getStartedSection { + background: #fff; + text-align: center; +} + +[data-theme='dark'] .getStartedSection { + background: var(--ifm-color-gray-900); +} + +.getStartedActions { + display: flex; + justify-content: center; + gap: 1rem; + margin-top: 2rem; +} + +/* Contribution Section */ +.contributionSection { + padding: 4rem 0; + background: #fff; +} + +[data-theme='dark'] .contributionSection { + background: var(--ifm-color-gray-900); +} + +.contributionGrid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 2rem; + margin-top: 3rem; +} + +.contributionCard { + background: #f8f9fa; + border-radius: 12px; + padding: 2rem; + text-align: center; + transition: all 0.3s ease; + border: 2px solid transparent; +} + +[data-theme='dark'] .contributionCard { + background: var(--ifm-color-gray-800); +} + +.contributionCard:hover { + transform: translateY(-5px); + border-color: var(--ifm-color-primary); + box-shadow: 0 8px 20px rgba(255, 149, 0, 0.1); +} + +.contributionCard h3 { + color: #000; + margin-bottom: 1rem; + font-size: 1.3rem; +} + +[data-theme='dark'] .contributionCard h3 { + color: #fff; +} + +.contributionCard p { + color: #666; + margin-bottom: 2rem; + line-height: 1.6; +} + +[data-theme='dark'] .contributionCard p { + color: #ccc; +} + +.guidelinesSection { + margin-top: 4rem; + padding-top: 3rem; + border-top: 1px solid #e2e8f0; +} + +[data-theme='dark'] .guidelinesSection { + border-top: 1px solid #444; +} + +.guidelinesTitle { + text-align: center; + color: #000; + margin-bottom: 2rem; + font-size: 1.8rem; +} + +[data-theme='dark'] .guidelinesTitle { + color: #fff; +} + +.guidelinesGrid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 2rem; +} + +.guidelineItem { + text-align: center; + padding: 1.5rem; +} + +.guidelineItem h4 { + color: #000; + margin-bottom: 1rem; + font-size: 1.2rem; +} + +[data-theme='dark'] .guidelineItem h4 { + color: #fff; +} + +.guidelineItem p { + color: #666; + line-height: 1.6; + margin: 0; +} + +[data-theme='dark'] .guidelineItem p { + color: #ccc; +} + +@media screen and (max-width: 996px) { + .heroBanner { + padding: 3rem 1rem; + } + + .heroTitle, + .heroSubtitle { + font-size: 2.5rem; + } + + .buttons, + .getStartedActions { + flex-direction: column; + } + + .buttons a, + .getStartedActions a { + width: 100%; + } + + .featureGrid, + .platformGrid, + .hooksGrid { + grid-template-columns: 1fr; + } + + .statsContainer { + flex-direction: column; + gap: 1.5rem; + } + + .stepCard { + flex-direction: column; + text-align: center; + } + + .stepNumber { + margin-right: 0; + margin-bottom: 1rem; + align-self: center; + } + + .contributionGrid { + grid-template-columns: 1fr; + } +} diff --git a/wp-hooks-docs/src/theme/Footer/footer.module.css b/wp-hooks-docs/src/theme/Footer/footer.module.css new file mode 100644 index 000000000..858096f73 --- /dev/null +++ b/wp-hooks-docs/src/theme/Footer/footer.module.css @@ -0,0 +1,207 @@ +.mainFooter { + background-color: #202020; + background: url('./img/footer-bg.jpg') no-repeat center center; + background-size: cover; + color: #888889; + text-align: center; + min-height: 200px; + position: relative; + padding-top: 4rem; + padding-bottom: 4rem; +} + +.footerBackground { + bottom: 0; + left: 0; + opacity: .5; + position: absolute; + right: 0; + top: 0; + z-index: 0; + background: #000; +} + +.footerContainer { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; +} + +.footerCopyright, +.footerLogo, +.footerSocial { + flex-basis: 0; + flex-grow: 1; + width: 100%; + z-index: 1; +} + +.footerLogo { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 24px; +} + +.footer10upButton { + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: center; +} + +.footer10upButtonInner a { + border-radius: 48px; + background: rgba(0, 0, 0, 0.40); + display: flex; + padding: 6px 16px; + justify-content: center; + align-items: center; + gap: 8px; + font-family: Aeonik, sans-serif; + position: relative; + text-wrap: nowrap; + color: white; +} + +.footer10upButtonInner a:before { + background: linear-gradient(265.77deg, rgba(0, 0, 0, 0) 8.36%, #372b8e 42.17%, #e0adfc 71.15%, #fff 95.31%); + border-radius: 100px; + content: ""; + inset: -2px; + mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); + mask-composite: exclude; + opacity: 0; + padding: 2px; + pointer-events: none; + position: absolute; + transition: opacity 0.3s ease-out; +} + +.footer10upButtonInner a:hover { + text-decoration: none; + color: white; + background: rgba(0, 0, 0, 0.40); +} + +.footer10upButtonInner a:hover:before { + opacity: 1; +} + +.footerCopyright { + text-align: left; +} + +.footerSocial { + text-align: right; +} + +.footerSocial a svg { + margin-right: 15px; + width: 25px; +} + +.footerSocial a svg:hover { + transform: scale(1.1); +} + +.footerSocial a svg path { + fill: #9d9d9d; +} + +.footerSocial a svg:hover path { + fill: #fff; +} + +.footerCopyrightLinks a { + color: #9d9d9d; + text-decoration: underline; +} + +.footerCopyrightLinks a:hover { + color: #fff; +} + + +@media (max-width: 900px) { + .footerContainer { + display: flex; + flex-direction: column; + gap: 24px; + } + + .footerLogo, + .footerSocial, + .footerCopyright { + text-align: center; + flex-basis: 100%; + flex-grow: 1; + } + + .footerLogo { + order: -1; + } +} + + +.getStartedSection { + padding: 3rem 0; +} + +.getStartedSection { + background: #fff; + text-align: center; +} + +[data-theme='dark'] .getStartedSection, [data-theme='dark'] .greySection { + background: var(--ifm-navbar-background-color); +} + +.greySection { + background: #f8f9fa; +} + +.sectionTitle { + font-size: 2.5rem; + text-align: center; + margin-bottom: 1rem; + color: #000; +} + +[data-theme='dark'] .sectionTitle { + color: #fff; +} + +.sectionDescription { + text-align: center; + max-width: 700px; + margin: 0 auto 1rem; + color: #666; + font-size: 1.1rem; + line-height: 1.6; +} + +[data-theme='dark'] .sectionDescription { + color: #ccc; +} + + +.getStartedActions { + display: flex; + justify-content: center; + gap: 1rem; + margin-top: 2rem; +} + +@media screen and (max-width: 996px) { + .getStartedActions { + flex-direction: column; + } + + .getStartedActions a { + width: 100%; + } +} + diff --git a/wp-hooks-docs/src/theme/Footer/img/footer-bg.jpg b/wp-hooks-docs/src/theme/Footer/img/footer-bg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fd9c92ea66babeda514b832a79963b69d77dbbbe GIT binary patch literal 64816 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 literal 0 HcmV?d00001 diff --git a/wp-hooks-docs/src/theme/Footer/index.js b/wp-hooks-docs/src/theme/Footer/index.js new file mode 100644 index 000000000..d290969ac --- /dev/null +++ b/wp-hooks-docs/src/theme/Footer/index.js @@ -0,0 +1,162 @@ +import React from 'react'; + +import clsx from 'clsx'; +import { ThemeClassNames } from '@docusaurus/theme-common'; +import FooterCopyright from '@theme/Footer/Copyright'; +import styles from './footer.module.css'; +import Link from '@docusaurus/Link'; + +/** + * Get started section component + * + * @return {JSX.Element} The get started section component + */ +function GetStartedSection() { + return ( +
+
+

Ready to Get Started?

+

+ Distributor is a WordPress plugin that makes it easy to + syndicate and reuse content across your websites — whether + in a single multisite or across the web. +

+
+ + Download Distributor Plugin + +
+
+
+ ); +} + +function Footer() { + const currentYear = new Date().getFullYear().toString(); + + return ( + <> + + + + ); +} + +export default React.memo( Footer ); diff --git a/wp-hooks-docs/static/img/favicon.png b/wp-hooks-docs/static/img/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..2c335d86fe0a69988f0322594a775c1fd33f1d20 GIT binary patch literal 21991 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�`x>?Kaomv?4Pxt2 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 +Group 2 +Created using Figma + + + + + + + + + + + + + + + + + + + + + + + + + + + From 2170563181875a03c2c186a5af491b6ca41a8a35 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Fri, 12 Sep 2025 16:21:21 +0530 Subject: [PATCH 07/25] Fix broken links. --- wp-hooks-docs/docs/01.get-started/index.md | 2 +- wp-hooks-docs/docs/02.advanced-docs/08.auto-distribution.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/wp-hooks-docs/docs/01.get-started/index.md b/wp-hooks-docs/docs/01.get-started/index.md index 159ce27a1..d9f01750c 100644 --- a/wp-hooks-docs/docs/01.get-started/index.md +++ b/wp-hooks-docs/docs/01.get-started/index.md @@ -1,6 +1,6 @@ --- id: get-started -title: "ClassifAI Docs - Get Started" +title: "Distributor Docs - Get Started" sidebar_label: "Get Started" --- diff --git a/wp-hooks-docs/docs/02.advanced-docs/08.auto-distribution.md b/wp-hooks-docs/docs/02.advanced-docs/08.auto-distribution.md index d47d59e05..e487d1165 100644 --- a/wp-hooks-docs/docs/02.advanced-docs/08.auto-distribution.md +++ b/wp-hooks-docs/docs/02.advanced-docs/08.auto-distribution.md @@ -57,7 +57,7 @@ add_filter( 'dt_auto_distribution_default_status', 'ad_demo_modify_page_default_ The `dt_auto_distribute_post` filter allows you to filter whether an individual post will be auto-distributed. The filter accepts a number of arguments providing the context of the post, -see the [filter's docs](./dt_auto_distribute_post.html) for further information.' +see the [filter's docs](../hooks/Filters/dt_auto_distribute_post.md) for further information.' This filter is only run for post types that are supported, see above. From e91a3f4ccb607e8961ca8a1700ce10aef54b027a Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Fri, 12 Sep 2025 16:22:19 +0530 Subject: [PATCH 08/25] Remove old docs. --- docs/auto-distribution.md | 69 ---- docs/connect-to-an-existing-post.md | 107 ------ docs/demo-with-playground.md | 12 - .../exclude-meta-from-yoast-duplicate-post.md | 26 -- .../migration-guide-version-1-to-version-2.md | 58 ---- docs/post-meta.md | 78 ----- docs/snippets.md | 202 ------------ docs/stored-id-handling.md | 305 ------------------ docs/tutorials.json | 26 -- 9 files changed, 883 deletions(-) delete mode 100644 docs/auto-distribution.md delete mode 100644 docs/connect-to-an-existing-post.md delete mode 100644 docs/demo-with-playground.md delete mode 100644 docs/exclude-meta-from-yoast-duplicate-post.md delete mode 100644 docs/migration-guide-version-1-to-version-2.md delete mode 100644 docs/post-meta.md delete mode 100644 docs/snippets.md delete mode 100644 docs/stored-id-handling.md delete mode 100644 docs/tutorials.json diff --git a/docs/auto-distribution.md b/docs/auto-distribution.md deleted file mode 100644 index a01283a89..000000000 --- a/docs/auto-distribution.md +++ /dev/null @@ -1,69 +0,0 @@ -You can enable the automatic distribution of posts upon publication by togging on the "auto-distribute" feature. - -To enable auto-distribution, you can include this code in your site's feature plugin. - -```php - add_filter( 'dt_auto_distribution_enabled', '__return_true' ); -``` - -This code must run prior to or on the `plugins_loaded` hook. If running on the `plugins_loaded` hook, it must do so at priority 19 or lower. - -With auto-distribution enabled, the following will occur: - -* upon publication, posts and pages will be pushed to all internal and external connections, -* the default status for pushing posts is `publish`, and, -* auto distribution is runs via cron jobs to avoid slowing down the publication process for users. - -## Filters - -The auto-distribution feature comes with a number of filters in additional to the one above. - -### Auto-distributed post types - -By default only posts and pages are auto-distributed. To enable auto-distribution of custom post -types requires the `auto_distribute_supported_post_types` filter be used. - -To add a custom post type to supported post types would require the code: - -```php -add_filter( 'auto_distribute_supported_post_types', function( $post_types ) { - $post_types[] = 'my_cpt'; - return $post_types; -} ); -``` - -### Default post status. - -The `dt_auto_distribution_default_status` filter allows you to filter the default post status for -distribution. To modify the default post status for one post type but not others, you can use the code: - -```php -function ad_demo_modify_page_default_status( $default_status, $post ) { - if ( 'page' !== get_post_type() ) { - return $default_status; - } - - return 'draft'; -} -add_filter( 'dt_auto_distribution_default_status', 'ad_demo_modify_page_default_status', 10, 2 ); -``` - -### Whether to auto-distribute a post - -The `dt_auto_distribute_post` filter allows you to filter whether an individual post will be -auto-distributed. The filter accepts a number of arguments providing the context of the post, -see the [filter's docs](./dt_auto_distribute_post.html) for further information.' - -This filter is only run for post types that are supported, see above. - -To prevent auto-distribution to a certain connection type: - -```php -function ad_demo_no_external( $should_distribute, $post, $user_id, $connection_type ) { - if ( $connection_type === 'external' ) { - return false; - } - return $should_distribute; -} -add_filter( 'dt_auto_distribute_post', 'ad_demo_no_external', 10, 4 ); -``` diff --git a/docs/connect-to-an-existing-post.md b/docs/connect-to-an-existing-post.md deleted file mode 100644 index be0f10d17..000000000 --- a/docs/connect-to-an-existing-post.md +++ /dev/null @@ -1,107 +0,0 @@ -In a scenario where you have existing sites that share content and that content was in place prior to the addition of Distributor, it is useful to be able to connect that content together without having to delete one and push/pull the other one. Behind the scenes, Distributor stores a handful of details in post meta, so these details will need to be manually added for the links to work. - -At it's simplest, you'll need to connect the two sites together and then add the proper meta (as detailed below) for the items that should be linked together, both on the source site and the receiving site. - -## Network Connections - -There are different pieces of data that will need to be set on each side of a connection, what we'll call as the source site and the receiving site. - -The only piece of data needed on the source site to connect two items in Network Connections is the `dt_connection_map`. This is a serialized array of data that contains the mapping from the site ID to the post ID, along with a timestamp of when the item was linked. Note that the site ID and post ID should correspond to the data on the receiving site, not source site. - -This data structure will look like the following: - -```php -$data = [ - [ - 'external' => [], - 'internal' => [ - 2 => [ - 'post_id' => 674, - 'time' => 1693494494, - ], - ], - ], -]; -``` - -In the above example, the `external` array will always be empty, unless an item is also linked to an external site. For the `internal` array, each item in the array will have a key that corresponds to the site ID (2 in the example above) and then the `post_id` should be the destination post ID. - -As a further example, if I have a post with an ID of 100 that lives on a site with an ID of 1 and I want that post to be linked to an existing post with an ID of 50 on the site with an ID of 2, the `dt_connection_map` data that is stored with the original item (ID of 100, site ID 1) would look like the following: - -```php -$data = [ - [ - 'external' => [], - 'internal' => [ - 2 => [ - 'post_id' => 50, - 'time' => 1693494494, - ], - ], - ], -]; -update_post_meta( 100, 'dt_connection_map', $data ); -``` - -And then on the receiving site, the following data is needed: `dt_original_post_id`, `dt_original_post_url`, `dt_original_blog_id` and `dt_syndicate_time`. These should all be fairly self explanatory but to use the same example from above: - -- `dt_original_post_id` would be set to 100 -- `dt_original_post_url` would be the full URL of that post with an ID of 100 -- `dt_original_blog_id` would be set to 1 -- `dt_syndicate_time` should match the same timestamp set in the connection map, in this case 1693494494 - -## External Connections - -External Connections share a similar data structure as detailed above but they also contain a Subscriptions piece, which is more complicated to manually replicate (as such, full details on how to replicate the Subscription is not outlined here). - -Similar to the above, there's a data mapping that is needed on the source site: `dt_connection_map`. This is a serialized array of data that contains the mapping from the external connection ID to the post ID, along with a timestamp of when the item was linked. Note that the post ID should correspond to the data on the receiving site, not source site. - -This data structure will look like the following: - -```php -$data = [ - [ - 'external' => [ - 2 => [ - 'post_id' => 50, - 'time' => 1693494494, - ], - ], - 'internal' => [], - ], -]; -``` - -In the above example, the `internal` array will always be empty, unless an item is also linked to an internal site. For the `external` array, each item in the array will have a key that corresponds to the connection ID (2 in the example above) and then the `post_id` should be the destination post ID. - -As a further example, if I have a post with an ID of 100 that lives on a site with an ID of 1 and I want that post to be linked to an existing post with an ID of 50 on the site with a connection ID of 2, the `dt_connection_map` data that is stored with the original item (ID of 100, site ID 1) would look like the following: - -```php -$data = [ - [ - 'external' => [ - 2 => [ - 'post_id' => 50, - 'time' => 1693494494, - ], - ], - 'internal' => [], - ], -]; -update_post_meta( 100, 'dt_connection_map', $data ); -``` - -In addition, there needs to be a piece of data on the source site that contains the subscription information: `dt_subscriptions`. This contains a serialized array of data that links a hashed signature to the subscription post ID. This isn't something that is easily manually reproduced. Suggestion is to look at using the existing `Distributor\Subscriptions\create_subscription` function to replicate this data. - -And then on the receiving site, the following data is needed: `dt_original_post_id`, `dt_original_post_url`, `dt_original_source_id`, `dt_original_site_name`, `dt_original_site_url`, `dt_subscription_signature`, `dt_full_connection` and `dt_syndicate_time`. - -From the example above: - -- `dt_original_post_id` would be set to 100 -- `dt_original_post_url` would be the full URL of that post with an ID of 100 -- `dt_original_source_id` would be set to 2 (the source connection ID) -- `dt_original_site_name` would be the name of the source site -- `dt_original_site_url` would be the URL of the source site -- `dt_subscription_signature` would be the signature of the subscription mentioned above -- `dt_full_connection` would be set to `true` -- `dt_syndicate_time` should match the same timestamp set in the connection map, in this case 1693494494 diff --git a/docs/demo-with-playground.md b/docs/demo-with-playground.md deleted file mode 100644 index 55c8b19e8..000000000 --- a/docs/demo-with-playground.md +++ /dev/null @@ -1,12 +0,0 @@ -Use WordPress Playground to quickly spin up a project to demonstrate the plugin's features. - -## Example - -This is an example URL you can use to run the project: `https://playground.wordpress.net/?blueprint-url=https://raw.githubusercontent.com/10up/distributor/refs/heads/develop/.github/blueprints/blueprint.json` - -## Features - -The project has the following features: -- it is a multisite installation -- the Distributor plugin is installed and activated network-wide -- the network contains two websites diff --git a/docs/exclude-meta-from-yoast-duplicate-post.md b/docs/exclude-meta-from-yoast-duplicate-post.md deleted file mode 100644 index 59ef12659..000000000 --- a/docs/exclude-meta-from-yoast-duplicate-post.md +++ /dev/null @@ -1,26 +0,0 @@ -When duplicating a source post using Yoast Duplicate Post, a distributed post can be connected to two sources if the subscription meta is not excluded. - -[External reference](https://developer.yoast.com/duplicate-post/filters-actions/#duplicate_post_excludelist_filter) - -```php -/** - * Filters out custom fields from being duplicated in addition to the defaults. - * - * @param array $meta_excludelist The default exclusion list, based on the “Do not copy these fields” setting, plus some other field names. - * - * @return array The custom fields to exclude. - */ -function client_prefix__custom_fields_filter( $meta_excludelist ) { - // Merges the defaults array with our own array of custom fields. - return array_merge( - $meta_excludelist, [ - 'dt_connection_map', - 'dt_subscription_update', - 'dt_subscriptions', - 'dt_subscription_signature', - ] - ); -} - -add_filter( 'duplicate_post_excludelist_filter', 'client_prefix__custom_fields_filter' ); -``` \ No newline at end of file diff --git a/docs/migration-guide-version-1-to-version-2.md b/docs/migration-guide-version-1-to-version-2.md deleted file mode 100644 index 34c17f540..000000000 --- a/docs/migration-guide-version-1-to-version-2.md +++ /dev/null @@ -1,58 +0,0 @@ -Version 2.0.0 of Distributor includes a number of breaking changes that will require updates to custom code you may have written for distributor. - -## External connections require a minimum of version 2.0.0 - -It is recommended that both ends of an external connection run the same version of Distributor. - -Version 2.0.0 of Distributor will prevent the pulling of posts from sites running Version 1.9.x or lower of Distributor. - -### Remove canonical links for both Internal and External Connections - -The code snippet required to prevent sites from displaying the source post as canonical URLs for distributed posts has changed. - -If you have implemented this using the code snippet from our tutorial file, please update your code to the following: - -```php -/** - * Stop Distributor from changing the canonical links. - * - * This removes Distributor's canonical functionality from both Internal and - * External Connections. - * - * This accounts for sites using either WordPress or Yoast SEO to generate the - * canonical URL. - */ -add_action( 'plugins_loaded', function() { - remove_filter( 'get_canonical_url', '\\Distributor\\Hooks\\get_canonical_url', 10, 2 ); - remove_filter( 'wpseo_canonical', '\\Distributor\\Hooks\\wpseo_canonical', 10, 2 ); -} ); -``` - -### REST API Changes - -The distributor REST API endpoint at `/wp/v2/distributor/list-pull-content` has been modified substantially and will now reject connections from 1.x versions of Distributor. - -The fields returned by the endpoint have been modified to match the names used by `wp_insert_post` and `wp_update_post`. - -#### Additional parameters - -* `include` (Array|Int): Ensure result set includes specific Post IDs. Default empty. -* `order` (`asc`|`desc`): Specify order of returned data. Default `desc`. -* `orderby` (`author`|`date`|`id`|`include`|`modified`|`parent`|`relevance`|`slug`|`title`): Field to order results by. Default `date`, `relevance` for search queries. - -#### Modified parameters - -* `post_type` (String|String[]): Modified to accept multiple post types. Post types are limited to posts the connected account can edit, are public post types and visible in the WordPress REST API's standard endpoints. Default `post`. -* `post_status` (String|String[]): Modified to accept multiple post statuses. Statuses are limited to public statuses only. Default `publish`. - -### `dt_push_post` action deprecated - -The `dt_push_post` action has been deprecated in favor of two actions: `dt_push_external_post` and `dt_push_network_post`. - -Extenders using the old action are advised to switch to the new actions as a matter of priority. The deprecated action had conflicting arguments so can not be relied upon to pass data consistently. - -### Internationalization improvements - -The generation of translation files has been updated to include strings included in JavaScript files. - -Version 2.0.0 of Distributor has also improved pluralization of strings and combined similar strings to reduce the burden on translators. diff --git a/docs/post-meta.md b/docs/post-meta.md deleted file mode 100644 index c1ef78145..000000000 --- a/docs/post-meta.md +++ /dev/null @@ -1,78 +0,0 @@ ---- - -### Table of Contents -- [Source post meta](#source-post-meta) - - [Network connections](#network-connections) -- [Distributed post meta](#distributed-post-meta) - - [Connects a Source Post to a Subscription](#connects-a-source-post-to-a-subscription) - - [Network connections](#network-connections-1) - - [External connections](#external-connections) -- [Distributed post meta](#distributed-post-meta-1) -- [Connections post type post meta](#connections-post-type-post-meta) - - [External connections](#external-connections-1) - ---- - -### Source post meta - -- `dt_unlinked` Whether this post is linked to the original version. For the original post this is set to true. - -#### Network connections - -- `dt_connection_map` - ---- - -### Distributed post meta - -- `dt_original_post_id` -- `dt_original_media_url` -- `dt_original_media_id` -- `dt_original_source_id` -- `dt_original_post_deleted` -- `dt_original_post_parent` -- `dt_original_site_name` -- `dt_original_site_url` -- `dt_original_post_url` -- `dt_original_deleted` Whether the original post has been deleted. -- `dt_unlinked` Whether this post is linked to the original version. -- `dt_original_file_path` Only saved if filter `dt_process_media_save_source_file_path` is used. -- `dt_syndicate_time` - -#### Connects a Source Post to a Subscription -- `dt_subscriptions` -- `dt_subscription_update` - -#### Network connections - -- `dt_original_blog_id` - -#### External connections - -- `dt_full_connection` - ---- - -### Distributed post meta - -For non-public `dt_subscription` post type. - -- `dt_subscription_remote_post_id` -- `dt_subscription_signature` -- `dt_subscription_remote_post_id` -- `dt_subscription_target_url` -- `dt_subscription_post_id` - ---- - -### Connections post type post meta - -#### External connections - -- `dt_sync_log` -- `dt_external_connection_type` -- `dt_external_connection_allowed_roles` -- `dt_external_connection_check_time` -- `dt_external_connection_url` -- `dt_external_connection_auth` -- `dt_external_connections` Stores what we can do with a given external connection (push or pull). diff --git a/docs/snippets.md b/docs/snippets.md deleted file mode 100644 index 1c2a438aa..000000000 --- a/docs/snippets.md +++ /dev/null @@ -1,202 +0,0 @@ ---- - -### Table of Contents -- [Limit to certain post types](#limit-to-certain-post-types) -- [Limit to certain user capabilities](#limit-to-certain-user-capabilities) -- [Limit to certain sites on the network](#limit-to-certain-sites-on-the-network) -- [Remove canonical links for both Internal and External Connections](#remove-canonical-links-for-both-internal-and-external-connections) -- [Push original publication date](#push-original-publication-date) -- [Automatically unlink posts](#automatically-unlink-posts) -- [Modify custom meta data](#modify-custom-meta-data) -- [Exclude meta key from distribution](#exclude-meta-key-from-distribution) - ---- - -### Limit to certain post types - -```php -add_filter( 'distributable_post_types', 'client_prefix_filter_post_types' ); -/** - * Filter the post types we can distribute. - * - * @see https://10up.github.io/distributor/distributable_post_types.html - * - * @return array - */ -function client_prefix_filter_post_types() : array { - return array( 'post', 'page' ); -} -``` - -### Limit to certain user capabilities - -```php -add_filter( 'dt_syndicatable_capabilities', 'client_prefix_filter_user_capabilities' ); -/** - * Filter the user capabilities that are allowed to distribute content. - * - * @see https://10up.github.io/distributor/dt_syndicatable_capabilities.html - * - * @return string - */ -function client_prefix_filter_user_capabilities() : string { - return 'manage_options'; -} -``` - -### Limit to certain sites on the network - -```php -add_filter( 'dt_authorized_sites', 'client_prefix_filter_authorized_sites', 10, 2 ); -/** - * Filter certain sites from the authorized sites list. - * - * @see https://10up.github.io/distributor/dt_authorized_sites.html - * - * @param array $authorized_sites Authorized sites. - * @param string $context Push or pull. - * - * @return array - */ -function client_prefix_filter_authorized_sites( array $authorized_sites, string $context ) : array { - return array_filter( - $authorized_sites, - function( $site ) { - return '/' === $site['site']->path; - } - ); -} -``` - -### Remove canonical links for both Internal and External Connections - -```php -/** - * Stop Distributor from changing the canonical links. - * - * This removes Distributor's canonical functionality from both Internal and - * External Connections. - * - * This accounts for sites using either WordPress or Yoast SEO to generate the - * canonical URL. - */ -add_action( 'plugins_loaded', function() { - remove_filter( 'get_canonical_url', '\\Distributor\\Hooks\\get_canonical_url', 10, 2 ); - remove_filter( 'wpseo_canonical', '\\Distributor\\Hooks\\wpseo_canonical', 10, 2 ); -} ); -``` - -### Push original publication date - -```php -/** - * Keep the publication date on the new pushed post. - * - * This filter is used to filter the arguments sent to the remote server during a push. The below code snippet passes the original published date to the new pushed post and sets the same published date instead of setting it as per the current time. - */ -add_filter( 'dt_push_post_args', function( $post_body, $post, $args, $connection ) { - - // When pushing to an external connection, we use the REST API, so the name of the field is `date`. - // But when pushing to an internal connection, the attributes are sent to wp_insert_post, which expects `post_date`. - $field_prefix =( $connection instanceof \Distributor\ExternalConnections\WordPressExternalConnection ) ? '' : 'post_'; - - $post_body[ $field_prefix . 'date'] = $post->post_date; - $post_body[ $field_prefix . 'date_gmt'] = $post->post_date_gmt; - - return $post_body; -}, 10, 4 ); - -/** - * This filters the the arguments passed into wp_insert_post during a pull - */ -add_filter( 'dt_pull_post_args', function( $post_array, $remote_id, $post ) { - $post_array['post_date'] = $post->post_date; - $post_array['post_date_gmt'] = $post->post_date_gmt; - - return $post_array; -}, 10, 3 ); -``` - -### Automatically unlink posts -```php -/** - * Auto unlink distributor posts automatically. - * - * Runs on the `dt_after_set_meta` hook. - * - * @param mixed $meta All received meta for the post - * @param mixed $existing_meta Existing meta for the post - * @param mixed $post_id Post ID - * @return void - */ -function client_prefix_auto_unlink_distributed_posts( $meta, $existing_meta, $post_id ) { - $post = get_post( $post_id ); - - if ( ! $post ) { - return; - } - - $is_distributed = get_post_meta( $post->ID, 'dt_original_post_id', true ) ? true : false; - - if ( ! $is_distributed ) { - return; - } - - update_post_meta( $post->ID, 'dt_unlinked', true ); -} -add_action( 'dt_after_set_meta', 'client_prefix_auto_unlink_distributed_posts', 10, 3 ); -``` - -### Modify custom meta data - -```php -/** - * Set default post meta if not set in the original. - * - * @param {array} $meta All received meta for the post - * @param {array} $existing_meta Existing meta for the post - * @param {int} $post_id Post ID - */ -function client_prefix_modify_meta( $meta, $existing_meta, $post_id ) { - // Set post meta if not set. - if ( ! isset( $existing_meta['my_meta_key'] ) ) { - add_post_meta( $post_id, 'my_meta_key', 'my meta value' ); - } -} -add_action( 'dt_after_set_meta', 'client_prefix_modify_meta', 10, 3 ); -``` - -### Exclude meta key from distribution - -```php -/** - * Denylist a meta key from distribution. - */ -add_filter( 'dt_excluded_meta', function( $meta_keys ) { - $meta_keys[] = 'my_meta_key'; - return $meta_keys; -} ); -``` - -### Turn off automatic updates for distributed content - -```php -/** - * Prevent auto-updates from happening for network connections. - */ -add_action( - 'init', - function() { - remove_action( 'wp_after_insert_post', [ '\Distributor\InternalConnections\NetworkSiteConnection', 'update_syndicated' ], 99 ); - } -); - -/** - * Prevent auto-updates from happening for external connections. - */ -add_action( - 'init', - function() { - remove_action( 'wp_after_insert_post', 'Distributor\Subscriptions\send_notifications', 99 ); - } -); diff --git a/docs/stored-id-handling.md b/docs/stored-id-handling.md deleted file mode 100644 index 9f094a3b1..000000000 --- a/docs/stored-id-handling.md +++ /dev/null @@ -1,305 +0,0 @@ -Distributor often deals with data stored as ID references. This is common in WordPress, where IDs are used in block attributes, shortcodes, or post meta by popular plugins like ACF, Elementor, and Gravity Forms. However, when content is distributed to another site, these stored IDs may not match, leading to data loss or unexpected content. - -To address this, Distributor provides a helper function: `distributor_register_data`. This function allows developers to register data references, specify where the data is located, define how to extract the ID, and register callbacks to process the data on both the source and target sites. - -## Overview - -The `distributor_register_data` function streamlines the process of updating stored ID references by: - -- **Identifying the data location:** Whether in post meta or within post content (shortcodes or blocks). -- **Extracting the reference:** Defining how to locate the specific attribute or meta key holding the ID. -- **Processing the data:** Using pre-distribution and post-distribution callbacks to prepare and update the reference IDs, ensuring they point to the correct data on the target site. - -#### **Important:** Ensure that this data registration code is added to both the source and target sites. Also, verify that your Distributor plugin version is the same on both sites and equal to or greater than the required version (2.2.0). - -## Function Definition - -### `distributor_register_data` - -This function registers a data piece by providing details about its location and the callbacks needed to process it during distribution. - -#### Parameters - -- **`$data_name`** (`string`): - The unique identifier for the data. - -- **`$args`** (`array`): - An array of parameters that describe: - - - **`location`** (`string`): - Where the data is stored. Accepted values are `'post_meta'` or `'post_content'`. - - - **`attributes`** (`array`): - Additional details depending on the location: - - **For post meta:** - - `meta_key` (`string`): The post meta key. **(Required)** - - **For shortcodes:** - - `shortcode` (`string`): The shortcode tag. **(Required)** - - `shortcode_attribute` (`string|array`): The attribute(s) containing the data. **(Required)** - - **For blocks:** - - `block_name` (`string`): The block name. **(Required)** - - `block_attribute` (`string|array`): The attribute(s) containing the data. **(Required)** - - - **`type`** (`string`): - Type of data. Accepted values are `'media'`, `'post'`, or `'term'`. If set, the default callbacks will be used. - **Note:** If this parameter is used in conjunction with custom callbacks, the custom callbacks will be used instead. - - - **`pre_distribute_cb`** (`callable`): - A callback function that prepares the data on the source site before distribution. It should return extra data that is required for processing on the target site. - - - **`post_distribute_cb`** (`callable`): - A callback function that processes the extra data on the target site and returns the updated ID to replace the source reference. - ---- - -## Examples - -Below are several examples demonstrating how to use the `distributor_register_data` function in different scenarios. - -### 1. Handling an Image ID Stored in Post Meta - -In this example, we register an image ID stored in post meta under the key `example_image_id`. - -#### With Custom Callback Functions - -```php -distributor_register_data( 'example_post_meta_data', array( - 'location' => 'post_meta', - 'attributes' => array( - 'meta_key' => 'example_image_id', - ), - // Callback function to prepare the data before sending it from the source site. - // $image_id is the ID stored in post meta (example_image_id). - // $source_post_id is the ID of the post being distributed. - 'pre_distribute_cb' => function ( $image_id, $source_post_id ) { - if ( ! $image_id ) { - return array(); - } - - $image_url = wp_get_attachment_image_url( $image_id, 'full' ); - - // Return extra data required for processing on the target site. - return array( - 'image_url' => $image_url, - ); - }, - // Callback function to process the data before saving it on the target site. - // $extra_data is the data returned from pre_distribute_cb. - // $source_data is the original data stored in post meta. - // $post_data is the distributed post's data. - 'post_distribute_cb' => function ( $extra_data, $source_data, $post_data ) { - if ( ! isset( $extra_data['image_url'] ) ) { - return; - } - - $source_image_url = $extra_data['image_url']; - $source_image_id = $source_data; - - // Check if the media already exists on the target site. If yes, return its ID. - $image_id = Utils\get_attachment_id_by_original_data( $source_image_id, $source_image_url ); - - // If the media is not found, process and save it. - if ( ! $image_id ) { - $image_id = Utils\process_media( $source_image_url, 0, [] ); - update_post_meta( $image_id, 'dt_original_media_id', wp_slash( $source_image_id ) ); - update_post_meta( $image_id, 'dt_original_media_url', wp_slash( $source_image_url ) ); - } - - // Return the media ID to replace the source reference. - return $image_id; - }, -) ); -``` - -**Explanation** - -1. **Pre-Distribution Callback (`pre_distribute_cb`):** - - Retrieves the URL of the image using the stored ID. - - Returns an array with the image URL to be used on the target site. - -2. **Post-Distribution Callback (`post_distribute_cb`):** - - Checks whether the media already exists on the target site by comparing the source image data. - - If not found, processes (uploads) the media and updates the relevant post meta with the original source data. - - Returns the new image ID for the distributed post. - -#### Using the `type` Parameter to Utilize the Default Callback - -```php -distributor_register_data( 'example_post_meta_data', array( - 'location' => 'post_meta', - 'attributes' => array( - 'meta_key' => 'example_image_id', - ), - 'type' => 'media' -) ); -``` - -### 2. Handling Term ID in a Block Attribute - -This example demonstrates how to handle a term ID stored in a block attribute. - -#### With Custom Callback Functions - -```php -distributor_register_data( - 'example_block_data', - array( - 'location' => 'post_content', - 'attributes' => array( - 'block_name' => 'example/block-name', - 'block_attribute' => 'id', - ), - 'pre_distribute_cb' => function( $source_term_id ) { - if ( ! $source_term_id ) { - return array(); - } - - $term = get_term_by( 'id', $source_term_id, 'category' ); - if ( ! $term ) { - return array(); - } - - // Return extra data required for processing on the target site. - return array( - 'name' => $term->name, - 'slug' => $term->slug, - 'description' => $term->description, - 'taxonomy' => $term->taxonomy, - ); - }, - 'post_distribute_cb' => function( $extra_data, $source_data, $post_data ) { - if ( ! isset( $extra_data['name'] ) && ! isset( $extra_data['taxonomy'] ) ) { - return; - } - - $existing_term = get_term_by( 'name', $extra_data['name'], $extra_data['taxonomy'] ); - if ( $existing_term ) { - return $existing_term->term_id; - } - - // Create the term on the target site. - $term = wp_insert_term( - $extra_data['name'], - $extra_data['taxonomy'], - array( - 'description' => $extra_data['description'], - 'slug' => $extra_data['slug'], - ) - ); - - if ( is_wp_error( $term ) ) { - return; - } - - return $term['term_id']; - }, - ) -); -``` - -#### Using the `type` Parameter to Utilize the Default Callback - -```php -distributor_register_data( - 'example_block_data', - array( - 'location' => 'post_content', - 'attributes' => array( - 'block_name' => 'example/block-name', - 'block_attribute' => 'id', - ), - 'type' => 'term' - ) -); -``` - - -### 3. Handling Post ID in a Shortcode Attribute - -This example shows how to manage a post ID stored within a shortcode attribute. - -#### With Custom Callback Functions -```php -// Distributor data registration for the post ID stored in shortcode attribute. -distributor_register_data( 'example_post_data', array( - 'location' => 'post_content', - 'attributes' => array( - 'shortcode' => 'extra_shortcode', - 'shortcode_attribute' => 'example_post_id', - ), - 'pre_distribute_cb' => function( $post_id ) { - if ( ! $post_id ) { - return array(); - } - - $post = get_post( $post_id ); - if ( ! $post ) { - return array(); - } - - return array( - 'post_type' => $post->post_type, - 'post_title' => $post->post_title, - 'post_status' => $post->post_status, - 'post_content' => $post->post_content, - ); - }, - 'post_distribute_cb' => function( $extra_data, $source_data, $post_data ) { - if ( ! isset( $extra_data['post_type'] ) && ! isset( $extra_data['post_title'] ) ) { - return $source_data; - } - - $posts = get_posts( - array( - 'post_type' => $extra_data['post_type'], - 'meta_query' => array( - array( - 'key' => 'dt_original_post_id', - 'value' => $source_data, - 'compare' => '=', - ), - ), - 'post_status' => 'any', - 'fields' => 'ids', - 'no_found_rows' => true, - 'update_post_meta_cache' => false, - 'update_post_term_cache' => false, - 'numberposts' => 1, - 'orderby' => 'post_date ID', - 'order' => 'ASC', - ) - ); - - if ( ! empty( $posts ) ) { - return $posts[0]; - } - - // Create the post. - $post_id = wp_insert_post( $extra_data ); - - if ( is_wp_error( $post_id ) ) { - return; - } - - // Store the original post ID in post meta to check for existing posts on subsequent distributions. - update_post_meta( $post_id, 'dt_original_post_id', wp_slash( $source_data ) ); - - return $post_id; - }, -) ); -``` - -#### Using the `type` Parameter to Utilize the Default Callback -```php -// Distributor data registration for the post ID stored in shortcode attribute. -distributor_register_data( 'example_post_data', array( - 'location' => 'post_content', - 'attributes' => array( - 'shortcode' => 'extra_shortcode', - 'shortcode_attribute' => 'example_post_id', - ), - 'type' => 'post' -) ); -``` - -If you have any doubts, encounter any different scenarios to handle, or need further assistance, please feel free to report an issue on our [GitHub repository](https://github.com/10up/distributor) and we would be happy to help. diff --git a/docs/tutorials.json b/docs/tutorials.json deleted file mode 100644 index 1570e6a68..000000000 --- a/docs/tutorials.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "connect-to-an-existing-post": { - "title": "Connect to an Existing Post" - }, - "exclude-meta-from-yoast-duplicate-post": { - "title": "Exclude Meta from Yoast Duplicate Post" - }, - "migration-guide-version-1-to-version-2": { - "title": "Migration Guide Version 1 to Version 2" - }, - "post-meta": { - "title": "Post Meta" - }, - "snippets": { - "title": "Snippets" - }, - "demo-with-playground": { - "title": "Demo with Playground" - }, - "stored-id-handling": { - "title": "Stored ID Handling" - }, - "auto-distribution": { - "title": "Enabling Auto Distribution" - } -} From 08deaff5c0c324d4c8bfdf1745e889e4a758bc57 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Fri, 12 Sep 2025 16:22:56 +0530 Subject: [PATCH 09/25] Remove hook docs template. --- .github/hookdoc-tmpl/README.md | 48 ---------- .github/hookdoc-tmpl/layout.tmpl | 54 ----------- .github/hookdoc-tmpl/static/styles-10up.css | 99 --------------------- 3 files changed, 201 deletions(-) delete mode 100644 .github/hookdoc-tmpl/README.md delete mode 100644 .github/hookdoc-tmpl/layout.tmpl delete mode 100644 .github/hookdoc-tmpl/static/styles-10up.css diff --git a/.github/hookdoc-tmpl/README.md b/.github/hookdoc-tmpl/README.md deleted file mode 100644 index 3cdd9b339..000000000 --- a/.github/hookdoc-tmpl/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# Welcome to the Distributor Developer Documentation - -This resource is generated documentation on actions and filters found in the Distributor plugin. Use the sidebar to browse and navigate. - -For more information about using Distributor with WordPress, please see the [Distributor website](https://distributorplugin.com/). - -## Migrating to version 2.0 - -Version 2.0 of Distributor contains breaking changes. Please review the migration guide tutorial and follow any steps required. - -To report an issue with Distributor or contribute back to the project, please visit the [GitHub repository](https://github.com/10up/distributor/). - -## Developers - -### Running Locally - -If you are compiling Distributor locally, note that the recommended version of Node.js is version 16.x. The minimum version required is Node.js 12.x. - -An `.nvmrc` file is included in the plugin repository. It's recommended you install [fnm (fast node manager)](https://github.com/Schniz/fnm/), [nvm (node version manager)](https://github.com/nvm-sh/nvm) or similar when developing locally. - -### Testing - -The plugin contains a standard test suite compatible with PHPUnit. If you want to test across multiple PHP versions, a [Dockunit](https://github.com/dockunit/dockunit) file is included. - -### Debugging - -You can define a constant `DISTRIBUTOR_DEBUG` to `true` to increase the ease of debugging in Distributor. This will make all remote requests blocking and expose the subscription post type. - -Enabling this will also provide more debugging information in your error log for image side loading issues. The specific logging method may change in the future. - -### Application Passwords - -Application passwords are only available for live sites running over an HTTPS connection. - -For your local development environment, you will need these snippets to enable application passwords without the need for an HTTPS connection. A local development environment is one that "can reach the internet but **is not reachable from the internet**". - -```php -// In your local environment's wp-config.php file. -define( 'WP_ENVIRONMENT_TYPE', 'local' ); - -// In a custom plugin on your local environment. -add_filter( 'wp_is_application_passwords_available', '__return_true' ); - -add_action( 'wp_authorize_application_password_request_errors', function( $error ) { - $error->remove( 'invalid_redirect_scheme' ); -} ); -``` - diff --git a/.github/hookdoc-tmpl/layout.tmpl b/.github/hookdoc-tmpl/layout.tmpl deleted file mode 100644 index 4830fc316..000000000 --- a/.github/hookdoc-tmpl/layout.tmpl +++ /dev/null @@ -1,54 +0,0 @@ - - - - - <?js= title ?> - 10up Distributor Developer Documentation - - - - - - - - - - - - class="home"> - -
- - -

- - - - - - -
- - - -
- - - - - diff --git a/.github/hookdoc-tmpl/static/styles-10up.css b/.github/hookdoc-tmpl/static/styles-10up.css deleted file mode 100644 index c90102c17..000000000 --- a/.github/hookdoc-tmpl/static/styles-10up.css +++ /dev/null @@ -1,99 +0,0 @@ -body { - background: #fefefe; - color: #232323; - font-family: 'IBM Plex Sans', sans-serif; - font-size: 1.3rem; - font-weight: 300; -} - -h1, h2, h3 { - line-height: 1.2; - font-family: 'Playfair Display', sans-serif; - font-weight: 900; - letter-spacing: -.01em; -} - -h1.page-title { - font-size: 42px; - margin-top: .5em; -} - -nav ul { - font-size: 1.2rem; -} - -nav li a { - background-image: none; -} - -nav li a:hover { - text-decoration: underline; -} - -code, pre, -nav ul a, nav ul a:visited, nav ul a:active, -.name, .signature, -.params .name, .props .name, -.name code { - font-family: 'IBM Plex Mono', monospace; -} - -article h1 { - margin: 12px 0 32px; -} - -a { - background-image: linear-gradient(transparent calc(100% - 7px), #f2dede 0), - linear-gradient(transparent calc(100% - 7px), #cef8f7 0); - background-position: 0 0; - background-repeat: no-repeat; - background-size: 0 100%, 100% 100%; - color: #232323; - text-decoration: none; - transition: all .1s; -} - -a:visited, -a:active { - color: #232323; -} - -a:focus, -a:hover { - background-size: 100% 100%, 100% 100%; - color: #232323; - text-decoration: none; -} - -a.banner { - background-image: none; - margin-left: -10px; -} - -a.banner img { - width: 100%; - max-width: 888px; -} - -footer { - text-align: center; - font-size: .8em; - font-style: normal; - font-weight: 300; -} - -.home #main > section:first-of-type, -.home nav > h2 { - display: none; -} - -.prettyprint.source { - font-size: 14px; -} - -.prettyprint code { - padding: 2px 10px; - line-height: 16px; - min-height: 16px; - height: auto; -} From ad8b74c03192af4c9be97cb3da20b4d213f215cb Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Fri, 12 Sep 2025 16:37:36 +0530 Subject: [PATCH 10/25] Remove hook docs config. --- hookdoc-conf.json | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 hookdoc-conf.json diff --git a/hookdoc-conf.json b/hookdoc-conf.json deleted file mode 100644 index 9b8763005..000000000 --- a/hookdoc-conf.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "opts": { - "destination": "docs-built", - "template": "node_modules/wp-hookdoc/template", - "recurse": true, - "readme": "./.github/hookdoc-tmpl/README.md", - "tutorials": "./docs" - }, - "source": { - "includePattern": ".+\\.(php|inc)?$" - }, - "plugins": [ - "node_modules/wp-hookdoc/plugin", - "plugins/markdown" - ], - "markdown": { - "idInHeadings": true - }, - "templates": { - "default": { - "layoutFile": ".github/hookdoc-tmpl/layout.tmpl", - "staticFiles": { - "include": [ - "./.github/hookdoc-tmpl/static" - ] - } - } - } -} From 6d40f61d152014283ff2b8ebcdae93f83f610f21 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Fri, 12 Sep 2025 16:38:06 +0530 Subject: [PATCH 11/25] Add readme instruction for building docs site. --- README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/README.md b/README.md index d4aef6ac2..02b17ec24 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,36 @@ By default, drafts are the preferred status and can't be changed at the source s Oftentimes the communication Distributor attempts to make across sites using the REST API will be flagged by various security plugins and surreptitiously blocked. If you run into an issue like this, please reach out to the support for your security plugin and ask about getting Distributor unblocked ([here is an example for doing so with Wordfence](https://wordpress.org/support/topic/distributor-plugin-being-blocked/)). +## Building and Running Documentation Site + +The Distributor documentation site is built using [WP Hooks Documentor](https://github.com/10up/wp-hooks-documentor). Follow these steps to build and run the documentation site locally: + +### 1. Build Documentation + +```bash +# Install dependencies and build the plugin +npm i && npm run build:docs +``` + +This will: +- Install all required dependencies +- Process all hook documentation from the codebase +- Generate the documentation site in the `./docs-built` directory + +### 2. Run Documentation Site Locally + +```bash +# Navigate to docs-built directory and start the server +cd ./docs-built && npm run serve +``` + +The documentation site will be available at [http://localhost:3000](http://localhost:3000). + +### 3. Deployment + +The documentation site will automatically deploy to GitHub Pages when changes are merged into the `trunk` branch. You can view the live documentation at [https://10up.github.io/distributor/](https://10up.github.io/distributor/). + + ## Developers See [Distributor Developer Documentation](https://10up.github.io/distributor/#developers). From 758c359198c6438c0403124f704ee7b958212f78 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Fri, 12 Sep 2025 16:38:22 +0530 Subject: [PATCH 12/25] Make light theme default. --- wp-hooks-docs/docusaurus.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wp-hooks-docs/docusaurus.config.js b/wp-hooks-docs/docusaurus.config.js index 4b1ca9150..595250b3c 100644 --- a/wp-hooks-docs/docusaurus.config.js +++ b/wp-hooks-docs/docusaurus.config.js @@ -101,7 +101,7 @@ const config = { ], }, colorMode: { - defaultMode: 'dark', + defaultMode: 'light', disableSwitch: false, respectPrefersColorScheme: true, }, From 338a8850ba4451ee913967dbe9a4962a95d661ca Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Fri, 12 Sep 2025 16:38:45 +0530 Subject: [PATCH 13/25] Update docs workflow. --- .github/workflows/build-docs.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 03ab470d4..f4903a380 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -1,4 +1,4 @@ -name: Build Hook Docs +name: Build Developer Docs on: push: @@ -11,13 +11,18 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Setup proper PHP version + uses: shivammathur/setup-php@9e72090525849c5e82e596468b86eb55e9cc5401 # v2.32.0 + with: + php-version: 8.3 + - name: Use desired version of NodeJS uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0 with: - node-version-file: '.nvmrc' + node-version: 20 - name: Check versions - run: npm -v; node -v + run: npm -v; node -v; php -v - name: npm install, and build docs run: | @@ -31,4 +36,4 @@ jobs: uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: './docs-built' + publish_dir: './docs-built/build' From e81ab39ab50934439407649f6a052298e0f12ca1 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Fri, 12 Sep 2025 16:46:22 +0530 Subject: [PATCH 14/25] Add developers page. --- .gitignore | 2 + README.md | 3 +- package.json | 2 +- .../docs/01.get-started/04.developers.md | 38 +++++++++++++++++++ 4 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 wp-hooks-docs/docs/01.get-started/04.developers.md diff --git a/.gitignore b/.gitignore index 8c357b0ee..1af2abe50 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ tests/cypress/reports tests/cypress/downloads distributor.zip +/wp-hooks-docs/docs/hooks +/wp-hooks-docs/docs/01.get-started/04.CHANGELOG.md diff --git a/README.md b/README.md index 02b17ec24..49a247334 100644 --- a/README.md +++ b/README.md @@ -200,10 +200,9 @@ The documentation site will be available at [http://localhost:3000](http://local The documentation site will automatically deploy to GitHub Pages when changes are merged into the `trunk` branch. You can view the live documentation at [https://10up.github.io/distributor/](https://10up.github.io/distributor/). - ## Developers -See [Distributor Developer Documentation](https://10up.github.io/distributor/#developers). +See [Distributor Developer Documentation](https://10up.github.io/distributor/get-started/developers). ## Changelog diff --git a/package.json b/package.json index aee47ed6a..652d70359 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ }, "scripts": { "build": "wp-scripts build", - "prebuild:docs": "cp ./CHANGELOG.md ./wp-hooks-docs/docs/01.get-started/04.CHANGELOG.md", + "prebuild:docs": "cp ./CHANGELOG.md ./wp-hooks-docs/docs/01.get-started/05.CHANGELOG.md", "build:docs": "rm -rf docs-built && wp-hooks-documentor generate", "check-engines": "wp-scripts check-engines", "check-licenses": "wp-scripts check-licenses", diff --git a/wp-hooks-docs/docs/01.get-started/04.developers.md b/wp-hooks-docs/docs/01.get-started/04.developers.md new file mode 100644 index 000000000..3e4856599 --- /dev/null +++ b/wp-hooks-docs/docs/01.get-started/04.developers.md @@ -0,0 +1,38 @@ +--- +id: developers +title: "Developers Guide" +--- + +### Running Locally + +If you are compiling Distributor locally, note that the recommended version of Node.js is version 16.x. The minimum version required is Node.js 12.x. + +An `.nvmrc` file is included in the plugin repository. It's recommended you install [fnm (fast node manager)](https://github.com/Schniz/fnm/), [nvm (node version manager)](https://github.com/nvm-sh/nvm) or similar when developing locally. + +### Testing + +The plugin contains a standard test suite compatible with PHPUnit. If you want to test across multiple PHP versions, a [Dockunit](https://github.com/dockunit/dockunit) file is included. + +### Debugging + +You can define a constant `DISTRIBUTOR_DEBUG` to `true` to increase the ease of debugging in Distributor. This will make all remote requests blocking and expose the subscription post type. + +Enabling this will also provide more debugging information in your error log for image side loading issues. The specific logging method may change in the future. + +### Application Passwords + +Application passwords are only available for live sites running over an HTTPS connection. + +For your local development environment, you will need these snippets to enable application passwords without the need for an HTTPS connection. A local development environment is one that "can reach the internet but **is not reachable from the internet**". + +```php +// In your local environment's wp-config.php file. +define( 'WP_ENVIRONMENT_TYPE', 'local' ); + +// In a custom plugin on your local environment. +add_filter( 'wp_is_application_passwords_available', '__return_true' ); + +add_action( 'wp_authorize_application_password_request_errors', function( $error ) { + $error->remove( 'invalid_redirect_scheme' ); +} ); +``` From ac12c1e075e87aad60b1b352f2ffbc581b5f9a20 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Fri, 12 Sep 2025 17:24:36 +0530 Subject: [PATCH 15/25] Ignore eslint checks on docs. --- .eslintignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.eslintignore b/.eslintignore index 909f2a92d..adee3731c 100644 --- a/.eslintignore +++ b/.eslintignore @@ -4,3 +4,4 @@ dist/* docs-built/* vendor/* gulp-tasks/* +wp-hooks-docs/* From 58e35eb6be27018b22b8176d364a3409ea91eb66 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Fri, 12 Sep 2025 19:24:17 +0530 Subject: [PATCH 16/25] Bump `@10up/wp-hooks-documentor` from 1.0.0 to 1.0.1 --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 30914e913..5b6b28fb5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ }, "devDependencies": { "@10up/cypress-wp-utils": "^0.6.0", - "@10up/wp-hooks-documentor": "^1.0.0", + "@10up/wp-hooks-documentor": "^1.0.1", "@wordpress/env": "^10.29.0", "@wordpress/scripts": "^29.0.0", "compare-versions": "^4.1.3", @@ -37,9 +37,9 @@ } }, "node_modules/@10up/wp-hooks-documentor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@10up/wp-hooks-documentor/-/wp-hooks-documentor-1.0.0.tgz", - "integrity": "sha512-fwAcLzoyliJQUqvlVQNiq8U84/ZzYL832XSFonEoaw9hpLKa4UdHFy84o3Uvg5NQqPX44fJgJin5DPxS116IFA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@10up/wp-hooks-documentor/-/wp-hooks-documentor-1.0.1.tgz", + "integrity": "sha512-bdFUlc0at64IHFxnaNjIomkrdYWVkVR0IG2LRChNauVoU+IXGAqAJaikYsHPBV0TjhyTDVJ/DUO8NfHnNjmnpA==", "dev": true, "license": "MIT", "dependencies": { @@ -22513,9 +22513,9 @@ "dev": true }, "@10up/wp-hooks-documentor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@10up/wp-hooks-documentor/-/wp-hooks-documentor-1.0.0.tgz", - "integrity": "sha512-fwAcLzoyliJQUqvlVQNiq8U84/ZzYL832XSFonEoaw9hpLKa4UdHFy84o3Uvg5NQqPX44fJgJin5DPxS116IFA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@10up/wp-hooks-documentor/-/wp-hooks-documentor-1.0.1.tgz", + "integrity": "sha512-bdFUlc0at64IHFxnaNjIomkrdYWVkVR0IG2LRChNauVoU+IXGAqAJaikYsHPBV0TjhyTDVJ/DUO8NfHnNjmnpA==", "dev": true, "requires": { "commander": "^14.0.0", diff --git a/package.json b/package.json index 652d70359..9a2b557a9 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ }, "devDependencies": { "@10up/cypress-wp-utils": "^0.6.0", - "@10up/wp-hooks-documentor": "^1.0.0", + "@10up/wp-hooks-documentor": "^1.0.1", "@wordpress/env": "^10.29.0", "@wordpress/scripts": "^29.0.0", "compare-versions": "^4.1.3", From ae136b6a3d19bc0316fa0e0bb7d2805830b5c5c0 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Sat, 13 Sep 2025 01:17:12 +0530 Subject: [PATCH 17/25] Add PHP and Node version requirements in readme. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 49a247334..1124d5043 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,7 @@ Oftentimes the communication Distributor attempts to make across sites using the The Distributor documentation site is built using [WP Hooks Documentor](https://github.com/10up/wp-hooks-documentor). Follow these steps to build and run the documentation site locally: ### 1. Build Documentation +Node.js >= 20.0 and PHP >= 8.3 will be needed in your development environment. ```bash # Install dependencies and build the plugin From 262c6dac90e7588cdad2f445a1326de10daf0763 Mon Sep 17 00:00:00 2001 From: Jeffrey Paul Date: Tue, 13 Jan 2026 08:26:43 -0600 Subject: [PATCH 18/25] Update wp-hooks-docs/src/theme/Footer/index.js Co-authored-by: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> --- wp-hooks-docs/src/theme/Footer/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wp-hooks-docs/src/theme/Footer/index.js b/wp-hooks-docs/src/theme/Footer/index.js index d290969ac..bc54612ca 100644 --- a/wp-hooks-docs/src/theme/Footer/index.js +++ b/wp-hooks-docs/src/theme/Footer/index.js @@ -61,7 +61,7 @@ function Footer() { copyright={ `Finely crafted by Fueled, ©${ currentYear }` } /> - Support + Issues    |  From 1b1ea39f2bc46dd47aa1c9b46ccf70e16921836d Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Wed, 14 Jan 2026 09:28:06 +1100 Subject: [PATCH 19/25] Update node version to 20. --- .nvmrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.nvmrc b/.nvmrc index 3c032078a..209e3ef4b 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18 +20 From 2fac1e39a6f7a664c9e497916b6c1562e5cb5612 Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Thu, 22 Jan 2026 15:01:12 +1100 Subject: [PATCH 20/25] Fix typo in duplicate changelog location. --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1af2abe50..8396c578c 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,4 @@ tests/cypress/downloads distributor.zip /wp-hooks-docs/docs/hooks -/wp-hooks-docs/docs/01.get-started/04.CHANGELOG.md +/wp-hooks-docs/docs/01.get-started/05.CHANGELOG.md From 8e89580f2ce957e6639c0ec5dceee0cf84de3767 Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Thu, 22 Jan 2026 15:03:39 +1100 Subject: [PATCH 21/25] Remove target blanks. --- wp-hooks-docs/src/theme/Footer/index.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/wp-hooks-docs/src/theme/Footer/index.js b/wp-hooks-docs/src/theme/Footer/index.js index bc54612ca..c0811c057 100644 --- a/wp-hooks-docs/src/theme/Footer/index.js +++ b/wp-hooks-docs/src/theme/Footer/index.js @@ -110,8 +110,6 @@ function Footer() { href="https://www.facebook.com/10up.agency/?ref=distributor" title="10up on Facebook" aria-hidden="true" - target="_blank" - rel="noopener nofollow noreferrer" >
@@ -98,7 +98,7 @@ function Footer() { height="24" style={ { width: '21px' } } src="https://distributorplugin.com/wp-content/uploads/sites/2/2025/06/svgexport-3-1.svg" - alt="10up Logo" + alt="10up" />{ ' ' } WordPress Practice From 1d49cff2d87d6191d3b50b2f056139a82d39cb22 Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Thu, 22 Jan 2026 15:21:17 +1100 Subject: [PATCH 23/25] Improve accessibility of social media links in footer. --- wp-hooks-docs/src/theme/Footer/index.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/wp-hooks-docs/src/theme/Footer/index.js b/wp-hooks-docs/src/theme/Footer/index.js index c832c50d6..2223d77ce 100644 --- a/wp-hooks-docs/src/theme/Footer/index.js +++ b/wp-hooks-docs/src/theme/Footer/index.js @@ -108,40 +108,37 @@ function Footer() {
From eeaea18214b5af9e82c7f31822a08b5a1446041d Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Fri, 23 Jan 2026 10:49:10 +1100 Subject: [PATCH 24/25] Use default styles for launch. --- wp-hooks-docs/src/css/custom.css | 107 ------------------------------- 1 file changed, 107 deletions(-) diff --git a/wp-hooks-docs/src/css/custom.css b/wp-hooks-docs/src/css/custom.css index 86ef0ec3b..f479726fe 100644 --- a/wp-hooks-docs/src/css/custom.css +++ b/wp-hooks-docs/src/css/custom.css @@ -2,110 +2,3 @@ * Custom CSS for WordPress Hooks Documentation */ -:root { - --ifm-color-primary: #ffc619; - --ifm-color-primary-dark: #ffc619; - --ifm-color-primary-darker: #ffc619; - --ifm-color-primary-darkest: #ffc619; - --ifm-color-primary-light: #ffc619; - --ifm-color-primary-lighter: #ffc619; - --ifm-color-primary-lightest: #ffc619; - --ifm-code-font-size: 95%; - --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); -} - -[data-theme='dark'] { - --ifm-color-primary: #fee450; - --ifm-color-primary-dark: #fedf2f; - --ifm-color-primary-darker: #fedc1e; - --ifm-color-primary-darkest: #e8c601; - --ifm-color-primary-light: #fee971; - --ifm-color-primary-lighter: #feec82; - --ifm-color-primary-lightest: #fff3b4; - --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); -} - -.hook-type { - display: inline-block; - padding: 0.2rem 0.5rem; - border-radius: 4px; - font-size: 0.8rem; - font-weight: 600; - margin-bottom: 1rem; -} - -.hook-type.action { - background-color: var(--ifm-color-primary-lightest); - color: var(--ifm-color-primary-darkest); -} - -.hook-type.filter { - background-color: var(--ifm-color-primary-darker); - color: white; -} - -.hook-parameters { - margin-top: 1rem; - padding: 1rem; - background-color: var(--ifm-color-emphasis-100); - border-radius: 8px; -} - -.hook-source { - font-family: var(--ifm-font-family-monospace); - font-size: var(--ifm-code-font-size); - padding: 0.2rem 0.4rem; - background-color: var(--ifm-color-emphasis-200); - border-radius: 4px; -} - -.navbar__logo img { - height: 2rem; -} - -.navbar__search > * { - max-width: 47.5rem; - width: 100%; - margin: 0 auto; -} - -.main-wrapper .navbar__search > button { - right: calc(50% - 47.5rem / 2 + 4rem); - width: auto; - background: var(--ifm-navbar-search-input-background-color); - border-radius: .25rem; - width: 2rem; - height: 2rem; - transition: background-color .2s; -} - -.main-wrapper .navbar__search span { - width: 100%; -} - -.main-wrapper .navbar__search input::placeholder { - color: var(--ifm-color-content); -} - -.navbar__search [class^=searchHintContainer_] { - display: none !important; -} - -.main-wrapper .navbar__search input { - background-color: #fff; - border-radius: 0.75em; - height: 4rem; - display: block; - width: 100%; - border: 3px solid var(--ifm-color-content); - padding: 1rem 1.5rem; - background-position: right 1.5rem center; - margin-right: auto; - margin-left: auto; - font-size: 1.125rem; - color: var(--ifm-color-content); -} - -[data-theme='light'] .markdown > ul > li > a { - font-weight: 700; -} From 18977bdff86b9a42dd05038099eafdf1afbfc848 Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Fri, 23 Jan 2026 10:49:34 +1100 Subject: [PATCH 25/25] Tweaks for a11y. --- wp-hooks-docs/src/css/custom.css | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/wp-hooks-docs/src/css/custom.css b/wp-hooks-docs/src/css/custom.css index f479726fe..f052f57f0 100644 --- a/wp-hooks-docs/src/css/custom.css +++ b/wp-hooks-docs/src/css/custom.css @@ -2,3 +2,15 @@ * Custom CSS for WordPress Hooks Documentation */ +html:not([data-theme='dark']) { + --ifm-color-primary: #3071db; + --ifm-breadcrumb-color-active: #2c6bd4; + --ifm-hover-overlay: rgba(0, 0, 0, 0.1) +} + +html[data-theme='dark'] { + --ifm-color-primary: #367ff6; + --ifm-breadcrumb-color-active: #3884ff; + --ifm-breadcrumb-item-background-active: #202022; + --ifm-hover-overlay: rgba(255, 255, 255, 0.1) +}