From 92fc3ecec0b882bfb72bd4aa9f7f7167632aa3fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Menrath?= Date: Fri, 11 Oct 2024 10:53:12 +0200 Subject: [PATCH 1/7] first draft for event reminders --- build/reminder/block.json | 8 + build/reminder/plugin.asset.php | 1 + build/reminder/plugin.js | 215 ++++++++++++++++++ build/reminder/plugin.js.map | 1 + .../activitypub/transformer/class-event.php | 18 +- .../transformer/class-gatherpress.php | 2 +- .../transformer/class-the-events-calendar.php | 2 +- .../transformer/class-vs-event-list.php | 2 +- includes/class-reminder.php | 176 ++++++++++++++ includes/class-settings.php | 15 +- includes/class-setup.php | 35 +++ includes/plugins/class-event-plugin.php | 2 + package.json | 19 ++ src/reminder/block.json | 9 + src/reminder/plugin.js | 43 ++++ templates/settings.php | 20 ++ 16 files changed, 562 insertions(+), 6 deletions(-) create mode 100644 build/reminder/block.json create mode 100644 build/reminder/plugin.asset.php create mode 100644 build/reminder/plugin.js create mode 100644 build/reminder/plugin.js.map create mode 100644 includes/class-reminder.php create mode 100755 package.json create mode 100644 src/reminder/block.json create mode 100644 src/reminder/plugin.js diff --git a/build/reminder/block.json b/build/reminder/block.json new file mode 100644 index 0000000..ad2fc04 --- /dev/null +++ b/build/reminder/block.json @@ -0,0 +1,8 @@ +{ + "name": "reminder", + "title": "Reminder Plugin: not a block, but block.json is very useful.", + "category": "widgets", + "icon": "admin-comments", + "keywords": [], + "editorScript": "file:./plugin.js" +} \ No newline at end of file diff --git a/build/reminder/plugin.asset.php b/build/reminder/plugin.asset.php new file mode 100644 index 0000000..9b2089b --- /dev/null +++ b/build/reminder/plugin.asset.php @@ -0,0 +1 @@ + array('react-jsx-runtime', 'wp-components', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-i18n', 'wp-plugins'), 'version' => 'f38cf2ab9e8621193407'); diff --git a/build/reminder/plugin.js b/build/reminder/plugin.js new file mode 100644 index 0000000..99955d4 --- /dev/null +++ b/build/reminder/plugin.js @@ -0,0 +1,215 @@ +/******/ (() => { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ({ + +/***/ "react/jsx-runtime": +/*!**********************************!*\ + !*** external "ReactJSXRuntime" ***! + \**********************************/ +/***/ ((module) => { + +module.exports = window["ReactJSXRuntime"]; + +/***/ }), + +/***/ "@wordpress/components": +/*!************************************!*\ + !*** external ["wp","components"] ***! + \************************************/ +/***/ ((module) => { + +module.exports = window["wp"]["components"]; + +/***/ }), + +/***/ "@wordpress/core-data": +/*!**********************************!*\ + !*** external ["wp","coreData"] ***! + \**********************************/ +/***/ ((module) => { + +module.exports = window["wp"]["coreData"]; + +/***/ }), + +/***/ "@wordpress/data": +/*!******************************!*\ + !*** external ["wp","data"] ***! + \******************************/ +/***/ ((module) => { + +module.exports = window["wp"]["data"]; + +/***/ }), + +/***/ "@wordpress/editor": +/*!********************************!*\ + !*** external ["wp","editor"] ***! + \********************************/ +/***/ ((module) => { + +module.exports = window["wp"]["editor"]; + +/***/ }), + +/***/ "@wordpress/i18n": +/*!******************************!*\ + !*** external ["wp","i18n"] ***! + \******************************/ +/***/ ((module) => { + +module.exports = window["wp"]["i18n"]; + +/***/ }), + +/***/ "@wordpress/plugins": +/*!*********************************!*\ + !*** external ["wp","plugins"] ***! + \*********************************/ +/***/ ((module) => { + +module.exports = window["wp"]["plugins"]; + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/compat get default export */ +/******/ (() => { +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = (module) => { +/******/ var getter = module && module.__esModule ? +/******/ () => (module['default']) : +/******/ () => (module); +/******/ __webpack_require__.d(getter, { a: getter }); +/******/ return getter; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. +(() => { +/*!********************************!*\ + !*** ./src/reminder/plugin.js ***! + \********************************/ +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _wordpress_editor__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/editor */ "@wordpress/editor"); +/* harmony import */ var _wordpress_editor__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_editor__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var _wordpress_plugins__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/plugins */ "@wordpress/plugins"); +/* harmony import */ var _wordpress_plugins__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_plugins__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @wordpress/components */ "@wordpress/components"); +/* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_wordpress_components__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var _wordpress_data__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @wordpress/data */ "@wordpress/data"); +/* harmony import */ var _wordpress_data__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_wordpress_data__WEBPACK_IMPORTED_MODULE_3__); +/* harmony import */ var _wordpress_core_data__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @wordpress/core-data */ "@wordpress/core-data"); +/* harmony import */ var _wordpress_core_data__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_wordpress_core_data__WEBPACK_IMPORTED_MODULE_4__); +/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n"); +/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__); +/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! react/jsx-runtime */ "react/jsx-runtime"); +/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__); + + + + + + + +const reminderTimeGapDefault = activityPubEventBridge.reminderTypeGap; +const Reminder = () => { + const postType = (0,_wordpress_data__WEBPACK_IMPORTED_MODULE_3__.useSelect)(select => select('core/editor').getCurrentPostType(), []); + const [meta, setMeta] = (0,_wordpress_core_data__WEBPACK_IMPORTED_MODULE_4__.useEntityProp)('postType', postType, 'meta'); + const reminderTimeGap = meta?.activitypub_event_bridge_reminder_time_gap ? meta?.activitypub_event_bridge_reminder_time_gap : reminderTimeGapDefault; + return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(_wordpress_editor__WEBPACK_IMPORTED_MODULE_0__.PluginDocumentSettingPanel, { + name: "activitypub", + title: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('Send reminder before event\'s start', 'activitypub'), + children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(_wordpress_components__WEBPACK_IMPORTED_MODULE_2__.SelectControl, { + label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('Time gap', 'activitypub'), + value: reminderTimeGap, + options: [{ + label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('Disabled', 'activitypub-event-bridge'), + value: 0 + }, { + label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('6 hours', 'activitypub-event-bridge'), + value: 21600 + }, { + label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('1 day', 'activitypub-event-bridge'), + value: 86400 + }, { + label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('3 days', 'activitypub-event-bridge'), + value: 259200 + }, { + label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('1 week', 'activitypub-event-bridge'), + value: 604800 + }], + onChange: value => { + setMeta({ + ...meta, + activitypub_event_bridge_reminder_time_gap: value + }); + }, + __nextHasNoMarginBottom: true + }) + }); +}; +(0,_wordpress_plugins__WEBPACK_IMPORTED_MODULE_1__.registerPlugin)('activitypub-event-bridge-reminder', { + render: Reminder +}); +})(); + +/******/ })() +; +//# sourceMappingURL=plugin.js.map \ No newline at end of file diff --git a/build/reminder/plugin.js.map b/build/reminder/plugin.js.map new file mode 100644 index 0000000..2eaaa1c --- /dev/null +++ b/build/reminder/plugin.js.map @@ -0,0 +1 @@ +{"version":3,"file":"reminder/plugin.js","mappings":";;;;;;;;;;AAAA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;UCAA;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;UAEA;UACA;;UAEA;UACA;UACA;;;;;WCtBA;WACA;WACA;WACA;WACA;WACA,iCAAiC,WAAW;WAC5C;WACA;;;;;WCPA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;;;;;;;;;;;;;;ACN+D;AACX;AACE;AACV;AACS;AAChB;AAAA;AAErC,MAAMQ,sBAAsB,GAAGC,sBAAsB,CAACC,eAAe;AAErE,MAAMC,QAAQ,GAAGA,CAAA,KAAM;EACtB,MAAMC,QAAQ,GAAGT,0DAAS,CACvBU,MAAM,IAAMA,MAAM,CAAE,aAAc,CAAC,CAACC,kBAAkB,CAAC,CAAC,EAC1D,EACD,CAAC;EACD,MAAM,CAAEC,IAAI,EAAEC,OAAO,CAAE,GAAGZ,mEAAa,CAAE,UAAU,EAAEQ,QAAQ,EAAE,MAAO,CAAC;EAEvE,MAAMK,eAAe,GAAGF,IAAI,EAAEG,0CAA0C,GAAGH,IAAI,EAAEG,0CAA0C,GAAGV,sBAAsB;EAEpJ,oBACCD,sDAAA,CAACP,yEAA0B;IAC1BmB,IAAI,EAAC,aAAa;IAClBC,KAAK,EAAGf,mDAAE,CAAE,qCAAqC,EAAE,aAAc,CAAG;IAAAgB,QAAA,eAEpEd,sDAAA,CAACL,gEAAa;MACboB,KAAK,EAAGjB,mDAAE,CAAE,UAAU,EAAE,aAAc,CAAG;MACzCkB,KAAK,EAAGN,eAAiB;MACzBO,OAAO,EAAG,CACT;QAAEF,KAAK,EAAEjB,mDAAE,CAAE,UAAU,EAAE,0BAA2B,CAAC;QAAEkB,KAAK,EAAE;MAAE,CAAC,EACjE;QAAED,KAAK,EAAEjB,mDAAE,CAAE,SAAS,EAAE,0BAA2B,CAAC;QAAEkB,KAAK,EAAE;MAAM,CAAC,EACpE;QAAED,KAAK,EAAEjB,mDAAE,CAAE,OAAO,EAAE,0BAA2B,CAAC;QAAEkB,KAAK,EAAE;MAAM,CAAC,EAClE;QAAED,KAAK,EAAEjB,mDAAE,CAAE,QAAQ,EAAE,0BAA2B,CAAC;QAAEkB,KAAK,EAAE;MAAO,CAAC,EACpE;QAAED,KAAK,EAAEjB,mDAAE,CAAE,QAAQ,EAAE,0BAA2B,CAAC;QAAEkB,KAAK,EAAE;MAAO,CAAC,CAClE;MACHE,QAAQ,EAAKF,KAAK,IAAM;QACvBP,OAAO,CAAE;UAAE,GAAGD,IAAI;UAAEG,0CAA0C,EAAEK;QAAM,CAAE,CAAC;MAC1E,CAAG;MACHG,uBAAuB;IAAA,CACvB;EAAC,CACyB,CAAC;AAE/B,CAAC;AAEDzB,kEAAc,CAAE,mCAAmC,EAAE;EAAE0B,MAAM,EAAEhB;AAAS,CAAE,CAAC,C","sources":["webpack://activitypub-poll/external window \"ReactJSXRuntime\"","webpack://activitypub-poll/external window [\"wp\",\"components\"]","webpack://activitypub-poll/external window [\"wp\",\"coreData\"]","webpack://activitypub-poll/external window [\"wp\",\"data\"]","webpack://activitypub-poll/external window [\"wp\",\"editor\"]","webpack://activitypub-poll/external window [\"wp\",\"i18n\"]","webpack://activitypub-poll/external window [\"wp\",\"plugins\"]","webpack://activitypub-poll/webpack/bootstrap","webpack://activitypub-poll/webpack/runtime/compat get default export","webpack://activitypub-poll/webpack/runtime/define property getters","webpack://activitypub-poll/webpack/runtime/hasOwnProperty shorthand","webpack://activitypub-poll/webpack/runtime/make namespace object","webpack://activitypub-poll/./src/reminder/plugin.js"],"sourcesContent":["module.exports = window[\"ReactJSXRuntime\"];","module.exports = window[\"wp\"][\"components\"];","module.exports = window[\"wp\"][\"coreData\"];","module.exports = window[\"wp\"][\"data\"];","module.exports = window[\"wp\"][\"editor\"];","module.exports = window[\"wp\"][\"i18n\"];","module.exports = window[\"wp\"][\"plugins\"];","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { PluginDocumentSettingPanel } from '@wordpress/editor';\nimport { registerPlugin } from '@wordpress/plugins';\nimport { SelectControl } from '@wordpress/components';\nimport { useSelect } from '@wordpress/data';\nimport { useEntityProp } from '@wordpress/core-data';\nimport { __ } from '@wordpress/i18n';\n\nconst reminderTimeGapDefault = activityPubEventBridge.reminderTypeGap;\n\nconst Reminder = () => {\n\tconst postType = useSelect(\n\t\t( select ) => select( 'core/editor' ).getCurrentPostType(),\n\t\t[]\n\t);\n\tconst [ meta, setMeta ] = useEntityProp( 'postType', postType, 'meta' );\n\n\tconst reminderTimeGap = meta?.activitypub_event_bridge_reminder_time_gap ? meta?.activitypub_event_bridge_reminder_time_gap : reminderTimeGapDefault;\n\n\treturn (\n\t\t\n\t\t\t {\n\t\t\t\t\tsetMeta( { ...meta, activitypub_event_bridge_reminder_time_gap: value } );\n\t\t\t\t} }\n\t\t\t\t__nextHasNoMarginBottom\n\t\t\t/>\n\t\t\n\t);\n}\n\nregisterPlugin( 'activitypub-event-bridge-reminder', { render: Reminder } );"],"names":["PluginDocumentSettingPanel","registerPlugin","SelectControl","useSelect","useEntityProp","__","jsx","_jsx","reminderTimeGapDefault","activityPubEventBridge","reminderTypeGap","Reminder","postType","select","getCurrentPostType","meta","setMeta","reminderTimeGap","activitypub_event_bridge_reminder_time_gap","name","title","children","label","value","options","onChange","__nextHasNoMarginBottom","render"],"sourceRoot":""} \ No newline at end of file diff --git a/includes/activitypub/transformer/class-event.php b/includes/activitypub/transformer/class-event.php index 5fb09f4..6d40a21 100644 --- a/includes/activitypub/transformer/class-event.php +++ b/includes/activitypub/transformer/class-event.php @@ -11,6 +11,7 @@ namespace ActivityPub_Event_Bridge\Activitypub\Transformer; // Exit if accessed directly. defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore +use Activitypub\Activity\Activity; use Activitypub\Activity\Extended_Object\Event as Event_Object; use Activitypub\Activity\Extended_Object\Place; use Activitypub\Transformer\Post; @@ -148,7 +149,7 @@ abstract class Event extends Post { * * This is mandatory and must be implemented in the final event transformer class. */ - abstract protected function get_start_time(): string; + abstract public function get_start_time(): string; /** * Get the end time. @@ -376,4 +377,19 @@ abstract class Event extends Post { return $activitypub_object; } + + /** + * Creates an activity for announcing itself. + * + * @return Activity The Activity. + */ + public function to_announce_self_activity() { + $activity = new Activity(); + $activity->set_type( 'Announce' ); + + // Pre-fill the Activity with data (for example cc and to). + $activity->set_object( $this->get_id() ); + + return $activity; + } } diff --git a/includes/activitypub/transformer/class-gatherpress.php b/includes/activitypub/transformer/class-gatherpress.php index b67bc58..b0a5f7d 100644 --- a/includes/activitypub/transformer/class-gatherpress.php +++ b/includes/activitypub/transformer/class-gatherpress.php @@ -80,7 +80,7 @@ final class GatherPress extends Event { /** * Get the end time from the event object. */ - protected function get_start_time(): string { + public function get_start_time(): string { return $this->gp_event->get_datetime_start( 'Y-m-d\TH:i:s\Z' ); } diff --git a/includes/activitypub/transformer/class-the-events-calendar.php b/includes/activitypub/transformer/class-the-events-calendar.php index 9d76aaa..65e7f7d 100644 --- a/includes/activitypub/transformer/class-the-events-calendar.php +++ b/includes/activitypub/transformer/class-the-events-calendar.php @@ -83,7 +83,7 @@ final class The_Events_Calendar extends Event { /** * Get the end time from the event object. */ - protected function get_start_time(): string { + public function get_start_time(): string { $date = date_create( $this->tribe_event->start_date, wp_timezone() ); return \gmdate( 'Y-m-d\TH:i:s\Z', $date->getTimestamp() ); } diff --git a/includes/activitypub/transformer/class-vs-event-list.php b/includes/activitypub/transformer/class-vs-event-list.php index c476404..d528bea 100644 --- a/includes/activitypub/transformer/class-vs-event-list.php +++ b/includes/activitypub/transformer/class-vs-event-list.php @@ -58,7 +58,7 @@ final class VS_Event_List extends Event_Transformer { /** * Get the end time from the events metadata. */ - protected function get_start_time(): string { + public function get_start_time(): string { $start_time = get_post_meta( $this->wp_object->ID, 'event-start-date', true ); return \gmdate( 'Y-m-d\TH:i:s\Z', $start_time ); } diff --git a/includes/class-reminder.php b/includes/class-reminder.php new file mode 100644 index 0000000..e9b83df --- /dev/null +++ b/includes/class-reminder.php @@ -0,0 +1,176 @@ + true, + 'single' => true, + 'type' => 'integer', + 'sanitize_callback' => 'absint', + ) + ); + } + } + + /** + * Enqueue the block editor assets. + */ + public static function enqueue_editor_assets() { + // Check for our supported post types. + $current_screen = \get_current_screen(); + $event_post_types = Setup::get_instance()->get_active_event_plugins_post_types(); + if ( ! $current_screen || ! in_array( $current_screen->post_type, $event_post_types, true ) ) { + return; + } + $asset_data = include ACTIVITYPUB_EVENT_BRIDGE_PLUGIN_DIR . 'build/reminder/plugin.asset.php'; + $plugin_url = plugins_url( 'build/reminder/plugin.js', ACTIVITYPUB_EVENT_BRIDGE_PLUGIN_FILE ); + wp_enqueue_script( 'activitypub-event-bridge-reminder', $plugin_url, $asset_data['dependencies'], $asset_data['version'], true ); + + // Pass the the default site wide time gap option to the settings block on the events edit page. + wp_localize_script( + 'activitypub-event-bridge-reminder', + 'activityPubEventBridge', + array( + 'reminderTypeGap' => \get_option( 'activitypub_event_bridge_reminder_time_gap', 0 ), + ) + ); + } + + /** + * Schedule Activities. + * + * @param string $new_status New post status. + * @param string $old_status Old post status. + * @param WP_Post $post Post object. + */ + public static function maybe_schedule_event_post_announcement( $new_status, $old_status, $post ): void { + $reminder_time_gap = (int) get_post_meta( $post->ID, 'activitypub_event_bridge_reminder_time_gap', true ); + + if ( '' === $reminder_time_gap ) { + $reminder_time_gap = \get_option( 'activitypub_event_bridge_reminder_time_gap', 0 ); + } + + if ( 0 === $reminder_time_gap || ! is_int( $reminder_time_gap ) ) { + return; + } + + $post = get_post( $post ); + + if ( ! $post ) { + return; + } + + // Do not send activities if post is password protected. + if ( \post_password_required( $post ) ) { + return; + } + + // Only schedule an reminder for event post types. + if ( ! Setup::get_instance()->is_post_type_event_of_active_event_plugin( $post->post_type ) ) { + return; + } + + // Do not schedule a reminder if the event is not published. + if ( 'publish' !== $new_status ) { + return; + } + + // Get start time of the event. + $event_transformer = Transformer_Factory::get_transformer( $post ); + + if ( \is_wp_error( $event_transformer ) || ! $event_transformer instanceof Event_Transformer ) { + return; + } + + $start_time = $event_transformer->get_start_time(); + $start_datetime = new DateTime( $start_time ); + $start_timestamp = $start_datetime->getTimestamp(); + + // Get the time when the reminder of the event's start should be sent. + $schedule_time = $start_timestamp - $reminder_time_gap; + + if ( $schedule_time < \time() ) { + return; + } + + $hook = 'activitypub_event_bridge_send_event_reminder'; + $args = array( $post->ID ); + + \wp_clear_scheduled_hook( $hook, $args ); + \wp_schedule_single_event( $schedule_time, $hook, $args ); + } + + /** + * Send a reminder for an event post. + * + * This currently sends an Announce activity. + * + * @param int $post_id The WordPress post ID of the event post. + */ + public static function send_event_reminder( $post_id ): void { + $post = \get_post( $post_id ); + + $transformer = Transformer_Factory::get_transformer( $post ); + + if ( \is_wp_error( $transformer ) || ! $transformer instanceof Event_Transformer ) { + return; + } + + $user_id = $transformer->get_wp_user_id(); + + if ( is_user_disabled( $user_id ) ) { + return; + } + + $activity = $transformer->to_announce_self_activity( 'Announce' ); + + Activity_Dispatcher::send_activity_to_followers( $activity, $user_id, $post ); + } +} diff --git a/includes/class-settings.php b/includes/class-settings.php index 922531f..64deec4 100644 --- a/includes/class-settings.php +++ b/includes/class-settings.php @@ -44,7 +44,7 @@ class Settings { 'activitypub_event_bridge_default_event_category', array( 'type' => 'string', - 'description' => \__( 'Define your own custom post template', 'activitypub' ), + 'description' => \__( 'Default standardized federated event category.s', 'activitypub' ), 'show_in_rest' => true, 'default' => self::DEFAULT_EVENT_CATEGORY, 'sanitize_callback' => array( self::class, 'sanitize_mapped_event_category' ), @@ -56,11 +56,22 @@ class Settings { 'activitypub_event_bridge_event_category_mappings', array( 'type' => 'array', - 'description' => \__( 'Define your own custom post template', 'activitypub' ), + 'description' => \__( 'Category mappings to standardized federated event categories.', 'activitypub' ), 'default' => array(), 'sanitize_callback' => array( self::class, 'sanitize_event_category_mappings' ), ) ); + + \register_setting( + 'activitypub-event-bridge', + 'activitypub_event_bridge_reminder_time_gap', + array( + 'type' => 'array', + 'description' => \__( 'Time gap in seconds when a reminder is triggered that the event is about to start.', 'activitypub' ), + 'default' => array(), + 'sanitize_callback' => 'absint', + ) + ); } /** diff --git a/includes/class-setup.php b/includes/class-setup.php index bafe050..47712f9 100644 --- a/includes/class-setup.php +++ b/includes/class-setup.php @@ -18,6 +18,7 @@ defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore use ActivityPub_Event_Bridge\Admin\Event_Plugin_Admin_Notices; use ActivityPub_Event_Bridge\Admin\General_Admin_Notices; use ActivityPub_Event_Bridge\Admin\Settings_Page; +use ActivityPub_Event_Bridge\Reminder; use ActivityPub_Event_Bridge\Plugins\Event_Plugin; require_once ABSPATH . 'wp-admin/includes/plugin.php'; @@ -118,6 +119,38 @@ class Setup { return $this->active_event_plugins; } + /** + * Getter function for the active event plugins post types. + * + * @return array List of event post types of the active event plugins. + */ + public function get_active_event_plugins_post_types() { + $post_types = array(); + foreach ( $this->active_event_plugins as $event_plugin ) { + $post_types[] = $event_plugin->get_post_type(); + } + + return $post_types; + } + + /** + * Function to check whether a post type is an event post type of an active event plugin. + * + * @param string $post_type The post type. + * + * @return bool True if it is an event post type. + */ + public function is_post_type_event_of_active_event_plugin( $post_type ) { + foreach ( $this->active_event_plugins as $event_plugin ) { + if ( $post_type === $event_plugin->get_post_type() ) { + return true; + } + } + + return false; + } + + /** * Holds all the classes for the supported event plugins. * @@ -185,6 +218,8 @@ class Setup { return; } + add_action( 'init', array( Reminder::class, 'init' ) ); + add_filter( 'activitypub_transformer', array( $this, 'register_activitypub_event_transformer' ), 10, 3 ); } diff --git a/includes/plugins/class-event-plugin.php b/includes/plugins/class-event-plugin.php index f70cfa0..386fa1b 100644 --- a/includes/plugins/class-event-plugin.php +++ b/includes/plugins/class-event-plugin.php @@ -69,6 +69,8 @@ abstract class Event_Plugin { /** * Returns the Activitypub transformer for the event plugins event post type. + * + * @return string */ public static function get_activitypub_event_transformer_class(): string { return str_replace( 'Plugins', 'Activitypub\Transformer', static::class ); diff --git a/package.json b/package.json new file mode 100755 index 0000000..304245e --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ + +{ + "name": "activitypub-poll", + "version": "0.1.0", + "author": { + "name": "André Menrath", + "web": "https://graz.social/@linos" + }, + "scripts": { + "dev": "wp-scripts start", + "build": "wp-scripts build", + "readme": "grunt wp_readme_to_markdown" + }, + "devDependencies": { + "@wordpress/scripts": "^30.0.2", + "grunt-wp-readme-to-markdown": "^2.0.1", + "classnames": "^2.3.2" + } +} diff --git a/src/reminder/block.json b/src/reminder/block.json new file mode 100644 index 0000000..77f8e07 --- /dev/null +++ b/src/reminder/block.json @@ -0,0 +1,9 @@ +{ + "name": "reminder", + "title": "Reminder Plugin: not a block, but block.json is very useful.", + "category": "widgets", + "icon": "admin-comments", + "keywords": [ + ], + "editorScript": "file:./plugin.js" +} \ No newline at end of file diff --git a/src/reminder/plugin.js b/src/reminder/plugin.js new file mode 100644 index 0000000..4454d28 --- /dev/null +++ b/src/reminder/plugin.js @@ -0,0 +1,43 @@ +import { PluginDocumentSettingPanel } from '@wordpress/editor'; +import { registerPlugin } from '@wordpress/plugins'; +import { SelectControl } from '@wordpress/components'; +import { useSelect } from '@wordpress/data'; +import { useEntityProp } from '@wordpress/core-data'; +import { __ } from '@wordpress/i18n'; + +const reminderTimeGapDefault = activityPubEventBridge.reminderTypeGap; + +const Reminder = () => { + const postType = useSelect( + ( select ) => select( 'core/editor' ).getCurrentPostType(), + [] + ); + const [ meta, setMeta ] = useEntityProp( 'postType', postType, 'meta' ); + + const reminderTimeGap = meta?.activitypub_event_bridge_reminder_time_gap ? meta?.activitypub_event_bridge_reminder_time_gap : reminderTimeGapDefault; + + return ( + + { + setMeta( { ...meta, activitypub_event_bridge_reminder_time_gap: value } ); + } } + __nextHasNoMarginBottom + /> + + ); +} + +registerPlugin( 'activitypub-event-bridge-reminder', { render: Reminder } ); \ No newline at end of file diff --git a/templates/settings.php b/templates/settings.php index 8eb382e..3f4366b 100644 --- a/templates/settings.php +++ b/templates/settings.php @@ -29,6 +29,15 @@ require_once ACTIVITYPUB_EVENT_BRIDGE_PLUGIN_DIR . '/includes/event-categories.p $selected_default_event_category = \get_option( 'activitypub_event_bridge_default_event_category', 'MEETING' ); $current_category_mapping = \get_option( 'activitypub_event_bridge_event_category_mappings', array() ); +$reminder_time_gap = \get_option( 'activitypub_event_bridge_reminder_time_gap', 0 ); + +$reminder_time_gap_choices = array( + 0 => __( 'Disabled', 'activitypub-event-bridge' ), + 21600 => __( '6 hours', 'activitypub-event-bridge' ), + 86400 => __( '1 day', 'activitypub-event-bridge' ), + 259200 => __( '3 days', 'activitypub-event-bridge' ), + 604800 => __( '1 week', 'activitypub-event-bridge' ), +) ?>
@@ -98,6 +107,17 @@ $current_category_mapping = \get_option( 'activitypub_event_bridge_event_
+
+

+

+ +
-- 2.39.5 From 63bf5d26cedd80b2087bf7fe93323b44b54f288f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Menrath?= Date: Fri, 11 Oct 2024 19:05:20 +0200 Subject: [PATCH 2/7] add tests --- .forgejo/workflows/phpunit.yml | 5 + composer.json | 3 +- includes/class-reminder.php | 25 +++- includes/class-settings.php | 2 +- templates/settings.php | 10 +- tests/bootstrap.php | 6 +- tests/test-class-plugin-events-manger.php | 2 +- tests/test-class-plugin-gatherpress.php | 4 +- tests/test-class-reminder.php | 168 ++++++++++++++++++++++ 9 files changed, 208 insertions(+), 17 deletions(-) create mode 100644 tests/test-class-reminder.php diff --git a/.forgejo/workflows/phpunit.yml b/.forgejo/workflows/phpunit.yml index b67d8e9..125474c 100644 --- a/.forgejo/workflows/phpunit.yml +++ b/.forgejo/workflows/phpunit.yml @@ -74,6 +74,11 @@ jobs: if: steps.cache-wordpress.outputs.cache-hit != 'false' run: bash bin/install-wp-tests.sh wordpress_test root root 127.0.0.1 6.6 false true true true + - name: Run Feature tests of the ActivityPub Event Bridge + run: cd /workspace/Event-Federation/wordpress-activitypub-event-bridge/ && ./vendor/bin/phpunit --filter=reminder + env: + PHP_VERSION: ${{ matrix.php-version }} + - name: Run Integration tests for The Events Calendar run: cd /workspace/Event-Federation/wordpress-activitypub-event-bridge/ && ./vendor/bin/phpunit --filter=the_events_calendar env: diff --git a/composer.json b/composer.json index 1b3bb36..30c3b01 100644 --- a/composer.json +++ b/composer.json @@ -55,8 +55,9 @@ ], "test-debug": [ "@prepare-test", - "@test-wp-event-manager" + "@test-features" ], + "test-features": "phpunit --filter=reminder", "test-vs-event-list": "phpunit --filter=vs_event_list", "test-the-events-calendar": "phpunit --filter=the_events_calendar", "test-gatherpress": "phpunit --filter=gatherpress", diff --git a/includes/class-reminder.php b/includes/class-reminder.php index e9b83df..eb2e8a1 100644 --- a/includes/class-reminder.php +++ b/includes/class-reminder.php @@ -31,7 +31,9 @@ class Reminder { */ public static function init() { // Post transitions. - \add_action( 'transition_post_status', array( self::class, 'maybe_schedule_event_post_announcement' ), 33, 3 ); + \add_action( 'transition_post_status', array( self::class, 'maybe_schedule_event_reminder' ), 33, 3 ); + + \add_action( 'delete_post', array( self::class, 'unschedule_event_reminder' ), 33, 1 ); // Send an event reminder. \add_action( 'activitypub_event_bridge_send_event_reminder', array( self::class, 'send_event_reminder' ), 10, 1 ); @@ -90,10 +92,10 @@ class Reminder { * @param string $old_status Old post status. * @param WP_Post $post Post object. */ - public static function maybe_schedule_event_post_announcement( $new_status, $old_status, $post ): void { + public static function maybe_schedule_event_reminder( $new_status, $old_status, $post ): void { $reminder_time_gap = (int) get_post_meta( $post->ID, 'activitypub_event_bridge_reminder_time_gap', true ); - if ( '' === $reminder_time_gap ) { + if ( ! $reminder_time_gap ) { $reminder_time_gap = \get_option( 'activitypub_event_bridge_reminder_time_gap', 0 ); } @@ -117,6 +119,10 @@ class Reminder { return; } + if ( 'trash' === $new_status ) { + self::unschedule_event_reminder( $post->ID ); + } + // Do not schedule a reminder if the event is not published. if ( 'publish' !== $new_status ) { return; @@ -147,6 +153,17 @@ class Reminder { \wp_schedule_single_event( $schedule_time, $hook, $args ); } + /** + * Unschedule the event reminder. + * + * @param int $post_id The WordPress post ID of the event post. + */ + public static function unschedule_event_reminder( $post_id ): void { + $hook = 'activitypub_event_bridge_send_event_reminder'; + $args = array( $post_id ); + \wp_clear_scheduled_hook( $hook, $args ); + } + /** * Send a reminder for an event post. * @@ -165,7 +182,7 @@ class Reminder { $user_id = $transformer->get_wp_user_id(); - if ( is_user_disabled( $user_id ) ) { + if ( $user_id > 0 && is_user_disabled( $user_id ) ) { return; } diff --git a/includes/class-settings.php b/includes/class-settings.php index 64deec4..7d5bb8d 100644 --- a/includes/class-settings.php +++ b/includes/class-settings.php @@ -68,7 +68,7 @@ class Settings { array( 'type' => 'array', 'description' => \__( 'Time gap in seconds when a reminder is triggered that the event is about to start.', 'activitypub' ), - 'default' => array(), + 'default' => 0, // Zero leads to this feature being deactivated. 'sanitize_callback' => 'absint', ) ); diff --git a/templates/settings.php b/templates/settings.php index 3f4366b..c01d762 100644 --- a/templates/settings.php +++ b/templates/settings.php @@ -32,11 +32,11 @@ $current_category_mapping = \get_option( 'activitypub_event_bridge_event_ $reminder_time_gap = \get_option( 'activitypub_event_bridge_reminder_time_gap', 0 ); $reminder_time_gap_choices = array( - 0 => __( 'Disabled', 'activitypub-event-bridge' ), - 21600 => __( '6 hours', 'activitypub-event-bridge' ), - 86400 => __( '1 day', 'activitypub-event-bridge' ), - 259200 => __( '3 days', 'activitypub-event-bridge' ), - 604800 => __( '1 week', 'activitypub-event-bridge' ), + 0 => __( 'Disabled', 'activitypub-event-bridge' ), + HOUR_IN_SECONDS * 6 => __( '6 hours', 'activitypub-event-bridge' ), + DAY_IN_SECONDS => __( '1 day', 'activitypub-event-bridge' ), + DAY_IN_SECONDS * 3 => __( '3 days', 'activitypub-event-bridge' ), + WEEK_IN_SECONDS => __( '1 week', 'activitypub-event-bridge' ), ) ?> diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 359543a..560ce4a 100755 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -46,9 +46,6 @@ function _manually_load_plugin() { $plugin_file = null; // See if we want to run integration tests for a specific event-plugin. switch ( $activitypub_event_extension_integration_filter ) { - case 'the_events_calendar': - $plugin_file = 'the-events-calendar/the-events-calendar.php'; - break; case 'vs_event_list': $plugin_file = 'very-simple-event-list/vsel.php'; break; @@ -61,6 +58,9 @@ function _manually_load_plugin() { case 'wp_event_manager': $plugin_file = 'wp-event-manager/wp-event-manager.php'; break; + default: + // By default we test other stuff using The Events Calendar. + $plugin_file = 'the-events-calendar/the-events-calendar.php'; } if ( $plugin_file ) { diff --git a/tests/test-class-plugin-events-manger.php b/tests/test-class-plugin-events-manger.php index 665b3c8..c27f9c5 100644 --- a/tests/test-class-plugin-events-manger.php +++ b/tests/test-class-plugin-events-manger.php @@ -16,7 +16,7 @@ class Test_Events_Manager extends WP_UnitTestCase { parent::set_up(); if ( ! class_exists( 'EM_Events' ) ) { - self::markTestSkipped( 'VS Event List plugin is not active.' ); + self::markTestSkipped( 'Events Manager plugin is not active.' ); } // For tests allow every user to create new events. diff --git a/tests/test-class-plugin-gatherpress.php b/tests/test-class-plugin-gatherpress.php index 1fffa4b..377e368 100644 --- a/tests/test-class-plugin-gatherpress.php +++ b/tests/test-class-plugin-gatherpress.php @@ -1,12 +1,12 @@ 'Minimal Venue', + 'status' => 'publish', + ); + + public const MOCKUP_EVENT = array( + 'title' => 'My Event', + 'content' => 'Come to my event!', + 'start_date' => '+10 days 15:00:00', + 'duration' => HOUR_IN_SECONDS, + 'status' => 'publish', + ); + + /** + * Override the setup function, so that tests don't run if the Events Calendar is not active. + */ + public function set_up() { + parent::set_up(); + + if ( ! class_exists( '\Tribe__Events__Main' ) ) { + self::markTestSkipped( 'The Events Calendar is not active.' ); + } + + // For tests allow every user to create new events. + update_option( 'dbem_events_anonymous_submissions', true ); + + // Make sure that ActivityPub support is enabled for Events Manager. + $aec = \ActivityPub_Event_Bridge\Setup::get_instance(); + $aec->activate_activitypub_support_for_active_event_plugins(); + + // Delete all posts afterwards. + _delete_all_posts(); + } + + public function test_event_reminder_not_being_scheduled_by_default() { + // Create a The Events Calendar Event. + $wp_object = tribe_events() + ->set_args( self::MOCKUP_EVENT ) + ->create(); + + $scheduled_event = \wp_get_scheduled_event( 'activitypub_event_bridge_send_event_reminder', array( $wp_object->ID ) ); + + $this->assertEquals( false, $scheduled_event ); + } + + public function test_event_reminder_scheduled_with_site_wide_option() { + \update_option( 'activitypub_event_bridge_reminder_time_gap', DAY_IN_SECONDS ); + // Create a The Events Calendar Event. + $wp_object = tribe_events() + ->set_args( self::MOCKUP_EVENT ) + ->create(); + + $scheduled_event = \wp_get_scheduled_event( 'activitypub_event_bridge_send_event_reminder', array( $wp_object->ID ) ); + + $this->assertNotEquals( false, $scheduled_event ); + $this->assertEquals( strtotime( self::MOCKUP_EVENT['start_date'] ) - DAY_IN_SECONDS, $scheduled_event->timestamp ); + $this->assertEquals( false, $scheduled_event->schedule ); + $this->assertEquals( 'activitypub_event_bridge_send_event_reminder', $scheduled_event->hook ); + } + + public function test_event_reminder_scheduled_with_per_event_override() { + \update_option( 'activitypub_event_bridge_reminder_time_gap', DAY_IN_SECONDS ); + + // Create a The Events Calendar Event. + $wp_object = tribe_events() + ->set_args( + array_merge( + self::MOCKUP_EVENT, + array( 'activitypub_event_bridge_reminder_time_gap' => DAY_IN_SECONDS * 3 ), + ) + ) + ->create(); + + $scheduled_event = \wp_get_scheduled_event( 'activitypub_event_bridge_send_event_reminder', array( $wp_object->ID ) ); + + $this->assertNotEquals( false, $scheduled_event ); + $this->assertEquals( strtotime( self::MOCKUP_EVENT['start_date'] ) - DAY_IN_SECONDS * 3, $scheduled_event->timestamp ); + $this->assertEquals( false, $scheduled_event->schedule ); + $this->assertEquals( 'activitypub_event_bridge_send_event_reminder', $scheduled_event->hook ); + + // Now update the option once more to see if the schedule got updated too. + $post_id = array_key_first( + tribe_events( $wp_object->ID ) + ->set_args( + array( 'activitypub_event_bridge_reminder_time_gap' => HOUR_IN_SECONDS ), + ) + ->save() + ); + + $scheduled_event = \wp_get_scheduled_event( 'activitypub_event_bridge_send_event_reminder', array( $post_id ) ); + $this->assertNotEquals( false, $scheduled_event ); + $this->assertEquals( strtotime( self::MOCKUP_EVENT['start_date'] ) - HOUR_IN_SECONDS, $scheduled_event->timestamp ); + } + + public function test_event_reminder_deleted_event() { + \update_option( 'activitypub_event_bridge_reminder_time_gap', DAY_IN_SECONDS ); + + // Create a The Events Calendar Event. + $wp_object = tribe_events() + ->set_args( + array_merge( + self::MOCKUP_EVENT, + array( 'activitypub_event_bridge_reminder_time_gap' => DAY_IN_SECONDS * 3 ), + ) + ) + ->create(); + + $scheduled_event = \wp_get_scheduled_event( 'activitypub_event_bridge_send_event_reminder', array( $wp_object->ID ) ); + + $this->assertNotEquals( false, $scheduled_event ); + $this->assertEquals( strtotime( self::MOCKUP_EVENT['start_date'] ) - DAY_IN_SECONDS * 3, $scheduled_event->timestamp ); + $this->assertEquals( false, $scheduled_event->schedule ); + $this->assertEquals( 'activitypub_event_bridge_send_event_reminder', $scheduled_event->hook ); + + // Now delete the event. + tribe_events( $wp_object->ID )->delete(); + + $scheduled_event = \wp_get_scheduled_event( 'activitypub_event_bridge_send_event_reminder', array( $wp_object->ID ) ); + $this->assertEquals( false, $scheduled_event ); + } + + public function test_event_reminder_event_moved_to_trash() { + \update_option( 'activitypub_event_bridge_reminder_time_gap', DAY_IN_SECONDS ); + + // Create a The Events Calendar Event. + $wp_object = tribe_events() + ->set_args( + array_merge( + self::MOCKUP_EVENT, + array( 'activitypub_event_bridge_reminder_time_gap' => DAY_IN_SECONDS * 3 ), + ) + ) + ->create(); + + $scheduled_event = \wp_get_scheduled_event( 'activitypub_event_bridge_send_event_reminder', array( $wp_object->ID ) ); + + $this->assertNotEquals( false, $scheduled_event ); + $this->assertEquals( strtotime( self::MOCKUP_EVENT['start_date'] ) - DAY_IN_SECONDS * 3, $scheduled_event->timestamp ); + $this->assertEquals( false, $scheduled_event->schedule ); + $this->assertEquals( 'activitypub_event_bridge_send_event_reminder', $scheduled_event->hook ); + + // Now move the event to the trash. + $post_id = array_key_first( + tribe_events( $wp_object->ID ) + ->set_args( + array( 'post_status' => 'trash' ), + ) + ->save() + ); + + $scheduled_event = \wp_get_scheduled_event( 'activitypub_event_bridge_send_event_reminder', array( $wp_object->ID ) ); + $this->assertEquals( false, $scheduled_event ); + } +} -- 2.39.5 From 2d902806ee44c00503589c381c927898ff318dbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Menrath?= Date: Fri, 11 Oct 2024 19:07:07 +0200 Subject: [PATCH 3/7] phpcs --- build/reminder/plugin.asset.php | 5 ++++- tests/test-class-reminder.php | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/build/reminder/plugin.asset.php b/build/reminder/plugin.asset.php index 9b2089b..e5882df 100644 --- a/build/reminder/plugin.asset.php +++ b/build/reminder/plugin.asset.php @@ -1 +1,4 @@ - array('react-jsx-runtime', 'wp-components', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-i18n', 'wp-plugins'), 'version' => 'f38cf2ab9e8621193407'); + array( 'react-jsx-runtime', 'wp-components', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-i18n', 'wp-plugins' ), + 'version' => 'f38cf2ab9e8621193407', +); diff --git a/tests/test-class-reminder.php b/tests/test-class-reminder.php index bba387e..22a17a4 100644 --- a/tests/test-class-reminder.php +++ b/tests/test-class-reminder.php @@ -103,7 +103,7 @@ class Test_Reminder extends WP_UnitTestCase { $scheduled_event = \wp_get_scheduled_event( 'activitypub_event_bridge_send_event_reminder', array( $post_id ) ); $this->assertNotEquals( false, $scheduled_event ); - $this->assertEquals( strtotime( self::MOCKUP_EVENT['start_date'] ) - HOUR_IN_SECONDS, $scheduled_event->timestamp ); + $this->assertEquals( strtotime( self::MOCKUP_EVENT['start_date'] ) - HOUR_IN_SECONDS, $scheduled_event->timestamp ); } public function test_event_reminder_deleted_event() { -- 2.39.5 From d5dcfd5e0cd0cbfa8f47f623a91008151bb12aa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Menrath?= Date: Fri, 11 Oct 2024 19:20:47 +0200 Subject: [PATCH 4/7] exclude js from phpcs --- phpcs.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/phpcs.xml b/phpcs.xml index cf1bc1b..be78f9a 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -26,6 +26,9 @@ /node_modules/* + + *.js + *.min.js -- 2.39.5 From 4263233e10e7975f96c4661e0b6cf4882b4fcad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Menrath?= Date: Fri, 11 Oct 2024 19:27:12 +0200 Subject: [PATCH 5/7] fix phpcs --- phpcs.xml | 3 +++ tests/test-class-reminder.php | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/phpcs.xml b/phpcs.xml index be78f9a..6c4b250 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -29,6 +29,9 @@ *.js + + /build/*/*asset.php + *.min.js diff --git a/tests/test-class-reminder.php b/tests/test-class-reminder.php index 22a17a4..689f5f6 100644 --- a/tests/test-class-reminder.php +++ b/tests/test-class-reminder.php @@ -46,6 +46,9 @@ class Test_Reminder extends WP_UnitTestCase { _delete_all_posts(); } + /** + * Test that with the default reminder setting (time-gap is zero) no reminder event is scheduled. + */ public function test_event_reminder_not_being_scheduled_by_default() { // Create a The Events Calendar Event. $wp_object = tribe_events() @@ -57,6 +60,9 @@ class Test_Reminder extends WP_UnitTestCase { $this->assertEquals( false, $scheduled_event ); } + /** + * Test that with a side-wide option the reminder is scheduled. + */ public function test_event_reminder_scheduled_with_site_wide_option() { \update_option( 'activitypub_event_bridge_reminder_time_gap', DAY_IN_SECONDS ); // Create a The Events Calendar Event. @@ -72,6 +78,9 @@ class Test_Reminder extends WP_UnitTestCase { $this->assertEquals( 'activitypub_event_bridge_send_event_reminder', $scheduled_event->hook ); } + /** + * Test that a specific event can override the side-wide reminder default. + */ public function test_event_reminder_scheduled_with_per_event_override() { \update_option( 'activitypub_event_bridge_reminder_time_gap', DAY_IN_SECONDS ); @@ -106,6 +115,9 @@ class Test_Reminder extends WP_UnitTestCase { $this->assertEquals( strtotime( self::MOCKUP_EVENT['start_date'] ) - HOUR_IN_SECONDS, $scheduled_event->timestamp ); } + /** + * Test that the scheduled reminder is removed when the event is deleted. + */ public function test_event_reminder_deleted_event() { \update_option( 'activitypub_event_bridge_reminder_time_gap', DAY_IN_SECONDS ); @@ -133,6 +145,9 @@ class Test_Reminder extends WP_UnitTestCase { $this->assertEquals( false, $scheduled_event ); } + /** + * Test that the scheduled reminder is removed when the event is moved to trash. + */ public function test_event_reminder_event_moved_to_trash() { \update_option( 'activitypub_event_bridge_reminder_time_gap', DAY_IN_SECONDS ); -- 2.39.5 From d7a5b140ec040fb9156bd447d4fbb9083f29bcb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Menrath?= Date: Sat, 12 Oct 2024 09:42:21 +0200 Subject: [PATCH 6/7] use builds not dev js --- build/reminder/plugin.asset.php | 5 +- build/reminder/plugin.js | 216 +------------------------------- build/reminder/plugin.js.map | 1 - 3 files changed, 2 insertions(+), 220 deletions(-) delete mode 100644 build/reminder/plugin.js.map diff --git a/build/reminder/plugin.asset.php b/build/reminder/plugin.asset.php index e5882df..169666b 100644 --- a/build/reminder/plugin.asset.php +++ b/build/reminder/plugin.asset.php @@ -1,4 +1 @@ - array( 'react-jsx-runtime', 'wp-components', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-i18n', 'wp-plugins' ), - 'version' => 'f38cf2ab9e8621193407', -); + array('react-jsx-runtime', 'wp-components', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-i18n', 'wp-plugins'), 'version' => 'd491284dfb7e5078a777'); diff --git a/build/reminder/plugin.js b/build/reminder/plugin.js index 99955d4..967d07b 100644 --- a/build/reminder/plugin.js +++ b/build/reminder/plugin.js @@ -1,215 +1 @@ -/******/ (() => { // webpackBootstrap -/******/ "use strict"; -/******/ var __webpack_modules__ = ({ - -/***/ "react/jsx-runtime": -/*!**********************************!*\ - !*** external "ReactJSXRuntime" ***! - \**********************************/ -/***/ ((module) => { - -module.exports = window["ReactJSXRuntime"]; - -/***/ }), - -/***/ "@wordpress/components": -/*!************************************!*\ - !*** external ["wp","components"] ***! - \************************************/ -/***/ ((module) => { - -module.exports = window["wp"]["components"]; - -/***/ }), - -/***/ "@wordpress/core-data": -/*!**********************************!*\ - !*** external ["wp","coreData"] ***! - \**********************************/ -/***/ ((module) => { - -module.exports = window["wp"]["coreData"]; - -/***/ }), - -/***/ "@wordpress/data": -/*!******************************!*\ - !*** external ["wp","data"] ***! - \******************************/ -/***/ ((module) => { - -module.exports = window["wp"]["data"]; - -/***/ }), - -/***/ "@wordpress/editor": -/*!********************************!*\ - !*** external ["wp","editor"] ***! - \********************************/ -/***/ ((module) => { - -module.exports = window["wp"]["editor"]; - -/***/ }), - -/***/ "@wordpress/i18n": -/*!******************************!*\ - !*** external ["wp","i18n"] ***! - \******************************/ -/***/ ((module) => { - -module.exports = window["wp"]["i18n"]; - -/***/ }), - -/***/ "@wordpress/plugins": -/*!*********************************!*\ - !*** external ["wp","plugins"] ***! - \*********************************/ -/***/ ((module) => { - -module.exports = window["wp"]["plugins"]; - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/compat get default export */ -/******/ (() => { -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __webpack_require__.n = (module) => { -/******/ var getter = module && module.__esModule ? -/******/ () => (module['default']) : -/******/ () => (module); -/******/ __webpack_require__.d(getter, { a: getter }); -/******/ return getter; -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __webpack_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } -/******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __webpack_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); -/******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/************************************************************************/ -var __webpack_exports__ = {}; -// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. -(() => { -/*!********************************!*\ - !*** ./src/reminder/plugin.js ***! - \********************************/ -__webpack_require__.r(__webpack_exports__); -/* harmony import */ var _wordpress_editor__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/editor */ "@wordpress/editor"); -/* harmony import */ var _wordpress_editor__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_editor__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var _wordpress_plugins__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/plugins */ "@wordpress/plugins"); -/* harmony import */ var _wordpress_plugins__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_plugins__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @wordpress/components */ "@wordpress/components"); -/* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_wordpress_components__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _wordpress_data__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @wordpress/data */ "@wordpress/data"); -/* harmony import */ var _wordpress_data__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_wordpress_data__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _wordpress_core_data__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @wordpress/core-data */ "@wordpress/core-data"); -/* harmony import */ var _wordpress_core_data__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_wordpress_core_data__WEBPACK_IMPORTED_MODULE_4__); -/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n"); -/* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__); -/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! react/jsx-runtime */ "react/jsx-runtime"); -/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__); - - - - - - - -const reminderTimeGapDefault = activityPubEventBridge.reminderTypeGap; -const Reminder = () => { - const postType = (0,_wordpress_data__WEBPACK_IMPORTED_MODULE_3__.useSelect)(select => select('core/editor').getCurrentPostType(), []); - const [meta, setMeta] = (0,_wordpress_core_data__WEBPACK_IMPORTED_MODULE_4__.useEntityProp)('postType', postType, 'meta'); - const reminderTimeGap = meta?.activitypub_event_bridge_reminder_time_gap ? meta?.activitypub_event_bridge_reminder_time_gap : reminderTimeGapDefault; - return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(_wordpress_editor__WEBPACK_IMPORTED_MODULE_0__.PluginDocumentSettingPanel, { - name: "activitypub", - title: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('Send reminder before event\'s start', 'activitypub'), - children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(_wordpress_components__WEBPACK_IMPORTED_MODULE_2__.SelectControl, { - label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('Time gap', 'activitypub'), - value: reminderTimeGap, - options: [{ - label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('Disabled', 'activitypub-event-bridge'), - value: 0 - }, { - label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('6 hours', 'activitypub-event-bridge'), - value: 21600 - }, { - label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('1 day', 'activitypub-event-bridge'), - value: 86400 - }, { - label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('3 days', 'activitypub-event-bridge'), - value: 259200 - }, { - label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_5__.__)('1 week', 'activitypub-event-bridge'), - value: 604800 - }], - onChange: value => { - setMeta({ - ...meta, - activitypub_event_bridge_reminder_time_gap: value - }); - }, - __nextHasNoMarginBottom: true - }) - }); -}; -(0,_wordpress_plugins__WEBPACK_IMPORTED_MODULE_1__.registerPlugin)('activitypub-event-bridge-reminder', { - render: Reminder -}); -})(); - -/******/ })() -; -//# sourceMappingURL=plugin.js.map \ No newline at end of file +(()=>{"use strict";const e=window.wp.editor,t=window.wp.plugins,i=window.wp.components,n=window.wp.data,a=window.wp.coreData,r=window.wp.i18n,d=window.ReactJSXRuntime,p=activityPubEventBridge.reminderTypeGap;(0,t.registerPlugin)("activitypub-event-bridge-reminder",{render:()=>{const t=(0,n.useSelect)((e=>e("core/editor").getCurrentPostType()),[]),[_,b]=(0,a.useEntityProp)("postType",t,"meta"),u=_?.activitypub_event_bridge_reminder_time_gap?_?.activitypub_event_bridge_reminder_time_gap:p;return(0,d.jsx)(e.PluginDocumentSettingPanel,{name:"activitypub",title:(0,r.__)("Send reminder before event's start","activitypub"),children:(0,d.jsx)(i.SelectControl,{label:(0,r.__)("Time gap","activitypub"),value:u,options:[{label:(0,r.__)("Disabled","activitypub-event-bridge"),value:0},{label:(0,r.__)("6 hours","activitypub-event-bridge"),value:21600},{label:(0,r.__)("1 day","activitypub-event-bridge"),value:86400},{label:(0,r.__)("3 days","activitypub-event-bridge"),value:259200},{label:(0,r.__)("1 week","activitypub-event-bridge"),value:604800}],onChange:e=>{b({..._,activitypub_event_bridge_reminder_time_gap:e})},__nextHasNoMarginBottom:!0})})}})})(); \ No newline at end of file diff --git a/build/reminder/plugin.js.map b/build/reminder/plugin.js.map deleted file mode 100644 index 2eaaa1c..0000000 --- a/build/reminder/plugin.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"reminder/plugin.js","mappings":";;;;;;;;;;AAAA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;;;;;ACAA;;;;;;UCAA;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;UAEA;UACA;;UAEA;UACA;UACA;;;;;WCtBA;WACA;WACA;WACA;WACA;WACA,iCAAiC,WAAW;WAC5C;WACA;;;;;WCPA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA;;;;;WCPA;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D;;;;;;;;;;;;;;;;;;;;;;;;;ACN+D;AACX;AACE;AACV;AACS;AAChB;AAAA;AAErC,MAAMQ,sBAAsB,GAAGC,sBAAsB,CAACC,eAAe;AAErE,MAAMC,QAAQ,GAAGA,CAAA,KAAM;EACtB,MAAMC,QAAQ,GAAGT,0DAAS,CACvBU,MAAM,IAAMA,MAAM,CAAE,aAAc,CAAC,CAACC,kBAAkB,CAAC,CAAC,EAC1D,EACD,CAAC;EACD,MAAM,CAAEC,IAAI,EAAEC,OAAO,CAAE,GAAGZ,mEAAa,CAAE,UAAU,EAAEQ,QAAQ,EAAE,MAAO,CAAC;EAEvE,MAAMK,eAAe,GAAGF,IAAI,EAAEG,0CAA0C,GAAGH,IAAI,EAAEG,0CAA0C,GAAGV,sBAAsB;EAEpJ,oBACCD,sDAAA,CAACP,yEAA0B;IAC1BmB,IAAI,EAAC,aAAa;IAClBC,KAAK,EAAGf,mDAAE,CAAE,qCAAqC,EAAE,aAAc,CAAG;IAAAgB,QAAA,eAEpEd,sDAAA,CAACL,gEAAa;MACboB,KAAK,EAAGjB,mDAAE,CAAE,UAAU,EAAE,aAAc,CAAG;MACzCkB,KAAK,EAAGN,eAAiB;MACzBO,OAAO,EAAG,CACT;QAAEF,KAAK,EAAEjB,mDAAE,CAAE,UAAU,EAAE,0BAA2B,CAAC;QAAEkB,KAAK,EAAE;MAAE,CAAC,EACjE;QAAED,KAAK,EAAEjB,mDAAE,CAAE,SAAS,EAAE,0BAA2B,CAAC;QAAEkB,KAAK,EAAE;MAAM,CAAC,EACpE;QAAED,KAAK,EAAEjB,mDAAE,CAAE,OAAO,EAAE,0BAA2B,CAAC;QAAEkB,KAAK,EAAE;MAAM,CAAC,EAClE;QAAED,KAAK,EAAEjB,mDAAE,CAAE,QAAQ,EAAE,0BAA2B,CAAC;QAAEkB,KAAK,EAAE;MAAO,CAAC,EACpE;QAAED,KAAK,EAAEjB,mDAAE,CAAE,QAAQ,EAAE,0BAA2B,CAAC;QAAEkB,KAAK,EAAE;MAAO,CAAC,CAClE;MACHE,QAAQ,EAAKF,KAAK,IAAM;QACvBP,OAAO,CAAE;UAAE,GAAGD,IAAI;UAAEG,0CAA0C,EAAEK;QAAM,CAAE,CAAC;MAC1E,CAAG;MACHG,uBAAuB;IAAA,CACvB;EAAC,CACyB,CAAC;AAE/B,CAAC;AAEDzB,kEAAc,CAAE,mCAAmC,EAAE;EAAE0B,MAAM,EAAEhB;AAAS,CAAE,CAAC,C","sources":["webpack://activitypub-poll/external window \"ReactJSXRuntime\"","webpack://activitypub-poll/external window [\"wp\",\"components\"]","webpack://activitypub-poll/external window [\"wp\",\"coreData\"]","webpack://activitypub-poll/external window [\"wp\",\"data\"]","webpack://activitypub-poll/external window [\"wp\",\"editor\"]","webpack://activitypub-poll/external window [\"wp\",\"i18n\"]","webpack://activitypub-poll/external window [\"wp\",\"plugins\"]","webpack://activitypub-poll/webpack/bootstrap","webpack://activitypub-poll/webpack/runtime/compat get default export","webpack://activitypub-poll/webpack/runtime/define property getters","webpack://activitypub-poll/webpack/runtime/hasOwnProperty shorthand","webpack://activitypub-poll/webpack/runtime/make namespace object","webpack://activitypub-poll/./src/reminder/plugin.js"],"sourcesContent":["module.exports = window[\"ReactJSXRuntime\"];","module.exports = window[\"wp\"][\"components\"];","module.exports = window[\"wp\"][\"coreData\"];","module.exports = window[\"wp\"][\"data\"];","module.exports = window[\"wp\"][\"editor\"];","module.exports = window[\"wp\"][\"i18n\"];","module.exports = window[\"wp\"][\"plugins\"];","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { PluginDocumentSettingPanel } from '@wordpress/editor';\nimport { registerPlugin } from '@wordpress/plugins';\nimport { SelectControl } from '@wordpress/components';\nimport { useSelect } from '@wordpress/data';\nimport { useEntityProp } from '@wordpress/core-data';\nimport { __ } from '@wordpress/i18n';\n\nconst reminderTimeGapDefault = activityPubEventBridge.reminderTypeGap;\n\nconst Reminder = () => {\n\tconst postType = useSelect(\n\t\t( select ) => select( 'core/editor' ).getCurrentPostType(),\n\t\t[]\n\t);\n\tconst [ meta, setMeta ] = useEntityProp( 'postType', postType, 'meta' );\n\n\tconst reminderTimeGap = meta?.activitypub_event_bridge_reminder_time_gap ? meta?.activitypub_event_bridge_reminder_time_gap : reminderTimeGapDefault;\n\n\treturn (\n\t\t\n\t\t\t {\n\t\t\t\t\tsetMeta( { ...meta, activitypub_event_bridge_reminder_time_gap: value } );\n\t\t\t\t} }\n\t\t\t\t__nextHasNoMarginBottom\n\t\t\t/>\n\t\t\n\t);\n}\n\nregisterPlugin( 'activitypub-event-bridge-reminder', { render: Reminder } );"],"names":["PluginDocumentSettingPanel","registerPlugin","SelectControl","useSelect","useEntityProp","__","jsx","_jsx","reminderTimeGapDefault","activityPubEventBridge","reminderTypeGap","Reminder","postType","select","getCurrentPostType","meta","setMeta","reminderTimeGap","activitypub_event_bridge_reminder_time_gap","name","title","children","label","value","options","onChange","__nextHasNoMarginBottom","render"],"sourceRoot":""} \ No newline at end of file -- 2.39.5 From 1bb603331ab1d2ad3b04c90969e1216a34c03880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Menrath?= Date: Sun, 13 Oct 2024 15:39:23 +0200 Subject: [PATCH 7/7] improve docs and readability --- includes/class-reminder.php | 49 +++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/includes/class-reminder.php b/includes/class-reminder.php index eb2e8a1..40f9674 100644 --- a/includes/class-reminder.php +++ b/includes/class-reminder.php @@ -32,13 +32,15 @@ class Reminder { public static function init() { // Post transitions. \add_action( 'transition_post_status', array( self::class, 'maybe_schedule_event_reminder' ), 33, 3 ); - \add_action( 'delete_post', array( self::class, 'unschedule_event_reminder' ), 33, 1 ); // Send an event reminder. \add_action( 'activitypub_event_bridge_send_event_reminder', array( self::class, 'send_event_reminder' ), 10, 1 ); + // Load the block which allows overriding the reminder time for an individual event in the post settings. \add_action( 'enqueue_block_editor_assets', array( self::class, 'enqueue_editor_assets' ) ); + + // Register the post-meta which stores per-event overrides of the side-wide default of the reminder time gap. \add_action( 'init', array( self::class, 'register_postmeta' ), 11 ); } @@ -93,23 +95,17 @@ class Reminder { * @param WP_Post $post Post object. */ public static function maybe_schedule_event_reminder( $new_status, $old_status, $post ): void { - $reminder_time_gap = (int) get_post_meta( $post->ID, 'activitypub_event_bridge_reminder_time_gap', true ); - - if ( ! $reminder_time_gap ) { - $reminder_time_gap = \get_option( 'activitypub_event_bridge_reminder_time_gap', 0 ); - } - - if ( 0 === $reminder_time_gap || ! is_int( $reminder_time_gap ) ) { - return; - } - + // Re-Check that we got a valid post. $post = get_post( $post ); if ( ! $post ) { return; } - // Do not send activities if post is password protected. + // At first always unschedule the reminder for this event, it will be added again, in case. + self::unschedule_event_reminder( $post->ID ); + + // Do not set reminders if post is password protected. if ( \post_password_required( $post ) ) { return; } @@ -119,15 +115,24 @@ class Reminder { return; } - if ( 'trash' === $new_status ) { - self::unschedule_event_reminder( $post->ID ); - } - // Do not schedule a reminder if the event is not published. if ( 'publish' !== $new_status ) { return; } + // See if a reminder time gap is set for the event individually in the events post-meta. + $reminder_time_gap = (int) get_post_meta( $post->ID, 'activitypub_event_bridge_reminder_time_gap', true ); + + // If not fallback to the global reminder time gap. + if ( ! $reminder_time_gap ) { + $reminder_time_gap = \get_option( 'activitypub_event_bridge_reminder_time_gap', 0 ); + } + + // Any non positive integer means that this feature is not active for this event post. + if ( 0 === $reminder_time_gap || ! is_int( $reminder_time_gap ) ) { + return; + } + // Get start time of the event. $event_transformer = Transformer_Factory::get_transformer( $post ); @@ -142,15 +147,13 @@ class Reminder { // Get the time when the reminder of the event's start should be sent. $schedule_time = $start_timestamp - $reminder_time_gap; + // If the reminder time has already passed "now" skip it. if ( $schedule_time < \time() ) { return; } - $hook = 'activitypub_event_bridge_send_event_reminder'; - $args = array( $post->ID ); - - \wp_clear_scheduled_hook( $hook, $args ); - \wp_schedule_single_event( $schedule_time, $hook, $args ); + // All checks passed: schedule a single event which will trigger the sending of the reminder for this event post. + \wp_schedule_single_event( $schedule_time, 'activitypub_event_bridge_send_event_reminder', array( $post->ID ) ); } /** @@ -159,9 +162,7 @@ class Reminder { * @param int $post_id The WordPress post ID of the event post. */ public static function unschedule_event_reminder( $post_id ): void { - $hook = 'activitypub_event_bridge_send_event_reminder'; - $args = array( $post_id ); - \wp_clear_scheduled_hook( $hook, $args ); + \wp_clear_scheduled_hook( 'activitypub_event_bridge_send_event_reminder', array( $post_id ) ); } /** -- 2.39.5