Changeset 3129044
- Timestamp:
- 07/31/2024 09:51:26 PM (20 months ago)
- Location:
- pushly/trunk
- Files:
-
- 4 deleted
- 14 edited
-
build/meta-box-rtl.css (modified) (1 diff)
-
build/meta-box.asset.php (modified) (1 diff)
-
build/meta-box.css (modified) (1 diff)
-
build/meta-box.css.map (deleted)
-
build/meta-box.js (modified) (1 diff)
-
build/meta-box.js.map (deleted)
-
build/settings-rtl.css (modified) (1 diff)
-
build/settings.asset.php (modified) (1 diff)
-
build/settings.css (modified) (1 diff)
-
build/settings.css.map (deleted)
-
build/settings.js (modified) (1 diff)
-
build/settings.js.map (deleted)
-
environment.php (modified) (1 diff)
-
includes/admin/class-pushly-admin-post.php (modified) (1 diff)
-
includes/admin/class-pushly-admin-settings.php (modified) (4 diffs)
-
includes/admin/class-pushly-admin-util.php (modified) (4 diffs)
-
includes/admin/models/notification.php (modified) (1 diff)
-
readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
pushly/trunk/build/meta-box-rtl.css
r3118516 r3129044 1 /*!****************************************************************************************************************************************************************************************************************************************************************************************************************!*\ 2 !*** css ./node_modules/@wordpress/scripts/node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[4].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[4].use[2]!./node_modules/@wordpress/scripts/node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[4].use[3]!./src/admin/meta-box.scss ***! 3 \****************************************************************************************************************************************************************************************************************************************************************************************************************/ 4 #pushly-meta-box .components-base-control { 5 margin-bottom: 10px; 6 } 7 #pushly-meta-box .pushly-row { 8 display: flex; 9 flex-direction: row; 10 } 11 #pushly-meta-box .dashicon { 12 margin-right: 3px; 13 margin-top: 2px; 14 font-size: 14px; 15 color: #3b9c85; 16 } 1 #pushly-meta-box .components-base-control{margin-bottom:10px}#pushly-meta-box .pushly-row{display:flex;flex-direction:row}#pushly-meta-box .dashicon{color:#3b9c85;font-size:14px;margin-right:3px;margin-top:2px} -
pushly/trunk/build/meta-box.asset.php
r3118516 r3129044 1 <?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-data', 'wp-edit-post', 'wp-element', 'wp-i18n'), 'version' => 'b 1c6e775bbf12b5aca72');1 <?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-data', 'wp-edit-post', 'wp-element', 'wp-i18n'), 'version' => 'b246ef4b18f2e502d679'); -
pushly/trunk/build/meta-box.css
r3118516 r3129044 1 /*!****************************************************************************************************************************************************************************************************************************************************************************************************************!*\ 2 !*** css ./node_modules/@wordpress/scripts/node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[4].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[4].use[2]!./node_modules/@wordpress/scripts/node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[4].use[3]!./src/admin/meta-box.scss ***! 3 \****************************************************************************************************************************************************************************************************************************************************************************************************************/ 4 #pushly-meta-box .components-base-control { 5 margin-bottom: 10px; 6 } 7 #pushly-meta-box .pushly-row { 8 display: flex; 9 flex-direction: row; 10 } 11 #pushly-meta-box .dashicon { 12 margin-left: 3px; 13 margin-top: 2px; 14 font-size: 14px; 15 color: #3b9c85; 16 } 17 18 /*# sourceMappingURL=meta-box.css.map*/ 1 #pushly-meta-box .components-base-control{margin-bottom:10px}#pushly-meta-box .pushly-row{display:flex;flex-direction:row}#pushly-meta-box .dashicon{color:#3b9c85;font-size:14px;margin-left:3px;margin-top:2px} -
pushly/trunk/build/meta-box.js
r3118516 r3129044 1 /******/ (() => { // webpackBootstrap 2 /******/ "use strict"; 3 /******/ var __webpack_modules__ = ({ 4 5 /***/ "./src/admin/components/Icons.js": 6 /*!***************************************!*\ 7 !*** ./src/admin/components/Icons.js ***! 8 \***************************************/ 9 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 10 11 __webpack_require__.r(__webpack_exports__); 12 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 13 /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) 14 /* harmony export */ }); 15 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); 16 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 17 18 const icons = {}; 19 icons.pushly = (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("svg", { 20 xmlns: "http://www.w3.org/2000/svg", 21 viewBox: "0 0 1024 1024", 22 "data-icon": "pushly", 23 height: "1024", 24 width: "1024", 25 fill: "#4dbca2", 26 "aria-hidden": "true" 27 }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("path", { 28 d: "M974.602,155.62c-19.456-28.467-44.726-50.823-75.131-66.504C868.37,73.101,833.89,65,796.94,65H627.57 c-133.438,0-191.312,150.503-195.753,172.101l-137.34,633.557c-4.745,23.105-0.408,44.47,12.582,61.762l0.583,0.754 c13.851,17.016,33.681,25.999,57.352,25.999c21.516,0,41.884-7.499,60.515-22.298l0.626-0.496 c18.031-15.303,29.467-33.975,33.898-55.611L509.199,661h0.096l0.156-1H683.35c15.58,0,31.317-1.585,47.115-4.47 c1.955-0.312,3.893-0.726,5.831-1.083c0.295-0.061,0.582-0.103,0.877-0.172c16.945-3.241,33.612-8.571,53.521-16.931 c35.149-14.876,67.996-36.081,97.489-62.89c63.287-56.596,104.206-126.062,121.646-206.563 C1027.156,286.257,1015.304,214.818,974.602,155.62z M844.334,357.59c-8.264,37.557-26.19,69.391-54.831,97.311 c-4.814,4.549-9.603,8.563-14.399,12.308C753.041,484.05,731.056,492,708.376,492h-163.21l26.304-123.719l15.815-74.359 c6.108-16.141,16.554-30.349,31.195-42.254l0.6-0.269C632.671,241.041,647.218,234,662.442,232h109.29 c26.442,0,45.682,10.73,60.558,34.24C847.923,292.036,851.834,321.858,844.334,357.59z" 29 }), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("path", { 30 d: "M355.313,182.636l-0.382-0.486c-8.838-10.845-21.473-16.58-36.558-16.58c-13.738,0-26.721,4.784-38.617,14.221 l-0.382,0.317c-11.515,9.763-18.805,21.774-21.62,35.582L226.785,362h-0.209l-82.23,386.867 c-3.032,14.736-0.261,28.353,8.021,39.371l0.383,0.491c8.828,10.853,21.472,16.567,36.557,16.567c13.739,0,26.729-4.78,38.6-14.218 l0.392-0.32c11.514-9.76,18.787-21.706,21.637-35.514L280.904,609h0.199l82.239-386.928 C366.367,207.33,363.586,193.667,355.313,182.636z" 31 }), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("path", { 32 d: "M168.494,240.5l-0.339-0.43c-7.725-9.506-18.821-14.533-32.039-14.533c-12.035,0-23.418,4.192-33.837,12.461l-0.348,0.282 c-10.08,8.56-16.476,18.842-18.952,30.942L78.321,287h-0.174L6.075,626.329c-2.659,12.931-0.226,24.976,7.021,34.647l0.339,0.482 c7.734,9.506,18.831,14.558,32.048,14.558c12.044,0,23.427-4.175,33.838-12.456l0.339-0.262c10.097-8.56,16.476-19.02,18.96-31.133 L125.768,504h0.182l49.584-228.937C178.184,262.141,175.759,250.164,168.494,240.5z" 33 })); 34 /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (icons); 35 36 /***/ }), 37 38 /***/ "./src/admin/components/MetaBox.js": 39 /*!*****************************************!*\ 40 !*** ./src/admin/components/MetaBox.js ***! 41 \*****************************************/ 42 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 43 44 __webpack_require__.r(__webpack_exports__); 45 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 46 /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) 47 /* harmony export */ }); 48 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); 49 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 50 /* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/components */ "@wordpress/components"); 51 /* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__); 52 /* harmony import */ var _wordpress_data__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @wordpress/data */ "@wordpress/data"); 53 /* harmony import */ var _wordpress_data__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_wordpress_data__WEBPACK_IMPORTED_MODULE_2__); 54 /* harmony import */ var _wordpress_edit_post__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @wordpress/edit-post */ "@wordpress/edit-post"); 55 /* harmony import */ var _wordpress_edit_post__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_wordpress_edit_post__WEBPACK_IMPORTED_MODULE_3__); 56 /* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n"); 57 /* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__); 58 /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element"); 59 /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_5__); 60 /* harmony import */ var _wordpress_api_fetch__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! @wordpress/api-fetch */ "@wordpress/api-fetch"); 61 /* harmony import */ var _wordpress_api_fetch__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(_wordpress_api_fetch__WEBPACK_IMPORTED_MODULE_6__); 62 /* harmony import */ var _Icons__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./Icons */ "./src/admin/components/Icons.js"); 63 64 65 66 67 68 69 70 71 const MetaBox = () => { 72 const [isSendingNotification, setIsSendingNotification] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_5__.useState)(false); 73 const [isCustomizingContent, setIsCustomizingContent] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_5__.useState)(false); 74 const [isCustomizingAudience, setIsCustomizingAudience] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_5__.useState)(false); 75 const [customTitle, setCustomTitle] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_5__.useState)(); 76 const [customBody, setCustomBody] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_5__.useState)(); 77 const [selectedSegmentIds, setSelectedSegmentIds] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_5__.useState)([]); 78 const [segments, setSegments] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_5__.useState)([]); 79 const { 80 postMeta 81 } = (0,_wordpress_data__WEBPACK_IMPORTED_MODULE_2__.useSelect)(select => { 82 return { 83 postMeta: select('core/editor').getEditedPostAttribute('meta') 84 }; 85 }); 86 const { 87 editPost 88 } = (0,_wordpress_data__WEBPACK_IMPORTED_MODULE_2__.useDispatch)('core/editor', [postMeta?.pushly_unique, postMeta?.pushly_send_notification, postMeta?.pushly_customize_notification_content, postMeta?.pushly_custom_title, postMeta?.pushly_custom_body, postMeta?.pushly_customize_audience, postMeta?.pushly_audience_ids]); 89 90 // load segments 91 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_5__.useEffect)(() => { 92 _wordpress_api_fetch__WEBPACK_IMPORTED_MODULE_6___default()({ 93 path: '/pushly/v1/segments' 94 }).then(segments => { 95 if (segments?.status === 'success') { 96 setSegments(segments.data); 97 } 98 }); 99 }, []); 100 101 // set the initial post meta values 102 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_5__.useEffect)(() => { 103 setIsSendingNotification(postMeta?.pushly_send_notification); 104 setIsCustomizingContent(postMeta?.pushly_customize_notification_content); 105 setCustomTitle(postMeta?.pushly_custom_title); 106 setCustomBody(postMeta?.pushly_custom_body); 107 setIsCustomizingAudience(postMeta?.pushly_customize_audience); 108 setSelectedSegmentIds(postMeta?.pushly_audience_ids); 109 110 /** 111 * The editor won't send the post meta when a post is initially saved if none of the meta values have 112 * changed from what was initially returned from the backend. In order to work around this issue we 113 * set this erroneous pushly_unique meta value on page load to ensure the post meta is always sent. 114 * 115 * The primary issue we're working around here is that we want `pushly_send_notification` to: 116 * 1. On a new post default to the auto_send_status 117 * 2. On editing an existing post default to the previously saved status 118 * 119 * Because `pushly_send_notification` is a boolean and cannot be set to `null` we must first set it 120 * to the boolean value matching `auto_send_enabled` - In the case of auto_send_enabled=true the 121 * initial page load will show the box checked but the checked value is not actually sent to the 122 * backend on post save (because the form thinks nothing in the `meta` has changed). As a result 123 * of this, the backend will not see the `pushly_send_notification` was enabled so no notification 124 * will be sent. 125 */ 126 editPost({ 127 meta: { 128 pushly_unique: Date.now() 129 } 130 }); 131 }, []); 132 return (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_edit_post__WEBPACK_IMPORTED_MODULE_3__.PluginDocumentSettingPanel, { 133 name: "pushly-meta-box", 134 title: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('Pushly Notifications', 'pushly'), 135 icon: _Icons__WEBPACK_IMPORTED_MODULE_7__["default"].pushly, 136 __nextHasNoMarginBottom: true 137 }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { 138 id: "pushly-meta-box" 139 }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.CheckboxControl, { 140 checked: isSendingNotification 141 // help={__('Schedule a notification for this post', 'pushly')} 142 , 143 label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('Send Notification', 'pushly'), 144 onChange: value => { 145 setIsSendingNotification(value); 146 editPost({ 147 meta: { 148 pushly_send_notification: value 149 } 150 }); 151 }, 152 __nextHasNoMarginBottom: true 153 }), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.CheckboxControl, { 154 checked: isCustomizingContent, 155 disabled: !isSendingNotification 156 // help={__('Schedule a notification for this post', 'pushly')} 157 , 158 label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('Customize Content', 'pushly'), 159 onChange: value => { 160 setIsCustomizingContent(value); 161 editPost({ 162 meta: { 163 pushly_customize_notification_content: value 164 } 165 }); 166 }, 167 __nextHasNoMarginBottom: true 168 }), isCustomizingContent && (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.TextControl, { 169 name: "pushlyCustomTitle", 170 label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('Title'), 171 value: customTitle, 172 onChange: value => { 173 setCustomTitle(value); 174 editPost({ 175 meta: { 176 pushly_custom_title: value 177 } 178 }); 179 }, 180 __next40pxDefaultSize: true, 181 __nextHasNoMarginBottom: true 182 }), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.TextControl, { 183 name: "pushlyCustomBody", 184 label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('Body'), 185 value: customBody, 186 onChange: value => { 187 setCustomBody(value); 188 editPost({ 189 meta: { 190 pushly_custom_body: value 191 } 192 }); 193 }, 194 __next40pxDefaultSize: true, 195 __nextHasNoMarginBottom: true 196 })), segments.length > 0 && (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("div", { 197 className: "pushly-row" 198 }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.CheckboxControl, { 199 checked: isCustomizingAudience, 200 disabled: !isSendingNotification, 201 label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('Refine Audience', 'pushly'), 202 onChange: value => { 203 setIsCustomizingAudience(value); 204 editPost({ 205 meta: { 206 pushly_customize_audience: value 207 } 208 }); 209 }, 210 __nextHasNoMarginBottom: true 211 }), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.Tooltip, { 212 text: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('When checked the notification will be targeted to specific users ' + 'When unchecked all users will be targeted.', 'pushly') 213 }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.Icon, { 214 icon: "info-outline", 215 size: "16" 216 }))), isCustomizingAudience && segments.length > 0 && (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.FormTokenField, { 217 label: 'Targeted Segments', 218 value: selectedSegmentIds && selectedSegmentIds.flatMap(segmentId => { 219 const found = segments.find(segment => segment.id === segmentId); 220 return found ? found.name : []; 221 }), 222 suggestions: segments.map(value => value.name), 223 onChange: segmentNames => { 224 const segmentIds = segmentNames.flatMap(segmentName => { 225 const found = segments.find(segment => segment.name === segmentName); 226 return found ? found.id : []; 227 }); 228 setSelectedSegmentIds(segmentIds); 229 editPost({ 230 meta: { 231 pushly_audience_ids: segmentIds 232 } 233 }); 234 }, 235 __experimentalExpandOnFocus: true, 236 __nextHasNoMarginBottom: true 237 }))); 238 }; 239 /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (MetaBox); 240 241 /***/ }), 242 243 /***/ "./src/admin/meta-box.scss": 244 /*!*********************************!*\ 245 !*** ./src/admin/meta-box.scss ***! 246 \*********************************/ 247 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 248 249 __webpack_require__.r(__webpack_exports__); 250 // extracted by mini-css-extract-plugin 251 252 253 /***/ }), 254 255 /***/ "react": 256 /*!************************!*\ 257 !*** external "React" ***! 258 \************************/ 259 /***/ ((module) => { 260 261 module.exports = window["React"]; 262 263 /***/ }), 264 265 /***/ "@wordpress/api-fetch": 266 /*!**********************************!*\ 267 !*** external ["wp","apiFetch"] ***! 268 \**********************************/ 269 /***/ ((module) => { 270 271 module.exports = window["wp"]["apiFetch"]; 272 273 /***/ }), 274 275 /***/ "@wordpress/components": 276 /*!************************************!*\ 277 !*** external ["wp","components"] ***! 278 \************************************/ 279 /***/ ((module) => { 280 281 module.exports = window["wp"]["components"]; 282 283 /***/ }), 284 285 /***/ "@wordpress/data": 286 /*!******************************!*\ 287 !*** external ["wp","data"] ***! 288 \******************************/ 289 /***/ ((module) => { 290 291 module.exports = window["wp"]["data"]; 292 293 /***/ }), 294 295 /***/ "@wordpress/edit-post": 296 /*!**********************************!*\ 297 !*** external ["wp","editPost"] ***! 298 \**********************************/ 299 /***/ ((module) => { 300 301 module.exports = window["wp"]["editPost"]; 302 303 /***/ }), 304 305 /***/ "@wordpress/element": 306 /*!*********************************!*\ 307 !*** external ["wp","element"] ***! 308 \*********************************/ 309 /***/ ((module) => { 310 311 module.exports = window["wp"]["element"]; 312 313 /***/ }), 314 315 /***/ "@wordpress/i18n": 316 /*!******************************!*\ 317 !*** external ["wp","i18n"] ***! 318 \******************************/ 319 /***/ ((module) => { 320 321 module.exports = window["wp"]["i18n"]; 322 323 /***/ }) 324 325 /******/ }); 326 /************************************************************************/ 327 /******/ // The module cache 328 /******/ var __webpack_module_cache__ = {}; 329 /******/ 330 /******/ // The require function 331 /******/ function __webpack_require__(moduleId) { 332 /******/ // Check if module is in cache 333 /******/ var cachedModule = __webpack_module_cache__[moduleId]; 334 /******/ if (cachedModule !== undefined) { 335 /******/ return cachedModule.exports; 336 /******/ } 337 /******/ // Create a new module (and put it into the cache) 338 /******/ var module = __webpack_module_cache__[moduleId] = { 339 /******/ // no module.id needed 340 /******/ // no module.loaded needed 341 /******/ exports: {} 342 /******/ }; 343 /******/ 344 /******/ // Execute the module function 345 /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); 346 /******/ 347 /******/ // Return the exports of the module 348 /******/ return module.exports; 349 /******/ } 350 /******/ 351 /************************************************************************/ 352 /******/ /* webpack/runtime/compat get default export */ 353 /******/ (() => { 354 /******/ // getDefaultExport function for compatibility with non-harmony modules 355 /******/ __webpack_require__.n = (module) => { 356 /******/ var getter = module && module.__esModule ? 357 /******/ () => (module['default']) : 358 /******/ () => (module); 359 /******/ __webpack_require__.d(getter, { a: getter }); 360 /******/ return getter; 361 /******/ }; 362 /******/ })(); 363 /******/ 364 /******/ /* webpack/runtime/define property getters */ 365 /******/ (() => { 366 /******/ // define getter functions for harmony exports 367 /******/ __webpack_require__.d = (exports, definition) => { 368 /******/ for(var key in definition) { 369 /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { 370 /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); 371 /******/ } 372 /******/ } 373 /******/ }; 374 /******/ })(); 375 /******/ 376 /******/ /* webpack/runtime/hasOwnProperty shorthand */ 377 /******/ (() => { 378 /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) 379 /******/ })(); 380 /******/ 381 /******/ /* webpack/runtime/make namespace object */ 382 /******/ (() => { 383 /******/ // define __esModule on exports 384 /******/ __webpack_require__.r = (exports) => { 385 /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 386 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 387 /******/ } 388 /******/ Object.defineProperty(exports, '__esModule', { value: true }); 389 /******/ }; 390 /******/ })(); 391 /******/ 392 /************************************************************************/ 393 var __webpack_exports__ = {}; 394 // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. 395 (() => { 396 /*!*******************************!*\ 397 !*** ./src/admin/meta-box.js ***! 398 \*******************************/ 399 __webpack_require__.r(__webpack_exports__); 400 /* harmony import */ var _meta_box_scss__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./meta-box.scss */ "./src/admin/meta-box.scss"); 401 /* harmony import */ var _components_MetaBox__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./components/MetaBox */ "./src/admin/components/MetaBox.js"); 402 /* harmony import */ var _components_Icons__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./components/Icons */ "./src/admin/components/Icons.js"); 403 404 405 406 (function (wp, React) { 407 wp.domReady(function () { 408 wp.plugins.registerPlugin('pushly', { 409 icon: _components_Icons__WEBPACK_IMPORTED_MODULE_2__["default"].pushly, 410 render: _components_MetaBox__WEBPACK_IMPORTED_MODULE_1__["default"] 411 }); 412 }); 413 })(window.wp, window.React); 414 })(); 415 416 /******/ })() 417 ; 418 //# sourceMappingURL=meta-box.js.map 1 (()=>{"use strict";var e={n:t=>{var n=t&&t.__esModule?()=>t.default:()=>t;return e.d(n,{a:n}),n},d:(t,n)=>{for(var o in n)e.o(n,o)&&!e.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:n[o]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)};const t=window.React,n=window.wp.components,o=window.wp.data,a=window.wp.editPost,l=window.wp.i18n,c=window.wp.element,s=window.wp.apiFetch;var i=e.n(s);const u={};u.pushly=(0,t.createElement)("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 1024 1024","data-icon":"pushly",height:"1024",width:"1024",fill:"#4dbca2","aria-hidden":"true"},(0,t.createElement)("path",{d:"M974.602,155.62c-19.456-28.467-44.726-50.823-75.131-66.504C868.37,73.101,833.89,65,796.94,65H627.57 c-133.438,0-191.312,150.503-195.753,172.101l-137.34,633.557c-4.745,23.105-0.408,44.47,12.582,61.762l0.583,0.754 c13.851,17.016,33.681,25.999,57.352,25.999c21.516,0,41.884-7.499,60.515-22.298l0.626-0.496 c18.031-15.303,29.467-33.975,33.898-55.611L509.199,661h0.096l0.156-1H683.35c15.58,0,31.317-1.585,47.115-4.47 c1.955-0.312,3.893-0.726,5.831-1.083c0.295-0.061,0.582-0.103,0.877-0.172c16.945-3.241,33.612-8.571,53.521-16.931 c35.149-14.876,67.996-36.081,97.489-62.89c63.287-56.596,104.206-126.062,121.646-206.563 C1027.156,286.257,1015.304,214.818,974.602,155.62z M844.334,357.59c-8.264,37.557-26.19,69.391-54.831,97.311 c-4.814,4.549-9.603,8.563-14.399,12.308C753.041,484.05,731.056,492,708.376,492h-163.21l26.304-123.719l15.815-74.359 c6.108-16.141,16.554-30.349,31.195-42.254l0.6-0.269C632.671,241.041,647.218,234,662.442,232h109.29 c26.442,0,45.682,10.73,60.558,34.24C847.923,292.036,851.834,321.858,844.334,357.59z"}),(0,t.createElement)("path",{d:"M355.313,182.636l-0.382-0.486c-8.838-10.845-21.473-16.58-36.558-16.58c-13.738,0-26.721,4.784-38.617,14.221 l-0.382,0.317c-11.515,9.763-18.805,21.774-21.62,35.582L226.785,362h-0.209l-82.23,386.867 c-3.032,14.736-0.261,28.353,8.021,39.371l0.383,0.491c8.828,10.853,21.472,16.567,36.557,16.567c13.739,0,26.729-4.78,38.6-14.218 l0.392-0.32c11.514-9.76,18.787-21.706,21.637-35.514L280.904,609h0.199l82.239-386.928 C366.367,207.33,363.586,193.667,355.313,182.636z"}),(0,t.createElement)("path",{d:"M168.494,240.5l-0.339-0.43c-7.725-9.506-18.821-14.533-32.039-14.533c-12.035,0-23.418,4.192-33.837,12.461l-0.348,0.282 c-10.08,8.56-16.476,18.842-18.952,30.942L78.321,287h-0.174L6.075,626.329c-2.659,12.931-0.226,24.976,7.021,34.647l0.339,0.482 c7.734,9.506,18.831,14.558,32.048,14.558c12.044,0,23.427-4.175,33.838-12.456l0.339-0.262c10.097-8.56,16.476-19.02,18.96-31.133 L125.768,504h0.182l49.584-228.937C178.184,262.141,175.759,250.164,168.494,240.5z"}));const _=u,h=()=>{const[e,s]=(0,c.useState)(!1),[u,h]=(0,c.useState)(!1),[d,r]=(0,c.useState)(!1),[m,p]=(0,c.useState)(),[y,w]=(0,c.useState)(),[g,f]=(0,c.useState)([]),[C,b]=(0,c.useState)([]),{postMeta:x}=(0,o.useSelect)((e=>({postMeta:e("core/editor").getEditedPostAttribute("meta")}))),{editPost:E}=(0,o.useDispatch)("core/editor",[x?.pushly_unique,x?.pushly_send_notification,x?.pushly_customize_notification_content,x?.pushly_custom_title,x?.pushly_custom_body,x?.pushly_customize_audience,x?.pushly_audience_ids]);return(0,c.useEffect)((()=>{i()({path:"/pushly/v1/segments"}).then((e=>{"success"===e?.status&&b(e.data)}))}),[]),(0,c.useEffect)((()=>{s(x?.pushly_send_notification),h(x?.pushly_customize_notification_content),p(x?.pushly_custom_title),w(x?.pushly_custom_body),r(x?.pushly_customize_audience),f(x?.pushly_audience_ids),E({meta:{pushly_unique:Date.now()}})}),[]),(0,t.createElement)(a.PluginDocumentSettingPanel,{name:"pushly-meta-box",title:(0,l.__)("Pushly Notifications","pushly"),icon:_.pushly,__nextHasNoMarginBottom:!0},(0,t.createElement)("div",{id:"pushly-meta-box"},(0,t.createElement)(n.CheckboxControl,{checked:e,label:(0,l.__)("Send Notification","pushly"),onChange:e=>{s(e),E({meta:{pushly_send_notification:e}})},__nextHasNoMarginBottom:!0}),(0,t.createElement)(n.CheckboxControl,{checked:u,disabled:!e,label:(0,l.__)("Customize Content","pushly"),onChange:e=>{h(e),E({meta:{pushly_customize_notification_content:e}})},__nextHasNoMarginBottom:!0}),u&&(0,t.createElement)(t.Fragment,null,(0,t.createElement)(n.TextControl,{name:"pushlyCustomTitle",label:(0,l.__)("Title"),value:m,onChange:e=>{p(e),E({meta:{pushly_custom_title:e}})},__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0}),(0,t.createElement)(n.TextControl,{name:"pushlyCustomBody",label:(0,l.__)("Body"),value:y,onChange:e=>{w(e),E({meta:{pushly_custom_body:e}})},__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0})),C.length>0&&(0,t.createElement)("div",{className:"pushly-row"},(0,t.createElement)(n.CheckboxControl,{checked:d,disabled:!e,label:(0,l.__)("Refine Audience","pushly"),onChange:e=>{r(e),E({meta:{pushly_customize_audience:e}})},__nextHasNoMarginBottom:!0}),(0,t.createElement)(n.Tooltip,{text:(0,l.__)("When checked the notification will be targeted to specific users When unchecked all users will be targeted.","pushly")},(0,t.createElement)(n.Icon,{icon:"info-outline",size:"16"}))),d&&C.length>0&&(0,t.createElement)(n.FormTokenField,{label:"Targeted Segments",value:g&&g.flatMap((e=>{const t=C.find((t=>t.id===e));return t?t.name:[]})),suggestions:C.map((e=>e.name)),onChange:e=>{const t=e.flatMap((e=>{const t=C.find((t=>t.name===e));return t?t.id:[]}));f(t),E({meta:{pushly_audience_ids:t}})},__experimentalExpandOnFocus:!0,__nextHasNoMarginBottom:!0})))};var d;d=window.wp,window.React,d.domReady((function(){d.plugins.registerPlugin("pushly",{icon:_.pushly,render:h})}))})(); -
pushly/trunk/build/settings-rtl.css
r3118516 r3129044 1 /*!****************************************************************************************************************************************************************************************************************************************************************************************************************!*\ 2 !*** css ./node_modules/@wordpress/scripts/node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[4].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[4].use[2]!./node_modules/@wordpress/scripts/node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[4].use[3]!./src/admin/settings.scss ***! 3 \****************************************************************************************************************************************************************************************************************************************************************************************************************/ 4 #pushly-settings { 5 max-width: 800px; 6 } 7 #pushly-settings h1 { 8 padding-block: 8px; 9 } 10 #pushly-settings .components-notice, #pushly-settings .components-notice__content { 11 margin: 0; 12 } 13 #pushly-settings .components-notice-list { 14 display: flex; 15 flex-direction: column; 16 gap: 8px; 17 margin-block-end: 16px; 18 } 19 #pushly-settings .components-panel { 20 margin-block-end: 8px; 21 } 22 #pushly-settings .components-text-control__input[disabled], 23 #pushly-settings .components-checkbox__input[disabled] { 24 color: rgba(0, 0, 0, 0.25); 25 background-color: #f5f5f5; 26 cursor: not-allowed; 27 opacity: 1; 28 } 29 #pushly-settings .components-button .is-primary { 30 margin-top: 10px; 31 } 1 #pushly-settings{max-width:800px}#pushly-settings h1{padding-block:8px}#pushly-settings .components-notice,#pushly-settings .components-notice__content{margin:0}#pushly-settings .components-notice-list{display:flex;flex-direction:column;gap:8px;margin-block-end:16px}#pushly-settings .components-panel{margin-block-end:8px}#pushly-settings .components-checkbox__input[disabled],#pushly-settings .components-text-control__input[disabled]{background-color:#f5f5f5;color:rgba(0,0,0,.25);cursor:not-allowed;opacity:1}#pushly-settings .components-button .is-primary{margin-top:10px}#pushly-settings .settings-help-text{color:#757575;font-size:12px;font-style:normal;margin-bottom:0;margin-top:8px} -
pushly/trunk/build/settings.asset.php
r3118516 r3129044 1 <?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices'), 'version' => ' 2432d59cbdd1dabb0405');1 <?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices'), 'version' => 'eb085392066bfaec7d85'); -
pushly/trunk/build/settings.css
r3118516 r3129044 1 /*!****************************************************************************************************************************************************************************************************************************************************************************************************************!*\ 2 !*** css ./node_modules/@wordpress/scripts/node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[4].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[4].use[2]!./node_modules/@wordpress/scripts/node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[4].use[3]!./src/admin/settings.scss ***! 3 \****************************************************************************************************************************************************************************************************************************************************************************************************************/ 4 #pushly-settings { 5 max-width: 800px; 6 } 7 #pushly-settings h1 { 8 padding-block: 8px; 9 } 10 #pushly-settings .components-notice, #pushly-settings .components-notice__content { 11 margin: 0; 12 } 13 #pushly-settings .components-notice-list { 14 display: flex; 15 flex-direction: column; 16 gap: 8px; 17 margin-block-end: 16px; 18 } 19 #pushly-settings .components-panel { 20 margin-block-end: 8px; 21 } 22 #pushly-settings .components-text-control__input[disabled], 23 #pushly-settings .components-checkbox__input[disabled] { 24 color: rgba(0, 0, 0, 0.25); 25 background-color: #f5f5f5; 26 cursor: not-allowed; 27 opacity: 1; 28 } 29 #pushly-settings .components-button .is-primary { 30 margin-top: 10px; 31 } 32 33 /*# sourceMappingURL=settings.css.map*/ 1 #pushly-settings{max-width:800px}#pushly-settings h1{padding-block:8px}#pushly-settings .components-notice,#pushly-settings .components-notice__content{margin:0}#pushly-settings .components-notice-list{display:flex;flex-direction:column;gap:8px;margin-block-end:16px}#pushly-settings .components-panel{margin-block-end:8px}#pushly-settings .components-checkbox__input[disabled],#pushly-settings .components-text-control__input[disabled]{background-color:#f5f5f5;color:rgba(0,0,0,.25);cursor:not-allowed;opacity:1}#pushly-settings .components-button .is-primary{margin-top:10px}#pushly-settings .settings-help-text{color:#757575;font-size:12px;font-style:normal;margin-bottom:0;margin-top:8px} -
pushly/trunk/build/settings.js
r3118516 r3129044 1 /******/ (() => { // webpackBootstrap 2 /******/ "use strict"; 3 /******/ var __webpack_modules__ = ({ 4 5 /***/ "./src/admin/components/Notices.js": 6 /*!*****************************************!*\ 7 !*** ./src/admin/components/Notices.js ***! 8 \*****************************************/ 9 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 10 11 __webpack_require__.r(__webpack_exports__); 12 /* harmony export */ __webpack_require__.d(__webpack_exports__, { 13 /* harmony export */ Notices: () => (/* binding */ Notices) 14 /* harmony export */ }); 15 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); 16 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 17 /* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/components */ "@wordpress/components"); 18 /* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__); 19 /* harmony import */ var _wordpress_notices__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @wordpress/notices */ "@wordpress/notices"); 20 /* harmony import */ var _wordpress_notices__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_wordpress_notices__WEBPACK_IMPORTED_MODULE_2__); 21 /* harmony import */ var _wordpress_data__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @wordpress/data */ "@wordpress/data"); 22 /* harmony import */ var _wordpress_data__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_wordpress_data__WEBPACK_IMPORTED_MODULE_3__); 23 24 25 26 27 function Notices() { 28 var _notices$filter; 29 const notices = (0,_wordpress_data__WEBPACK_IMPORTED_MODULE_3__.useSelect)(select => select(_wordpress_notices__WEBPACK_IMPORTED_MODULE_2__.store).getNotices(), []); 30 const { 31 removeNotice 32 } = (0,_wordpress_data__WEBPACK_IMPORTED_MODULE_3__.useDispatch)(_wordpress_notices__WEBPACK_IMPORTED_MODULE_2__.store); 33 const snackbarNotices = (_notices$filter = notices?.filter(({ 34 type 35 }) => type === 'default')) !== null && _notices$filter !== void 0 ? _notices$filter : []; 36 return (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.NoticeList, { 37 notices: snackbarNotices, 38 className: "components-editor-notices__snackbar", 39 onRemove: removeNotice 40 }); 41 } 42 43 /***/ }), 44 45 /***/ "./src/admin/settings.scss": 46 /*!*********************************!*\ 47 !*** ./src/admin/settings.scss ***! 48 \*********************************/ 49 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 50 51 __webpack_require__.r(__webpack_exports__); 52 // extracted by mini-css-extract-plugin 53 54 55 /***/ }), 56 57 /***/ "react": 58 /*!************************!*\ 59 !*** external "React" ***! 60 \************************/ 61 /***/ ((module) => { 62 63 module.exports = window["React"]; 64 65 /***/ }), 66 67 /***/ "@wordpress/api-fetch": 68 /*!**********************************!*\ 69 !*** external ["wp","apiFetch"] ***! 70 \**********************************/ 71 /***/ ((module) => { 72 73 module.exports = window["wp"]["apiFetch"]; 74 75 /***/ }), 76 77 /***/ "@wordpress/components": 78 /*!************************************!*\ 79 !*** external ["wp","components"] ***! 80 \************************************/ 81 /***/ ((module) => { 82 83 module.exports = window["wp"]["components"]; 84 85 /***/ }), 86 87 /***/ "@wordpress/data": 88 /*!******************************!*\ 89 !*** external ["wp","data"] ***! 90 \******************************/ 91 /***/ ((module) => { 92 93 module.exports = window["wp"]["data"]; 94 95 /***/ }), 96 97 /***/ "@wordpress/dom-ready": 98 /*!**********************************!*\ 99 !*** external ["wp","domReady"] ***! 100 \**********************************/ 101 /***/ ((module) => { 102 103 module.exports = window["wp"]["domReady"]; 104 105 /***/ }), 106 107 /***/ "@wordpress/element": 108 /*!*********************************!*\ 109 !*** external ["wp","element"] ***! 110 \*********************************/ 111 /***/ ((module) => { 112 113 module.exports = window["wp"]["element"]; 114 115 /***/ }), 116 117 /***/ "@wordpress/i18n": 118 /*!******************************!*\ 119 !*** external ["wp","i18n"] ***! 120 \******************************/ 121 /***/ ((module) => { 122 123 module.exports = window["wp"]["i18n"]; 124 125 /***/ }), 126 127 /***/ "@wordpress/notices": 128 /*!*********************************!*\ 129 !*** external ["wp","notices"] ***! 130 \*********************************/ 131 /***/ ((module) => { 132 133 module.exports = window["wp"]["notices"]; 134 135 /***/ }) 136 137 /******/ }); 138 /************************************************************************/ 139 /******/ // The module cache 140 /******/ var __webpack_module_cache__ = {}; 141 /******/ 142 /******/ // The require function 143 /******/ function __webpack_require__(moduleId) { 144 /******/ // Check if module is in cache 145 /******/ var cachedModule = __webpack_module_cache__[moduleId]; 146 /******/ if (cachedModule !== undefined) { 147 /******/ return cachedModule.exports; 148 /******/ } 149 /******/ // Create a new module (and put it into the cache) 150 /******/ var module = __webpack_module_cache__[moduleId] = { 151 /******/ // no module.id needed 152 /******/ // no module.loaded needed 153 /******/ exports: {} 154 /******/ }; 155 /******/ 156 /******/ // Execute the module function 157 /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); 158 /******/ 159 /******/ // Return the exports of the module 160 /******/ return module.exports; 161 /******/ } 162 /******/ 163 /************************************************************************/ 164 /******/ /* webpack/runtime/compat get default export */ 165 /******/ (() => { 166 /******/ // getDefaultExport function for compatibility with non-harmony modules 167 /******/ __webpack_require__.n = (module) => { 168 /******/ var getter = module && module.__esModule ? 169 /******/ () => (module['default']) : 170 /******/ () => (module); 171 /******/ __webpack_require__.d(getter, { a: getter }); 172 /******/ return getter; 173 /******/ }; 174 /******/ })(); 175 /******/ 176 /******/ /* webpack/runtime/define property getters */ 177 /******/ (() => { 178 /******/ // define getter functions for harmony exports 179 /******/ __webpack_require__.d = (exports, definition) => { 180 /******/ for(var key in definition) { 181 /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { 182 /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); 183 /******/ } 184 /******/ } 185 /******/ }; 186 /******/ })(); 187 /******/ 188 /******/ /* webpack/runtime/hasOwnProperty shorthand */ 189 /******/ (() => { 190 /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) 191 /******/ })(); 192 /******/ 193 /******/ /* webpack/runtime/make namespace object */ 194 /******/ (() => { 195 /******/ // define __esModule on exports 196 /******/ __webpack_require__.r = (exports) => { 197 /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 198 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 199 /******/ } 200 /******/ Object.defineProperty(exports, '__esModule', { value: true }); 201 /******/ }; 202 /******/ })(); 203 /******/ 204 /************************************************************************/ 205 var __webpack_exports__ = {}; 206 // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. 207 (() => { 208 /*!*******************************!*\ 209 !*** ./src/admin/settings.js ***! 210 \*******************************/ 211 __webpack_require__.r(__webpack_exports__); 212 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ "react"); 213 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); 214 /* harmony import */ var _settings_scss__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./settings.scss */ "./src/admin/settings.scss"); 215 /* harmony import */ var _wordpress_dom_ready__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @wordpress/dom-ready */ "@wordpress/dom-ready"); 216 /* harmony import */ var _wordpress_dom_ready__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_wordpress_dom_ready__WEBPACK_IMPORTED_MODULE_2__); 217 /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element"); 218 /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_3__); 219 /* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @wordpress/i18n */ "@wordpress/i18n"); 220 /* harmony import */ var _wordpress_i18n__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__); 221 /* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @wordpress/components */ "@wordpress/components"); 222 /* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_wordpress_components__WEBPACK_IMPORTED_MODULE_5__); 223 /* harmony import */ var _wordpress_api_fetch__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! @wordpress/api-fetch */ "@wordpress/api-fetch"); 224 /* harmony import */ var _wordpress_api_fetch__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(_wordpress_api_fetch__WEBPACK_IMPORTED_MODULE_6__); 225 /* harmony import */ var _wordpress_data__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! @wordpress/data */ "@wordpress/data"); 226 /* harmony import */ var _wordpress_data__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(_wordpress_data__WEBPACK_IMPORTED_MODULE_7__); 227 /* harmony import */ var _wordpress_notices__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! @wordpress/notices */ "@wordpress/notices"); 228 /* harmony import */ var _wordpress_notices__WEBPACK_IMPORTED_MODULE_8___default = /*#__PURE__*/__webpack_require__.n(_wordpress_notices__WEBPACK_IMPORTED_MODULE_8__); 229 /* harmony import */ var _components_Notices__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./components/Notices */ "./src/admin/components/Notices.js"); 230 231 232 233 234 235 236 237 238 239 240 241 /** SETTINGS **/ 242 243 const useSettings = () => { 244 const [cdnDomain, setCdnDomain] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useState)(); 245 const [sdkKey, setSdkKey] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useState)(); 246 const [apiKey, setApiKey] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useState)(); 247 const [sendingEnabled, setSendingEnabled] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useState)(); 248 const [autoSendEnabled, setAutoSendEnabled] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useState)(); 249 const [initializationDisabled, setInitializationDisabled] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useState)(); 250 const [initialInitializationDisabled, setInitialInitializationDisabled] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useState)(); 251 const { 252 createSuccessNotice, 253 createErrorNotice, 254 removeNotice 255 } = (0,_wordpress_data__WEBPACK_IMPORTED_MODULE_7__.useDispatch)(_wordpress_notices__WEBPACK_IMPORTED_MODULE_8__.store); 256 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useEffect)(() => { 257 _wordpress_api_fetch__WEBPACK_IMPORTED_MODULE_6___default()({ 258 path: '/wp/v2/settings' 259 }).then(settings => { 260 var _settings$pushly; 261 const pushly = (_settings$pushly = settings?.pushly) !== null && _settings$pushly !== void 0 ? _settings$pushly : {}; 262 setSdkKey(pushly.sdk_key === undefined ? null : pushly.sdk_key); 263 setApiKey(pushly.api_key === undefined ? null : pushly.api_key); 264 setSendingEnabled(pushly.sending_enabled === undefined ? false : pushly.sending_enabled); 265 setAutoSendEnabled(pushly.auto_send_enabled === undefined ? null : pushly.auto_send_enabled); 266 setInitializationDisabled(pushly.initialization_disabled === undefined ? false : pushly.initialization_disabled); 267 setInitialInitializationDisabled(pushly.initialization_disabled === undefined ? false : pushly.initialization_disabled); 268 setCdnDomain(pushly_env.CDN_DOMAIN); 269 }); 270 }, []); 271 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.useEffect)(() => { 272 if (sendingEnabled && autoSendEnabled === null) { 273 setAutoSendEnabled(sendingEnabled); 274 } 275 }, [sendingEnabled]); 276 const saveSettings = () => { 277 if (sendingEnabled && !apiKey) { 278 removeNotice('pushly'); 279 createErrorNotice('API Key must be provided when sending is enabled.', { 280 id: 'pushly', 281 type: 'default', 282 explicitDismiss: true 283 }); 284 return; 285 } 286 287 // we are going to resolve additional domain settings here to add to the settings store 288 fetch(`https://${cdnDomain}/domain-settings/${sdkKey}`).then(response => { 289 if (response.ok) { 290 response.json().then(settings => { 291 // this comes from wp_add_inline_script in class-pushly-admin-settings 292 _wordpress_api_fetch__WEBPACK_IMPORTED_MODULE_6___default().use(_wordpress_api_fetch__WEBPACK_IMPORTED_MODULE_6___default().createNonceMiddleware(pushly_env.API_NONCE)); 293 _wordpress_api_fetch__WEBPACK_IMPORTED_MODULE_6___default()({ 294 path: '/wp/v2/settings', 295 method: 'POST', 296 data: { 297 pushly: { 298 domain_id: settings.domain.id, 299 sdk_key: sdkKey, 300 api_key: apiKey, 301 sending_enabled: sendingEnabled, 302 auto_send_enabled: autoSendEnabled, 303 initialization_disabled: initializationDisabled 304 } 305 } 306 }).then(() => { 307 removeNotice('pushly'); 308 createSuccessNotice((0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('Settings saved', 'pushly'), { 309 id: 'pushly', 310 type: 'default' 311 }); 312 }).catch(reason => { 313 // sanitize to user-friendly error message 314 const message = reason.data.details.pushly.message.replace('of pushly', '').replace('pushly[', '').replace(']', '').replace('.', '').split("_").map(snake => snake.split("_").map(substr => substr.charAt(0).toUpperCase() + substr.slice(1)).join(" ")).join(" ").trim(); 315 removeNotice('pushly'); 316 createErrorNotice(message, { 317 id: 'pushly', 318 type: 'default', 319 explicitDismiss: true 320 }); 321 }); 322 }); 323 } else { 324 removeNotice('pushly'); 325 createErrorNotice((0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('The provided SDK Key is not valid', 'pushly'), { 326 id: 'pushly', 327 type: 'default', 328 explicitDismiss: true 329 }); 330 } 331 }); 332 }; 333 return { 334 sdkKey, 335 setSdkKey, 336 apiKey, 337 setApiKey, 338 sendingEnabled, 339 setSendingEnabled, 340 autoSendEnabled, 341 setAutoSendEnabled, 342 initializationDisabled, 343 setInitializationDisabled, 344 initialInitializationDisabled, 345 saveSettings 346 }; 347 }; 348 const SettingsPage = () => { 349 const { 350 sdkKey, 351 setSdkKey, 352 apiKey, 353 setApiKey, 354 sendingEnabled, 355 setSendingEnabled, 356 autoSendEnabled, 357 setAutoSendEnabled, 358 initializationDisabled, 359 setInitializationDisabled, 360 initialInitializationDisabled, 361 saveSettings 362 } = useSettings(); 363 return (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(react__WEBPACK_IMPORTED_MODULE_0__.Fragment, null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)("h1", null, "Pushly Settings"), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_components_Notices__WEBPACK_IMPORTED_MODULE_9__.Notices, null), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_5__.Panel, null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_5__.PanelBody, { 364 title: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('Configuration', 'pushly') 365 }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_5__.PanelRow, null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_5__.TextControl, { 366 name: "sdkKey", 367 label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('SDK Key', 'pushly'), 368 help: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('Enter the SDK Key associated to this Wordpress installation.', 'pushly'), 369 value: sdkKey, 370 onChange: value => setSdkKey(value), 371 type: "api-key", 372 __next40pxDefaultSize: true, 373 __nextHasNoMarginBottom: true 374 }))), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_5__.PanelBody, { 375 title: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('Notifications', 'pushly') 376 }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_5__.PanelRow, null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_5__.ToggleControl, { 377 name: "sendingEnabled", 378 checked: sendingEnabled, 379 help: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('When enabled, a new section will be added to the post editor that allows ' + 'you to send a notification.', 'pushly'), 380 label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('Send Notifications for New Posts', 'pushly'), 381 onChange: value => setSendingEnabled(value === true), 382 __nextHasNoMarginBottom: true 383 })), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_5__.PanelRow, null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_5__.ToggleControl, { 384 name: "autoSendEnabled", 385 checked: sendingEnabled && (autoSendEnabled || autoSendEnabled === null), 386 help: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('When enabled, the checkbox to send a notification on the post editor w' + 'ill be checked by default.', 'pushly'), 387 label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('Automatic Sending', 'pushly'), 388 disabled: !sendingEnabled, 389 onChange: value => setAutoSendEnabled(value), 390 __nextHasNoMarginBottom: true 391 })), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_5__.PanelRow, null, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_5__.TextControl, { 392 name: "apiKey", 393 type: "password", 394 label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('API Key'), 395 help: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('Provide the Wordpress API Key to enable notification sending ' + 'directly from the plugin.', 'pushly'), 396 value: apiKey, 397 disabled: !sendingEnabled, 398 onChange: value => setApiKey(value), 399 __next40pxDefaultSize: true, 400 __nextHasNoMarginBottom: true 401 }))), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_5__.PanelBody, { 402 title: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('Advanced', 'pushly'), 403 initialOpen: initialInitializationDisabled 404 }, (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_5__.ToggleControl, { 405 name: "initializationDisabled", 406 checked: initializationDisabled, 407 help: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('When this box is checked the Pushly SDK will not automatically run on your ' + 'site; Existing subscribers can still receive notifications.', 'pushly'), 408 label: (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('Disable SDK Initialization', 'pushly'), 409 onChange: value => setInitializationDisabled(value), 410 __nextHasNoMarginBottom: true 411 }))), (0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(_wordpress_components__WEBPACK_IMPORTED_MODULE_5__.Button, { 412 variant: "primary", 413 onClick: saveSettings, 414 __next40pxDefaultSize: true 415 }, (0,_wordpress_i18n__WEBPACK_IMPORTED_MODULE_4__.__)('Save', 'pushly'))); 416 }; 417 _wordpress_dom_ready__WEBPACK_IMPORTED_MODULE_2___default()(() => { 418 // pushly_env from wp_add_inline_script in class-pushly-admin-settings; if it is not set then something is wrong 419 if (typeof pushly_env !== 'undefined' && pushly_env?.CDN_DOMAIN && pushly_env?.API_NONCE) { 420 const target = document.getElementById('pushly-settings'); 421 if (target) { 422 if (_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.createRoot) { 423 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.createRoot)(target).render((0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(SettingsPage, null)); 424 } else { 425 // createRoot was added in React 18 WP 6.2 but we need to support 6.1 for the first release; 426 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_3__.render)((0,react__WEBPACK_IMPORTED_MODULE_0__.createElement)(SettingsPage, null), target); 427 } 428 } 429 } 430 }); 431 })(); 432 433 /******/ })() 434 ; 435 //# sourceMappingURL=settings.js.map 1 (()=>{"use strict";var e={n:t=>{var n=t&&t.__esModule?()=>t.default:()=>t;return e.d(n,{a:n}),n},d:(t,n)=>{for(var a in n)e.o(n,a)&&!e.o(t,a)&&Object.defineProperty(t,a,{enumerable:!0,get:n[a]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)};const t=window.React,n=window.wp.domReady;var a=e.n(n);const l=window.wp.element,i=window.wp.i18n,s=window.wp.components,o=window.wp.apiFetch;var d=e.n(o);const p=window.wp.data,r=window.wp.notices;function u(){var e;const n=(0,p.useSelect)((e=>e(r.store).getNotices()),[]),{removeNotice:a}=(0,p.useDispatch)(r.store),l=null!==(e=n?.filter((({type:e})=>"default"===e)))&&void 0!==e?e:[];return(0,t.createElement)(s.NoticeList,{notices:l,className:"components-editor-notices__snackbar",onRemove:a})}const c=()=>{const{loading:e,sdkKey:n,setSdkKey:a,apiKey:o,setApiKey:c,sendingEnabled:_,setSendingEnabled:h,autoSendEnabled:y,setAutoSendEnabled:m,initializationDisabled:b,setInitializationDisabled:g,initialInitializationDisabled:E,possiblePostTypes:v,enabledPostTypes:w,setEnabledPostTypes:S,saveSettings:f}=(()=>{const[e,t]=(0,l.useState)(!0),[n,a]=(0,l.useState)(""),[s,o]=(0,l.useState)(""),[u,c]=(0,l.useState)(""),[_,h]=(0,l.useState)(),[y,m]=(0,l.useState)(),[b]=(0,l.useState)([]),[g,E]=(0,l.useState)([]),[v,w]=(0,l.useState)(),[S,f]=(0,l.useState)(),{createSuccessNotice:P,createErrorNotice:k,removeNotice:N}=(0,p.useDispatch)(r.store);return(0,l.useEffect)((()=>{const e=d()({path:"/wp/v2/settings"}).then((e=>{var t;const n=null!==(t=e?.pushly)&&void 0!==t?t:{};o(void 0===n.sdk_key?null:n.sdk_key),c(void 0===n.api_key?null:n.api_key),h(void 0!==n.sending_enabled&&n.sending_enabled),m(void 0===n.auto_send_enabled?null:n.auto_send_enabled),E(void 0===n.enabled_post_types||0===n.enabled_post_types.length?["post"]:n.enabled_post_types),w(void 0!==n.initialization_disabled&&n.initialization_disabled),f(void 0!==n.initialization_disabled&&n.initialization_disabled),a(pushly_env.CDN_DOMAIN)})),n=d()({path:"/wp/v2/types"}).then((e=>{const t=["attachment","nav_menu_item"];Object.values(e).forEach((e=>{t.includes(e.slug)||e.slug.startsWith("wp_")||b.push(e)}))}));Promise.all([e,n]).then((()=>t(!1)))}),[]),(0,l.useEffect)((()=>{_&&null===y&&m(_)}),[_]),{loading:e,sdkKey:s,setSdkKey:o,apiKey:u,setApiKey:c,sendingEnabled:_,setSendingEnabled:h,autoSendEnabled:y,setAutoSendEnabled:m,possiblePostTypes:b,enabledPostTypes:g,setEnabledPostTypes:E,initializationDisabled:v,setInitializationDisabled:w,initialInitializationDisabled:S,saveSettings:()=>{if(_&&!u)return N("pushly"),void k("API Key must be provided when sending is enabled.",{id:"pushly",type:"default",explicitDismiss:!0});fetch(`https://${n}/domain-settings/${s}`).then((e=>{e.ok?e.json().then((e=>{d().use(d().createNonceMiddleware(pushly_env.API_NONCE)),d()({path:"/wp/v2/settings",method:"POST",data:{pushly:{domain_id:e.domain.id,sdk_key:s,api_key:u,sending_enabled:_,auto_send_enabled:y,initialization_disabled:v,enabled_post_types:g}}}).then((()=>{N("pushly"),P((0,i.__)("Settings saved","pushly"),{id:"pushly",type:"default"})})).catch((e=>{const t=e.data.details.pushly.message.replace("of pushly","").replace("pushly[","").replace("]","").replace(".","").split("_").map((e=>e.split("_").map((e=>e.charAt(0).toUpperCase()+e.slice(1))).join(" "))).join(" ").trim();N("pushly"),k(t,{id:"pushly",type:"default",explicitDismiss:!0})}))})):(N("pushly"),k((0,i.__)("The provided SDK Key is not valid","pushly"),{id:"pushly",type:"default",explicitDismiss:!0}))}))}}})();return e?null:(0,t.createElement)(t.Fragment,null,(0,t.createElement)("h1",null,"Pushly Settings"),(0,t.createElement)(u,null),(0,t.createElement)(s.Panel,null,(0,t.createElement)(s.PanelBody,{title:(0,i.__)("Configuration","pushly")},(0,t.createElement)(s.PanelRow,null,(0,t.createElement)(s.TextControl,{name:"sdkKey",label:(0,i.__)("SDK Key","pushly"),help:(0,i.__)("Enter the SDK Key associated to this Wordpress installation.","pushly"),value:n,onChange:e=>a(e.toString().trim()),type:"api-key",__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0}))),(0,t.createElement)(s.PanelBody,{title:(0,i.__)("Notifications","pushly")},(0,t.createElement)(s.PanelRow,null,(0,t.createElement)(s.ToggleControl,{name:"sendingEnabled",checked:_,help:(0,i.__)("Enable to add a new Notifications section to the post editor.","pushly"),label:(0,i.__)("Send Notifications for New Posts","pushly"),onChange:e=>h(!0===e),__nextHasNoMarginBottom:!0})),(0,t.createElement)(s.PanelRow,null,(0,t.createElement)(s.ToggleControl,{name:"autoSendEnabled",checked:_&&(y||null===y),help:(0,i.__)("Enable to automatically check the Send Notification checkbox in the post editor.","pushly"),label:(0,i.__)("Automatic Sending","pushly"),disabled:!_,onChange:e=>m(e),__nextHasNoMarginBottom:!0})),(0,t.createElement)(s.PanelRow,null,(0,t.createElement)("div",null,(0,t.createElement)(s.TextControl,{name:"apiKey",type:"password",label:(0,i.__)("API Key"),value:o,disabled:!_,onChange:e=>c(e.toString().trim()),__next40pxDefaultSize:!0,__nextHasNoMarginBottom:!0}),(0,t.createElement)("div",{class:"settings-help-text"},"Follow ",(0,t.createElement)("a",{href:"https://documentation.pushly.com/integration/web-browser-push/wordpress-plugin#step-1-create-your-wordpress-api-key",target:"_blank"}," the Pushly documentation")," to generate your API Key."))),(0,t.createElement)(s.PanelRow,null,(0,t.createElement)("div",null,(0,t.createElement)(s.FormTokenField,{label:"Enabled Post Types",value:w&&w.flatMap((e=>{const t=v.find((t=>t.slug===e));return t?`${t.name} (${t.slug})`:[]})),suggestions:v.map((e=>`${e.name} (${e.slug})`)),disabled:!_,onChange:e=>{const t=e.flatMap((e=>{const t=v.find((t=>`${t.name} (${t.slug})`===e));return t?t.slug:[]}));S(t)},__experimentalExpandOnFocus:!0,__nextHasNoMarginBottom:!0,__experimentalShowHowTo:!1}),(0,t.createElement)("div",{class:"settings-help-text"},"Select the post types that should be eligible for push notifications.")))),(0,t.createElement)(s.PanelBody,{title:(0,i.__)("Advanced","pushly"),initialOpen:E},(0,t.createElement)(s.ToggleControl,{name:"initializationDisabled",checked:b,help:(0,i.__)("When this box is checked the Pushly SDK will not automatically run on your site; Existing subscribers can still receive notifications.","pushly"),label:(0,i.__)("Disable SDK Initialization","pushly"),onChange:e=>g(e),__nextHasNoMarginBottom:!0}))),(0,t.createElement)(s.Button,{variant:"primary",onClick:f,__next40pxDefaultSize:!0},(0,i.__)("Save","pushly")))};a()((()=>{if("undefined"!=typeof pushly_env&&pushly_env?.CDN_DOMAIN&&pushly_env?.API_NONCE){const e=document.getElementById("pushly-settings");e&&(l.createRoot?(0,l.createRoot)(e).render((0,t.createElement)(c,null)):(0,l.render)((0,t.createElement)(c,null),e))}}))})(); -
pushly/trunk/environment.php
r3118516 r3129044 36 36 define( 'PUSHLY__CDN_DOMAIN', "pushlycdn.com" ); 37 37 } 38 39 if ( ! defined( 'PUSHLY__K_DOMAIN' ) ) { 40 define( 'PUSHLY__K_DOMAIN', "k.p-n.io" ); 41 } 42 43 if ( ! defined( 'PUSHLY__PLUGIN_VERSION' ) ) { 44 $version_array = get_file_data( PUSHLY__DIR . "/pushly.php", array( 'Version' ), 'plugin' ); 45 if ( ! empty( $version_array[0] ) ) { 46 define( "PUSHLY__PLUGIN_VERSION", $version_array[0] ); 47 } else { 48 define( "PUSHLY__PLUGIN_VERSION", '0.0.0' ); 49 } 50 } -
pushly/trunk/includes/admin/class-pushly-admin-post.php
r3118516 r3129044 1 1 <?php 2 2 3 if ( !defined('ABSPATH')) {4 exit;3 if ( ! defined( 'ABSPATH' ) ) { 4 exit; 5 5 } 6 6 7 class Pushly_Admin_Post 8 { 9 /** 10 * @var Pushly_Admin_Post 11 */ 12 private static $_instance; 13 14 /** 15 * Stores the pushly configuration options/settings 16 * 17 * @var array 18 */ 19 private $_options; 20 21 public static function instance() 22 { 23 if (empty(self::$_instance)) { 24 self::$_instance = new self(); 25 } 26 27 return self::$_instance; 28 } 29 30 /** 31 * Constructor 32 */ 33 public function __construct() 34 { 35 $this->_options = get_option('pushly'); 36 37 if ( 38 !empty($this->_options["sdk_key"]) 39 && !empty($this->_options["sending_enabled"]) 40 && !empty($this->_options["api_key"]) 41 ) { 42 // Meta Box 43 add_action('admin_init', [$this, 'register_post_meta']); 44 add_action('rest_api_init', [$this, 'register_post_meta']); 45 add_action('enqueue_block_editor_assets', [$this, 'enqueue_meta_box_assets_for_gutenberg'], 10, 3); 46 add_action('admin_enqueue_scripts', [$this, 'enqueue_meta_box_assets_for_classic'], 10, 3); 47 48 // Post Saving 49 add_action('transition_post_status', [$this, 'transition_post_status'], 10, 3); 50 add_action('add_meta_boxes', [$this, 'add_meta_box_for_classic'], 1); 51 add_action('admin_notices', [$this, 'emit_notice']); 52 53 // API Methods 54 add_action('rest_api_init', [$this, 'register_api_routes']); 55 } 56 } 57 58 59 /* Meta Box Methods */ 60 61 /** 62 * Registers all the meta properties that we will be storing on each individual post. 63 * 64 * pushly_notification_id: If a Pushly notification already exists for the post it will be stored in this property 65 * pushly_send_notification: Whether the "send notification" checkbox is checked or not 66 * pushly_customize_notification_content: Whether the "customize content" checkbox is checked or not 67 * pushly_custom_title: The user-supplied title that is used for the post's notification 68 * pushly_custom_body: The user-supplied body that is used for the post's notification 69 * pushly_customize_audience: Whether the "refine audience" checkbox is checked or not 70 * pushly_audience_ids: A list of segment IDs that the user has been chosen for this post's notification 71 */ 72 public function register_post_meta() 73 { 74 register_post_meta( 75 'post', 76 'pushly_needs_saving', 77 [ 78 'single' => true, 79 'type' => 'boolean', 80 'show_in_rest' => false, 81 ] 82 ); 83 84 register_post_meta( 85 'post', 86 'pushly_unique', 87 [ 88 'single' => true, 89 'type' => 'integer', 90 'default' => 0, 91 'show_in_rest' => true, 92 ] 93 ); 94 95 register_post_meta( 96 'post', 97 'pushly_notification_id', 98 [ 99 'single' => true, 100 'type' => 'string', 101 'show_in_rest' => false, 102 ] 103 ); 104 105 // whether the send notification checkbox is checked or not 106 register_post_meta( 107 'post', 108 'pushly_send_notification', 109 [ 110 'single' => true, 111 'type' => 'boolean', 112 'default' => !empty($this->_options['auto_send_enabled']), 113 'show_in_rest' => true, 114 'sanitize_callback' => 'wp_validate_boolean', 115 116 ] 117 ); 118 119 register_post_meta( 120 'post', 121 'pushly_customize_notification_content', 122 [ 123 'single' => true, 124 'type' => 'boolean', 125 'show_in_rest' => true, 126 'sanitize_callback' => 'wp_validate_boolean', 127 128 ] 129 ); 130 131 register_post_meta( 132 'post', 133 'pushly_custom_title', 134 [ 135 'single' => true, 136 'type' => 'string', 137 'show_in_rest' => true, 138 'sanitize_callback' => 'sanitize_text_field', 139 ] 140 ); 141 142 register_post_meta( 143 'post', 144 'pushly_custom_body', 145 [ 146 'single' => true, 147 'type' => 'string', 148 'show_in_rest' => true, 149 'sanitize_callback' => 'sanitize_text_field', 150 ] 151 ); 152 153 register_post_meta( 154 'post', 155 'pushly_customize_audience', 156 [ 157 'single' => true, 158 'type' => 'boolean', 159 'default' => false, 160 'show_in_rest' => true, 161 'sanitize_callback' => 'wp_validate_boolean', 162 163 ] 164 ); 165 166 register_post_meta( 167 'post', 168 'pushly_audience_ids', 169 [ 170 'single' => true, 171 'type' => 'array', 172 'show_in_rest' => array( 173 'schema' => array( 174 'type' => 'array', 175 'items' => array( 176 'type' => 'integer', 177 ), 178 ), 179 ), 180 'auth_callback' => function () { 181 return current_user_can('edit_posts'); 182 } 183 ] 184 ); 185 } 186 187 /** 188 * Enqueues the JS and CSS assets for Gutenberg editor page 189 * 190 * @param $admin_page 191 * 192 * @return void 193 */ 194 public function enqueue_meta_box_assets_for_gutenberg() 195 { 196 // this avoids errors when editing themes; there is probably a better way to prevent this. 197 if (get_current_screen()->base !== 'post') { 198 return; 199 } 200 201 $asset_file = PUSHLY__DIR_BUILD . '/meta-box.asset.php'; 202 203 if (!file_exists($asset_file)) { 204 return; 205 } 206 207 $asset = include $asset_file; 208 209 wp_enqueue_script( 210 'pushly', 211 plugins_url('build/meta-box.js', PUSHLY__DIR_BUILD), 212 $asset['dependencies'], 213 $asset['version'], 214 true 215 ); 216 217 wp_enqueue_style( 218 'pushly-style', 219 plugins_url('build/meta-box.css', PUSHLY__DIR_BUILD), 220 array_filter( 221 $asset['dependencies'], 222 function ($style) { 223 return wp_style_is($style, 'registered'); 224 } 225 ), 226 $asset['version'] 227 ); 228 } 229 230 /** 231 * Enqueues the JS and CSS assets for the Classic Editor 232 * 233 * @param $admin_page 234 * 235 * @return void 236 */ 237 public function enqueue_meta_box_assets_for_classic($admin_page) 238 { 239 if (!in_array($admin_page, ['post.php', 'post-new.php'])) { 240 return; 241 } 242 243 wp_enqueue_script( 244 'pushly', 245 plugins_url('includes/admin/views/classic/meta-box.js', PUSHLY__DIR_SRC), 246 ['jquery'] 247 ); 248 } 249 250 /** 251 * Adds meta box properties and enqueues asserts for Classic editor 252 * 253 * @param $admin_page 254 * 255 * @return void 256 */ 257 public function add_meta_box_for_classic($admin_page) 258 { 259 add_meta_box( 260 'pushly_meta_box', 261 __('Pushly Notifications'), 262 [$this, 'build_classic_meta_box'], 263 'post', 264 'side', 265 'default', 266 ['__back_compat_meta_box' => true] 267 ); 268 } 269 270 public function build_classic_meta_box($post) 271 { 272 $meta = get_post_meta($post->ID); 273 if ($meta) { 274 // meta fields when fetched via get_post_meta are automatically nested in an array, unwind this 275 foreach ($meta as &$v) { 276 $v = array_shift($v); 277 } 278 } 279 280 $current_options = get_option('pushly'); 281 $send_notification = !empty($meta['pushly_send_notification']) ? $meta['pushly_send_notification'] : $current_options['auto_send_enabled']; 282 $customize_notification_content = !empty($meta['pushly_customize_notification_content']) ? $meta['pushly_customize_notification_content'] : false; 283 $custom_title = !empty($meta['pushly_custom_title']) ? $meta['pushly_custom_title'] : null; 284 $custom_body = !empty($meta['pushly_custom_body']) ? $meta['pushly_custom_body'] : null; 285 286 require_once PUSHLY__DIR . '/includes/admin/views/classic/meta-box.php'; 287 echo build_classic_meta_box_html( 288 $send_notification, 289 $customize_notification_content, 290 $custom_title, 291 $custom_body 292 ); 293 } 294 295 /* POST Saving Methods */ 296 297 /** 298 * Fired when a Post's status transitions. 299 * 300 * Called by WordPress when wp_insert_post() is called. 301 * 302 * As wp_insert_post() is called by WordPress and the REST API whenever creating or updating a Post 303 * we can safely rely on this hook on any post save. 304 * 305 * @param string $new_status New Status 306 * @param string $old_status Old Status 307 * @param WP_Post $post Post 308 */ 309 public function transition_post_status($new_status, $old_status, $post) 310 { 311 /* 312 * We only want to run if the post type is `post`; In the future we may want to add additional types here 313 * or allow the client to add specific types via the settings page. 314 */ 315 $post_types = ['post']; 316 if (!in_array($post->post_type, $post_types)) { 317 return; 318 } 319 320 $this->on_should_save_notification($post, $old_status, $new_status); 321 } 322 323 /** 324 * Determines if the post should create/update a notification and hooks/calls the appropriate 325 * methods to invoke based on whether the request came from the Gutenberg, Rest API, 326 * or Classic Editor. 327 * 328 * We will never act on posts that are moving from `trash` to published. 329 * We will only act on posts that are in `publish` or `future` status. Posts in `future` status 330 * wll have their post meta set but will not be sent until they move to `publish` status. 331 * 332 * Because of these duplicate requests we have to implement logic to only act one of the requests. We will 333 * prefer to act on the Legacy request since more data is always available at that point. In order 334 * to accomplish this we will use a metadata flag `pushly_needs_saving` to conditionally invoke 335 * the desired method/hook only on the second request. 336 * 337 * @param string $new_status New Status 338 * @param string $old_status Old Status 339 * @param WP_Post $post Post 340 * 341 * @return void 342 */ 343 344 private function on_should_save_notification($post, $old_status, $new_status) 345 { 346 if ($old_status === 'trash') { 347 // to be safe, we never publish posts that were previously trashed 348 return; 349 } 350 351 if (get_post_meta($post->ID, 'pushly_needs_saving', true)) { 352 /** 353 * The previous request flagged that the request should be treated as a publish request (likely 354 * we're using Gutenberg and request to post.php was made after the REST API), do this now. 355 */ 356 delete_post_meta($post->ID, 'pushly_needs_saving'); 357 add_action('wp_insert_post', [$this, 'save_notification_from_post_id'], 999); 358 } else if (in_array($new_status, ['publish', 'future'])) { 359 /** 360 * We need to determine the source of the request and act accordingly depending on if 361 * it came in via Classic Editor, Gutenberg Editor, or REST API. 362 */ 363 if (!defined('REST_REQUEST') || !REST_REQUEST) { 364 /** 365 * The request came via the Classic Editor or a transition post background job 366 * 367 * Metadata is included in the call to wp_insert_post(), meaning that it's saved to the Post before 368 * we use it. So we don't need to do anything special here. 369 * 370 * We can just directly hook `wp_insert_post` if the post came in this way. 371 */ 372 add_action('wp_insert_post', [$this, 'save_notification_from_post_id'], 999); 373 } else if ($this->is_gutenberg_post($post)) { 374 /** 375 * The request came via the Gutenberg Editor. 376 * 377 * If Gutenberg is being used two requests may be sent: 378 * - a REST API request that includes the post data and metadata registered *in* Gutenberg 379 * - a Legacy request including metadata registered *outside* of Gutenberg (e.g., `add_meta_box` data) 380 * 381 * This is where we will define our `pushly_needs_saving` meta flag to be handled hy the 382 * subsequent request. 383 */ 384 update_post_meta($post->ID, 'pushly_needs_saving', 1); 385 } else { 386 /** 387 * The request came via the REST API. 388 * 389 * If this is a REST API request, we can't use the `wp_insert_post` action because any metadata 390 * included in the REST API request is *not* included in the call to wp_insert_post(). Instead, we 391 * can use `rest_after_insert_*` which guarantees all metadata is saved before invocation. 392 */ 393 add_action("rest_after_insert_{$post->post_type}", [$this, 'save_notification_from_post'], 99, 2); 394 } 395 } 396 } 397 398 /** 399 * Helper function to determine if the Post is using the Gutenberg Editor. 400 * 401 * @param WP_Post $post Post 402 * 403 * @return bool Whether the post was created using Gutenberg 404 */ 405 private function is_gutenberg_post($post) 406 { 407 408 // This will fail if a Post is created or updated with no content and only a title. 409 if (strpos($post->post_content, '<!-- wp:') === false) { 410 return false; 411 } 412 413 return true; 414 } 415 416 /** 417 * Intermediate function to always pull the post from the DB and forward on to the primary 418 * `pushly_save_notification_from_post` responsible for saving the notification. 419 * 420 * @param int $post_id Post ID 421 * 422 * @return mixed WP_Error|Notification 423 */ 424 public function save_notification_from_post_id($post_id) 425 { 426 $post = get_post($post_id); 427 428 return $this->save_notification_from_post($post); 429 } 430 431 /** 432 * Primary function responsible for building the notification payload that will 433 * ultimately be sent to the API. 434 * 435 * @param $post 436 * 437 * @return Notification|void|WP_Error 438 */ 439 public function save_notification_from_post($post) 440 { 441 $meta = $this->get_request_post_meta($post); 442 443 /* 444 * We *always* require `pushly_send_notification` to be set in the post meta directly from a form/ajax 445 * action. This ensures that the post was created in the WordPress editor and not via a 3rd 446 * party plugin. 447 * 448 * If we want to semd notifications from posts created by 3rd party plugins this logic will need to be 449 * reworked to incorporate `auto_send_status` and a new meta field added only on default editor meta boxes. 450 */ 451 if (empty($meta['pushly_send_notification'])) { 452 // the notification box was not checked on the editor, post should be marked as not sending 453 update_post_meta($post->ID, 'pushly_send_notification', false); 454 455 // nothing else to do, short circuit 456 return; 457 } 458 459 /* 460 * Retrieve `pushly_notification_id` from the post. If this is set then a notification already 461 * exists for this post, so we will exit; This helps guard against duplicate notification 462 * creation. 463 */ 464 $pushly_notification_id = get_post_meta($post->ID, 'pushly_notification_id', true); 465 if (!empty($pushly_notification_id)) { 466 // since we never pre-schedule notifications we can safely exit here 467 return; 468 } 469 470 // set metadata that the user has chosen to send a notification for this post 471 update_post_meta($post->ID, 'pushly_send_notification', true); 472 473 // determine if we should use customized title/body or derive from post 474 if (!empty($meta['pushly_customize_notification_content']) 475 && !empty($meta['pushly_custom_title']) 476 ) { 477 $title = $meta['pushly_custom_title']; 478 $body = !empty($meta['pushly_custom_body']) ? $meta['pushly_custom_body'] : null; 479 480 update_post_meta($post->ID, 'pushly_customize_notification_content', true); 481 update_post_meta($post->ID, 'pushly_custom_title', $title); 482 update_post_meta($post->ID, 'pushly_custom_body', $body); 483 } else { 484 $title = $post->post_title; 485 // we never use a body unless the user has explicitly provided it 486 $body = null; 487 488 update_post_meta($post->ID, 'pushly_customize_notification_content', false); 489 } 490 491 $notification_meta = []; 492 if (!empty($meta['pushly_customize_audience']) && !empty($meta['pushly_audience_ids'])) { 493 $notification_meta['segment_ids'] = $meta['pushly_audience_ids']; 494 495 update_post_meta($post->ID, 'pushly_customize_audience', true); 496 update_post_meta($post->ID, 'pushly_audience_ids', $meta['pushly_audience_ids']); 497 } 498 499 // from here only needs to run if the post is in "publish" state - i.e., time to send a notification 500 if ($post->post_status === "publish") { 501 $notification_payload = [ 502 'ID' => $post->ID, 503 'title' => $title, 504 'body' => $body, 505 'landing_url' => get_permalink($post->ID), 506 'schedule_date' => $post->post_date_gmt, 507 ]; 508 509 // get tags for the post - these will be used as notification keywords 510 $tags = get_the_tags($post->ID); 511 if (!empty($tags)) { 512 $notification_payload['tag_names'] = array_map(function ($value) { 513 return $value->name; 514 }, $tags); 515 } 516 517 // get categories for the post - these will be used as notification keywords 518 $categories = get_the_category($post->ID); 519 if (!empty($categories)) { 520 $notification_payload['category_names'] = array_map(function ($value) { 521 return $value->name; 522 }, $categories); 523 } 524 525 // get the featured image ID from the post 526 if (has_post_thumbnail($post->ID)) { 527 $notification_payload['image_id'] = get_post_thumbnail_id($post->ID); 528 } 529 530 // build the Notification object that will be sent via the API 531 $notification = Notification::from_post($notification_payload, $notification_meta); 532 $response = $this->api_save_notification($notification); 533 534 $error = null; 535 if (is_wp_error($response)) { 536 $error = $response->get_error_message(); 537 } else if (!empty($response['status'])) { 538 if ($response['status'] === 'success') { 539 update_post_meta($post->ID, 'pushly_notification_id', $response['data']['id']); 540 } else if ($response['status'] === 'error' && !empty($response['message'])) { 541 $error = $response['message']; 542 } 543 } 544 545 /* 546 * Note: this only works for classic editor, to do this for Gutenberg we would have 547 * to either expose an API to fetch notices post-save or some other hack. We have 548 * left no feedback in the Gutenberg editor for the time-being. 549 */ 550 set_transient("pushly_notice", $error); 551 } 552 } 553 554 /* Post Util Methods */ 555 556 /** 557 * We prefer to load the post meta directly from the request as the post meta is not always 558 * 100% up-to-date when retrieving from the database especially before `rest_after_insert_post` 559 * was introduced. 560 * 561 * We will fall back to the database to account for post status transitions. 562 * 563 * @param $post 564 * 565 * @return array|mixed 566 */ 567 protected function get_request_post_meta( 568 $post 569 ) 570 { 571 $meta = []; 572 if (!empty($_POST)) { 573 // Requests from the Classic Editor (and quick edit) use $_POST 574 if (!current_user_can('edit_posts')) { 575 return []; 576 } 577 578 /* 579 * Ensure nonces are set for Classic editor 580 * 581 * First, check if this is from quick edit (inlineditnonce) 582 * Next, check if this is from pushly meta box 583 * 584 * If neither of these operations validates a nonce we shouldn't do anything 585 */ 586 if (!check_ajax_referer('inlineeditnonce', '_inline_edit', false)) { 587 // need to verify valid nonce 588 589 if (empty($_POST['pushly_meta_box_nonce']) 590 || !wp_verify_nonce($_POST['pushly_meta_box_nonce'], 'pushly_save_notification_meta_box') 591 ) { 592 return $meta; 593 } 594 } 595 596 foreach ($_POST as $key => $value) { 597 if (substr($key, 0, 7) === "pushly_") { 598 $meta[$key] = $value; 599 } 600 } 601 } else { 602 /* 603 * Requests that use the API/Gutenberg use a JSON post body rather than a form POST. We will 604 * decode the body here into an associative array and assign `meta` the same way it 605 * would have come in from $_POST. 606 */ 607 $json = file_get_contents('php://input'); 608 if ($json) { 609 $json = json_decode($json, true); 610 if (isset($json['meta'])) { 611 $meta = $json['meta']; 612 } 613 } 614 } 615 616 if (empty($meta)) { 617 // likely this is a scheduled post moving to future or an after rest insert, so we grab the existing meta 618 $meta = get_post_meta($post->ID); 619 620 if ($meta) { 621 // meta fields when fetched via get_post_meta are automatically nested in an array, unwind this 622 foreach ($meta as &$v) { 623 $v = array_shift($v); 624 } 625 626 // audiences are serialized when fetched from get_post_meta, deserialize to a php array 627 if (!empty($meta['pushly_customize_audience']) && !empty($meta['pushly_audience_ids'])) { 628 $meta['pushly_audience_ids'] = unserialize($meta['pushly_audience_ids']); 629 } 630 } 631 } 632 633 return $meta; 634 } 635 636 /** 637 * Emits notice HTML that will be shown when the page is rendered 638 * 639 * @return void 640 */ 641 public function emit_notice() 642 { 643 if (!$this->is_using_gutenberg()) { 644 $screen = get_current_screen(); 645 // Only render this notice in the post editor. 646 if (!$screen || 'post' !== $screen->base) { 647 return; 648 } 649 650 $message = get_transient('pushly_notice'); 651 if ($message) { 652 delete_transient('pushly_notice'); 653 654 printf( 655 '<div class="notice error is-dismissible"><p>Pushly Notifications: %s</p></div>', 656 esc_html(__("Failed to Send - ") . $message) 657 ); 658 } 659 } 660 } 661 662 /** 663 * Determines if the site is using Gutenberg or not. 664 * 665 * This method will not work if it is called too early in the WordPress initialization 666 * process. It must be used after `replace_editor` hook (or any subsequent hook) is executed. 667 * 668 * @See https://wordpress.stackexchange.com/a/309955 669 * 670 * @return bool 671 */ 672 protected function is_using_gutenberg() 673 { 674 if (function_exists('is_gutenberg_page') && is_gutenberg_page()) { 675 return true; 676 } 677 678 $current_screen = get_current_screen(); 679 if (method_exists($current_screen, 'is_block_editor') && $current_screen->is_block_editor()) { 680 return true; 681 } 682 683 return false; 684 } 685 686 /* API Methods */ 687 public function register_api_routes() 688 { 689 register_rest_route( 690 'pushly/v1', 691 '/segments', 692 array( 693 'methods' => 'GET', 694 'permission_callback' => function () { 695 return current_user_can('edit_posts'); 696 }, 697 'callback' => function ($request) { 698 return rest_ensure_response($this->api_get_segments()); 699 }, 700 ) 701 ); 702 } 703 704 protected function api_get_segments( 705 $options = null 706 ) 707 { 708 $options = Pushly_Admin_Util::get_api_options(); 709 $domain_id = $options['domain_id']; 710 $api_key = Pushly_Admin_Util::decrypt_api_key($options['sdk_key'], $options['sdk_key'], $options['api_key']); 711 712 $url = "https://" . PUSHLY__API_DOMAIN . "/domains/{$domain_id}/segments?pagination=0&source=standard&include_default=0&fields=id,name"; 713 $response = wp_remote_get($url, [ 714 'headers' => array( 715 'X-API-KEY' => $api_key 716 ) 717 ]); 718 if (is_wp_error($response)) { 719 return new WP_Error( 720 'internal_error', 721 'Unable to fetch segments from Pushly API', 722 array('status' => 503) 723 ); 724 } else { 725 return json_decode($response['body'], true); 726 } 727 } 728 729 730 protected function api_save_notification( 731 $notification 732 ) 733 { 734 $options = Pushly_Admin_Util::get_api_options(); 735 $domain_id = $options['domain_id']; 736 $api_key = Pushly_Admin_Util::decrypt_api_key($options['sdk_key'], $options['sdk_key'], $options['api_key']); 737 738 if (!empty($notification->id)) { 739 // updating notification 740 $url = "https://" . PUSHLY__API_DOMAIN . "/domains/{$domain_id}/notifications/{$notification->id}"; 741 $response = wp_remote_request($url, array( 742 'method' => 'PATCH', 743 'body' => wp_json_encode($notification), 744 'headers' => array( 745 'Content-Type' => 'application/json', 746 'X-API-KEY' => $api_key, 747 ) 748 )); 749 } else { 750 // creating notification 751 $url = "https://" . PUSHLY__API_DOMAIN . "/domains/{$domain_id}/notifications"; 752 $response = wp_remote_post($url, array( 753 'body' => wp_json_encode($notification), 754 'headers' => array( 755 'Content-Type' => 'application/json', 756 'X-API-KEY' => $api_key, 757 ) 758 )); 759 } 760 761 if (is_wp_error($response)) { 762 return new WP_Error( 763 'internal_error', 764 'Failed to save Pushly notification', 765 array('status' => 503) 766 ); 767 } else { 768 return json_decode($response['body'], true); 769 } 770 } 7 class Pushly_Admin_Post { 8 /** 9 * @var Pushly_Admin_Post 10 */ 11 private static $_instance; 12 13 /** 14 * Stores the pushly configuration options/settings 15 * 16 * @var array 17 */ 18 private $_options; 19 20 public static function instance() { 21 if ( empty( self::$_instance ) ) { 22 self::$_instance = new self(); 23 } 24 25 return self::$_instance; 26 } 27 28 /** 29 * Constructor 30 */ 31 public function __construct() { 32 $this->_options = get_option( 'pushly' ); 33 34 if ( 35 ! empty( $this->_options ) 36 && ! empty( $this->_options["sdk_key"] ) 37 && ! empty( $this->_options["sending_enabled"] ) 38 && ! empty( $this->_options["api_key"] ) 39 ) { 40 // Meta Box 41 add_action( 'admin_init', [ $this, 'register_post_meta' ] ); 42 add_action( 'rest_api_init', [ $this, 'register_post_meta' ] ); 43 add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_meta_box_assets_for_gutenberg' ], 10, 3 ); 44 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_meta_box_assets_for_classic' ], 10, 3 ); 45 46 // Post Saving 47 add_action( 'transition_post_status', [ $this, 'transition_post_status' ], 10, 3 ); 48 add_action( 'add_meta_boxes', [ $this, 'add_meta_box_for_classic' ], 1 ); 49 add_action( 'admin_notices', [ $this, 'emit_notice' ] ); 50 51 // API Methods 52 add_action( 'rest_api_init', [ $this, 'register_api_routes' ] ); 53 } 54 } 55 56 57 /* Meta Box Methods */ 58 59 /** 60 * Registers all the meta properties that we will be storing on each individual post. 61 * 62 * pushly_notification_id: If a Pushly notification already exists for the post it will be stored in this property 63 * pushly_send_notification: Whether the "send notification" checkbox is checked or not 64 * pushly_customize_notification_content: Whether the "customize content" checkbox is checked or not 65 * pushly_custom_title: The user-supplied title that is used for the post's notification 66 * pushly_custom_body: The user-supplied body that is used for the post's notification 67 * pushly_customize_audience: Whether the "refine audience" checkbox is checked or not 68 * pushly_audience_ids: A list of segment IDs that the user has been chosen for this post's notification 69 */ 70 public function register_post_meta() { 71 if ( ! empty( $this->_options["enabled_post_types"] ) ) { 72 foreach ( $this->_options["enabled_post_types"] as $post_type ) { 73 register_post_meta( 74 $post_type, 75 'pushly_needs_saving', 76 [ 77 'single' => true, 78 'type' => 'boolean', 79 'show_in_rest' => false, 80 ] 81 ); 82 83 register_post_meta( 84 $post_type, 85 'pushly_unique', 86 [ 87 'single' => true, 88 'type' => 'integer', 89 'default' => 0, 90 'show_in_rest' => true, 91 ] 92 ); 93 94 register_post_meta( 95 $post_type, 96 'pushly_notification_id', 97 [ 98 'single' => true, 99 'type' => 'string', 100 'show_in_rest' => false, 101 ] 102 ); 103 104 // whether the send notification checkbox is checked or not 105 register_post_meta( 106 $post_type, 107 'pushly_send_notification', 108 [ 109 'single' => true, 110 'type' => 'boolean', 111 'default' => ! empty( $this->_options['auto_send_enabled'] ), 112 'show_in_rest' => true, 113 'sanitize_callback' => 'wp_validate_boolean', 114 115 ] 116 ); 117 118 register_post_meta( 119 $post_type, 120 'pushly_customize_notification_content', 121 [ 122 'single' => true, 123 'type' => 'boolean', 124 'show_in_rest' => true, 125 'sanitize_callback' => 'wp_validate_boolean', 126 127 ] 128 ); 129 130 register_post_meta( 131 $post_type, 132 'pushly_custom_title', 133 [ 134 'single' => true, 135 'type' => 'string', 136 'show_in_rest' => true, 137 'sanitize_callback' => 'sanitize_text_field', 138 ] 139 ); 140 141 register_post_meta( 142 $post_type, 143 'pushly_custom_body', 144 [ 145 'single' => true, 146 'type' => 'string', 147 'show_in_rest' => true, 148 'sanitize_callback' => 'sanitize_text_field', 149 ] 150 ); 151 152 register_post_meta( 153 $post_type, 154 'pushly_customize_audience', 155 [ 156 'single' => true, 157 'type' => 'boolean', 158 'default' => false, 159 'show_in_rest' => true, 160 'sanitize_callback' => 'wp_validate_boolean', 161 162 ] 163 ); 164 165 register_post_meta( 166 $post_type, 167 'pushly_audience_ids', 168 [ 169 'single' => true, 170 'type' => 'array', 171 'show_in_rest' => array( 172 'schema' => array( 173 'type' => 'array', 174 'items' => array( 175 'type' => 'integer', 176 ), 177 ), 178 ), 179 'auth_callback' => function () { 180 return current_user_can( 'edit_posts' ); 181 } 182 ] 183 ); 184 } 185 } 186 } 187 188 /** 189 * Enqueues the JS and CSS assets for Gutenberg editor page 190 * 191 * @param $admin_page 192 * 193 * @return void 194 */ 195 public function enqueue_meta_box_assets_for_gutenberg() { 196 if ( ! empty( $this->_options["enabled_post_types"] ) ) { 197 // this avoids errors when editing themes; there is probably a better way to prevent this. 198 $current_screen = get_current_screen(); 199 if ( $current_screen->base !== 'post' || ! in_array( $current_screen->post_type, $this->_options["enabled_post_types"] ) ) { 200 return; 201 } 202 203 $asset_file = PUSHLY__DIR_BUILD . '/meta-box.asset.php'; 204 205 if ( ! file_exists( $asset_file ) ) { 206 return; 207 } 208 209 $asset = include $asset_file; 210 211 wp_enqueue_script( 212 'pushly', 213 plugins_url( 'build/meta-box.js', PUSHLY__DIR_BUILD ), 214 $asset['dependencies'], 215 $asset['version'], 216 true 217 ); 218 219 wp_enqueue_style( 220 'pushly-style', 221 plugins_url( 'build/meta-box.css', PUSHLY__DIR_BUILD ), 222 array_filter( 223 $asset['dependencies'], 224 function ( $style ) { 225 return wp_style_is( $style, 'registered' ); 226 } 227 ), 228 $asset['version'] 229 ); 230 } 231 } 232 233 /** 234 * Enqueues the JS and CSS assets for the Classic Editor 235 * 236 * @param $admin_page 237 * 238 * @return void 239 */ 240 public function enqueue_meta_box_assets_for_classic( $admin_page ) { 241 if ( ! empty( $this->_options["enabled_post_types"] ) ) { 242 $current_screen = get_current_screen(); 243 if ( $current_screen->base !== 'post' || ! in_array( $current_screen->post_type, $this->_options["enabled_post_types"] ) ) { 244 return; 245 } 246 247 wp_enqueue_script( 248 'pushly', 249 plugins_url( 'includes/admin/views/classic/meta-box.js', PUSHLY__DIR_SRC ), 250 [ 'jquery' ] 251 ); 252 } 253 } 254 255 /** 256 * Adds meta box properties and enqueues asserts for Classic editor 257 * 258 * @param $admin_page 259 * 260 * @return void 261 */ 262 public function add_meta_box_for_classic( $admin_page ) { 263 if ( ! empty( $this->_options["enabled_post_types"] ) ) { 264 add_meta_box( 265 'pushly_meta_box', 266 __( 'Pushly Notifications' ), 267 [ $this, 'build_classic_meta_box' ], 268 $this->_options["enabled_post_types"], 269 'side', 270 'default', 271 [ '__back_compat_meta_box' => true ] 272 ); 273 } 274 } 275 276 public function build_classic_meta_box( $post ) { 277 $meta = get_post_meta( $post->ID ); 278 if ( $meta ) { 279 // meta fields when fetched via get_post_meta are automatically nested in an array, unwind this 280 foreach ( $meta as &$v ) { 281 $v = array_shift( $v ); 282 } 283 } 284 285 $current_options = get_option( 'pushly' ); 286 $send_notification = ! empty( $meta['pushly_send_notification'] ) ? $meta['pushly_send_notification'] : $current_options['auto_send_enabled']; 287 $customize_notification_content = ! empty( $meta['pushly_customize_notification_content'] ) ? $meta['pushly_customize_notification_content'] : false; 288 $custom_title = ! empty( $meta['pushly_custom_title'] ) ? $meta['pushly_custom_title'] : null; 289 $custom_body = ! empty( $meta['pushly_custom_body'] ) ? $meta['pushly_custom_body'] : null; 290 291 require_once PUSHLY__DIR . '/includes/admin/views/classic/meta-box.php'; 292 echo build_classic_meta_box_html( 293 $send_notification, 294 $customize_notification_content, 295 $custom_title, 296 $custom_body 297 ); 298 } 299 300 /* POST Saving Methods */ 301 302 /** 303 * Fired when a Post's status transitions. 304 * 305 * Called by WordPress when wp_insert_post() is called. 306 * 307 * As wp_insert_post() is called by WordPress and the REST API whenever creating or updating a Post 308 * we can safely rely on this hook on any post save. 309 * 310 * @param string $new_status New Status 311 * @param string $old_status Old Status 312 * @param WP_Post $post Post 313 */ 314 public function transition_post_status( $new_status, $old_status, $post ) { 315 if ( ! empty( $this->_options["enabled_post_types"] ) ) { 316 if ( ! in_array( $post->post_type, $this->_options["enabled_post_types"] ) ) { 317 if ( in_array( $new_status, [ 'publish', 'future' ] ) ) { 318 Pushly_Admin_Util::log_to_event_stream( "disabled_post_type", "Did not send notification due to `{$post->post_type}` not being an enabled post type." ); 319 } 320 321 return; 322 } 323 324 $this->on_should_save_notification( $post, $old_status, $new_status ); 325 } else { 326 Pushly_Admin_Util::log_to_event_stream( "empty_enabled_post_types", "Did not send notification due to empty enabled_post_types." ); 327 } 328 } 329 330 /** 331 * Determines if the post should create/update a notification and hooks/calls the appropriate 332 * methods to invoke based on whether the request came from the Gutenberg, Rest API, 333 * or Classic Editor. 334 * 335 * We will never act on posts that are moving from `trash` to published. 336 * We will only act on posts that are in `publish` or `future` status. Posts in `future` status 337 * wll have their post meta set but will not be sent until they move to `publish` status. 338 * 339 * Because of these duplicate requests we have to implement logic to only act one of the requests. We will 340 * prefer to act on the Legacy request since more data is always available at that point. In order 341 * to accomplish this we will use a metadata flag `pushly_needs_saving` to conditionally invoke 342 * the desired method/hook only on the second request. 343 * 344 * @param string $new_status New Status 345 * @param string $old_status Old Status 346 * @param WP_Post $post Post 347 * 348 * @return void 349 */ 350 351 private function on_should_save_notification( $post, $old_status, $new_status ) { 352 if ( $old_status === 'trash' ) { 353 // to be safe, we never publish posts that were previously trashed 354 return; 355 } 356 357 if ( get_post_meta( $post->ID, 'pushly_needs_saving', true ) ) { 358 /** 359 * The previous request flagged that the request should be treated as a publish request (likely 360 * we're using Gutenberg and request to post.php was made after the REST API), do this now. 361 */ 362 delete_post_meta( $post->ID, 'pushly_needs_saving' ); 363 add_action( 'wp_insert_post', [ $this, 'save_notification_from_post_id' ], 999 ); 364 } else if ( in_array( $new_status, [ 'publish', 'future' ] ) ) { 365 /** 366 * We need to determine the source of the request and act accordingly depending on if 367 * it came in via Classic Editor, Gutenberg Editor, or REST API. 368 */ 369 if ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) { 370 /** 371 * The request came via the Classic Editor or a transition post background job 372 * 373 * Metadata is included in the call to wp_insert_post(), meaning that it's saved to the Post before 374 * we use it. So we don't need to do anything special here. 375 * 376 * We can just directly hook `wp_insert_post` if the post came in this way. 377 */ 378 add_action( 'wp_insert_post', [ $this, 'save_notification_from_post_id' ], 999 ); 379 } else if ( $this->is_gutenberg_post( $post ) ) { 380 /** 381 * The request came via the Gutenberg Editor. 382 * 383 * If Gutenberg is being used two requests may be sent: 384 * - a REST API request that includes the post data and metadata registered *in* Gutenberg 385 * - a Legacy request including metadata registered *outside* of Gutenberg (e.g., `add_meta_box` data) 386 * 387 * This is where we will define our `pushly_needs_saving` meta flag to be handled hy the 388 * subsequent request. 389 */ 390 update_post_meta( $post->ID, 'pushly_needs_saving', 1 ); 391 } else { 392 /** 393 * The request came via the REST API. 394 * 395 * If this is a REST API request, we can't use the `wp_insert_post` action because any metadata 396 * included in the REST API request is *not* included in the call to wp_insert_post(). Instead, we 397 * can use `rest_after_insert_*` which guarantees all metadata is saved before invocation. 398 */ 399 add_action( "rest_after_insert_{$post->post_type}", [ $this, 'save_notification_from_post' ], 99, 2 ); 400 } 401 } 402 } 403 404 /** 405 * Helper function to determine if the Post is using the Gutenberg Editor. 406 * 407 * @param WP_Post $post Post 408 * 409 * @return bool Whether the post was created using Gutenberg 410 */ 411 private function is_gutenberg_post( $post ) { 412 413 // This will fail if a Post is created or updated with no content and only a title. 414 if ( strpos( $post->post_content, '<!-- wp:' ) === false ) { 415 return false; 416 } 417 418 return true; 419 } 420 421 /** 422 * Intermediate function to always pull the post from the DB and forward on to the primary 423 * `pushly_save_notification_from_post` responsible for saving the notification. 424 * 425 * @param int $post_id Post ID 426 * 427 * @return mixed WP_Error|Notification 428 */ 429 public function save_notification_from_post_id( $post_id ) { 430 $post = get_post( $post_id ); 431 432 return $this->save_notification_from_post( $post ); 433 } 434 435 /** 436 * Primary function responsible for building the notification payload that will 437 * ultimately be sent to the API. 438 * 439 * @param $post 440 * 441 * @return Notification|void|WP_Error 442 */ 443 public function save_notification_from_post( $post ) { 444 try { 445 446 $meta = $this->get_request_post_meta( $post ); 447 448 /* 449 * We *always* require `pushly_send_notification` to be set in the post meta directly from a form/ajax 450 * action. This ensures that the post was created in the WordPress editor and not via a 3rd 451 * party plugin. 452 * 453 * If we want to semd notifications from posts created by 3rd party plugins this logic will need to be 454 * reworked to incorporate `auto_send_status` and a new meta field added only on default editor meta boxes. 455 */ 456 if ( empty( $meta['pushly_send_notification'] ) ) { 457 Pushly_Admin_Util::log_to_event_stream( "send_notification_status", "Did not send notification due to false pushly_send_notification status." ); 458 459 // the notification box was not checked on the editor, post should be marked as not sending 460 update_post_meta( $post->ID, 'pushly_send_notification', false ); 461 462 // nothing else to do, short circuit 463 return; 464 } 465 466 /* 467 * Retrieve `pushly_notification_id` from the post. If this is set then a notification already 468 * exists for this post, so we will exit; This helps guard against duplicate notification 469 * creation. 470 */ 471 $pushly_notification_id = get_post_meta( $post->ID, 'pushly_notification_id', true ); 472 if ( ! empty( $pushly_notification_id ) ) { 473 Pushly_Admin_Util::log_to_event_stream( "post_already_sent", "Did not send notification due to notification already being sent for post ({$pushly_notification_id})." ); 474 475 // since we never pre-schedule notifications we can safely exit here 476 return; 477 } 478 479 // set metadata that the user has chosen to send a notification for this post 480 update_post_meta( $post->ID, 'pushly_send_notification', true ); 481 482 // determine if we should use customized title/body or derive from post 483 if ( ! empty( $meta['pushly_customize_notification_content'] ) 484 && ! empty( $meta['pushly_custom_title'] ) 485 ) { 486 $title = $meta['pushly_custom_title']; 487 $body = ! empty( $meta['pushly_custom_body'] ) ? $meta['pushly_custom_body'] : null; 488 489 update_post_meta( $post->ID, 'pushly_customize_notification_content', true ); 490 update_post_meta( $post->ID, 'pushly_custom_title', $title ); 491 update_post_meta( $post->ID, 'pushly_custom_body', $body ); 492 } else { 493 $title = $post->post_title; 494 // we never use a body unless the user has explicitly provided it 495 $body = null; 496 497 update_post_meta( $post->ID, 'pushly_customize_notification_content', false ); 498 } 499 500 $notification_meta = []; 501 if ( ! empty( $meta['pushly_customize_audience'] ) && ! empty( $meta['pushly_audience_ids'] ) ) { 502 $notification_meta['segment_ids'] = $meta['pushly_audience_ids']; 503 504 update_post_meta( $post->ID, 'pushly_customize_audience', true ); 505 update_post_meta( $post->ID, 'pushly_audience_ids', $meta['pushly_audience_ids'] ); 506 } 507 508 // from here only needs to run if the post is in "publish" state - i.e., time to send a notification 509 if ( $post->post_status === "publish" ) { 510 $notification_payload = [ 511 'ID' => $post->ID, 512 'title' => $title, 513 'body' => $body, 514 'landing_url' => get_permalink( $post->ID ), 515 'schedule_date' => $post->post_date_gmt, 516 ]; 517 518 // get tags for the post - these will be used as notification keywords 519 $tags = get_the_tags( $post->ID ); 520 if ( ! empty( $tags ) ) { 521 $notification_payload['tag_names'] = array_map( function ( $value ) { 522 return $value->name; 523 }, $tags ); 524 } 525 526 // get categories for the post - these will be used as notification keywords 527 $categories = get_the_category( $post->ID ); 528 if ( ! empty( $categories ) ) { 529 $notification_payload['category_names'] = array_map( function ( $value ) { 530 return $value->name; 531 }, $categories ); 532 } 533 534 // get the featured image ID from the post 535 if ( has_post_thumbnail( $post->ID ) ) { 536 $notification_payload['image_id'] = get_post_thumbnail_id( $post->ID ); 537 } 538 539 // build the Notification object that will be sent via the API 540 $notification = Notification::from_post( $notification_payload, $notification_meta ); 541 if ( $notification ) { 542 $response = $this->api_save_notification( $notification ); 543 if ( ! empty( $response['data']['id'] ) ) { 544 update_post_meta( $post->ID, 'pushly_notification_id', $response['data']['id'] ); 545 } 546 } 547 } else { 548 Pushly_Admin_Util::log_to_event_stream( "invalid_post_status", "Did not send notification due to invalid post status ({$post->post_status})." ); 549 } 550 } catch ( Exception $e ) { 551 Pushly_Admin_Util::log_to_event_stream( "unknown_exception", "Encountered unknown exception during send: {$e->getMessage()}" ); 552 } 553 } 554 555 /* Post Util Methods */ 556 557 /** 558 * We prefer to load the post meta directly from the request as the post meta is not always 559 * 100% up-to-date when retrieving from the database especially before `rest_after_insert_post` 560 * was introduced. 561 * 562 * We will fall back to the database to account for post status transitions. 563 * 564 * @param $post 565 * 566 * @return array|mixed 567 */ 568 protected function get_request_post_meta( 569 $post 570 ) { 571 $meta = []; 572 if ( ! empty( $_POST ) ) { 573 // Requests from the Classic Editor (and quick edit) use $_POST 574 if ( ! current_user_can( 'edit_posts' ) ) { 575 Pushly_Admin_Util::log_to_event_stream( "insufficient_capability", "Did not send notification due to missing edit_posts capability." ); 576 577 return []; 578 } 579 580 /* 581 * Ensure nonces are set for Classic editor 582 * 583 * First, check if this is from quick edit (inlineditnonce) 584 * Next, check if this is from pushly meta box 585 * 586 * If neither of these operations validates a nonce we shouldn't do anything 587 */ 588 if ( ! check_ajax_referer( 'inlineeditnonce', '_inline_edit', false ) ) { 589 // need to verify valid nonce 590 591 if ( empty( $_POST['pushly_meta_box_nonce'] ) 592 || ! wp_verify_nonce( $_POST['pushly_meta_box_nonce'], 'pushly_save_notification_meta_box' ) 593 ) { 594 Pushly_Admin_Util::log_to_event_stream( "invalid_nonce", "Did not send notification due to invalid pushly_meta_box_nonce nonce." ); 595 596 return $meta; 597 } 598 } 599 600 foreach ( $_POST as $key => $value ) { 601 if ( str_starts_with( $key, "pushly_" ) ) { 602 $meta[ $key ] = $value; 603 } 604 } 605 } else { 606 /* 607 * Requests that use the API/Gutenberg use a JSON post body rather than a form POST. We will 608 * decode the body here into an associative array and assign `meta` the same way it 609 * would have come in from $_POST. 610 */ 611 $json = file_get_contents( 'php://input' ); 612 if ( $json ) { 613 $json = json_decode( $json, true ); 614 if ( isset( $json['meta'] ) ) { 615 $meta = $json['meta']; 616 } 617 } 618 } 619 620 if ( empty( $meta ) ) { 621 // likely this is a scheduled post moving to future or an after rest insert, so we grab the existing meta 622 $meta = get_post_meta( $post->ID ); 623 624 if ( $meta ) { 625 // meta fields when fetched via get_post_meta are automatically nested in an array, unwind this 626 foreach ( $meta as &$v ) { 627 $v = array_shift( $v ); 628 } 629 630 // audiences are serialized when fetched from get_post_meta, deserialize to a php array 631 if ( ! empty( $meta['pushly_customize_audience'] ) && ! empty( $meta['pushly_audience_ids'] ) ) { 632 $meta['pushly_audience_ids'] = unserialize( $meta['pushly_audience_ids'] ); 633 } 634 } 635 } 636 637 return $meta; 638 } 639 640 /** 641 * Emits notice HTML that will be shown when the page is rendered 642 * 643 * @return void 644 */ 645 public function emit_notice() { 646 if ( ! $this->is_using_gutenberg() ) { 647 $screen = get_current_screen(); 648 // Only render this notice in the post editor. 649 if ( ! $screen || 'post' !== $screen->base ) { 650 return; 651 } 652 653 $message = get_transient( 'pushly_notice' ); 654 if ( $message ) { 655 delete_transient( 'pushly_notice' ); 656 657 printf( 658 '<div class="notice error is-dismissible"><p>Pushly Notifications: %s</p></div>', 659 esc_html( __( "Failed to Send - " ) . $message ) 660 ); 661 } 662 } 663 } 664 665 /** 666 * Determines if the site is using Gutenberg or not. 667 * 668 * This method will not work if it is called too early in the WordPress initialization 669 * process. It must be used after `replace_editor` hook (or any subsequent hook) is executed. 670 * 671 * @See https://wordpress.stackexchange.com/a/309955 672 * 673 * @return bool 674 */ 675 protected function is_using_gutenberg() { 676 if ( function_exists( 'is_gutenberg_page' ) && is_gutenberg_page() ) { 677 return true; 678 } 679 680 $current_screen = get_current_screen(); 681 if ( method_exists( $current_screen, 'is_block_editor' ) && $current_screen->is_block_editor() ) { 682 return true; 683 } 684 685 return false; 686 } 687 688 /* API Methods */ 689 public function register_api_routes() { 690 register_rest_route( 691 'pushly/v1', 692 '/segments', 693 array( 694 'methods' => 'GET', 695 'permission_callback' => function () { 696 return current_user_can( 'edit_posts' ); 697 }, 698 'callback' => function ( $request ) { 699 return rest_ensure_response( $this->api_get_segments() ); 700 }, 701 ) 702 ); 703 } 704 705 protected function api_get_segments( 706 $options = null 707 ) { 708 $segments = []; 709 710 try { 711 $options = Pushly_Admin_Util::get_api_options(); 712 $domain_id = $options['domain_id']; 713 $api_key = Pushly_Admin_Util::decrypt_api_key( $options['sdk_key'], $options['sdk_key'], $options['api_key'] ); 714 715 $url = "https://" . PUSHLY__API_DOMAIN . "/domains/{$domain_id}/segments?pagination=0&source=standard&include_default=0&fields=id,name"; 716 $response = wp_remote_get( $url, [ 717 'headers' => array( 718 'X-API-KEY' => $api_key 719 ) 720 ] ); 721 722 if ( is_wp_error( $response ) ) { 723 Pushly_Admin_Util::log_to_event_stream( "segment_fetch_error", $response->get_error_message() ); 724 } else { 725 $response_body = json_decode( $response['body'], true ); 726 727 if ( ! empty( $response_body['status'] ) ) { 728 if ( $response_body['status'] === 'success' ) { 729 $segments = $response_body; 730 } else if ( $response_body['status'] === 'error' && ! empty( $response_body['message'] ) ) { 731 Pushly_Admin_Util::log_to_event_stream( "segment_fetch_error", $response_body['message'] ); 732 } 733 } 734 } 735 736 return $segments; 737 } catch ( Exception $e ) { 738 Pushly_Admin_Util::log_to_event_stream( "unknown_exception", "Encountered unknown exception during segment fetch: {$e->getMessage()}" ); 739 } 740 } 741 742 protected function api_save_notification( 743 $notification 744 ) { 745 $return = null; 746 747 $options = Pushly_Admin_Util::get_api_options(); 748 $domain_id = $options['domain_id']; 749 $api_key = Pushly_Admin_Util::decrypt_api_key( $options['sdk_key'], $options['sdk_key'], $options['api_key'] ); 750 751 if ( ! empty( $notification->id ) ) { 752 // updating notification 753 $url = "https://" . PUSHLY__API_DOMAIN . "/domains/{$domain_id}/notifications/{$notification->id}"; 754 $response = wp_remote_request( $url, array( 755 'method' => 'PATCH', 756 'body' => wp_json_encode( $notification ), 757 'headers' => array( 758 'Content-Type' => 'application/json', 759 'X-API-KEY' => $api_key, 760 ) 761 ) ); 762 } else { 763 // creating notification 764 $url = "https://" . PUSHLY__API_DOMAIN . "/domains/{$domain_id}/notifications"; 765 $response = wp_remote_post( $url, array( 766 'body' => wp_json_encode( $notification ), 767 'headers' => array( 768 'Content-Type' => 'application/json', 769 'X-API-KEY' => $api_key, 770 ) 771 ) ); 772 } 773 774 if ( is_wp_error( $response ) ) { 775 Pushly_Admin_Util::log_to_event_stream( "send_error", $response->get_error_message() ); 776 } else { 777 $response_body = json_decode( $response['body'], true ); 778 779 if ( ! empty( $response_body['status'] ) ) { 780 if ( $response_body['status'] === 'success' ) { 781 $return = $response_body["data"]; 782 } else if ( $response_body['status'] === 'error' && ! empty( $response_body['message'] ) ) { 783 Pushly_Admin_Util::log_to_event_stream( "send_error", $response_body['message'] ); 784 } 785 } 786 } 787 788 return $return; 789 } 771 790 } -
pushly/trunk/includes/admin/class-pushly-admin-settings.php
r3118516 r3129044 134 134 */ 135 135 public function register_settings() { 136 $new_options = []; 137 136 138 // this migrates from the v1 Pushly plugin options store 137 139 $legacy_options = get_option( 'pushly_options' ); … … 140 142 141 143 if ( ! empty( $legacy_options['pushly_domain_key'] ) ) { 142 $migrated_options = [ 'sdk_key' => $legacy_options['pushly_domain_key'] ]; 143 update_option( 'pushly', $migrated_options ); 144 $new_options['sdk_key'] = $legacy_options['pushly_domain_key']; 144 145 } 146 } 147 148 // any new settings that are added should be defaulted here 149 $current_options = get_option( 'pushly_options' ); 150 if (empty($current_options['enabled_post_types'])) { 151 $new_options['enabled_post_types'] = ['post']; 152 } 153 154 if ( ! empty( $new_options ) ) { 155 update_option( 'pushly', array_merge($new_options, $current_options )); 145 156 } 146 157 … … 163 174 'type' => 'boolean', 164 175 ], 176 'enabled_post_types' => [ 177 'type' => 'array' 178 ], 165 179 'initialization_disabled' => [ 166 180 'type' => 'boolean', … … 168 182 'show_post_success_message' => [ 169 183 'type' => 'boolean', 170 ] 184 ], 171 185 ], 172 186 ]; -
pushly/trunk/includes/admin/class-pushly-admin-util.php
r3118516 r3129044 6 6 7 7 class Pushly_Admin_Util { 8 private static $_settings; 9 private static $_options; 10 8 11 public static function get_api_options() { 9 12 $options = get_option( 'pushly' ); 10 13 if ( empty( $options['api_key'] ) ) { 11 return new WP_Error( 12 'no_api_key', 13 'API key not set in Pushly Settings', 14 array( 'status' => 424 ) 15 ); 14 Pushly_Admin_Util::log_to_event_stream( "no_api_key", "Settings does not contain `api_key`." ); 16 15 } else { 17 16 return $options; 18 17 } 19 18 } 20 21 19 22 20 public static function encrypt_api_key( $salt, $passphrase, $value ) { … … 31 29 $raw_value = openssl_encrypt( $value . $salt, $method, $passphrase, 0, $iv ); 32 30 if ( ! $raw_value ) { 33 return new WP_Error( 34 'internal_error', 35 'An error occurred when updating settings', 36 array( 'status' => 500 ) 37 ); 31 Pushly_Admin_Util::log_to_event_stream( "encrypt_api_key_failed", "Failed to encrypt API key." ); 38 32 } 39 33 … … 56 50 $value = openssl_decrypt( $raw_value, $method, $passphrase, 0, $iv ); 57 51 if ( ! $value || substr( $value, - strlen( $salt ) ) !== $salt ) { 52 Pushly_Admin_Util::log_to_event_stream( "decrypt_api_key_failed", "Failed to decrypt API key." ); 53 58 54 return false; 59 55 } … … 61 57 return substr( $value, 0, - strlen( $salt ) ); 62 58 } 59 60 public static function log_to_event_stream( $error_type, $error_message, $data = null ) { 61 try { 62 if ( empty( self::$_options ) ) { 63 self::$_options = get_option( 'pushly' ); 64 } 65 66 if ( ! empty( self::$_options["sdk_key"] ) && ! empty( self::$_options["domain_id"] ) ) { 67 if ( empty( self::$_settings ) ) { 68 $settings_request = wp_remote_get( "https://" . PUSHLY__CDN_DOMAIN . "/domain-settings/" . self::$_options["sdk_key"] ); 69 if ( is_wp_error( $settings_request ) ) { 70 return; 71 } 72 73 self::$_settings = json_decode( wp_remote_retrieve_body( $settings_request ), true ); 74 } 75 76 if ( ! empty( self::$_settings["domain"]["flags"] ) && in_array( "WORDPRESS_DEBUG_EVENTS", self::$_settings["domain"]["flags"] ) ) { 77 global $wp_version; 78 79 if ( ! empty( $data ) ) { 80 $error_message .= " (" . serialize( $data ) . ")"; 81 } 82 83 $payload = [ 84 "domain_id" => self::$_options["domain_id"], 85 "action" => "error", 86 "data" => [ 87 "error_type" => "wordpress_{$error_type}", 88 "error_message" => $error_message 89 ], 90 "meta" => [ 91 "application" => [ 92 "identifier" => "wordpress", 93 "version" => $wp_version 94 ], 95 "sdk" => [ 96 "name" => "pushly-wordpress-plugin", 97 "version" => PUSHLY__PLUGIN_VERSION 98 ], 99 "event" => [ 100 "version" => 3 101 ] 102 ] 103 ]; 104 105 wp_remote_request( "https://" . PUSHLY__K_DOMAIN . "/event-stream", [ 106 'method' => 'POST', 107 'body' => wp_json_encode( $payload ), 108 ] ); 109 } 110 } 111 } catch ( Exception $e ) { 112 // nothing to do 113 } 114 } 63 115 } -
pushly/trunk/includes/admin/models/notification.php
r3118516 r3129044 104 104 105 105 return $notification; 106 } catch (Exception $e) { 107 return new WP_Error('pushly_error', __( "Pushly: An error occurred when attempting to save the notification.")); 106 } catch ( Exception $e ) { 107 Pushly_Admin_Util::log_to_event_stream( "notification_build_error", $e->getMessage() ); 108 return; 108 109 } 109 110 } -
pushly/trunk/readme.txt
r3118525 r3129044 5 5 Tested up to: 6.6 6 6 Requires PHP: 5.6.20 7 Stable tag: 2. 0.07 Stable tag: 2.1.0 8 8 License: GPLv2 or later 9 9 … … 41 41 = 2.0.0 = 42 42 * Adds ability to send notifications directly from your WordPress site 43 44 = 2.1.0 = 45 * Adds Enabled Post Types setting to enable sending notifications for custom post types
Note: See TracChangeset
for help on using the changeset viewer.