diff --git a/build/content-helper/dashboard-widget.asset.php b/build/content-helper/dashboard-widget.asset.php index a464c3757..403267823 100644 --- a/build/content-helper/dashboard-widget.asset.php +++ b/build/content-helper/dashboard-widget.asset.php @@ -1 +1 @@ - array('react', 'wp-api-fetch', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-url'), 'version' => 'd852566173ef137708b0'); + array('react', 'wp-api-fetch', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-url'), 'version' => 'b654f2e02d29a1665104'); diff --git a/build/content-helper/dashboard-widget.js b/build/content-helper/dashboard-widget.js index 7b17d4192..83b5454f9 100644 --- a/build/content-helper/dashboard-widget.js +++ b/build/content-helper/dashboard-widget.js @@ -1 +1 @@ -!function(){"use strict";var e={20:function(e,t,r){var n=r(609),a=Symbol.for("react.element"),s=Symbol.for("react.fragment"),o=Object.prototype.hasOwnProperty,i=n.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,l={key:!0,ref:!0,__self:!0,__source:!0};function c(e,t,r){var n,s={},c=null,u=null;for(n in void 0!==r&&(c=""+r),void 0!==t.key&&(c=""+t.key),void 0!==t.ref&&(u=t.ref),t)o.call(t,n)&&!l.hasOwnProperty(n)&&(s[n]=t[n]);if(e&&e.defaultProps)for(n in t=e.defaultProps)void 0===s[n]&&(s[n]=t[n]);return{$$typeof:a,type:e,key:c,ref:u,props:s,_owner:i.current}}t.Fragment=s,t.jsx=c,t.jsxs=c},848:function(e,t,r){e.exports=r(20)},609:function(e){e.exports=window.React}},t={};function r(n){var a=t[n];if(void 0!==a)return a.exports;var s=t[n]={exports:{}};return e[n](s,s.exports,r),s.exports}r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,{a:t}),t},r.d=function(e,t){for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},function(){var e,t,n,a=r(848),s=window.wp.element,o=window.wp.i18n,i=function(e){void 0===e&&(e=null);var t="";(null==e?void 0:e.children)&&(t=e.children);var r="content-helper-error-message";return(null==e?void 0:e.className)&&(r+=" "+e.className),(0,a.jsx)("div",{className:r,"data-testid":null==e?void 0:e.testId,dangerouslySetInnerHTML:{__html:t}})},l=function(e){var t;return void 0===e&&(e=null),(0,a.jsx)(i,{className:null==e?void 0:e.className,testId:"empty-credentials-message",children:null!==(t=window.wpParselyEmptyCredentialsMessage)&&void 0!==t?t:(0,o.__)("Please ensure that the Site ID and API Secret given in the plugin's settings are correct.","wp-parsely")})},c=function(){return c=Object.assign||function(e){for(var t,r=1,n=arguments.length;r0&&a[a.length-1])||6!==i[0]&&2!==i[0])){o=0;continue}if(3===i[0]&&(!a||i[1]>a[0]&&i[1]=1e4&&(clearInterval(s),r("Telemetry library not loaded"))}),100);else r("Telemetry not enabled")}))},e.prototype.trackEvent=function(t,r){var n;this.isLoaded?(0!==t.indexOf(e.TRACKS_PREFIX)&&(t=e.TRACKS_PREFIX+t),this.isEventNameValid(t)?(r=this.prepareProperties(r),null===(n=this._tkq)||void 0===n||n.push(["recordEvent",t,r])):console.error("Error tracking event: Invalid event name")):console.error("Error tracking event: Telemetry not loaded")},e.prototype.isTelemetryEnabled=function(){return this.isEnabled},e.prototype.isProprietyValid=function(t){return e.PROPERTY_REGEX.test(t)},e.prototype.isEventNameValid=function(t){return e.EVENT_NAME_REGEX.test(t)},e.prototype.prepareProperties=function(e){return(e=this.sanitizeProperties(e)).parsely_version=wpParselyTracksTelemetry.version,wpParselyTracksTelemetry.user&&(e._ut=wpParselyTracksTelemetry.user.type,e._ui=wpParselyTracksTelemetry.user.id),wpParselyTracksTelemetry.vipgo_env&&(e.vipgo_env=wpParselyTracksTelemetry.vipgo_env),this.sanitizeProperties(e)},e.prototype.sanitizeProperties=function(e){var t=this,r={};return Object.keys(e).forEach((function(n){t.isProprietyValid(n)&&(r[n]=e[n])})),r},e.TRACKS_PREFIX="wpparsely_",e.EVENT_NAME_REGEX=/^(([a-z0-9]+)_){2}([a-z0-9_]+)$/,e.PROPERTY_REGEX=/^[a-z_][a-z0-9_]*$/,e}(),h=(d.trackEvent,function(e){var t=e.defaultValue,r=e.items,n=e.onChange;return(0,a.jsx)("select",{onChange:n,value:t,children:r.map((function(e){return(0,a.jsx)("option",{value:e[0],children:e[1]},e[0])}))})}),f=window.wp.data,y=function(){return y=Object.assign||function(e){for(var t,r=1,n=arguments.length;rhere.',"wp-parsely"):s.code===j.ParselySuggestionsApiOpenAiError||s.code===j.ParselySuggestionsApiOpenAiUnavailable?s.message=(0,o.__)("The Parse.ly API returned an internal server error. Please retry with a different input, or try again later.","wp-parsely"):s.code===j.HttpRequestFailed&&s.message.includes("cURL error 28")?s.message=(0,o.__)("The Parse.ly API did not respond in a timely manner. Please try again later.","wp-parsely"):s.code===j.ParselySuggestionsApiSchemaError?s.message=(0,o.__)("The Parse.ly API returned a validation error. Please try again with different parameters.","wp-parsely"):s.code===j.ParselySuggestionsApiNoData?s.message=(0,o.__)("The Parse.ly API couldn't find any relevant data to fulfill the request. Please retry with a different input.","wp-parsely"):s.code===j.ParselySuggestionsApiOpenAiSchema?s.message=(0,o.__)("The Parse.ly API returned an incorrect response. Please try again later.","wp-parsely"):s.code===j.ParselySuggestionsApiAuthUnavailable&&(s.message=(0,o.__)("The Parse.ly API is currently unavailable. Please try again later.","wp-parsely")),s}return C(t,e),t.prototype.Message=function(e){return void 0===e&&(e=null),[j.PluginCredentialsNotSetMessageDetected,j.PluginSettingsSiteIdNotSet,j.PluginSettingsApiSecretNotSet].includes(this.code)?l(e):(this.code===j.FetchError&&(this.hint=this.Hint((0,o.__)("This error can sometimes be caused by ad-blockers or browser tracking protections. Please add this site to any applicable allow lists and try again.","wp-parsely"))),this.code!==j.ParselyApiForbidden&&this.code!==j.ParselySuggestionsApiNoAuthentication||(this.hint=this.Hint((0,o.__)("Please ensure that the Site ID and API Secret given in the plugin's settings are correct.","wp-parsely"))),this.code===j.HttpRequestFailed&&(this.hint=this.Hint((0,o.__)("The Parse.ly API cannot be reached. Please verify that you are online.","wp-parsely"))),(0,a.jsx)(i,{className:null==e?void 0:e.className,testId:"error",children:"

".concat(this.message,"

").concat(this.hint?this.hint:"")}))},t.prototype.Hint=function(e){return'

'.concat((0,o.__)("Hint:","wp-parsely")," ").concat(e,"

")},t.prototype.createErrorSnackbar=function(){//.test(this.message)||(0,f.dispatch)("core/notices").createNotice("error",this.message,{type:"snackbar"})},t}(Error),O=function(){function e(){this.abortControllers=new Map}return e.prototype.cancelRequest=function(e){if(e)(t=this.abortControllers.get(e))&&(t.abort(),this.abortControllers.delete(e));else{var t,r=Array.from(this.abortControllers.keys()).pop();r&&(t=this.abortControllers.get(r))&&(t.abort(),this.abortControllers.delete(r))}},e.prototype.cancelAll=function(){this.abortControllers.forEach((function(e){return e.abort()})),this.abortControllers.clear()},e.prototype.getOrCreateController=function(e){if(e&&this.abortControllers.has(e))return{abortController:this.abortControllers.get(e),abortId:e};var t=null!=e?e:"auto-"+Date.now(),r=new AbortController;return this.abortControllers.set(t,r),{abortController:r,abortId:t}},e.prototype.fetch=function(e,t){return r=this,n=void 0,s=function(){var r,n,a,s,i,l;return function(e,t){var r,n,a,s,o={label:0,sent:function(){if(1&a[0])throw a[1];return a[1]},trys:[],ops:[]};return s={next:i(0),throw:i(1),return:i(2)},"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function i(i){return function(l){return function(i){if(r)throw new TypeError("Generator is already executing.");for(;s&&(s=0,i[0]&&(o=0)),o;)try{if(r=1,n&&(a=2&i[0]?n.return:i[0]?n.throw||((a=n.return)&&a.call(n),0):n.next)&&!(a=a.call(n,i[1])).done)return a;switch(n=0,a&&(i=[2&i[0],a.value]),i[0]){case 0:case 1:a=i;break;case 4:return o.label++,{value:i[1],done:!1};case 5:o.label++,n=i[1],i=[0];continue;case 7:i=o.ops.pop(),o.trys.pop();continue;default:if(!((a=(a=o.trys).length>0&&a[a.length-1])||6!==i[0]&&2!==i[0])){o=0;continue}if(3===i[0]&&(!a||i[1]>a[0]&&i[1]0&&a[a.length-1])||6!==i[0]&&2!==i[0])){o=0;continue}if(3===i[0]&&(!a||i[1]>a[0]&&i[1]=c){var u=t;(a=n/c)%1>1/i&&(u=a>10?1:2),u=parseFloat(a.toFixed(2))===parseFloat(a.toFixed(0))?0:u,s=a.toFixed(u),o=l}i=c})),s+r+o}function z(e){var t=e.metric,r=e.post,n=e.avgEngagedIcon,s=e.viewsIcon;return"views"===t?(0,a.jsxs)("span",{className:"parsely-post-metric-data",children:[(0,a.jsx)("span",{className:"screen-reader-text",children:(0,o.__)("Number of Views","wp-parsely")}),s,$(r.views.toString())]}):"avg_engaged"===t?(0,a.jsxs)("span",{className:"parsely-post-metric-data",children:[(0,a.jsx)("span",{className:"screen-reader-text",children:(0,o.__)("Average Time","wp-parsely")}),n,r.avgEngaged]}):(0,a.jsx)("span",{className:"parsely-post-metric-data",children:"-"})}function X(e){var t,r=e.metric,n=e.post;return(0,a.jsx)("li",{className:"parsely-top-post",children:(0,a.jsxs)("div",{className:"parsely-top-post-content",children:[(0,a.jsx)(B,{post:n}),(0,a.jsxs)("div",{className:"parsely-top-post-data",children:[(0,a.jsx)(z,{metric:r,post:n}),(0,a.jsx)(Z,{post:n}),(0,a.jsxs)("a",{className:"parsely-top-post-icon-link",href:n.url,target:"_blank",rel:"noreferrer",children:[(0,a.jsx)("span",{className:"screen-reader-text",children:(0,o.__)("View Post (opens in new tab)","wp-parsely")}),(0,a.jsx)(q,{})]}),0!==n.postId&&(0,a.jsxs)("a",{className:"parsely-top-post-icon-link",href:(t=n.postId,"/wp-admin/post.php?post=".concat(t,"&action=edit")),target:"_blank",rel:"noreferrer",children:[(0,a.jsx)("span",{className:"screen-reader-text",children:(0,o.__)("Edit Post (opens in new tab)","wp-parsely")}),(0,a.jsx)(G,{})]}),(0,a.jsxs)("div",{className:"parsely-top-post-metadata",children:[(0,a.jsxs)("span",{className:"parsely-top-post-date",children:[(0,a.jsx)("span",{className:"screen-reader-text",children:(0,o.__)("Date","wp-parsely")}),M(new Date(n.date))]}),(0,a.jsxs)("span",{className:"parsely-top-post-author",children:[(0,a.jsx)("span",{className:"screen-reader-text",children:(0,o.__)("Author","wp-parsely")}),n.author]})]})]})]})},n.id)}function B(e){var t=e.post;return t.thumbnailUrl?(0,a.jsxs)("div",{className:"parsely-top-post-thumbnail",children:[(0,a.jsx)("span",{className:"screen-reader-text",children:(0,o.__)("Thumbnail","wp-parsely")}),(0,a.jsx)("img",{src:t.thumbnailUrl,alt:(0,o.__)("Post thumbnail","wp-parsely")})]}):(0,a.jsx)("div",{className:"parsely-top-post-thumbnail",children:(0,a.jsx)("span",{className:"screen-reader-text",children:(0,o.__)("Post thumbnail not available","wp-parsely")})})}function Z(e){var t=e.post;return(0,a.jsxs)("a",{className:"parsely-top-post-title",href:t.dashUrl,target:"_blank",rel:"noreferrer",children:[(0,a.jsx)("span",{className:"screen-reader-text",children:(0,o.__)("View in Parse.ly (opens in new tab)","wp-parsely")}),t.title]})}var W=function(){return W=Object.assign||function(e){for(var t,r=1,n=arguments.length;r0&&a[a.length-1])||6!==i[0]&&2!==i[0])){o=0;continue}if(3===i[0]&&(!a||i[1]>a[0]&&i[1]0&&e.retryFetch?[4,new Promise((function(e){return setTimeout(e,500)}))]:[3,3];case 1:return r.sent(),[4,t(n-1)];case 2:return r.sent(),[3,4];case 3:f(!1),v(e),r.label=4;case 4:return[2]}}))}))})),[2]}))}))};return f(!0),t(1),function(){f(!1),m([]),v(void 0)}}),[i,S]);var j=function(e,t){d.trackEvent("dash_widget_filter_changed",W({filter:e},t))},N=(0,a.jsxs)("div",{className:"parsely-top-posts-filters",children:[(0,a.jsx)(h,{defaultValue:i.Period,items:Object.values(e).map((function(e){return[e,A(e)]})),onChange:function(t){x(t.target.value,e)&&(l({Period:t.target.value}),j("period",{period:t.target.value}),T(1))}}),(0,a.jsx)(h,{defaultValue:i.Metric,items:Object.values(t).map((function(e){return[e,E(e)]})),onChange:function(e){x(e.target.value,t)&&(l({Metric:e.target.value}),j("metric",{metric:e.target.value}),T(1))}})]}),C=(0,a.jsxs)("div",{className:"parsely-top-posts-navigation",children:[(0,a.jsx)("button",{className:"parsely-top-posts-navigation-prev",disabled:S<=1,"aria-label":(0,o.__)("Previous page","wp-parsely"),onClick:function(){T(S-1),d.trackEvent("dash_widget_navigation",{navigation:"previous",to_page:S-1})},children:(0,o.__)("<< Previous","wp-parsely")}),(0,o.sprintf)(/* translators: 1: Current page */ /* translators: 1: Current page */(0,o.__)("Page %1$d","wp-parsely"),S),(0,a.jsx)("button",{className:"parsely-top-posts-navigation-next",disabled:!u&&_.length<5,"aria-label":(0,o.__)("Next page","wp-parsely"),onClick:function(){T(S+1),d.trackEvent("dash_widget_navigation",{navigation:"next",to_page:S+1})},children:(0,o.__)("Next >>","wp-parsely")})]});if(g)return(0,a.jsxs)(a.Fragment,{children:[N,g.Message(),S>1&&C]});var k=(0,a.jsx)("div",{className:"parsely-spinner-wrapper",children:(0,a.jsx)(p.Spinner,{})});return(0,a.jsxs)(a.Fragment,{children:[N,u?k:(0,a.jsx)("ol",{className:"parsely-top-posts",style:{counterReset:"item "+5*(S-1)},children:_.map((function(e){return(0,a.jsx)(X,{metric:i.Metric,post:e},e.id)}))}),(_.length>=5||S>1)&&C]})}var J=function(r){var n;try{n=JSON.parse(r)}catch(r){return{Metric:t.Views,Period:e.Days7}}return x(null==n?void 0:n.Metric,t)||(n.Metric=t.Views),x(null==n?void 0:n.Period,e)||(n.Period=e.Days7),n};window.addEventListener("load",(function(){var e=document.querySelector("#wp-parsely-dashboard-widget > .inside");if(null!==e){var t=(0,a.jsx)(S,{endpoint:"dashboard-widget-settings",defaultSettings:J(window.wpParselyContentHelperSettings),children:(0,a.jsx)(u,{children:(0,a.jsx)(Q,{})})});s.createRoot?(0,s.createRoot)(e).render(t):(0,s.render)(t,e)}}),!1)}()}(); \ No newline at end of file +!function(){"use strict";var e={20:function(e,t,r){var n=r(609),a=Symbol.for("react.element"),s=Symbol.for("react.fragment"),o=Object.prototype.hasOwnProperty,i=n.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,l={key:!0,ref:!0,__self:!0,__source:!0};function c(e,t,r){var n,s={},c=null,u=null;for(n in void 0!==r&&(c=""+r),void 0!==t.key&&(c=""+t.key),void 0!==t.ref&&(u=t.ref),t)o.call(t,n)&&!l.hasOwnProperty(n)&&(s[n]=t[n]);if(e&&e.defaultProps)for(n in t=e.defaultProps)void 0===s[n]&&(s[n]=t[n]);return{$$typeof:a,type:e,key:c,ref:u,props:s,_owner:i.current}}t.Fragment=s,t.jsx=c,t.jsxs=c},848:function(e,t,r){e.exports=r(20)},609:function(e){e.exports=window.React}},t={};function r(n){var a=t[n];if(void 0!==a)return a.exports;var s=t[n]={exports:{}};return e[n](s,s.exports,r),s.exports}r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,{a:t}),t},r.d=function(e,t){for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},function(){var e,t,n,a=r(848),s=window.wp.element,o=window.wp.i18n,i=function(e){void 0===e&&(e=null);var t="";(null==e?void 0:e.children)&&(t=e.children);var r="content-helper-error-message";return(null==e?void 0:e.className)&&(r+=" "+e.className),(0,a.jsx)("div",{className:r,"data-testid":null==e?void 0:e.testId,dangerouslySetInnerHTML:{__html:t}})},l=function(e){var t;return void 0===e&&(e=null),(0,a.jsx)(i,{className:null==e?void 0:e.className,testId:"empty-credentials-message",children:null!==(t=window.wpParselyEmptyCredentialsMessage)&&void 0!==t?t:(0,o.__)("Please ensure that the Site ID and API Secret given in the plugin's settings are correct.","wp-parsely")})},c=function(){return c=Object.assign||function(e){for(var t,r=1,n=arguments.length;r0&&a[a.length-1])||6!==i[0]&&2!==i[0])){o=0;continue}if(3===i[0]&&(!a||i[1]>a[0]&&i[1]=1e4&&(clearInterval(s),r("Telemetry library not loaded"))}),100);else r("Telemetry not enabled")}))},e.prototype.trackEvent=function(t,r){var n;this.isLoaded?(0!==t.indexOf(e.TRACKS_PREFIX)&&(t=e.TRACKS_PREFIX+t),this.isEventNameValid(t)?(r=this.prepareProperties(r),null===(n=this._tkq)||void 0===n||n.push(["recordEvent",t,r])):console.error("Error tracking event: Invalid event name")):console.error("Error tracking event: Telemetry not loaded")},e.prototype.isTelemetryEnabled=function(){return this.isEnabled},e.prototype.isProprietyValid=function(t){return e.PROPERTY_REGEX.test(t)},e.prototype.isEventNameValid=function(t){return e.EVENT_NAME_REGEX.test(t)},e.prototype.prepareProperties=function(e){return(e=this.sanitizeProperties(e)).parsely_version=wpParselyTracksTelemetry.version,wpParselyTracksTelemetry.user&&(e._ut=wpParselyTracksTelemetry.user.type,e._ui=wpParselyTracksTelemetry.user.id),wpParselyTracksTelemetry.vipgo_env&&(e.vipgo_env=wpParselyTracksTelemetry.vipgo_env),this.sanitizeProperties(e)},e.prototype.sanitizeProperties=function(e){var t=this,r={};return Object.keys(e).forEach((function(n){t.isProprietyValid(n)&&(r[n]=e[n])})),r},e.TRACKS_PREFIX="wpparsely_",e.EVENT_NAME_REGEX=/^(([a-z0-9]+)_){2}([a-z0-9_]+)$/,e.PROPERTY_REGEX=/^[a-z_][a-z0-9_]*$/,e}(),h=(d.trackEvent,function(e){var t=e.defaultValue,r=e.items,n=e.onChange;return(0,a.jsx)("select",{onChange:n,value:t,children:r.map((function(e){return(0,a.jsx)("option",{value:e[0],children:e[1]},e[0])}))})}),f=window.wp.data,y=function(){return y=Object.assign||function(e){for(var t,r=1,n=arguments.length;rhere.',"wp-parsely"):s.code===j.ParselySuggestionsApiOpenAiError||s.code===j.ParselySuggestionsApiOpenAiUnavailable?s.message=(0,o.__)("The Parse.ly API returned an internal server error. Please retry with a different input, or try again later.","wp-parsely"):s.code===j.HttpRequestFailed&&s.message.includes("cURL error 28")?s.message=(0,o.__)("The Parse.ly API did not respond in a timely manner. Please try again later.","wp-parsely"):s.code===j.ParselySuggestionsApiSchemaError?s.message=(0,o.__)("The Parse.ly API returned a validation error. Please try again with different parameters.","wp-parsely"):s.code===j.ParselySuggestionsApiNoData?s.message=(0,o.__)("The Parse.ly API couldn't find any relevant data to fulfill the request. Please retry with a different input.","wp-parsely"):s.code===j.ParselySuggestionsApiOpenAiSchema?s.message=(0,o.__)("The Parse.ly API returned an incorrect response. Please try again later.","wp-parsely"):s.code===j.ParselySuggestionsApiAuthUnavailable&&(s.message=(0,o.__)("The Parse.ly API is currently unavailable. Please try again later.","wp-parsely")),s}return C(t,e),t.prototype.Message=function(e){return void 0===e&&(e=null),[j.PluginCredentialsNotSetMessageDetected,j.PluginSettingsSiteIdNotSet,j.PluginSettingsApiSecretNotSet].includes(this.code)?l(e):(this.code===j.FetchError&&(this.hint=this.Hint((0,o.__)("This error can sometimes be caused by ad-blockers or browser tracking protections. Please add this site to any applicable allow lists and try again.","wp-parsely"))),this.code!==j.ParselyApiForbidden&&this.code!==j.ParselySuggestionsApiNoAuthentication||(this.hint=this.Hint((0,o.__)("Please ensure that the Site ID and API Secret given in the plugin's settings are correct.","wp-parsely"))),this.code===j.HttpRequestFailed&&(this.hint=this.Hint((0,o.__)("The Parse.ly API cannot be reached. Please verify that you are online.","wp-parsely"))),(0,a.jsx)(i,{className:null==e?void 0:e.className,testId:"error",children:"

".concat(this.message,"

").concat(this.hint?this.hint:"")}))},t.prototype.Hint=function(e){return'

'.concat((0,o.__)("Hint:","wp-parsely")," ").concat(e,"

")},t.prototype.createErrorSnackbar=function(){//.test(this.message)||(0,f.dispatch)("core/notices").createNotice("error",this.message,{type:"snackbar"})},t}(Error),O=function(){function e(){this.abortControllers=new Map}return e.prototype.cancelRequest=function(e){if(e)(t=this.abortControllers.get(e))&&(t.abort(),this.abortControllers.delete(e));else{var t,r=Array.from(this.abortControllers.keys()).pop();r&&(t=this.abortControllers.get(r))&&(t.abort(),this.abortControllers.delete(r))}},e.prototype.cancelAll=function(){this.abortControllers.forEach((function(e){return e.abort()})),this.abortControllers.clear()},e.prototype.getOrCreateController=function(e){if(e&&this.abortControllers.has(e))return{abortController:this.abortControllers.get(e),abortId:e};var t=null!=e?e:"auto-"+Date.now(),r=new AbortController;return this.abortControllers.set(t,r),{abortController:r,abortId:t}},e.prototype.fetch=function(e,t){return r=this,n=void 0,s=function(){var r,n,a,s,i,l;return function(e,t){var r,n,a,s,o={label:0,sent:function(){if(1&a[0])throw a[1];return a[1]},trys:[],ops:[]};return s={next:i(0),throw:i(1),return:i(2)},"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function i(i){return function(l){return function(i){if(r)throw new TypeError("Generator is already executing.");for(;s&&(s=0,i[0]&&(o=0)),o;)try{if(r=1,n&&(a=2&i[0]?n.return:i[0]?n.throw||((a=n.return)&&a.call(n),0):n.next)&&!(a=a.call(n,i[1])).done)return a;switch(n=0,a&&(i=[2&i[0],a.value]),i[0]){case 0:case 1:a=i;break;case 4:return o.label++,{value:i[1],done:!1};case 5:o.label++,n=i[1],i=[0];continue;case 7:i=o.ops.pop(),o.trys.pop();continue;default:if(!((a=(a=o.trys).length>0&&a[a.length-1])||6!==i[0]&&2!==i[0])){o=0;continue}if(3===i[0]&&(!a||i[1]>a[0]&&i[1]0&&a[a.length-1])||6!==i[0]&&2!==i[0])){o=0;continue}if(3===i[0]&&(!a||i[1]>a[0]&&i[1]=c){var u=t;(a=n/c)%1>1/i&&(u=a>10?1:2),u=parseFloat(a.toFixed(2))===parseFloat(a.toFixed(0))?0:u,s=a.toFixed(u),o=l}i=c})),s+r+o}function z(e){var t=e.metric,r=e.post,n=e.avgEngagedIcon,s=e.viewsIcon;return"views"===t?(0,a.jsxs)("span",{className:"parsely-post-metric-data",children:[(0,a.jsx)("span",{className:"screen-reader-text",children:(0,o.__)("Number of Views","wp-parsely")}),s,$(r.views.toString())]}):"avg_engaged"===t?(0,a.jsxs)("span",{className:"parsely-post-metric-data",children:[(0,a.jsx)("span",{className:"screen-reader-text",children:(0,o.__)("Average Time","wp-parsely")}),n,r.avgEngaged]}):(0,a.jsx)("span",{className:"parsely-post-metric-data",children:"-"})}function X(e){var t,r=e.metric,n=e.post;return(0,a.jsx)("li",{className:"parsely-top-post",children:(0,a.jsxs)("div",{className:"parsely-top-post-content",children:[(0,a.jsx)(B,{post:n}),(0,a.jsxs)("div",{className:"parsely-top-post-data",children:[(0,a.jsx)(z,{metric:r,post:n}),(0,a.jsx)(Z,{post:n}),(0,a.jsxs)("a",{className:"parsely-top-post-icon-link",href:n.url,target:"_blank",rel:"noreferrer",children:[(0,a.jsx)("span",{className:"screen-reader-text",children:(0,o.__)("View Post (opens in new tab)","wp-parsely")}),(0,a.jsx)(q,{})]}),0!==n.postId&&(0,a.jsxs)("a",{className:"parsely-top-post-icon-link",href:(t=n.postId,"/wp-admin/post.php?post=".concat(t,"&action=edit")),target:"_blank",rel:"noreferrer",children:[(0,a.jsx)("span",{className:"screen-reader-text",children:(0,o.__)("Edit Post (opens in new tab)","wp-parsely")}),(0,a.jsx)(G,{})]}),(0,a.jsxs)("div",{className:"parsely-top-post-metadata",children:[(0,a.jsxs)("span",{className:"parsely-top-post-date",children:[(0,a.jsx)("span",{className:"screen-reader-text",children:(0,o.__)("Date","wp-parsely")}),M(new Date(n.date))]}),(0,a.jsxs)("span",{className:"parsely-top-post-author",children:[(0,a.jsx)("span",{className:"screen-reader-text",children:(0,o.__)("Author","wp-parsely")}),n.author]})]})]})]})},n.id)}function B(e){var t=e.post;return t.thumbnailUrl?(0,a.jsxs)("div",{className:"parsely-top-post-thumbnail",children:[(0,a.jsx)("span",{className:"screen-reader-text",children:(0,o.__)("Thumbnail","wp-parsely")}),(0,a.jsx)("img",{src:t.thumbnailUrl,alt:(0,o.__)("Post thumbnail","wp-parsely")})]}):(0,a.jsx)("div",{className:"parsely-top-post-thumbnail",children:(0,a.jsx)("span",{className:"screen-reader-text",children:(0,o.__)("Post thumbnail not available","wp-parsely")})})}function Z(e){var t=e.post;return(0,a.jsxs)("a",{className:"parsely-top-post-title",href:t.dashUrl,target:"_blank",rel:"noreferrer",children:[(0,a.jsx)("span",{className:"screen-reader-text",children:(0,o.__)("View in Parse.ly (opens in new tab)","wp-parsely")}),t.title]})}var W=function(){return W=Object.assign||function(e){for(var t,r=1,n=arguments.length;r0&&a[a.length-1])||6!==i[0]&&2!==i[0])){o=0;continue}if(3===i[0]&&(!a||i[1]>a[0]&&i[1]0&&e.retryFetch?[4,new Promise((function(e){return setTimeout(e,500)}))]:[3,3];case 1:return r.sent(),[4,t(n-1)];case 2:return r.sent(),[3,4];case 3:f(!1),v(e),r.label=4;case 4:return[2]}}))}))})),[2]}))}))};return f(!0),t(1),function(){f(!1),m([]),v(void 0)}}),[i,S]);var j=function(e,t){d.trackEvent("dash_widget_filter_changed",W({filter:e},t))},N=(0,a.jsxs)("div",{className:"parsely-top-posts-filters",children:[(0,a.jsx)(h,{defaultValue:i.Period,items:Object.values(e).map((function(e){return[e,A(e)]})),onChange:function(t){x(t.target.value,e)&&(l({Period:t.target.value}),j("period",{period:t.target.value}),T(1))}}),(0,a.jsx)(h,{defaultValue:i.Metric,items:Object.values(t).map((function(e){return[e,E(e)]})),onChange:function(e){x(e.target.value,t)&&(l({Metric:e.target.value}),j("metric",{metric:e.target.value}),T(1))}})]}),C=(0,a.jsxs)("div",{className:"parsely-top-posts-navigation",children:[(0,a.jsx)("button",{className:"parsely-top-posts-navigation-prev",disabled:S<=1,"aria-label":(0,o.__)("Previous page","wp-parsely"),onClick:function(){T(S-1),d.trackEvent("dash_widget_navigation",{navigation:"previous",to_page:S-1})},children:(0,o.__)("<< Previous","wp-parsely")}),(0,o.sprintf)(/* translators: 1: Current page */ /* translators: 1: Current page */(0,o.__)("Page %1$d","wp-parsely"),S),(0,a.jsx)("button",{className:"parsely-top-posts-navigation-next",disabled:!u&&_.length<5,"aria-label":(0,o.__)("Next page","wp-parsely"),onClick:function(){T(S+1),d.trackEvent("dash_widget_navigation",{navigation:"next",to_page:S+1})},children:(0,o.__)("Next >>","wp-parsely")})]});if(g)return(0,a.jsxs)(a.Fragment,{children:[N,g.Message(),S>1&&C]});var k=(0,a.jsx)("div",{className:"parsely-spinner-wrapper",children:(0,a.jsx)(p.Spinner,{})});return(0,a.jsxs)(a.Fragment,{children:[N,u?k:(0,a.jsx)("ol",{className:"parsely-top-posts",style:{counterReset:"item "+5*(S-1)},children:_.map((function(e){return(0,a.jsx)(X,{metric:i.Metric,post:e},e.id)}))}),(_.length>=5||S>1)&&C]})}var J=function(r){var n;try{n=JSON.parse(r)}catch(r){return{Metric:t.Views,Period:e.Days7}}return x(null==n?void 0:n.Metric,t)||(n.Metric=t.Views),x(null==n?void 0:n.Period,e)||(n.Period=e.Days7),n};window.addEventListener("load",(function(){var e=document.querySelector("#wp-parsely-dashboard-widget > .inside");if(null!==e){var t=(0,a.jsx)(S,{endpoint:"dashboard-widget",defaultSettings:J(window.wpParselyContentHelperSettings),children:(0,a.jsx)(u,{children:(0,a.jsx)(Q,{})})});s.createRoot?(0,s.createRoot)(e).render(t):(0,s.render)(t,e)}}),!1)}()}(); \ No newline at end of file diff --git a/build/content-helper/editor-sidebar.asset.php b/build/content-helper/editor-sidebar.asset.php index a791b1c32..e3d486552 100644 --- a/build/content-helper/editor-sidebar.asset.php +++ b/build/content-helper/editor-sidebar.asset.php @@ -1 +1 @@ - array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom-ready', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-plugins', 'wp-primitives', 'wp-url'), 'version' => 'eaad1ca5764f3fbdb9e6'); + array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom-ready', 'wp-editor', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-plugins', 'wp-primitives', 'wp-url'), 'version' => '7d87b64bdabeaf1dc579'); diff --git a/build/content-helper/editor-sidebar.js b/build/content-helper/editor-sidebar.js index 697b66067..9723913d5 100644 --- a/build/content-helper/editor-sidebar.js +++ b/build/content-helper/editor-sidebar.js @@ -1,9 +1,9 @@ -!function(){"use strict";var e={20:function(e,t,n){var r=n(609),i=Symbol.for("react.element"),s=Symbol.for("react.fragment"),o=Object.prototype.hasOwnProperty,a=r.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,l={key:!0,ref:!0,__self:!0,__source:!0};function c(e,t,n){var r,s={},c=null,u=null;for(r in void 0!==n&&(c=""+n),void 0!==t.key&&(c=""+t.key),void 0!==t.ref&&(u=t.ref),t)o.call(t,r)&&!l.hasOwnProperty(r)&&(s[r]=t[r]);if(e&&e.defaultProps)for(r in t=e.defaultProps)void 0===s[r]&&(s[r]=t[r]);return{$$typeof:i,type:e,key:c,ref:u,props:s,_owner:a.current}}t.Fragment=s,t.jsx=c,t.jsxs=c},848:function(e,t,n){e.exports=n(20)},609:function(e){e.exports=window.React}},t={};function n(r){var i=t[r];if(void 0!==i)return i.exports;var s=t[r]={exports:{}};return e[r](s,s.exports,n),s.exports}n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,{a:t}),t},n.d=function(e,t){for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},function(){n.d({},{_:function(){return ur}});var e,t,r,i,s,o,a,l,c,u,d,p=n(848),f=window.wp.components,h=window.wp.data,v=window.wp.domReady,g=n.n(v);void 0!==window.wp&&(null!==(t=null===(e=window.wp.editor)||void 0===e?void 0:e.PluginDocumentSettingPanel)&&void 0!==t||(null!==(i=null===(r=window.wp.editPost)||void 0===r?void 0:r.PluginDocumentSettingPanel)&&void 0!==i||(null===(s=window.wp.editSite)||void 0===s||s.PluginDocumentSettingPanel)),d=null!==(a=null===(o=window.wp.editor)||void 0===o?void 0:o.PluginSidebar)&&void 0!==a?a:null!==(c=null===(l=window.wp.editPost)||void 0===l?void 0:l.PluginSidebar)&&void 0!==c?c:null===(u=window.wp.editSite)||void 0===u?void 0:u.PluginSidebar);var y,m,w,b=window.wp.element,_=window.wp.i18n,x=window.wp.primitives,k=(0,p.jsx)(x.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",children:(0,p.jsx)(x.Path,{fillRule:"evenodd",d:"M11.25 5h1.5v15h-1.5V5zM6 10h1.5v10H6V10zm12 4h-1.5v6H18v-6z",clipRule:"evenodd"})}),S=window.wp.plugins,P=function(){function e(){this._tkq=[],this.isLoaded=!1,this.isEnabled=!1,"undefined"!=typeof wpParselyTracksTelemetry&&(this.isEnabled=!0,this.loadTrackingLibrary())}return e.getInstance=function(){return window.wpParselyTelemetryInstance||Object.defineProperty(window,"wpParselyTelemetryInstance",{value:new e,writable:!1,configurable:!1,enumerable:!1}),window.wpParselyTelemetryInstance},e.prototype.loadTrackingLibrary=function(){var e=this,t=document.createElement("script");t.async=!0,t.src="//stats.wp.com/w.js",t.onload=function(){e.isLoaded=!0,e._tkq=window._tkq||[]},document.head.appendChild(t)},e.trackEvent=function(t){return n=this,r=arguments,s=function(t,n){var r;return void 0===n&&(n={}),function(e,t){var n,r,i,s,o={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return s={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function a(a){return function(l){return function(a){if(n)throw new TypeError("Generator is already executing.");for(;s&&(s=0,a[0]&&(o=0)),o;)try{if(n=1,r&&(i=2&a[0]?r.return:a[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,a[1])).done)return i;switch(r=0,i&&(a=[2&a[0],i.value]),a[0]){case 0:case 1:i=a;break;case 4:return o.label++,{value:a[1],done:!1};case 5:o.label++,r=a[1],a=[0];continue;case 7:a=o.ops.pop(),o.trys.pop();continue;default:if(!((i=(i=o.trys).length>0&&i[i.length-1])||6!==a[0]&&2!==a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]=1e4&&(clearInterval(s),n("Telemetry library not loaded"))}),100);else n("Telemetry not enabled")}))},e.prototype.trackEvent=function(t,n){var r;this.isLoaded?(0!==t.indexOf(e.TRACKS_PREFIX)&&(t=e.TRACKS_PREFIX+t),this.isEventNameValid(t)?(n=this.prepareProperties(n),null===(r=this._tkq)||void 0===r||r.push(["recordEvent",t,n])):console.error("Error tracking event: Invalid event name")):console.error("Error tracking event: Telemetry not loaded")},e.prototype.isTelemetryEnabled=function(){return this.isEnabled},e.prototype.isProprietyValid=function(t){return e.PROPERTY_REGEX.test(t)},e.prototype.isEventNameValid=function(t){return e.EVENT_NAME_REGEX.test(t)},e.prototype.prepareProperties=function(e){return(e=this.sanitizeProperties(e)).parsely_version=wpParselyTracksTelemetry.version,wpParselyTracksTelemetry.user&&(e._ut=wpParselyTracksTelemetry.user.type,e._ui=wpParselyTracksTelemetry.user.id),wpParselyTracksTelemetry.vipgo_env&&(e.vipgo_env=wpParselyTracksTelemetry.vipgo_env),this.sanitizeProperties(e)},e.prototype.sanitizeProperties=function(e){var t=this,n={};return Object.keys(e).forEach((function(r){t.isProprietyValid(r)&&(n[r]=e[r])})),n},e.TRACKS_PREFIX="wpparsely_",e.EVENT_NAME_REGEX=/^(([a-z0-9]+)_){2}([a-z0-9_]+)$/,e.PROPERTY_REGEX=/^[a-z_][a-z0-9_]*$/,e}(),j=(P.trackEvent,function(){return(0,p.jsx)(f.SVG,{"aria-hidden":"true",version:"1.1",viewBox:"0 0 15 15",width:"15",height:"15",xmlns:"http://www.w3.org/2000/svg",children:(0,p.jsx)(f.Path,{d:"M0 14.0025V11.0025L7.5 3.5025L10.5 6.5025L3 14.0025H0ZM12 5.0025L13.56 3.4425C14.15 2.8525 14.15 1.9025 13.56 1.3225L12.68 0.4425C12.09 -0.1475 11.14 -0.1475 10.56 0.4425L9 2.0025L12 5.0025Z"})})}),T=function(e){var t=e.size,n=void 0===t?24:t,r=e.className,i=void 0===r?"wp-parsely-icon":r;return(0,p.jsxs)(f.SVG,{className:i,height:n,viewBox:"0 0 60 65",width:n,xmlns:"http://www.w3.org/2000/svg",children:[(0,p.jsx)(f.Path,{fill:"#5ba745",d:"M23.72,51.53c0-.18,0-.34-.06-.52a13.11,13.11,0,0,0-2.1-5.53A14.74,14.74,0,0,0,19.12,43c-.27-.21-.5-.11-.51.22l-.24,3.42c0,.33-.38.35-.49,0l-1.5-4.8a1.4,1.4,0,0,0-.77-.78,23.91,23.91,0,0,0-3.1-.84c-1.38-.24-3.39-.39-3.39-.39-.34,0-.45.21-.25.49l2.06,3.76c.2.27,0,.54-.29.33l-4.51-3.6a3.68,3.68,0,0,0-2.86-.48c-1,.16-2.44.46-2.44.46a.68.68,0,0,0-.39.25.73.73,0,0,0-.14.45S.41,43,.54,44a3.63,3.63,0,0,0,1.25,2.62L6.48,50c.28.2.09.49-.23.37l-4.18-.94c-.32-.12-.5,0-.4.37,0,0,.69,1.89,1.31,3.16a24,24,0,0,0,1.66,2.74,1.34,1.34,0,0,0,1,.52l5,.13c.33,0,.41.38.1.48L7.51,58c-.31.1-.34.35-.07.55a14.29,14.29,0,0,0,3.05,1.66,13.09,13.09,0,0,0,5.9.5,25.13,25.13,0,0,0,4.34-1,9.55,9.55,0,0,1-.08-1.2,9.32,9.32,0,0,1,3.07-6.91"}),(0,p.jsx)(f.Path,{fill:"#5ba745",d:"M59.7,41.53a.73.73,0,0,0-.14-.45.68.68,0,0,0-.39-.25s-1.43-.3-2.44-.46a3.64,3.64,0,0,0-2.86.48l-4.51,3.6c-.26.21-.49-.06-.29-.33l2.06-3.76c.2-.28.09-.49-.25-.49,0,0-2,.15-3.39.39a23.91,23.91,0,0,0-3.1.84,1.4,1.4,0,0,0-.77.78l-1.5,4.8c-.11.32-.48.3-.49,0l-.24-3.42c0-.33-.24-.43-.51-.22a14.74,14.74,0,0,0-2.44,2.47A13.11,13.11,0,0,0,36.34,51c0,.18,0,.34-.06.52a9.26,9.26,0,0,1,3,8.1,24.1,24.1,0,0,0,4.34,1,13.09,13.09,0,0,0,5.9-.5,14.29,14.29,0,0,0,3.05-1.66c.27-.2.24-.45-.07-.55l-3.22-1.17c-.31-.1-.23-.47.1-.48l5-.13a1.38,1.38,0,0,0,1-.52A24.6,24.6,0,0,0,57,52.92c.61-1.27,1.31-3.16,1.31-3.16.1-.33-.08-.49-.4-.37l-4.18.94c-.32.12-.51-.17-.23-.37l4.69-3.34A3.63,3.63,0,0,0,59.46,44c.13-1,.24-2.47.24-2.47"}),(0,p.jsx)(f.Path,{fill:"#5ba745",d:"M46.5,25.61c0-.53-.35-.72-.8-.43l-4.86,2.66c-.45.28-.56-.27-.23-.69l4.66-6.23a2,2,0,0,0,.28-1.68,36.51,36.51,0,0,0-2.19-4.89,34,34,0,0,0-2.81-3.94c-.33-.41-.74-.35-.91.16l-2.28,5.68c-.16.5-.6.48-.59-.05l.28-8.93a2.54,2.54,0,0,0-.66-1.64S35,4.27,33.88,3.27,30.78.69,30.78.69a1.29,1.29,0,0,0-1.54,0s-1.88,1.49-3.12,2.59-2.48,2.35-2.48,2.35A2.5,2.5,0,0,0,23,7.27l.27,8.93c0,.53-.41.55-.58.05l-2.29-5.69c-.17-.5-.57-.56-.91-.14a35.77,35.77,0,0,0-3,4.2,35.55,35.55,0,0,0-2,4.62,2,2,0,0,0,.27,1.67l4.67,6.24c.33.42.23,1-.22.69l-4.87-2.66c-.45-.29-.82-.1-.82.43a18.6,18.6,0,0,0,.83,5.07,20.16,20.16,0,0,0,5.37,7.77c3.19,3,5.93,7.8,7.45,11.08A9.6,9.6,0,0,1,30,49.09a9.31,9.31,0,0,1,2.86.45c1.52-3.28,4.26-8.11,7.44-11.09a20.46,20.46,0,0,0,5.09-7,19,19,0,0,0,1.11-5.82"}),(0,p.jsx)(f.Path,{fill:"#5ba745",d:"M36.12,58.44A6.12,6.12,0,1,1,30,52.32a6.11,6.11,0,0,1,6.12,6.12"})]})},L=function(){return L=Object.assign||function(e){for(var t,n=1,r=arguments.length;nhere.',"wp-parsely"):s.code===$.ParselySuggestionsApiOpenAiError||s.code===$.ParselySuggestionsApiOpenAiUnavailable?s.message=(0,_.__)("The Parse.ly API returned an internal server error. Please retry with a different input, or try again later.","wp-parsely"):s.code===$.HttpRequestFailed&&s.message.includes("cURL error 28")?s.message=(0,_.__)("The Parse.ly API did not respond in a timely manner. Please try again later.","wp-parsely"):s.code===$.ParselySuggestionsApiSchemaError?s.message=(0,_.__)("The Parse.ly API returned a validation error. Please try again with different parameters.","wp-parsely"):s.code===$.ParselySuggestionsApiNoData?s.message=(0,_.__)("The Parse.ly API couldn't find any relevant data to fulfill the request. Please retry with a different input.","wp-parsely"):s.code===$.ParselySuggestionsApiOpenAiSchema?s.message=(0,_.__)("The Parse.ly API returned an incorrect response. Please try again later.","wp-parsely"):s.code===$.ParselySuggestionsApiAuthUnavailable&&(s.message=(0,_.__)("The Parse.ly API is currently unavailable. Please try again later.","wp-parsely")),s}return re(t,e),t.prototype.Message=function(e){return void 0===e&&(e=null),[$.PluginCredentialsNotSetMessageDetected,$.PluginSettingsSiteIdNotSet,$.PluginSettingsApiSecretNotSet].includes(this.code)?K(e):(this.code===$.FetchError&&(this.hint=this.Hint((0,_.__)("This error can sometimes be caused by ad-blockers or browser tracking protections. Please add this site to any applicable allow lists and try again.","wp-parsely"))),this.code!==$.ParselyApiForbidden&&this.code!==$.ParselySuggestionsApiNoAuthentication||(this.hint=this.Hint((0,_.__)("Please ensure that the Site ID and API Secret given in the plugin's settings are correct.","wp-parsely"))),this.code===$.HttpRequestFailed&&(this.hint=this.Hint((0,_.__)("The Parse.ly API cannot be reached. Please verify that you are online.","wp-parsely"))),(0,p.jsx)(W,{className:null==e?void 0:e.className,testId:"error",children:"

".concat(this.message,"

").concat(this.hint?this.hint:"")}))},t.prototype.Hint=function(e){return'

'.concat((0,_.__)("Hint:","wp-parsely")," ").concat(e,"

")},t.prototype.createErrorSnackbar=function(){//.test(this.message)||(0,h.dispatch)("core/notices").createNotice("error",this.message,{type:"snackbar"})},t}(Error),se=function(e){var t=e.isDetectingEnabled,n=e.onLinkChange,r=e.onLinkRemove,i=e.onLinkAdd,s=e.debounceValue,o=void 0===s?500:s,a=(0,h.useSelect)((function(e){return{blocks:(0,e("core/block-editor").getBlocks)()}}),[]).blocks,l=(0,b.useRef)(a),c=(0,b.useRef)(t);return(0,b.useEffect)((function(){var e=(0,z.debounce)((function(){for(var t=[],s=0;s0)return r(e.innerBlocks,t[s].innerBlocks);if(JSON.stringify(e)!==JSON.stringify(t[s])){var o=t[s],a=i.parseFromString(e.attributes.content||"","text/html"),l=i.parseFromString((null==o?void 0:o.attributes.content)||"","text/html"),c=Array.from(a.querySelectorAll("a[data-smartlink]")),u=Array.from(l.querySelectorAll("a[data-smartlink]")),d=c.filter((function(e){return!u.some((function(t){return t.dataset.smartlink===e.dataset.smartlink}))})),p=u.filter((function(e){return!c.some((function(t){return t.dataset.smartlink===e.dataset.smartlink}))})),f=c.filter((function(e){var t=u.find((function(t){return t.dataset.smartlink===e.dataset.smartlink}));return t&&t.outerHTML!==e.outerHTML}));(d.length>0||p.length>0||f.length>0)&&n.push({block:e,prevBlock:o,addedLinks:d,removedLinks:p,changedLinks:f})}}}))};return r(e,t),n}(a,l.current);o.length>0&&(o.forEach((function(e){e.changedLinks.length>0&&n&&n(e),e.addedLinks.length>0&&i&&i(e),e.removedLinks.length>0&&r&&r(e)})),l.current=a)}),o);return e(t),function(){e.cancel()}}),[a,o,t,i,n,r]),null},oe=function(e){var t=e.value,n=e.onChange,r=e.max,i=e.min,s=e.suffix,o=e.size,a=e.label,l=e.initialPosition,c=e.disabled,u=e.className;return(0,p.jsxs)("div",{className:"parsely-inputrange-control ".concat(u||""),children:[(0,p.jsx)(f.__experimentalHeading,{className:"parsely-inputrange-control__label",level:3,children:a}),(0,p.jsxs)("div",{className:"parsely-inputrange-control__controls",children:[(0,p.jsx)(f.__experimentalNumberControl,{disabled:c,value:t,suffix:(0,p.jsx)(f.__experimentalInputControlSuffixWrapper,{children:s}),size:null!=o?o:"__unstable-large",min:i,max:r,onChange:function(e){var t=parseInt(e,10);isNaN(t)||n(t)}}),(0,p.jsx)(f.RangeControl,{disabled:c,value:t,showTooltip:!1,initialPosition:l,onChange:function(e){n(e)},withInputField:!1,min:i,max:r})]})]})},ae=function(e,t,n,r){return new(n||(n=Promise))((function(i,s){function o(e){try{l(r.next(e))}catch(e){s(e)}}function a(e){try{l(r.throw(e))}catch(e){s(e)}}function l(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(o,a)}l((r=r.apply(e,t||[])).next())}))},le=function(e,t){var n,r,i,s,o={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return s={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function a(a){return function(l){return function(a){if(n)throw new TypeError("Generator is already executing.");for(;s&&(s=0,a[0]&&(o=0)),o;)try{if(n=1,r&&(i=2&a[0]?r.return:a[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,a[1])).done)return i;switch(r=0,i&&(a=[2&a[0],i.value]),a[0]){case 0:case 1:i=a;break;case 4:return o.label++,{value:a[1],done:!1};case 5:o.label++,r=a[1],a=[0];continue;case 7:a=o.ops.pop(),o.trys.pop();continue;default:if(!((i=(i=o.trys).length>0&&i[i.length-1])||6!==a[0]&&2!==a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&i[i.length-1])||6!==a[0]&&2!==a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&i[i.length-1])||6!==a[0]&&2!==a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]=1e4&&(clearInterval(s),n("Telemetry library not loaded"))}),100);else n("Telemetry not enabled")}))},e.prototype.trackEvent=function(t,n){var r;this.isLoaded?(0!==t.indexOf(e.TRACKS_PREFIX)&&(t=e.TRACKS_PREFIX+t),this.isEventNameValid(t)?(n=this.prepareProperties(n),null===(r=this._tkq)||void 0===r||r.push(["recordEvent",t,n])):console.error("Error tracking event: Invalid event name")):console.error("Error tracking event: Telemetry not loaded")},e.prototype.isTelemetryEnabled=function(){return this.isEnabled},e.prototype.isProprietyValid=function(t){return e.PROPERTY_REGEX.test(t)},e.prototype.isEventNameValid=function(t){return e.EVENT_NAME_REGEX.test(t)},e.prototype.prepareProperties=function(e){return(e=this.sanitizeProperties(e)).parsely_version=wpParselyTracksTelemetry.version,wpParselyTracksTelemetry.user&&(e._ut=wpParselyTracksTelemetry.user.type,e._ui=wpParselyTracksTelemetry.user.id),wpParselyTracksTelemetry.vipgo_env&&(e.vipgo_env=wpParselyTracksTelemetry.vipgo_env),this.sanitizeProperties(e)},e.prototype.sanitizeProperties=function(e){var t=this,n={};return Object.keys(e).forEach((function(r){t.isProprietyValid(r)&&(n[r]=e[r])})),n},e.TRACKS_PREFIX="wpparsely_",e.EVENT_NAME_REGEX=/^(([a-z0-9]+)_){2}([a-z0-9_]+)$/,e.PROPERTY_REGEX=/^[a-z_][a-z0-9_]*$/,e}(),j=(P.trackEvent,function(){return(0,p.jsx)(f.SVG,{"aria-hidden":"true",version:"1.1",viewBox:"0 0 15 15",width:"15",height:"15",xmlns:"http://www.w3.org/2000/svg",children:(0,p.jsx)(f.Path,{d:"M0 14.0025V11.0025L7.5 3.5025L10.5 6.5025L3 14.0025H0ZM12 5.0025L13.56 3.4425C14.15 2.8525 14.15 1.9025 13.56 1.3225L12.68 0.4425C12.09 -0.1475 11.14 -0.1475 10.56 0.4425L9 2.0025L12 5.0025Z"})})}),T=function(e){var t=e.size,n=void 0===t?24:t,r=e.className,i=void 0===r?"wp-parsely-icon":r;return(0,p.jsxs)(f.SVG,{className:i,height:n,viewBox:"0 0 60 65",width:n,xmlns:"http://www.w3.org/2000/svg",children:[(0,p.jsx)(f.Path,{fill:"#5ba745",d:"M23.72,51.53c0-.18,0-.34-.06-.52a13.11,13.11,0,0,0-2.1-5.53A14.74,14.74,0,0,0,19.12,43c-.27-.21-.5-.11-.51.22l-.24,3.42c0,.33-.38.35-.49,0l-1.5-4.8a1.4,1.4,0,0,0-.77-.78,23.91,23.91,0,0,0-3.1-.84c-1.38-.24-3.39-.39-3.39-.39-.34,0-.45.21-.25.49l2.06,3.76c.2.27,0,.54-.29.33l-4.51-3.6a3.68,3.68,0,0,0-2.86-.48c-1,.16-2.44.46-2.44.46a.68.68,0,0,0-.39.25.73.73,0,0,0-.14.45S.41,43,.54,44a3.63,3.63,0,0,0,1.25,2.62L6.48,50c.28.2.09.49-.23.37l-4.18-.94c-.32-.12-.5,0-.4.37,0,0,.69,1.89,1.31,3.16a24,24,0,0,0,1.66,2.74,1.34,1.34,0,0,0,1,.52l5,.13c.33,0,.41.38.1.48L7.51,58c-.31.1-.34.35-.07.55a14.29,14.29,0,0,0,3.05,1.66,13.09,13.09,0,0,0,5.9.5,25.13,25.13,0,0,0,4.34-1,9.55,9.55,0,0,1-.08-1.2,9.32,9.32,0,0,1,3.07-6.91"}),(0,p.jsx)(f.Path,{fill:"#5ba745",d:"M59.7,41.53a.73.73,0,0,0-.14-.45.68.68,0,0,0-.39-.25s-1.43-.3-2.44-.46a3.64,3.64,0,0,0-2.86.48l-4.51,3.6c-.26.21-.49-.06-.29-.33l2.06-3.76c.2-.28.09-.49-.25-.49,0,0-2,.15-3.39.39a23.91,23.91,0,0,0-3.1.84,1.4,1.4,0,0,0-.77.78l-1.5,4.8c-.11.32-.48.3-.49,0l-.24-3.42c0-.33-.24-.43-.51-.22a14.74,14.74,0,0,0-2.44,2.47A13.11,13.11,0,0,0,36.34,51c0,.18,0,.34-.06.52a9.26,9.26,0,0,1,3,8.1,24.1,24.1,0,0,0,4.34,1,13.09,13.09,0,0,0,5.9-.5,14.29,14.29,0,0,0,3.05-1.66c.27-.2.24-.45-.07-.55l-3.22-1.17c-.31-.1-.23-.47.1-.48l5-.13a1.38,1.38,0,0,0,1-.52A24.6,24.6,0,0,0,57,52.92c.61-1.27,1.31-3.16,1.31-3.16.1-.33-.08-.49-.4-.37l-4.18.94c-.32.12-.51-.17-.23-.37l4.69-3.34A3.63,3.63,0,0,0,59.46,44c.13-1,.24-2.47.24-2.47"}),(0,p.jsx)(f.Path,{fill:"#5ba745",d:"M46.5,25.61c0-.53-.35-.72-.8-.43l-4.86,2.66c-.45.28-.56-.27-.23-.69l4.66-6.23a2,2,0,0,0,.28-1.68,36.51,36.51,0,0,0-2.19-4.89,34,34,0,0,0-2.81-3.94c-.33-.41-.74-.35-.91.16l-2.28,5.68c-.16.5-.6.48-.59-.05l.28-8.93a2.54,2.54,0,0,0-.66-1.64S35,4.27,33.88,3.27,30.78.69,30.78.69a1.29,1.29,0,0,0-1.54,0s-1.88,1.49-3.12,2.59-2.48,2.35-2.48,2.35A2.5,2.5,0,0,0,23,7.27l.27,8.93c0,.53-.41.55-.58.05l-2.29-5.69c-.17-.5-.57-.56-.91-.14a35.77,35.77,0,0,0-3,4.2,35.55,35.55,0,0,0-2,4.62,2,2,0,0,0,.27,1.67l4.67,6.24c.33.42.23,1-.22.69l-4.87-2.66c-.45-.29-.82-.1-.82.43a18.6,18.6,0,0,0,.83,5.07,20.16,20.16,0,0,0,5.37,7.77c3.19,3,5.93,7.8,7.45,11.08A9.6,9.6,0,0,1,30,49.09a9.31,9.31,0,0,1,2.86.45c1.52-3.28,4.26-8.11,7.44-11.09a20.46,20.46,0,0,0,5.09-7,19,19,0,0,0,1.11-5.82"}),(0,p.jsx)(f.Path,{fill:"#5ba745",d:"M36.12,58.44A6.12,6.12,0,1,1,30,52.32a6.11,6.11,0,0,1,6.12,6.12"})]})},L=function(){return L=Object.assign||function(e){for(var t,n=1,r=arguments.length;nhere.',"wp-parsely"):s.code===$.ParselySuggestionsApiOpenAiError||s.code===$.ParselySuggestionsApiOpenAiUnavailable?s.message=(0,_.__)("The Parse.ly API returned an internal server error. Please retry with a different input, or try again later.","wp-parsely"):s.code===$.HttpRequestFailed&&s.message.includes("cURL error 28")?s.message=(0,_.__)("The Parse.ly API did not respond in a timely manner. Please try again later.","wp-parsely"):s.code===$.ParselySuggestionsApiSchemaError?s.message=(0,_.__)("The Parse.ly API returned a validation error. Please try again with different parameters.","wp-parsely"):s.code===$.ParselySuggestionsApiNoData?s.message=(0,_.__)("The Parse.ly API couldn't find any relevant data to fulfill the request. Please retry with a different input.","wp-parsely"):s.code===$.ParselySuggestionsApiOpenAiSchema?s.message=(0,_.__)("The Parse.ly API returned an incorrect response. Please try again later.","wp-parsely"):s.code===$.ParselySuggestionsApiAuthUnavailable&&(s.message=(0,_.__)("The Parse.ly API is currently unavailable. Please try again later.","wp-parsely")),s}return re(t,e),t.prototype.Message=function(e){return void 0===e&&(e=null),[$.PluginCredentialsNotSetMessageDetected,$.PluginSettingsSiteIdNotSet,$.PluginSettingsApiSecretNotSet].includes(this.code)?K(e):(this.code===$.FetchError&&(this.hint=this.Hint((0,_.__)("This error can sometimes be caused by ad-blockers or browser tracking protections. Please add this site to any applicable allow lists and try again.","wp-parsely"))),this.code!==$.ParselyApiForbidden&&this.code!==$.ParselySuggestionsApiNoAuthentication||(this.hint=this.Hint((0,_.__)("Please ensure that the Site ID and API Secret given in the plugin's settings are correct.","wp-parsely"))),this.code===$.HttpRequestFailed&&(this.hint=this.Hint((0,_.__)("The Parse.ly API cannot be reached. Please verify that you are online.","wp-parsely"))),(0,p.jsx)(W,{className:null==e?void 0:e.className,testId:"error",children:"

".concat(this.message,"

").concat(this.hint?this.hint:"")}))},t.prototype.Hint=function(e){return'

'.concat((0,_.__)("Hint:","wp-parsely")," ").concat(e,"

")},t.prototype.createErrorSnackbar=function(){//.test(this.message)||(0,h.dispatch)("core/notices").createNotice("error",this.message,{type:"snackbar"})},t}(Error),se=function(e){var t=e.isDetectingEnabled,n=e.onLinkChange,r=e.onLinkRemove,i=e.onLinkAdd,s=e.debounceValue,o=void 0===s?500:s,a=(0,h.useSelect)((function(e){return{blocks:(0,e("core/block-editor").getBlocks)()}}),[]).blocks,l=(0,b.useRef)(a),c=(0,b.useRef)(t);return(0,b.useEffect)((function(){var e=(0,z.debounce)((function(){for(var t=[],s=0;s0)return r(e.innerBlocks,t[s].innerBlocks);if(JSON.stringify(e)!==JSON.stringify(t[s])){var o=t[s],a=i.parseFromString(e.attributes.content||"","text/html"),l=i.parseFromString((null==o?void 0:o.attributes.content)||"","text/html"),c=Array.from(a.querySelectorAll("a[data-smartlink]")),u=Array.from(l.querySelectorAll("a[data-smartlink]")),d=c.filter((function(e){return!u.some((function(t){return t.dataset.smartlink===e.dataset.smartlink}))})),p=u.filter((function(e){return!c.some((function(t){return t.dataset.smartlink===e.dataset.smartlink}))})),f=c.filter((function(e){var t=u.find((function(t){return t.dataset.smartlink===e.dataset.smartlink}));return t&&t.outerHTML!==e.outerHTML}));(d.length>0||p.length>0||f.length>0)&&n.push({block:e,prevBlock:o,addedLinks:d,removedLinks:p,changedLinks:f})}}}))};return r(e,t),n}(a,l.current);o.length>0&&(o.forEach((function(e){e.changedLinks.length>0&&n&&n(e),e.addedLinks.length>0&&i&&i(e),e.removedLinks.length>0&&r&&r(e)})),l.current=a)}),o);return e(t),function(){e.cancel()}}),[a,o,t,i,n,r]),null},oe=function(e){var t=e.value,n=e.onChange,r=e.max,i=e.min,s=e.suffix,o=e.size,a=e.label,l=e.initialPosition,c=e.disabled,u=e.className;return(0,p.jsxs)("div",{className:"parsely-inputrange-control ".concat(u||""),children:[(0,p.jsx)(f.__experimentalHeading,{className:"parsely-inputrange-control__label",level:3,children:a}),(0,p.jsxs)("div",{className:"parsely-inputrange-control__controls",children:[(0,p.jsx)(f.__experimentalNumberControl,{disabled:c,value:t,suffix:(0,p.jsx)(f.__experimentalInputControlSuffixWrapper,{children:s}),size:null!=o?o:"__unstable-large",min:i,max:r,onChange:function(e){var t=parseInt(e,10);isNaN(t)||n(t)}}),(0,p.jsx)(f.RangeControl,{disabled:c,value:t,showTooltip:!1,initialPosition:l,onChange:function(e){n(e)},withInputField:!1,min:i,max:r})]})]})},ae=function(e,t,n,r){return new(n||(n=Promise))((function(i,s){function o(e){try{l(r.next(e))}catch(e){s(e)}}function a(e){try{l(r.throw(e))}catch(e){s(e)}}function l(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(o,a)}l((r=r.apply(e,t||[])).next())}))},le=function(e,t){var n,r,i,s,o={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return s={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function a(a){return function(l){return function(a){if(n)throw new TypeError("Generator is already executing.");for(;s&&(s=0,a[0]&&(o=0)),o;)try{if(n=1,r&&(i=2&a[0]?r.return:a[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,a[1])).done)return i;switch(r=0,i&&(a=[2&a[0],i.value]),a[0]){case 0:case 1:i=a;break;case 4:return o.label++,{value:a[1],done:!1};case 5:o.label++,r=a[1],a=[0];continue;case 7:a=o.ops.pop(),o.trys.pop();continue;default:if(!((i=(i=o.trys).length>0&&i[i.length-1])||6!==a[0]&&2!==a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&i[i.length-1])||6!==a[0]&&2!==a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&i[i.length-1])||6!==a[0]&&2!==a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&i[i.length-1])||6!==a[0]&&2!==a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&i[i.length-1])||6!==a[0]&&2!==a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]

","\n\x3c!-- /wp:paragraph --\x3e");t&&h((0,Q.parse)(n))}),[s]),(0,p.jsxs)("div",{className:"smart-linking-review-suggestion",children:[(0,p.jsx)(f.KeyboardShortcuts,{shortcuts:{left:o,right:a,up:o,down:a}}),(0,p.jsx)("div",{className:"review-suggestion-post-title",children:null===(t=s.post_data)||void 0===t?void 0:t.title}),(0,p.jsxs)("div",{className:"review-suggestion-preview",children:[!(null===(n=s.post_data)||void 0===n?void 0:n.is_first_paragraph)&&(0,p.jsx)($e,{topOrBottom:"top"}),(0,p.jsx)(Ze,{block:d[0],link:s,useOriginalBlock:!0}),!(null===(r=s.post_data)||void 0===r?void 0:r.is_last_paragraph)&&(0,p.jsx)($e,{topOrBottom:"bottom"})]}),(0,p.jsx)(f.__experimentalDivider,{}),(0,p.jsx)(We,{link:s}),(0,p.jsxs)("div",{className:"review-controls",children:[(0,p.jsx)(f.Tooltip,{shortcut:"←",text:(0,_.__)("Previous","wp-parsely"),children:(0,p.jsx)(f.Button,{disabled:!l,className:"wp-parsely-review-suggestion-previous",onClick:o,icon:He,children:(0,_.__)("Previous","wp-parsely")})}),(0,p.jsx)("div",{className:"reviews-controls-middle",children:(0,p.jsx)(f.Button,{target:"_blank",href:(null===(i=s.post_data)||void 0===i?void 0:i.edit_link)+"&smart-link="+s.uid,variant:"secondary",onClick:function(){P.trackEvent("smart_linking_open_in_editor_pressed",{type:"inbound",uid:s.uid})},children:(0,_.__)("Open in the Editor","wp-parsely")})}),(0,p.jsx)(f.Tooltip,{shortcut:"→",text:(0,_.__)("Next","wp-parsely"),children:(0,p.jsxs)(f.Button,{disabled:!c,onClick:a,className:"wp-parsely-review-suggestion-next",children:[(0,_.__)("Next","wp-parsely"),(0,p.jsx)(X,{icon:ze})]})})]})]})},Ye=function(e){var t=e.size,n=void 0===t?24:t,r=e.className,i=void 0===r?"wp-parsely-icon":r;return(0,p.jsxs)(f.SVG,{xmlns:"http://www.w3.org/2000/svg",className:i,width:n,height:n,viewBox:"0 0 24 24",fill:"none",children:[(0,p.jsx)(f.Path,{d:"M8.18983 5.90381L8.83642 7.54325L10.4758 8.18983L8.83642 8.8364L8.18983 10.4759L7.54324 8.8364L5.90381 8.18983L7.54324 7.54325L8.18983 5.90381Z"}),(0,p.jsx)(f.Path,{d:"M15.048 5.90381L15.9101 8.08972L18.0961 8.95186L15.9101 9.81397L15.048 11.9999L14.1859 9.81397L12 8.95186L14.1859 8.08972L15.048 5.90381Z"}),(0,p.jsx)(f.Path,{d:"M11.238 10.4761L12.3157 13.2085L15.048 14.2861L12.3157 15.3638L11.238 18.0962L10.1603 15.3638L7.42798 14.2861L10.1603 13.2085L11.238 10.4761Z"})]})},Je=function(e,t,n){if(n||2===arguments.length)for(var r,i=0,s=t.length;ii.bottom)&&(n.scrollTop=r.offsetTop-n.offsetTop)}}}}),[t,l]);var u=function(){var e=document.querySelector(".smart-linking-review-sidebar-tabs [data-active-item]"),t=null==e?void 0:e.nextElementSibling;t||(t=document.querySelector('.smart-linking-review-sidebar-tabs [role="tab"]')),t&&t.click()},d=(0,p.jsxs)("span",{className:"smart-linking-menu-label",children:[(0,_.__)("NEW","wp-parsely"),(0,p.jsx)(Ye,{})]}),h=[];n&&n.length>0&&h.push({name:"outbound",title:(0,_.__)("Outbound","wp-parsely")}),r&&r.length>0&&h.push({name:"inbound",title:(0,_.__)("Inbound","wp-parsely")});var v="outbound";return h=h.filter((function(e){return"outbound"===e.name&&r&&0===r.length&&(e.title=(0,_.__)("Outbound Smart Links","wp-parsely"),v="outbound"),"inbound"===e.name&&n&&0===n.length&&(e.title=(0,_.__)("Inbound Smart Links","wp-parsely"),v="inbound"),e})),(0,p.jsxs)("div",{className:"smart-linking-review-sidebar",ref:s,children:[(0,p.jsx)(f.KeyboardShortcuts,{shortcuts:{tab:function(){return u()},"shift+tab":function(){return u()}}}),(0,p.jsx)(f.TabPanel,{className:"smart-linking-review-sidebar-tabs",initialTabName:v,tabs:h,onSelect:function(e){var t,s;"outbound"===e&&n&&n.length>0&&i(n[0]),"inbound"===e&&r&&r.length>0&&i(r[0]),P.trackEvent("smart_linking_modal_tab_selected",{tab:e,total_inbound:null!==(t=null==r?void 0:r.length)&&void 0!==t?t:0,total_outbound:null!==(s=null==n?void 0:n.length)&&void 0!==s?s:0})},children:function(e){return(0,p.jsxs)(p.Fragment,{children:["outbound"===e.name&&(0,p.jsx)(p.Fragment,{children:n&&0!==n.length?n.map((function(e,n){return(0,p.jsxs)(f.MenuItem,{ref:function(e){o.current[n]=e},className:(null==t?void 0:t.uid)===e.uid?"is-selected":"",role:"menuitemradio",isSelected:(null==t?void 0:t.uid)===e.uid,onClick:function(){return i(e)},children:[(0,p.jsx)("span",{className:"smart-linking-menu-item",children:e.text}),!e.applied&&d]},e.uid)})):(0,p.jsxs)(p.Fragment,{children:[" ",(0,_.__)("No outbound links found.","wp-parsely")]})}),"inbound"===e.name&&(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)("div",{className:"review-sidebar-tip",children:(0,_.__)("This section shows external posts that link back to the current post.","wp-parsely")}),r&&0!==r.length?r.map((function(e,r){var s;return(0,p.jsx)(f.MenuItem,{ref:function(e){o.current[(n?n.length:0)+r]=e},className:(null==t?void 0:t.uid)===e.uid?"is-selected":"",role:"menuitemradio",isSelected:(null==t?void 0:t.uid)===e.uid,onClick:function(){return i(e)},children:(0,p.jsx)("span",{className:"smart-linking-menu-item",children:null===(s=e.post_data)||void 0===s?void 0:s.title})},e.uid)})):(0,p.jsxs)(p.Fragment,{children:[" ",(0,_.__)("No inbound links found.","wp-parsely")]})]})]})}})]})},Xe=(0,p.jsx)(x.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",children:(0,p.jsx)(x.Path,{d:"M12 13.06l3.712 3.713 1.061-1.06L13.061 12l3.712-3.712-1.06-1.06L12 10.938 8.288 7.227l-1.061 1.06L10.939 12l-3.712 3.712 1.06 1.061L12 13.061z"})}),et=(0,p.jsx)(x.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",children:(0,p.jsx)(x.Path,{d:"M16.7 7.1l-6.3 8.5-3.3-2.5-.9 1.2 4.5 3.4L17.9 8z"})}),tt=function(e){var t,n,r,i,s=null===(t=e.link.match)||void 0===t?void 0:t.blockId,o=(0,h.useSelect)((function(e){var t=e("core/block-editor"),n=t.getBlock,r=t.getBlockParents;return s?{block:n(s),parents:r(s).map((function(e){return n(e)})).filter((function(e){return void 0!==e}))}:{block:void 0,parents:[]}}),[s]),a=o.block,l=o.parents;return a?(0,p.jsxs)("div",{className:"review-suggestions-breadcrumbs",children:[l.map((function(e,t){var n;return(0,p.jsxs)("span",{children:[(0,p.jsx)("span",{className:"breadcrumbs-parent-block",children:null===(n=(0,Q.getBlockType)(e.name))||void 0===n?void 0:n.title}),(0,p.jsx)("span",{className:"breadcrumbs-parent-separator",children:" / "})]},t)})),(0,p.jsxs)("span",{className:"breadcrumbs-current-block",children:[(0,p.jsx)("span",{className:"breadcrumbs-current-block-type",children:null===(n=(0,Q.getBlockType)(a.name))||void 0===n?void 0:n.title}),(null===(i=null===(r=a.attributes)||void 0===r?void 0:r.metadata)||void 0===i?void 0:i.name)&&(0,p.jsx)("span",{className:"breadcrumbs-current-block-name",children:a.attributes.metadata.name})]})]}):(0,p.jsx)(p.Fragment,{})},nt=function(e){var t,n=e.link,r=(0,b.useState)(n.href),i=r[0],s=r[1],o=(0,b.useState)(null===(t=n.destination)||void 0===t?void 0:t.post_type),a=o[0],l=o[1],c=(0,b.useRef)(null),u=(0,h.useDispatch)(Te).updateSmartLink;return(0,b.useEffect)((function(){n.destination?l(n.destination.post_type):(l((0,_.__)("External","wp-parsely")),De.getInstance().getPostTypeByURL(n.href).then((function(e){e&&l(e.post_type),n.destination=e,u(n)})))}),[n,u]),(0,b.useEffect)((function(){var e=function(){if(c.current){var e=c.current.offsetWidth,t=Math.floor(e/8);s(function(e,t){var n=e.replace(/(^\w+:|^)\/\//,"").replace(/^www\./,"");if(!t||n.length<=t)return n;var r=n.split("/")[0],i=n.substring(r.length);t-=r.length;var s=Math.floor((t-3)/2),o=i.substring(0,s),a=i.substring(i.length-s);return"".concat(r).concat(o,"...").concat(a)}(n.href,t))}};return e(),window.addEventListener("resize",e),function(){window.removeEventListener("resize",e)}}),[n]),(0,p.jsx)(f.MenuItem,{ref:c,info:i,iconPosition:"left",icon:Ge,shortcut:a,className:"block-editor-link-control__search-item wp-parsely-link-suggestion-link-details",children:n.title})},rt=function(e){var t=e.link,n=e.onNext,r=e.onPrevious,i=e.onAccept,s=e.onReject,o=e.onRemove,a=e.onSelectInEditor,l=e.hasPrevious,c=e.hasNext;if(t&&void 0!==t.post_data)return(0,p.jsx)(Ke,{link:t,onNext:n,onPrevious:r,onAccept:i,onReject:s,onRemove:o,onSelectInEditor:a,hasPrevious:l,hasNext:c});if(!(null==t?void 0:t.match))return(0,p.jsx)(p.Fragment,{children:(0,_.__)("This Smart Link does not have any matches in the current content.","wp-parsely")});var u=t.match.blockId,d=(0,h.select)("core/block-editor").getBlock(u),v=t.applied;return d?(0,p.jsxs)("div",{className:"smart-linking-review-suggestion",children:[(0,p.jsx)(f.KeyboardShortcuts,{shortcuts:{left:r,right:n,up:r,down:n,a:function(){t&&!t.applied&&i()},r:function(){t&&(t.applied?o():s())}}}),(0,p.jsx)(tt,{link:t}),(0,p.jsx)("div",{className:"review-suggestion-preview",children:(0,p.jsx)(Ze,{block:d,link:t})}),(0,p.jsx)(f.__experimentalDivider,{}),(0,p.jsx)(nt,{link:t}),(0,p.jsxs)("div",{className:"review-controls",children:[(0,p.jsx)(f.Tooltip,{shortcut:"←",text:(0,_.__)("Previous","wp-parsely"),children:(0,p.jsx)(f.Button,{disabled:!l,className:"wp-parsely-review-suggestion-previous",onClick:r,icon:He,children:(0,_.__)("Previous","wp-parsely")})}),(0,p.jsxs)("div",{className:"reviews-controls-middle",children:[!v&&(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(f.Tooltip,{shortcut:"R",text:(0,_.__)("Reject","wp-parsely"),children:(0,p.jsx)(f.Button,{className:"wp-parsely-review-suggestion-reject",icon:Xe,onClick:s,variant:"secondary",children:(0,_.__)("Reject","wp-parsely")})}),(0,p.jsx)(f.Tooltip,{shortcut:"A",text:(0,_.__)("Accept","wp-parsely"),children:(0,p.jsx)(f.Button,{className:"wp-parsely-review-suggestion-accept",icon:et,onClick:i,variant:"secondary",children:(0,_.__)("Accept","wp-parsely")})})]}),v&&(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)(f.Tooltip,{shortcut:"R",text:(0,_.__)("Remove","wp-parsely"),children:(0,p.jsx)(f.Button,{className:"wp-parsely-review-suggestion-reject",icon:Xe,onClick:o,variant:"secondary",children:(0,_.__)("Remove","wp-parsely")})}),(0,p.jsx)(f.Button,{className:"wp-parsely-review-suggestion-accept",onClick:a,variant:"secondary",children:(0,_.__)("Select in Editor","wp-parsely")})]})]}),(0,p.jsx)(f.Tooltip,{shortcut:"→",text:(0,_.__)("Next","wp-parsely"),children:(0,p.jsxs)(f.Button,{disabled:!c,onClick:n,className:"wp-parsely-review-suggestion-next",children:[(0,_.__)("Next","wp-parsely"),(0,p.jsx)(X,{icon:ze})]})})]})]}):(0,p.jsx)(p.Fragment,{children:(0,_.__)("No block is selected.","wp-parsely")})},it=function(e,t,n,r){return new(n||(n=Promise))((function(i,s){function o(e){try{l(r.next(e))}catch(e){s(e)}}function a(e){try{l(r.throw(e))}catch(e){s(e)}}function l(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(o,a)}l((r=r.apply(e,t||[])).next())}))},st=function(e,t){var n,r,i,s,o={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return s={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function a(a){return function(l){return function(a){if(n)throw new TypeError("Generator is already executing.");for(;s&&(s=0,a[0]&&(o=0)),o;)try{if(n=1,r&&(i=2&a[0]?r.return:a[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,a[1])).done)return i;switch(r=0,i&&(a=[2&a[0],i.value]),a[0]){case 0:case 1:i=a;break;case 4:return o.label++,{value:a[1],done:!1};case 5:o.label++,r=a[1],a=[0];continue;case 7:a=o.ops.pop(),o.trys.pop();continue;default:if(!((i=(i=o.trys).length>0&&i[i.length-1])||6!==a[0]&&2!==a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&(a=o[0],(l=a.parentNode)&&(c=document.createTextNode(null!==(u=a.textContent)&&void 0!==u?u:""),l.replaceChild(c,a),te.updateBlockAttributes(n,{content:s.innerHTML}))),[4,E(t.uid)]):[2]):[2];case 1:return d.sent(),[2]}}))}))},C=(0,b.useCallback)((function(){c(!1),w().filter((function(e){return!e.applied})).length>0?o(!0):(ne.unlockPostAutosaving("smart-linking-review-modal"),t())}),[w,t]),A=function(e){o(!1),e?(c(!1),T().then((function(){C()}))):c(!0)},O=function(){if(ue(k)){var e=g.indexOf(k);if(!g[t=e+1])return;S(g[t])}else{var t;if(e=v.indexOf(k),!v[t=e+1])return;S(v[t])}},R=function(){if(ue(k)){var e=g.indexOf(k);if(!g[t=e-1])return;S(g[t])}else{var t;if(e=v.indexOf(k),!v[t=e-1])return;S(v[t])}};return(0,b.useEffect)((function(){l?ne.lockPostAutosaving("smart-linking-review-modal"):l&&0===d.length&&C()}),[l,t,d,C]),(0,b.useEffect)((function(){c(n)}),[n]),(0,p.jsxs)(p.Fragment,{children:[l&&(0,p.jsx)(f.Modal,{title:(0,_.__)("Review Smart Links","wp-parsely"),className:"wp-parsely-smart-linking-review-modal",onRequestClose:C,shouldCloseOnClickOutside:!1,shouldCloseOnEsc:!1,children:(0,p.jsxs)("div",{className:"smart-linking-modal-body",children:[(0,p.jsx)(Qe,{outboundLinks:v,inboundLinks:g,activeLink:k,setSelectedLink:S}),k&&(ue(k)?(0,p.jsx)(Ke,{link:k,onNext:O,onPrevious:R,hasNext:g.indexOf(k)0}):(0,p.jsx)(rt,{link:k,hasNext:m().indexOf(k)0,onNext:O,onPrevious:R,onAccept:function(){return it(void 0,void 0,void 0,(function(){var e,t;return st(this,(function(n){switch(n.label){case 0:return k.match?(r(k),[4,(i=k.match.blockId,s=k,it(void 0,void 0,void 0,(function(){var e,t;return st(this,(function(n){switch(n.label){case 0:return(e=document.createElement("a")).href=s.href,e.title=s.title,e.setAttribute("data-smartlink",s.uid),(t=(0,h.select)("core/block-editor").getBlock(i))?(fe(t,s,e),s.applied=!0,[4,L(s)]):[2];case 1:return n.sent(),[2]}}))})))]):[2];case 1:return n.sent(),P.trackEvent("smart_linking_link_accepted",{link:k.href,title:k.title,text:k.text,uid:k.uid}),0===y().length?(C(),[2]):(e=v.indexOf(k),v[t=e+1]?S(v[t]):S(v[0]),[2])}var i,s}))}))},onReject:function(){return it(void 0,void 0,void 0,(function(){var e,t;return st(this,(function(n){switch(n.label){case 0:return e=v.indexOf(k),v[t=e+1]?S(v[t]):v[0]?S(v[0]):C(),[4,E(k.uid)];case 1:return n.sent(),P.trackEvent("smart_linking_link_rejected",{link:k.href,title:k.title,text:k.text,uid:k.uid}),[2]}}))}))},onRemove:function(){return it(void 0,void 0,void 0,(function(){var e,t,n,r;return st(this,(function(i){switch(i.label){case 0:return k.match?(e=(0,h.select)("core/block-editor").getBlock(k.match.blockId))?(t=m(),n=t.indexOf(k),r=n-1,[4,N(e,k)]):[3,2]:[2];case 1:if(i.sent(),P.trackEvent("smart_linking_link_removed",{link:k.href,title:k.title,text:k.text,uid:k.uid}),0===(t=m()).length&&g.length>0)return S(g[0]),[2];if(0===t.length&&0===g.length)return C(),[2];if(t[r])return S(t[r]),[2];S(t[0]),i.label=2;case 2:return[2]}}))}))},onSelectInEditor:function(){if(k.match){var e=(0,h.select)("core/block-editor").getBlock(k.match.blockId);if(e){te.selectBlock(e.clientId);var t=document.querySelector('[data-block="'.concat(e.clientId,'"]'));t&&ke(t,k.uid),P.trackEvent("smart_linking_select_in_editor_pressed",{type:"outbound",uid:k.uid}),C()}}}}))]})}),s&&(0,p.jsxs)(f.Modal,{title:(0,_.__)("Review Smart Links","wp-parsely"),onRequestClose:function(){return A(!1)},className:"wp-parsely-smart-linking-close-dialog",children:[(0,_.__)("Are you sure you want to close? All un-accepted smart links will not be added.","wp-parsely"),(0,p.jsxs)("div",{className:"smart-linking-close-dialog-actions",children:[(0,p.jsx)(f.Button,{variant:"secondary",onClick:function(){return A(!1)},children:(0,_.__)("Go Back","wp-parsely")}),(0,p.jsx)(f.Button,{variant:"primary",onClick:function(){return A(!0)},children:(0,_.__)("Close","wp-parsely")})]})]})]})})),at=function(){return at=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&i[i.length-1])||6!==a[0]&&2!==a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&k("success",/* translators: %d: number of smart links applied */ /* translators: %d: number of smart links applied */ (0,_.sprintf)((0,_.__)("%s smart links successfully applied.","wp-parsely"),g),{type:"snackbar"}):y(0)}),[w]),(0,b.useEffect)((function(){if(!(Object.keys(R).length>0)){var e={maxLinksPerPost:a.SmartLinking.MaxLinks};te(e)}}),[te,a]);var he=(0,h.useSelect)((function(e){var t=e("core/block-editor"),r=t.getSelectedBlock,i=t.getBlock,s=t.getBlocks,o=e("core/editor"),a=o.getEditedPostContent,l=o.getCurrentPostAttribute;return{allBlocks:s(),selectedBlock:n?i(n):r(),postContent:a(),postPermalink:l("link")}}),[n]),ve=he.allBlocks,me=he.selectedBlock,xe=he.postContent,ke=he.postPermalink,Se=function(e){return lt(void 0,void 0,void 0,(function(){var t,n,r,i,s;return ct(this,(function(o){switch(o.label){case 0:t=[],o.label=1;case 1:return o.trys.push([1,4,,9]),[4,re((n=E||!me)?_e.All:_e.Selected)];case 2:return o.sent(),a=ke.replace(/^https?:\/\//i,""),r=["http://"+a,"https://"+a],i=function(e){return e.map((function(e){return e.href}))}(F),r.push.apply(r,i),[4,De.getInstance().generateSmartLinks(me&&!n?(0,Q.getBlockContent)(me):xe,O,r)];case 3:return t=o.sent(),[3,9];case 4:if((s=o.sent()).code&&s.code===$.ParselyAborted)throw s.numRetries=3-e,s;return e>0&&s.retryFetch?(console.error(s),[4,ce(!0)]):[3,8];case 5:return o.sent(),[4,ue()];case 6:return o.sent(),[4,Se(e-1)];case 7:return[2,o.sent()];case 8:throw s;case 9:return[2,t]}var a}))}))},Pe=function(){for(var e=[],t=0;t[type="button"]').forEach((function(e){e.setAttribute("disabled","disabled")}))},Ne=function(){document.querySelectorAll('.edit-post-header__settings>[type="button"]').forEach((function(e){e.removeAttribute("disabled")})),ne.unlockPostSaving("wp-parsely-block-overlay")};return(0,p.jsxs)("div",{className:"wp-parsely-smart-linking",children:[(0,p.jsx)(se,{isDetectingEnabled:!L,onLinkRemove:function(e){!function(e){ae(this,void 0,void 0,(function(){var t,n,r;return le(this,(function(i){switch(i.label){case 0:return[4,we((0,Q.getBlockContent)(e),e.clientId)];case 1:return t=i.sent(),n=t.missingSmartLinks,r=t.didAnyFixes,n.forEach((function(e){(0,h.dispatch)(Te).removeSmartLink(e.uid)})),[2,r]}}))}))}(e.block)}}),(0,p.jsxs)(f.PanelRow,{className:t,children:[(0,p.jsxs)("div",{className:"smart-linking-text",children:[(0,_.__)("Automatically insert links to your most relevant, top performing content.","wp-parsely"),(0,p.jsxs)(f.Button,{href:"https://docs.parse.ly/plugin-content-helper/#h-smart-linking-beta",target:"_blank",variant:"link",children:[(0,_.__)("Learn more about Parse.ly AI","wp-parsely"),(0,p.jsx)(X,{icon:ee,size:18,className:"parsely-external-link-icon"})]})]}),C&&(0,p.jsx)(f.Notice,{status:"info",onRemove:function(){return Z(null)},className:"wp-parsely-content-helper-error",children:C.Message()}),w&&g>0&&(0,p.jsx)(f.Notice,{status:"success",onRemove:function(){return x(!1)},className:"wp-parsely-smart-linking-suggested-links",children:(0,_.sprintf)(/* translators: 1 - number of smart links generated */ /* translators: 1 - number of smart links generated */ (0,_.__)("Successfully added %s smart links.","wp-parsely"),g>0?g:A.length)}),(0,p.jsx)(Ce,{disabled:T,selectedBlock:me,onSettingChange:function(e,t){var n;d({SmartLinking:at(at({},a.SmartLinking),(n={},n[e]=t,n))}),"MaxLinks"===e&&oe(t)}}),(0,p.jsx)("div",{className:"smart-linking-generate",children:(0,p.jsx)(f.Button,{onClick:function(){return lt(void 0,void 0,void 0,(function(){var e,t,n,r,s,o,a,l;return ct(this,(function(c){switch(c.label){case 0:return[4,q(!0)];case 1:return c.sent(),[4,de()];case 2:return c.sent(),[4,Z(null)];case 3:return c.sent(),x(!1),P.trackEvent("smart_linking_generate_pressed",{is_full_content:E,selected_block:null!==(o=null==me?void 0:me.name)&&void 0!==o?o:"none",context:i}),[4,Pe(E?"all":null==me?void 0:me.clientId)];case 4:c.sent(),e=setTimeout((function(){var e;q(!1),P.trackEvent("smart_linking_generate_timeout",{is_full_content:E,selected_block:null!==(e=null==me?void 0:me.name)&&void 0!==e?e:"none",context:i}),je(E?"all":null==me?void 0:me.clientId)}),18e4),t=I,c.label=5;case 5:return c.trys.push([5,8,10,15]),[4,Se(3)];case 6:return n=c.sent(),[4,(u=n,lt(void 0,void 0,void 0,(function(){var e;return ct(this,(function(t){switch(t.label){case 0:return u=u.filter((function(e){return!F.some((function(t){return t.uid===e.uid&&t.applied}))})),e=ke.replace(/^https?:\/\//,"").replace(/\/+$/,""),u=(u=u.filter((function(t){return!t.href.includes(e)||(console.warn("PCH Smart Linking: Skipping self-reference link: ".concat(t.href)),!1)}))).filter((function(e){return!F.some((function(t){return t.href===e.href?(console.warn("PCH Smart Linking: Skipping duplicate link: ".concat(e.href)),!0):t.text===e.text&&t.offset!==e.offset&&(console.warn("PCH Smart Linking: Skipping duplicate link text: ".concat(e.text)),!0)}))})),u=(u=ge(E?ve:[me],u,{}).filter((function(e){return e.match}))).filter((function(e){if(!e.match)return!1;var t=e.match.blockLinkPosition,n=t+e.text.length;return!F.some((function(r){if(!r.match)return!1;if(e.match.blockId!==r.match.blockId)return!1;var i=r.match.blockLinkPosition,s=i+r.text.length;return t>=i&&n<=s}))})),[4,W(u)];case 1:return t.sent(),[2,u]}}))})))];case 7:if(0===c.sent().length)throw new ie((0,_.__)("No smart links were generated.","wp-parsely"),$.ParselySuggestionsApiNoData,"");return pe(!0),[3,15];case 8:return r=c.sent(),s=new ie(null!==(a=r.message)&&void 0!==a?a:"An unknown error has occurred.",null!==(l=r.code)&&void 0!==l?l:$.UnknownError),r.code&&r.code===$.ParselyAborted&&(s.message=(0,_.sprintf)(/* translators: %d: number of retry attempts, %s: attempt plural */ /* translators: %d: number of retry attempts, %s: attempt plural */ (0,_.__)("The Smart Linking process was cancelled after %1$d %2$s.","wp-parsely"),r.numRetries,(0,_._n)("attempt","attempts",r.numRetries,"wp-parsely"))),console.error(r),[4,Z(s)];case 9:return c.sent(),s.createErrorSnackbar(),[3,15];case 10:return[4,q(!1)];case 11:return c.sent(),[4,re(t)];case 12:return c.sent(),[4,ce(!1)];case 13:return c.sent(),[4,je(E?"all":null==me?void 0:me.clientId)];case 14:return c.sent(),clearTimeout(e),[7];case 15:return[2]}var u}))}))},variant:"primary",isBusy:T,disabled:T,children:M?(0,_.sprintf)(/* translators: %1$d: number of retry attempts, %2$d: maximum number of retries */ /* translators: %1$d: number of retry attempts, %2$d: maximum number of retries */ -(0,_.__)("Retrying… Attempt %1$d of %2$d","wp-parsely"),D,3):T?(0,_.__)("Generating Smart Links…","wp-parsely"):(0,_.__)("Add Smart Links","wp-parsely")})}),(G.length>0||V.length>0)&&(0,p.jsx)("div",{className:"smart-linking-manage",children:(0,p.jsx)(f.Button,{onClick:function(){return lt(void 0,void 0,void 0,(function(){var e,t;return ct(this,(function(n){switch(n.label){case 0:return[4,be()];case 1:return e=n.sent(),t=ye(),[4,W(t)];case 2:return n.sent(),pe(!0),P.trackEvent("smart_linking_review_pressed",{num_smart_links:F.length,has_fixed_links:e,context:i}),[2]}}))}))},variant:"secondary",disabled:T,children:(0,_.__)("Review Smart Links","wp-parsely")})})]}),L&&(0,p.jsx)(ot,{isOpen:L,onAppliedLink:function(){y((function(e){return e+1}))},onClose:function(){x(!0),pe(!1)}})]})},ft=function(){return ft=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&i[i.length-1])||6!==a[0]&&2!==a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0)&&(t(),e())}))}))]}))},new((n=void 0)||(n=Promise))((function(i,s){function o(e){try{l(r.next(e))}catch(e){s(e)}}function a(e){try{l(r.throw(e))}catch(e){s(e)}}function l(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(o,a)}l((r=r.apply(e,t||[])).next())}));var e,t,n,r}().then((function(){var t=document.querySelector(".wp-block-post-content");ke(t,e)}))})))},Pt=(0,p.jsx)(x.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",children:(0,p.jsx)(x.Path,{d:"M7 11.5h10V13H7z"})}),jt=(0,p.jsx)(x.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",children:(0,p.jsx)(x.Path,{d:"M13 19h-2v-2h2v2zm0-6h-2v-2h2v2zm0-6h-2V5h2v2z"})}),Tt=function(e){var t=e.title,n=e.icon,r=e.subtitle,i=e.level,s=void 0===i?2:i,o=e.children,a=e.controls,l=e.onClick,c=e.isOpen,u=e.isLoading,d=e.dropdownChildren;return(0,p.jsxs)("div",{className:"performance-stat-panel",children:[(0,p.jsxs)(f.__experimentalHStack,{className:"panel-header level-"+s,children:[(0,p.jsx)(f.__experimentalHeading,{level:s,children:t}),r&&!c&&(0,p.jsx)("span",{className:"panel-subtitle",children:r}),a&&!d&&(0,p.jsx)(f.DropdownMenu,{icon:n,label:(0,_.__)("Settings","wp-parsely"),className:"panel-settings-button",controls:a}),d&&(0,p.jsx)(f.DropdownMenu,{icon:n,label:(0,_.__)("Settings","wp-parsely"),className:"panel-settings-button",children:d}),n&&!d&&!a&&(0,p.jsx)(f.Button,{icon:n,className:"panel-settings-button",isPressed:c,onClick:l})]}),(0,p.jsx)("div",{className:"panel-body",children:u?(0,p.jsx)("div",{className:"parsely-spinner-wrapper","data-testid":"parsely-spinner-wrapper",children:(0,p.jsx)(f.Spinner,{})}):o})]})};function Lt(e,t,n){void 0===t&&(t=1),void 0===n&&(n="");var r=parseInt(e.replace(/\D/g,""),10);if(r<1e3)return e;r<1e4&&(t=1);var i=r,s=r.toString(),o="",a=0;return Object.entries({1e3:"k","1,000,000":"M","1,000,000,000":"B","1,000,000,000,000":"T","1,000,000,000,000,000":"Q"}).forEach((function(e){var n=e[0],l=e[1],c=parseInt(n.replace(/\D/g,""),10);if(r>=c){var u=t;(i=r/c)%1>1/a&&(u=i>10?1:2),u=parseFloat(i.toFixed(2))===parseFloat(i.toFixed(0))?0:u,s=i.toFixed(u),o=l}a=c})),s+n+o}var Et=function(e){var t=e.data,n=e.isLoading,r=(0,b.useState)(m.Views),i=r[0],s=r[1],o=(0,b.useState)(!1),a=o[0],l=o[1];n||delete t.referrers.types.totals;var c=function(e){switch(e){case"social":return(0,_.__)("Social","wp-parsely");case"search":return(0,_.__)("Search","wp-parsely");case"other":return(0,_.__)("Other","wp-parsely");case"internal":return(0,_.__)("Internal","wp-parsely");case"direct":return(0,_.__)("Direct","wp-parsely")}return e},u=(0,_.sprintf)((0,_.__)("By %s","wp-parsely"),V(i)); +(0,_.__)("Retrying… Attempt %1$d of %2$d","wp-parsely"),D,3):T?(0,_.__)("Generating Smart Links…","wp-parsely"):(0,_.__)("Add Smart Links","wp-parsely")})}),(G.length>0||V.length>0)&&(0,p.jsx)("div",{className:"smart-linking-manage",children:(0,p.jsx)(f.Button,{onClick:function(){return lt(void 0,void 0,void 0,(function(){var e,t;return ct(this,(function(n){switch(n.label){case 0:return[4,be()];case 1:return e=n.sent(),t=ye(),[4,W(t)];case 2:return n.sent(),pe(!0),P.trackEvent("smart_linking_review_pressed",{num_smart_links:F.length,has_fixed_links:e,context:i}),[2]}}))}))},variant:"secondary",disabled:T,children:(0,_.__)("Review Smart Links","wp-parsely")})})]}),L&&(0,p.jsx)(ot,{isOpen:L,onAppliedLink:function(){y((function(e){return e+1}))},onClose:function(){x(!0),pe(!1)}})]})},ft=function(){return ft=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&i[i.length-1])||6!==a[0]&&2!==a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0)&&(t(),e())}))}))]}))},new((n=void 0)||(n=Promise))((function(i,s){function o(e){try{l(r.next(e))}catch(e){s(e)}}function a(e){try{l(r.throw(e))}catch(e){s(e)}}function l(e){var t;e.done?i(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(o,a)}l((r=r.apply(e,t||[])).next())}));var e,t,n,r}().then((function(){var t=document.querySelector(".wp-block-post-content");ke(t,e)}))})))},Pt=(0,p.jsx)(x.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",children:(0,p.jsx)(x.Path,{d:"M7 11.5h10V13H7z"})}),jt=(0,p.jsx)(x.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",children:(0,p.jsx)(x.Path,{d:"M13 19h-2v-2h2v2zm0-6h-2v-2h2v2zm0-6h-2V5h2v2z"})}),Tt=function(e){var t=e.title,n=e.icon,r=e.subtitle,i=e.level,s=void 0===i?2:i,o=e.children,a=e.controls,l=e.onClick,c=e.isOpen,u=e.isLoading,d=e.dropdownChildren;return(0,p.jsxs)("div",{className:"performance-stat-panel",children:[(0,p.jsxs)(f.__experimentalHStack,{className:"panel-header level-"+s,children:[(0,p.jsx)(f.__experimentalHeading,{level:s,children:t}),r&&!c&&(0,p.jsx)("span",{className:"panel-subtitle",children:r}),a&&!d&&(0,p.jsx)(f.DropdownMenu,{icon:n,label:(0,_.__)("Settings","wp-parsely"),className:"panel-settings-button",controls:a}),d&&(0,p.jsx)(f.DropdownMenu,{icon:n,label:(0,_.__)("Settings","wp-parsely"),className:"panel-settings-button",children:d}),n&&!d&&!a&&(0,p.jsx)(f.Button,{icon:n,className:"panel-settings-button",isPressed:c,onClick:l})]}),(0,p.jsx)("div",{className:"panel-body",children:u?(0,p.jsx)("div",{className:"parsely-spinner-wrapper","data-testid":"parsely-spinner-wrapper",children:(0,p.jsx)(f.Spinner,{})}):o})]})};function Lt(e,t,n){void 0===t&&(t=1),void 0===n&&(n="");var r=parseInt(e.replace(/\D/g,""),10);if(r<1e3)return e;r<1e4&&(t=1);var i=r,s=r.toString(),o="",a=0;return Object.entries({1e3:"k","1,000,000":"M","1,000,000,000":"B","1,000,000,000,000":"T","1,000,000,000,000,000":"Q"}).forEach((function(e){var n=e[0],l=e[1],c=parseInt(n.replace(/\D/g,""),10);if(r>=c){var u=t;(i=r/c)%1>1/a&&(u=i>10?1:2),u=parseFloat(i.toFixed(2))===parseFloat(i.toFixed(0))?0:u,s=i.toFixed(u),o=l}a=c})),s+n+o}var Et=function(e){var t=e.data,n=e.isLoading,r=(0,b.useState)(m.Views),i=r[0],s=r[1],o=(0,b.useState)(!1),a=o[0],l=o[1];n||delete t.referrers.types.totals;var c=function(e){switch(e){case"social":return(0,_.__)("Social","wp-parsely");case"search":return(0,_.__)("Search","wp-parsely");case"other":return(0,_.__)("Other","wp-parsely");case"internal":return(0,_.__)("Internal","wp-parsely");case"direct":return(0,_.__)("Direct","wp-parsely")}return e},u=(0,_.sprintf)((0,_.__)("By %s","wp-parsely"),V(i)); /* translators: %s: metric description */return(0,p.jsxs)(Tt,{title:(0,_.__)("Categories","wp-parsely"),level:3,subtitle:u,isOpen:a,onClick:function(){return l(!a)},children:[a&&(0,p.jsx)("div",{className:"panel-settings",children:(0,p.jsx)(f.SelectControl,{value:i,prefix:(0,_.__)("By: ","wp-parsely"),onChange:function(e){D(e,m)&&s(e)},children:Object.values(m).map((function(e){return(0,p.jsxs)("option",{value:e,disabled:"avg_engaged"===e,children:[V(e),"avg_engaged"===e&&(0,_.__)(" (coming soon)","wp-parsely")]},e)}))})}),n?(0,p.jsx)("div",{className:"parsely-spinner-wrapper","data-testid":"parsely-spinner-wrapper",children:(0,p.jsx)(f.Spinner,{})}):(0,p.jsxs)("div",{children:[(0,p.jsx)("div",{className:"multi-percentage-bar",children:Object.entries(t.referrers.types).map((function(e){var t=e[0],n=e[1],r=(0,_.sprintf)(/* translators: 1: Referrer type, 2: Percentage value, %%: Escaped percent sign */ /* translators: 1: Referrer type, 2: Percentage value, %%: Escaped percent sign */ (0,_.__)("%1$s: %2$s%%","wp-parsely"),c(t),n.viewsPercentage);return(0,p.jsx)(f.Tooltip /* translators: %s: percentage value */,{ @@ -25,4 +25,4 @@ message:(0,_.sprintf)((0,_.__)('by author "%1$s"',"wp-parsely"),n.value)};throw (0,_.__)("Top related posts by %1$s in the %2$s.","wp-parsely"),I.value,F(r,!0)):null!=E?E:""})}),j&&j.Message(),x&&(0,p.jsx)("div",{className:"related-posts-loading-message","data-testid":"parsely-related-posts-loading-message",children:(0,_.__)("Loading…","wp-parsely")}),!x&&!j&&0===A.length&&(0,p.jsx)("div",{className:"related-posts-empty","data-testid":"parsely-related-posts-empty",children:(0,_.__)("No related posts found.","wp-parsely")}),!x&&A.length>0&&(0,p.jsx)("div",{className:"related-posts-list",children:A.map((function(e){return(0,p.jsx)(hn,{metric:i,post:e,postContent:H},e.id)}))})]})]})]})},jn=(0,p.jsx)(x.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",children:(0,p.jsx)(x.Path,{d:"m19 7-3-3-8.5 8.5-1 4 4-1L19 7Zm-7 11.5H5V20h7v-1.5Z"})}),Tn=function(){return(0,p.jsx)(f.SVG,{xmlns:"http://www.w3.org/2000/svg",width:"18",height:"18",viewBox:"0 0 18 18",fill:"none",children:(0,p.jsx)(f.Path,{fillRule:"evenodd",clipRule:"evenodd",d:"M13.5034 7.91642L9 12.0104L4.49662 7.91642L5.25337 7.08398L8.99999 10.49L12.7466 7.08398L13.5034 7.91642Z",fill:"#1E1E1E"})})},Ln={journalist:{label:(0,_.__)("Journalist","wp-parsely")},editorialWriter:{label:(0,_.__)("Editorial Writer","wp-parsely")},investigativeReporter:{label:(0,_.__)("Investigative Reporter","wp-parsely")},techAnalyst:{label:(0,_.__)("Tech Analyst","wp-parsely")},businessAnalyst:{label:(0,_.__)("Business Analyst","wp-parsely")},culturalCommentator:{label:(0,_.__)("Cultural Commentator","wp-parsely")},scienceCorrespondent:{label:(0,_.__)("Science Correspondent","wp-parsely")},politicalAnalyst:{label:(0,_.__)("Political Analyst","wp-parsely")},healthWellnessAdvocate:{label:(0,_.__)("Health and Wellness Advocate","wp-parsely")},environmentalJournalist:{label:(0,_.__)("Environmental Journalist","wp-parsely")},custom:{label:(0,_.__)("Custom Persona","wp-parsely"),icon:jn}},En=Object.keys(Ln),Nn=function(e){return"custom"===e||""===e?Ln.custom.label:Cn(e)?e:Ln[e].label},Cn=function(e){return!En.includes(e)||"custom"===e},An=function(e){var t=e.value,n=e.onChange,r=(0,b.useState)(""),i=r[0],s=r[1],o=(0,z.useDebounce)(n,500);return(0,p.jsx)("div",{className:"parsely-persona-selector-custom",children:(0,p.jsx)(f.TextControl,{value:i||t,placeholder:(0,_.__)("Enter a custom persona…","wp-parsely"),onChange:function(e){if(""===e)return n(""),void s("");e.length>32&&(e=e.slice(0,32)),o(e),s(e)}})})},On=function(e){var t=e.persona,n=e.value,r=void 0===n?(0,_.__)("Select a persona…","wp-parsely"):n,i=e.label,s=void 0===i?(0,_.__)("Persona","wp-parsely"):i,o=e.onChange,a=e.onDropdownChange,l=e.disabled,c=void 0!==l&&l,u=e.allowCustom,d=void 0!==u&&u;return(0,p.jsxs)(f.Disabled,{isDisabled:c,children:[s&&(0,p.jsx)("div",{className:"wp-parsely-dropdown-label",children:s}),(0,p.jsx)(f.DropdownMenu,{label:(0,_.__)("Persona","wp-parsely"),className:"parsely-persona-selector-dropdown"+(c?" is-disabled":""),popoverProps:{className:"wp-parsely-popover"},toggleProps:{children:(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)("div",{className:"parsely-persona-selector-label",children:Cn(t)?Ln.custom.label:r}),(0,p.jsx)(Tn,{})]})},children:function(e){var n=e.onClose;return(0,p.jsx)(f.MenuGroup,{label:(0,_.__)("Persona","wp-parsely"),children:(0,p.jsx)(p.Fragment,{children:En.map((function(e){if(!d&&"custom"===e)return null;var r=Ln[e],i=e===t||Cn(t)&&"custom"===e;return(0,p.jsxs)(f.MenuItem,{isSelected:i,className:i?"is-selected":"",role:"menuitemradio",onClick:function(){null==a||a(e),o(e),n(),"custom"===e&&setTimeout((function(){var e=document.querySelector(".parsely-persona-selector-custom input");e&&e.focus()}),0)},children:[r.icon&&(0,p.jsx)(X,{icon:r.icon}),r.label]},e)}))})})}}),d&&Cn(t)&&(0,p.jsx)(An,{onChange:function(e){o(""!==e?e:"custom")},value:"custom"===t?"":t})]})},Rn={neutral:{label:(0,_.__)("Neutral","wp-parsely")},formal:{label:(0,_.__)("Formal","wp-parsely")},humorous:{label:(0,_.__)("Humorous","wp-parsely")},confident:{label:(0,_.__)("Confident","wp-parsely")},provocative:{label:(0,_.__)("Provocative","wp-parsely")},serious:{label:(0,_.__)("Serious","wp-parsely")},inspirational:{label:(0,_.__)("Inspirational","wp-parsely")},skeptical:{label:(0,_.__)("Skeptical","wp-parsely")},conversational:{label:(0,_.__)("Conversational","wp-parsely")},analytical:{label:(0,_.__)("Analytical","wp-parsely")},custom:{label:(0,_.__)("Custom Tone","wp-parsely"),icon:jn}},In=Object.keys(Rn),Bn=function(e){return"custom"===e||""===e?Rn.custom.label:Mn(e)?e:Rn[e].label},Mn=function(e){return!In.includes(e)||"custom"===e},Dn=function(e){var t=e.value,n=e.onChange,r=(0,b.useState)(""),i=r[0],s=r[1],o=(0,z.useDebounce)(n,500);return(0,p.jsx)("div",{className:"parsely-tone-selector-custom",children:(0,p.jsx)(f.TextControl,{value:i||t,placeholder:(0,_.__)("Enter a custom tone","wp-parsely"),onChange:function(e){if(""===e)return n(""),void s("");e.length>32&&(e=e.slice(0,32)),o(e),s(e)}})})},Fn=function(e){var t=e.tone,n=e.value,r=void 0===n?(0,_.__)("Select a tone","wp-parsely"):n,i=e.label,s=void 0===i?(0,_.__)("Tone","wp-parsely"):i,o=e.onChange,a=e.onDropdownChange,l=e.disabled,c=void 0!==l&&l,u=e.allowCustom,d=void 0!==u&&u;return(0,p.jsxs)(f.Disabled,{isDisabled:c,children:[(0,p.jsx)("div",{className:"wp-parsely-dropdown-label",children:s}),(0,p.jsx)(f.DropdownMenu,{label:(0,_.__)("Tone","wp-parsely"),className:"parsely-tone-selector-dropdown"+(c?" is-disabled":""),popoverProps:{className:"wp-parsely-popover"},toggleProps:{children:(0,p.jsxs)(p.Fragment,{children:[(0,p.jsx)("div",{className:"parsely-tone-selector-label",children:Mn(t)?Rn.custom.label:r}),(0,p.jsx)(Tn,{})]})},children:function(e){var n=e.onClose;return(0,p.jsx)(f.MenuGroup,{label:(0,_.__)("Select a tone","wp-parsely"),children:(0,p.jsx)(p.Fragment,{children:In.map((function(e){if(!d&&"custom"===e)return null;var r=Rn[e],i=e===t||Mn(t)&&"custom"===e;return(0,p.jsxs)(f.MenuItem,{isSelected:i,className:i?"is-selected":"",role:"menuitemradio",onClick:function(){null==a||a(e),o(e),n(),"custom"===e&&setTimeout((function(){var e=document.querySelector(".parsely-tone-selector-custom input");e&&e.focus()}),0)},children:[r.icon&&(0,p.jsx)(X,{icon:r.icon}),r.label]},e)}))})})}}),d&&Mn(t)&&(0,p.jsx)(Dn,{onChange:function(e){o(""!==e?e:"custom")},value:"custom"===t?"":t})]})},Vn=(0,p.jsx)(x.SVG,{width:"24",height:"24",viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg",children:(0,p.jsx)(x.Path,{d:"M10.97 10.159a3.382 3.382 0 0 0-2.857.955l1.724 1.723-2.836 2.913L7 17h1.25l2.913-2.837 1.723 1.723a3.38 3.38 0 0 0 .606-.825c.33-.63.446-1.343.35-2.032L17 10.695 13.305 7l-2.334 3.159Z"})}),Gn=(0,p.jsx)(x.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",children:(0,p.jsx)(x.Path,{d:"M18.3 11.7c-.6-.6-1.4-.9-2.3-.9H6.7l2.9-3.3-1.1-1-4.5 5L8.5 16l1-1-2.7-2.7H16c.5 0 .9.2 1.3.5 1 1 1 3.4 1 4.5v.3h1.5v-.2c0-1.5 0-4.3-1.5-5.7z"})}),Hn=(0,p.jsx)(x.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",children:(0,p.jsx)(x.Path,{fillRule:"evenodd",clipRule:"evenodd",d:"M12 5.5A2.25 2.25 0 0 0 9.878 7h4.244A2.251 2.251 0 0 0 12 5.5ZM12 4a3.751 3.751 0 0 0-3.675 3H5v1.5h1.27l.818 8.997a2.75 2.75 0 0 0 2.739 2.501h4.347a2.75 2.75 0 0 0 2.738-2.5L17.73 8.5H19V7h-3.325A3.751 3.751 0 0 0 12 4Zm4.224 4.5H7.776l.806 8.861a1.25 1.25 0 0 0 1.245 1.137h4.347a1.25 1.25 0 0 0 1.245-1.137l.805-8.861Z"})}),zn=(0,p.jsx)(x.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",children:(0,p.jsx)(x.Path,{d:"m21.5 9.1-6.6-6.6-4.2 5.6c-1.2-.1-2.4.1-3.6.7-.1 0-.1.1-.2.1-.5.3-.9.6-1.2.9l3.7 3.7-5.7 5.7v1.1h1.1l5.7-5.7 3.7 3.7c.4-.4.7-.8.9-1.2.1-.1.1-.2.2-.3.6-1.1.8-2.4.6-3.6l5.6-4.1zm-7.3 3.5.1.9c.1.9 0 1.8-.4 2.6l-6-6c.8-.4 1.7-.5 2.6-.4l.9.1L15 4.9 19.1 9l-4.9 3.6z"})}),Un=function(){return Un=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&i[i.length-1])||6!==a[0]&&2!==a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&i[i.length-1])||6!==a[0]&&2!==a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0&&i[i.length-1])||6!==a[0]&&2!==a[0])){o=0;continue}if(3===a[0]&&(!i||a[1]>i[0]&&a[1]0?(0,p.jsx)("span",{className:"parsely-write-titles-text",children:(0,b.createInterpolateElement)( // translators: %1$s is the tone, %2$s is the persona. // translators: %1$s is the tone, %2$s is the persona. -(0,_.__)("We've generated a few titles based on the content of your post, written as a .","wp-parsely"),{tone:(0,p.jsx)("strong",{children:Bn(a)}),persona:(0,p.jsx)("strong",{children:Nn(u)})})}):(0,_.__)("Use Parse.ly AI to generate a title for your post.","wp-parsely"),(0,p.jsxs)(f.Button,{href:"https://docs.parse.ly/plugin-content-helper/#h-title-suggestions-beta",target:"_blank",variant:"link",children:[(0,_.__)("Learn more about Parse.ly AI","wp-parsely"),(0,p.jsx)(X,{icon:ee,size:18,className:"parsely-external-link-icon"})]})]}),i&&(0,p.jsx)(f.Notice,{className:"wp-parsely-content-helper-error",onRemove:function(){return s(void 0)},status:"info",children:i.Message()}),void 0!==k&&(0,p.jsx)(Jn,{title:k,type:fn.PostTitle,isOriginal:!0}),00&&(0,p.jsx)(Qn,{pinnedTitles:m,isOpen:!0}),y.length>0&&(0,p.jsx)(er,{suggestions:y,isOpen:!0,isLoading:g})]}),(0,p.jsx)(Xn,{isLoading:g,onPersonaChange:function(e){C("Persona",e),d(e)},onSettingChange:C,onToneChange:function(e){C("Tone",e),l(e)},persona:t.TitleSuggestions.Persona,tone:t.TitleSuggestions.Tone}),(0,p.jsx)("div",{className:"title-suggestions-generate",children:(0,p.jsxs)(f.Button,{variant:"primary",isBusy:g,disabled:g||"custom"===a||"custom"===u,onClick:function(){return ir(void 0,void 0,void 0,(function(){return sr(this,(function(e){switch(e.label){case 0:return s(void 0),!1!==g?[3,2]:(P.trackEvent("title_suggestions_generate_pressed",{request_more:y.length>0,total_titles:y.length,total_pinned:y.filter((function(e){return e.isPinned})).length,tone:a,persona:u}),[4,(t=fn.PostTitle,n=A,r=a,i=u,ir(void 0,void 0,void 0,(function(){var e,o,a;return sr(this,(function(l){switch(l.label){case 0:return[4,T(!0)];case 1:l.sent(),e=nr.getInstance(),l.label=2;case 2:return l.trys.push([2,5,,6]),[4,e.generateTitles(n,3,r,i)];case 3:return o=l.sent(),[4,j(t,o)];case 4:return l.sent(),[3,6];case 5:return a=l.sent(),s(a),j(t,[]),[3,6];case 6:return[4,T(!1)];case 7:return l.sent(),[2]}}))})))]);case 1:e.sent(),e.label=2;case 2:return[2]}var t,n,r,i}))}))},children:[g&&(0,_.__)("Generating Titles…","wp-parsely"),!g&&w.length>0&&(0,_.__)("Generate More","wp-parsely"),!g&&0===w.length&&(0,_.__)("Generate Titles","wp-parsely")]})})]})})},ar=function(){return ar=Object.assign||function(e){for(var t,n=1,r=arguments.length;n titles based on the content of your post, written as a .","wp-parsely"),{tone:(0,p.jsx)("strong",{children:Bn(a)}),persona:(0,p.jsx)("strong",{children:Nn(u)})})}):(0,_.__)("Use Parse.ly AI to generate a title for your post.","wp-parsely"),(0,p.jsxs)(f.Button,{href:"https://docs.parse.ly/plugin-content-helper/#h-title-suggestions-beta",target:"_blank",variant:"link",children:[(0,_.__)("Learn more about Parse.ly AI","wp-parsely"),(0,p.jsx)(X,{icon:ee,size:18,className:"parsely-external-link-icon"})]})]}),i&&(0,p.jsx)(f.Notice,{className:"wp-parsely-content-helper-error",onRemove:function(){return s(void 0)},status:"info",children:i.Message()}),void 0!==k&&(0,p.jsx)(Jn,{title:k,type:fn.PostTitle,isOriginal:!0}),00&&(0,p.jsx)(Qn,{pinnedTitles:m,isOpen:!0}),y.length>0&&(0,p.jsx)(er,{suggestions:y,isOpen:!0,isLoading:g})]}),(0,p.jsx)(Xn,{isLoading:g,onPersonaChange:function(e){C("Persona",e),d(e)},onSettingChange:C,onToneChange:function(e){C("Tone",e),l(e)},persona:t.TitleSuggestions.Persona,tone:t.TitleSuggestions.Tone}),(0,p.jsx)("div",{className:"title-suggestions-generate",children:(0,p.jsxs)(f.Button,{variant:"primary",isBusy:g,disabled:g||"custom"===a||"custom"===u,onClick:function(){return ir(void 0,void 0,void 0,(function(){return sr(this,(function(e){switch(e.label){case 0:return s(void 0),!1!==g?[3,2]:(P.trackEvent("title_suggestions_generate_pressed",{request_more:y.length>0,total_titles:y.length,total_pinned:y.filter((function(e){return e.isPinned})).length,tone:a,persona:u}),[4,(t=fn.PostTitle,n=A,r=a,i=u,ir(void 0,void 0,void 0,(function(){var e,o,a;return sr(this,(function(l){switch(l.label){case 0:return[4,T(!0)];case 1:l.sent(),e=nr.getInstance(),l.label=2;case 2:return l.trys.push([2,5,,6]),[4,e.generateTitles(n,3,r,i)];case 3:return o=l.sent(),[4,j(t,o)];case 4:return l.sent(),[3,6];case 5:return a=l.sent(),s(a),j(t,[]),[3,6];case 6:return[4,T(!1)];case 7:return l.sent(),[2]}}))})))]);case 1:e.sent(),e.label=2;case 2:return[2]}var t,n,r,i}))}))},children:[g&&(0,_.__)("Generating Titles…","wp-parsely"),!g&&w.length>0&&(0,_.__)("Generate More","wp-parsely"),!g&&0===w.length&&(0,_.__)("Generate Titles","wp-parsely")]})})]})})},ar=function(){return ar=Object.assign||function(e){for(var t,n=1,r=arguments.length;n $response The response received by the proxy. - * @return array The generated data. - */ - abstract protected function generate_data( $response ): array; - - /** - * Cached "proxy" to the Parse.ly API endpoint. - * - * @param WP_REST_Request $request The request object. - * @return stdClass|WP_Error stdClass containing the data or a WP_Error object on failure. - */ - abstract public function get_items( WP_REST_Request $request ); - - /** - * Returns whether the endpoint is available for access by the current - * user. - * - * @since 3.14.0 Renamed from `permission_callback()`. - * - * @return bool - */ - public function is_available_to_current_user(): bool { - return $this->api->is_available_to_current_user(); - } - - /** - * Constructor. - * - * @param Parsely $parsely Instance of Parsely class. - * @param Remote_API_Interface $api API object which does the actual calls to the Parse.ly API. - */ - public function __construct( Parsely $parsely, Remote_API_Interface $api ) { - $this->parsely = $parsely; - $this->api = $api; - } - - /** - * Registers the endpoint's WP REST route. - * - * @param string $endpoint The endpoint's route (e.g. /stats/posts). - * @param array $methods The HTTP methods to use for the endpoint. - */ - protected function register_endpoint( string $endpoint, array $methods = array( WP_REST_Server::READABLE ) ): void { - if ( ! apply_filters( 'wp_parsely_enable_' . Utils::convert_endpoint_to_filter_key( $endpoint ) . '_api_proxy', true ) ) { - return; - } - - $get_items_args = array( - 'query' => array( - 'default' => array(), - 'sanitize_callback' => function ( array $query ) { - $sanitized_query = array(); - foreach ( $query as $key => $value ) { - $sanitized_query[ sanitize_key( $key ) ] = sanitize_text_field( $value ); - } - - return $sanitized_query; - }, - ), - ); - - $rest_route_args = array( - array( - 'methods' => $methods, - 'callback' => array( $this, 'get_items' ), - 'permission_callback' => array( $this, 'is_available_to_current_user' ), - 'args' => $get_items_args, - 'show_in_index' => $this->is_available_to_current_user(), - ), - ); - - register_rest_route( 'wp-parsely/v1', $endpoint, $rest_route_args ); - } - - /** - * Cached "proxy" to the endpoint. - * - * @param WP_REST_Request $request The request object. - * @param bool $require_api_secret Specifies if the API Secret is required. - * @param string|null $param_item The param element to use to get the items. - * @return stdClass|WP_Error stdClass containing the data or a WP_Error object on failure. - */ - protected function get_data( WP_REST_Request $request, bool $require_api_secret = true, string $param_item = null ) { - // Validate Site ID and secret. - $validation = $this->validate_apikey_and_secret( $require_api_secret ); - if ( is_wp_error( $validation ) ) { - return $validation; - } - - if ( null !== $param_item ) { - $params = $request->get_param( $param_item ); - } else { - $params = $request->get_params(); - } - - if ( is_array( $params ) && isset( $params['itm_source'] ) ) { - $this->itm_source = $params['itm_source']; - } - - // A proxy with caching behavior is used here. - $response = $this->api->get_items( $params ); - - if ( is_wp_error( $response ) ) { - return $response; - } - - return (object) array( - 'data' => $this->generate_data( $response ), // @phpstan-ignore-line. - ); - } - - /** - * Validates that the Site ID and secret are set. - * If the API secret is not required, it will not be validated. - * - * @since 3.13.0 - * - * @param bool $require_api_secret Specifies if the API Secret is required. - * @return WP_Error|bool - */ - protected function validate_apikey_and_secret( bool $require_api_secret = true ) { - if ( false === $this->parsely->site_id_is_set() ) { - return new WP_Error( - 'parsely_site_id_not_set', - __( 'A Parse.ly Site ID must be set in site options to use this endpoint', 'wp-parsely' ), - array( 'status' => 403 ) - ); - } - - if ( $require_api_secret && false === $this->parsely->api_secret_is_set() ) { - return new WP_Error( - 'parsely_api_secret_not_set', - __( 'A Parse.ly API Secret must be set in site options to use this endpoint', 'wp-parsely' ), - array( 'status' => 403 ) - ); - } - - return true; - } - - /** - * Extracts the post data from the passed object. - * - * Should only be used with endpoints that return post data. - * - * @since 3.10.0 - * - * @param stdClass $item The object to extract the data from. - * @return array The extracted data. - */ - protected function extract_post_data( stdClass $item ): array { - $data = array(); - - if ( isset( $item->author ) ) { - $data['author'] = $item->author; - } - - if ( isset( $item->metrics->views ) ) { - $data['views'] = number_format_i18n( $item->metrics->views ); - } - - if ( isset( $item->metrics->visitors ) ) { - $data['visitors'] = number_format_i18n( $item->metrics->visitors ); - } - - // The avg_engaged metric can be in different locations depending on the - // endpoint and passed sort/url parameters. - $avg_engaged = $item->metrics->avg_engaged ?? $item->avg_engaged ?? null; - if ( null !== $avg_engaged ) { - $data['avgEngaged'] = Utils::get_formatted_duration( (float) $avg_engaged ); - } - - if ( isset( $item->pub_date ) ) { - $data['date'] = wp_date( Utils::get_date_format(), strtotime( $item->pub_date ) ); - } - - if ( isset( $item->title ) ) { - $data['title'] = $item->title; - } - - if ( isset( $item->url ) ) { - $site_id = $this->parsely->get_site_id(); - // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.url_to_postid_url_to_postid - $post_id = url_to_postid( $item->url ); // 0 if the post cannot be found. - - $post_url = Parsely::get_url_with_itm_source( $item->url, null ); - if ( Utils::parsely_is_https_supported() ) { - $post_url = str_replace( 'http://', 'https://', $post_url ); - } - - $data['rawUrl'] = $post_url; - $data['dashUrl'] = Parsely::get_dash_url( $site_id, $post_url ); - $data['id'] = Parsely::get_url_with_itm_source( $post_url, null ); // Unique. - $data['postId'] = $post_id; // Might not be unique. - $data['url'] = Parsely::get_url_with_itm_source( $post_url, $this->itm_source ); - - // Set thumbnail URL, falling back to the Parse.ly thumbnail if needed. - $thumbnail_url = get_the_post_thumbnail_url( $post_id, 'thumbnail' ); - if ( false !== $thumbnail_url ) { - $data['thumbnailUrl'] = $thumbnail_url; - } elseif ( isset( $item->thumb_url_medium ) ) { - $data['thumbnailUrl'] = $item->thumb_url_medium; - } - } - - return $data; - } - - /** - * Generates the post data from the passed response. - * - * Should only be used with endpoints that return post data. - * - * @since 3.10.0 - * - * @param array $response The response received by the proxy. - * @return array The generated data. - */ - protected function generate_post_data( array $response ): array { - $data = array(); - - foreach ( $response as $item ) { - $data [] = (object) $this->extract_post_data( $item ); - } - - return $data; - } -} diff --git a/src/RemoteAPI/content-suggestions/class-content-suggestions-base-api.php b/src/RemoteAPI/content-suggestions/class-content-suggestions-base-api.php index 3d2a1b050..ec071681b 100644 --- a/src/RemoteAPI/content-suggestions/class-content-suggestions-base-api.php +++ b/src/RemoteAPI/content-suggestions/class-content-suggestions-base-api.php @@ -10,7 +10,6 @@ namespace Parsely\RemoteAPI\ContentSuggestions; -use Parsely\Endpoints\Base_Endpoint; use Parsely\Parsely; use Parsely\RemoteAPI\Base_Endpoint_Remote; use UnexpectedValueException; @@ -61,7 +60,7 @@ public function is_available_to_current_user( $request = null ): bool { return current_user_can( // phpcs:ignore WordPress.WP.Capabilities.Undetermined $this->apply_capability_filters( - Base_Endpoint::DEFAULT_ACCESS_CAPABILITY + self::DEFAULT_ACCESS_CAPABILITY ) ); } diff --git a/src/content-helper/common/class-content-helper-feature.php b/src/content-helper/common/class-content-helper-feature.php index b0edb6505..83c6f04b7 100644 --- a/src/content-helper/common/class-content-helper-feature.php +++ b/src/content-helper/common/class-content-helper-feature.php @@ -166,15 +166,17 @@ protected function inject_inline_scripts( $settings = rest_do_request( new WP_REST_Request( 'GET', - '/wp-parsely/v1' . $settings_route + '/wp-parsely/v2/settings/' . $settings_route ) )->get_data(); } - if ( ! is_string( $settings ) ) { - $settings = ''; + if ( ! is_array( $settings ) ) { + $settings = array(); } + $settings = wp_json_encode( $settings ); + wp_add_inline_script( static::get_script_id(), "window.wpParselyContentHelperSettings = '$settings';", diff --git a/src/content-helper/common/settings/provider.tsx b/src/content-helper/common/settings/provider.tsx index ef4ea23f7..5e696b4bc 100644 --- a/src/content-helper/common/settings/provider.tsx +++ b/src/content-helper/common/settings/provider.tsx @@ -78,6 +78,7 @@ type ReactDeps = React.DependencyList | undefined; * * @since 3.13.0 * @since 3.14.0 Moved from `content-helper/common/hooks/useSaveSettings.ts`. + * @since 3.17.0 Updated to the new API endpoints. * * @param {string} endpoint The settings endpoint to send the data to. * @param {Settings} data The data to send. @@ -96,7 +97,7 @@ const useSaveSettings = ( } apiFetch( { - path: '/wp-parsely/v1/user-meta/content-helper/' + endpoint, + path: '/wp-parsely/v2/settings/' + endpoint, method: 'PUT', data, } ); diff --git a/src/content-helper/dashboard-widget/class-dashboard-widget.php b/src/content-helper/dashboard-widget/class-dashboard-widget.php index a166adff6..bab7daff5 100644 --- a/src/content-helper/dashboard-widget/class-dashboard-widget.php +++ b/src/content-helper/dashboard-widget/class-dashboard-widget.php @@ -10,7 +10,6 @@ namespace Parsely\Content_Helper; -use Parsely\Endpoints\User_Meta\Dashboard_Widget_Settings_Endpoint; use Parsely\Parsely; use Parsely\RemoteAPI\Analytics_Posts_API; @@ -136,7 +135,7 @@ public function enqueue_assets(): void { true ); - $this->inject_inline_scripts( Dashboard_Widget_Settings_Endpoint::get_route() ); + $this->inject_inline_scripts( 'dashboard-widget' ); wp_enqueue_style( static::get_style_id(), diff --git a/src/content-helper/dashboard-widget/dashboard-widget.tsx b/src/content-helper/dashboard-widget/dashboard-widget.tsx index 09959ee2f..b67f4acf6 100644 --- a/src/content-helper/dashboard-widget/dashboard-widget.tsx +++ b/src/content-helper/dashboard-widget/dashboard-widget.tsx @@ -56,7 +56,7 @@ window.addEventListener( if ( null !== container ) { const component = diff --git a/src/content-helper/editor-sidebar/class-editor-sidebar.php b/src/content-helper/editor-sidebar/class-editor-sidebar.php index 9d0accb25..d98e7bd19 100644 --- a/src/content-helper/editor-sidebar/class-editor-sidebar.php +++ b/src/content-helper/editor-sidebar/class-editor-sidebar.php @@ -12,9 +12,7 @@ use Parsely\Content_Helper\Editor_Sidebar\Smart_Linking; use Parsely\Dashboard_Link; -use Parsely\Endpoints\User_Meta\Editor_Sidebar_Settings_Endpoint; use Parsely\Parsely; -use Parsely\Content_Helper\Content_Helper_Feature; use Parsely\Utils\Utils; use WP_Post; @@ -163,7 +161,7 @@ public function run(): void { true ); - $this->inject_inline_scripts( Editor_Sidebar_Settings_Endpoint::get_route() ); + $this->inject_inline_scripts( 'editor-sidebar' ); // Inject inline variables for the editor sidebar, without UTM parameters. $parsely_post_url = $this->get_parsely_post_url( null, false ); diff --git a/src/content-helper/editor-sidebar/editor-sidebar.tsx b/src/content-helper/editor-sidebar/editor-sidebar.tsx index f3e36d922..2c57a4988 100644 --- a/src/content-helper/editor-sidebar/editor-sidebar.tsx +++ b/src/content-helper/editor-sidebar/editor-sidebar.tsx @@ -228,7 +228,7 @@ const ContentHelperEditorSidebar = (): React.JSX.Element => { title={ __( 'Parse.ly', 'wp-parsely' ) } > @@ -279,7 +279,7 @@ registerPlugin( BLOCK_PLUGIN_ID, { icon: LeafIcon, render: () => ( diff --git a/src/content-helper/editor-sidebar/smart-linking/smart-linking.tsx b/src/content-helper/editor-sidebar/smart-linking/smart-linking.tsx index bdec814f6..cb40a3558 100644 --- a/src/content-helper/editor-sidebar/smart-linking/smart-linking.tsx +++ b/src/content-helper/editor-sidebar/smart-linking/smart-linking.tsx @@ -53,7 +53,7 @@ const withSettingsProvider = createHigherOrderComponent( ( BlockEdit ) => { return ( diff --git a/src/rest-api/class-rest-api-controller.php b/src/rest-api/class-rest-api-controller.php index 5ce4e76fa..007c49e54 100644 --- a/src/rest-api/class-rest-api-controller.php +++ b/src/rest-api/class-rest-api-controller.php @@ -11,6 +11,7 @@ namespace Parsely\REST_API; use Parsely\REST_API\Content_Helper\Content_Helper_Controller; +use Parsely\REST_API\Settings\Settings_Controller; use Parsely\REST_API\Stats\Stats_Controller; /** @@ -62,6 +63,7 @@ public function init(): void { $controllers = array( new Content_Helper_Controller( $this->get_parsely() ), new Stats_Controller( $this->get_parsely() ), + new Settings_Controller( $this->get_parsely() ), ); // Initialize the controllers. diff --git a/src/Endpoints/user-meta/class-base-endpoint-user-meta.php b/src/rest-api/settings/class-base-settings-endpoint.php similarity index 64% rename from src/Endpoints/user-meta/class-base-endpoint-user-meta.php rename to src/rest-api/settings/class-base-settings-endpoint.php index 72d1ca9e3..e4c47bf4b 100644 --- a/src/Endpoints/user-meta/class-base-endpoint-user-meta.php +++ b/src/rest-api/settings/class-base-settings-endpoint.php @@ -1,7 +1,6 @@ , default: mixed} */ -abstract class Base_Endpoint_User_Meta extends Base_Endpoint { +abstract class Base_Settings_Endpoint extends Base_Endpoint { /** * The meta entry's default value. Initialized in the constructor. * * @since 3.13.0 + * @since 3.17.0 Moved from Base_Endpoint_User_Meta. * * @var array */ @@ -38,6 +41,7 @@ abstract class Base_Endpoint_User_Meta extends Base_Endpoint { * constructor. * * @since 3.13.0 + * @since 3.17.0 Moved from Base_Endpoint_User_Meta. * * @var array> */ @@ -47,6 +51,7 @@ abstract class Base_Endpoint_User_Meta extends Base_Endpoint { * The current user's ID. * * @since 3.14.0 + * @since 3.17.0 Moved from Base_Endpoint_User_Meta. * * @var int */ @@ -56,6 +61,7 @@ abstract class Base_Endpoint_User_Meta extends Base_Endpoint { * Returns the meta entry's key. * * @since 3.13.0 + * @since 3.17.0 Moved from Base_Endpoint_User_Meta. * * @return string The meta entry's key. */ @@ -65,6 +71,7 @@ abstract protected function get_meta_key(): string; * Returns the endpoint's subvalues specifications. * * @since 3.13.0 + * @since 3.17.0 Moved from Base_Endpoint_User_Meta. * * @return array */ @@ -74,11 +81,12 @@ abstract protected function get_subvalues_specs(): array; * Constructor. * * @since 3.13.0 + * @since 3.17.0 Moved from Base_Endpoint_User_Meta. * - * @param Parsely $parsely Parsely instance. + * @param Base_API_Controller $controller The REST API controller. */ - public function __construct( Parsely $parsely ) { - parent::__construct( $parsely ); + public function __construct( Base_API_Controller $controller ) { + parent::__construct( $controller ); $subvalues_specs = $this->get_subvalues_specs(); @@ -89,109 +97,151 @@ public function __construct( Parsely $parsely ) { } /** - * Registers the endpoint's WP REST route. + * Initializes the endpoint and sets the current user ID. * - * @since 3.13.0 + * @since 3.17.0 */ - public function run(): void { - // Initialize the current user ID here, as doing it in the constructor - // is too early. + public function init(): void { + parent::init(); $this->current_user_id = get_current_user_id(); - - $this->register_endpoint( - static::get_route(), - 'process_request', - array( 'GET', 'PUT' ) - ); } /** - * Returns the endpoint's route. - * - * @since 3.13.0 + * Registers the routes for the endpoint. * - * @return string The endpoint's route. + * @since 3.17.0 */ - public static function get_route(): string { - return static::ENDPOINT; + public function register_routes(): void { + /** + * GET settings/{endpoint}/get + * Retrieves the settings for the current user. + */ + $this->register_rest_route( + '/get', + array( 'GET' ), + array( $this, 'get_settings' ) + ); + + /** + * PUT settings/{endpoint}/set + * Updates the settings for the current user. + */ + $this->register_rest_route( + '/set', + array( 'PUT' ), + array( $this, 'set_settings' ) + ); + + /** + * GET|PUT settings/{endpoint} + * Handles direct requests to the endpoint. + */ + $this->register_rest_route( + '/', + array( 'GET', 'PUT' ), + array( $this, 'process_request' ) + ); } /** - * Processes the requests sent to the endpoint. + * API Endpoint: GET|PUT settings/{endpoint}/ * - * @since 3.13.0 + * Processes the requests sent directly to the main endpoint. + * + * @since 3.17.0 * * @param WP_REST_Request $request The request sent to the endpoint. - * @return string The meta entry's value as JSON. + * @return WP_REST_Response|WP_Error The response object. */ - public function process_request( WP_REST_Request $request ): string { + public function process_request( WP_REST_Request $request ) { $request_method = $request->get_method(); // Update the meta entry's value if the request method is PUT. if ( 'PUT' === $request_method ) { - $meta_value = $request->get_json_params(); - $this->set_value( $meta_value ); + return $this->set_settings( $request ); } - return $this->get_value(); + return $this->get_settings(); } /** - * Returns whether the endpoint is available for access by the current - * user. + * API Endpoint: GET settings/{endpoint}/get * - * @since 3.14.0 - * @since 3.16.0 Added the `$request` parameter. + * Retrieves the settings for the current user. * - * @param WP_REST_Request|null $request The request object. - * @return bool - */ - public function is_available_to_current_user( $request = null ): bool { - return current_user_can( 'edit_user', $this->current_user_id ); - } - - /** - * Returns the meta entry's value as JSON. - * - * @since 3.13.0 + * @since 3.17.0 * - * @return string The meta entry's value as JSON. + * @return WP_REST_Response The response object. */ - protected function get_value(): string { - $meta_key = $this->get_meta_key(); - $meta_value = get_user_meta( $this->current_user_id, $meta_key, true ); + public function get_settings(): WP_REST_Response { + $meta_key = $this->get_meta_key(); + $settings = get_user_meta( $this->current_user_id, $meta_key, true ); - if ( ! is_array( $meta_value ) || 0 === count( $meta_value ) ) { - $meta_value = $this->default_value; + if ( ! is_array( $settings ) || 0 === count( $settings ) ) { + $settings = $this->default_value; } - $result = wp_json_encode( $meta_value ); - - return false !== $result ? $result : ''; + return new WP_REST_Response( $settings, 200 ); } /** - * Sets the meta entry's value. + * API Endpoint: PUT settings/{endpoint}/set * - * @since 3.13.0 + * Updates the settings for the current user. * - * @param array $meta_value The value to set the meta entry to. - * @return bool Whether updating the meta entry's value was successful. + * @since 3.17.0 + * + * @param WP_REST_Request $request The request object. + * @return WP_REST_Response|WP_Error The response object. */ - protected function set_value( array $meta_value ): bool { + public function set_settings( WP_REST_Request $request ) { + $meta_value = $request->get_json_params(); + + // Validates the settings format. + if ( ! is_array( $meta_value ) ) { // @phpstan-ignore-line + return new WP_Error( + 'ch_settings_invalid_format', + __( 'Settings must be a valid JSON array', 'wp-parsely' ) + ); + } + $sanitized_value = $this->sanitize_value( $meta_value ); + // If the current settings are the same as the new settings, return early. + $current_settings = $this->get_settings(); + if ( $current_settings->get_data() === $sanitized_value ) { + return $current_settings; + } + $update_meta = update_user_meta( $this->current_user_id, $this->get_meta_key(), $sanitized_value ); - if ( false !== $update_meta ) { - return true; + if ( false === $update_meta ) { + return new WP_Error( + 'ch_settings_update_failed', + __( 'Failed to update settings', 'wp-parsely' ) + ); } - return false; + return new WP_REST_Response( $sanitized_value, 200 ); + } + + /** + * Returns whether the endpoint is available for access by the current + * user. + * + * @since 3.14.0 + * @since 3.16.0 Added the `$request` parameter. + * @since 3.17.0 Moved from Base_Endpoint_User_Meta. + * + * @param WP_REST_Request|null $request The request object. + * @return bool + */ + public function is_available_to_current_user( ?WP_REST_Request $request = null ): bool { + return current_user_can( 'edit_user', $this->current_user_id ); } /** @@ -199,6 +249,7 @@ protected function set_value( array $meta_value ): bool { * * @since 3.13.0 * @since 3.14.0 Added support for nested arrays. + * @since 3.17.0 Moved from Base_Endpoint_User_Meta. * * @param array $meta_value The meta value to sanitize. * @param string $parent_key The parent key for the current level of the meta. @@ -246,8 +297,9 @@ protected function sanitize_value( array $meta_value, string $parent_key = '' ): /** * Sanitizes the passed subvalue. * - * @since 3.14.0 Added support for nested arrays. * @since 3.13.0 + * @since 3.14.0 Added support for nested arrays. + * @since 3.17.0 Moved from Base_Endpoint_User_Meta. * * @param string $composite_key The subvalue's key. * @param mixed $value The value to sanitize. @@ -284,41 +336,11 @@ protected function sanitize_subvalue( string $composite_key, $value ) { return $value; } - /** - * Checks if a given composite key is valid. - * - * @since 3.14.3 - * - * @param string|mixed $composite_key The composite key representing the nested path. - * @return bool Whether the key is valid. - */ - protected function is_valid_key( $composite_key ): bool { - if ( ! is_string( $composite_key ) ) { - return false; // Key path is not a string. - } - - $keys = explode( '.', $composite_key ); - $current = $this->valid_subvalues; - - foreach ( $keys as $key ) { - if ( ! is_array( $current ) || ! isset( $current[ $key ] ) ) { - return false; // Key path is invalid. - } - - if ( isset( $current[ $key ]['values'] ) ) { - $current = $current[ $key ]['values']; - } else { - $current = $current[ $key ]; - } - } - - return true; - } - /** * Gets the valid values for a given setting path. * * @since 3.14.3 + * @since 3.17.0 Moved from Base_Endpoint_User_Meta. * * @param array $keys The path to the setting. * @return array The valid values for the setting path. @@ -344,6 +366,7 @@ protected function get_valid_values( array $keys ): array { * Gets the default value for a given setting path. * * @since 3.14.3 + * @since 3.17.0 Moved from Base_Endpoint_User_Meta. * * @param array $keys The path to the setting. * @return mixed|array|null The default value for the setting path. @@ -370,6 +393,7 @@ protected function get_default( array $keys ) { * Gets the specifications for nested settings based on a composite key. * * @since 3.14.3 + * @since 3.17.0 Moved from Base_Endpoint_User_Meta. * * @param string $composite_key The composite key representing the nested path. * @return array The specifications for the nested path. diff --git a/src/Endpoints/user-meta/class-dashboard-widget-settings-endpoint.php b/src/rest-api/settings/class-endpoint-dashboard-widget-settings.php similarity index 59% rename from src/Endpoints/user-meta/class-dashboard-widget-settings-endpoint.php rename to src/rest-api/settings/class-endpoint-dashboard-widget-settings.php index 32ae65efd..00473d8f8 100644 --- a/src/Endpoints/user-meta/class-dashboard-widget-settings-endpoint.php +++ b/src/rest-api/settings/class-endpoint-dashboard-widget-settings.php @@ -1,30 +1,39 @@ */ diff --git a/src/Endpoints/user-meta/class-editor-sidebar-settings-endpoint.php b/src/rest-api/settings/class-endpoint-editor-sidebar-settings.php similarity index 78% rename from src/Endpoints/user-meta/class-editor-sidebar-settings-endpoint.php rename to src/rest-api/settings/class-endpoint-editor-sidebar-settings.php index 75de86489..5f600fa3c 100644 --- a/src/Endpoints/user-meta/class-editor-sidebar-settings-endpoint.php +++ b/src/rest-api/settings/class-endpoint-editor-sidebar-settings.php @@ -1,30 +1,39 @@ */ diff --git a/src/rest-api/settings/class-settings-controller.php b/src/rest-api/settings/class-settings-controller.php new file mode 100644 index 000000000..30760c938 --- /dev/null +++ b/src/rest-api/settings/class-settings-controller.php @@ -0,0 +1,47 @@ +register_endpoints( $endpoints ); + } +} diff --git a/tests/Integration/Endpoints/Proxy/BaseProxyEndpointTest.php b/tests/Integration/Endpoints/Proxy/BaseProxyEndpointTest.php deleted file mode 100644 index 208e0abaa..000000000 --- a/tests/Integration/Endpoints/Proxy/BaseProxyEndpointTest.php +++ /dev/null @@ -1,245 +0,0 @@ -wp_rest_server_global_backup = $GLOBALS['wp_rest_server'] ?? null; - $endpoint = $this->get_endpoint(); - $this->rest_api_init_proxy = static function () use ( $endpoint ) { - $endpoint->run(); - }; - add_action( 'rest_api_init', $this->rest_api_init_proxy ); - } - - /** - * Teardown method called after each test. - * - * Resets globals. - */ - public function tear_down(): void { - parent::tear_down(); - remove_action( 'rest_api_init', $this->rest_api_init_proxy ); - - // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound - $GLOBALS['wp_rest_server'] = $this->wp_rest_server_global_backup; - } - - /** - * Verifies that the route is registered. - * - * @param array $methods The methods supported by the route. - */ - public function run_test_register_routes_by_default( - array $methods = array( 'GET' => true ) - ): void { - $routes = rest_get_server()->get_routes(); - self::assertArrayHasKey( self::$route, $routes ); - self::assertCount( 1, $routes[ self::$route ] ); - self::assertSame( $methods, $routes[ self::$route ][0]['methods'] ); - } - - /** - * Verifies that the route is not registered when the respective filter is - * set to false. - */ - public function run_test_do_not_register_route_when_proxy_is_disabled(): void { - // Override some setup steps in order to set the filter to false. - remove_action( 'rest_api_init', $this->rest_api_init_proxy ); - $endpoint = $this->get_endpoint(); - $this->rest_api_init_proxy = static function () use ( $endpoint ) { - add_filter( 'wp_parsely_enable_' . self::$filter_key . '_api_proxy', '__return_false' ); - $endpoint->run(); - }; - add_action( 'rest_api_init', $this->rest_api_init_proxy ); - - $routes = rest_get_server()->get_routes(); - self::assertFalse( array_key_exists( self::$route, $routes ) ); - } - - /** - * Verifies that calls return an error and do not perform a remote call when - * the Site ID is not populated in site options. - * - * @param WP_REST_Request|null $request The request object to be used. - */ - public function run_test_get_items_fails_without_site_id_set( - ?WP_REST_Request $request = null - ): void { - $this->run_test_get_items_fails( - array( 'apikey' => '' ), - 'parsely_site_id_not_set', - 'A Parse.ly Site ID must be set in site options to use this endpoint', - $request - ); - } - - /** - * Verifies that calls return an error and do not perform a remote call when - * the API Secret is not populated in site options. - * - * @param WP_REST_Request|null $request The request object to be used. - */ - public function run_test_get_items_fails_without_api_secret_set( - ?WP_REST_Request $request = null - ): void { - $this->run_test_get_items_fails( - array( - 'apikey' => 'example.com', - 'api_secret' => '', - ), - 'parsely_api_secret_not_set', - 'A Parse.ly API Secret must be set in site options to use this endpoint', - $request - ); - } - - /** - * Verifies that attempting to get items under the given conditions will - * fail. - * - * @param array $options The WordPress options to be set. - * @param string $expected_error_code The expected error code. - * @param string $expected_error_message The expected error message. - * @param WP_REST_Request|null $request The request object to be used. - */ - private function run_test_get_items_fails( - array $options, - string $expected_error_code, - string $expected_error_message, - ?WP_REST_Request $request = null - ): void { - TestCase::set_options( $options ); - if ( null === $request ) { - $request = new WP_REST_Request( 'GET', self::$route ); - } - - $response = rest_get_server()->dispatch( $request ); - /** - * Variable. - * - * @var WP_Error - */ - $error = $response->as_error(); - self::assertSame( 403, $response->get_status() ); - self::assertSame( $expected_error_code, $error->get_error_code() ); - self::assertSame( $expected_error_message, $error->get_error_message() ); - } - - /** - * Verifies default user capability filter. - */ - public function run_test_user_is_allowed_to_make_proxy_api_call_if_default_user_capability_is_changed(): void { - $this->set_current_user_to_contributor(); - add_filter( - 'wp_parsely_user_capability_for_all_private_apis', - function () { - return 'edit_posts'; - } - ); - - self::assertTrue( static::get_endpoint()->is_available_to_current_user() ); - } - - /** - * Verifies endpoint specific user capability filter. - * - * @param string|null $filter_key The key to use for the filter. - */ - public function run_test_user_is_allowed_to_make_proxy_api_call_if_endpoint_specific_user_capability_is_changed( - $filter_key = null - ): void { - $this->set_current_user_to_contributor(); - $filter_key = $filter_key ?? static::$filter_key; - - add_filter( - 'wp_parsely_user_capability_for_' . $filter_key . '_api', - function () { - return 'edit_posts'; - } - ); - - self::assertTrue( static::get_endpoint()->is_available_to_current_user() ); - } -} diff --git a/tests/Integration/Endpoints/RestMetadataTest.php b/tests/Integration/Endpoints/RestMetadataTest.php index 226e1071c..23854b34c 100644 --- a/tests/Integration/Endpoints/RestMetadataTest.php +++ b/tests/Integration/Endpoints/RestMetadataTest.php @@ -14,7 +14,6 @@ use Parsely\Endpoints\Rest_Metadata; use Parsely\Tests\Integration\TestCase; - /** * Integration Tests for the REST API Metadata Endpoint. */ diff --git a/tests/Integration/Endpoints/UserMeta/BaseUserMetaEndpointTest.php b/tests/Integration/Endpoints/UserMeta/BaseUserMetaEndpointTest.php deleted file mode 100644 index c2ad8ec40..000000000 --- a/tests/Integration/Endpoints/UserMeta/BaseUserMetaEndpointTest.php +++ /dev/null @@ -1,169 +0,0 @@ - - */ - protected $default_value = array(); - - /** - * Generates a JSON string for the passed period, metric, and extra data. - * - * @since 3.13.0 - * - * @param string|null $metric The Metric value. - * @param string|null $period The Period value. - * @param array $extra_data Any Extra key/value pairs to add. - * @return string The generated JSON string. - */ - abstract protected function generate_json( - ?string $metric = null, - ?string $period = null, - array $extra_data = array() - ): string; - - /** - * Verifies that the endpoint returns the correct default value. - * - * @since 3.13.0 - */ - public function run_test_endpoint_returns_value_on_get_request(): void { - $this->set_current_user_to_admin(); - - $value = rest_do_request( - new WP_REST_Request( - 'GET', - self::$route - ) - )->get_data(); - - $expected = $this->wp_json_encode( - $this->default_value - ); - - self::assertSame( $expected, $value ); - } - - /** - * Provides data for testing PUT requests. - * - * @since 3.13.0 - * @return iterable - */ - public function provide_put_requests_data(): iterable { - $default_value = $this->generate_json( 'views', '7d' ); - $valid_value = $this->generate_json( 'avg_engaged', '1h' ); - - // Valid non-default value. It should be returned unmodified. - yield 'valid period and metric values' => array( - 'test_data' => $valid_value, - 'expected' => $valid_value, - ); - - // Missing or problematic keys. Defaults should be used for the missing or problematic keys. - yield 'valid period value, no metric value' => array( - 'test_data' => $this->generate_json( null, '1h' ), - 'expected' => $this->generate_json( 'views', '1h' ), - ); - yield 'valid metric value, no period value' => array( - 'test_data' => $this->generate_json( 'avg_engaged' ), - 'expected' => $this->generate_json( 'avg_engaged', '7d' ), - ); - yield 'no values' => array( - 'test_data' => $this->generate_json(), - 'expected' => $default_value, - ); - - // Invalid values. They should be adjusted to their defaults. - yield 'invalid period value' => array( - 'test_data' => $this->generate_json( 'avg_engaged', 'invalid' ), - 'expected' => $this->generate_json( 'avg_engaged', '7d' ), - ); - yield 'invalid metric value' => array( - 'test_data' => $this->generate_json( 'invalid', '1h' ), - 'expected' => $this->generate_json( 'views', '1h' ), - ); - yield 'invalid period and metric values' => array( - 'test_data' => $this->generate_json( 'invalid', 'invalid' ), - 'expected' => $default_value, - ); - - // Invalid extra data passed. Any such data should be discarded. - yield 'invalid additional value' => array( - 'test_data' => $this->generate_json( - 'avg_engaged', - '1h', - array( 'invalid' ) - ), - 'expected' => $valid_value, - ); - yield 'invalid additional key/value pair' => array( - 'test_data' => $this->generate_json( - 'avg_engaged', - '1h', - array( 'invalid_key' => 'invalid_value' ) - ), - 'expected' => $valid_value, - ); - } - - /** - * Sends a PUT request to the endpoint. - * - * @since 3.13.0 - * - * @param string $data The data to be sent in the request. - * @return string The response returned by the endpoint. - */ - protected function send_put_request( string $data ): string { - $this->set_current_user_to_admin(); - $result = $this->send_wp_rest_request( 'PUT', self::$route, $data ); - - if ( ! is_string( $result ) ) { - return ''; - } - - return $result; - } - - /** - * Verifies that the route is not registered when the respective filter is - * set to false. - * - * @since 3.16.0 - */ - public function run_test_do_not_register_route_when_proxy_is_disabled(): void { - // Override some setup steps in order to set the filter to false. - remove_action( 'rest_api_init', $this->rest_api_init_proxy ); - $endpoint = $this->get_endpoint(); - $this->rest_api_init_proxy = static function () use ( $endpoint ) { - add_filter( 'wp_parsely_enable_' . self::$filter_key . '_api_proxy', '__return_false' ); - $endpoint->run(); - }; - add_action( 'rest_api_init', $this->rest_api_init_proxy ); - - $routes = rest_get_server()->get_routes(); - self::assertFalse( array_key_exists( self::$route, $routes ) ); - } -} diff --git a/tests/Integration/Endpoints/UserMeta/DashboardWidgetSettingsEndpointTest.php b/tests/Integration/Endpoints/UserMeta/DashboardWidgetSettingsEndpointTest.php deleted file mode 100644 index 7af087282..000000000 --- a/tests/Integration/Endpoints/UserMeta/DashboardWidgetSettingsEndpointTest.php +++ /dev/null @@ -1,204 +0,0 @@ - - */ - protected $default_value = array( - 'Metric' => 'views', - 'Period' => '7d', - ); - - /** - * Initializes all required values for the test. - * - * @since 3.13.0 - */ - public static function initialize(): void { - $route = Dashboard_Widget_Settings_Endpoint::get_route(); - - self::$route = '/wp-parsely/v1' . $route; - self::$filter_key = Utils::convert_endpoint_to_filter_key( $route ); - } - - /** - * Returns the endpoint to be used in tests. - * - * @since 3.13.0 - * - * @return Base_Endpoint_User_Meta The endpoint to be used in tests. - */ - public function get_endpoint(): Base_Endpoint_User_Meta { - return new Dashboard_Widget_Settings_Endpoint( new Parsely() ); - } - - /** - * Verifies that the route is registered. - * - * @since 3.13.0 - * - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_route - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::run - * @uses \Parsely\Endpoints\Base_Endpoint::__construct - * @uses \Parsely\Endpoints\Base_Endpoint::register_endpoint - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::__construct - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_subvalues_specs - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::is_available_to_current_user - * @uses \Parsely\Endpoints\User_Meta\Dashboard_Widget_Settings_Endpoint::get_subvalues_specs - * @uses \Parsely\Parsely::__construct - * @uses \Parsely\Parsely::allow_parsely_remote_requests - * @uses \Parsely\Parsely::are_credentials_managed - * @uses \Parsely\Parsely::set_managed_options - * @uses \Parsely\Utils\Utils::convert_endpoint_to_filter_key - */ - public function test_register_routes_by_default(): void { - parent::run_test_register_routes_by_default( - array( - 'GET' => true, - 'PUT' => true, - ) - ); - } - - /** - * Verifies that the route is not registered when the endpoint filter is set - * to false. - * - * @since 3.13.0 - * - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::run - * @uses \Parsely\Endpoints\Base_Endpoint::__construct - * @uses \Parsely\Endpoints\Base_Endpoint::register_endpoint - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::__construct - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_route - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_subvalues_specs - * @uses \Parsely\Endpoints\User_Meta\Dashboard_Widget_Settings_Endpoint::get_subvalues_specs - * @uses \Parsely\Parsely::__construct - * @uses \Parsely\Parsely::allow_parsely_remote_requests - * @uses \Parsely\Parsely::are_credentials_managed - * @uses \Parsely\Parsely::set_managed_options - * @uses \Parsely\Utils\Utils::convert_endpoint_to_filter_key - */ - public function test_verify_that_route_is_not_registered_when_endpoint_is_disabled(): void { - parent::run_test_do_not_register_route_when_proxy_is_disabled(); - } - - /** - * Verifies that the endpoint returns the correct default settings. - * - * @since 3.13.0 - * - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::__construct - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_subvalues_specs - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_value - * @covers \Parsely\Endpoints\User_Meta\Dashboard_Widget_Settings_Endpoint::get_subvalues_specs - * @covers \Parsely\Endpoints\User_Meta\Dashboard_Widget_Settings_Endpoint::process_request - * @uses \Parsely\Endpoints\Base_Endpoint::__construct - * @uses \Parsely\Endpoints\Base_Endpoint::register_endpoint - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::is_available_to_current_user - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::run - * @uses \Parsely\Endpoints\User_Meta\Dashboard_Widget_Settings_Endpoint::get_meta_key - * @uses \Parsely\Endpoints\User_Meta\Dashboard_Widget_Settings_Endpoint::get_route - * @uses \Parsely\Parsely::__construct - * @uses \Parsely\Parsely::allow_parsely_remote_requests - * @uses \Parsely\Parsely::are_credentials_managed - * @uses \Parsely\Parsely::set_managed_options - * @uses \Parsely\Utils\Utils::convert_endpoint_to_filter_key - */ - public function test_endpoint_returns_value_on_get_request(): void { - parent::run_test_endpoint_returns_value_on_get_request(); - } - - /** - * Verifies that the endpoint can correctly handle PUT requests. - * - * @since 3.13.0 - * - * @param string $test_data The data to send in the PUT request. - * @param string $expected The expected value of the setting after the PUT request. - * - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_subvalues_specs - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_value - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::sanitize_subvalue - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::sanitize_value - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::set_value - * @covers \Parsely\Endpoints\User_Meta\Dashboard_Widget_Settings_Endpoint::get_subvalues_specs - * @covers \Parsely\Endpoints\User_Meta\Dashboard_Widget_Settings_Endpoint::process_request - * @uses \Parsely\Endpoints\Base_Endpoint::__construct - * @uses \Parsely\Endpoints\Base_Endpoint::register_endpoint - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::__construct - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::is_available_to_current_user - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::run - * @uses \Parsely\Endpoints\User_Meta\Dashboard_Widget_Settings_Endpoint::get_meta_key - * @uses \Parsely\Endpoints\User_Meta\Dashboard_Widget_Settings_Endpoint::get_route - * @uses \Parsely\Parsely::__construct - * @uses \Parsely\Parsely::allow_parsely_remote_requests - * @uses \Parsely\Parsely::are_credentials_managed - * @uses \Parsely\Parsely::set_managed_options - * @uses \Parsely\Utils\Utils::convert_endpoint_to_filter_key - * - * @dataProvider provide_put_requests_data - */ - public function test_endpoint_correctly_handles_put_requests( - string $test_data, - string $expected - ): void { - $value = $this->send_put_request( $test_data ); - self::assertSame( $expected, $value ); - } - - /** - * Generates a JSON string for the passed period, metric, and extra data. - * - * @since 3.13.0 - * - * @param string|null $metric The Metric value. - * @param string|null $period The Period value. - * @param array $extra_data Any Extra key/value pairs to add. - * @return string The generated JSON string. - */ - protected function generate_json( - ?string $metric = null, - ?string $period = null, - array $extra_data = array() - ): string { - $array = $this->default_value; - unset( $array['Metric'], $array['Period'] ); - - if ( null !== $metric ) { - $array['Metric'] = $metric; - } - - if ( null !== $period ) { - $array['Period'] = $period; - } - - ksort( $array ); - - return $this->wp_json_encode( array_merge( $array, $extra_data ) ); - } -} diff --git a/tests/Integration/Endpoints/UserMeta/EditorSidebarSettingsEndpointTest.php b/tests/Integration/Endpoints/UserMeta/EditorSidebarSettingsEndpointTest.php deleted file mode 100644 index 00071489b..000000000 --- a/tests/Integration/Endpoints/UserMeta/EditorSidebarSettingsEndpointTest.php +++ /dev/null @@ -1,309 +0,0 @@ - - */ - protected $default_value = array( - 'InitialTabName' => 'tools', - 'PerformanceStats' => array( - 'Period' => '7d', - 'VisibleDataPoints' => array( 'views', 'visitors', 'avgEngaged', 'recirculation' ), - 'VisiblePanels' => array( 'overview', 'categories', 'referrers' ), - ), - 'RelatedPosts' => array( - 'FilterBy' => 'unavailable', - 'FilterValue' => '', - 'Metric' => 'views', - 'Open' => false, - 'Period' => '7d', - ), - 'SmartLinking' => array( - 'MaxLinks' => 10, - 'MaxLinkWords' => 4, - 'Open' => false, - ), - 'TitleSuggestions' => array( - 'Open' => false, - 'Persona' => 'journalist', - 'Tone' => 'neutral', - ), - ); - - /** - * Initializes all required values for the test. - * - * @since 3.13.0 - */ - public static function initialize(): void { - $route = Editor_Sidebar_Settings_Endpoint::get_route(); - - self::$route = '/wp-parsely/v1' . $route; - self::$filter_key = Utils::convert_endpoint_to_filter_key( $route ); - } - - /** - * Returns the endpoint to be used in tests. - * - * @since 3.13.0 - * - * @return Base_Endpoint_User_Meta The endpoint to be used in tests. - */ - public function get_endpoint(): Base_Endpoint_User_Meta { - return new Editor_Sidebar_Settings_Endpoint( new Parsely() ); - } - - /** - * Verifies that the route is registered. - * - * @since 3.13.0 - * - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_route - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::run - * @uses \Parsely\Endpoints\Base_Endpoint::__construct - * @uses \Parsely\Endpoints\Base_Endpoint::register_endpoint - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::__construct - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_subvalues_specs - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::is_available_to_current_user - * @uses \Parsely\Endpoints\User_Meta\Editor_Sidebar_Settings_Endpoint::get_subvalues_specs - * @uses \Parsely\Parsely::__construct - * @uses \Parsely\Parsely::allow_parsely_remote_requests - * @uses \Parsely\Parsely::are_credentials_managed - * @uses \Parsely\Parsely::set_managed_options - * @uses \Parsely\Utils\Utils::convert_endpoint_to_filter_key - */ - public function test_register_routes_by_default(): void { - parent::run_test_register_routes_by_default( - array( - 'GET' => true, - 'PUT' => true, - ) - ); - } - - /** - * Verifies that the route is not registered when the endpoint filter is set - * to false. - * - * @since 3.13.0 - * - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::run - * @uses \Parsely\Endpoints\Base_Endpoint::__construct - * @uses \Parsely\Endpoints\Base_Endpoint::register_endpoint - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::__construct - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_route - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_subvalues_specs - * @uses \Parsely\Endpoints\User_Meta\Editor_Sidebar_Settings_Endpoint::get_subvalues_specs - * @uses \Parsely\Parsely::__construct - * @uses \Parsely\Parsely::allow_parsely_remote_requests - * @uses \Parsely\Parsely::are_credentials_managed - * @uses \Parsely\Parsely::set_managed_options - * @uses \Parsely\Utils\Utils::convert_endpoint_to_filter_key - */ - public function test_verify_that_route_is_not_registered_when_endpoint_is_disabled(): void { - parent::run_test_do_not_register_route_when_proxy_is_disabled(); - } - - /** - * Verifies that the endpoint returns the correct default settings. - * - * @since 3.13.0 - * - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::__construct - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_subvalues_specs - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_value - * @covers \Parsely\Endpoints\User_Meta\Editor_Sidebar_Settings_Endpoint::get_subvalues_specs - * @covers \Parsely\Endpoints\User_Meta\Editor_Sidebar_Settings_Endpoint::process_request - * @uses \Parsely\Endpoints\Base_Endpoint::__construct - * @uses \Parsely\Endpoints\Base_Endpoint::register_endpoint - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::is_available_to_current_user - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::run - * @uses \Parsely\Endpoints\User_Meta\Editor_Sidebar_Settings_Endpoint::get_meta_key - * @uses \Parsely\Endpoints\User_Meta\Editor_Sidebar_Settings_Endpoint::get_route - * @uses \Parsely\Parsely::__construct - * @uses \Parsely\Parsely::allow_parsely_remote_requests - * @uses \Parsely\Parsely::are_credentials_managed - * @uses \Parsely\Parsely::set_managed_options - * @uses \Parsely\Utils\Utils::convert_endpoint_to_filter_key - */ - public function test_endpoint_returns_value_on_get_request(): void { - parent::run_test_endpoint_returns_value_on_get_request(); - } - - /** - * Verifies that the endpoint can correctly handle PUT requests. - * - * @since 3.13.0 - * - * @param string $test_data The data to send in the PUT request. - * @param string $expected The expected value of the setting after the PUT request. - * - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_subvalues_specs - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_value - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::sanitize_subvalue - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::sanitize_value - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::set_value - * @covers \Parsely\Endpoints\User_Meta\Editor_Sidebar_Settings_Endpoint::get_subvalues_specs - * @covers \Parsely\Endpoints\User_Meta\Editor_Sidebar_Settings_Endpoint::process_request - * @uses \Parsely\Endpoints\Base_Endpoint::__construct - * @uses \Parsely\Endpoints\Base_Endpoint::register_endpoint - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::__construct - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_default - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_nested_specs - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_valid_values - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::is_available_to_current_user - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::is_valid_key - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::run - * @uses \Parsely\Endpoints\User_Meta\Editor_Sidebar_Settings_Endpoint::get_meta_key - * @uses \Parsely\Endpoints\User_Meta\Editor_Sidebar_Settings_Endpoint::get_route - * @uses \Parsely\Parsely::__construct - * @uses \Parsely\Parsely::allow_parsely_remote_requests - * @uses \Parsely\Parsely::are_credentials_managed - * @uses \Parsely\Parsely::set_managed_options - * @uses \Parsely\Utils\Utils::convert_endpoint_to_filter_key - * @dataProvider provide_put_requests_data - */ - public function test_endpoint_correctly_handles_put_requests( - string $test_data, - string $expected - ): void { - $value = $this->send_put_request( $test_data ); - self::assertSame( $expected, $value ); - } - - /** - * Tests that the endpoint can correctly handle PUT requests with valid - * nested PerformanceStats values. - * - * @since 3.14.0 - * - * @covers \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::sanitize_subvalue - * @uses \Parsely\Endpoints\Base_Endpoint::__construct() - * @uses \Parsely\Endpoints\Base_Endpoint::register_endpoint() - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::__construct() - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_route() - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_value() - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::is_available_to_current_user() - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::process_request() - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::run() - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::sanitize_value() - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::set_value() - * @uses \Parsely\Endpoints\User_Meta\Editor_Sidebar_Settings_Endpoint::get_meta_key() - * @uses \Parsely\Endpoints\User_Meta\Editor_Sidebar_Settings_Endpoint::get_subvalues_specs() - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_nested_specs - * @uses \Parsely\Endpoints\User_Meta\Base_Endpoint_User_Meta::get_valid_values - * @uses \Parsely\Parsely::__construct() - * @uses \Parsely\Parsely::allow_parsely_remote_requests() - * @uses \Parsely\Parsely::are_credentials_managed() - * @uses \Parsely\Parsely::set_managed_options() - * @uses \Parsely\Utils\Utils::convert_endpoint_to_filter_key() - */ - public function test_valid_nested_performance_stats_settings_period(): void { - $this->set_current_user_to_admin(); - - $value = $this->send_put_request( - $this->generate_json( - 'views', - '7d', - array( - 'PerformanceStats' => array( - 'Period' => '1h', - 'VisibleDataPoints' => array( 'views', 'avgEngaged', 'recirculation' ), - 'VisiblePanels' => array( 'overview', 'referrers' ), - ), - ) - ) - ); - - $expected = $this->wp_json_encode( - array_merge( - $this->default_value, - array( - 'PerformanceStats' => array( - 'Period' => '1h', - 'VisibleDataPoints' => array( 'views', 'avgEngaged', 'recirculation' ), - 'VisiblePanels' => array( 'overview', 'referrers' ), - ), - ) - ) - ); - - self::assertSame( $expected, $value ); - } - - /** - * Generates a JSON string for the passed period, metric, and extra data. - * - * @since 3.13.0 - * - * @param string|null $metric The RelatedPostsMetric value. - * @param string|null $period The RelatedPostsPeriod value. - * @param array $extra_data Any Extra key/value pairs to add. - * @return string The generated JSON string. - */ - protected function generate_json( - ?string $metric = null, - ?string $period = null, - array $extra_data = array() - ): string { - $array = $this->default_value; - assert( is_array( $array['RelatedPosts'] ) ); - - unset( $array['RelatedPosts']['Metric'], $array['RelatedPosts']['Period'] ); - - if ( null !== $metric ) { - $array['RelatedPosts']['Metric'] = $metric; - } - - if ( null !== $period ) { - $array['RelatedPosts']['Period'] = $period; - } - - $merged_array = array_merge( $array, $extra_data ); - - $this->ksortRecursive( $merged_array, SORT_NATURAL | SORT_FLAG_CASE ); - - return $this->wp_json_encode( $merged_array ); - } - - /** - * Recursively sorts an array by key using a specified sort flag. - * - * @since 3.14.3 - * - * @param array &$unsorted_array The array to be sorted, passed by reference. - * @param int $sort_flags Optional sorting flags. Defaults to SORT_REGULAR. - */ - private function ksortRecursive( array &$unsorted_array, int $sort_flags = SORT_REGULAR ): void { - ksort( $unsorted_array, $sort_flags ); - foreach ( $unsorted_array as &$value ) { - if ( is_array( $value ) ) { - $this->ksortRecursive( $value, $sort_flags ); - } - } - } -} diff --git a/tests/Integration/RestAPI/BaseEndpointTest.php b/tests/Integration/RestAPI/BaseEndpointTest.php index 00f2f7bb8..46638f182 100644 --- a/tests/Integration/RestAPI/BaseEndpointTest.php +++ b/tests/Integration/RestAPI/BaseEndpointTest.php @@ -78,8 +78,12 @@ class BaseEndpointTest extends TestCase { * Constructor that initializes the Parsely and API controller instances. * * @since 3.17.0 + * + * @param string|null $name The name of the test. + * @param array $data The data for the test. + * @param string $data_name The name of the data. */ - public function __construct() { + public function __construct( $name = null, array $data = array(), $data_name = '' ) { // Create Parsely class, if not already created (by an inherited class). if ( null === $this->parsely ) { $this->parsely = new Parsely(); @@ -90,11 +94,11 @@ public function __construct() { $this->api_controller = new REST_API_Controller( $this->parsely ); } - parent::__construct(); + parent::__construct( $name, $data, $data_name ); } /** - * Sets up the test environment. + * Setup method called before each test. * * @since 3.17.0 */ @@ -248,7 +252,7 @@ public function test_route_is_registered(): void { * @uses \Parsely\REST_API\Base_Endpoint::register_rest_route * @uses \Parsely\REST_API\Base_Endpoint::validate_site_id_and_secret * @uses \Parsely\REST_API\Base_Endpoint::apply_capability_filters - * @uses \Parsely\REST_API\Base_Endpoint::get_default_access_capability + * @uses \Parsely\REST_API\Base_Endpoint::get_default_access_capability * @uses \Parsely\REST_API\Content_Helper\Content_Helper_Controller::get_route_prefix * @uses \Parsely\REST_API\Stats\Stats_Controller::get_route_prefix * @uses \Parsely\Utils\Utils::convert_endpoint_to_filter_key diff --git a/tests/Integration/RestAPI/Settings/BaseSettingsEndpointTest.php b/tests/Integration/RestAPI/Settings/BaseSettingsEndpointTest.php new file mode 100644 index 000000000..3e9627893 --- /dev/null +++ b/tests/Integration/RestAPI/Settings/BaseSettingsEndpointTest.php @@ -0,0 +1,221 @@ + $extra_data Any Extra key/value pairs to add. + * @return array The generated JSON array. + */ + abstract protected function generate_json( + ?string $metric = null, + ?string $period = null, + array $extra_data = array() + ): array; + + /** + * Returns the default value for the endpoint. + * + * @since 3.17.0 + * + * @return array The default value for the endpoint. + */ + abstract protected function get_default_value(): array; + + /** + * Verifies that the route is registered. + * + * @since 3.17.0 + */ + protected function run_test_route_is_registered(): void { + $routes = rest_get_server()->get_routes(); + + // Check that the main route is registered. + $expected_route = $this->get_endpoint()->get_full_endpoint( '/' ); + self::assertArrayHasKey( $expected_route, $routes ); + + // Check that the route is associated with the GET and PUT methods. + $route_data = $routes[ $expected_route ]; + self::assertArrayHasKey( 'GET', $route_data[0]['methods'] ); + self::assertArrayHasKey( 'PUT', $route_data[0]['methods'] ); + + // Check the `/get` route. + $expected_route = $this->get_endpoint()->get_full_endpoint( '/get' ); + self::assertArrayHasKey( $expected_route, $routes ); + + // Check that the route is associated with the GET method. + $route_data = $routes[ $expected_route ]; + self::assertArrayHasKey( 'GET', $route_data[0]['methods'] ); + + // Check the `/set` route. + $expected_route = $this->get_endpoint()->get_full_endpoint( '/set' ); + self::assertArrayHasKey( $expected_route, $routes ); + + // Check that the route is associated with the PUT method. + $route_data = $routes[ $expected_route ]; + self::assertArrayHasKey( 'PUT', $route_data[0]['methods'] ); + } + + /** + * Verifies that the endpoint returns the correct default value. + * + * @since 3.13.0 + * @since 3.17.0 Moved from the old BaseUserMetaEndpointTest class. + */ + public function run_test_endpoint_returns_value_on_get_request(): void { + $this->set_current_user_to_admin(); + + $value = rest_do_request( + new WP_REST_Request( + 'GET', + $this->get_endpoint()->get_full_endpoint( '/get' ) + ) + )->get_data(); + $value = $this->wp_json_encode( $value ); + + $expected = $this->wp_json_encode( + $this->get_default_value() + ); + + self::assertSame( $expected, $value ); + } + + /** + * Provides data for testing PUT requests. + * + * @since 3.13.0 + * @since 3.17.0 Moved from the old BaseUserMetaEndpointTest class. + * + * @return iterable + */ + public function provide_put_requests_data(): iterable { + $default_value = $this->generate_json( 'views', '7d' ); + $valid_value = $this->generate_json( 'avg_engaged', '1h' ); + + // Valid non-default value. It should be returned unmodified. + yield 'valid period and metric values' => array( + 'test_data' => $valid_value, + 'expected' => $valid_value, + ); + + // Missing or problematic keys. Defaults should be used for the missing or problematic keys. + yield 'valid period value, no metric value' => array( + 'test_data' => $this->generate_json( null, '1h' ), + 'expected' => $this->generate_json( 'views', '1h' ), + ); + yield 'valid metric value, no period value' => array( + 'test_data' => $this->generate_json( 'avg_engaged' ), + 'expected' => $this->generate_json( 'avg_engaged', '7d' ), + ); + yield 'no values' => array( + 'test_data' => $this->generate_json(), + 'expected' => $default_value, + ); + + // Invalid values. They should be adjusted to their defaults. + yield 'invalid period value' => array( + 'test_data' => $this->generate_json( 'avg_engaged', 'invalid' ), + 'expected' => $this->generate_json( 'avg_engaged', '7d' ), + ); + yield 'invalid metric value' => array( + 'test_data' => $this->generate_json( 'invalid', '1h' ), + 'expected' => $this->generate_json( 'views', '1h' ), + ); + yield 'invalid period and metric values' => array( + 'test_data' => $this->generate_json( 'invalid', 'invalid' ), + 'expected' => $default_value, + ); + + // Invalid extra data passed. Any such data should be discarded. + yield 'invalid additional value' => array( + 'test_data' => $this->generate_json( + 'avg_engaged', + '1h', + array( 'invalid' ) + ), + 'expected' => $valid_value, + ); + yield 'invalid additional key/value pair' => array( + 'test_data' => $this->generate_json( + 'avg_engaged', + '1h', + array( 'invalid_key' => 'invalid_value' ) + ), + 'expected' => $valid_value, + ); + } + + /** + * Sends a PUT request to the endpoint. + * + * @since 3.13.0 + * @since 3.17.0 Moved from the old BaseUserMetaEndpointTest class. + * + * @param array $data The data to be sent in the request. + * @return array The response returned by the endpoint. + */ + protected function send_put_request( array $data ): array { + $this->set_current_user_to_admin(); + $result = $this->send_wp_rest_request( + 'PUT', + $this->get_endpoint()->get_full_endpoint( '/set' ), + $this->wp_json_encode( $data ) + ); + + if ( ! is_array( $result ) ) { + return array(); + } + + return $result; + } + + /** + * Verifies that the endpoint is not available if the API Secret is not set. + * + * This test is disabled since the endpoint does not require an API secret. + * + * @since 3.17.0 + * + * @coversNothing + */ + public function test_is_available_to_current_user_returns_error_api_secret_not_set(): void { + self::assertTrue( true ); + } + + /** + * Verifies that the endpoint is not available if the Site ID is not set. + * + * This test is disabled since the endpoint does not require a site ID. + * + * @since 3.17.0 + * + * @coversNothing + */ + public function test_is_available_to_current_user_returns_error_site_id_not_set(): void { + self::assertTrue( true ); + } +} diff --git a/tests/Integration/RestAPI/Settings/EndpointDashboardWidgetSettingsTest.php b/tests/Integration/RestAPI/Settings/EndpointDashboardWidgetSettingsTest.php new file mode 100644 index 000000000..7d2f6ca6b --- /dev/null +++ b/tests/Integration/RestAPI/Settings/EndpointDashboardWidgetSettingsTest.php @@ -0,0 +1,204 @@ +api_controller = new Content_Helper_Controller( $this->parsely ); + $this->endpoint = new Endpoint_Dashboard_Widget_Settings( $this->api_controller ); + + parent::set_up(); + } + + /** + * Returns the endpoint to be used in tests. + * + * @since 3.17.0 + * + * @return \Parsely\REST_API\Base_Endpoint + */ + public function get_endpoint(): \Parsely\REST_API\Base_Endpoint { + return $this->endpoint; + } + + /** + * Returns the default value for the endpoint. + * + * @since 3.17.0 + * + * @return array The default value for the endpoint. + */ + public function get_default_value(): array { + return array( + 'Metric' => 'views', + 'Period' => '7d', + ); + } + + /** + * Generates a JSON array for the passed period, metric, and extra data. + * + * @since 3.13.0 + * @since 3.17.0 Moved from old test class. + * + * @param string|null $metric The Metric value. + * @param string|null $period The Period value. + * @param array $extra_data Any Extra key/value pairs to add. + * @return array The generated JSON array. + */ + protected function generate_json( + ?string $metric = null, + ?string $period = null, + array $extra_data = array() + ): array { + $array = $this->get_default_value(); + unset( $array['Metric'], $array['Period'] ); + + if ( null !== $metric ) { + $array['Metric'] = $metric; + } + + if ( null !== $period ) { + $array['Period'] = $period; + } + + ksort( $array ); + + return array_merge( $array, $extra_data ); + } + + /** + * Verifies that the route is registered. + * + * @since 3.17.0 + * + * @covers \Parsely\REST_API\Settings\Endpoint_Dashboard_Widget_Settings::register_routes + * @uses \Parsely\REST_API\Base_API_Controller::__construct + * @uses \Parsely\REST_API\Base_API_Controller::get_full_namespace + * @uses \Parsely\REST_API\Base_API_Controller::get_parsely + * @uses \Parsely\REST_API\Base_API_Controller::prefix_route + * @uses \Parsely\REST_API\Base_Endpoint::__construct + * @uses \Parsely\REST_API\Base_Endpoint::get_full_endpoint + * @uses \Parsely\REST_API\Base_Endpoint::init + * @uses \Parsely\REST_API\Base_Endpoint::register_rest_route + * @uses \Parsely\REST_API\Content_Helper\Content_Helper_Controller::get_route_prefix + * @uses \Parsely\REST_API\REST_API_Controller::get_namespace + * @uses \Parsely\REST_API\REST_API_Controller::get_version + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::__construct + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::init + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::is_available_to_current_user + * @uses \Parsely\REST_API\Settings\Endpoint_Dashboard_Widget_Settings::get_endpoint_name + * @uses \Parsely\REST_API\Settings\Endpoint_Dashboard_Widget_Settings::get_subvalues_specs + * @uses \Parsely\Utils\Utils::convert_endpoint_to_filter_key + */ + public function test_route_is_registered(): void { + parent::run_test_route_is_registered(); + } + + /** + * Verifies that the endpoint returns the correct default settings. + * + * @since 3.13.0 + * @since 3.17.0 Moved from old test class. + * + * @covers \Parsely\REST_API\Settings\Endpoint_Dashboard_Widget_Settings::process_request + * @covers \Parsely\REST_API\Settings\Endpoint_Dashboard_Widget_Settings::get_settings + * @uses \Parsely\REST_API\Base_API_Controller::__construct + * @uses \Parsely\REST_API\Base_API_Controller::get_full_namespace + * @uses \Parsely\REST_API\Base_API_Controller::get_parsely + * @uses \Parsely\REST_API\Base_API_Controller::prefix_route + * @uses \Parsely\REST_API\Base_Endpoint::__construct + * @uses \Parsely\REST_API\Base_Endpoint::get_full_endpoint + * @uses \Parsely\REST_API\Base_Endpoint::init + * @uses \Parsely\REST_API\Base_Endpoint::register_rest_route + * @uses \Parsely\REST_API\Content_Helper\Content_Helper_Controller::get_route_prefix + * @uses \Parsely\REST_API\REST_API_Controller::get_namespace + * @uses \Parsely\REST_API\REST_API_Controller::get_version + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::__construct + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::init + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::is_available_to_current_user + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::register_routes + * @uses \Parsely\REST_API\Settings\Endpoint_Dashboard_Widget_Settings::get_endpoint_name + * @uses \Parsely\REST_API\Settings\Endpoint_Dashboard_Widget_Settings::get_meta_key + * @uses \Parsely\REST_API\Settings\Endpoint_Dashboard_Widget_Settings::get_subvalues_specs + * @uses \Parsely\Utils\Utils::convert_endpoint_to_filter_key + */ + public function test_endpoint_returns_value_on_get_request(): void { + parent::run_test_endpoint_returns_value_on_get_request(); + } + + /** + * Verifies that the endpoint can correctly handle PUT requests. + * + * @since 3.13.0 + * @since 3.17.0 Moved from old test class. + * + * @param array $test_data The data to send in the PUT request. + * @param array $expected The expected value of the setting after the PUT request. + * + * @covers \Parsely\REST_API\Settings\Endpoint_Dashboard_Widget_Settings::get_subvalues_specs + * @covers \Parsely\REST_API\Settings\Endpoint_Dashboard_Widget_Settings::sanitize_subvalue + * @covers \Parsely\REST_API\Settings\Endpoint_Dashboard_Widget_Settings::sanitize_value + * @covers \Parsely\REST_API\Settings\Endpoint_Dashboard_Widget_Settings::set_settings + * @uses \Parsely\REST_API\Base_API_Controller::__construct + * @uses \Parsely\REST_API\Base_API_Controller::get_full_namespace + * @uses \Parsely\REST_API\Base_API_Controller::get_parsely + * @uses \Parsely\REST_API\Base_API_Controller::prefix_route + * @uses \Parsely\REST_API\Base_Endpoint::__construct + * @uses \Parsely\REST_API\Base_Endpoint::get_full_endpoint + * @uses \Parsely\REST_API\Base_Endpoint::init + * @uses \Parsely\REST_API\Base_Endpoint::register_rest_route + * @uses \Parsely\REST_API\Content_Helper\Content_Helper_Controller::get_route_prefix + * @uses \Parsely\REST_API\REST_API_Controller::get_namespace + * @uses \Parsely\REST_API\REST_API_Controller::get_version + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::__construct + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::get_settings + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::get_valid_values + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::init + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::is_available_to_current_user + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::register_routes + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::get_default + * @uses \Parsely\REST_API\Settings\Endpoint_Dashboard_Widget_Settings::get_endpoint_name + * @uses \Parsely\REST_API\Settings\Endpoint_Dashboard_Widget_Settings::get_meta_key + * @uses \Parsely\Utils\Utils::convert_endpoint_to_filter_key + * @dataProvider provide_put_requests_data + */ + public function test_endpoint_correctly_handles_put_requests( + array $test_data, + array $expected + ): void { + $value = $this->send_put_request( $test_data ); + self::assertSame( $expected, $value ); + } +} diff --git a/tests/Integration/RestAPI/Settings/EndpointEditorSidebarSettingsTest.php b/tests/Integration/RestAPI/Settings/EndpointEditorSidebarSettingsTest.php new file mode 100644 index 000000000..c87f1e4c0 --- /dev/null +++ b/tests/Integration/RestAPI/Settings/EndpointEditorSidebarSettingsTest.php @@ -0,0 +1,312 @@ +api_controller = new Content_Helper_Controller( $this->parsely ); + $this->endpoint = new Endpoint_Editor_Sidebar_Settings( $this->api_controller ); + + parent::set_up(); + } + + /** + * Returns the endpoint to be used in tests. + * + * @since 3.17.0 + * + * @return \Parsely\REST_API\Base_Endpoint + */ + public function get_endpoint(): \Parsely\REST_API\Base_Endpoint { + return $this->endpoint; + } + + /** + * Returns the default value for the endpoint. + * + * @since 3.17.0 + * + * @return array The default value for the endpoint. + */ + public function get_default_value(): array { + return array( + 'InitialTabName' => 'tools', + 'PerformanceStats' => array( + 'Period' => '7d', + 'VisibleDataPoints' => array( 'views', 'visitors', 'avgEngaged', 'recirculation' ), + 'VisiblePanels' => array( 'overview', 'categories', 'referrers' ), + ), + 'RelatedPosts' => array( + 'FilterBy' => 'unavailable', + 'FilterValue' => '', + 'Metric' => 'views', + 'Open' => false, + 'Period' => '7d', + ), + 'SmartLinking' => array( + 'MaxLinks' => 10, + 'MaxLinkWords' => 4, + 'Open' => false, + ), + 'TitleSuggestions' => array( + 'Open' => false, + 'Persona' => 'journalist', + 'Tone' => 'neutral', + ), + ); + } + + /** + * Verifies that the route is registered. + * + * @since 3.17.0 + * + * @covers \Parsely\REST_API\Settings\Endpoint_Editor_Sidebar_Settings::register_routes + * @uses \Parsely\REST_API\Base_API_Controller::__construct + * @uses \Parsely\REST_API\Base_API_Controller::get_full_namespace + * @uses \Parsely\REST_API\Base_API_Controller::get_parsely + * @uses \Parsely\REST_API\Base_API_Controller::prefix_route + * @uses \Parsely\REST_API\Base_Endpoint::__construct + * @uses \Parsely\REST_API\Base_Endpoint::get_full_endpoint + * @uses \Parsely\REST_API\Base_Endpoint::init + * @uses \Parsely\REST_API\Base_Endpoint::register_rest_route + * @uses \Parsely\REST_API\Content_Helper\Content_Helper_Controller::get_route_prefix + * @uses \Parsely\REST_API\REST_API_Controller::get_namespace + * @uses \Parsely\REST_API\REST_API_Controller::get_version + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::__construct + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::init + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::is_available_to_current_user + * @uses \Parsely\REST_API\Settings\Endpoint_Editor_Sidebar_Settings::get_endpoint_name + * @uses \Parsely\REST_API\Settings\Endpoint_Editor_Sidebar_Settings::get_subvalues_specs + * @uses \Parsely\Utils\Utils::convert_endpoint_to_filter_key + */ + public function test_route_is_registered(): void { + parent::run_test_route_is_registered(); + } + + /** + * Verifies that the endpoint returns the correct default settings. + * + * @since 3.13.0 + * @since 3.17.0 Moved from old test class. + * + * @covers \Parsely\REST_API\Settings\Endpoint_Editor_Sidebar_Settings::process_request + * @covers \Parsely\REST_API\Settings\Endpoint_Editor_Sidebar_Settings::get_settings + * @uses \Parsely\REST_API\Base_API_Controller::__construct + * @uses \Parsely\REST_API\Base_API_Controller::get_full_namespace + * @uses \Parsely\REST_API\Base_API_Controller::get_parsely + * @uses \Parsely\REST_API\Base_API_Controller::prefix_route + * @uses \Parsely\REST_API\Base_Endpoint::__construct + * @uses \Parsely\REST_API\Base_Endpoint::get_full_endpoint + * @uses \Parsely\REST_API\Base_Endpoint::init + * @uses \Parsely\REST_API\Base_Endpoint::register_rest_route + * @uses \Parsely\REST_API\Content_Helper\Content_Helper_Controller::get_route_prefix + * @uses \Parsely\REST_API\REST_API_Controller::get_namespace + * @uses \Parsely\REST_API\REST_API_Controller::get_version + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::__construct + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::init + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::is_available_to_current_user + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::register_routes + * @uses \Parsely\REST_API\Settings\Endpoint_Editor_Sidebar_Settings::get_endpoint_name + * @uses \Parsely\REST_API\Settings\Endpoint_Editor_Sidebar_Settings::get_meta_key + * @uses \Parsely\REST_API\Settings\Endpoint_Editor_Sidebar_Settings::get_subvalues_specs + * @uses \Parsely\Utils\Utils::convert_endpoint_to_filter_key + */ + public function test_endpoint_returns_value_on_get_request(): void { + parent::run_test_endpoint_returns_value_on_get_request(); + } + + /** + * Verifies that the endpoint can correctly handle PUT requests. + * + * @param array $test_data The data to send in the PUT request. + * @param array $expected The expected value of the setting after the PUT request. + * + * @since 3.13.0 + * @since 3.17.0 Moved from old test class. + * + * @covers \Parsely\REST_API\Settings\Endpoint_Editor_Sidebar_Settings::get_subvalues_specs + * @covers \Parsely\REST_API\Settings\Endpoint_Editor_Sidebar_Settings::sanitize_subvalue + * @covers \Parsely\REST_API\Settings\Endpoint_Editor_Sidebar_Settings::sanitize_value + * @covers \Parsely\REST_API\Settings\Endpoint_Editor_Sidebar_Settings::set_settings + * @uses \Parsely\REST_API\Base_API_Controller::__construct + * @uses \Parsely\REST_API\Base_API_Controller::get_full_namespace + * @uses \Parsely\REST_API\Base_API_Controller::get_parsely + * @uses \Parsely\REST_API\Base_API_Controller::prefix_route + * @uses \Parsely\REST_API\Base_Endpoint::__construct + * @uses \Parsely\REST_API\Base_Endpoint::get_full_endpoint + * @uses \Parsely\REST_API\Base_Endpoint::init + * @uses \Parsely\REST_API\Base_Endpoint::register_rest_route + * @uses \Parsely\REST_API\Content_Helper\Content_Helper_Controller::get_route_prefix + * @uses \Parsely\REST_API\REST_API_Controller::get_namespace + * @uses \Parsely\REST_API\REST_API_Controller::get_version + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::__construct + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::get_nested_specs + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::get_settings + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::get_valid_values + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::init + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::is_available_to_current_user + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::register_routes + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::get_default + * @uses \Parsely\REST_API\Settings\Endpoint_Editor_Sidebar_Settings::get_endpoint_name + * @uses \Parsely\REST_API\Settings\Endpoint_Editor_Sidebar_Settings::get_meta_key + * @uses \Parsely\Utils\Utils::convert_endpoint_to_filter_key + * @dataProvider provide_put_requests_data* + */ + public function test_endpoint_correctly_handles_put_requests( + array $test_data, + array $expected + ): void { + $value = $this->send_put_request( $test_data ); + self::assertSame( $expected, $value ); + } + + /** + * Tests that the endpoint can correctly handle PUT requests with valid + * nested PerformanceStats values. + * + * @since 3.14.0 + * @since 3.17.0 Moved from old test class. + * + * @covers \Parsely\REST_API\Settings\Endpoint_Editor_Sidebar_Settings::sanitize_subvalue + * @uses \Parsely\REST_API\Base_API_Controller::__construct + * @uses \Parsely\REST_API\Base_API_Controller::get_full_namespace + * @uses \Parsely\REST_API\Base_API_Controller::get_parsely + * @uses \Parsely\REST_API\Base_API_Controller::prefix_route + * @uses \Parsely\REST_API\Base_Endpoint::__construct + * @uses \Parsely\REST_API\Base_Endpoint::get_full_endpoint + * @uses \Parsely\REST_API\Base_Endpoint::init + * @uses \Parsely\REST_API\Base_Endpoint::register_rest_route + * @uses \Parsely\REST_API\Content_Helper\Content_Helper_Controller::get_route_prefix + * @uses \Parsely\REST_API\REST_API_Controller::get_namespace + * @uses \Parsely\REST_API\REST_API_Controller::get_version + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::__construct + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::get_nested_specs + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::get_settings + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::get_valid_values + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::init + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::is_available_to_current_user + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::register_routes + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::sanitize_value + * @uses \Parsely\REST_API\Settings\Base_Settings_Endpoint::set_settings + * @uses \Parsely\REST_API\Settings\Endpoint_Editor_Sidebar_Settings::get_endpoint_name + * @uses \Parsely\REST_API\Settings\Endpoint_Editor_Sidebar_Settings::get_meta_key + * @uses \Parsely\REST_API\Settings\Endpoint_Editor_Sidebar_Settings::get_subvalues_specs + * @uses \Parsely\Utils\Utils::convert_endpoint_to_filter_key + */ + public function test_valid_nested_performance_stats_settings_period(): void { + $this->set_current_user_to_admin(); + + $value = $this->send_put_request( + $this->generate_json( + 'views', + '7d', + array( + 'PerformanceStats' => array( + 'Period' => '1h', + 'VisibleDataPoints' => array( 'views', 'avgEngaged', 'recirculation' ), + 'VisiblePanels' => array( 'overview', 'referrers' ), + ), + ) + ) + ); + + $expected = array_merge( + $this->get_default_value(), + array( + 'PerformanceStats' => array( + 'Period' => '1h', + 'VisibleDataPoints' => array( 'views', 'avgEngaged', 'recirculation' ), + 'VisiblePanels' => array( 'overview', 'referrers' ), + ), + ) + ); + + self::assertSame( $expected, $value ); + } + + /** + * Generates a JSON array for the passed period, metric, and extra data. + * + * @since 3.13.0 + * @since 3.17.0 Moved from old test class. + * + * @param string|null $metric The RelatedPostsMetric value. + * @param string|null $period The RelatedPostsPeriod value. + * @param array $extra_data Any Extra key/value pairs to add. + * @return array The generated JSON array. + */ + protected function generate_json( + ?string $metric = null, + ?string $period = null, + array $extra_data = array() + ): array { + $array = $this->get_default_value(); + assert( is_array( $array['RelatedPosts'] ) ); + + unset( $array['RelatedPosts']['Metric'], $array['RelatedPosts']['Period'] ); + + if ( null !== $metric ) { + $array['RelatedPosts']['Metric'] = $metric; + } + + if ( null !== $period ) { + $array['RelatedPosts']['Period'] = $period; + } + + $merged_array = array_merge( $array, $extra_data ); + + $this->ksortRecursive( $merged_array, SORT_NATURAL | SORT_FLAG_CASE ); + + return $merged_array; + } + + /** + * Recursively sorts an array by key using a specified sort flag. + * + * @since 3.14.3 + * @since 3.17.0 Moved from old test class. + * + * @param array &$unsorted_array The array to be sorted, passed by reference. + * @param int $sort_flags Optional sorting flags. Defaults to SORT_REGULAR. + */ + private function ksortRecursive( array &$unsorted_array, int $sort_flags = SORT_REGULAR ): void { + ksort( $unsorted_array, $sort_flags ); + foreach ( $unsorted_array as &$value ) { + if ( is_array( $value ) ) { + $this->ksortRecursive( $value, $sort_flags ); + } + } + } +} diff --git a/wp-parsely.php b/wp-parsely.php index cf5012bef..29997ea08 100644 --- a/wp-parsely.php +++ b/wp-parsely.php @@ -30,23 +30,11 @@ use Parsely\Content_Helper\Editor_Sidebar; use Parsely\Content_Helper\Excerpt_Generator; use Parsely\Content_Helper\Post_List_Stats; -use Parsely\Endpoints\Analytics_Post_Detail_API_Proxy; -use Parsely\Endpoints\Analytics_Posts_API_Proxy; use Parsely\Endpoints\GraphQL_Metadata; -use Parsely\Endpoints\Referrers_Post_Detail_API_Proxy; -use Parsely\Endpoints\Related_API_Proxy; use Parsely\Endpoints\Rest_Metadata; -use Parsely\Endpoints\User_Meta\Dashboard_Widget_Settings_Endpoint; -use Parsely\Endpoints\User_Meta\Editor_Sidebar_Settings_Endpoint; use Parsely\Integrations\Amp; use Parsely\Integrations\Google_Web_Stories; use Parsely\Integrations\Integrations; -use Parsely\RemoteAPI\Analytics_Post_Detail_API; -use Parsely\RemoteAPI\Analytics_Posts_API; -use Parsely\RemoteAPI\Referrers_Post_Detail_API; -use Parsely\RemoteAPI\Related_API; -use Parsely\RemoteAPI\Remote_API_Cache; -use Parsely\RemoteAPI\WordPress_Cache; use Parsely\REST_API\REST_API_Controller; use Parsely\UI\Admin_Bar; use Parsely\UI\Admin_Warning; @@ -134,10 +122,6 @@ function parsely_wp_admin_early_register(): void { function parsely_rest_api_init(): void { $rest = new Rest_Metadata( $GLOBALS['parsely'] ); $rest->run(); - - // Content Helper settings endpoints. - ( new Dashboard_Widget_Settings_Endpoint( $GLOBALS['parsely'] ) )->run(); - ( new Editor_Sidebar_Settings_Endpoint( $GLOBALS['parsely'] ) )->run(); } add_action( 'init', __NAMESPACE__ . '\\init_recommendations_block' ); @@ -225,34 +209,3 @@ function parsely_integrations( $parsely = null ): Integrations { return $parsely_integrations; } - -/** - * Instantiates and runs the specified API endpoint. - * - * @since 3.6.0 - * - * @param string $api_class_name The proxy class to instantiate. - * @param string $proxy_api_class_name The API proxy class to instantiate and run. - * @param WordPress_Cache $wp_cache The WordPress cache instance to be used. - */ -function parsely_run_rest_api_endpoint( - string $api_class_name, - string $proxy_api_class_name, - WordPress_Cache &$wp_cache -): void { - /** - * Internal Variable. - * - * @var RemoteAPI\Base_Endpoint_Remote $remote_api - */ - $remote_api = new $api_class_name( $GLOBALS['parsely'] ); - $remote_api_cache = new Remote_API_Cache( $remote_api, $wp_cache ); - - /** - * Internal Variable. - * - * @var Endpoints\Base_API_Proxy $remote_api_proxy - */ - $remote_api_proxy = new $proxy_api_class_name( $GLOBALS['parsely'], $remote_api_cache ); - $remote_api_proxy->run(); -}