Plugin Directory

Changeset 3129044


Ignore:
Timestamp:
07/31/2024 09:51:26 PM (20 months ago)
Author:
pushlydev
Message:

Release 2.1.0

Location:
pushly/trunk
Files:
4 deleted
14 edited

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' => 'b1c6e775bbf12b5aca72');
     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  
    3636    define( 'PUSHLY__CDN_DOMAIN', "pushlycdn.com" );
    3737}
     38
     39if ( ! defined( 'PUSHLY__K_DOMAIN' ) ) {
     40    define( 'PUSHLY__K_DOMAIN', "k.p-n.io" );
     41}
     42
     43if ( ! 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  
    11<?php
    22
    3 if (!defined('ABSPATH')) {
    4     exit;
     3if ( ! defined( 'ABSPATH' ) ) {
     4    exit;
    55}
    66
    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     }
     7class 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    }
    771790}
  • pushly/trunk/includes/admin/class-pushly-admin-settings.php

    r3118516 r3129044  
    134134     */
    135135    public function register_settings() {
     136        $new_options = [];
     137
    136138        // this migrates from the v1 Pushly plugin options store
    137139        $legacy_options = get_option( 'pushly_options' );
     
    140142
    141143            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'];
    144145            }
     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 ));
    145156        }
    146157
     
    163174                    'type' => 'boolean',
    164175                ],
     176                'enabled_post_types' => [
     177                    'type' => 'array'
     178                ],
    165179                'initialization_disabled'   => [
    166180                    'type' => 'boolean',
     
    168182                'show_post_success_message' => [
    169183                    'type' => 'boolean',
    170                 ]
     184                ],
    171185            ],
    172186        ];
  • pushly/trunk/includes/admin/class-pushly-admin-util.php

    r3118516 r3129044  
    66
    77class Pushly_Admin_Util {
     8    private static $_settings;
     9    private static $_options;
     10
    811    public static function get_api_options() {
    912        $options = get_option( 'pushly' );
    1013        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`." );
    1615        } else {
    1716            return $options;
    1817        }
    1918    }
    20 
    2119
    2220    public static function encrypt_api_key( $salt, $passphrase, $value ) {
     
    3129        $raw_value = openssl_encrypt( $value . $salt, $method, $passphrase, 0, $iv );
    3230        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." );
    3832        }
    3933
     
    5650        $value = openssl_decrypt( $raw_value, $method, $passphrase, 0, $iv );
    5751        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
    5854            return false;
    5955        }
     
    6157        return substr( $value, 0, - strlen( $salt ) );
    6258    }
     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    }
    63115}
  • pushly/trunk/includes/admin/models/notification.php

    r3118516 r3129044  
    104104
    105105            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;
    108109        }
    109110    }
  • pushly/trunk/readme.txt

    r3118525 r3129044  
    55Tested up to: 6.6
    66Requires PHP: 5.6.20
    7 Stable tag: 2.0.0
     7Stable tag: 2.1.0
    88License: GPLv2 or later
    99
     
    4141= 2.0.0 =
    4242* 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.