=0?i=e.src.slice(f,u++):u=l+1):u=l+1,i||(i=e.src.slice(s,l)),!(p=e.env.references[r(i)]))return e.pos=j,!1;g=p.href,m=p.title}return t||(c=e.src.slice(s,l),e.md.inline.parse(c,e.md,e.env,h=[]),(b=e.push("image","img",0)).attrs=n=[["src",g],["alt",""]],b.children=h,b.content=c,m&&n.push(["title",m])),e.pos=u,e.posMax=v,!0}},function(e,t,n){"use strict";var r=/^<([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>/,a=/^<([a-zA-Z][a-zA-Z0-9+.\-]{1,31}):([^<>\x00-\x20]*)>/;e.exports=function(e,t){var n,o,c,i,l,s,u=e.pos;return 60===e.src.charCodeAt(u)&&(!((n=e.src.slice(u)).indexOf(">")<0)&&(a.test(n)?(i=(o=n.match(a))[0].slice(1,-1),l=e.md.normalizeLink(i),!!e.md.validateLink(l)&&(t||((s=e.push("link_open","a",1)).attrs=[["href",l]],s.markup="autolink",s.info="auto",(s=e.push("text","",0)).content=e.md.normalizeLinkText(i),(s=e.push("link_close","a",-1)).markup="autolink",s.info="auto"),e.pos+=o[0].length,!0)):!!r.test(n)&&(i=(c=n.match(r))[0].slice(1,-1),l=e.md.normalizeLink("mailto:"+i),!!e.md.validateLink(l)&&(t||((s=e.push("link_open","a",1)).attrs=[["href",l]],s.markup="autolink",s.info="auto",(s=e.push("text","",0)).content=e.md.normalizeLinkText(i),(s=e.push("link_close","a",-1)).markup="autolink",s.info="auto"),e.pos+=c[0].length,!0))))}},function(e,t,n){"use strict";var r=n(133).HTML_TAG_RE;e.exports=function(e,t){var n,a,o,c=e.pos;return!!e.md.options.html&&(o=e.posMax,!(60!==e.src.charCodeAt(c)||c+2>=o)&&(!(33!==(n=e.src.charCodeAt(c+1))&&63!==n&&47!==n&&!function(e){var t=32|e;return t>=97&&t<=122}(n))&&(!!(a=e.src.slice(c).match(r))&&(t||(e.push("html_inline","",0).content=e.src.slice(c,c+a[0].length)),e.pos+=a[0].length,!0))))}},function(e,t,n){"use strict";var r=n(128),a=n(29).has,o=n(29).isValidEntityCode,c=n(29).fromCodePoint,i=/^((?:x[a-f0-9]{1,6}|[0-9]{1,7}));/i,l=/^&([a-z][a-z0-9]{1,31});/i;e.exports=function(e,t){var n,s,u=e.pos,p=e.posMax;if(38!==e.src.charCodeAt(u))return!1;if(u+1c;r-=o.jump+1)if((o=t[r]).marker===a.marker&&(-1===i&&(i=r),o.open&&o.end<0&&(l=!1,(o.close||a.open)&&(o.length+a.length)%3==0&&(o.length%3==0&&a.length%3==0||(l=!0)),!l))){s=r>0&&!t[r-1].open?t[r-1].jump+1:0,a.jump=n-r+s,a.open=!1,o.end=n,o.jump=s,o.close=!1,i=-1;break}-1!==i&&(u[a.marker][(a.length||0)%3]=i)}}e.exports=function(e){var t,n=e.tokens_meta,a=e.tokens_meta.length;for(r(0,e.delimiters),t=0;t0&&r++,"text"===a[t].type&&t+10&&(this.level++,this._prev_delimiters.push(this.delimiters),this.delimiters=[],o={delimiters:this.delimiters}),this.pendingLevel=this.level,this.tokens.push(a),this.tokens_meta.push(o),a},i.prototype.scanDelims=function(e,t){var n,r,i,l,s,u,p,d,m,b=e,h=!0,f=!0,g=this.posMax,j=this.src.charCodeAt(e);for(n=e>0?this.src.charCodeAt(e-1):32;b=3&&":"===e[t-3]||t>=3&&"/"===e[t-3]?0:r.match(n.re.no_http)[0].length:0}},"mailto:":{validate:function(e,t,n){var r=e.slice(t);return n.re.mailto||(n.re.mailto=new RegExp("^"+n.re.src_email_name+"@"+n.re.src_host_strict,"i")),n.re.mailto.test(r)?r.match(n.re.mailto)[0].length:0}}},s="biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф".split("|");function u(e){var t=e.re=n(283)(e.__opts__),r=e.__tlds__.slice();function i(e){return e.replace("%TLDS%",t.src_tlds)}e.onCompile(),e.__tlds_replaced__||r.push("a[cdefgilmnoqrstuwxz]|b[abdefghijmnorstvwyz]|c[acdfghiklmnoruvwxyz]|d[ejkmoz]|e[cegrstu]|f[ijkmor]|g[abdefghilmnpqrstuwy]|h[kmnrtu]|i[delmnoqrst]|j[emop]|k[eghimnprwyz]|l[abcikrstuvy]|m[acdeghklmnopqrstuvwxyz]|n[acefgilopruz]|om|p[aefghklmnrstwy]|qa|r[eosuw]|s[abcdeghijklmnortuvxyz]|t[cdfghjklmnortvwz]|u[agksyz]|v[aceginu]|w[fs]|y[et]|z[amw]"),r.push(t.src_xn),t.src_tlds=r.join("|"),t.email_fuzzy=RegExp(i(t.tpl_email_fuzzy),"i"),t.link_fuzzy=RegExp(i(t.tpl_link_fuzzy),"i"),t.link_no_ip_fuzzy=RegExp(i(t.tpl_link_no_ip_fuzzy),"i"),t.host_fuzzy_test=RegExp(i(t.tpl_host_fuzzy_test),"i");var l=[];function s(e,t){throw new Error('(LinkifyIt) Invalid schema "'+e+'": '+t)}e.__compiled__={},Object.keys(e.__schemas__).forEach((function(t){var n=e.__schemas__[t];if(null!==n){var r={validate:null,link:null};if(e.__compiled__[t]=r,"[object Object]"===a(n))return!function(e){return"[object RegExp]"===a(e)}(n.validate)?o(n.validate)?r.validate=n.validate:s(t,n):r.validate=function(e){return function(t,n){var r=t.slice(n);return e.test(r)?r.match(e)[0].length:0}}(n.validate),void(o(n.normalize)?r.normalize=n.normalize:n.normalize?s(t,n):r.normalize=function(e,t){t.normalize(e)});!function(e){return"[object String]"===a(e)}(n)?s(t,n):l.push(t)}})),l.forEach((function(t){e.__compiled__[e.__schemas__[t]]&&(e.__compiled__[t].validate=e.__compiled__[e.__schemas__[t]].validate,e.__compiled__[t].normalize=e.__compiled__[e.__schemas__[t]].normalize)})),e.__compiled__[""]={validate:null,normalize:function(e,t){t.normalize(e)}};var u=Object.keys(e.__compiled__).filter((function(t){return t.length>0&&e.__compiled__[t]})).map(c).join("|");e.re.schema_test=RegExp("(^|(?!_)(?:[><|]|"+t.src_ZPCc+"))("+u+")","i"),e.re.schema_search=RegExp("(^|(?!_)(?:[><|]|"+t.src_ZPCc+"))("+u+")","ig"),e.re.pretest=RegExp("("+e.re.schema_test.source+")|("+e.re.host_fuzzy_test.source+")|@","i"),function(e){e.__index__=-1,e.__text_cache__=""}(e)}function p(e,t){var n=e.__index__,r=e.__last_index__,a=e.__text_cache__.slice(n,r);this.schema=e.__schema__.toLowerCase(),this.index=n+t,this.lastIndex=r+t,this.raw=a,this.text=a,this.url=a}function d(e,t){var n=new p(e,t);return e.__compiled__[n.schema].normalize(n,e),n}function m(e,t){if(!(this instanceof m))return new m(e,t);var n;t||(n=e,Object.keys(n||{}).reduce((function(e,t){return e||i.hasOwnProperty(t)}),!1)&&(t=e,e={})),this.__opts__=r({},i,t),this.__index__=-1,this.__last_index__=-1,this.__schema__="",this.__text_cache__="",this.__schemas__=r({},l,e),this.__compiled__={},this.__tlds__=s,this.__tlds_replaced__=!1,this.re={},u(this)}m.prototype.add=function(e,t){return this.__schemas__[e]=t,u(this),this},m.prototype.set=function(e){return this.__opts__=r(this.__opts__,e),this},m.prototype.test=function(e){if(this.__text_cache__=e,this.__index__=-1,!e.length)return!1;var t,n,r,a,o,c,i,l;if(this.re.schema_test.test(e))for((i=this.re.schema_search).lastIndex=0;null!==(t=i.exec(e));)if(a=this.testSchemaAt(e,t[2],i.lastIndex)){this.__schema__=t[2],this.__index__=t.index+t[1].length,this.__last_index__=t.index+t[0].length+a;break}return this.__opts__.fuzzyLink&&this.__compiled__["http:"]&&(l=e.search(this.re.host_fuzzy_test))>=0&&(this.__index__<0||l=0&&null!==(r=e.match(this.re.email_fuzzy))&&(o=r.index+r[1].length,c=r.index+r[0].length,(this.__index__<0||othis.__last_index__)&&(this.__schema__="mailto:",this.__index__=o,this.__last_index__=c)),this.__index__>=0},m.prototype.pretest=function(e){return this.re.pretest.test(e)},m.prototype.testSchemaAt=function(e,t,n){return this.__compiled__[t.toLowerCase()]?this.__compiled__[t.toLowerCase()].validate(e,n,this):0},m.prototype.match=function(e){var t=0,n=[];this.__index__>=0&&this.__text_cache__===e&&(n.push(d(this,t)),t=this.__last_index__);for(var r=t?e.slice(t):e;this.test(r);)n.push(d(this,t)),r=r.slice(this.__last_index__),t+=this.__last_index__;return n.length?n:null},m.prototype.tlds=function(e,t){return e=Array.isArray(e)?e:[e],t?(this.__tlds__=this.__tlds__.concat(e).sort().filter((function(e,t,n){return e!==n[t-1]})).reverse(),u(this),this):(this.__tlds__=e.slice(),this.__tlds_replaced__=!0,u(this),this)},m.prototype.normalize=function(e){e.schema||(e.url="http://"+e.url),"mailto:"!==e.schema||/^mailto:/i.test(e.url)||(e.url="mailto:"+e.url)},m.prototype.onCompile=function(){},e.exports=m},function(e,t,n){"use strict";e.exports=function(e){var t={};t.src_Any=n(130).source,t.src_Cc=n(131).source,t.src_Z=n(132).source,t.src_P=n(99).source,t.src_ZPCc=[t.src_Z,t.src_P,t.src_Cc].join("|"),t.src_ZCc=[t.src_Z,t.src_Cc].join("|");return t.src_pseudo_letter="(?:(?![><|]|"+t.src_ZPCc+")"+t.src_Any+")",t.src_ip4="(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)",t.src_auth="(?:(?:(?!"+t.src_ZCc+"|[@/\\[\\]()]).)+@)?",t.src_port="(?::(?:6(?:[0-4]\\d{3}|5(?:[0-4]\\d{2}|5(?:[0-2]\\d|3[0-5])))|[1-5]?\\d{1,4}))?",t.src_host_terminator="(?=$|[><|]|"+t.src_ZPCc+")(?!-|_|:\\d|\\.-|\\.(?!$|"+t.src_ZPCc+"))",t.src_path="(?:[/?#](?:(?!"+t.src_ZCc+"|[><|]|[()[\\]{}.,\"'?!\\-]).|\\[(?:(?!"+t.src_ZCc+"|\\]).)*\\]|\\((?:(?!"+t.src_ZCc+"|[)]).)*\\)|\\{(?:(?!"+t.src_ZCc+'|[}]).)*\\}|\\"(?:(?!'+t.src_ZCc+'|["]).)+\\"|\\\'(?:(?!'+t.src_ZCc+"|[']).)+\\'|\\'(?="+t.src_pseudo_letter+"|[-]).|\\.{2,}[a-zA-Z0-9%/&]|\\.(?!"+t.src_ZCc+"|[.]).|"+(e&&e["---"]?"\\-(?!--(?:[^-]|$))(?:-*)|":"\\-+|")+"\\,(?!"+t.src_ZCc+").|\\!+(?!"+t.src_ZCc+"|[!]).|\\?(?!"+t.src_ZCc+"|[?]).)+|\\/)?",t.src_email_name='[\\-;:&=\\+\\$,\\.a-zA-Z0-9_][\\-;:&=\\+\\$,\\"\\.a-zA-Z0-9_]*',t.src_xn="xn--[a-z0-9\\-]{1,59}",t.src_domain_root="(?:"+t.src_xn+"|"+t.src_pseudo_letter+"{1,63})",t.src_domain="(?:"+t.src_xn+"|(?:"+t.src_pseudo_letter+")|(?:"+t.src_pseudo_letter+"(?:-|"+t.src_pseudo_letter+"){0,61}"+t.src_pseudo_letter+"))",t.src_host="(?:(?:(?:(?:"+t.src_domain+")\\.)*"+t.src_domain+"))",t.tpl_host_fuzzy="(?:"+t.src_ip4+"|(?:(?:(?:"+t.src_domain+")\\.)+(?:%TLDS%)))",t.tpl_host_no_ip_fuzzy="(?:(?:(?:"+t.src_domain+")\\.)+(?:%TLDS%))",t.src_host_strict=t.src_host+t.src_host_terminator,t.tpl_host_fuzzy_strict=t.tpl_host_fuzzy+t.src_host_terminator,t.src_host_port_strict=t.src_host+t.src_port+t.src_host_terminator,t.tpl_host_port_fuzzy_strict=t.tpl_host_fuzzy+t.src_port+t.src_host_terminator,t.tpl_host_port_no_ip_fuzzy_strict=t.tpl_host_no_ip_fuzzy+t.src_port+t.src_host_terminator,t.tpl_host_fuzzy_test="localhost|www\\.|\\.\\d{1,3}\\.|(?:\\.(?:%TLDS%)(?:"+t.src_ZPCc+"|>|$))",t.tpl_email_fuzzy='(^|[><|]|"|\\(|'+t.src_ZCc+")("+t.src_email_name+"@"+t.tpl_host_fuzzy_strict+")",t.tpl_link_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`||]|"+t.src_ZPCc+"))((?![$+<=>^`||])"+t.tpl_host_port_fuzzy_strict+t.src_path+")",t.tpl_link_no_ip_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`||]|"+t.src_ZPCc+"))((?![$+<=>^`||])"+t.tpl_host_port_no_ip_fuzzy_strict+t.src_path+")",t}},function(e,t,n){"use strict";n.r(t),n.d(t,"ucs2decode",(function(){return b})),n.d(t,"ucs2encode",(function(){return h})),n.d(t,"decode",(function(){return j})),n.d(t,"encode",(function(){return v})),n.d(t,"toASCII",(function(){return k})),n.d(t,"toUnicode",(function(){return _}));var r=n(21),a=n.n(r),o=2147483647,c=/^xn--/,i=/[^\0-\x7E]/,l=/[\x2E\u3002\uFF0E\uFF61]/g,s={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},u=Math.floor,p=String.fromCharCode;function d(e){throw new RangeError(s[e])}function m(e,t){var n=e.split("@"),r="";n.length>1&&(r=n[0]+"@",e=n[1]);var a=function(e,t){for(var n=[],r=e.length;r--;)n[r]=t(e[r]);return n}((e=e.replace(l,".")).split("."),t).join(".");return r+a}function b(e){for(var t=[],n=0,r=e.length;n=55296&&a<=56319&&n>1,e+=u(e/t);e>455;r+=36)e=u(e/35);return u(r+36*e/(e+38))},j=function(e){var t,n=[],r=e.length,a=0,c=128,i=72,l=e.lastIndexOf("-");l<0&&(l=0);for(var s=0;s=128&&d("not-basic"),n.push(e.charCodeAt(s));for(var p=l>0?l+1:0;p=r&&d("invalid-input");var f=(t=e.charCodeAt(p++))-48<10?t-22:t-65<26?t-65:t-97<26?t-97:36;(f>=36||f>u((o-a)/b))&&d("overflow"),a+=f*b;var j=h<=i?1:h>=i+26?26:h-i;if(fu(o/v)&&d("overflow"),b*=v}var _=n.length+1;i=g(a-m,_,0==m),u(a/_)>o-c&&d("overflow"),c+=u(a/_),a%=_,n.splice(a++,0,c)}return String.fromCodePoint.apply(String,n)},v=function(e){var t=[],n=(e=b(e)).length,r=128,a=0,c=72,i=!0,l=!1,s=void 0;try{for(var m,h=e[Symbol.iterator]();!(i=(m=h.next()).done);i=!0){var j=m.value;j<128&&t.push(p(j))}}catch(z){l=!0,s=z}finally{try{i||null==h.return||h.return()}finally{if(l)throw s}}var v=t.length,_=v;for(v&&t.push("-");_=r&&xu((o-a)/S)&&d("overflow"),a+=(k-r)*S,r=k;var A=!0,N=!1,P=void 0;try{for(var T,B=e[Symbol.iterator]();!(A=(T=B.next()).done);A=!0){var I=T.value;if(Io&&d("overflow"),I==r){for(var M=a,F=36;;F+=36){var L=F<=c?1:F>=c+26?26:F-c;if(M1&&void 0!==arguments[1]&&arguments[1];t.state.isLoading||(n&&t.props.noticeOperations.removeAllNotices(),t.setState({account:n?t.defaultAccount:t.state.account,isLoading:!0,media:n?[]:t.state.media,nextHandle:!n&&t.state.nextHandle},(function(){return t.getMediaRequest(e)})))})),z()(F()(t),"handleApiError",(function(e){var n;"authorization_required"!==e.code?((null===(n=e.errors)||void 0===n?void 0:n.length)&&(e={code:e.errors[0].error,message:e.errors[0].message}),t.props.noticeOperations.createErrorNotice("internal_server_error"===e.code?"Internal server error":e.message),t.setState({isLoading:!1,isCopying:!1})):t.setState({isAuthenticated:!1,isLoading:!1,isCopying:!1})})),z()(F()(t),"getMediaRequest",(function(e){var n=t.state,r=n.nextHandle,a=n.media;if(!1===r&&a.length>0)t.setState({isLoading:!1});else{var o=t.getRequestUrl(e);t.setAuthenticated(!0),l()({path:o,method:"GET",parse:void 0===window.wpcomFetch}).then((function(e){t.setState({account:e.meta.account,media:t.mergeMedia(a,e.media),nextHandle:e.meta.next_page,isLoading:!1})})).catch(t.handleApiError)}})),z()(F()(t),"copyMedia",(function(e,n,r){var a;t.setState({isCopying:e}),t.props.noticeOperations.removeAllNotices(),t.modalElement&&t.modalElement.focus(),l()({path:n,method:"POST",data:{external_ids:e.map((function(e){return e.guid})),media:e.map((function(e){return{guid:e.guid,caption:e.caption,title:e.title}})),service:r,post_id:null!==(a=t.props.postId)&&void 0!==a?a:0}}).then((function(e){e.media&&(e=e.media.map((function(e){return{alt:e.alt,caption:e.caption,id:e.ID,type:"image",url:e.URL}})));var n=t.props,r=n.value,a=n.addToGallery,o=n.multiple?e:e[0],c=e.find((function(e){return e.errors}));if(c){var i=c.errors,l=Object.keys(i)[0];t.handleApiError({code:l,message:i[l]})}else t.props.onClose(),t.props.onSelect(a?r.concat(e):o)})).catch(t.handleApiError)})),z()(F()(t),"onChangePath",(function(e,n){t.setState({path:e},n)})),t.defaultAccount={image:"",name:""},t.state={account:t.defaultAccount,media:[],nextHandle:!1,isLoading:!1,isCopying:null,isAuthenticated:!0,path:{ID:"recent"}},t}return R()(n,t),N()(n,[{key:"mergeMedia",value:function(e,t){return Object(U.uniqBy)(e.concat(t),"ID")}},{key:"getRequestUrl",value:function(e){var t=this.state.nextHandle;return t?e+"&page_handle="+encodeURIComponent(t):e}},{key:"render",value:function(){var t=this.state,n=t.account,r=t.isAuthenticated,a=t.isCopying,o=t.isLoading,c=t.media,i=t.nextHandle,l=t.path,s=this.props,p=s.allowedTypes,d=s.multiple,m=void 0!==d&&d,b=s.noticeUI,h=s.onClose,f=a?Object(u.__)("Inserting media","jetpack"):Object(u.__)("Select media","jetpack"),g=a?Object(u.__)("When the media is finished copying and inserting, you will be returned to the editor.","jetpack"):Object(u.__)("Select the media you would like to insert into the editor.","jetpack"),j="jetpack-external-media-browser__description",v=H()({"jetpack-external-media-browser":!0,"jetpack-external-media-browser--is-copying":a});return Object(_.createElement)(G.Modal,{onRequestClose:h,title:f,aria:{describedby:j},className:v},Object(_.createElement)("div",{ref:this.contentRef},b,Object(_.createElement)("p",{id:j,className:"jetpack-external-media-browser--visually-hidden"},g),Object(_.createElement)(e,{account:n,getMedia:this.getMedia,copyMedia:this.copyMedia,isCopying:a,isLoading:o,media:c,pageHandle:i,allowedTypes:p,isAuthenticated:r,setAuthenticated:this.setAuthenticated,multiple:m,path:l,onChangePath:this.onChangePath})))}}]),n}(_.Component);return Object(s.withSelect)((function(e){return{postId:e("core/editor").getCurrentPostId()}}))(Object(G.withNotices)(t))}))}var te=n(91),ne=function(e,t){var n,r=new te.a;r.open(e,null,"toolbar=0,location=0,status=0,menubar=0,"+r.getScreenCenterSpecs(780,700)),r.once("close",(function(){var e={};n&&n.keyring_id&&(e.keyring_id=Number(n.keyring_id),e.id_token=n.id_token,e.user=n.user),t(e)})),r.on("message",(function(e){return n=e}))},re=n(30),ae={list:"/wpcom/v2/external-media/list/",copy:Object(m.b)()?"/rest/v1.1/external-media-upload?service=":"/wpcom/v2/external-media/copy/",connection:"/wpcom/v2/external-media/connection/"};function oe(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return ae[e]?Object(re.addQueryArgs)(ae[e]+t,n):null}var ce=Object(_.memo)((function(){return Object(_.createElement)(_.Fragment,null,Object(_.createElement)(C.c,null),Object(_.createElement)("p",null,Object(u.__)("To show your Google Photos library you need to connect your Google account.","jetpack")),Object(_.createElement)("p",null,Object(u.__)("You can remove the connection in either of these places:","jetpack")),Object(_.createElement)("ul",null,Object(_.createElement)("li",null,Object(_.createElement)("a",{target:"_blank",rel:"noopener noreferrer",href:"https://myaccount.google.com/security"},Object(u.__)("Google Security page","jetpack"))),Object(_.createElement)("li",null,Object(_.createElement)("a",{target:"_blank",rel:"noopener noreferrer",href:"https://wordpress.com/marketing/connections/"},Object(u.__)("WordPress.com Connections","jetpack")))))}));var ie=Object(_.memo)((function(){return Object(_.createElement)("p",null,Object(u.__)("Awaiting authorization","jetpack"))}));var le=function(e){var t=e.setAuthenticated,n=Object(_.useState)(!1),r=E()(n,2),a=r[0],o=r[1],c=Object(_.useCallback)((function(){o(!0),l()({path:oe("connection","google_photos")}).then((function(e){if(e.error)throw e.message;ne(e.connect_URL,(function(){o(!1),t(!0)}))})).catch((function(){o(!1)}))}),[t]);return Object(_.createElement)("div",{className:"jetpack-external-media-auth"},a?Object(_.createElement)(ie,null):Object(_.createElement)(ce,null),Object(_.createElement)(G.Button,{isPrimary:!0,disabled:a,onClick:c},Object(u.__)("Authorize","jetpack")))};var se=Object(_.memo)((function(){var e="jetpack-external-media-browser__media__item jetpack-external-media-browser__media__placeholder";return Object(_.createElement)(_.Fragment,null,Object(_.createElement)("div",{className:e}),Object(_.createElement)("div",{className:e}),Object(_.createElement)("div",{className:e}))}));var ue=function(e){var t=e.item,n=e.focus,r=e.isSelected,a=e.isCopying,o=void 0!==a&&a,c=t.thumbnails,i=t.caption,l=t.name,s=t.title,p=t.type,d=t.children,m=void 0===d?0:d,b=c.medium,h=void 0===b?null:b,f=c.fmt_hd,g=void 0===f?null:f,j=s||i||l,v=H()({"jetpack-external-media-browser__media__item":!0,"jetpack-external-media-browser__media__item__selected":r,"jetpack-external-media-browser__media__folder":"folder"===p,"is-transient":o}),k=Object(_.useRef)(null);return Object(_.useEffect)((function(){n&&k.current.focus()}),[n]),Object(_.createElement)("li",{ref:k,className:v,onClick:o?void 0:function(t){var n=e.item,r=e.index;e.onClick&&e.onClick(t,{item:n,index:r})},onKeyDown:o?void 0:function(t){var n=e.item,r=e.index;e.onKeyDown&&e.onKeyDown(t,{item:n,index:r})},role:"checkbox",tabIndex:"0","aria-checked":!!r,"aria-disabled":!!o},r&&o&&Object(_.createElement)("div",{className:"jetpack-external-media-browser__media__copying_indicator"},Object(_.createElement)(G.Spinner,null),Object(_.createElement)("div",{className:"jetpack-external-media-browser__media__copying_indicator__label"},Object(u.__)("Inserting Image…","jetpack"))),Object(_.createElement)("img",{src:h||g,alt:j}),"folder"===p&&Object(_.createElement)("div",{className:"jetpack-external-media-browser__media__info"},Object(_.createElement)("div",{className:"jetpack-external-media-browser__media__name"},l),Object(_.createElement)("div",{className:"jetpack-external-media-browser__media__count"},m)))},pe=Object(_.memo)((function(){return Object(_.createElement)("div",{className:"jetpack-external-media-browser__empty"},Object(_.createElement)("p",null,Object(u.__)("Sorry, but nothing matched your search criteria.","jetpack")))}));var de=function(e){var t=e.media,n=e.isCopying,r=e.isLoading,a=e.pageHandle,o=e.className,c=e.multiple,i=e.setPath,l=e.nextPage,s=e.onCopy,p=Object(_.useState)([]),d=E()(p,2),m=d[0],b=d[1],h=Object(_.useState)(-1),f=E()(h,2),g=f[0],j=f[1],v=Object(_.useRef)(-1),k=Object(_.useRef)(null),y=Object(_.useCallback)((function(e){var t=[e];"folder"===e.type?i(e.ID):c?(t=m.slice(0,9).concat(e),m.find((function(t){return e.ID===t.ID}))&&(t=m.filter((function(t){return t.ID!==e.ID})))):1===m.length&&e.ID===m[0].ID&&(t=[]),b(t)}),[m,c,i]),O=Object(_.useCallback)((function(){s(m)}),[m,s]),w=t.filter((function(e){return"folder"!==e.type})).length>0,C=H()({"jetpack-external-media-browser__media":!0,"jetpack-external-media-browser__media__loading":r}),x=H()(z()({"jetpack-external-media-browser":!0},o,!0)),S=function(){var e=1,t=k.current.children;if(t.length>0)for(var n=t[0].offsetTop;e=1&&j(n-1);break;case W.RIGHT:n=v.current&&j(n-v.current);break;case W.DOWN:n0?t[0].value:""}var Oe=function(e){var t=Object(_.useState)(ye([])),n=E()(t,2),r=n[0],o=n[1],c=e.isLoading,i=e.isCopying,l=e.filters,s=e.canChangeMedia,p=function(e,t){return t?e:e.filter((function(e){return"mediaType"!==e.value}))}(ke(l),s);return 0===p.length?null:Object(_.createElement)(_.Fragment,null,Object(_.createElement)(G.SelectControl,{label:Object(u.__)("Filters","jetpack"),value:r,disabled:c||i,options:p,onChange:o}),Object(_.createElement)(G.Button,{disabled:c||i,isSecondary:!0,isSmall:!0,onClick:function(){var t,n,c=(t=l,n=r,a()({},t,z()({},n,"favorite"===n||"")));e.setFilters(c),o(ye(c))}},Object(u.__)("Add Filter","jetpack")))},we=function(e){var t=e.setAuthenticated,n=Object(_.useState)(!1),r=E()(n,2),a=r[0],o=r[1],c=Object(_.useCallback)((function(){o(!0),l()({method:"DELETE",path:oe("connection","google_photos")}).then((function(){return t(!1)})).catch((function(){return o(!1)}))}),[t]);return Object(_.createElement)(G.Button,{isSecondary:!0,className:"jetpack-external-media-browser__disconnect",onClick:c,disabled:a,isBusy:a},Object(u.__)("Disconnect from Google Photos","jetpack"))},Ee=function(e){var t=e.account,n=e.setAuthenticated,r=t||{},a=r.image,o=r.name;return Object(_.createElement)("div",{className:"jetpack-external-media-header__account"},Object(_.createElement)("div",{className:"jetpack-external-media-header__account-info"},a&&Object(_.createElement)("img",{className:"jetpack-external-media-header__account-image",src:a,alt:"",height:"18",width:"18"}),o&&Object(_.createElement)("div",{className:"jetpack-external-media-header__account-name"},o)),Object(_.createElement)(we,{setAuthenticated:n}))};var Ce=Object(_.memo)((function(e){var t=e.path,n=e.setPath;return Object(_.createElement)(_.Fragment,null,Object(_.createElement)(G.Button,{isTertiary:!0,onClick:function(){return n("/")}},Object(u.__)("Albums","jetpack")),"→ ",t.name)})),xe=n(44),Se=n.n(xe),Ae=Se()();var Ne=function(e){var t,n=e.account,r=e.allowedTypes,a=e.copyMedia,o=e.getMedia,c=e.isCopying,i=e.isLoading,l=e.media,s=e.multiple,p=e.onChangePath,d=e.pageHandle,m=e.path,b=e.setAuthenticated,h=e.showAdditionalFilters,f=void 0!==h&&h,g=(t=r)&&1===t.length&&"image"===t[0],j=Object(_.useState)(g?{mediaType:"photo",date:{range:"ANY"}}:{date:{range:"ANY"}}),v=E()(j,2),k=v[0],y=v[1],O=Object(_.useRef)(""),w=Object(_.useRef)(""),C="recent"===m.ID?function(e){var t=e.mediaType,n=e.category,r=e.favorite,a=e.date,o=[];if(t&&o.push("mediaType="+t),n&&"video"!==t&&o.push("categoryInclude="+n),void 0!==r&&o.push("feature=favorite"),a){var c=null,i=null;switch(a.range){case"LAST_7_DAYS":c=Se()(Ae).subtract(7,"days"),i=Ae;break;case"LAST_30_DAYS":c=Se()(Ae).subtract(30,"days"),i=Ae;break;case"LAST_6_MONTHS":c=Se()(Ae).subtract(6,"months"),i=Ae;break;case"LAST_12_MONTHS":c=Se()(Ae).subtract(1,"year"),i=Ae;break;case"CUSTOM":var l=parseInt(a.month),s=parseInt(a.year);isNaN(l)||isNaN(s)||(-1===l?(c=Se()([s,0]),i=Se()(c).endOf("year")):(c=Se()([s,l]),i=Se()(c).endOf("month")))}var u=c?c.format("YYYY-MM-DD"):"0000-00-00",p=i?i.format("YYYY-MM-DD"):"0000-00-00";o.push("dateRange=".concat(u,":").concat(p))}return o.length>0?o:null}(k):null,x={number:20,path:m.ID};C&&(x.filter=C);var S=oe("list","google_photos",x),A=Object(_.useCallback)((function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];o(S,t)}),[o,S]),N=Object(_.useCallback)((function(e){var t=l.find((function(t){return t.ID===e}));w.current=m,p(t||{ID:e})}),[l,p,w,m]),P=Object(_.useCallback)((function(e){a(e,oe("copy","google_photos"),"google_photos")}),[a]);return Object(_.useEffect)((function(){O!==S&&(O.current=S,A({},m!==w.current))}),[O,S,A,m]),Object(_.createElement)("div",{className:"jetpack-external-media-wrapper__google"},Object(_.createElement)("div",{className:"jetpack-external-media-header__view"},Object(_.createElement)(G.SelectControl,{className:"jetpack-external-media-header__select",label:Object(u.__)("View","jetpack"),value:"recent"!==m.ID?"/":"recent",disabled:i||c,options:$,onChange:N}),f&&"recent"===m.ID&&Object(_.createElement)(Oe,{filters:k,isLoading:i,setFilters:y,canChangeMedia:!g}),Object(_.createElement)("div",{className:"jetpack-external-media-header__filter"},"recent"===m.ID&&Object(_.createElement)(ve,{filters:k,isLoading:i,setFilters:y,canChangeMedia:!g}),"recent"!==m.ID&&"/"!==m.ID&&Object(_.createElement)(Ce,{path:m,setPath:N})),(!i||l.length>0)&&Object(_.createElement)(Ee,{account:n,setAuthenticated:b})),Object(_.createElement)(de,{className:"jetpack-external-media-browser__google",key:S,media:l,isCopying:c,isLoading:i,nextPage:A,onCopy:P,pageHandle:d,multiple:s,setPath:N}))};var Pe=ee()((function(e){return e.isAuthenticated?Object(_.createElement)(Ne,e):Object(_.createElement)(le,e)}));var Te=ee()((function(e){var t=e.media,n=e.isCopying,r=e.isLoading,a=e.pageHandle,o=e.multiple,c=e.copyMedia,i=e.getMedia,l=Object(_.useState)(Object(U.sample)(J)),s=E()(l,2),p=s[0],d=s[1],m=Object(_.useState)(""),b=E()(m,2),h=b[0],f=b[1],g=Object(_.useCallback)((function(e){c(e,oe("copy","pexels"),"pexels")}),[c]),j=Object(_.useCallback)((function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];p&&i(oe("list","pexels",{number:20,path:"recent",search:p}),t)}),[i,p]),v=Object(_.useRef)(),k=Object(_.useCallback)((function(e){e.preventDefault(),f(p),j(e,!0),v.current=p}),[j,p]);Object(_.useEffect)(j,[]);var y=Object(_.useRef)(null);return Object(_.useEffect)((function(){if(y.current){var e=Array.from(y.current.elements).find((function(e){return"search"===e.type}));e&&(e.focus(),e.select())}}),[]),Object(_.createElement)("div",{className:"jetpack-external-media-wrapper__pexels"},Object(_.createElement)("form",{ref:y,className:"jetpack-external-media-header__pexels",onSubmit:k},Object(_.createElement)(G.TextControl,{"aria-label":Object(u.__)("Search","jetpack"),type:"search",value:p,onChange:d,disabled:!!n}),Object(_.createElement)(G.Button,{isPrimary:!0,onClick:k,type:"submit",disabled:!p.length||p===v.current||n},Object(u.__)("Search","jetpack"))),Object(_.createElement)(de,{key:h,className:"jetpack-external-media-browser__pexels",media:t,isCopying:n,isLoading:r,nextPage:j,onCopy:g,pageHandle:a,multiple:o}))})),Be=[{id:"google_photos",label:Object(u.__)("Google Photos","jetpack"),icon:Object(_.createElement)(C.b,{className:"components-menu-items__item-icon"}),keyword:"google photos"},{id:"pexels",label:Object(u.__)("Pexels Free Photos","jetpack"),icon:Object(_.createElement)(C.f,{className:"components-menu-items__item-icon"}),keyword:"pexels"}];var Ie=n(102),Me=Object(_.createElement)(Ie.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},Object(_.createElement)(Ie.Path,{d:"M18.7 3H5.3C4 3 3 4 3 5.3v13.4C3 20 4 21 5.3 21h13.4c1.3 0 2.3-1 2.3-2.3V5.3C21 4 20 3 18.7 3zm.8 15.7c0 .4-.4.8-.8.8H5.3c-.4 0-.8-.4-.8-.8V5.3c0-.4.4-.8.8-.8h13.4c.4 0 .8.4.8.8v13.4zM10 15l5-3-5-3v6z"}));var Fe=function(e){var t=e.originalButton,n=void 0===t?null:t,r=e.open,a=e.setSource;return Object(_.createElement)(_.Fragment,null,n&&n({open:r}),Be.map((function(e){var t=e.icon,n=e.id,r=e.label;return Object(_.createElement)(G.MenuItem,{icon:t,key:n,onClick:function(){return a(n)}},r)})))};var Le=function(e){var t=e.mediaProps,n=e.open,r=e.setSelectedSource,a=e.isFeatured,o=e.isReplace,c=t.render;if(a&&void 0===t.value)return c({open:n});if(o)return Object(_.createElement)(Fe,{originalButton:c,open:n,setSource:r});var i=Object(u.__)("Select Image","jetpack");return t.multiple&&(i=Object(u.__)("Select Images","jetpack")),t.allowedTypes.length>1&&(i=Object(u.__)("Select Media","jetpack")),Object(_.createElement)(_.Fragment,null,a&&c({open:n}),Object(_.createElement)(G.Dropdown,{position:"bottom right",contentClassName:"jetpack-external-media-button-menu__options",renderToggle:function(e){var t=e.isOpen,n=e.onToggle;return Object(_.createElement)(G.Button,{isTertiary:!a,isPrimary:a,className:"jetpack-external-media-button-menu","aria-haspopup":"true","aria-expanded":t,onClick:n},i)},renderContent:function(){return Object(_.createElement)(G.NavigableMenu,{"aria-label":i},Object(_.createElement)(G.MenuGroup,null,Object(_.createElement)(G.MenuItem,{icon:Me,onClick:n},Object(u.__)("Media Library","jetpack")),Object(_.createElement)(Fe,{open:n,setSource:r})))}}))},Re=function(e){return e.unstableFeaturedImageFlow||e.modalClass&&-1!==e.modalClass.indexOf("featured-image")},De=function(e){return void 0===e.multiple&&!Re(e)};var ze=function(e){var t,n=e.mediaProps,r=Object(_.useState)(null),a=E()(r,2),o=a[0],c=a[1],i="pexels"===(t=o)?Te:"google_photos"===t?Pe:null;return Object(_.createElement)("div",{onClick:function(e){return e.stopPropagation()}},Object(_.createElement)(Le,g()({},e,{setSelectedSource:c,isReplace:De(n),isFeatured:Re(n)})),i&&Object(_.createElement)(i,g()({onClose:function(e){e&&(e.stopPropagation(),e.target.closest(".jetpack-external-media-header__dropdown"))||c(null)}},n)))};n(202);if(Object(O.a)()&&"function"==typeof y.useBlockEditContext){Object(k.addFilter)("editor.MediaUpload","external-media/replace-media-upload",(function(e){return function(t){var n=Object(y.useBlockEditContext)().name,r=t.render;if(function(e,t){return["core/cover","core/image","core/gallery","core/media-text","jetpack/image-compare","jetpack/slideshow","jetpack/tiled-gallery"].indexOf(e)>-1&&-1===t.toString().indexOf("coblocks")}(n,r)||function(e){return e.unstableFeaturedImageFlow||e.modalClass&&e.modalClass.indexOf("featured-image")>-1}(t)){var a=t.allowedTypes,o=t.gallery,c=void 0!==o&&o,i=t.value,l=void 0===i?[]:i;a.indexOf("image")>-1&&!(c&&l.length>0)&&(r=function(e){return Object(_.createElement)(ze,g()({},e,{mediaProps:t}))})}return Object(_.createElement)(e,g()({},t,{render:r}))}}),100),Object(k.addFilter)("blocks.registerBlockType","external-media/individual-blocks",(function(e,t){return"core/image"!==t?e:a()({},e,{keywords:[].concat(v()(e.keywords),v()(Be.map((function(e){return e.keyword}))))})}))}var Ue=n(17),Ve={name:"facebook",title:"Facebook",icon:{src:"facebook",foreground:Object(Ue.a)()},keywords:[Object(u._x)("social","block search term","jetpack")],description:Object(u.__)("Embed a Facebook post.","jetpack"),patterns:[/^https?:\/\/www\.facebook.com\/.+/i],attributes:{providerNameSlug:"facebook",previewable:!1,responsive:!0}};Object(k.addFilter)("blocks.registerBlockType","reactivateFacebookEmbedBlockVariation",(function(e,t){if("core/embed"!==t||!e.variations)return e;var n=e.variations.filter((function(e){return e.name!==Ve.name}));return e.variations=[].concat(v()(n),[Ve]),e}));var He=n(51);var qe={name:"instagram",title:"Instagram",icon:{src:"instagram",foreground:Object(Ue.a)()},keywords:[Object(u._x)("image","block search term","jetpack"),Object(u._x)("social","block search term","jetpack")],description:Object(u.__)("Embed an Instagram post.","jetpack"),patterns:[/^https?:\/\/(www\.)?instagr(\.am|am\.com)\/.+/i],attributes:{providerNameSlug:"instagram",responsive:!0}};Object(k.addFilter)("blocks.registerBlockType","reactivateInstagramEmbedBlockVariation",(function(e,t){if("core/embed"!==t||!e.variations||!Object(U.get)(Object(He.a)(),["jetpack","is_active"],!1))return e;var n=e.variations.filter((function(e){return e.name!==qe.name}));return e.variations=[].concat(v()(n),[qe]),e}));var Ge=n(16),We={name:"loom",title:"Loom",icon:C.e,keywords:[Object(u.__)("video","jetpack")],description:Object(u.__)("Embed a Loom video.","jetpack"),patterns:[/^https?:\/\/(www\.)?loom\.com\/share\/.+/i],attributes:{providerNameSlug:"loom",responsive:!0}};Object(Ge.registerBlockVariation)("core/embed",We);var Ke=n(55),$e=n.n(Ke),Ze=n(37),Je=n(98),Ye=function(e){var t=e.onRedirect,n=e.align,r=e.className,a=e.title,o=void 0===a?Object(u.__)("Premium Block","jetpack"):a,c=e.description,i=void 0===c?Object(u.__)("Upgrade your plan to use this premium block","jetpack"):c,l=e.buttonText,s=void 0===l?Object(u.__)("Upgrade","jetpack"):l,p=e.visible,d=void 0===p||p,m=e.requiredPlan,b=e.context,h=Object(Je.a)(m,t),f=E()(h,3),g=f[0],j=f[1],v=f[2],k=H()(r,"jetpack-upgrade-plan-banner",{"wp-block":"editor-canvas"===b,"block-editor-block-list__block":"editor-canvas"===b,"jetpack-upgrade-plan__hidden":!d}),y=Object(u.__)("Redirecting…","jetpack");return Object(_.createElement)("div",{className:k,"data-align":n},Object(_.createElement)("div",{className:"jetpack-upgrade-plan-banner__wrapper"},o&&Object(_.createElement)("strong",{className:H()("banner-title",z()({},"".concat(r,"__title"),r))},o),i&&Object(_.createElement)("span",{className:"".concat(r,"__description banner-description")},i),Object(_.createElement)(G.Button,{href:v?null:g,onClick:j,target:"_top",className:H()("is-primary",{"jetpack-upgrade-plan__hidden":!g}),isBusy:v},v?y:s)))},Qe=Object(_.createContext)(),Xe=function(e){var t=e.onBannerVisibilityChange,n=e.children;return Object(_.createElement)(Qe.Provider,{value:t,children:n})},et=n(59),tt=function(e){var t=e.plan,n=e.blockName,r=e.context;et.a.tracks.recordEvent("jetpack_editor_block_upgrade_click",{plan:t,block:n,context:r})},nt=Object(q.createHigherOrderComponent)((function(e){return function(t){var n,r=Object(Ze.a)(null==t?void 0:t.name);if(!r)return Object(_.createElement)(e,t);var a=Object(Ze.d)(t.name),o=Object(Ze.c)(t.name),c=Object(_.useState)(!a),i=E()(c,2),l=i[0],u=i[1],p=Object(_.useState)(!1),d=E()(p,2),m=d[0],b=d[1],h=Object(s.useSelect)((function(e){return e("core/block-editor").hasSelectedInnerBlock(null==t?void 0:t.clientId)}),[]),f=((null==t?void 0:t.isSelected)||h)&&l,j={plan:r,blockName:t.name,context:"editor-canvas"};Object(_.useEffect)((function(){f&&b(!0)}),[f,b]),Object(_.useEffect)((function(){var e,t,n,r;!m&&f&&(t=(e=j).plan,n=e.blockName,r=e.context,et.a.tracks.recordEvent("jetpack_editor_block_upgrade_banner_impression",{plan:t,block:n,context:r}))}),[m,j,f]),Object(_.useEffect)((function(){return u(!a)}),[t.attributes,u,a]);var v=H()(null==t?void 0:t.className,{"is-upgradable":f});return Object(_.createElement)(Xe,{onBannerVisibilityChange:u},Object(_.createElement)(Ye,{className:"is-".concat(t.name.replace(/\//,"-"),"-paid-block"),title:null,align:null==t||null===(n=t.attributes)||void 0===n?void 0:n.align,visible:f,description:null==o?void 0:o.description,requiredPlan:r,context:"editor-canvas",onRedirect:function(){return tt(j)}}),Object(_.createElement)(e,g()({},t,{className:v})))}}),"withUpgradeBanner"),rt=n(97);n(204);function at(){return Object(U.get)(Object(He.a)(),["allowedMimeTypes"],[])}function ot(e,t){if(!e||!t)return!1;if(!at())return!1;var n,r,a=(n=t)?Object(U.pickBy)(at(),(function(e){return Object(U.startsWith)(e,"".concat(n,"/"))})):{},o=(r=a)?Object(U.flatten)(Object(U.map)(Object(U.keys)(r),(function(e){return e.split("|")}))):[];if("string"==typeof e){var c=e.split(".").pop();return c&&o.includes(c)}return"object"==typeof e&&(e.type&&Object(U.values)(a).includes(e.type))}var ct=Object(q.createHigherOrderComponent)((function(e){return function(t){var n=Object(y.useBlockEditContext)().name,r=Object(Ze.c)(n);if(!(null==r?void 0:r.mediaPlaceholder)||!Object(Ze.e)(n))return Object(_.createElement)(e,t);var a=r.fileType,o=t.onError,c=Object(_.useContext)(Qe),i=Object(_.useCallback)((function(e){return c((null==e?void 0:e.length)&&ot(e[0],a))}),[a,c]),l=Object(_.useCallback)((function(e){var t,n,r=null==e||null===(t=e[0])||void 0===t||null===(n=t.props)||void 0===n?void 0:n.children;return ot(r,a)?i([r]):o(e)}),[i,a,o]);return Object(_.createElement)("div",{className:"paid-block-media-placeholder"},Object(_.createElement)(e,g()({},t,{onFilesPreUpload:i,onError:l})))}}),"withMediaPlaceholderUpgradable"),it=Object(q.createHigherOrderComponent)((function(e){return function(t){var n=Object(y.useBlockEditContext)().name,r=Object(Ze.c)(n),a=Object(_.useRef)();if(!(null==r?void 0:r.mediaReplaceFlow)||!Object(Ze.e)(n))return Object(_.createElement)(e,t);var o=r.fileType,c=Object(_.useContext)(Qe);return Object(_.createElement)(e,g()({},t,{onFilesUpload:function(e){a.current=(null==e?void 0:e.length)?e[0]:null,c((null==e?void 0:e.length)&&ot(e[0],o))},createNotice:function(e,n,r){if(ot(a.current,o))return a.current=null,null;t.createNotice(e,n,r)}}))}}),"withMediaReplaceFlowUpgradable"),lt=[];Object(k.addFilter)("blocks.registerBlockType","jetpack/paid-block",(function(e,t){return Object(Ze.e)(t)&&(lt.includes(t)||lt.push(t),e.keywords=Object(U.uniq)([].concat(v()(e.keywords),["premium",Object(u.__)("premium","jetpack")])),Object(Ze.d)(t)||(e.icon=Object(rt.a)(e.icon))),e})),Object(k.addFilter)("editor.BlockEdit","jetpack/paid-block-edit",(function(e){return function(t){var n=Object(Ze.a)(null==t?void 0:t.name);if(!n)return Object(_.createElement)(e,t);return Object(_.createElement)(_.Fragment,null,Object(_.createElement)(y.InspectorControls,null,Object(_.createElement)(Ye,{description:null,requiredPlan:n,context:"sidebar",onRedirect:function(){return tt({plan:n,blockName:t.name,context:"sidebar"})}})),Object(_.createElement)(e,t))}}),30),Object(k.addFilter)("editor.BlockListBlock","jetpack/paid-block-with-warning",nt),Object(k.addFilter)("editor.MediaPlaceholder","jetpack/paid-block-media-placeholder",ct),Object(k.addFilter)("editor.MediaReplaceFlow","jetpack/paid-block-media-placeholder",it),$e()((function(){Object(Ze.f)()&&document.body.classList.add("jetpack-enable-upgrade-nudge")}));n(205);if("object"==typeof window&&"object"==typeof window.Jetpack_Editor_Initial_State&&"object"==typeof window.Jetpack_Editor_Initial_State.tracksUserData&&void 0!==window.Jetpack_Editor_Initial_State.wpcomBlogId){var st=window.Jetpack_Editor_Initial_State.tracksUserData,ut=st.userid,pt=st.username;et.a.initialize(ut,pt,{blog_id:window.Jetpack_Editor_Initial_State.wpcomBlogId})}},function(e,t,n){"use strict";n.r(t);var r={};n.r(r),n.d(r,"save",(function(){return Lt})),n.d(r,"attributes",(function(){return Rt})),n.d(r,"support",(function(){return Dt}));var a={};n.r(a),n.d(a,"save",(function(){return Cn})),n.d(a,"attributes",(function(){return Sn})),n.d(a,"supports",(function(){return An}));var o=n(20),c=n(9),i=n.n(c),l=n(3),s=n.n(l),u=n(0),p=n(1),d=n(16),m=n(4),b=n(2),h=n(21),f=n.n(h),g=n(11),j=n.n(g),v=n(15),_=n.n(v),k=n(12),y=n.n(k),O=n(13),w=n.n(O),E=n(6),C=n.n(E),x=n(14),S=n.n(x),A=n(5),N=n(52),P=[{icon:Object(u.createElement)(b.SVG,{xmlns:"http://www.w3.org/2000/svg",width:"24",height:"24",viewBox:"0 0 24 24"},Object(u.createElement)(b.Path,{fill:"none",d:"M0 0h24v24H0V0z"}),Object(u.createElement)(b.Path,{d:"M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14z"})),title:Object(p._x)("Original","image style","jetpack"),value:void 0},{icon:Object(u.createElement)(b.SVG,{xmlns:"http://www.w3.org/2000/svg",width:"24",height:"24",viewBox:"0 0 24 24"},Object(u.createElement)(b.Path,{fill:"none",d:"M0 0h24v24H0V0z"}),Object(u.createElement)(b.Path,{d:"M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm11 10h2V5h-4v2h2v8zm7-14H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14z"})),title:Object(p._x)("Black and White","image style","jetpack"),value:"black-and-white"},{icon:Object(u.createElement)(b.SVG,{xmlns:"http://www.w3.org/2000/svg",width:"24",height:"24",viewBox:"0 0 24 24"},Object(u.createElement)(b.Path,{fill:"none",d:"M0 0h24v24H0V0z"}),Object(u.createElement)(b.Path,{d:"M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm18-4H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zm-4-4h-4v-2h2c1.1 0 2-.89 2-2V7c0-1.11-.9-2-2-2h-4v2h4v2h-2c-1.1 0-2 .89-2 2v4h6v-2z"})),title:Object(p._x)("Sepia","image style","jetpack"),value:"sepia"},{icon:Object(u.createElement)(b.SVG,{xmlns:"http://www.w3.org/2000/svg",width:"24",height:"24",viewBox:"0 0 24 24"},Object(u.createElement)(b.Path,{fill:"none",d:"M0 0h24v24H0V0z"}),Object(u.createElement)(b.Path,{d:"M21 1H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zM3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm14 8v-1.5c0-.83-.67-1.5-1.5-1.5.83 0 1.5-.67 1.5-1.5V7c0-1.11-.9-2-2-2h-4v2h4v2h-2v2h2v2h-4v2h4c1.1 0 2-.89 2-2z"})),title:"1977",value:"1977"},{icon:Object(u.createElement)(b.SVG,{xmlns:"http://www.w3.org/2000/svg",width:"24",height:"24",viewBox:"0 0 24 24"},Object(u.createElement)(b.Path,{fill:"none",d:"M0 0h24v24H0V0z"}),Object(u.createElement)(b.Path,{d:"M3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm12 10h2V5h-2v4h-2V5h-2v6h4v4zm6-14H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14z"})),title:Object(p._x)("Clarendon","image style","jetpack"),value:"clarendon"},{icon:Object(u.createElement)(b.SVG,{xmlns:"http://www.w3.org/2000/svg",width:"24",height:"24",viewBox:"0 0 24 24"},Object(u.createElement)(b.Path,{fill:"none",d:"M0 0h24v24H0z"}),Object(u.createElement)(b.Path,{d:"M21 1H7c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V3c0-1.1-.9-2-2-2zm0 16H7V3h14v14zM3 5H1v16c0 1.1.9 2 2 2h16v-2H3V5zm14 8v-2c0-1.11-.9-2-2-2h-2V7h4V5h-6v6h4v2h-4v2h4c1.1 0 2-.89 2-2z"})),title:Object(p._x)("Gingham","image style","jetpack"),value:"gingham"}],T=Object(p.__)("Pick an image filter","jetpack");function B(e){var t=e.value,n=e.onChange;return Object(u.createElement)(b.Dropdown,{position:"bottom right",className:"editor-block-switcher",contentClassName:"editor-block-switcher__popover",renderToggle:function(e){var t=e.onToggle,n=e.isOpen;return Object(u.createElement)(b.Toolbar,{controls:[{onClick:t,extraProps:{"aria-haspopup":"true","aria-expanded":n},title:T,tooltip:T,icon:Object(u.createElement)(b.SVG,{xmlns:"http://www.w3.org/2000/svg",width:"24",height:"24",viewBox:"0 0 24 24"},Object(u.createElement)(b.Path,{fill:"none",d:"M0 0h24v24H0V0z"}),Object(u.createElement)(b.Path,{d:"M19 10v9H4.98V5h9V3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-9h-2zm-2.94-2.06L17 10l.94-2.06L20 7l-2.06-.94L17 4l-.94 2.06L14 7zM12 8l-1.25 2.75L8 12l2.75 1.25L12 16l1.25-2.75L16 12l-2.75-1.25z"}))}]})},renderContent:function(e){var r=e.onClose;return Object(u.createElement)(b.NavigableMenu,{className:"tiled-gallery__filter-picker-menu"},P.map((function(e){var a,o=e.icon,c=e.title,i=e.value;return Object(u.createElement)(b.MenuItem,{className:t===i?"is-active":void 0,icon:o,isSelected:t===i,key:i||"original",onClick:(a=i,function(){n(t===a?void 0:a),r()}),role:"menuitemcheckbox"},c)})))}})}var I=n(7),M=n.n(I),F=n(25),L=n(32),R=n(10),D=Object(u.createElement)(b.SVG,{width:"18",height:"18",viewBox:"0 0 18 18",xmlns:"http://www.w3.org/2000/svg"},Object(u.createElement)(b.Path,{d:"M5 8.70002L10.6 14.4L12 12.9L7.8 8.70002L12 4.50002L10.6 3.00002L5 8.70002Z"})),z=Object(u.createElement)(b.SVG,{width:"18",height:"18",viewBox:"0 0 18 18",xmlns:"http://www.w3.org/2000/svg"},Object(u.createElement)(b.Path,{d:"M13 8.7L7.4 3L6 4.5L10.2 8.7L6 12.9L7.4 14.4L13 8.7Z"})),U=Object(u.createElement)(b.SVG,{viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},Object(u.createElement)(b.Path,{d:"M7.41,8.59L12,13.17l4.59-4.58L18,10l-6,6l-6-6L7.41,8.59z"})),V=Object(u.createElement)(b.SVG,{viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},Object(u.createElement)(b.Path,{d:"M12,8l-6,6l1.41,1.41L12,10.83l4.59,4.58L18,14L12,8z"})),H=Object(u.createElement)(b.SVG,{width:"18",height:"18",viewBox:"-2 -2 24 24",xmlns:"http://www.w3.org/2000/svg"},Object(u.createElement)(b.Path,{d:"M14.95 6.46L11.41 10l3.54 3.54-1.41 1.41L10 11.42l-3.53 3.53-1.42-1.42L8.58 10 5.05 6.47l1.42-1.42L10 8.58l3.54-3.53z"})),q=function(e){function t(){var e,n;j()(this,t);for(var r=arguments.length,a=new Array(r),o=0;o=2}]);function ge(e,t,n){var r=Object(m.sum)(Object(m.take)(t,3));return t.length>=3&&4!==t.length&&6!==t.length&&me(e)&&(r<2.5||r<5&&t.length>=3&&t[0]===t[2]||n)}function je(e,t){var n=Object(m.sum)(Object(m.take)(t,4));return de(e)&&n<3.5&&t.length>5||n<7&&4===t.length}function ve(e,t){return function(n){return!Object(m.some)(Object(m.takeRight)(n,t),(function(t){return Object(m.isEqual)(t,e)}))}}function _e(e){return function(t){return t.length>=e.length&&Object(m.every)(Object(m.zipWith)(e,t.slice(0,e.length),(function(e,t){return e(t)})))}}function ke(e){return e>=1&&e<2}function ye(e){return e<1}function Oe(e){return function(t){return t>=e}}function we(e){return function(t){return t1&&void 0!==arguments[1]?arguments[1]:{},n=t.isWide,r=function e(t,r){if(!r.length)return t;var a;a=r.length>15&&ee(r)&&X(t)?[2,1,2]:r.length>15&&te(r)&&ne(t)?[3,1,3]:5!==r.length&&re(r)&&ae(t)?[1,2,1]:oe(r)&&ce(t)?[1,3]:ie(r)&&le(t)?[3,1]:se(r)&&ue(t)?[1,2]:n&&(5===r.length||10!==r.length&&r.length>6)&&pe(t)&&Object(m.sum)(Object(m.take)(r,5))<5?[1,1,1,1,1]:je(t,r)?[1,1,1,1]:ge(t,r,n)?[1,1,1]:be(r)&&he(t)?[2,1]:fe(r)?[1]:r.length>3?[1,1]:Array(r.length).fill(1);var o=t.concat([a]),c=Object(m.sum)(a);return e(o,r.slice(c))};return r([],e)}(i,{isWide:["full","wide"].includes(t)}),s=0;return Object(u.createElement)(Z,{galleryRef:this.gallery},l.map((function(e,t){return Object(u.createElement)(J,{key:t},e.map((function(e,n){var r=o.slice(s,s+e);return s+=e,Object(u.createElement)($,{key:n,width:c?c[t][n]:void 0},r)})))})))}}]),t}(u.Component),Ce=n(27);function xe(e){var t=e.columns,n=e.renderedImages,r=Math.min(Ce.h,t),a=n.length%r;return Object(u.createElement)(Z,null,[].concat(f()(a?[Object(m.take)(n,a)]:[]),f()(Object(m.chunk)(Object(m.drop)(n,a),r))).map((function(e,t){return Object(u.createElement)(J,{key:t,className:"columns-".concat(e.length)},e.map((function(e,t){return Object(u.createElement)($,{key:t},e)})))})))}var Se=n(74),Ae=function(e){function t(){return j()(this,t),y()(this,w()(t).apply(this,arguments))}return S()(t,e),_()(t,[{key:"renderImage",value:function(e,t){var n=this.props,r=n.columns,a=n.imageFilter,o=n.images,c=n.isSave,i=n.linkTo,l=n.layoutStyle,s=n.onMoveBackward,d=n.onMoveForward,m=n.onRemoveImage,b=n.onSelectImage,h=n.selectedImage,f=n.setImageAttributes,g=Object(p.sprintf)(Object(p.__)("image %1$d of %2$d in gallery","jetpack"),t+1,o.length),j=c?W:G,v=Object(Se.b)(e,{layoutStyle:l}),_=v.src,k=v.srcSet;return Object(u.createElement)(j,{alt:e.alt,"aria-label":g,columns:r,height:e.height,id:e.id,imageFilter:a,isFirstItem:0===t,isLastItem:t+1===o.length,isSelected:h===t,key:t,link:e.link,linkTo:i,onMoveBackward:c?void 0:s(t),onMoveForward:c?void 0:d(t),onRemove:c?void 0:m(t),onSelect:c?void 0:b(t),origUrl:e.url,setAttributes:c?void 0:f(t),showMovers:o.length>1,srcSet:k,url:_,width:e.width})}},{key:"render",value:function(){var e=this.props,t=e.align,n=e.children,r=e.className,a=e.columns,o=e.images,c=e.layoutStyle,i=e.roundedCorners,l=e.onResize,p=e.isSave,d=e.columnWidths,m=Object(Se.a)(c)?xe:Ee,b=this.props.images.map(this.renderImage,this),h=c!==Ce.c?Math.min(i,Ce.i):0;return Object(u.createElement)("div",{className:M()(r,s()({},"has-rounded-corners-".concat(h),h>0))},Object(u.createElement)(m,{align:t,columns:a,columnWidths:p?d:void 0,images:o,layoutStyle:c,renderedImages:b,onResize:p?void 0:l}),n)}}]),t}(u.Component),Ne=n(42),Pe=n(63),Te=[{value:"attachment",label:Object(p.__)("Attachment Page","jetpack")},{value:"media",label:Object(p.__)("Media File","jetpack")},{value:"none",label:Object(p.__)("None","jetpack")}];function Be(e){return Math.min(3,e.images.length)}var Ie=function(e){var t=Object(m.pick)(e,[["alt"],["id"],["link"]]);return t.url=Object(m.get)(e,["sizes","large","url"])||Object(m.get)(e,["media_details","sizes","large","source_url"])||e.url,t},Me=function(e){function t(){var e,n;j()(this,t);for(var r=arguments.length,a=new Array(r),o=0;o1&&Object(u.createElement)(b.RangeControl,{label:Object(p.__)("Columns","jetpack"),value:m,onChange:this.setColumnsNumber,min:1,max:Math.min(Ce.h,f.length)}),k!==Ce.c&&Object(u.createElement)(b.RangeControl,{label:Object(p.__)("Rounded corners","jetpack"),value:j,onChange:this.setRoundedCorners,min:0,max:Ce.i}),Object(u.createElement)(b.SelectControl,{label:Object(p.__)("Link To","jetpack"),value:g,onChange:this.setLinkTo,options:Te}))),i,Object(u.createElement)(Ae,{align:s,className:o,columns:m,imageFilter:h,images:f,layoutStyle:k,linkTo:g,onMoveBackward:this.onMoveBackward,onMoveForward:this.onMoveForward,onRemoveImage:this.onRemoveImage,onSelectImage:this.onSelectImage,onResize:this.onResize,roundedCorners:j,selectedImage:a?t:null,setImageAttributes:this.setImageAttributes},v,a&&Object(u.createElement)("div",{className:"tiled-gallery__add-item"},Object(u.createElement)(b.FormFileUpload,{multiple:!0,isLarge:!0,className:"tiled-gallery__add-item-button",onChange:this.uploadFromFiles,accept:"image/*",icon:"insert"},Object(p.__)("Upload an image","jetpack")))))}}],[{key:"getDerivedStateFromProps",value:function(e,t){return e.isSelected||null===t.selectedImage?null:{selectedImage:null}}}]),t}(u.Component),Fe=Object(b.withNotices)(Me);var Le=n(24),Re=n(17),De=n(53),ze=(n(300),n(41)),Ue=n.n(ze),Ve=n(70),He=n.n(Ve),qe=n(38);function Ge(e){var t,n=e["aria-label"],r=e.alt,a=e.height,o=e.id,c=e.link,i=e.linkTo,l=e.origUrl,s=e.url,p=e.width;if(Object(L.isBlobURL)(l))return null;switch(i){case"media":t=s;break;case"attachment":t=c}var d=Object(u.createElement)("img",{alt:r,"aria-label":n,"data-height":a,"data-id":o,"data-link":c,"data-url":l,"data-width":p,src:s});return Object(u.createElement)("figure",{className:"tiled-gallery__item"},t?Object(u.createElement)("a",{href:t},d):d)}function We(e){var t=e.children;return Object(u.createElement)("div",{className:"tiled-gallery__col"},t)}function Ke(e){var t=e.children,n=e.galleryRef;return Object(u.createElement)("div",{className:"tiled-gallery__gallery",ref:n},t)}function $e(e){var t=e.children,n=e.className;return Object(u.createElement)("div",{className:M()("tiled-gallery__row",n)},t)}var Ze=n(8),Je=n.n(Ze),Ye=[{isDefault:!0,name:"rectangular"},{name:"circle"},{name:"square"},{name:"columns"}];function Qe(e,t){var n=(t-e.reduce((function(e,t){return e+t}),0))/e.length;return e.map((function(e){return e+n}))}function Xe(e,t){!function(e,t,n){var r=Je()(t,2),a=r[0],o=r[1],c=1/a*(n-4*(e.childElementCount-1)-o);!function(e,t){var n=t.rawHeight,r=t.rowWidth,a=tt(e),o=a.map((function(e){return(n-4*(e.childElementCount-1))*rt(e)[0]})),c=Qe(o,r);a.forEach((function(e,t){var r=o[t],a=c[t];!function(e,t){var n=t.colHeight,r=t.width,a=t.rawWidth,o=Qe(nt(e).map((function(e){return a/at(e)})),n);Array.from(e.children).forEach((function(e,t){var n=o[t];e.setAttribute("style","height:".concat(n,"px;width:").concat(r,"px;"))}))}(e,{colHeight:n-4*(e.childElementCount-1),width:a,rawWidth:r})}))}(e,{rawHeight:c,rowWidth:n-4*(e.childElementCount-1)})}(e,function(e){return tt(e).map(rt).reduce((function(e,t){var n=Je()(e,2),r=n[0],a=n[1],o=Je()(t,2);return[r+o[0],a+o[1]]}),[0,0])}(e),t)}function et(e){return Array.from(e.querySelectorAll(".tiled-gallery__row"))}function tt(e){return Array.from(e.querySelectorAll(".tiled-gallery__col"))}function nt(e){return Array.from(e.querySelectorAll(".tiled-gallery__item > img, .tiled-gallery__item > a > img"))}function rt(e){var t=nt(e),n=t.length,r=1/t.map(at).reduce((function(e,t){return e+1/t}),0);return[r,r*n||1]}function at(e){var t=parseInt(e.dataset.width,10),n=parseInt(e.dataset.height,10);return t&&!Number.isNaN(t)&&n&&!Number.isNaN(n)?t/n:1}function ot(e){var t=e.height,n=e.width;return t&&n?n/t:1}var ct=Ct([2,1,2],5),it=xt([St,St,At,St,St]),lt=xt([St,St,St,At,St,St,St]),st=Ct([3,1,3],5),ut=xt([At,St,St,At]),pt=Ct([1,2,1],5),dt=xt([At,St,St,St]),mt=Ct([1,3],3),bt=xt([St,St,St,At]),ht=Ct([3,1],3),ft=xt([Pt(1.6),Object(m.overEvery)(Nt(.9),Pt(2)),Object(m.overEvery)(Nt(.9),Pt(2))]),gt=Ct([1,2],3),jt=Ct([1,1,1,1,1],1),vt=Ct([1,1,1,1],1),_t=Ct([1,1,1],3),kt=xt([Object(m.overEvery)(Nt(.9),Pt(2)),Object(m.overEvery)(Nt(.9),Pt(2)),Pt(1.6)]),yt=Ct([2,1],3),Ot=xt([function(e){return e>=2}]);function wt(e,t,n){var r=Object(m.sum)(Object(m.take)(t,3));return t.length>=3&&4!==t.length&&6!==t.length&&_t(e)&&(r<2.5||r<5&&t.length>=3&&t[0]===t[2]||n)}function Et(e,t){var n=Object(m.sum)(Object(m.take)(t,4));return vt(e)&&n<3.5&&t.length>5||n<7&&4===t.length}function Ct(e,t){return function(n){return!Object(m.some)(Object(m.takeRight)(n,t),(function(t){return Object(m.isEqual)(t,e)}))}}function xt(e){return function(t){return t.length>=e.length&&Object(m.every)(Object(m.zipWith)(e,t.slice(0,e.length),(function(e,t){return e(t)})))}}function St(e){return e>=1&&e<2}function At(e){return e<1}function Nt(e){return function(t){return t>=e}}function Pt(e){return function(t){return t1&&void 0!==arguments[1]?arguments[1]:{},n=t.isWide,r=function e(t,r){if(!r.length)return t;var a;a=r.length>15&&it(r)&&ct(t)?[2,1,2]:r.length>15&<(r)&&st(t)?[3,1,3]:5!==r.length&&ut(r)&&pt(t)?[1,2,1]:dt(r)&&mt(t)?[1,3]:bt(r)&&ht(t)?[3,1]:ft(r)&>(t)?[1,2]:n&&(5===r.length||10!==r.length&&r.length>6)&&jt(t)&&Object(m.sum)(Object(m.take)(r,5))<5?[1,1,1,1,1]:Et(t,r)?[1,1,1,1]:wt(t,r,n)?[1,1,1]:kt(r)&&yt(t)?[2,1]:Ot(r)?[1]:r.length>3?[1,1]:Array(r.length).fill(1);var o=t.concat([a]),c=Object(m.sum)(a);return e(o,r.slice(c))};return r([],e)}(c,{isWide:["full","wide"].includes(t)}),l=0;return Object(u.createElement)(Ke,{galleryRef:this.gallery},i.map((function(e,t){return Object(u.createElement)($e,{key:t},e.map((function(e,t){var n=o.slice(l,l+e);return l+=e,Object(u.createElement)(We,{key:t},n)})))})))}}]),t}(u.Component);function Bt(e){var t=e.columns,n=e.renderedImages,r=Math.min(20,t),a=n.length%r;return Object(u.createElement)(Ke,null,[].concat(f()(a?[Object(m.take)(n,a)]:[]),f()(Object(m.chunk)(Object(m.drop)(n,a),r))).map((function(e,t){return Object(u.createElement)($e,{key:t,className:"columns-".concat(e.length)},e.map((function(e,t){return Object(u.createElement)(We,{key:t},e)})))})))}var It=function(e){function t(){return j()(this,t),y()(this,w()(t).apply(this,arguments))}return S()(t,e),_()(t,[{key:"photonize",value:function(e){var t=e.height,n=e.width,r=e.url;if(r){if(Object(L.isBlobURL)(r)||/^https?:\/\/localhost/.test(r))return r;var a=r.split("?",1)[0],o=function(e){var t=Object(qe.parse)(e).host;return/\.files\.wordpress\.com$/.test(t)}(r)?Ft:He.a;if(Mt(this.props.layoutStyle)&&n&&t){var c=Math.min(2e3,n,t);return o(a,{resize:"".concat(c,",").concat(c)})}return o(a)}}},{key:"renderImage",value:function(e,t){var n=this.props,r=n.images,a=n.linkTo,o=n.selectedImage,c=Object(p.sprintf)(Object(p.__)("image %1$d of %2$d in gallery","jetpack"),t+1,r.length);return Object(u.createElement)(Ge,{alt:e.alt,"aria-label":c,height:e.height,id:e.id,origUrl:e.url,isSelected:o===t,key:t,link:e.link,linkTo:a,url:this.photonize(e),width:e.width})}},{key:"render",value:function(){var e=this.props,t=e.align,n=e.children,r=e.className,a=e.columns,o=e.images,c=e.layoutStyle,i=Mt(c)?Bt:Tt,l=this.props.images.map(this.renderImage,this);return Object(u.createElement)("div",{className:r},Object(u.createElement)(i,{align:t,columns:a,images:o,layoutStyle:c,renderedImages:l}),n)}}]),t}(u.Component);function Mt(e){return["circle","square"].includes(e)}function Ft(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n={width:"w",height:"h",letterboxing:"lb",removeLetterboxing:"ulb"},r=Object(qe.parse)(e),a=(r.auth,r.hash,r.port,r.query,r.search,Ue()(r,["auth","hash","port","query","search"]));return a.query=Object.keys(t).reduce((function(e,r){return Object.assign(e,s()({},n.hasOwnProperty(r)?n[r]:r,t[r]))}),{}),Object(qe.format)(a)}function Lt(e){var t=e.attributes,n=t.images;if(!n.length)return null;var r=t.align,a=t.className,o=t.columns,c=void 0===o?function(e){return Math.min(3,e.images.length)}(t):o,i=t.linkTo;return Object(u.createElement)(It,{align:r,className:a,columns:c,images:n,layoutStyle:Object(Ne.a)(Ye,a),linkTo:i})}var Rt={align:{default:"center",type:"string"},className:{default:"is-style-".concat("rectangular"),type:"string"},columns:{type:"number"},ids:{default:[],type:"array"},images:{type:"array",default:[],source:"query",selector:".tiled-gallery__item",query:{alt:{attribute:"alt",default:"",selector:"img",source:"attribute"},caption:{selector:"figcaption",source:"html",type:"string"},height:{attribute:"data-height",selector:"img",source:"attribute",type:"number"},id:{attribute:"data-id",selector:"img",source:"attribute"},link:{attribute:"data-link",selector:"img",source:"attribute"},url:{attribute:"data-url",selector:"img",source:"attribute"},width:{attribute:"data-width",selector:"img",source:"attribute",type:"number"}}},linkTo:{default:"none",type:"string"}},Dt={align:["center","wide","full"],customClassName:!1,html:!1},zt=[{isDefault:!0,name:"rectangular"},{name:"circle"},{name:"square"},{name:"columns"}];function Ut(e){var t,n=e.alt,r=e.imageFilter,a=e.height,o=e.id,c=e.link,i=e.linkTo,l=e.origUrl,p=e.url,d=e.width;if(Object(L.isBlobURL)(l))return null;switch(i){case"media":t=p;break;case"attachment":t=c}var m=Object(u.createElement)("img",{alt:n,"data-height":a,"data-id":o,"data-link":c,"data-url":l,"data-width":d,src:p});return Object(u.createElement)("figure",{className:M()("tiled-gallery__item",s()({},"filter__".concat(r),!!r))},t?Object(u.createElement)("a",{href:t},m):m)}function Vt(e){var t=e.children;return Object(u.createElement)("div",{className:"tiled-gallery__col"},t)}function Ht(e){var t=e.children,n=e.galleryRef;return Object(u.createElement)("div",{className:"tiled-gallery__gallery",ref:n},t)}function qt(e){var t=e.children,n=e.className;return Object(u.createElement)("div",{className:M()("tiled-gallery__row",n)},t)}function Gt(e){var t=e.height,n=e.width;return t&&n?n/t:1}var Wt=mn([2,1,2],5),Kt=bn([hn,hn,fn,hn,hn]),$t=bn([hn,hn,hn,fn,hn,hn,hn]),Zt=mn([3,1,3],5),Jt=bn([fn,hn,hn,fn]),Yt=mn([1,2,1],5),Qt=bn([fn,hn,hn,hn]),Xt=mn([1,3],3),en=bn([hn,hn,hn,fn]),tn=mn([3,1],3),nn=bn([jn(1.6),Object(m.overEvery)(gn(.9),jn(2)),Object(m.overEvery)(gn(.9),jn(2))]),rn=mn([1,2],3),an=mn([1,1,1,1,1],1),on=mn([1,1,1,1],1),cn=mn([1,1,1],3),ln=bn([Object(m.overEvery)(gn(.9),jn(2)),Object(m.overEvery)(gn(.9),jn(2)),jn(1.6)]),sn=mn([2,1],3),un=bn([function(e){return e>=2}]);function pn(e,t,n){var r=Object(m.sum)(Object(m.take)(t,3));return t.length>=3&&4!==t.length&&6!==t.length&&cn(e)&&(r<2.5||r<5&&t.length>=3&&t[0]===t[2]||n)}function dn(e,t){var n=Object(m.sum)(Object(m.take)(t,4));return on(e)&&n<3.5&&t.length>5||n<7&&4===t.length}function mn(e,t){return function(n){return!Object(m.some)(Object(m.takeRight)(n,t),(function(t){return Object(m.isEqual)(t,e)}))}}function bn(e){return function(t){return t.length>=e.length&&Object(m.every)(Object(m.zipWith)(e,t.slice(0,e.length),(function(e,t){return e(t)})))}}function hn(e){return e>=1&&e<2}function fn(e){return e<1}function gn(e){return function(t){return t>=e}}function jn(e){return function(t){return t1&&void 0!==arguments[1]?arguments[1]:{},n=t.isWide,r=function e(t,r){if(!r.length)return t;var a;a=r.length>15&&Kt(r)&&Wt(t)?[2,1,2]:r.length>15&&$t(r)&&Zt(t)?[3,1,3]:5!==r.length&&Jt(r)&&Yt(t)?[1,2,1]:Qt(r)&&Xt(t)?[1,3]:en(r)&&tn(t)?[3,1]:nn(r)&&rn(t)?[1,2]:n&&(5===r.length||10!==r.length&&r.length>6)&&an(t)&&Object(m.sum)(Object(m.take)(r,5))<5?[1,1,1,1,1]:dn(t,r)?[1,1,1,1]:pn(t,r,n)?[1,1,1]:ln(r)&&sn(t)?[2,1]:un(r)?[1]:r.length>3?[1,1]:Array(r.length).fill(1);var o=t.concat([a]),c=Object(m.sum)(a);return e(o,r.slice(c))};return r([],e)}(c,{isWide:["full","wide"].includes(t)}),l=0;return Object(u.createElement)(Ht,{galleryRef:this.gallery},i.map((function(e,t){return Object(u.createElement)(qt,{key:t},e.map((function(e,t){var n=o.slice(l,l+e);return l+=e,Object(u.createElement)(Vt,{key:t},n)})))})))}}]),t}(u.Component);function _n(e){var t=e.columns,n=e.renderedImages,r=Math.min(20,t),a=n.length%r;return Object(u.createElement)(Ht,null,[].concat(f()(a?[Object(m.take)(n,a)]:[]),f()(Object(m.chunk)(Object(m.drop)(n,a),r))).map((function(e,t){return Object(u.createElement)(qt,{key:t,className:"columns-".concat(e.length)},e.map((function(e,t){return Object(u.createElement)(Vt,{key:t},e)})))})))}function kn(e){return["circle","square"].includes(e)}function yn(){if("undefined"!=typeof jetpack_plan&&"vip"===jetpack_plan.data)return!0}function On(e){var t=Object(qe.parse)(e).host;return/\.files\.wordpress\.com$/.test(t)}function wn(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n={width:"w",height:"h",letterboxing:"lb",removeLetterboxing:"ulb"},r=Object(qe.parse)(e),a=(r.auth,r.hash,r.port,r.query,r.search,Ue()(r,["auth","hash","port","query","search"]));return a.query=Object.keys(t).reduce((function(e,r){return Object.assign(e,s()({},n.hasOwnProperty(r)?n[r]:r,t[r]))}),{}),Object(qe.format)(a)}var En=function(e){function t(){return j()(this,t),y()(this,w()(t).apply(this,arguments))}return S()(t,e),_()(t,[{key:"renderImage",value:function(e,t){var n=this.props,r=n.columns,a=n.imageFilter,o=n.images,c=n.linkTo,i=n.layoutStyle,l=n.selectedImage,s=Object(p.sprintf)(Object(p.__)("image %1$d of %2$d in gallery","jetpack"),t+1,o.length),d=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!e.height||!e.url||!e.width)return{};if(Object(L.isBlobURL)(e.url)||/^https?:\/\/localhost/.test(e.url)||/^https?:\/\/.*\.local\//.test(e.url))return{src:e.url};var n,r=e.url.split("?",1)[0],a=e.height,o=e.width,c=t.layoutStyle,i=On(r)||!0===yn()?wn:He.a;if(kn(c)&&o&&a){var l=Math.min(2e3,o,a);n=i(r,{resize:"".concat(l,",").concat(l)})}else n=i(r);var s,u=300,p=600;if(kn(c)){var d=Math.min(p,o,a),b=Math.min(2e3,o,a);s=Object(m.range)(d,b,u).map((function(e){var t=i(r,{resize:"".concat(e,",").concat(e),strip:"info"});return t?"".concat(t," ").concat(e,"w"):null})).filter(Boolean).join(",")}else{var h=Math.min(p,o),f=Math.min(2e3,o);s=Object(m.range)(h,f,u).map((function(e){var t=i(r,{strip:"info",width:e});return t?"".concat(t," ").concat(e,"w"):null})).filter(Boolean).join(",")}return Object.assign({src:n},s&&{srcSet:s})}(e,{layoutStyle:i}),b=d.src,h=d.srcSet;return Object(u.createElement)(Ut,{alt:e.alt,"aria-label":s,columns:r,height:e.height,id:e.id,imageFilter:a,isFirstItem:0===t,isLastItem:t+1===o.length,isSelected:l===t,key:t,link:e.link,linkTo:c,origUrl:e.url,showMovers:o.length>1,srcSet:h,url:b,width:e.width})}},{key:"render",value:function(){var e=this.props,t=e.align,n=e.children,r=e.className,a=e.columns,o=e.images,c=e.layoutStyle,i=e.roundedCorners,l=kn(c)?_n:vn,p=this.props.images.map(this.renderImage,this),d="circle"!==c?Math.min(i,20):0;return Object(u.createElement)("div",{className:M()(r,s()({},"has-rounded-corners-".concat(d),d>0))},Object(u.createElement)(l,{align:t,columns:a,images:o,layoutStyle:c,renderedImages:p}),n)}}]),t}(u.Component);function Cn(e){var t=e.attributes,n=t.imageFilter,r=t.images;if(!r.length)return null;var a=t.align,o=t.className,c=t.columns,i=void 0===c?function(e){return Math.min(3,e.images.length)}(t):c,l=t.linkTo,s=t.roundedCorners;return Object(u.createElement)(En,{align:a,className:o,columns:i,imageFilter:n,images:r,isSave:!0,layoutStyle:Object(Ne.a)(zt,o),linkTo:l,roundedCorners:s})}var xn,Sn={align:{default:"center",type:"string"},className:{default:"is-style-".concat("rectangular"),type:"string"},columns:{type:"number"},ids:{default:[],type:"array"},imageFilter:{type:"string"},images:{type:"array",default:[],source:"query",selector:".tiled-gallery__item",query:{alt:{attribute:"alt",default:"",selector:"img",source:"attribute"},height:{attribute:"data-height",selector:"img",source:"attribute",type:"number"},id:{attribute:"data-id",selector:"img",source:"attribute"},link:{attribute:"data-link",selector:"img",source:"attribute"},url:{attribute:"data-url",selector:"img",source:"attribute"},width:{attribute:"data-width",selector:"img",source:"attribute",type:"number"}}},linkTo:{default:"none",type:"string"},roundedCorners:{type:"integer",default:0}},An={align:["center","wide","full"],customClassName:!1,html:!1},Nn=[r,a],Pn=n(153),Tn=n.n(Pn),Bn=n(154),In=n.n(Bn),Mn=n(155),Fn=n.n(Mn),Ln=n(156),Rn=n.n(Ln),Dn=n(157),zn=n.n(Dn),Un=n(158),Vn=n.n(Un),Hn=(xn={},s()(xn,Ce.e,Object(p._x)("Tiled mosaic","Tiled gallery layout","jetpack")),s()(xn,Ce.c,Object(p._x)("Circles","Tiled gallery layout","jetpack")),s()(xn,Ce.d,Object(p._x)("Tiled columns","Tiled gallery layout","jetpack")),s()(xn,Ce.f,Object(p._x)("Square tiles","Tiled gallery layout","jetpack")),xn),qn=Ce.g.map((function(e){return i()({},e,{label:Hn[e.name]})}));function Gn(e){return Object(m.filter)(e,(function(e){var t=e.id,n=e.url;return t&&n}))}var Wn={align:{default:"center",type:"string"},className:{default:"is-style-".concat(Ce.e),type:"string"},columns:{type:"number"},columnWidths:{default:[],type:"array"},ids:{default:[],type:"array"},imageFilter:{type:"string"},images:{type:"array",default:[],source:"query",selector:".tiled-gallery__item",query:{alt:{attribute:"alt",default:"",selector:"img",source:"attribute"},height:{attribute:"data-height",selector:"img",source:"attribute",type:"number"},id:{attribute:"data-id",selector:"img",source:"attribute"},link:{attribute:"data-link",selector:"img",source:"attribute"},url:{attribute:"data-url",selector:"img",source:"attribute"},width:{attribute:"data-width",selector:"img",source:"attribute",type:"number"}}},linkTo:{default:"none",type:"string"},roundedCorners:{type:"integer",default:0}},Kn={align:"center",className:"is-style-rectangular",images:[{alt:"",link:"",url:Tn.a,width:160,height:95},{alt:"",link:"",url:In.a,width:160,height:107},{alt:"",link:"",url:Fn.a,width:304,height:203},{alt:"",link:"",url:Rn.a,width:312,height:207},{alt:"",link:"",url:zn.a,width:152,height:101},{alt:"",link:"",url:Vn.a,width:152,height:105}],linkTo:"none"},$n=Object(u.createElement)(b.SVG,{viewBox:"0 0 24 24",width:24,height:24},Object(u.createElement)(b.Path,{fill:"currentColor",d:"M19 5v2h-4V5h4M9 5v6H5V5h4m10 8v6h-4v-6h4M9 17v2H5v-2h4M21 3h-8v6h8V3zM11 3H3v10h8V3zm10 8h-8v10h8V11zm-10 4H3v6h8v-6z"})),Zn={attributes:Wn,category:Object(De.a)("media","layout"),description:Object(p.__)("Display multiple images in an elegantly organized tiled layout.","jetpack")+(Object(Le.b)()?"":" "+Object(p.__)("Serves images using Jetpack's fast global network of servers.","jetpack")),icon:{src:$n,foreground:Object(Re.a)()},keywords:[Object(p._x)("columns","block search term","jetpack"),Object(p._x)("images","block search term","jetpack"),Object(p._x)("photos","block search term","jetpack"),Object(p._x)("pictures","block search term","jetpack"),Object(p._x)("square","block search term","jetpack"),Object(p._x)("circle","block search term","jetpack"),Object(p._x)("mosaic","block search term","jetpack")],styles:qn,supports:{align:["center","wide","full"],customClassName:!1,html:!1},title:Object(p.__)("Tiled Gallery","jetpack"),transforms:{from:[{type:"block",isMultiBlock:!0,blocks:["core/image"],isMatch:function(e){return Gn(e).length>0},transform:function(e){var t=Gn(e);return Object(d.createBlock)("jetpack/".concat("tiled-gallery"),{images:t.map((function(e){return{id:e.id,url:e.url,alt:e.alt}})),ids:t.map((function(e){return e.id}))})}},{type:"block",blocks:["core/gallery","jetpack/slideshow"],transform:function(e){var t=Gn(e.images);return t.length>0?Object(d.createBlock)("jetpack/".concat("tiled-gallery"),{images:t.map((function(e){return{id:e.id,url:e.url,alt:e.alt}})),ids:t.map((function(e){return e.id}))}):Object(d.createBlock)("jetpack/".concat("tiled-gallery"))}}],to:[{type:"block",blocks:["core/gallery"],transform:function(e){var t=e.images,n=e.ids,r=e.columns,a=e.linkTo;return Object(d.createBlock)("core/gallery",{images:t,ids:n,columns:r,imageCrop:!0,linkTo:a})}},{type:"block",blocks:["core/image"],transform:function(e){var t=e.align,n=e.images;return n.length>0?n.map((function(e){var n=e.id,r=e.url,a=e.alt;return Object(d.createBlock)("core/image",{align:t,id:n,url:r,alt:a})})):Object(d.createBlock)("core/image")}}]},edit:Fe,save:function(e){var t=e.attributes,n=t.imageFilter,r=t.images;if(!r.length)return null;var a=t.align,o=t.className,c=t.columns,i=void 0===c?Be(t):c,l=t.linkTo,s=t.roundedCorners,p=t.columnWidths;return Object(u.createElement)(Ae,{align:a,className:o,columns:i,imageFilter:n,images:r,isSave:!0,layoutStyle:Object(Ne.a)(Ce.g,o),linkTo:l,roundedCorners:s,columnWidths:p})},deprecated:Nn,example:{attributes:Kn}};Object(o.a)("tiled-gallery",Zn)},function(e,t,n){"use strict";n.r(t);var r=n(20),a=n(9),o=n.n(a),c=n(0),i=n(1),l=n(16),s=n(2),u=n(5),p=(n(212),n(8)),d=n.n(p),m=n(4),b=n(7),h=n.n(b),f=n(58),g=n.n(f),j=n(18),v=n(10),_=n(25),k=n(47),y=n(24),O=n(26),w=n(17),E=Object(m.compact)([{name:"contact-form",title:Object(i.__)("Contact Form","jetpack"),description:Object(i.__)("Add a contact form to your page.","jetpack"),icon:Object(O.a)(Object(c.createElement)(s.Path,{fill:Object(w.a)(),d:"M21.99 8c0-.72-.37-1.35-.94-1.7l-8.04-4.71c-.62-.37-1.4-.37-2.02 0L2.95 6.3C2.38 6.65 2 7.28 2 8v10c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2l-.01-10zm-11.05 4.34l-7.2-4.5 7.25-4.25c.62-.37 1.4-.37 2.02 0l7.25 4.25-7.2 4.5c-.65.4-1.47.4-2.12 0z"}),48,48,"-4 -4 32 32"),innerBlocks:[["jetpack/field-name",{required:!0}],["jetpack/field-email",{required:!0}],["jetpack/field-textarea",{}],["jetpack/button",{text:Object(i.__)("Contact Us","jetpack"),element:"button"}]]},!Object(y.b)()&&{name:"newsletter-form",title:Object(i.__)("Newsletter Sign-up","jetpack"),description:Object(i.__)("A simple way to collect information from folks visiting your site.","jetpack"),icon:Object(O.a)(Object(c.createElement)(s.Path,{fill:Object(w.a)(),d:"M37.9999 7.59998C49.3999 7.59998 68.3999 26.6 68.3999 26.6V68.4H7.59985V26.6C7.59985 26.6 26.5999 7.59998 37.9999 7.59998ZM64.5999 63.536L50.4259 52.44L64.5999 41.8L62.9659 40.394L54.3779 45.334L55.2899 28.956L21.9639 26.98L20.2159 44.232L12.6539 40.622L11.3999 41.8L25.5739 52.44L12.5019 63.27L14.0219 64.904L37.9999 49.4L62.8139 65.17L64.5999 63.536Z"}),48,48,"-6 -6 92 92"),innerBlocks:[["jetpack/field-name",{required:!0}],["jetpack/field-email",{required:!0}],["jetpack/field-consent",{}],["jetpack/button",{text:Object(i.__)("Subscribe","jetpack"),element:"button"}]]},{name:"rsvp-form",title:Object(i.__)("RSVP Form","jetpack"),description:Object(i.__)("Add an RSVP form to your page","jetpack"),icon:Object(O.a)(Object(c.createElement)(s.Path,{fill:Object(w.a)(),d:"M10 9V7.41c0-.89-1.08-1.34-1.71-.71L3.7 11.29c-.39.39-.39 1.02 0 1.41l4.59 4.59c.63.63 1.71.19 1.71-.7V14.9c5 0 8.5 1.6 11 5.1-1-5-4-10-11-11z"}),48,48,"-4 -3 32 32"),innerBlocks:[["jetpack/field-name",{required:!0}],["jetpack/field-email",{required:!0}],["jetpack/field-radio",{label:Object(i.__)("Attending?","jetpack"),required:!0,options:[Object(i.__)("Yes","jetpack"),Object(i.__)("No","jetpack")]}],["jetpack/field-textarea",{label:Object(i.__)("Other Details","jetpack")}],["jetpack/button",{text:Object(i.__)("Send RSVP","jetpack"),element:"button"}]],attributes:{subject:Object(i.__)("A new RSVP from your website","jetpack")}},{name:"registration-form",title:Object(i.__)("Registration Form","jetpack"),description:Object(i.__)("Add a Registration form to your page","jetpack"),icon:Object(O.a)(Object(c.createElement)(s.Path,{fill:Object(w.a)(),d:"M11.34 15.02c.39.39 1.02.39 1.41 0l6.36-6.36c.39-.39.39-1.02 0-1.41L14.16 2.3c-.38-.4-1.01-.4-1.4-.01L6.39 8.66c-.39.39-.39 1.02 0 1.41l4.95 4.95zm2.12-10.61L17 7.95l-4.95 4.95-3.54-3.54 4.95-4.95zm6.95 11l-2.12-2.12c-.18-.18-.44-.29-.7-.29h-.27l-2 2h1.91L19 17H5l1.78-2h2.05l-2-2h-.42c-.27 0-.52.11-.71.29l-2.12 2.12c-.37.38-.58.89-.58 1.42V20c0 1.1.9 2 2 2h14c1.1 0 2-.89 2-2v-3.17c0-.53-.21-1.04-.59-1.42z"}),48,48,"-4 -3 32 32"),innerBlocks:[["jetpack/field-name",{required:!0}],["jetpack/field-email",{required:!0}],["jetpack/field-telephone",{label:Object(i.__)("Phone Number","jetpack")}],["jetpack/field-select",{label:Object(i.__)("How did you hear about us?","jetpack"),options:[Object(i.__)("Search Engine","jetpack"),Object(i.__)("Social Media","jetpack"),Object(i.__)("TV","jetpack"),Object(i.__)("Radio","jetpack"),Object(i.__)("Friend or Family","jetpack")]}],["jetpack/field-textarea",{label:Object(i.__)("Other Details","jetpack")}],["jetpack/button",{text:Object(i.__)("Send","jetpack"),element:"button"}]],attributes:{subject:Object(i.__)("A new registration from your website","jetpack")}},{name:"appointment-form",title:Object(i.__)("Appointment Form","jetpack"),description:Object(i.__)("Add an Appointment booking form to your page","jetpack"),icon:Object(O.a)(Object(c.createElement)(s.Path,{fill:Object(w.a)(),d:"M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V8c0-.55-.45-1-1-1s-1 .45-1 1v2H2c-.55 0-1 .45-1 1s.45 1 1 1h2v2c0 .55.45 1 1 1s1-.45 1-1v-2h2c.55 0 1-.45 1-1s-.45-1-1-1H6zm9 4c-2.67 0-8 1.34-8 4v1c0 .55.45 1 1 1h14c.55 0 1-.45 1-1v-1c0-2.66-5.33-4-8-4z"}),48,48,"-4 -3 32 32"),innerBlocks:[["jetpack/field-name",{required:!0}],["jetpack/field-email",{required:!0}],["jetpack/field-telephone",{required:!0}],["jetpack/field-date",{label:Object(i.__)("Date","jetpack"),required:!0}],["jetpack/field-radio",{label:Object(i.__)("Time","jetpack"),required:!0,options:[Object(i.__)("Morning","jetpack"),Object(i.__)("Afternoon","jetpack")]}],["jetpack/field-textarea",{label:Object(i.__)("Notes","jetpack")}],["jetpack/button",{text:Object(i.__)("Book Appointment","jetpack"),element:"button"}]],attributes:{subject:Object(i.__)("A new appointment booked from your website","jetpack")}},{name:"feedback-form",title:Object(i.__)("Feedback Form","jetpack"),description:Object(i.__)("Add a Feedback form to your page","jetpack"),icon:Object(O.a)(Object(c.createElement)(s.Path,{fill:Object(w.a)(),d:"M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.03 0 3.8-1.11 4.75-2.75.19-.33-.05-.75-.44-.75H7.69c-.38 0-.63.42-.44.75.95 1.64 2.72 2.75 4.75 2.75z"}),48,48,"-4 -3 32 32"),innerBlocks:[["jetpack/field-name",{required:!0}],["jetpack/field-email",{required:!0}],["jetpack/field-radio",{label:Object(i.__)("Please rate our website","jetpack"),required:!0,options:[Object(i.__)("1 - Very Bad","jetpack"),Object(i.__)("2 - Poor","jetpack"),Object(i.__)("3 - Average","jetpack"),Object(i.__)("4 - Good","jetpack"),Object(i.__)("5 - Excellent","jetpack")]}],["jetpack/field-textarea",{label:Object(i.__)("How could we improve?","jetpack")}],["jetpack/button",{text:Object(i.__)("Send Feedback","jetpack"),element:"button"}]],attributes:{subject:Object(i.__)("New feedback received from your website","jetpack")}}]),C=n(22),x=n.n(C),S=n(77),A=n.n(S),N=function(e){var t=e.error;return Object(c.createElement)(s.Notice,{isDismissible:!1,status:"error"},Object(c.createInterpolateElement)(Object(i.__)('The CRM Jetpack Form extension failed to activate. The error message was " ".',"jetpack"),{error:Object(c.createElement)("span",null,t)}))},P=function(e){var t=e.isActivatingExt,n=e.setIsActivatingExt,r=e.extActivationError,a=function(e,t,n,r){return function(){t(void 0),e(!0),x()({path:"/jetpack/v4/jetpack_crm",method:"POST",data:{extension:"jetpackforms"}}).then((function(e){if("success"!==e.code)throw new Error(e.code);var t=Object.assign({},n);t.jp_form_ext_enabled=!0,r(t)})).catch((function(e){t(e.message)})).finally((function(){e(!1)}))}}(n,e.setExtActivationError,e.crmData,e.setCRMData);return t?Object(c.createElement)(s.Spinner,null):r?Object(c.createElement)(N,{error:r}):Object(c.createElement)(s.Button,{isSecondary:!0,onClick:a},Object(i.__)("Enable Jetpack Forms Extension","jetpack"))},T=function(){return Object(c.createElement)("p",{className:"jetpack-contact-form__crm_text"},Object(i.__)("A site administrator must enable the CRM Jetpack Forms extension.","jetpack"))},B=function(){return Object(c.createElement)("p",{className:"jetpack-contact-form__crm_text"},Object(i.__)("You can integrate this contact form with Jetpack CRM by enabling Jetpack CRM's Jetpack Forms extension.","jetpack"))},I=function(e){var t=e.isActivatingExt,n=e.setIsActivatingExt,r=e.extActivationError,a=e.setExtActivationError,o=e.crmData,i=e.setCRMData;return o.can_activate_extension?Object(c.createElement)("div",null,Object(c.createElement)(B,null),Object(c.createElement)("br",null),Object(c.createElement)(P,{isActivatingExt:t,setIsActivatingExt:n,extActivationError:r,setExtActivationError:a,crmData:o,setCRMData:i})):Object(c.createElement)(T,null)},M=Object.freeze({ACTIVE:1,INSTALLED:2,NOT_INSTALLED:3}),F=function(){return Object(c.createElement)("p",{className:"jetpack-contact-form__crm_text"},Object(i.__)("The Jetpack CRM is installed but has an invalid version.","jetpack"))},L=function(){return Object(c.createElement)("p",{className:"jetpack-contact-form__crm_text"},Object(i.__)("The Zero BS CRM plugin is now Jetpack CRM. Update to the latest version to integrate your contact form with your CRM.","jetpack"))},R=function(){return Object(c.createElement)("p",{className:"jetpack-contact-form__crm_text"},Object(c.createInterpolateElement)(Object(i.__)("You can save contacts from Jetpack contact forms in Jetpack CRM. Learn more at jetpackcrm.com ","jetpack"),{a:Object(c.createElement)(s.ExternalLink,{href:"https://jetpackcrm.com"})}))},D=function(){return Object(c.createElement)("p",{className:"jetpack-contact-form__crm_text"},Object(i.__)("You already have the Jetpack CRM plugin installed, but it's not activated. Activate the Jetpack CRM plugin to save contacts from this contact form in your Jetpack CRM.","jetpack"))},z=function(e){var t=e.crmData,n=e.setCRMData,r=e.jetpackCRM,a=e.setAttributes,o=Object(c.useState)(!1),l=d()(o,2),u=l[0],p=l[1],m=Object(c.useState)(!1),b=d()(m,2),h=b[0],f=b[1];return t.jp_form_ext_enabled?A.a.satisfies(A.a.coerce(t.crm_version),"3.0.19 - 4.0.0")?Object(c.createElement)("p",{className:"jetpack-contact-form__crm_text"},Object(i.__)("Contacts from this form will be stored in Jetpack CRM.","jetpack")):Object(c.createElement)(s.ToggleControl,{className:"jetpack-contact-form__crm_toggle",label:Object(i.__)("Jetpack CRM","jetpack"),checked:r,onChange:function(e){return a({jetpackCRM:e})},help:Object(i.__)("Store contact form submissions in your CRM.","jetpack")}):Object(c.createElement)(I,{isActivatingExt:u,setIsActivatingExt:p,extActivationError:h,setExtActivationError:f,crmData:t,setCRMData:n})},U=function(e){var t=e.crmData,n=e.setCRMData,r=e.jetpackCRM,a=e.setAttributes,o=A.a.coerce(t.crm_version);if(t.crm_installed&&!o)return Object(c.createElement)(F,null);if(t.crm_installed&&A.a.lt(o,"3.0.19"))return Object(c.createElement)(L,null);var i=M.NOT_INSTALLED;return t.crm_active?i=M.ACTIVE:t.crm_installed&&(i=M.INSTALLED),Object(c.createElement)("div",{"aria-live":"polite"},M.ACTIVE===i&&Object(c.createElement)(z,{crmData:t,setCRMData:n,jetpackCRM:r,setAttributes:a}),M.INSTALLED===i&&Object(c.createElement)(D,null),M.NOT_INSTALLED===i&&Object(c.createElement)(R,null))},V=function(e){var t=e.isFetchingCRMData,n=e.hasCRMDataError,r=e.crmData,a=e.setCRMData,o=e.jetpackCRM,i=e.setAttributes;return t?Object(c.createElement)(s.Spinner,null):n?null:Object(c.createElement)(U,{crmData:r,setCRMData:a,jetpackCRM:o,setAttributes:i})},H=function(e){var t=e.jetpackCRM,n=e.setAttributes,r=Object(c.useState)(!0),a=d()(r,2),o=a[0],l=a[1],u=Object(c.useState)(!1),p=d()(u,2),m=p[0],b=p[1],h=Object(c.useState)(),f=d()(h,2),g=f[0],j=f[1];return Object(c.useEffect)((function(){x()({path:"/jetpack/v4/jetpack_crm"}).then((function(e){if(e.error)throw e.message;b(!1),j(e)})).catch((function(){return b(!0)})).finally((function(){return l(!1)}))}),[]),Object(c.createElement)(s.PanelBody,{title:Object(i.__)("CRM Integration","jetpack"),initialOpen:!1},Object(c.createElement)(s.BaseControl,null,Object(c.createElement)(V,{isFetchingCRMData:o,hasCRMDataError:m,crmData:g,setCRMData:j,jetpackCRM:t,setAttributes:n})))},q=n(28),G=n.n(q),W=function(){var e,t,n=(e=Object(v.useSelect)((function(e){return e("core/block-editor").getSelectedBlock()}),[]),t=Object(v.useDispatch)("core/block-editor").insertBlock,{insertConsentBlock:Object(c.useCallback)(G()(regeneratorRuntime.mark((function n(){var r,a,o,c;return regeneratorRuntime.wrap((function(n){for(;;)switch(n.prev=n.next){case 0:return-1===(a=(null!==(r=e.innerBlocks)&&void 0!==r?r:[]).findIndex((function(e){return"jetpack/button"===e.name})))&&(a=(null!==(o=e.innerBlocks)&&void 0!==o?o:[]).length),n.next=4,Object(l.createBlock)("jetpack/field-consent");case 4:return c=n.sent,n.next=7,t(c,a,e.clientId,!1);case 7:case"end":return n.stop()}}),n)}))),[t,e.clientId,e.innerBlocks])}).insertConsentBlock;return Object(c.createElement)(c.Fragment,null,Object(c.createElement)("p",null,Object(i.__)("You’re already collecting email contacts. Why not make sure you have permission to email them too?","jetpack")),Object(c.createElement)(s.Button,{isSecondary:!0,onClick:n,style:{marginBottom:"1em"}},Object(i.__)("Add email permission request","jetpack")),Object(c.createElement)("br",null))},K=function(){var e=Object(v.useSelect)((function(e){return e("core/block-editor").getSelectedBlock()}),[]);return Object(c.useMemo)((function(){return t=e.innerBlocks,n=t.some((function(e){return"jetpack/field-email"===e.name})),r=t.some((function(e){return"jetpack/field-consent"===e.name})),!!n&&!r;var t,n,r}),[e.innerBlocks])?Object(c.createElement)(W,null):null};function $(){return($=G()(regeneratorRuntime.mark((function e(){var t;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!Object(y.b)()){e.next=2;break}return e.abrupt("return",Promise.reject());case 2:return e.prev=2,e.next=5,x()({path:"/jetpack/v4/plugins"});case 5:return t=e.sent,e.abrupt("return",t);case 9:return e.prev=9,e.t0=e.catch(2),e.abrupt("return",Promise.reject(e.t0.message));case 12:case"end":return e.stop()}}),e,null,[[2,9]])})))).apply(this,arguments)}function Z(e){return J.apply(this,arguments)}function J(){return(J=G()(regeneratorRuntime.mark((function e(t){var n;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!Object(y.b)()){e.next=2;break}return e.abrupt("return",Promise.reject());case 2:return e.prev=2,e.next=5,x()({path:"/jetpack/v4/plugins",method:"POST",data:{slug:t,status:"active",source:"block-editor"}});case 5:return n=e.sent,e.abrupt("return",n);case 9:return e.prev=9,e.t0=e.catch(2),e.abrupt("return",Promise.reject(e.t0.message));case 12:case"end":return e.stop()}}),e,null,[[2,9]])})))).apply(this,arguments)}function Y(e){return Q.apply(this,arguments)}function Q(){return(Q=G()(regeneratorRuntime.mark((function e(t){var n;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!Object(y.b)()){e.next=2;break}return e.abrupt("return",Promise.reject());case 2:return e.prev=2,e.next=5,x()({path:"/jetpack/v4/plugins/".concat(t),method:"POST",data:{status:"active",source:"block-editor"}});case 5:return n=e.sent,e.abrupt("return",n);case 9:return e.prev=9,e.t0=e.catch(2),e.abrupt("return",Promise.reject(e.t0.message));case 12:case"end":return e.stop()}}),e,null,[[2,9]])})))).apply(this,arguments)}var X=function(e){var t=e.error;return Object(c.createElement)(s.Notice,{isDismissible:!1,status:"error"},Object(c.createInterpolateElement)(Object(i.__)("The plugin failed to install. Please check the plugin information for detailed requirements.","jetpack"),{a:Object(c.createElement)(s.ExternalLink,{href:"https://wordpress.org/plugins/creative-mail-by-constant-contact"}),b:Object(c.createElement)("span",null,t)}))},ee=n(51),te=Object.freeze({ACTIVE:1,INSTALLED:2,NOT_INSTALLED:3}),ne=function(e){var t=e.isActivating?Object(i.__)("Activating…","jetpack"):Object(i.__)("Installing…","jetpack");return Object(c.createElement)(s.Button,{isSecondary:!0,icon:Object(c.createElement)(s.Icon,{style:{animation:"rotation 2s infinite linear"},icon:"update"}),disabled:!0,"aria-label":t},t)},re=function(e){var t=e.installAndActivateCreativeMailPlugin,n=e.isInstalling;return Object(c.createElement)("p",null,Object(c.createElement)("em",{style:{color:"rgba(38, 46, 57, 0.7)"}},Object(i.__)("To start sending email campaigns, install the Creative Mail plugin for WordPress.","jetpack"),Object(c.createElement)("br",null),n&&Object(c.createElement)(ne,null),!n&&Object(c.createElement)(s.Button,{isSecondary:!0,onClick:t},Object(i.__)("Install Creative Mail plugin","jetpack"))))},ae=function(e){var t=e.activateCreativeMailPlugin,n=e.isInstalling;return Object(c.createElement)("p",null,Object(c.createElement)("em",null,Object(i.__)("To start sending email campaigns, activate the Creative Mail plugin for WordPress.","jetpack")),Object(c.createElement)("br",null),n&&Object(c.createElement)(ne,{isActivating:!0}),!n&&Object(c.createElement)(s.Button,{isSecondary:!0,onClick:t},Object(i.__)("Activate Creative Mail Plugin","jetpack")))},oe=function(){return Object(c.createElement)("p",null,Object(c.createElement)("em",null,Object(i.__)("You’re all setup for email marketing with Creative Mail.","jetpack"),Object(c.createElement)("br",null),Object(c.createElement)(s.ExternalLink,{href:(e=Object(m.get)(Object(ee.a)(),"adminUrl",!1),"".concat(e,"admin.php?page=creativemail"))},Object(i.__)("Open Creative Mail settings","jetpack"))));var e},ce=function(e){var t=e.pluginState,n=e.onCreativeMailPluginClick,r=e.isInstalling;return Object(c.createElement)("div",{"aria-live":"polite"},te.ACTIVE===t&&Object(c.createElement)(oe,null),te.INSTALLED===t&&Object(c.createElement)(ae,{activateCreativeMailPlugin:function(){return n(Y,"creative-mail-by-constant-contact/creative-mail-plugin")},isInstalling:r}),te.NOT_INSTALLED===t&&Object(c.createElement)(re,{installAndActivateCreativeMailPlugin:function(){return n(Z,"creative-mail-by-constant-contact")},isInstalling:r}))},ie="".concat("creative-mail-by-constant-contact/creative-mail-plugin",".php"),le=function(e){var t=e.pluginState,n=e.setPluginState,r=Object(c.useState)(),a=d()(r,2),o=a[0],i=a[1],l=Object(c.useState)(!1),s=d()(l,2),u=s[0],p=function(e,t,n){return Object(c.useCallback)((function(r,a){e(void 0),t(!0),r(a).then((function(){n(te.ACTIVE)})).catch((function(t){e(t)})).finally((function(){return t(!1)}))}),[t,e,n])}(i,s[1],n);return o?Object(c.createElement)(X,{error:o}):Object(c.createElement)(ce,{pluginState:t,onCreativeMailPluginClick:p,isInstalling:u})},se=function(e){var t=e.isFetchingPlugins,n=e.hasError,r=e.pluginState,a=e.setPluginState;return t?Object(c.createElement)(s.Spinner,null):n?null:Object(c.createElement)(le,{pluginState:r,setPluginState:a})},ue=function(){var e=Object(c.useState)(!0),t=d()(e,2),n=t[0],r=t[1],a=Object(c.useState)(!1),o=d()(a,2),i=o[0],l=o[1],s=Object(c.useState)(te.NOT_INSTALLED),u=d()(s,2),p=u[0],b=u[1];return Object(c.useEffect)((function(){(function(){return $.apply(this,arguments)})().then((function(e){l(!1),Object(m.get)(e,ie)&&(Object(m.get)(e,[ie,"active"])?b(te.ACTIVE):b(te.INSTALLED))})).catch((function(){return l(!0)})).finally((function(){return r(!1)}))}),[b,r,l]),Object(c.createElement)(se,{isFetchingPlugins:n,hasError:i,pluginState:p,setPluginState:b})},pe=function(){return Object(c.createElement)(s.PanelBody,{title:Object(i.__)("Newsletter Integration","jetpack"),initialOpen:!1},Object(c.createElement)(s.BaseControl,null,Object(c.createElement)(K,null),Object(c.createElement)(ue,null)))},de=["jetpack/markdown","core/paragraph","core/image","core/heading","core/gallery","core/list","core/quote","core/shortcode","core/audio","core/code","core/cover","core/file","core/html","core/separator","core/spacer","core/subhead","core/table","core/verse","core/video"];var me=Object(j.compose)([Object(v.withSelect)((function(e,t){var n=e("core/blocks"),r=n.getBlockType,a=n.getBlockVariations,o=n.getDefaultBlockVariation,c=e("core/block-editor").getBlocks,i=e("core/editor").getEditedPostAttribute,l=e("core"),s=l.getSite,u=l.getUser,p=c(t.clientId),d=i("author"),b=d&&u(d)&&u(d).email,h=i("title");return{blockType:r&&r(t.name),defaultVariation:o&&o(t.name,"block"),variations:a&&a(t.name,"block"),innerBlocks:p,hasInnerBlocks:p.length>0,siteTitle:Object(m.get)(s&&s(),["title"]),postTitle:h,postAuthorEmail:b}})),Object(v.withDispatch)((function(e){var t=e("core/block-editor");return{replaceInnerBlocks:t.replaceInnerBlocks,selectBlock:t.selectBlock}})),j.withInstanceId])((function(e){var t=e.attributes,n=e.setAttributes,r=e.siteTitle,a=e.postTitle,o=e.postAuthorEmail,p=e.hasInnerBlocks,b=e.replaceInnerBlocks,f=e.selectBlock,j=e.clientId,v=e.instanceId,O=e.className,w=e.blockType,C=e.variations,x=e.defaultVariation,S=t.to,A=t.subject,N=t.customThankyou,P=t.customThankyouMessage,T=t.customThankyouRedirect,B=t.jetpackCRM,I=Object(c.useState)(!1),M=d()(I,2),F=M[0],L=M[1],R=h()(O,"jetpack-contact-form"),D=function(e){e.attributes&&n(e.attributes),e.innerBlocks&&b(j,function e(t){return Object(m.map)(t,(function(t){var n=d()(t,3),r=n[0],a=n[1],o=n[2],c=void 0===o?[]:o;return Object(l.createBlock)(r,a,e(c))}))}(e.innerBlocks)),f(j)};Object(c.useEffect)((function(){p||l.registerBlockVariation||D(E[0])}));var z=function(e){return 0!==(e=e.trim()).length&&(!g.a.validate(e)&&{email:e})},U=function(e){if(0===e.target.value.length)return L(!1),void n({to:o});var t=e.target.value.split(",").map(z).filter(Boolean);t&&t.length&&L(t)},V=function(e){L(!1),n({to:e.trim()})},q=function(){var e=void 0!==S?S:"";void 0===S&&o&&n({to:e=o});var t=void 0!==A?A:"";return void 0===A&&void 0!==r&&void 0!==a&&n({subject:t="["+r+"] "+a}),Object(c.createElement)(c.Fragment,null,Object(c.createElement)(s.TextControl,{"aria-describedby":"contact-form-".concat(v,"-email-").concat(F&&F.length>0?"error":"help"),label:Object(i.__)("Email address to send to","jetpack"),placeholder:Object(i.__)("name@example.com","jetpack"),onKeyDown:function(e){"Enter"===event.key&&(e.preventDefault(),e.stopPropagation())},value:e,onBlur:U,onChange:V,help:Object(i.__)("You can enter multiple email addresses separated by commas.","jetpack")}),Object(c.createElement)(k.a,{isError:!0,id:"contact-form-".concat(v,"-email-error")},function(){if(F){if(1===F.length)return F[0]&&F[0].email?Object(i.sprintf)(Object(i.__)("%s is not a valid email address.","jetpack"),F[0].email):F[0];if(2===F.length)return Object(i.sprintf)(Object(i.__)("%s and %s are not a valid email address.","jetpack"),F[0].email,F[1].email);var e=F.map((function(e){return e.email}));return Object(i.sprintf)(Object(i.__)("%s are not a valid email address.","jetpack"),e.join(", "))}return null}()),Object(c.createElement)(s.TextControl,{label:Object(i.__)("Email subject line","jetpack"),value:t,placeholder:Object(i.__)("Enter a subject","jetpack"),onChange:function(e){return n({subject:e})},help:Object(i.__)("Choose a subject line that you recognize as an email from your website.","jetpack")}),Object(c.createElement)(s.SelectControl,{label:Object(i.__)("On Submission","jetpack"),value:N,options:[{label:Object(i.__)("Show a summary of submitted fields","jetpack"),value:""},{label:Object(i.__)("Show a custom text message","jetpack"),value:"message"},{label:Object(i.__)("Redirect to another webpage","jetpack"),value:"redirect"}],onChange:function(e){return n({customThankyou:e})}}),"message"===N&&Object(c.createElement)(s.TextareaControl,{label:Object(i.__)("Message Text","jetpack"),value:P,placeholder:Object(i.__)("Thank you for your submission!","jetpack"),onChange:function(e){return n({customThankyouMessage:e})}}),"redirect"===N&&Object(c.createElement)(s.BaseControl,{label:Object(i.__)("Redirect Address","jetpack"),id:"contact-form-".concat(v,"-thankyou-url")},Object(c.createElement)(u.URLInput,{id:"contact-form-".concat(v,"-thankyou-url"),value:T,className:"jetpack-contact-form__thankyou-redirect-url",onChange:function(e){return n({customThankyouRedirect:e})}})))};return!p&&l.registerBlockVariation?Object(c.createElement)("div",{className:R},Object(c.createElement)(u.__experimentalBlockVariationPicker,{icon:Object(m.get)(w,["icon","src"]),label:Object(m.get)(w,["title"]),instructions:Object(i.__)("Please select which type of form you'd like to add, or create your own using the skip option.","jetpack"),variations:C,allowSkip:!0,onSelect:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:x;D(e)}})):Object(c.createElement)(c.Fragment,null,s.ToolbarGroup&&Object(c.createElement)(u.BlockControls,null,Object(c.createElement)(s.ToolbarGroup,null,Object(c.createElement)(s.Dropdown,{position:"bottom right",className:"jetpack-contact-form-settings-selector",contentClassName:"jetpack-contact-form__popover",renderToggle:function(e){return function(e,t){return Object(c.createElement)(s.Button,{className:"components-toolbar__control jetpack-contact-form__toggle",label:Object(i.__)("Edit Form Settings","jetpack"),onClick:t,onKeyDown:function(n){e||n.keyCode!==_.DOWN||(n.preventDefault(),n.stopPropagation(),t())},icon:Object(c.createElement)(s.Icon,{icon:"edit"})})}(e.isOpen,e.onToggle)},renderContent:function(){return q()}}))),Object(c.createElement)(u.InspectorControls,null,Object(c.createElement)(s.PanelBody,{title:Object(i.__)("Form Settings","jetpack")},q()),!Object(y.b)()&&Object(c.createElement)(c.Fragment,null,Object(c.createElement)(H,{jetpackCRM:B,setAttributes:n}),Object(c.createElement)(pe,null))),Object(c.createElement)("div",{className:R},Object(c.createElement)(u.InnerBlocks,{allowedBlocks:de,templateInsertUpdatesSelection:!1})))})),be={subject:{type:"string"},to:{type:"string"},customThankyou:{type:"string",default:""},customThankyouMessage:{type:"string",default:""},customThankyouRedirect:{type:"string",default:""},jetpackCRM:{type:"boolean",default:!0}},he=["submit_button_text","has_form_settings_set","submitButtonText","backgroundButtonColor","textButtonColor","customBackgroundButtonColor","customTextButtonColor","submitButtonClasses","hasFormSettingsSet"],fe=[{attributes:o()({submit_button_text:{type:"string",default:Object(i.__)("Submit","jetpack")},has_form_settings_set:{type:"string",default:null},submitButtonText:{type:"string",default:Object(i.__)("Submit","jetpack")},backgroundButtonColor:{type:"string"},textButtonColor:{type:"string"},customBackgroundButtonColor:{type:"string"},customTextButtonColor:{type:"string"},submitButtonClasses:{type:"string"}},be),migrate:function(e,t){var n=Object(m.omit)(e,he),r={text:e.submitButtonText||e.submit_button_text||Object(i.__)("Submit","jetpack"),backgroundColor:e.backgroundButtonColor,textColor:e.textButtonColor,customBackgroundColor:e.customBackgroundButtonColor,customTextColor:e.customTextButtonColor};return[n,t.concat(Object(l.createBlock)("jetpack/button",o()({element:"button"},r)))]},isEligible:function(e){return!(!e.has_form_settings_set&&!e.hasFormSettingsSet)},save:u.InnerBlocks.Content}],ge=n(23),je=n.n(ge),ve=n(31),_e=n(45);function ke(e){var t=e.setAttributes,n=e.width;return Object(c.createElement)(s.BaseControl,{label:Object(i.__)("Field Width","jetpack"),help:Object(i.__)("Adjust the width of the field to include multiple fields on a single line.","jetpack"),className:"jetpack-field-label__width"},Object(c.createElement)(s.ButtonGroup,{"aria-label":Object(i.__)("Field Width","jetpack")},[25,50,75,100].map((function(e){return Object(c.createElement)(s.Button,{key:e,isSmall:!0,isPrimary:e===n,onClick:function(){return t({width:e})}},e,"%")}))))}function ye(e){var t=e.setAttributes,n=e.id;return Object(c.createElement)(s.TextControl,{label:Object(i.__)("Unique CSS ID","jetpack"),value:n,onChange:function(e){return t({id:e})},help:Object(i.__)("A unique ID that can be used in CSS or as an anchor.","jetpack")})}var Oe=function(e){var t=e.setAttributes,n=e.width,r=e.id,a=e.required;return Object(c.createElement)(c.Fragment,null,Object(c.createElement)(u.BlockControls,null,Object(c.createElement)(s.Toolbar,null,Object(c.createElement)(s.ToolbarButton,{title:Object(i.__)("Required","jetpack"),icon:Object(O.a)(Object(c.createElement)(s.Path,{d:"M8.23118 8L16 16M8 16L15.7688 8 M6.5054 11.893L17.6567 11.9415M12.0585 17.6563L12 6.5",stroke:"currentColor"})),onClick:function(){t({required:!a})},className:a?"is-pressed":void 0}))),Object(c.createElement)(u.InspectorControls,null,Object(c.createElement)(s.PanelBody,{title:Object(i.__)("Field Settings","jetpack")},Object(c.createElement)(s.ToggleControl,{label:Object(i.__)("Field is required","jetpack"),className:"jetpack-field-label__required",checked:a,onChange:function(e){return t({required:e})},help:Object(i.__)("Does this field have to be completed for the form to be submitted?","jetpack")}),Object(c.createElement)(ke,{setAttributes:t,width:n}))),Object(c.createElement)(u.InspectorAdvancedControls,null,Object(c.createElement)(ye,{setAttributes:t,id:r})))};function we(e){var t=e.id,n=e.type,r=e.required,a=e.label,o=e.setAttributes,l=e.placeholder,u=e.width;return Object(c.createElement)(c.Fragment,null,Object(c.createElement)("div",{className:"jetpack-field"},Object(c.createElement)(_e.a,{required:r,label:a,setAttributes:o}),Object(c.createElement)(s.Disabled,null,Object(c.createElement)(s.TextControl,{type:n,placeholder:l,value:l,onChange:function(e){return o({placeholder:e})},title:Object(i.__)("Set the placeholder text","jetpack")}))),Object(c.createElement)(Oe,{id:t,required:r,width:u,setAttributes:o}))}var Ee=Object(j.createHigherOrderComponent)((function(e){return function(t){if(t.name.indexOf("jetpack/field")>-1){var n=t.attributes.width?"jetpack-field__width-"+t.attributes.width:"";return Object(c.createElement)(e,je()({},t,{className:n}))}return Object(c.createElement)(e,t)}}),"withCustomClassName");function Ce(e){var t=e.id,n=e.required,r=e.label,a=e.setAttributes,o=e.placeholder,l=e.width;return Object(c.createElement)(c.Fragment,null,Object(c.createElement)("div",{className:"jetpack-field"},Object(c.createElement)(_e.a,{required:n,label:r,setAttributes:a}),Object(c.createElement)(s.Disabled,null,Object(c.createElement)(s.TextareaControl,{placeholder:o,value:o,onChange:function(e){return a({placeholder:e})},title:Object(i.__)("Set the placeholder text","jetpack")}))),Object(c.createElement)(Oe,{id:t,required:n,setAttributes:a,width:l}))}Object(ve.addFilter)("editor.BlockListBlock","jetpack/contact-form",Ee);var xe=Object(j.withInstanceId)((function(e){var t=e.id,n=e.instanceId,r=e.required,a=e.label,o=e.setAttributes,l=e.width,p=e.defaultValue;return Object(c.createElement)(s.BaseControl,{id:"jetpack-field-checkbox-".concat(n),className:"jetpack-field jetpack-field-checkbox",label:Object(c.createElement)(c.Fragment,null,Object(c.createElement)("input",{className:"jetpack-field-checkbox__checkbox",type:"checkbox",disabled:!0,checked:p}),Object(c.createElement)(_e.a,{required:r,label:a,setAttributes:o}),Object(c.createElement)(Oe,{id:t,required:r,width:l,setAttributes:o}),Object(c.createElement)(u.InspectorControls,null,Object(c.createElement)(s.PanelBody,{title:Object(i.__)("Checkbox Settings","jetpack")},Object(c.createElement)(s.ToggleControl,{label:Object(i.__)("Checked by default","jetpack"),checked:p,onChange:function(e){return o({defaultValue:e?"true":""})}}))))})})),Se=n(11),Ae=n.n(Se),Ne=n(15),Pe=n.n(Ne),Te=n(12),Be=n.n(Te),Ie=n(13),Me=n.n(Ie),Fe=n(6),Le=n.n(Fe),Re=n(14),De=n.n(Re),ze=function(e){function t(){var e,n;Ae()(this,t);for(var r=arguments.length,a=new Array(r),o=0;o0&&void 0!==arguments[0]?arguments[0]:null,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,n=m.slice(0);null===t?(n.splice(e,1),e>0&&g(e-1)):(n.splice(e,1,t),g(e)),l({options:n})},v=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,t=m.slice(0),n=0;"object"==typeof e?(t.push(""),n=t.length-1):(t.splice(e+1,0,""),n=e+1),g(n),l({options:t})};return Object(c.createElement)(c.Fragment,null,Object(c.createElement)(s.BaseControl,{id:"jetpack-field-multiple-".concat(r),className:"jetpack-field jetpack-field-multiple",label:Object(c.createElement)(_e.a,{required:a,label:o,setAttributes:l,isSelected:u,resetFocus:function(){return g(null)}})},Object(c.createElement)("ol",{className:"jetpack-field-multiple__list",id:"jetpack-field-multiple-".concat(r)},m.map((function(e,t){return Object(c.createElement)(ze,{type:n,key:t,option:e,index:t,onChangeOption:j,onAddOption:v,isInFocus:t===f&&u,isSelected:u})}))),u&&Object(c.createElement)(s.Button,{className:"jetpack-field-multiple__add-option",icon:"insert",label:Object(i.__)("Insert option","jetpack"),onClick:v},Object(i.__)("Add option","jetpack"))),Object(c.createElement)(Oe,{id:t,required:a,setAttributes:l,width:p}))})),Ve=Object(j.withInstanceId)((function(e){var t,n=e.id,r=e.instanceId,a=e.width,o=e.consentType,l=e.implicitConsentMessage,p=e.explicitConsentMessage,d=e.setAttributes;return Object(c.createElement)(s.BaseControl,{id:"jetpack-field-consent-".concat(r),className:"jetpack-field jetpack-field-consent",label:Object(c.createElement)(c.Fragment,null,"explicit"===o&&Object(c.createElement)("input",{className:"jetpack-field-consent__checkbox",type:"checkbox",disabled:!0}),Object(c.createElement)(_e.a,{required:!1,label:null!==(t={implicit:l,explicit:p}[o])&&void 0!==t?t:"",setAttributes:d,labelFieldName:"".concat(o,"ConsentMessage"),placeholder:Object(i.sprintf)(Object(i.__)("Add %s consent message…","jetpack"),o)}),Object(c.createElement)(u.InspectorControls,null,Object(c.createElement)(s.PanelBody,{title:Object(i.__)("Field Settings","jetpack")},Object(c.createElement)(ke,{setAttributes:d,width:a}))),Object(c.createElement)(u.InspectorAdvancedControls,null,Object(c.createElement)(ye,{setAttributes:d,id:n})),Object(c.createElement)(u.InspectorControls,null,Object(c.createElement)(s.PanelBody,{title:Object(i.__)("Consent Settings","jetpack")},Object(c.createElement)(s.BaseControl,null,Object(c.createElement)(s.SelectControl,{label:Object(i.__)("Permission to email","jetpack"),value:o,options:[{label:Object(i.__)("Mention that you can email","jetpack"),value:"implicit"},{label:Object(i.__)("Add a privacy checkbox","jetpack"),value:"explicit"}],onChange:function(e){return d({consentType:e})}})))))})})),He=Object(O.a)(Object(c.createElement)(s.Path,{d:"M13 7.5h5v2h-5zm0 7h5v2h-5zM19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14zM11 6H6v5h5V6zm-1 4H7V7h3v3zm1 3H6v5h5v-5zm-1 4H7v-3h3v3z"})),qe={title:Object(i.__)("Form","jetpack"),description:Object(i.__)("A simple way to get feedback from folks visiting your site.","jetpack"),icon:{src:He,foreground:Object(w.a)()},keywords:[Object(i._x)("email","block search term","jetpack"),Object(i._x)("feedback","block search term","jetpack"),Object(i._x)("contact form","block search term","jetpack")],supports:{html:!1},attributes:be,edit:me,save:u.InnerBlocks.Content,variations:E,category:"grow",deprecated:fe},Ge={category:"grow",parent:["jetpack/contact-form"],supports:{reusable:!1,html:!1},attributes:{label:{type:"string",default:null},required:{type:"boolean",default:!1},options:{type:"array",default:[]},defaultValue:{type:"string",default:""},placeholder:{type:"string",default:""},id:{type:"string",default:""},width:{type:"number",default:100}},transforms:{to:[{type:"block",blocks:["jetpack/field-text"],isMatch:function(e){return!e.options.length},transform:function(e){return Object(l.createBlock)("jetpack/field-text",e)}},{type:"block",blocks:["jetpack/field-name"],isMatch:function(e){return!e.options.length},transform:function(e){return Object(l.createBlock)("jetpack/field-name",e)}},{type:"block",blocks:["jetpack/field-email"],isMatch:function(e){return!e.options.length},transform:function(e){return Object(l.createBlock)("jetpack/field-email",e)}},{type:"block",blocks:["jetpack/field-url"],isMatch:function(e){return!e.options.length},transform:function(e){return Object(l.createBlock)("jetpack/field-url",e)}},{type:"block",blocks:["jetpack/field-date"],isMatch:function(e){return!e.options.length},transform:function(e){return Object(l.createBlock)("jetpack/field-date",e)}},{type:"block",blocks:["jetpack/field-telephone"],isMatch:function(e){return!e.options.length},transform:function(e){return Object(l.createBlock)("jetpack/field-telephone",e)}},{type:"block",blocks:["jetpack/field-textarea"],isMatch:function(e){return!e.options.length},transform:function(e){return Object(l.createBlock)("jetpack/field-textarea",e)}},{type:"block",blocks:["jetpack/field-checkbox-multiple"],isMatch:function(e){return 1<=e.options.length},transform:function(e){return Object(l.createBlock)("jetpack/field-checkbox-multiple",e)}},{type:"block",blocks:["jetpack/field-radio"],isMatch:function(e){return 1<=e.options.length},transform:function(e){return Object(l.createBlock)("jetpack/field-radio",e)}},{type:"block",blocks:["jetpack/field-select"],isMatch:function(e){return 1<=e.options.length},transform:function(e){return Object(l.createBlock)("jetpack/field-select",e)}},{type:"block",blocks:["jetpack/field-consent"],isMatch:function(e){return 1<=e.options.length},transform:function(e){return Object(l.createBlock)("jetpack/field-consent",e)}}]},save:function(){return null},example:{}},We=function(e){var t=e.attributes,n=e.name;return null===t.label?Object(l.getBlockType)(n).title:t.label},Ke=function(e){return function(t){return Object(c.createElement)(we,{type:e,label:We(t),required:t.attributes.required,setAttributes:t.setAttributes,isSelected:t.isSelected,defaultValue:t.attributes.defaultValue,placeholder:t.attributes.placeholder,id:t.attributes.id,width:t.attributes.width})}},$e=function(e){return function(t){return Object(c.createElement)(Ue,{label:We(t),required:t.attributes.required,options:t.attributes.options,setAttributes:t.setAttributes,type:e,isSelected:t.isSelected,id:t.attributes.id,width:t.attributes.width})}},Ze=[{name:"field-text",settings:o()({},Ge,{title:Object(i.__)("Text","jetpack"),description:Object(i.__)("When you need just a small amount of text, add a text input.","jetpack"),icon:Object(O.a)(Object(c.createElement)(s.Path,{fill:Object(w.a)(),d:"M4 9h16v2H4V9zm0 4h10v2H4v-2z"})),edit:Ke("text")})},{name:"field-name",settings:o()({},Ge,{title:Object(i.__)("Name","jetpack"),description:Object(i.__)("Introductions are important. Add an input for folks to add their name.","jetpack"),icon:Object(O.a)(Object(c.createElement)(s.Path,{fill:Object(w.a)(),d:"M12 6c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2m0 10c2.7 0 5.8 1.29 6 2H6c.23-.72 3.31-2 6-2m0-12C9.79 4 8 5.79 8 8s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 10c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"})),edit:Ke("text")})},{name:"field-email",settings:o()({},Ge,{title:Object(i.__)("Email","jetpack"),keywords:[Object(i.__)("e-mail","jetpack"),Object(i.__)("mail","jetpack"),"email"],description:Object(i.__)("Want to reply to folks? Add an email address input.","jetpack"),icon:Object(O.a)(Object(c.createElement)(s.Path,{fill:Object(w.a)(),d:"M22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6zm-2 0l-8 5-8-5h16zm0 12H4V8l8 5 8-5v10z"})),edit:Ke("email")})},{name:"field-url",settings:o()({},Ge,{title:Object(i.__)("Website","jetpack"),keywords:["url",Object(i.__)("internet page","jetpack"),"link"],description:Object(i.__)("Add an address input for a website.","jetpack"),icon:Object(O.a)(Object(c.createElement)(s.Path,{fill:Object(w.a)(),d:"M20 18c1.1 0 1.99-.9 1.99-2L22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2H0v2h24v-2h-4zM4 6h16v10H4V6z"})),edit:Ke("url")})},{name:"field-date",settings:o()({},Ge,{title:Object(i.__)("Date Picker","jetpack"),keywords:[Object(i.__)("Calendar","jetpack"),Object(i.__)("day month year","block search term","jetpack")],description:Object(i.__)("The best way to set a date. Add a date picker.","jetpack"),icon:Object(O.a)(Object(c.createElement)(s.Path,{fill:Object(w.a)(),d:"M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V9h14v10zm0-12H5V5h14v2zM7 11h5v5H7z"})),edit:Ke("text")})},{name:"field-telephone",settings:o()({},Ge,{title:Object(i.__)("Phone Number","jetpack"),keywords:[Object(i.__)("Phone","jetpack"),Object(i.__)("Cellular phone","jetpack"),Object(i.__)("Mobile","jetpack")],description:Object(i.__)("Add a phone number input.","jetpack"),icon:Object(O.a)(Object(c.createElement)(s.Path,{fill:Object(w.a)(),d:"M6.54 5c.06.89.21 1.76.45 2.59l-1.2 1.2c-.41-1.2-.67-2.47-.76-3.79h1.51m9.86 12.02c.85.24 1.72.39 2.6.45v1.49c-1.32-.09-2.59-.35-3.8-.75l1.2-1.19M7.5 3H4c-.55 0-1 .45-1 1 0 9.39 7.61 17 17 17 .55 0 1-.45 1-1v-3.49c0-.55-.45-1-1-1-1.24 0-2.45-.2-3.57-.57-.1-.04-.21-.05-.31-.05-.26 0-.51.1-.71.29l-2.2 2.2c-2.83-1.45-5.15-3.76-6.59-6.59l2.2-2.2c.28-.28.36-.67.25-1.02C8.7 6.45 8.5 5.25 8.5 4c0-.55-.45-1-1-1z"})),edit:Ke("tel")})},{name:"field-textarea",settings:o()({},Ge,{title:Object(i.__)("Message","jetpack"),keywords:[Object(i.__)("Textarea","jetpack"),"textarea",Object(i.__)("Multiline text","jetpack")],description:Object(i.__)("Let folks speak their mind. This text box is great for longer responses.","jetpack"),icon:Object(O.a)(Object(c.createElement)(s.Path,{fill:Object(w.a)(),d:"M21 11.01L3 11v2h18zM3 16h12v2H3zM21 6H3v2.01L21 8z"})),edit:function(e){return Object(c.createElement)(Ce,{label:We(e),required:e.attributes.required,setAttributes:e.setAttributes,isSelected:e.isSelected,defaultValue:e.attributes.defaultValue,placeholder:e.attributes.placeholder,id:e.attributes.id,width:e.attributes.width})}})},{name:"field-checkbox",settings:o()({},Ge,{title:Object(i.__)("Checkbox","jetpack"),keywords:[Object(i.__)("Confirm","jetpack"),Object(i.__)("Accept","jetpack")],description:Object(i.__)("Add a single checkbox.","jetpack"),icon:Object(O.a)(Object(c.createElement)(s.Path,{fill:Object(w.a)(),d:"M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V5h14v14zM17.99 9l-1.41-1.42-6.59 6.59-2.58-2.57-1.42 1.41 4 3.99z"})),edit:function(e){return Object(c.createElement)(xe,{label:e.attributes.label,required:e.attributes.required,setAttributes:e.setAttributes,isSelected:e.isSelected,defaultValue:e.attributes.defaultValue,id:e.attributes.id,width:e.attributes.width})},attributes:o()({},Ge.attributes,{label:{type:"string",default:""}})})},{name:"field-consent",settings:o()({},Ge,{title:Object(i.__)("Consent","jetpack"),keywords:[Object(i.__)("Consent","jetpack")],description:Object(i.__)("Ask for consent","jetpack"),icon:Object(O.a)(Object(c.createElement)(s.Path,{fill:Object(w.a)(),d:"m81 370h142v40h-142zm0-39h142v-40h-142zm0-79h245v-40h-245zm378 260h-40c0-40.253906-32.746094-73-73-73s-73 32.746094-73 73h-40c0-42.085938 23.128906-78.867188 57.34375-98.3125-11.40625-13.023438-18.34375-30.054688-18.34375-48.6875 0-40.804688 33.195312-74 74-74s74 33.195312 74 74c0 18.632812-6.9375 35.664062-18.34375 48.6875 34.214844 19.445312 57.34375 56.226562 57.34375 98.3125zm-113-113c18.746094 0 34-15.253906 34-34s-15.253906-34-34-34-34 15.253906-34 34 15.253906 34 34 34zm-286 73h138.316406c-3.460937 12.757812-5.316406 26.164062-5.316406 40h-133c-33.085938 0-60-26.914062-60-60v-392c0-33.085938 26.914062-60 60-60h203.757812l142.132813 142.855469v125.210937c-12.042969-7.476562-25.453125-12.765625-39.890625-15.324218v-81.632813h-71.109375c-33.085937 0-60-26.914063-60-60v-71.109375h-174.890625c-11.027344 0-20 8.972656-20 20v392c0 11.027344 8.972656 20 20 20zm234.890625-340.890625h42.972656l-62.972656-63.234375v43.234375c0 11.03125 8.96875 20 20 20zm0 0"}),24,25,"-26 0 512 512"),attributes:o()({},Ge.attributes,{label:{type:"string",default:Object(i.__)("Consent","jetpack")},consentType:{type:"string",default:"implicit"},implicitConsentMessage:{type:"string",default:Object(i.__)("By submitting your information, you're giving us permission to email you. You may unsubscribe at any time.","jetpack")},explicitConsentMessage:{type:"string",default:Object(i.__)("Can we send you an email from time to time?","jetpack")}}),edit:function(e){var t=e.attributes,n=e.isSelected,r=e.setAttributes,a=t.id,o=t.width,i=t.consentType,l=t.implicitConsentMessage,s=t.explicitConsentMessage;return Object(c.createElement)(Ve,{id:a,isSelected:n,width:o,consentType:i,implicitConsentMessage:l,explicitConsentMessage:s,setAttributes:r})}})},{name:"field-checkbox-multiple",settings:o()({},Ge,{title:Object(i.__)("Checkbox Group","jetpack"),keywords:[Object(i.__)("Choose Multiple","jetpack"),Object(i.__)("Option","jetpack")],description:Object(i.__)("People love options. Add several checkbox items.","jetpack"),icon:Object(O.a)(Object(c.createElement)(s.Path,{fill:Object(w.a)(),d:"M18 7l-1.41-1.41-6.34 6.34 1.41 1.41L18 7zm4.24-1.41L11.66 16.17 7.48 12l-1.41 1.41L11.66 19l12-12-1.42-1.41zM.41 13.41L6 19l1.41-1.41L1.83 12 .41 13.41z"})),edit:$e("checkbox"),attributes:o()({},Ge.attributes,{label:{type:"string",default:"Choose several"}})})},{name:"field-radio",settings:o()({},Ge,{title:Object(i.__)("Radio","jetpack"),keywords:[Object(i.__)("Choose","jetpack"),Object(i.__)("Select","jetpack"),Object(i.__)("Option","jetpack")],description:Object(i.__)("Inspired by radios, only one radio item can be selected at a time. Add several radio button items.","jetpack"),icon:Object(O.a)(Object(c.createElement)(c.Fragment,null,Object(c.createElement)(s.Path,{fill:Object(w.a)(),d:"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"}),Object(c.createElement)(s.Circle,{cx:"12",cy:"12",r:"5"}))),edit:$e("radio"),attributes:o()({},Ge.attributes,{label:{type:"string",default:"Choose one"}})})},{name:"field-select",settings:o()({},Ge,{title:Object(i.__)("Select","jetpack"),keywords:[Object(i.__)("Choose","jetpack"),Object(i.__)("Dropdown","jetpack"),Object(i.__)("Option","jetpack")],description:Object(i.__)("Compact, but powerful. Add a select box with several items.","jetpack"),icon:Object(O.a)(Object(c.createElement)(s.Path,{fill:Object(w.a)(),d:"M3 17h18v2H3zm16-5v1H5v-1h14m2-2H3v5h18v-5zM3 6h18v2H3z"})),edit:$e("select"),attributes:o()({},Ge.attributes,{label:{type:"string",default:"Select one"}})})}];Object(r.a)("contact-form",qe,Ze)},function(e,t,n){"use strict";n.r(t);var r=n(20),a=n(0),o=n(1),c=n(2),i={subscribePlaceholder:{type:"string",default:Object(o.__)("Enter your email address","jetpack")},showSubscribersTotal:{type:"boolean",default:!1},buttonOnNewLine:{type:"boolean",default:!1},submitButtonText:{type:"string",default:Object(o.__)("Sign Up","jetpack")},emailFieldBackgroundColor:{type:"string"},customEmailFieldBackgroundColor:{type:"string"},emailFieldGradient:{type:"string"},customEmailFieldGradient:{type:"string"},buttonBackgroundColor:{type:"string"},customButtonBackgroundColor:{type:"string"},buttonGradient:{type:"string"},customButtonGradient:{type:"string"},textColor:{type:"string"},customTextColor:{type:"string"},fontSize:{type:"number"},customFontSize:{type:"number"},borderRadius:{type:"number"},borderWeight:{type:"number"},borderColor:{type:"string"},customBorderColor:{type:"string"},padding:{type:"number"},spacing:{type:"number"}},l=n(4),s={attributes:{subscribeButton:{type:"string",default:Object(o.__)("Subscribe","jetpack")},showSubscribersTotal:{type:"boolean",default:!1}},migrate:function(e){return{subscribeButton:"",submitButtonText:e.subscribeButton,showSubscribersTotal:e.showSubscribersTotal,customBackgroundButtonColor:"",customTextButtonColor:"",submitButtonClasses:""}},isEligible:function(e){return!!Object(l.isEmpty)(e.subscribeButton)},save:function(e){var t=e.attributes;return Object(a.createElement)(a.RawHTML,null,'[jetpack_subscription_form show_subscribers_total="'.concat(t.showSubscribersTotal,'" show_only_email_and_button="true"]'))}};var u={attributes:{subscribePlaceholder:{type:"string",default:Object(o.__)("Email Address","jetpack")},subscribeButton:{type:"string",default:Object(o.__)("Subscribe","jetpack")},showSubscribersTotal:{type:"boolean",default:!1},submitButtonText:{type:"string",default:Object(o.__)("Subscribe","jetpack")},backgroundButtonColor:{type:"string"},textButtonColor:{type:"string"},customBackgroundButtonColor:{type:"string"},customTextButtonColor:{type:"string"},submitButtonClasses:{type:"string"}},migrate:function(e){return{subscribePlaceholder:e.subscribePlaceholder,showSubscribersTotal:e.showSubscribersTotal,buttonOnNewLine:!0,submitButtonText:e.submitButtonText,buttonBackgroundColor:e.backgroundButtonColor?e.backgroundButtonColor:"primary",customButtonBackgroundColor:e.customBackgroundButtonColor,textColor:e.textButtonColor?e.textButtonColor:"background",customTextColor:e.customTextButtonColor}},save:function(e){var t=e.attributes,n=t.showSubscribersTotal,r=t.submitButtonClasses,o=t.customBackgroundButtonColor,c=t.customTextButtonColor,i=t.submitButtonText;return Object(a.createElement)(a.RawHTML,null,'[jetpack_subscription_form show_only_email_and_button="true" custom_background_button_color="'.concat(o,'" custom_text_button_color="').concat(c,'" submit_button_text="').concat(i,'" submit_button_classes="').concat(r,'" show_subscribers_total="').concat(n,'" ]'))}},p={subscribePlaceholder:{type:"string",default:Object(o.__)("Enter your email address","jetpack")},showSubscribersTotal:{type:"boolean",default:!1},buttonOnNewLine:{type:"boolean",default:!1},submitButtonText:{type:"string",default:Object(o.__)("Sign Up","jetpack")},emailFieldBackgroundColor:{type:"string"},customEmailFieldBackgroundColor:{type:"string"},emailFieldGradient:{type:"string"},customEmailFieldGradient:{type:"string"},buttonBackgroundColor:{type:"string"},customButtonBackgroundColor:{type:"string"},buttonGradient:{type:"string"},customButtonGradient:{type:"string"},textColor:{type:"string"},customTextColor:{type:"string"},fontSize:{type:"number"},customFontSize:{type:"number"},borderRadius:{type:"number"},borderWeight:{type:"number"},borderColor:{type:"string"},customBorderColor:{type:"string"},padding:{type:"number"},spacing:{type:"number"}},d=n(5),m=n(7),b=n.n(m);function h(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,r=t.subscribePlaceholder,o=t.showSubscribersTotal,c=t.buttonOnNewLine,i=t.submitButtonText,l=t.emailFieldBackgroundColor,s=t.customEmailFieldBackgroundColor,u=t.emailFieldGradient,m=t.customEmailFieldGradient,h=t.buttonBackgroundColor,f=t.customButtonBackgroundColor,g=t.buttonGradient,j=t.customButtonGradient,v=t.textColor,_=t.customTextColor,k=t.fontSize,y=t.customFontSize,O=t.borderRadius,w=t.borderWeight,E=t.borderColor,C=t.customBorderColor,x=t.padding,S=t.spacing,A=!!d.__experimentalGetGradientClass,N=Object(d.getColorClassName)("color",v),P=Object(d.getFontSizeClass)(k),T=Object(d.getColorClassName)("border-color",E),B=Object(d.getColorClassName)("background-color",h),I=A?Object(d.__experimentalGetGradientClass)(g):void 0,M=Object(d.getColorClassName)("background-color",l),F=A?Object(d.__experimentalGetGradientClass)(u):void 0,L=b()(0===O?"no-border-radius":void 0,P,T),R=b()(L,v?"has-text-color":void 0,N,h||g?"has-background":void 0,B,I),D=b()(L,M,F),z=!M&&m?m:s,U=!B&&j?j:f,V=function(){return b()(e,"wp-block-jetpack-subscriptions__supports-newline",c?"wp-block-jetpack-subscriptions__use-newline":void 0,o?"wp-block-jetpack-subscriptions__show-subs":void 0)},H=r,q=i;return"check-text-defaults"===n&&(H=r===p.subscribePlaceholder.default?"Enter your email address":r,q=i===p.submitButtonText.default?"Sign Up":i),Object(a.createElement)("div",{className:V()},Object(a.createElement)(a.RawHTML,null,'\n\t\t\t[jetpack_subscription_form\n\t\t\t\tsubscribe_placeholder="'.concat(H,'"\n\t\t\t\tshow_subscribers_total="').concat(o,'"\n\t\t\t\tbutton_on_newline="').concat(c,'"\n\t\t\t\tsubmit_button_text="').concat(q,'"\n\t\t\t\tcustom_background_emailfield_color="').concat(z,'"\n\t\t\t\tcustom_background_button_color="').concat(U,'"\n\t\t\t\tcustom_text_button_color="').concat(_,'"\n\t\t\t\tcustom_font_size="').concat(y||16,'"\n\t\t\t\tcustom_border_radius="').concat(O||0,'"\n\t\t\t\tcustom_border_weight="').concat(w||1,'"\n\t\t\t\tcustom_border_color="').concat(C,'"\n\t\t\t\tcustom_padding="').concat(x||15,'"\n\t\t\t\tcustom_spacing="').concat(S||10,'"\n\t\t\t\tsubmit_button_classes="').concat(R,'"\n\t\t\t\temail_field_classes="').concat(D,'"\n\t\t\t\tshow_only_email_and_button="true"\n\t\t\t]')))}var f=[s,u,{attributes:p,save:function(e){return h(e.className,e.attributes)}},{attributes:p,save:function(e){return h(e.className,e.attributes,"check-text-defaults")}}],g=n(9),j=n.n(g),v=n(3),_=n.n(v),k=n(8),y=n.n(k),O=n(22),w=n.n(O),E=n(18),C=(n(185),n(43)),x=window.getComputedStyle,S=!!d.__experimentalUseGradient,A=Object(c.withFallbackStyles)((function(e,t){var n=t.buttonBackgroundColor,r=t.textColor,a=n&&n.color,o=r&&r.color,c=e.querySelector('[contenteditable="true"]');return{fallbackButtonBackgroundColor:a||!e?void 0:x(c).backgroundColor,fallbackTextColor:o||!e?void 0:x(c).color}}));var N=Object(E.compose)([Object(d.withColors)({emailFieldBackgroundColor:"backgroundColor"},{buttonBackgroundColor:"backgroundColor"},{textColor:"color"},"borderColor"),Object(d.withFontSizes)("fontSize"),A])((function(e){var t,n,r,s=e.className,u=e.attributes,p=e.setAttributes,m=e.emailFieldBackgroundColor,h=e.buttonBackgroundColor,f=e.setButtonBackgroundColor,g=e.fallbackButtonBackgroundColor,v=e.textColor,k=e.fallbackTextColor,O=e.setTextColor,E=e.borderColor,x=e.fontSize,A=Object(C.a)(i,u);Object(l.isEqual)(A,u)||p(A);var N,P,T=A.borderRadius,B=A.borderWeight,I=A.padding,M=A.spacing,F=A.submitButtonText,L=A.subscribePlaceholder,R=A.showSubscribersTotal,D=A.buttonOnNewLine,z=Object(a.useState)(""),U=y()(z,2),V=U[0],H=U[1],q=Object(a.useState)(""),G=y()(q,2),W=G[0],K=G[1],$=S?Object(d.__experimentalUseGradient)({gradientAttribute:"emailFieldGradient",customGradientAttribute:"customEmailFieldGradient"}):{},Z=S?Object(d.__experimentalUseGradient)({gradientAttribute:"buttonGradient",customGradientAttribute:"customButtonGradient"}):{},J=(t={"no-border-radius":0===T},_()(t,x.class,x.class),_()(t,"has-text-color",v.color),_()(t,v.class,v.class),t),Y=j()({},J,(n={"has-background":m.color||$.gradientValue},_()(n,m.class,!$.gradientValue&&m.class),_()(n,$.gradientClass,$.gradientClass),n)),Q=j()({},J,(r={"has-background":h.color||Z.gradientValue},_()(r,h.class,!Z.gradientValue&&h.class),_()(r,Z.gradientClass,Z.gradientClass),r)),X=function(e){return e||10},ee={color:v.color,borderColor:E.color,borderRadius:T?T+"px":"0px",borderWidth:B?B+"px":"1px",fontSize:x.size?x.size+"px":"16px",padding:(N=I,P=N||15,P+"px "+Math.round(1.5*P)+"px "+P+"px "+Math.round(1.5*P)+"px")},te=j()({},ee,{},!m.color&&$.gradientValue?{background:$.gradientValue}:{backgroundColor:m.color}),ne=j()({},ee,{},!h.color&&Z.gradientValue?{background:Z.gradientValue}:{backgroundColor:h.color},{},D?{marginTop:X(M)+"px"}:{marginLeft:X(M)+"px"});return Object(a.useEffect)((function(){w()({path:"/wpcom/v2/subscribers/count"}).then((function(e){e.hasOwnProperty("count")?(H(Object(o.sprintf)(Object(o._n)("Join %s other subscriber","Join %s other subscribers",e.count,"jetpack"),e.count)),K(e.count)):(H(Object(o.__)("Subscriber count unavailable","jetpack")),K(0))}))}),[]),Object(a.createElement)(a.Fragment,null,Object(a.createElement)(d.InspectorControls,null,S&&Object(a.createElement)(d.__experimentalPanelColorGradientSettings,{title:Object(o.__)("Color Settings","jetpack"),className:"wp-block-jetpack-subscriptions__backgroundpanel",settings:[{colorValue:h.color,onColorChange:f,gradientValue:Z.gradientValue,onGradientChange:Z.setGradient,label:Object(o.__)("Button Background Color","jetpack")},{colorValue:v.color,onColorChange:O,label:Object(o.__)("Button Text Color","jetpack")},{colorValue:E.color,onColorChange:function(e){p({borderColor:e,customBorderColor:e})},label:Object(o.__)("Border Color","jetpack")}],initialOpen:!0},Object(a.createElement)(d.ContrastChecker,{fontSize:x.size,textColor:v.color,backgroundColor:m.color,fallbackButtonBackgroundColor:g,fallbackTextColor:k})),!S&&Object(a.createElement)(d.PanelColorSettings,{title:Object(o.__)("Background Colors","jetpack"),className:"wp-block-jetpack-subscriptions__backgroundpanel",colorSettings:[{value:h.color,onChange:f,label:Object(o.__)("Button Background Color","jetpack")},{value:v.color,onChange:O,label:Object(o.__)("Button Text Color","jetpack")},{value:E.color,onColorChange:function(e){p({borderColor:e,customBorderColor:e})},label:Object(o.__)("Border Color","jetpack")}],initialOpen:!1},Object(a.createElement)(d.ContrastChecker,{fontSize:x.size,textColor:v.color,backgroundColor:m.color,fallbackButtonBackgroundColor:g,fallbackTextColor:k})),Object(a.createElement)(c.PanelBody,{title:Object(o.__)("Text Settings"),initialOpen:!1,className:"wp-block-jetpack-subscriptions__textpanel"},Object(a.createElement)(d.FontSizePicker,{withSlider:!0,value:x.size,onChange:function(e){var t=e||16;p({fontSize:t,customFontSize:t})}})),Object(a.createElement)(c.PanelBody,{title:Object(o.__)("Border Settings","jetpack"),initialOpen:!1,className:"wp-block-jetpack-subscriptions__borderpanel"},Object(a.createElement)(c.RangeControl,{value:T,label:Object(o.__)("Border Radius","jetpack"),min:0,max:50,initialPosition:0,allowReset:!0,onChange:function(e){return p({borderRadius:e})}}),Object(a.createElement)(c.RangeControl,{value:B,label:Object(o.__)("Border Weight","jetpack"),min:0,max:15,initialPosition:1,allowReset:!0,onChange:function(e){return p({borderWeight:e})}})),Object(a.createElement)(c.PanelBody,{title:Object(o.__)("Spacing Settings","jetpack"),initialOpen:!1,className:"wp-block-jetpack-subscriptions__spacingpanel"},Object(a.createElement)(c.RangeControl,{value:I,label:Object(o.__)("Space Inside","jetpack"),min:5,max:50,initialPosition:15,allowReset:!0,onChange:function(e){return p({padding:e})}}),Object(a.createElement)(c.RangeControl,{value:M,label:Object(o.__)("Space Between","jetpack"),min:0,max:50,initialPosition:10,allowReset:!0,onChange:function(e){return p({spacing:e})}})),Object(a.createElement)(c.PanelBody,{title:Object(o.__)("Display Settings"),initialOpen:!1,className:"wp-block-jetpack-subscriptions__displaypanel"},Object(a.createElement)(c.ToggleControl,{label:Object(o.__)("Show subscriber count","jetpack"),checked:R,onChange:function(){p({showSubscribersTotal:!R})},help:function(){if(!W||W<1)return Object(o.__)("This will remain hidden on your website until you have at least one subscriber.","jetpack")}}),Object(a.createElement)(c.ToggleControl,{label:Object(o.__)("Place button on new line","jetpack"),checked:D,onChange:function(){p({buttonOnNewLine:!D})}}))),Object(a.createElement)("div",{className:b()(s,"wp-block-jetpack-subscriptions__supports-newline",D?"wp-block-jetpack-subscriptions__use-newline":void 0,R?"wp-block-jetpack-subscriptions__show-subs":void 0)},Object(a.createElement)("div",{className:"wp-block-jetpack-subscriptions__form",role:"form"},Object(a.createElement)(c.TextControl,{placeholder:L,disabled:!0,className:b()(Y,"wp-block-jetpack-subscriptions__textfield"),style:te}),Object(a.createElement)(d.RichText,{className:b()(Q,"wp-block-jetpack-subscriptions__button","wp-block-button__link"),onChange:function(e){return p({submitButtonText:e})},style:ne,value:F,withoutInteractiveFormatting:!0,allowedFormats:["core/bold","core/italic","core/strikethrough"]})),R&&Object(a.createElement)("p",{className:"wp-block-jetpack-subscriptions__subscount"},V)))}));var P=n(17),T=Object(a.createElement)(c.SVG,{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg"},Object(a.createElement)(c.Rect,{x:"4.75",y:"6.75",width:"14.5",height:"10.5",rx:"1.25",stroke:Object(P.a)(),strokeWidth:"1.5",fill:"none"}),Object(a.createElement)(c.Path,{d:"M19 7L13.3609 12.2363C12.5935 12.9489 11.4065 12.9489 10.6391 12.2363L5 7",stroke:Object(P.a)(),strokeWidth:"1.5",strokeLinejoin:"bevel",fill:"none"})),B={title:Object(o.__)("Subscription Form","jetpack"),description:Object(a.createElement)("p",null,Object(o.__)("A form enabling readers to get notifications when new posts are published from this site.","jetpack")),icon:{src:T,foreground:Object(P.a)()},category:"grow",keywords:[Object(o._x)("subscribe","block search term","jetpack"),Object(o._x)("join","block search term","jetpack"),Object(o._x)("follow","block search term","jetpack")],attributes:i,edit:N,save:function(e){var t=e.className,n=e.attributes,r=n.subscribePlaceholder,o=n.showSubscribersTotal,c=n.buttonOnNewLine,s=n.submitButtonText,u=n.emailFieldBackgroundColor,p=n.customEmailFieldBackgroundColor,m=n.emailFieldGradient,h=n.customEmailFieldGradient,f=n.buttonBackgroundColor,g=n.customButtonBackgroundColor,j=n.buttonGradient,v=n.customButtonGradient,_=n.textColor,k=n.customTextColor,y=n.fontSize,O=n.customFontSize,w=n.borderRadius,E=n.borderWeight,C=n.borderColor,x=n.customBorderColor,S=n.padding,A=n.spacing,N=!!d.__experimentalGetGradientClass,P=Object(d.getColorClassName)("color",_),T=Object(d.getFontSizeClass)(y),B=Object(d.getColorClassName)("border-color",C),I=Object(d.getColorClassName)("background-color",f),M=N?Object(d.__experimentalGetGradientClass)(j):void 0,F=Object(d.getColorClassName)("background-color",u),L=N?Object(d.__experimentalGetGradientClass)(m):void 0,R=b()(0===w?"no-border-radius":void 0,T,B),D=b()(R,_?"has-text-color":void 0,P,f||j?"has-background":void 0,I,M),z=b()(R,F,L),U=!F&&h?h:p,V=!I&&v?v:g,H={subscribe_placeholder:r!==i.subscribePlaceholder.default?r:void 0,show_subscribers_total:o,button_on_newline:c,submit_button_text:s!==i.submitButtonText.default?s:void 0,custom_background_emailfield_color:U,custom_background_button_color:V,custom_text_button_color:k,custom_font_size:O||16,custom_border_radius:w||0,custom_border_weight:E||1,custom_border_color:x,custom_padding:S||15,custom_spacing:A||10,submit_button_classes:D,email_field_classes:z,show_only_email_and_button:!0},q=Object(l.reduce)(H,(function(e,t,n){return void 0===t?e:e+" ".concat(n,'="').concat(t,'"')}),"");return Object(a.createElement)("div",{className:b()(t,"wp-block-jetpack-subscriptions__supports-newline",c?"wp-block-jetpack-subscriptions__use-newline":void 0,o?"wp-block-jetpack-subscriptions__show-subs":void 0)},Object(a.createElement)(a.RawHTML,null,"[jetpack_subscription_form".concat(q,"]")))},example:{attributes:{}},deprecated:f};Object(r.a)("subscriptions",B)},function(e,t,n){"use strict";n.r(t);var r=n(20),a=n(0),o=n(1),c=n(5),i=n(2),l=n(7),s=n.n(l),u=["jetpack/markdown","jetpack/address","jetpack/email","jetpack/phone","jetpack/map","jetpack/business-hours","core/paragraph","core/image","core/heading","core/gallery","core/list","core/quote","core/shortcode","core/audio","core/code","core/cover","core/html","core/separator","core/spacer","core/subhead","core/video"],p=[["jetpack/email"],["jetpack/phone"],["jetpack/address"]],d=function(e){var t=e.isSelected;return Object(a.createElement)("div",{className:s()({"jetpack-contact-info-block":!0,"is-selected":t})},Object(a.createElement)(c.InnerBlocks,{allowedBlocks:u,templateLock:!1,template:p}))},m=n(26),b=(n(214),n(170),n(11)),h=n.n(b),f=n(15),g=n.n(f),j=n(12),v=n.n(j),_=n(13),k=n.n(_),y=n(6),O=n.n(y),w=n(14),E=n.n(w),C=function(e){var t=e.attributes,n=t.address,r=t.addressLine2,o=t.addressLine3,c=t.city,i=t.region,l=t.postal,s=t.country;return Object(a.createElement)(a.Fragment,null,n&&Object(a.createElement)("div",{className:"jetpack-address__address jetpack-address__address1"},n),r&&Object(a.createElement)("div",{className:"jetpack-address__address jetpack-address__address2"},r),o&&Object(a.createElement)("div",{className:"jetpack-address__address jetpack-address__address3"},o),c&&!(i||l)&&Object(a.createElement)("div",{className:"jetpack-address__city"},c),c&&(i||l)&&Object(a.createElement)("div",null,[Object(a.createElement)("span",{className:"jetpack-address__city"},c),", ",Object(a.createElement)("span",{className:"jetpack-address__region"},i)," ",Object(a.createElement)("span",{className:"jetpack-address__postal"},l)]),!c&&(i||l)&&Object(a.createElement)("div",null,[Object(a.createElement)("span",{className:"jetpack-address__region"},i)," ",Object(a.createElement)("span",{className:"jetpack-address__postal"},l)]),s&&Object(a.createElement)("div",{className:"jetpack-address__country"},s))},x=function(e){var t=e.attributes,n=t.address,r=t.addressLine2,a=t.addressLine3,o=t.city,c=t.region,i=t.postal,l=t.country,s=n?"".concat(n,","):"",u=r?"".concat(r,","):"",p=a?"".concat(a,","):"",d=o?"+".concat(o,","):"",m=c?"+".concat(c,","):"";m=i?"".concat(m,"+").concat(i):m;var b=l?"+".concat(l):"";return"https://www.google.com/maps/search/".concat(s).concat(u).concat(p).concat(d).concat(m).concat(b).replace(" ","+")},S=function(e){return[(t=e.attributes).address,t.addressLine2,t.addressLine3,t.city,t.region,t.postal,t.country].some((function(e){return""!==e}))&&Object(a.createElement)("div",{className:e.className},e.attributes.linkToGoogleMaps&&Object(a.createElement)("a",{href:x(e),target:"_blank",rel:"noopener noreferrer",title:Object(o.__)("Open address in Google Maps","jetpack")},Object(a.createElement)(C,e)),!e.attributes.linkToGoogleMaps&&Object(a.createElement)(C,e));var t},A=function(e){function t(){var e,n;h()(this,t);for(var r=arguments.length,a=new Array(r),o=0;o=Object(k.b)(a)?(h(t),B(!1)):e&&B(!0)}}),[a,h]),F=function(){I.current&&(I.current.focus(),A(!0))};return Object(s.useEffect)((function(){I.current&&I.current.addEventListener("blur",(function(){return A(!1)}))}),[I]),Object(s.useEffect)((function(){S||O||M(Object(C.a)(c,a,{symbol:""}))}),[a,c,O,S,M]),Object(s.useEffect)((function(){S||T||w(Object(C.a)(v,a,{symbol:""}))}),[a,S,T,M,v]),Object(s.createElement)("div",{className:b()("donations__amount",n,{"has-focus":S,"has-error":T}),role:"button",tabIndex:0,onClick:F,onKeyDown:F},g.a[a].symbol,u?Object(s.createElement)("div",{className:"donations__amount-value"},Object(C.a)(v||c,a,{symbol:""})):Object(s.createElement)(j.RichText,{allowedFormats:[],"aria-label":d,keepPlaceholderOnFocus:!0,multiline:!1,onChange:function(e){return M(e)},placeholder:Object(C.a)(c,a,{symbol:""}),ref:I,value:O,withoutInteractiveFormatting:!0}))},S=function(e){var t=e.activeTab,n=e.attributes,r=e.setAttributes,o=n.currency,c=n.oneTimeDonation,i=n.monthlyDonation,p=n.annualDonation,m=n.showCustomAmount,b=n.chooseAmountText,h=n.customAmountText,g={"one-time":"oneTimeDonation","1 month":"monthlyDonation","1 year":"annualDonation"},v=function(e){return n[g[t]][e]},_=function(e,a){var o=g[t],c=n[o];r(f()({},o,d()({},c,f()({},e,a))))},y=Object(s.useState)(o),O=l()(y,2),w=O[0],C=O[1],S=Object(k.b)(o),A=[10*S,30*S,200*S];Object(s.useEffect)((function(){w!==o&&(C(o),r({oneTimeDonation:d()({},c,{amounts:A}),monthlyDonation:d()({},i,{amounts:A}),annualDonation:d()({},p,{amounts:A})}))}),[o,w,A,c,i,p,r]);var N=v("amounts"),P=Object(u.useSelect)((function(e){return e("core/rich-text").getFormatTypes()}),[]).map((function(e){return e.name})).filter((function(e){return"core/link"!==e}));return Object(s.createElement)("div",{className:"donations__tab"},Object(s.createElement)(j.RichText,{tagName:"h4",placeholder:Object(a.__)("Write a message…","jetpack"),value:v("heading"),onChange:function(e){return _("heading",e)}}),Object(s.createElement)(j.RichText,{tagName:"p",placeholder:Object(a.__)("Write a message…","jetpack"),value:b,onChange:function(e){return r({chooseAmountText:e})}}),Object(s.createElement)("div",{className:"donations__amounts"},N.map((function(e,t){return Object(s.createElement)(x,{currency:o,defaultValue:A[t],label:Object(a.sprintf)(Object(a.__)("Tier %d","jetpack"),t+1),key:"jetpack-donations-amount-".concat(t),onChange:function(e){return function(e,t){var n=E()(N);n[t]=e,_("amounts",n)}(e,t)},value:e})}))),m&&Object(s.createElement)(s.Fragment,null,Object(s.createElement)(j.RichText,{tagName:"p",placeholder:Object(a.__)("Write a message…","jetpack"),value:h,onChange:function(e){return r({customAmountText:e})}}),Object(s.createElement)(x,{currency:o,label:Object(a.__)("Custom amount","jetpack"),defaultValue:100*Object(k.b)(o),className:"donations__custom-amount",disabled:!0})),Object(s.createElement)("hr",{className:"donations__separator"}),Object(s.createElement)(j.RichText,{tagName:"p",placeholder:Object(a.__)("Write a message…","jetpack"),value:v("extraText"),onChange:function(e){return _("extraText",e)}}),Object(s.createElement)("div",{className:"wp-block-button donations__donate-button-wrapper"},Object(s.createElement)(j.RichText,{className:"wp-block-button__link donations__donate-button",placeholder:Object(a.__)("Write a message…","jetpack"),value:v("buttonText"),onChange:function(e){return t=e,void r({oneTimeDonation:d()({},c,{buttonText:t}),monthlyDonation:d()({},i,{buttonText:t}),annualDonation:d()({},p,{buttonText:t})});var t},allowedFormats:P})))},A=n(90),N=function(e){var t=e.attributes,n=e.className,r=e.products,o=e.setAttributes,c=e.shouldUpgrade,i=e.stripeConnectUrl,p=t.oneTimeDonation,m=t.monthlyDonation,h=t.annualDonation,f=Object(s.useState)("one-time"),g=l()(f,2),j=g[0],v=g[1],_=Object(u.useSelect)((function(e){return e("core/editor").getCurrentPostId()}),[]),k=Object(s.useCallback)((function(e){return j===e}),[j]),y=d()({"one-time":{title:Object(a.__)("One-Time","jetpack")}},m.show&&{"1 month":{title:Object(a.__)("Monthly","jetpack")}},{},h.show&&{"1 year":{title:Object(a.__)("Yearly","jetpack")}});return Object(s.useEffect)((function(){p.planId===r["one-time"]&&m.planId===r["1 month"]&&h.planId===r["1 year"]||o({oneTimeDonation:d()({},p,{planId:r["one-time"]}),monthlyDonation:d()({},m,{planId:r["1 month"]}),annualDonation:d()({},h,{planId:r["1 year"]})})}),[p,m,h,o,r]),Object(s.useEffect)((function(){!m.show&&k("1 month")&&v("one-time"),!h.show&&k("1 year")&&v("one-time")}),[m,h,v,k]),Object(s.createElement)("div",{className:n},!c&&i&&Object(s.createElement)(A.a,{blockName:"donations",postId:_,stripeConnectUrl:i}),Object(s.createElement)("div",{className:"donations__container"},Object.keys(y).length>1&&Object(s.createElement)("div",{className:"donations__nav"},Object.entries(y).map((function(e){var t=l()(e,2),n=t[0],r=t[1].title;return Object(s.createElement)("div",{role:"button",tabIndex:0,className:b()("donations__nav-item",{"is-active":k(n)}),onClick:function(){return v(n)},onKeyDown:function(){return v(n)},key:"jetpack-donations-nav-item-".concat(n," ")},r)}))),Object(s.createElement)("div",{className:"donations__content"},Object(s.createElement)(S,{activeTab:j,attributes:t,setAttributes:o}))),Object(s.createElement)(O,e))},P=function(e){var t=e.className,n=e.error;return Object(s.createElement)(v.Placeholder,{icon:"lock",label:Object(a.__)("Donations","jetpack"),instructions:n,className:t})},T=n(28),B=n.n(T),I=n(22),M=n.n(I),F=function(){var e=B()(regeneratorRuntime.mark((function e(t){var n;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,M()({path:"/wpcom/v2/memberships/products",method:"POST",data:{type:"donation",currency:t}});case 3:return n=e.sent,e.abrupt("return",n);case 7:return e.prev=7,e.t0=e.catch(0),e.abrupt("return",Promise.reject(e.t0.message));case 10:case"end":return e.stop()}}),e,null,[[0,7]])})));return function(t){return e.apply(this,arguments)}}(),L=n(38),R=n(30),D=function(){var e=B()(regeneratorRuntime.mark((function e(){var t,n,r,a,o,c=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=c.length>0&&void 0!==c[0]?c[0]:null,n=Object(L.parse)(window.location.href,!0),r=n.query,a=Object(R.addQueryArgs)("/wpcom/v2/memberships/status",d()({source:"https://wordpress.com"===r.origin?"gutenberg-wpcom":"gutenberg"},t&&{type:t})),e.prev=3,e.next=6,M()({path:a,method:"GET"});case 6:return o=e.sent,e.abrupt("return",o);case 10:return e.prev=10,e.t0=e.catch(3),e.abrupt("return",Promise.reject(e.t0.message));case 13:case"end":return e.stop()}}),e,null,[[3,10]])})));return function(){return e.apply(this,arguments)}}(),z=function(e){var t=e.attributes,n=e.className,r=e.setAttributes,o=t.currency,i=Object(s.useState)(""),p=l()(i,2),d=p[0],m=p[1],b=Object(s.useState)(!1),h=l()(b,2),f=h[0],g=h[1],j=Object(s.useState)(!1),v=l()(j,2),_=v[0],k=v[1],y=Object(s.useState)([]),O=l()(y,2),w=O[0],E=O[1],C=Object(u.useSelect)((function(e){return e("core/editor").getCurrentPost()}),[]);Object(s.useEffect)((function(){r({fallbackLinkUrl:C.link})}),[C.link,r]);var x=function(e){m(e)},S=function(e){return e.reduce((function(e,t){var n=t.id,r=t.currency,a=t.type,c=t.interval;return r===o&&"donation"===a&&(e[c]=n),e}),{})},A=function(e){if(!e&&"object"!=typeof e||e.errors)m(Object(a.__)("Could not load data from WordPress.com.","jetpack"));else{g(e.should_upgrade_to_access_memberships),k(e.connect_url);var t,n,r=S(e.products);if(t=r,(n=Object.keys(t)).includes("one-time")&&n.includes("1 month")&&n.includes("1 year"))E(r);else e.should_upgrade_to_access_memberships||e.connect_url?E({"one-time":-1,"1 month":-1,"1 year":-1}):F(o).then((function(e){return E(S(e))}),x)}};return Object(s.useEffect)((function(){D("donation").then(A,x)}),[o]),d?Object(s.createElement)(P,{className:n,error:d}):Object(s.createElement)(N,c()({},e,{products:w,shouldUpgrade:f,stripeConnectUrl:_}))},U=function(e){var t=e.attributes,n=t.fallbackLinkUrl,r=t.oneTimeDonation,a=t.monthlyDonation,o=t.annualDonation;return r&&r.show&&r.planId&&-1!==r.planId?Object(s.createElement)("div",null,Object(s.createElement)(j.RichText.Content,{tagName:"h4",value:r.heading}),Object(s.createElement)(j.RichText.Content,{tagName:"p",value:r.extraText}),Object(s.createElement)(j.RichText.Content,{tagName:"a",className:"jetpack-donations-fallback-link",href:n,rel:"noopener noreferrer noamphtml",target:"_blank",value:r.buttonText}),a.show&&Object(s.createElement)(s.Fragment,null,Object(s.createElement)("hr",{className:"donations__separator"}),Object(s.createElement)(j.RichText.Content,{tagName:"h4",value:a.heading}),Object(s.createElement)(j.RichText.Content,{tagName:"p",value:a.extraText}),Object(s.createElement)(j.RichText.Content,{tagName:"a",className:"jetpack-donations-fallback-link",href:n,rel:"noopener noreferrer noamphtml",target:"_blank",value:a.buttonText})),o.show&&Object(s.createElement)(s.Fragment,null,Object(s.createElement)("hr",{className:"donations__separator"}),Object(s.createElement)(j.RichText.Content,{tagName:"h4",value:o.heading}),Object(s.createElement)(j.RichText.Content,{tagName:"p",value:o.extraText}),Object(s.createElement)(j.RichText.Content,{tagName:"a",className:"jetpack-donations-fallback-link",href:n,rel:"noopener noreferrer noamphtml",target:"_blank",value:o.buttonText}))):null},V={attributes:{currency:{type:"string",default:"USD"},oneTimeDonation:{type:"object",default:{show:!0,planId:null,amounts:[5,15,100],heading:Object(a.__)("Make a one-time donation","jetpack"),extraText:Object(a.__)("Your contribution is appreciated.","jetpack"),buttonText:Object(a.__)("Donate","jetpack")}},monthlyDonation:{type:"object",default:{show:!0,planId:null,amounts:[5,15,100],heading:Object(a.__)("Make a monthly donation","jetpack"),extraText:Object(a.__)("Your contribution is appreciated.","jetpack"),buttonText:Object(a.__)("Donate monthly","jetpack")}},annualDonation:{type:"object",default:{show:!0,planId:null,amounts:[5,15,100],heading:Object(a.__)("Make a yearly donation","jetpack"),extraText:Object(a.__)("Your contribution is appreciated.","jetpack"),buttonText:Object(a.__)("Donate yearly","jetpack")}},showCustomAmount:{type:"boolean",default:!0},chooseAmountText:{type:"string",default:Object(a.__)("Choose an amount","jetpack")},customAmountText:{type:"string",default:Object(a.__)("Or enter a custom amount","jetpack")}},supports:{html:!1},save:function(e){var t=e.attributes,n=t.currency,r=t.oneTimeDonation,o=t.monthlyDonation,c=t.annualDonation,i=t.showCustomAmount,u=t.chooseAmountText,p=t.customAmountText;if(!r||!r.show||-1===r.planId)return null;var m=d()({"one-time":{title:Object(a.__)("One-Time","jetpack")}},o.show&&{"1 month":{title:Object(a.__)("Monthly","jetpack")}},{},c.show&&{"1 year":{title:Object(a.__)("Yearly","jetpack")}});return Object(s.createElement)("div",null,Object(s.createElement)("div",{className:"donations__container"},Object.keys(m).length>1&&Object(s.createElement)("div",{className:"donations__nav"},Object.entries(m).map((function(e){var t=l()(e,2),n=t[0],r=t[1].title;return Object(s.createElement)("div",{role:"button",tabIndex:0,className:"donations__nav-item",key:"jetpack-donations-nav-item-".concat(n," "),"data-interval":n},r)}))),Object(s.createElement)("div",{className:"donations__content"},Object(s.createElement)("div",{className:"donations__tab"},Object(s.createElement)(j.RichText.Content,{tagName:"h4",className:"donations__one-time-item",value:r.heading}),o.show&&Object(s.createElement)(j.RichText.Content,{tagName:"h4",className:"donations__monthly-item",value:o.heading}),c.show&&Object(s.createElement)(j.RichText.Content,{tagName:"h4",className:"donations__annual-item",value:c.heading}),Object(s.createElement)(j.RichText.Content,{tagName:"p",value:u}),Object(s.createElement)("div",{className:"donations__amounts donations__one-time-item"},r.amounts.map((function(e){return Object(s.createElement)("div",{className:"donations__amount","data-amount":e},Object(C.a)(e,n))}))),o.show&&Object(s.createElement)("div",{className:"donations__amounts donations__monthly-item"},o.amounts.map((function(e){return Object(s.createElement)("div",{className:"donations__amount","data-amount":e},Object(C.a)(e,n))}))),c.show&&Object(s.createElement)("div",{className:"donations__amounts donations__annual-item"},c.amounts.map((function(e){return Object(s.createElement)("div",{className:"donations__amount","data-amount":e},Object(C.a)(e,n))}))),i&&Object(s.createElement)(s.Fragment,null,Object(s.createElement)(j.RichText.Content,{tagName:"p",value:p}),Object(s.createElement)("div",{className:"donations__amount donations__custom-amount"},g.a[n].symbol,Object(s.createElement)("div",{className:"donations__amount-value","data-currency":n,"data-empty-text":Object(C.a)(100*Object(k.b)(n),n,{symbol:""})}))),Object(s.createElement)("div",{className:"donations__separator"},"——"),Object(s.createElement)(j.RichText.Content,{tagName:"p",className:"donations__one-time-item",value:r.extraText}),o.show&&Object(s.createElement)(j.RichText.Content,{tagName:"p",className:"donations__monthly-item",value:o.extraText}),c.show&&Object(s.createElement)(j.RichText.Content,{tagName:"p",className:"donations__annual-item",value:c.extraText}),Object(s.createElement)("div",{className:"wp-block-button donations__donate-button-wrapper donations__one-time-item"},Object(s.createElement)(j.RichText.Content,{tagName:"a",className:"wp-block-button__link donations__donate-button donations__one-time-item",value:r.buttonText})),o.show&&Object(s.createElement)("div",{className:"wp-block-button donations__donate-button-wrapper donations__monthly-item"},Object(s.createElement)(j.RichText.Content,{tagName:"a",className:"wp-block-button__link donations__donate-button donations__monthly-item",value:o.buttonText})),c.show&&Object(s.createElement)("div",{className:"wp-block-button donations__donate-button-wrapper donations__annual-item"},Object(s.createElement)(j.RichText.Content,{tagName:"a",className:"wp-block-button__link donations__donate-button donations__annual-item",value:c.buttonText}))))))}},H=n(36),q=(n(217),{title:Object(a.__)("Donations","jetpack"),description:Object(a.__)("Collect one-time, monthly, or annually recurring donations.","jetpack"),icon:H.a,category:"earn",keywords:[Object(a.__)("Donations","jetpack")],supports:{html:!1},edit:z,save:U,example:{},deprecated:[V]});Object(r.a)("donations",q)},function(e,t,n){"use strict";n.r(t);var r=n(20),a=n(0),o=n(1),c=n(2),i=["USD","EUR","AUD","BRL","CAD","CZK","DKK","HKD","HUF","ILS","JPY","MYR","MXN","TWD","NZD","NOK","PHP","PLN","GBP","RUB","SGD","SEK","CHF","THB"],l=n(24),s=n(17),u=n(9),p=n.n(u),d=n(11),m=n.n(d),b=n(15),h=n.n(b),f=n(12),g=n.n(f),j=n(13),v=n.n(j),_=n(6),k=n.n(_),y=n(14),O=n.n(y),w=n(3),E=n.n(w),C=n(7),x=n.n(C),S=n(58),A=n.n(S),N=n(4),P=n(56),T=n(18),B=n(10),I=n(5),M=n(47),F=(n(295),n(146)),L=n.n(F),R=n(147),D=n.n(R),z=function(e){var t=e.title,n=void 0===t?"":t,r=e.content,c=void 0===r?"":r,i=e.formattedPrice,l=void 0===i?"":i,s=e.multiple,u=void 0!==s&&s,p=e.featuredMediaUrl,d=void 0===p?null:p,m=e.featuredMediaTitle,b=void 0===m?null:m;return Object(a.createElement)("div",{className:"jetpack-simple-payments-wrapper"},Object(a.createElement)("div",{className:"jetpack-simple-payments-product"},d&&Object(a.createElement)("div",{className:"jetpack-simple-payments-product-image"},Object(a.createElement)("figure",{className:"jetpack-simple-payments-image"},Object(a.createElement)("img",{src:d,alt:b}))),Object(a.createElement)("div",{className:"jetpack-simple-payments-details"},n&&Object(a.createElement)("div",{className:"jetpack-simple-payments-title"},Object(a.createElement)("p",null,n)),c&&Object(a.createElement)("div",{className:"jetpack-simple-payments-description"},Object(a.createElement)("p",null,c)),l&&Object(a.createElement)("div",{className:"jetpack-simple-payments-price"},Object(a.createElement)("p",null,l)),Object(a.createElement)("div",{className:"jetpack-simple-payments-purchase-box"},u&&Object(a.createElement)("div",{className:"jetpack-simple-payments-items"},Object(a.createElement)("input",{className:"jetpack-simple-payments-items-number",readOnly:!0,type:"number",value:"1"})),Object(a.createElement)("div",{className:"jetpack-simple-payments-button"},Object(a.createElement)("img",{alt:Object(o.__)("Pay with PayPal","jetpack"),src:L.a,srcSet:"".concat(D.a," 2x")}))))))},U=n(63),V=function(e){return function(t){return e({featuredMediaId:Object(N.get)(t,"id",0),featuredMediaUrl:Object(N.get)(t,"url",null),featuredMediaTitle:Object(N.get)(t,"title",null)})}},H=function(e){var t=e.featuredMediaId,n=e.featuredMediaUrl,r=e.featuredMediaTitle,i=e.setAttributes;return t?Object(a.createElement)("div",null,Object(a.createElement)(a.Fragment,null,Object(a.createElement)(I.BlockControls,null,Object(a.createElement)(c.Toolbar,null,Object(a.createElement)(I.MediaUpload,{onSelect:V(i),allowedTypes:["image"],value:t,render:function(e){var t=e.open;return Object(a.createElement)(U.a,{label:Object(o.__)("Edit Image","jetpack"),onClick:t})}}),Object(a.createElement)(c.ToolbarButton,{icon:"trash",title:Object(o.__)("Remove Image","jetpack"),onClick:function(){return i({featuredMediaId:null,featuredMediaUrl:null,featuredMediaTitle:null})}}))),Object(a.createElement)("figure",null,Object(a.createElement)("img",{src:n,alt:r})))):Object(a.createElement)(I.MediaPlaceholder,{icon:Object(a.createElement)(I.BlockIcon,{icon:"format-image"}),labels:{title:Object(o.__)("Product Image","jetpack")},accept:"image/*",allowedTypes:["image"],onSelect:V(i)})},q=function(e){var t=(""+e).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);return t?Math.max(0,(t[1]?t[1].length:0)-(t[2]?+t[2]:0)):0},G=function(){var e,t,n,r,a,o,c=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"en-US",i=null===(e=window)||void 0===e||null===(t=e.window)||void 0===t?void 0:t.navigator;return(null==i||null===(n=i.languages)||void 0===n?void 0:n.length)?i.languages[0]:null!==(r=null!==(a=null!==(o=null==i?void 0:i.userLanguage)&&void 0!==o?o:null==i?void 0:i.language)&&void 0!==a?a:null==i?void 0:i.browserLanguage)&&void 0!==r?r:c},W=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,t=arguments.length>1?arguments[1]:void 0,n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],r=Object(P.b)(t),a=r.precision,o=r.symbol,c=e.toFixed(a);return n?"".concat(c," ").concat(Object(N.trimEnd)(o,".")):c},K=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,t=arguments.length>1?arguments[1]:void 0,n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];if(!window.Intl||"function"!=typeof Intl.NumberFormat)return W(e,t,n);var r,a=Object(B.select)("core/block-editor").getSettings(),o=a.siteLocale,c=[o,G(),"en-US"],i={};n&&(i={style:"currency",currency:t});for(var l=0,s=c;la?0===a?(n.setState({fieldPriceError:Object(o.__)("We know every penny counts, but prices in this currency can’t contain decimal values.","jetpack")}),!1):(n.setState({fieldPriceError:Object(o.sprintf)(Object(o._n)("The price cannot have more than %d decimal place.","The price cannot have more than %d decimal places.",a,"jetpack"),a)}),!1):(n.state.fieldPriceError&&n.setState({fieldPriceError:null}),!0):(n.setState({fieldPriceError:Object(o.__)("If you’re selling something, you need a price tag. Add yours here.","jetpack")}),!1)})),E()(k()(n),"validateEmail",(function(){var e=n.props.attributes.email;return e?A.a.validate(e)?(n.state.fieldEmailError&&n.setState({fieldEmailError:null}),!0):(n.setState({fieldEmailError:Object(o.sprintf)(Object(o.__)("%s is not a valid email address.","jetpack"),e)}),!1):(n.setState({fieldEmailError:Object(o.__)("We want to make sure payments reach you, so please add an email address.","jetpack")}),!1)})),E()(k()(n),"validateTitle",(function(){return n.props.attributes.title?(n.state.fieldTitleError&&n.setState({fieldTitleError:null}),!0):(n.setState({fieldTitleError:Object(o.__)("Please add a brief title so that people know what they’re paying for.","jetpack")}),!1)})),E()(k()(n),"handleEmailChange",(function(e){n.props.setAttributes({email:e}),n.setState({fieldEmailError:null})})),E()(k()(n),"handleContentChange",(function(e){n.props.setAttributes({content:e})})),E()(k()(n),"handlePriceChange",(function(e){e=parseFloat(e),isNaN(e)?n.props.setAttributes({price:void 0}):n.props.setAttributes({price:e}),n.setState({fieldPriceError:null})})),E()(k()(n),"handleCurrencyChange",(function(e){n.props.setAttributes({currency:e})})),E()(k()(n),"handleMultipleChange",(function(e){n.props.setAttributes({multiple:!!e})})),E()(k()(n),"handleTitleChange",(function(e){n.props.setAttributes({title:e}),n.setState({fieldTitleError:null})})),E()(k()(n),"getCurrencyList",i.map((function(e){var t=Object(P.b)(e).symbol;return{value:e,label:t===e?e:"".concat(e," ").concat(Object(N.trimEnd)(t,"."))}}))),E()(k()(n),"renderSettings",(function(){return Object(a.createElement)(I.InspectorControls,null,Object(a.createElement)(c.PanelBody,{title:Object(o.__)("Settings","jetpack"),initialOpen:!1},Object(a.createElement)(c.BaseControl,{label:Object(o.__)("Purchase link text","jetpack"),help:Object(o.__)("Enter the text you want to display on a purchase link used as fallback when the PayPal button cannot be used (e.g. emails, AMP, etc.)","jetpack"),className:"jetpack-simple-payments__purchase-link-text"},Object(a.createElement)(c.TextControl,{placeholder:Object(o.__)("Click here to purchase","jetpack"),onChange:function(e){return n.props.setAttributes({postLinkText:e})},value:n.props.attributes.postLinkText}))))})),n}return O()(t,e),h()(t,[{key:"componentDidMount",value:function(){this.injectPaymentAttributes();var e=this.props,t=e.attributes,n=e.hasPublishAction,r=e.postLinkUrl,a=e.setAttributes;!t.productId&&n&&this.saveProduct();var c=r&&r!==this.props.attributes.postLinkUrl,i=!this.props.attributes.postLinkText;(c||i)&&a(p()({},c&&{postLinkUrl:r},{},i&&{postLinkText:Object(o.__)("Click here to purchase.","jetpack")}))}},{key:"componentDidUpdate",value:function(e){var t=this.props,n=t.hasPublishAction,r=t.isSelected,a=t.postLinkUrl,c=t.setAttributes;Object(N.isEqual)(e.simplePayment,this.props.simplePayment)||this.injectPaymentAttributes(),!e.isSaving&&this.props.isSaving&&n&&this.validateAttributes()?this.saveProduct():e.isSelected&&!r&&this.validateAttributes();var i=a&&a!==this.props.attributes.postLinkUrl,l=!this.props.attributes.postLinkText;(i||l)&&c(p()({},i&&{postLinkUrl:a},{},l&&{postLinkText:Object(o.__)("Click here to purchase.","jetpack")}))}},{key:"injectPaymentAttributes",value:function(){var e=this.props,t=e.simplePayment,n=e.featuredMedia;if(this.shouldInjectPaymentAttributes&&!Object(N.isEmpty)(t)){var r=this.props,a=r.attributes,o=r.setAttributes,c=a.content,i=a.currency,l=a.email,s=a.featuredMediaId,u=a.featuredMediaUrl,p=a.featuredMediaTitle,d=a.multiple,m=a.price,b=a.title;o({content:Object(N.get)(t,["content","raw"],c),currency:Object(N.get)(t,["meta","spay_currency"],i),email:Object(N.get)(t,["meta","spay_email"],l),featuredMediaId:Object(N.get)(t,["featured_media"],s),featuredMediaUrl:Object(N.get)(n,"url",u),featuredMediaTitle:Object(N.get)(n,"title",p),multiple:Boolean(Object(N.get)(t,["meta","spay_multiple"],Boolean(d))),price:Object(N.get)(t,["meta","spay_price"],m||void 0),title:Object(N.get)(t,["title","raw"],b)}),this.shouldInjectPaymentAttributes=!this.shouldInjectPaymentAttributes}}},{key:"toApi",value:function(){var e=this.props.attributes,t=e.content,n=e.currency,r=e.email,a=e.featuredMediaId,o=e.multiple,c=e.price,i=e.productId;return{id:i,content:t,featured_media:a,meta:{spay_currency:n,spay_email:r,spay_multiple:o,spay_price:c},status:i?"publish":"draft",title:e.title}}},{key:"saveProduct",value:function(){var e=this;if(!this.state.isSavingProduct){var t=this.props,n=t.attributes,r=t.setAttributes,a=n.email,c=Object(B.dispatch)("core").saveEntityRecord;this.setState({isSavingProduct:!0},(function(){c("postType","jp_pay_product",e.toApi()).then((function(e){return e&&r({productId:e.id}),e})).catch((function(t){if(t&&t.data){var n=t.data.key;e.setState({fieldEmailError:"spay_email"===n?Object(o.sprintf)(Object(o.__)("%s is not a valid email address.","jetpack"),a):null,fieldPriceError:"spay_price"===n?Object(o.__)("Invalid price.","jetpack"):null})}})).finally((function(){e.setState({isSavingProduct:!1})}))}))}}},{key:"render",value:function(){var e=this.state,t=e.fieldEmailError,n=e.fieldPriceError,r=e.fieldTitleError,i=this.props,l=i.attributes,s=i.instanceId,u=i.isSelected,p=i.setAttributes,d=i.simplePayment,m=l.content,b=l.currency,h=l.email,f=l.featuredMediaId,g=l.featuredMediaUrl,j=l.featuredMediaTitle,v=l.multiple,_=l.price,k=l.productId,y=l.title,O=k&&Object(N.isEmpty)(d);if(!u&&O)return Object(a.createElement)("div",{className:"simple-payments__loading"},Object(a.createElement)(z,{"aria-busy":"true",content:"█████",formattedPrice:"█████",title:"█████"}));if(!u&&h&&_&&y&&!t&&!n&&!r)return Object(a.createElement)(z,{"aria-busy":"false",content:m,featuredMediaUrl:g,featuredMediaTitle:j,formattedPrice:K(_,b),multiple:v,title:y});var w=O?c.Disabled:"div";return Object(a.createElement)(w,{className:"wp-block-jetpack-simple-payments"},this.renderSettings(),Object(a.createElement)(H,{featuredMediaId:f,featuredMediaUrl:g,featuredMediaTitle:j,setAttributes:p}),Object(a.createElement)("div",null,Object(a.createElement)(c.TextControl,{"aria-describedby":"".concat(s,"-title-error"),className:x()("simple-payments__field","simple-payments__field-title",{"simple-payments__field-has-error":r}),label:Object(o.__)("Item name","jetpack"),onChange:this.handleTitleChange,placeholder:Object(o.__)("Item name","jetpack"),required:!0,type:"text",value:y}),Object(a.createElement)(M.a,{id:"".concat(s,"-title-error"),isError:!0},r),Object(a.createElement)(c.TextareaControl,{className:"simple-payments__field simple-payments__field-content",label:Object(o.__)("Describe your item in a few words","jetpack"),onChange:this.handleContentChange,placeholder:Object(o.__)("Describe your item in a few words","jetpack"),"aria-label":Object(o.__)("Describe your item in a few words","jetpack"),value:m}),Object(a.createElement)("div",{className:"simple-payments__price-container"},Object(a.createElement)(c.SelectControl,{className:"simple-payments__field simple-payments__field-currency",label:Object(o.__)("Currency","jetpack"),onChange:this.handleCurrencyChange,options:this.getCurrencyList,value:b}),Object(a.createElement)(c.TextControl,{"aria-describedby":"".concat(s,"-price-error"),className:x()("simple-payments__field","simple-payments__field-price",{"simple-payments__field-has-error":n}),label:Object(o.__)("Price","jetpack"),onChange:this.handlePriceChange,placeholder:K(0,b,!1),required:!0,step:"1",type:"number",value:_||""}),Object(a.createElement)(M.a,{id:"".concat(s,"-price-error"),isError:!0},n)),Object(a.createElement)("div",{className:"simple-payments__field-multiple"},Object(a.createElement)(c.ToggleControl,{checked:Boolean(v),label:Object(o.__)("Allow people to buy more than one item at a time","jetpack"),onChange:this.handleMultipleChange})),Object(a.createElement)(c.TextControl,{"aria-describedby":"".concat(s,"-email-").concat(t?"error":"help"),className:x()("simple-payments__field","simple-payments__field-email",{"simple-payments__field-has-error":t}),label:Object(o.__)("Email","jetpack"),onChange:this.handleEmailChange,placeholder:Object(o.__)("Email","jetpack"),required:!0,type:"text",value:h}),Object(a.createElement)(M.a,{id:"".concat(s,"-email-error"),isError:!0},t),Object(a.createElement)(M.a,{id:"".concat(s,"-email-help")},Object(o.__)("Enter the email address associated with your PayPal account. Don’t have an account?","jetpack")+" ",Object(a.createElement)(c.ExternalLink,{href:"https://www.paypal.com/"},Object(o.__)("Create one on PayPal","jetpack")))))}}]),t}(a.Component),Z=Object(B.withSelect)((function(e,t){var n=e("core"),r=n.getEntityRecord,a=n.getMedia,o=e("core/editor"),c=o.isSavingPost,i=o.getCurrentPost,l=t.attributes,s=l.productId,u=l.featuredMediaId,p=s?Object(N.pick)(r("postType","jp_pay_product",s),[["content"],["meta","spay_currency"],["meta","spay_email"],["meta","spay_multiple"],["meta","spay_price"],["title","raw"],["featured_media"]]):void 0,d=i();return{hasPublishAction:!!Object(N.get)(d,["_links","wp:action-publish"]),isSaving:!!c(),simplePayment:p,featuredMedia:u?a(u):null,postLinkUrl:d.link}})),J=Object(T.compose)(Z,T.withInstanceId)($);var Y={attributes:{currency:{type:"string",default:"USD"},content:{type:"string",default:""},email:{type:"string",default:""},featuredMediaId:{type:"number",default:0},featuredMediaUrl:{type:"string",default:null},featuredMediaTitle:{type:"string",default:null},multiple:{type:"boolean",default:!1},price:{type:"number"},productId:{type:"number"},title:{type:"string",default:""}},supports:{className:!1,customClassName:!1,html:!1,reusable:!1},save:function(e){var t=e.attributes.productId;return t?Object(a.createElement)(a.RawHTML,null,'[simple-payment id="'.concat(t,'"]')):null}},Q=function(e){function t(){var e,n;m()(this,t);for(var r=arguments.length,l=new Array(r),s=0;sa?0===a?(n.setState({fieldPriceError:Object(o.__)("We know every penny counts, but prices in this currency can’t contain decimal values.","jetpack")}),!1):(n.setState({fieldPriceError:Object(o.sprintf)(Object(o._n)("The price cannot have more than %d decimal place.","The price cannot have more than %d decimal places.",a,"jetpack"),a)}),!1):(n.state.fieldPriceError&&n.setState({fieldPriceError:null}),!0):(n.setState({fieldPriceError:Object(o.__)("If you’re selling something, you need a price tag. Add yours here.","jetpack")}),!1)})),E()(k()(n),"validateEmail",(function(){var e=n.props.attributes.email;return e?A.a.validate(e)?(n.state.fieldEmailError&&n.setState({fieldEmailError:null}),!0):(n.setState({fieldEmailError:Object(o.sprintf)(Object(o.__)("%s is not a valid email address.","jetpack"),e)}),!1):(n.setState({fieldEmailError:Object(o.__)("We want to make sure payments reach you, so please add an email address.","jetpack")}),!1)})),E()(k()(n),"validateTitle",(function(){return n.props.attributes.title?(n.state.fieldTitleError&&n.setState({fieldTitleError:null}),!0):(n.setState({fieldTitleError:Object(o.__)("Please add a brief title so that people know what they’re paying for.","jetpack")}),!1)})),E()(k()(n),"handleEmailChange",(function(e){n.props.setAttributes({email:e}),n.setState({fieldEmailError:null})})),E()(k()(n),"handleContentChange",(function(e){n.props.setAttributes({content:e})})),E()(k()(n),"handlePriceChange",(function(e){e=parseFloat(e),isNaN(e)?n.props.setAttributes({price:void 0}):n.props.setAttributes({price:e}),n.setState({fieldPriceError:null})})),E()(k()(n),"handleCurrencyChange",(function(e){n.props.setAttributes({currency:e})})),E()(k()(n),"handleMultipleChange",(function(e){n.props.setAttributes({multiple:!!e})})),E()(k()(n),"handleTitleChange",(function(e){n.props.setAttributes({title:e}),n.setState({fieldTitleError:null})})),E()(k()(n),"getCurrencyList",i.map((function(e){var t=Object(P.b)(e).symbol;return{value:e,label:t===e?e:"".concat(e," ").concat(Object(N.trimEnd)(t,"."))}}))),E()(k()(n),"renderSettings",(function(){return Object(a.createElement)(I.InspectorControls,null,Object(a.createElement)(c.PanelBody,{title:Object(o.__)("Settings","jetpack"),initialOpen:!1},Object(a.createElement)(c.BaseControl,{label:Object(o.__)("Purchase link text","jetpack"),help:Object(o.__)("Enter the text you want to display on a purchase link used as fallback when the PayPal button cannot be used (e.g. emails, AMP, etc.)","jetpack"),className:"jetpack-simple-payments__purchase-link-text"},Object(a.createElement)(c.TextControl,{placeholder:Object(o.__)("Click here to purchase","jetpack"),onChange:function(e){return n.props.setAttributes({postLinkText:e})},value:n.props.attributes.postLinkText}))))})),n}return O()(t,e),h()(t,[{key:"componentDidMount",value:function(){this.injectPaymentAttributes();var e=this.props,t=e.attributes,n=e.hasPublishAction,r=e.postLinkUrl,a=e.setAttributes;!t.productId&&n&&this.saveProduct();var c=r&&r!==this.props.attributes.postLinkUrl,i=!this.props.attributes.postLinkText;(c||i)&&a(p()({},c&&{postLinkUrl:r},{},i&&{postLinkText:Object(o.__)("Click here to purchase.","jetpack")}))}},{key:"componentDidUpdate",value:function(e){var t=this.props,n=t.hasPublishAction,r=t.isSelected,a=t.postLinkUrl,c=t.setAttributes;Object(N.isEqual)(e.simplePayment,this.props.simplePayment)||this.injectPaymentAttributes(),!e.isSaving&&this.props.isSaving&&n&&this.validateAttributes()?this.saveProduct():e.isSelected&&!r&&this.validateAttributes();var i=a&&a!==this.props.attributes.postLinkUrl,l=!this.props.attributes.postLinkText;(i||l)&&c(p()({},i&&{postLinkUrl:a},{},l&&{postLinkText:Object(o.__)("Click here to purchase.","jetpack")}))}},{key:"injectPaymentAttributes",value:function(){var e=this.props,t=e.simplePayment,n=e.featuredMedia;if(this.shouldInjectPaymentAttributes&&!Object(N.isEmpty)(t)){var r=this.props,a=r.attributes,o=r.setAttributes,c=a.content,i=a.currency,l=a.email,s=a.featuredMediaId,u=a.featuredMediaUrl,p=a.featuredMediaTitle,d=a.multiple,m=a.price,b=a.title;o({content:Object(N.get)(t,["content","raw"],c),currency:Object(N.get)(t,["meta","spay_currency"],i),email:Object(N.get)(t,["meta","spay_email"],l),featuredMediaId:Object(N.get)(t,["featured_media"],s),featuredMediaUrl:Object(N.get)(n,"url",u),featuredMediaTitle:Object(N.get)(n,"title",p),multiple:Boolean(Object(N.get)(t,["meta","spay_multiple"],Boolean(d))),price:Object(N.get)(t,["meta","spay_price"],m||void 0),title:Object(N.get)(t,["title","raw"],b)}),this.shouldInjectPaymentAttributes=!this.shouldInjectPaymentAttributes}}},{key:"toApi",value:function(){var e=this.props.attributes,t=e.content,n=e.currency,r=e.email,a=e.featuredMediaId,o=e.multiple,c=e.price,i=e.productId;return{id:i,content:t,featured_media:a,meta:{spay_currency:n,spay_email:r,spay_multiple:o,spay_price:c},status:i?"publish":"draft",title:e.title}}},{key:"saveProduct",value:function(){var e=this;if(!this.state.isSavingProduct){var t=this.props,n=t.attributes,r=t.setAttributes,a=n.email,c=Object(B.dispatch)("core").saveEntityRecord;this.setState({isSavingProduct:!0},(function(){c("postType","jp_pay_product",e.toApi()).then((function(e){return e&&r({productId:e.id}),e})).catch((function(t){if(t&&t.data){var n=t.data.key;e.setState({fieldEmailError:"spay_email"===n?Object(o.sprintf)(Object(o.__)("%s is not a valid email address.","jetpack"),a):null,fieldPriceError:"spay_price"===n?Object(o.__)("Invalid price.","jetpack"):null})}})).finally((function(){e.setState({isSavingProduct:!1})}))}))}}},{key:"render",value:function(){var e=this.state,t=e.fieldEmailError,n=e.fieldPriceError,r=e.fieldTitleError,i=this.props,l=i.attributes,s=i.instanceId,u=i.isSelected,p=i.setAttributes,d=i.simplePayment,m=l.content,b=l.currency,h=l.email,f=l.featuredMediaId,g=l.featuredMediaUrl,j=l.featuredMediaTitle,v=l.multiple,_=l.price,k=l.productId,y=l.title,O=k&&Object(N.isEmpty)(d);if(!u&&O)return Object(a.createElement)("div",{className:"simple-payments__loading"},Object(a.createElement)(z,{"aria-busy":"true",content:"█████",formattedPrice:"█████",title:"█████"}));if(!u&&h&&_&&y&&!t&&!n&&!r)return Object(a.createElement)(z,{"aria-busy":"false",content:m,featuredMediaUrl:g,featuredMediaTitle:j,formattedPrice:W(_,b),multiple:v,title:y});var w=O?c.Disabled:"div";return Object(a.createElement)(w,{className:"wp-block-jetpack-simple-payments"},this.renderSettings(),Object(a.createElement)(H,{featuredMediaId:f,featuredMediaUrl:g,featuredMediaTitle:j,setAttributes:p}),Object(a.createElement)("div",null,Object(a.createElement)(c.TextControl,{"aria-describedby":"".concat(s,"-title-error"),className:x()("simple-payments__field","simple-payments__field-title",{"simple-payments__field-has-error":r}),label:Object(o.__)("Item name","jetpack"),onChange:this.handleTitleChange,placeholder:Object(o.__)("Item name","jetpack"),required:!0,type:"text",value:y}),Object(a.createElement)(M.a,{id:"".concat(s,"-title-error"),isError:!0},r),Object(a.createElement)(c.TextareaControl,{className:"simple-payments__field simple-payments__field-content",label:Object(o.__)("Describe your item in a few words","jetpack"),onChange:this.handleContentChange,placeholder:Object(o.__)("Describe your item in a few words","jetpack"),value:m}),Object(a.createElement)("div",{className:"simple-payments__price-container"},Object(a.createElement)(c.SelectControl,{className:"simple-payments__field simple-payments__field-currency",label:Object(o.__)("Currency","jetpack"),onChange:this.handleCurrencyChange,options:this.getCurrencyList,value:b}),Object(a.createElement)(c.TextControl,{"aria-describedby":"".concat(s,"-price-error"),className:x()("simple-payments__field","simple-payments__field-price",{"simple-payments__field-has-error":n}),label:Object(o.__)("Price","jetpack"),onChange:this.handlePriceChange,placeholder:W(0,b,!1),required:!0,step:"1",type:"number",value:_||""}),Object(a.createElement)(M.a,{id:"".concat(s,"-price-error"),isError:!0},n)),Object(a.createElement)("div",{className:"simple-payments__field-multiple"},Object(a.createElement)(c.ToggleControl,{checked:Boolean(v),label:Object(o.__)("Allow people to buy more than one item at a time","jetpack"),onChange:this.handleMultipleChange})),Object(a.createElement)(c.TextControl,{"aria-describedby":"".concat(s,"-email-").concat(t?"error":"help"),className:x()("simple-payments__field","simple-payments__field-email",{"simple-payments__field-has-error":t}),label:Object(o.__)("Email","jetpack"),onChange:this.handleEmailChange,placeholder:Object(o.__)("Email","jetpack"),required:!0,type:"text",value:h}),Object(a.createElement)(M.a,{id:"".concat(s,"-email-error"),isError:!0},t),Object(a.createElement)(M.a,{id:"".concat(s,"-email-help")},Object(o.__)("Enter the email address associated with your PayPal account. Don’t have an account?","jetpack")+" ",Object(a.createElement)(c.ExternalLink,{href:"https://www.paypal.com/"},Object(o.__)("Create one on PayPal","jetpack")))))}}]),t}(a.Component),X=Object(B.withSelect)((function(e,t){var n=e("core"),r=n.getEntityRecord,a=n.getMedia,o=e("core/editor"),c=o.isSavingPost,i=o.getCurrentPost,l=t.attributes,s=l.productId,u=l.featuredMediaId,p=s?Object(N.pick)(r("postType","jp_pay_product",s),[["content"],["meta","spay_currency"],["meta","spay_email"],["meta","spay_multiple"],["meta","spay_price"],["title","raw"],["featured_media"]]):void 0,d=i();return{hasPublishAction:!!Object(N.get)(d,["_links","wp:action-publish"]),isSaving:!!c(),simplePayment:p,featuredMedia:u?a(u):null,postLinkUrl:d.link}})),ee=Object(T.compose)(X,T.withInstanceId)(Q);var te=Object(a.createElement)(c.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},Object(a.createElement)(c.Path,{fill:"none",d:"M0 0h24v24H0V0z"}),Object(a.createElement)(c.Path,{d:"M20 4H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6c0-1.11-.89-2-2-2zm0 14H4v-6h16v6zm0-10H4V6h16v2z"})),ne=Object(l.b)()||Object(l.a)()?"https://wordpress.com/support/pay-with-paypal/":"https://jetpack.com/support/jetpack-blocks/pay-with-paypal/",re={title:Object(o.__)("Pay with PayPal","jetpack"),description:Object(a.createElement)(a.Fragment,null,Object(a.createElement)("p",null,Object(o.__)("Lets you add credit and debit card payment buttons with minimal setup.","jetpack")),Object(a.createElement)("p",null,Object(o.__)("Good for collecting donations or payments for products and services.","jetpack")),Object(a.createElement)(c.ExternalLink,{href:ne},Object(o.__)("Support reference","jetpack"))),icon:{src:te,foreground:Object(s.a)()},category:"earn",keywords:[Object(o._x)("buy","block search term","jetpack"),Object(o._x)("commerce","block search term","jetpack"),Object(o._x)("products","block search term","jetpack"),Object(o._x)("purchase","block search term","jetpack"),Object(o._x)("sell","block search term","jetpack"),Object(o._x)("shop","block search term","jetpack"),Object(o._x)("simple","block search term","jetpack"),Object(o._x)("payments","block search term","jetpack"),"PayPal"],attributes:{currency:{type:"string",default:"USD"},content:{type:"string",source:"html",selector:".jetpack-simple-payments-description p",default:""},email:{type:"string",default:""},featuredMediaId:{type:"number",default:0},featuredMediaUrl:{type:"string",source:"attribute",selector:".jetpack-simple-payments-image img",attribute:"src",default:null},featuredMediaTitle:{type:"string",source:"attribute",selector:".jetpack-simple-payments-image img",attribute:"alt",default:null},multiple:{type:"boolean",default:!1},postLinkUrl:{type:"string",source:"attribute",selector:".jetpack-simple-payments-purchase",attribute:"href"},postLinkText:{type:"string",source:"html",selector:".jetpack-simple-payments-purchase",default:Object(o.__)("Click here to purchase.","jetpack")},price:{type:"number"},productId:{type:"number"},title:{type:"string",source:"html",selector:".jetpack-simple-payments-title p",default:""}},transforms:{from:[{type:"shortcode",tag:"simple-payment",attributes:{productId:{type:"number",shortcode:function(e){var t=e.named.id;if(t){var n=parseInt(t,10);return n||void 0}}}}}]},edit:ee,save:function(e){var t=e.attributes,n=t.content,r=t.currency,o=t.featuredMediaUrl,c=t.featuredMediaTitle,i=t.postLinkUrl,l=t.postLinkText,s=t.price,u=t.productId,p=t.title;return u?Object(a.createElement)("div",{className:"jetpack-simple-payments-wrapper jetpack-simple-payments-".concat(u)},Object(a.createElement)("div",{className:"jetpack-simple-payments-product"},o&&Object(a.createElement)("div",{className:"jetpack-simple-payments-product-image"},Object(a.createElement)("div",{className:"jetpack-simple-payments-image"},Object(a.createElement)("figure",null,Object(a.createElement)("img",{src:o,alt:c})))),Object(a.createElement)("div",{className:"jetpack-simple-payments-details"},Object(a.createElement)("div",{className:"jetpack-simple-payments-title"},Object(a.createElement)("p",null,p)),Object(a.createElement)("div",{className:"jetpack-simple-payments-description"},Object(a.createElement)("p",null,n)),Object(a.createElement)("div",{className:"jetpack-simple-payments-price"},Object(a.createElement)("p",null,W(s,r))),Object(a.createElement)("a",{className:"jetpack-simple-payments-purchase",href:i,target:"_blank",rel:"noopener noreferrer"},l)))):null},supports:{className:!1,customClassName:!1,html:!1,reusable:!1}},ae=n(148),oe=n.n(ae),ce=(n(296),Object(a.createElement)(c.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},Object(a.createElement)(c.Path,{fill:"none",d:"M0 0h24v24H0V0z"}),Object(a.createElement)(c.Path,{d:"M20 4H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6c0-1.11-.89-2-2-2zm0 14H4v-6h16v6zm0-10H4V6h16v2z"}))),ie=Object(l.b)()||Object(l.a)()?"https://wordpress.com/support/pay-with-paypal/":"https://jetpack.com/support/jetpack-blocks/pay-with-paypal/",le={title:Object(o.__)("Pay with PayPal","jetpack"),description:Object(a.createElement)(a.Fragment,null,Object(a.createElement)("p",null,Object(o.__)("Lets you add credit and debit card payment buttons with minimal setup.","jetpack")),Object(a.createElement)("p",null,Object(o.__)("Good for collecting donations or payments for products and services.","jetpack")),Object(a.createElement)(c.ExternalLink,{href:ie},Object(o.__)("Support reference","jetpack"))),icon:{src:ce,foreground:Object(s.a)()},category:"earn",keywords:[Object(o._x)("buy","block search term","jetpack"),Object(o._x)("commerce","block search term","jetpack"),Object(o._x)("products","block search term","jetpack"),Object(o._x)("purchase","block search term","jetpack"),Object(o._x)("sell","block search term","jetpack"),Object(o._x)("shop","block search term","jetpack"),Object(o._x)("simple","block search term","jetpack"),Object(o._x)("payments","block search term","jetpack"),"PayPal"],attributes:{currency:{type:"string",default:"USD"},content:{type:"string",source:"html",selector:".jetpack-simple-payments-description p",default:""},email:{type:"string",default:""},featuredMediaId:{type:"number",default:0},featuredMediaUrl:{type:"string",source:"attribute",selector:".jetpack-simple-payments-image img",attribute:"src",default:null},featuredMediaTitle:{type:"string",source:"attribute",selector:".jetpack-simple-payments-image img",attribute:"alt",default:null},multiple:{type:"boolean",default:!1},postLinkUrl:{type:"string",source:"attribute",selector:".jetpack-simple-payments-purchase",attribute:"href"},postLinkText:{type:"string",source:"html",selector:".jetpack-simple-payments-purchase",default:Object(o.__)("Click here to purchase.","jetpack")},price:{type:"number"},productId:{type:"number"},title:{type:"string",source:"html",selector:".jetpack-simple-payments-title p",default:""}},transforms:{from:[{type:"shortcode",tag:"simple-payment",attributes:{productId:{type:"number",shortcode:function(e){var t=e.named.id;if(t){var n=parseInt(t,10);return n||void 0}}}}}]},edit:J,save:function(e){var t=e.attributes,n=t.content,r=t.currency,o=t.featuredMediaUrl,c=t.featuredMediaTitle,i=t.postLinkUrl,l=t.postLinkText,s=t.price,u=t.productId,p=t.title;return u?Object(a.createElement)("div",{className:"jetpack-simple-payments-wrapper jetpack-simple-payments-".concat(u)},Object(a.createElement)("div",{className:"jetpack-simple-payments-product"},o&&Object(a.createElement)("div",{className:"jetpack-simple-payments-product-image"},Object(a.createElement)("div",{className:"jetpack-simple-payments-image"},Object(a.createElement)("figure",null,Object(a.createElement)("img",{src:o,alt:c})))),Object(a.createElement)("div",{className:"jetpack-simple-payments-details"},Object(a.createElement)("div",{className:"jetpack-simple-payments-title"},Object(a.createElement)("p",null,p)),Object(a.createElement)("div",{className:"jetpack-simple-payments-description"},Object(a.createElement)("p",null,n)),Object(a.createElement)("div",{className:"jetpack-simple-payments-price"},Object(a.createElement)("p",null,K(s,r))),Object(a.createElement)("a",{className:"jetpack-simple-payments-purchase",href:i,target:"_blank",rel:"noopener noreferrer"},l)))):null},example:{attributes:{price:25,title:Object(o.__)("Jetpack t-shirt","jetpack"),content:Object(o.__)("Take flight in ultimate comfort with this stylish t-shirt featuring the Jetpack logo.","jetpack"),email:"jetpack@jetpack.com",featuredMediaUrl:oe.a}},supports:{className:!1,customClassName:!1,html:!1,reusable:!1},deprecated:[Y,re]};Object(r.a)("simple-payments",le)},function(e,t,n){"use strict";n.r(t);var r=n(20),a=n(1),o=n(33),c={element:{type:"string",enum:["a","button","input"]},saveInPostContent:{type:"boolean",default:!1},uniqueId:{type:"string"},passthroughAttributes:{type:"object"},text:{type:"string"},placeholder:{type:"string"},url:{type:"string"},textColor:{type:"string"},customTextColor:{type:"string",validator:o.a},backgroundColor:{type:"string"},customBackgroundColor:{type:"string",validator:o.a},gradient:{type:"string"},customGradient:{type:"string"},borderRadius:{type:"number"}},i=n(9),l=n.n(i),s=n(3),u=n.n(s),p=n(0),d=n(7),m=n.n(d),b=n(5),h=n(18),f=n(4),g=n(2),j=Object(g.withFallbackStyles)((function(e,t){var n=t.backgroundColor,r=t.textColor,a=Object(f.get)(n,"color"),o=!Object(f.get)(r,"color")&&e?e.querySelector('[contenteditable="true"]'):null;return{fallbackBackgroundColor:a||!e?void 0:getComputedStyle(e).backgroundColor,fallbackTextColor:r||!o?void 0:getComputedStyle(o).color}})),v=!!b.__experimentalUseGradient;function _(e){var t=e.borderRadius,n=void 0===t?"":t,r=e.setAttributes,o=Object(p.useCallback)((function(e){return r({borderRadius:e})}),[r]);return Object(p.createElement)(g.PanelBody,{title:Object(a.__)("Border Settings","jetpack")},Object(p.createElement)(g.RangeControl,{allowReset:!0,initialPosition:5,label:Object(a.__)("Border radius","jetpack"),max:50,min:0,onChange:o,value:n}))}function k(e){var t=e.backgroundColor,n=e.fallbackBackgroundColor,r=e.fallbackTextColor,o=e.gradientValue,c=e.setBackgroundColor,i=e.setGradient,l=e.setTextColor,s=e.textColor,u=Object(p.createElement)(b.ContrastChecker,{backgroundColor:t.color,fallbackBackgroundColor:n,fallbackTextColor:r,isLargeText:!1,textColor:s.color});return v?Object(p.createElement)(b.__experimentalPanelColorGradientSettings,{settings:[{colorValue:s.color,label:Object(a.__)("Text Color","jetpack"),onColorChange:l},{colorValue:t.color,gradientValue:o,label:Object(a.__)("Background","jetpack"),onColorChange:c,onGradientChange:i}],title:Object(a.__)("Background & Text Color","jetpack")},u):Object(p.createElement)(b.PanelColorSettings,{colorSettings:[{value:s.color,onChange:l,label:Object(a.__)("Text Color","jetpack")},{value:t.color,onChange:c,label:Object(a.__)("Background","jetpack")}],title:Object(a.__)("Background & Text Color","jetpack")},u)}var y=n(10);n(207);var O=Object(h.compose)(Object(b.withColors)({backgroundColor:"background-color"},{textColor:"color"}),j)((function(e){var t,n=e.attributes,r=e.backgroundColor,o=e.className,c=e.clientId,i=e.fallbackBackgroundColor,s=e.fallbackTextColor,d=e.setAttributes,h=e.setBackgroundColor,g=e.setTextColor,j=e.textColor,O=n.borderRadius,w=n.element,E=n.placeholder,C=n.text;!function(e){var t=e.attributes,n=e.clientId,r=e.setAttributes,a=t.passthroughAttributes,o=Object(y.useSelect)((function(e){var r=e("core/block-editor"),o=(0,r.getBlockAttributes)((0,r.getBlockHierarchyRootClientId)(n)),c=Object(f.mapValues)(a,(function(e){return o[e]}));return{attributesToSync:Object(f.pickBy)(c,(function(e,n){return e!==t[n]}))}})).attributesToSync;Object(p.useEffect)((function(){Object(f.isEmpty)(o)||r(o)}),[o,r])}({attributes:n,clientId:c,setAttributes:d});var x=v?Object(b.__experimentalUseGradient)({gradientAttribute:"gradient",customGradientAttribute:"customGradient"}):{},S=x.gradientClass,A=x.gradientValue,N=x.setGradient,P=m()("wp-block-button",o),T=m()("wp-block-button__link",(t={"has-background":r.color||A},u()(t,r.class,!A&&r.class),u()(t,"has-text-color",j.color),u()(t,j.class,j.class),u()(t,S,S),u()(t,"no-border-radius",0===O),t)),B=l()({},!r.color&&A?{background:A}:{backgroundColor:r.color},{color:j.color,borderRadius:O?O+"px":void 0});return Object(p.createElement)("div",{className:P},Object(p.createElement)(b.RichText,{allowedFormats:"input"===w?[]:void 0,className:T,disableLineBreaks:"input"===w,onChange:function(e){var t="input"===w?e.replace(/ /gim," "):e;d({text:t})},placeholder:E||Object(a.__)("Add text…","jetpack"),style:B,value:C,withoutInteractiveFormatting:!0}),Object(p.createElement)(b.InspectorControls,null,Object(p.createElement)(k,{backgroundColor:r,fallbackBackgroundColor:i,fallbackTextColor:s,gradientValue:A,setBackgroundColor:h,setGradient:N,setTextColor:g,textColor:j}),Object(p.createElement)(_,{borderRadius:O,setAttributes:d})))})),w=Object(p.createElement)(g.SVG,{viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},Object(p.createElement)(g.Path,{d:"M19 6.5H5c-1.1 0-2 .9-2 2v7c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-7c0-1.1-.9-2-2-2zm.5 9c0 .3-.2.5-.5.5H5c-.3 0-.5-.2-.5-.5v-7c0-.3.2-.5.5-.5h14c.3 0 .5.2.5.5v7zM8 13h8v-1.5H8V13z"}));var E=n(53),C={title:Object(a.__)("Button","jetpack"),icon:w,category:Object(E.a)("design","layout"),keywords:[],supports:{html:!1,inserter:!1,align:["left","center","right"]},styles:[{name:"fill",label:Object(a.__)("Fill","jetpack"),isDefault:!0},{name:"outline",label:Object(a.__)("Outline","jetpack")}],attributes:c,edit:O,save:function(e){var t,n=e.attributes,r=e.blockName,a=e.uniqueId,o=n.backgroundColor,c=n.borderRadius,i=n.className,l=n.customBackgroundColor,s=n.customGradient,d=n.customTextColor,h=n.gradient,f=n.saveInPostContent,g=n.text,j=n.textColor,_=n.url;if(!f)return null;var k=Object(b.getColorClassName)("background-color",o),y=v?Object(b.__experimentalGetGradientClass)(h):void 0,O=Object(b.getColorClassName)("color",j),w=m()("wp-block-button","jetpack-submit-button",i,u()({},"wp-block-jetpack-".concat(r),r)),E=m()("wp-block-button__link",(t={"has-text-color":j||d},u()(t,O,O),u()(t,"has-background",o||h||l||s),u()(t,k,k),u()(t,y,y),u()(t,"no-border-radius",0===c),t)),C={background:s||void 0,backgroundColor:k||s||h?void 0:l,color:O?void 0:d,borderRadius:c?c+"px":void 0};return Object(p.createElement)("div",{className:w},Object(p.createElement)(b.RichText.Content,{className:E,"data-id-attr":a||"placeholder",href:_,id:a,rel:"noopener noreferrer",role:"button",style:C,tagName:"a",target:"_blank",value:g}))}};Object(r.a)("button",C)},function(e,t,n){"use strict";n.r(t);var r=n(20),a=n(0),o=n(1),c=n(16),i=n(10),l=n(4),s=[{value:"en-US",label:"English"},{value:"fr-CA",label:"Français"},{value:"de-DE",label:"Deutsch"},{value:"es-MX",label:"Español"},{value:"ja-JP",label:"日本語"},{value:"nl-NL",label:"Nederlands"},{value:"it-IT",label:"Italiano"}],u=s.map((function(e){return e.value})),p={name:"button",label:Object(o.__)("Button (210 x 113 pixels)","jetpack")},d=function(e){return Object(l.compact)([{name:"standard",label:Object(o.__)("Standard (224 x 301 pixels)","jetpack"),isDefault:!0},{name:"tall",label:Object(o.__)("Tall (288 x 490 pixels)","jetpack")},{name:"wide",label:Object(o.__)("Wide (840 x 150 pixels)","jetpack")},(!e||1===e.length)&&p])},m=function(e){return d(e).map((function(e){return e.name}))},b=Object(i.select)("core/block-editor").getSettings().siteLocale,h=!Object(l.isEmpty)(b)&&u.includes(b)?b:"en-US",f={rid:{default:[],type:"array"},style:{default:"standard",type:"string",validValues:m()},iframe:{default:!0,type:"boolean"},domain:{default:"com",type:"string"},lang:{default:h,type:"string",validValues:u},newtab:{default:!1,type:"boolean"},negativeMargin:{default:!1,type:"boolean"}},g={attributes:f,supports:{align:!0,html:!1},save:function(e){var t=e.attributes.rid;return Object(a.createElement)(a.Fragment,null,t.map((function(e){return Object(a.createElement)("a",{href:"https://www.opentable.com/restref/client/?rid=".concat(e)},"https://www.opentable.com/restref/client/?rid=".concat(e))})))}},j=n(9),v=n.n(j),_={attributes:f,migrate:function(e){var t=e.style,n=e.className,r="standard"===t?"":"is-style-".concat(t);return v()({},e,{className:n?"".concat(n," ").concat(r):r})},isEligible:function(e){var t=e.style,n=e.className;return!(!t||"standard"===t)&&(!n||-1===n.indexOf("is-style-"))},save:function(e){var t=e.attributes.rid;return Object(a.createElement)("div",null,t.map((function(e){return Object(a.createElement)("a",{href:"https://www.opentable.com/restref/client/?rid=".concat(e)},"https://www.opentable.com/restref/client/?rid=".concat(e))})))}},k=n(3),y=n.n(k),O=n(8),w=n.n(O),E=(n(126),n(7)),C=n.n(E),x=n(5),S=n(2),A=(n(136),Object(a.createElement)(S.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 22 16",fill:"none",height:"16",width:"22"},Object(a.createElement)(S.Path,{d:"m1.997 5.982c-.39457-.00039-.7804.11622-1.108699.33511-.328295.21888-.584312.5302-.735674.89459-.15136174.36439-.1912714.76548-.1146819 1.15254.0765899.38707.2662379.74274.5449639 1.02202.278726.27929.634011.46965 1.020921.54702.38692.07732.78809.03826 1.15278-.11238.36469-.15063.67652-.40602.89606-.73387.21954-.32786.33693-.71345.33733-1.10803v-.002c.001-1.1-.89-1.994-1.992-1.995zm12.006 3.988c-.3946.0004-.7805-.11625-1.1088-.33517-.3283-.21893-.5843-.53031-.7357-.89476-.1513-.36444-.1912-.76558-.1145-1.15268s.2664-.74276.5453-1.022c.2788-.27925.6342-.46953 1.0211-.54679.387-.07725.7882-.038 1.1529.11278.3647.15079.6764.40634.8959.73432.2194.32799.3366.71369.3368 1.1083v.003c.0003.52814-.2092 1.03477-.5824 1.4085s-.8795.58397-1.4076.5845zm0-9.96999843c-1.5777-.0009886-3.1203.46588743-4.43262 1.34158843-1.31236.8757-2.33558 2.1209-2.94025 3.57813-.60467 1.45722-.76365 3.06103-.45683 4.60861.30683 1.54757 1.06567 2.96947 2.18058 4.08577 1.1149 1.1163 2.53582 1.8769 4.08302 2.1856 1.5472.3088 3.1512.1518 4.6091-.451 1.458-.6028 2.7045-1.6245 3.5819-2.9358.8773-1.3112 1.3461-2.8532 1.3471-4.4309v-.005c.0008-2.11466-.8384-4.14304-2.3331-5.63899-1.4946-1.495952-3.5222-2.3369478-5.6369-2.33800843z"}))),N=n(24),P=n(23),T=n.n(P),B=/^\s*(http[s]?:\/\/|\
+ ' . esc_html__( 'Every Jetpack site needs at least one connected admin for the features to work properly. Please connect to your WordPress.com account via the button below. Once you connect, you may refresh this page to see an option to change the connection owner.', 'jetpack' ) . '
';
+ $connect_url = \Jetpack::init()->build_connect_url( false, false, 'delete_connection_owner_notice' );
+ echo "" . esc_html__( 'Connect to WordPress.com', 'jetpack' ) . ' ';
+ }
+
+ echo '';
+ printf(
+ wp_kses(
+ /* translators: URL to Jetpack support doc regarding the primary user. */
+ __( "Learn more about the connection owner and what will break if you do not have one.", 'jetpack' ),
+ array(
+ 'a' => array(
+ 'href' => true,
+ 'target' => true,
+ 'rel' => true,
+ ),
+ )
+ ),
+ esc_url( Redirect::get_url( 'jetpack-support-primary-user' ) )
+ );
+ echo '
';
+ echo '';
+ printf(
+ wp_kses(
+ /* translators: URL to contact Jetpack support. */
+ __( 'As always, feel free to contact our support team if you have any questions.', 'jetpack' ),
+ array(
+ 'a' => array(
+ 'href' => true,
+ 'target' => true,
+ 'rel' => true,
+ ),
+ )
+ ),
+ esc_url( Redirect::get_url( 'jetpack-contact-support' ) )
+ );
+ echo '
';
+ echo '';
+ }
+
+ /**
+ * Dismisses a JITM feature class so that it will no longer be shown.
+ *
+ * @param string $id The id of the JITM that was dismissed.
+ * @param string $feature_class The feature class of the JITM that was dismissed.
+ *
+ * @return bool Always true.
+ */
+ public function dismiss( $id, $feature_class ) {
+ $this->tracking->record_user_event(
+ 'jitm_dismiss_client',
+ array(
+ 'jitm_id' => $id,
+ 'feature_class' => $feature_class,
+ )
+ );
+ $this->save_dismiss( $feature_class );
+ return true;
+ }
+
+ /**
+ * Asks the wpcom API for the current message to display keyed on query string and message path
+ *
+ * @param string $message_path The message path to ask for.
+ * @param string $query The query string originally from the front end.
+ * @param bool $full_jp_logo_exists If there is a full Jetpack logo already on the page.
+ *
+ * @return array The JITM's to show, or an empty array if there is nothing to show
+ */
+ public function get_messages( $message_path, $query, $full_jp_logo_exists ) {
+ // WooCommerce Services.
+ add_filter( 'jitm_woocommerce_services_msg', array( $this, 'jitm_woocommerce_services_msg' ) );
+ add_filter( 'jitm_jetpack_woo_services_install', array( $this, 'jitm_jetpack_woo_services_install' ) );
+ add_filter( 'jitm_jetpack_woo_services_activate', array( $this, 'jitm_jetpack_woo_services_activate' ) );
+
+ // Creative Mail.
+ add_filter( 'jitm_jetpack_creative_mail_install', array( $this, 'jitm_jetpack_creative_mail_install' ) );
+ add_filter( 'jitm_jetpack_creative_mail_activate', array( $this, 'jitm_jetpack_creative_mail_activate' ) );
+
+ $user = wp_get_current_user();
+
+ // Unauthenticated or invalid requests just bail.
+ if ( ! $user ) {
+ return array();
+ }
+
+ $user_roles = implode( ',', $user->roles );
+ $site_id = \Jetpack_Options::get_option( 'id' );
+
+ // Build our jitm request.
+ $path = add_query_arg(
+ array(
+ 'external_user_id' => urlencode_deep( $user->ID ),
+ 'user_roles' => urlencode_deep( $user_roles ),
+ 'query_string' => urlencode_deep( $query ),
+ 'mobile_browser' => jetpack_is_mobile( 'smart' ) ? 1 : 0,
+ '_locale' => get_user_locale(),
+ ),
+ sprintf( '/sites/%d/jitm/%s', $site_id, $message_path )
+ );
+
+ // Attempt to get from cache.
+ $envelopes = get_transient( 'jetpack_jitm_' . substr( md5( $path ), 0, 31 ) );
+
+ // If something is in the cache and it was put in the cache after the last sync we care about, use it.
+ $use_cache = false;
+
+ /** This filter is documented in class.jetpack.php */
+ if ( apply_filters( 'jetpack_just_in_time_msg_cache', false ) ) {
+ $use_cache = true;
+ }
+
+ if ( $use_cache ) {
+ $last_sync = (int) get_transient( 'jetpack_last_plugin_sync' );
+ $from_cache = $envelopes && $last_sync > 0 && $last_sync < $envelopes['last_response_time'];
+ } else {
+ $from_cache = false;
+ }
+
+ // Otherwise, ask again.
+ if ( ! $from_cache ) {
+ $wpcom_response = Client::wpcom_json_api_request_as_blog(
+ $path,
+ '2',
+ array(
+ 'user_id' => $user->ID,
+ 'user_roles' => implode( ',', $user->roles ),
+ ),
+ null,
+ 'wpcom'
+ );
+
+ // silently fail...might be helpful to track it?
+ if ( is_wp_error( $wpcom_response ) ) {
+ return array();
+ }
+
+ $envelopes = json_decode( $wpcom_response['body'] );
+
+ if ( ! is_array( $envelopes ) ) {
+ return array();
+ }
+
+ $expiration = isset( $envelopes[0] ) ? $envelopes[0]->ttl : 300;
+
+ // Do not cache if expiration is 0 or we're not using the cache.
+ if ( 0 !== $expiration && $use_cache ) {
+ $envelopes['last_response_time'] = time();
+
+ set_transient( 'jetpack_jitm_' . substr( md5( $path ), 0, 31 ), $envelopes, $expiration );
+ }
+ }
+
+ $hidden_jitms = \Jetpack_Options::get_option( 'hide_jitm' );
+ unset( $envelopes['last_response_time'] );
+
+ /**
+ * Allow adding your own custom JITMs after a set of JITMs has been received.
+ *
+ * @since 6.9.0
+ * @since 8.3.0 - Added Message path.
+ *
+ * @param array $envelopes array of existing JITMs.
+ * @param string $message_path The message path to ask for.
+ */
+ $envelopes = apply_filters( 'jetpack_jitm_received_envelopes', $envelopes, $message_path );
+
+ foreach ( $envelopes as $idx => &$envelope ) {
+
+ $dismissed_feature = isset( $hidden_jitms[ $envelope->feature_class ] ) && is_array( $hidden_jitms[ $envelope->feature_class ] ) ? $hidden_jitms[ $envelope->feature_class ] : null;
+
+ // If the this feature class has been dismissed and the request has not passed the ttl, skip it as it's been dismissed.
+ if ( is_array( $dismissed_feature ) && ( time() - $dismissed_feature['last_dismissal'] < $envelope->expires || $dismissed_feature['number'] >= $envelope->max_dismissal ) ) {
+ unset( $envelopes[ $idx ] );
+ continue;
+ }
+
+ $this->tracking->record_user_event(
+ 'jitm_view_client',
+ array(
+ 'jitm_id' => $envelope->id,
+ )
+ );
+
+ $url_params = array(
+ 'source' => "jitm-$envelope->id",
+ 'site' => ( new Status() )->get_site_suffix(),
+ 'u' => $user->ID,
+ );
+
+ // Get affiliate code and add it to the array of URL parameters.
+ $aff = Partner::init()->get_partner_code( Partner::AFFILIATE_CODE );
+ if ( '' !== $aff ) {
+ $url_params['aff'] = $aff;
+ }
+
+ $envelope->url = add_query_arg( $url_params, 'https://jetpack.com/redirect/' );
+
+ $envelope->jitm_stats_url = \Jetpack::build_stats_url( array( 'x_jetpack-jitm' => $envelope->id ) );
+
+ // phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
+ // $CTA is not valid per PHPCS, but it is part of the return from WordPress.com, so allowing.
+ if ( $envelope->CTA->hook ) {
+ $envelope->url = apply_filters( 'jitm_' . $envelope->CTA->hook, $envelope->url );
+ unset( $envelope->CTA->hook );
+ }
+ // phpcs:enable
+
+ if ( isset( $envelope->content->hook ) ) {
+ $envelope->content = apply_filters( 'jitm_' . $envelope->content->hook, $envelope->content );
+ unset( $envelope->content->hook );
+ }
+
+ // No point in showing an empty message.
+ if ( empty( $envelope->content->message ) ) {
+ unset( $envelopes[ $idx ] );
+ continue;
+ }
+
+ $envelope->content->icon = $this->generate_icon( $envelope->content->icon, $full_jp_logo_exists );
+
+ $jetpack = \Jetpack::init();
+ $jetpack->stat( 'jitm', $envelope->id . '-viewed-' . JETPACK__VERSION );
+ $jetpack->do_stats( 'server_side' );
+ }
+
+ return $envelopes;
+ }
+
+}
diff --git a/vendor/automattic/jetpack-jitm/src/class-pre-connection-jitm.php b/vendor/automattic/jetpack-jitm/src/class-pre-connection-jitm.php
new file mode 100644
index 0000000000000..802ca31d45cf8
--- /dev/null
+++ b/vendor/automattic/jetpack-jitm/src/class-pre-connection-jitm.php
@@ -0,0 +1,163 @@
+generate_admin_url(
+ array(
+ 'page' => 'jetpack',
+ '#/setup' => '',
+ )
+ );
+
+ return array(
+ array(
+ 'id' => 'jpsetup-posts',
+ 'message_path' => '/wp:edit-post:admin_notices/',
+ 'message' => __( 'Do you know which of these posts gets the most traffic?', 'jetpack' ),
+ 'description' => __( 'Set up Jetpack to get in-depth stats about your content and visitors.', 'jetpack' ),
+ 'button_link' => $jetpack_setup_url,
+ 'button_caption' => __( 'Set up Jetpack', 'jetpack' ),
+ ),
+ array(
+ 'id' => 'jpsetup-upload',
+ 'message_path' => '/wp:upload:admin_notices/',
+ 'message' => __( 'Do you want lightning-fast images?', 'jetpack' ),
+ 'description' => __( 'Set up Jetpack, enable Site Accelerator, and start serving your images lightning fast, for free.', 'jetpack' ),
+ 'button_link' => $jetpack_setup_url,
+ 'button_caption' => __( 'Set up Jetpack', 'jetpack' ),
+ ),
+ array(
+ 'id' => 'jpsetup-widgets',
+ 'message_path' => '/wp:widgets:admin_notices/',
+ 'message' => __( 'Looking for even more widgets?', 'jetpack' ),
+ 'description' => __( 'Set up Jetpack for great additional widgets that display business contact info and maps, blog stats, and top posts.', 'jetpack' ),
+ 'button_link' => $jetpack_setup_url,
+ 'button_caption' => __( 'Set up Jetpack', 'jetpack' ),
+ ),
+ );
+ }
+
+ /**
+ * Adds the input query arguments to the admin url.
+ *
+ * @param array $args The query arguments.
+ *
+ * @return string The admin url.
+ */
+ private function generate_admin_url( $args ) {
+ $args = wp_parse_args( $args );
+ $url = add_query_arg( $args, admin_url( 'admin.php' ) );
+ return $url;
+ }
+
+ /**
+ * Filters and formats the messages for the client-side JS renderer
+ *
+ * @param string $message_path Current message path.
+ *
+ * @return array Formatted messages.
+ */
+ private function filter_messages( $message_path ) {
+ $messages = $this->get_raw_messages();
+
+ $formatted_messages = array();
+
+ foreach ( $messages as $message ) {
+ if ( ! preg_match( $message['message_path'], $message_path ) ) {
+ continue;
+ }
+
+ if ( 'jpsetup-posts' === $message['id'] && wp_count_posts()->publish < 5 ) {
+ continue;
+ }
+
+ $obj = new \stdClass();
+ $obj->CTA = array( // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
+ 'message' => $message['button_caption'],
+ 'newWindow' => false,
+ );
+ $obj->url = $message['button_link'];
+ $obj->id = $message['id'];
+ $obj->is_dismissible = true;
+ $obj->content = array(
+ 'message' => $message['message'],
+ 'description' => $message['description'],
+ 'list' => array(),
+ 'icon' => 'jetpack',
+ );
+
+ $formatted_messages[] = $obj;
+ }
+
+ return $formatted_messages;
+ }
+
+ /**
+ * Retrieve the current message to display keyed on query string and message path
+ *
+ * @param string $message_path The message path to ask for.
+ * @param string $query The query string originally from the front end. Unused in this subclass.
+ * @param bool $full_jp_logo_exists If there is a full Jetpack logo already on the page.
+ *
+ * @return array The JITMs to show, or an empty array if there is nothing to show
+ */
+ public function get_messages( $message_path, $query, $full_jp_logo_exists ) {
+ /** This filter is documented in class.jetpack-connection-banner.php */
+ if ( ! apply_filters( 'jetpack_pre_connection_prompt_helpers', false ) ) {
+ // If filter jetpack_pre_connection_prompt_helpers is not set, return an empty array.
+ return array();
+ }
+
+ if ( ! current_user_can( 'install_plugins' ) ) {
+ return array();
+ }
+
+ $messages = $this->filter_messages( $message_path );
+
+ if ( empty( $messages ) ) {
+ return array();
+ }
+
+ $hidden_jitms = \Jetpack_Options::get_option( 'hide_jitm' );
+
+ foreach ( $messages as $idx => &$envelope ) {
+ $dismissed_feature = isset( $hidden_jitms[ 'pre-connection-' . $envelope->id ] ) &&
+ is_array( $hidden_jitms[ 'pre-connection-' . $envelope->id ] ) ? $hidden_jitms[ 'pre-connection-' . $envelope->id ] : null;
+
+ if ( is_array( $dismissed_feature ) ) {
+ unset( $messages[ $idx ] );
+ continue;
+ }
+
+ $envelope->content['icon'] = $this->generate_icon( $envelope->content['icon'], $full_jp_logo_exists );
+ }
+
+ return $messages;
+ }
+
+ /**
+ * Dismisses a JITM ID so that it will no longer be shown.
+ *
+ * @param string $id The id of the JITM that was dismissed.
+ *
+ * @return bool Always true
+ */
+ public function dismiss( $id ) {
+ $this->save_dismiss( 'pre-connection-' . $id );
+ return true;
+ }
+}
diff --git a/vendor/automattic/jetpack-lazy-images/src/images/1x1.trans.gif b/vendor/automattic/jetpack-lazy-images/src/images/1x1.trans.gif
new file mode 100644
index 0000000000000000000000000000000000000000..f191b280ce91e6cb8c387735c10ef9bc5da6c83b
GIT binary patch
literal 42
ocmZ?wbhEHbWMp7uXkY+=|Ns9h{$ybUF?B!$NQQxl(S^Yp0J!f4_W%F@
literal 0
HcmV?d00001
diff --git a/vendor/automattic/jetpack-lazy-images/src/js/intersectionobserver-polyfill.js b/vendor/automattic/jetpack-lazy-images/src/js/intersectionobserver-polyfill.js
new file mode 100644
index 0000000000000..0b3eba1e92de5
--- /dev/null
+++ b/vendor/automattic/jetpack-lazy-images/src/js/intersectionobserver-polyfill.js
@@ -0,0 +1,730 @@
+/**
+ * The following is an Intersection observer polyfill which is licensed under
+ * the W3C SOFTWARE AND DOCUMENT NOTICE AND LICENSE and can be found at:
+ * https://github.com/w3c/IntersectionObserver/tree/master/polyfill
+ */
+
+/**
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the W3C SOFTWARE AND DOCUMENT NOTICE AND LICENSE.
+ *
+ * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
+ *
+ */
+
+(function(window, document) {
+ 'use strict';
+
+
+ // Exits early if all IntersectionObserver and IntersectionObserverEntry
+ // features are natively supported.
+ if ('IntersectionObserver' in window &&
+ 'IntersectionObserverEntry' in window &&
+ 'intersectionRatio' in window.IntersectionObserverEntry.prototype) {
+
+ // Minimal polyfill for Edge 15's lack of `isIntersecting`
+ // See: https://github.com/w3c/IntersectionObserver/issues/211
+ if (!('isIntersecting' in window.IntersectionObserverEntry.prototype)) {
+ Object.defineProperty(window.IntersectionObserverEntry.prototype,
+ 'isIntersecting', {
+ get: function () {
+ return this.intersectionRatio > 0;
+ }
+ });
+ }
+ return;
+ }
+
+
+ /**
+ * An IntersectionObserver registry. This registry exists to hold a strong
+ * reference to IntersectionObserver instances currently observering a target
+ * element. Without this registry, instances without another reference may be
+ * garbage collected.
+ */
+ var registry = [];
+
+
+ /**
+ * Creates the global IntersectionObserverEntry constructor.
+ * https://w3c.github.io/IntersectionObserver/#intersection-observer-entry
+ * @param {Object} entry A dictionary of instance properties.
+ * @constructor
+ */
+ function IntersectionObserverEntry(entry) {
+ this.time = entry.time;
+ this.target = entry.target;
+ this.rootBounds = entry.rootBounds;
+ this.boundingClientRect = entry.boundingClientRect;
+ this.intersectionRect = entry.intersectionRect || getEmptyRect();
+ this.isIntersecting = !!entry.intersectionRect;
+
+ // Calculates the intersection ratio.
+ var targetRect = this.boundingClientRect;
+ var targetArea = targetRect.width * targetRect.height;
+ var intersectionRect = this.intersectionRect;
+ var intersectionArea = intersectionRect.width * intersectionRect.height;
+
+ // Sets intersection ratio.
+ if (targetArea) {
+ this.intersectionRatio = intersectionArea / targetArea;
+ } else {
+ // If area is zero and is intersecting, sets to 1, otherwise to 0
+ this.intersectionRatio = this.isIntersecting ? 1 : 0;
+ }
+ }
+
+
+ /**
+ * Creates the global IntersectionObserver constructor.
+ * https://w3c.github.io/IntersectionObserver/#intersection-observer-interface
+ * @param {Function} callback The function to be invoked after intersection
+ * changes have queued. The function is not invoked if the queue has
+ * been emptied by calling the `takeRecords` method.
+ * @param {Object=} opt_options Optional configuration options.
+ * @constructor
+ */
+ function IntersectionObserver(callback, opt_options) {
+
+ var options = opt_options || {};
+
+ if (typeof callback != 'function') {
+ throw new Error('callback must be a function');
+ }
+
+ if (options.root && options.root.nodeType != 1) {
+ throw new Error('root must be an Element');
+ }
+
+ // Binds and throttles `this._checkForIntersections`.
+ this._checkForIntersections = throttle(
+ this._checkForIntersections.bind(this), this.THROTTLE_TIMEOUT);
+
+ // Private properties.
+ this._callback = callback;
+ this._observationTargets = [];
+ this._queuedEntries = [];
+ this._rootMarginValues = this._parseRootMargin(options.rootMargin);
+
+ // Public properties.
+ this.thresholds = this._initThresholds(options.threshold);
+ this.root = options.root || null;
+ this.rootMargin = this._rootMarginValues.map(function(margin) {
+ return margin.value + margin.unit;
+ }).join(' ');
+ }
+
+
+ /**
+ * The minimum interval within which the document will be checked for
+ * intersection changes.
+ */
+ IntersectionObserver.prototype.THROTTLE_TIMEOUT = 100;
+
+
+ /**
+ * The frequency in which the polyfill polls for intersection changes.
+ * this can be updated on a per instance basis and must be set prior to
+ * calling `observe` on the first target.
+ */
+ IntersectionObserver.prototype.POLL_INTERVAL = null;
+
+ /**
+ * Use a mutation observer on the root element
+ * to detect intersection changes.
+ */
+ IntersectionObserver.prototype.USE_MUTATION_OBSERVER = true;
+
+
+ /**
+ * Starts observing a target element for intersection changes based on
+ * the thresholds values.
+ * @param {Element} target The DOM element to observe.
+ */
+ IntersectionObserver.prototype.observe = function(target) {
+ var isTargetAlreadyObserved = this._observationTargets.some(function(item) {
+ return item.element == target;
+ });
+
+ if (isTargetAlreadyObserved) {
+ return;
+ }
+
+ if (!(target && target.nodeType == 1)) {
+ throw new Error('target must be an Element');
+ }
+
+ this._registerInstance();
+ this._observationTargets.push({element: target, entry: null});
+ this._monitorIntersections();
+ this._checkForIntersections();
+ };
+
+
+ /**
+ * Stops observing a target element for intersection changes.
+ * @param {Element} target The DOM element to observe.
+ */
+ IntersectionObserver.prototype.unobserve = function(target) {
+ this._observationTargets =
+ this._observationTargets.filter(function(item) {
+
+ return item.element != target;
+ });
+ if (!this._observationTargets.length) {
+ this._unmonitorIntersections();
+ this._unregisterInstance();
+ }
+ };
+
+
+ /**
+ * Stops observing all target elements for intersection changes.
+ */
+ IntersectionObserver.prototype.disconnect = function() {
+ this._observationTargets = [];
+ this._unmonitorIntersections();
+ this._unregisterInstance();
+ };
+
+
+ /**
+ * Returns any queue entries that have not yet been reported to the
+ * callback and clears the queue. This can be used in conjunction with the
+ * callback to obtain the absolute most up-to-date intersection information.
+ * @return {Array} The currently queued entries.
+ */
+ IntersectionObserver.prototype.takeRecords = function() {
+ var records = this._queuedEntries.slice();
+ this._queuedEntries = [];
+ return records;
+ };
+
+
+ /**
+ * Accepts the threshold value from the user configuration object and
+ * returns a sorted array of unique threshold values. If a value is not
+ * between 0 and 1 and error is thrown.
+ * @private
+ * @param {Array|number=} opt_threshold An optional threshold value or
+ * a list of threshold values, defaulting to [0].
+ * @return {Array} A sorted list of unique and valid threshold values.
+ */
+ IntersectionObserver.prototype._initThresholds = function(opt_threshold) {
+ var threshold = opt_threshold || [0];
+ if (!Array.isArray(threshold)) threshold = [threshold];
+
+ return threshold.sort().filter(function(t, i, a) {
+ if (typeof t != 'number' || isNaN(t) || t < 0 || t > 1) {
+ throw new Error('threshold must be a number between 0 and 1 inclusively');
+ }
+ return t !== a[i - 1];
+ });
+ };
+
+
+ /**
+ * Accepts the rootMargin value from the user configuration object
+ * and returns an array of the four margin values as an object containing
+ * the value and unit properties. If any of the values are not properly
+ * formatted or use a unit other than px or %, and error is thrown.
+ * @private
+ * @param {string=} opt_rootMargin An optional rootMargin value,
+ * defaulting to '0px'.
+ * @return {Array} An array of margin objects with the keys
+ * value and unit.
+ */
+ IntersectionObserver.prototype._parseRootMargin = function(opt_rootMargin) {
+ var marginString = opt_rootMargin || '0px';
+ var margins = marginString.split(/\s+/).map(function(margin) {
+ var parts = /^(-?\d*\.?\d+)(px|%)$/.exec(margin);
+ if (!parts) {
+ throw new Error('rootMargin must be specified in pixels or percent');
+ }
+ return {value: parseFloat(parts[1]), unit: parts[2]};
+ });
+
+ // Handles shorthand.
+ margins[1] = margins[1] || margins[0];
+ margins[2] = margins[2] || margins[0];
+ margins[3] = margins[3] || margins[1];
+
+ return margins;
+ };
+
+
+ /**
+ * Starts polling for intersection changes if the polling is not already
+ * happening, and if the page's visibility state is visible.
+ * @private
+ */
+ IntersectionObserver.prototype._monitorIntersections = function() {
+ if (!this._monitoringIntersections) {
+ this._monitoringIntersections = true;
+
+ // If a poll interval is set, use polling instead of listening to
+ // resize and scroll events or DOM mutations.
+ if (this.POLL_INTERVAL) {
+ this._monitoringInterval = setInterval(
+ this._checkForIntersections, this.POLL_INTERVAL);
+ }
+ else {
+ addEvent(window, 'resize', this._checkForIntersections, true);
+ addEvent(document, 'scroll', this._checkForIntersections, true);
+
+ if (this.USE_MUTATION_OBSERVER && 'MutationObserver' in window) {
+ this._domObserver = new MutationObserver(this._checkForIntersections);
+ this._domObserver.observe(document, {
+ attributes: true,
+ childList: true,
+ characterData: true,
+ subtree: true
+ });
+ }
+ }
+ }
+ };
+
+
+ /**
+ * Stops polling for intersection changes.
+ * @private
+ */
+ IntersectionObserver.prototype._unmonitorIntersections = function() {
+ if (this._monitoringIntersections) {
+ this._monitoringIntersections = false;
+
+ clearInterval(this._monitoringInterval);
+ this._monitoringInterval = null;
+
+ removeEvent(window, 'resize', this._checkForIntersections, true);
+ removeEvent(document, 'scroll', this._checkForIntersections, true);
+
+ if (this._domObserver) {
+ this._domObserver.disconnect();
+ this._domObserver = null;
+ }
+ }
+ };
+
+
+ /**
+ * Scans each observation target for intersection changes and adds them
+ * to the internal entries queue. If new entries are found, it
+ * schedules the callback to be invoked.
+ * @private
+ */
+ IntersectionObserver.prototype._checkForIntersections = function() {
+ var rootIsInDom = this._rootIsInDom();
+ var rootRect = rootIsInDom ? this._getRootRect() : getEmptyRect();
+
+ this._observationTargets.forEach(function(item) {
+ var target = item.element;
+ var targetRect = getBoundingClientRect(target);
+ var rootContainsTarget = this._rootContainsTarget(target);
+ var oldEntry = item.entry;
+ var intersectionRect = rootIsInDom && rootContainsTarget &&
+ this._computeTargetAndRootIntersection(target, rootRect);
+
+ var newEntry = item.entry = new IntersectionObserverEntry({
+ time: now(),
+ target: target,
+ boundingClientRect: targetRect,
+ rootBounds: rootRect,
+ intersectionRect: intersectionRect
+ });
+
+ if (!oldEntry) {
+ this._queuedEntries.push(newEntry);
+ } else if (rootIsInDom && rootContainsTarget) {
+ // If the new entry intersection ratio has crossed any of the
+ // thresholds, add a new entry.
+ if (this._hasCrossedThreshold(oldEntry, newEntry)) {
+ this._queuedEntries.push(newEntry);
+ }
+ } else {
+ // If the root is not in the DOM or target is not contained within
+ // root but the previous entry for this target had an intersection,
+ // add a new record indicating removal.
+ if (oldEntry && oldEntry.isIntersecting) {
+ this._queuedEntries.push(newEntry);
+ }
+ }
+ }, this);
+
+ if (this._queuedEntries.length) {
+ this._callback(this.takeRecords(), this);
+ }
+ };
+
+
+ /**
+ * Accepts a target and root rect computes the intersection between then
+ * following the algorithm in the spec.
+ * TODO(philipwalton): at this time clip-path is not considered.
+ * https://w3c.github.io/IntersectionObserver/#calculate-intersection-rect-algo
+ * @param {Element} target The target DOM element
+ * @param {Object} rootRect The bounding rect of the root after being
+ * expanded by the rootMargin value.
+ * @return {?Object} The final intersection rect object or undefined if no
+ * intersection is found.
+ * @private
+ */
+ IntersectionObserver.prototype._computeTargetAndRootIntersection =
+ function(target, rootRect) {
+
+ // If the element isn't displayed, an intersection can't happen.
+ if (window.getComputedStyle(target).display == 'none') return;
+
+ var targetRect = getBoundingClientRect(target);
+ var intersectionRect = targetRect;
+ var parent = getParentNode(target);
+ var atRoot = false;
+
+ while (!atRoot) {
+ var parentRect = null;
+ var parentComputedStyle = parent.nodeType == 1 ?
+ window.getComputedStyle(parent) : {};
+
+ // If the parent isn't displayed, an intersection can't happen.
+ if (parentComputedStyle.display == 'none') return;
+
+ if (parent == this.root || parent == document) {
+ atRoot = true;
+ parentRect = rootRect;
+ } else {
+ // If the element has a non-visible overflow, and it's not the
+ // or element, update the intersection rect.
+ // Note: and cannot be clipped to a rect that's not also
+ // the document rect, so no need to compute a new intersection.
+ if (parent != document.body &&
+ parent != document.documentElement &&
+ parentComputedStyle.overflow != 'visible') {
+ parentRect = getBoundingClientRect(parent);
+ }
+ }
+
+ // If either of the above conditionals set a new parentRect,
+ // calculate new intersection data.
+ if (parentRect) {
+ intersectionRect = computeRectIntersection(parentRect, intersectionRect);
+
+ if (!intersectionRect) break;
+ }
+ parent = getParentNode(parent);
+ }
+ return intersectionRect;
+ };
+
+
+ /**
+ * Returns the root rect after being expanded by the rootMargin value.
+ * @return {Object} The expanded root rect.
+ * @private
+ */
+ IntersectionObserver.prototype._getRootRect = function() {
+ var rootRect;
+ if (this.root) {
+ rootRect = getBoundingClientRect(this.root);
+ } else {
+ // Use / instead of window since scroll bars affect size.
+ var html = document.documentElement;
+ var body = document.body;
+ rootRect = {
+ top: 0,
+ left: 0,
+ right: html.clientWidth || body.clientWidth,
+ width: html.clientWidth || body.clientWidth,
+ bottom: html.clientHeight || body.clientHeight,
+ height: html.clientHeight || body.clientHeight
+ };
+ }
+ return this._expandRectByRootMargin(rootRect);
+ };
+
+
+ /**
+ * Accepts a rect and expands it by the rootMargin value.
+ * @param {Object} rect The rect object to expand.
+ * @return {Object} The expanded rect.
+ * @private
+ */
+ IntersectionObserver.prototype._expandRectByRootMargin = function(rect) {
+ var margins = this._rootMarginValues.map(function(margin, i) {
+ return margin.unit == 'px' ? margin.value :
+ margin.value * (i % 2 ? rect.width : rect.height) / 100;
+ });
+ var newRect = {
+ top: rect.top - margins[0],
+ right: rect.right + margins[1],
+ bottom: rect.bottom + margins[2],
+ left: rect.left - margins[3]
+ };
+ newRect.width = newRect.right - newRect.left;
+ newRect.height = newRect.bottom - newRect.top;
+
+ return newRect;
+ };
+
+
+ /**
+ * Accepts an old and new entry and returns true if at least one of the
+ * threshold values has been crossed.
+ * @param {?IntersectionObserverEntry} oldEntry The previous entry for a
+ * particular target element or null if no previous entry exists.
+ * @param {IntersectionObserverEntry} newEntry The current entry for a
+ * particular target element.
+ * @return {boolean} Returns true if a any threshold has been crossed.
+ * @private
+ */
+ IntersectionObserver.prototype._hasCrossedThreshold =
+ function(oldEntry, newEntry) {
+
+ // To make comparing easier, an entry that has a ratio of 0
+ // but does not actually intersect is given a value of -1
+ var oldRatio = oldEntry && oldEntry.isIntersecting ?
+ oldEntry.intersectionRatio || 0 : -1;
+ var newRatio = newEntry.isIntersecting ?
+ newEntry.intersectionRatio || 0 : -1;
+
+ // Ignore unchanged ratios
+ if (oldRatio === newRatio) return;
+
+ for (var i = 0; i < this.thresholds.length; i++) {
+ var threshold = this.thresholds[i];
+
+ // Return true if an entry matches a threshold or if the new ratio
+ // and the old ratio are on the opposite sides of a threshold.
+ if (threshold == oldRatio || threshold == newRatio ||
+ threshold < oldRatio !== threshold < newRatio) {
+ return true;
+ }
+ }
+ };
+
+
+ /**
+ * Returns whether or not the root element is an element and is in the DOM.
+ * @return {boolean} True if the root element is an element and is in the DOM.
+ * @private
+ */
+ IntersectionObserver.prototype._rootIsInDom = function() {
+ return !this.root || containsDeep(document, this.root);
+ };
+
+
+ /**
+ * Returns whether or not the target element is a child of root.
+ * @param {Element} target The target element to check.
+ * @return {boolean} True if the target element is a child of root.
+ * @private
+ */
+ IntersectionObserver.prototype._rootContainsTarget = function(target) {
+ return containsDeep(this.root || document, target);
+ };
+
+
+ /**
+ * Adds the instance to the global IntersectionObserver registry if it isn't
+ * already present.
+ * @private
+ */
+ IntersectionObserver.prototype._registerInstance = function() {
+ if (registry.indexOf(this) < 0) {
+ registry.push(this);
+ }
+ };
+
+
+ /**
+ * Removes the instance from the global IntersectionObserver registry.
+ * @private
+ */
+ IntersectionObserver.prototype._unregisterInstance = function() {
+ var index = registry.indexOf(this);
+ if (index != -1) registry.splice(index, 1);
+ };
+
+
+ /**
+ * Returns the result of the performance.now() method or null in browsers
+ * that don't support the API.
+ * @return {number} The elapsed time since the page was requested.
+ */
+ function now() {
+ return window.performance && performance.now && performance.now();
+ }
+
+
+ /**
+ * Throttles a function and delays its executiong, so it's only called at most
+ * once within a given time period.
+ * @param {Function} fn The function to throttle.
+ * @param {number} timeout The amount of time that must pass before the
+ * function can be called again.
+ * @return {Function} The throttled function.
+ */
+ function throttle(fn, timeout) {
+ var timer = null;
+ return function () {
+ if (!timer) {
+ timer = setTimeout(function() {
+ fn();
+ timer = null;
+ }, timeout);
+ }
+ };
+ }
+
+
+ /**
+ * Adds an event handler to a DOM node ensuring cross-browser compatibility.
+ * @param {Node} node The DOM node to add the event handler to.
+ * @param {string} event The event name.
+ * @param {Function} fn The event handler to add.
+ * @param {boolean} opt_useCapture Optionally adds the even to the capture
+ * phase. Note: this only works in modern browsers.
+ */
+ function addEvent(node, event, fn, opt_useCapture) {
+ if (typeof node.addEventListener == 'function') {
+ node.addEventListener(event, fn, opt_useCapture || false);
+ }
+ else if (typeof node.attachEvent == 'function') {
+ node.attachEvent('on' + event, fn);
+ }
+ }
+
+
+ /**
+ * Removes a previously added event handler from a DOM node.
+ * @param {Node} node The DOM node to remove the event handler from.
+ * @param {string} event The event name.
+ * @param {Function} fn The event handler to remove.
+ * @param {boolean} opt_useCapture If the event handler was added with this
+ * flag set to true, it should be set to true here in order to remove it.
+ */
+ function removeEvent(node, event, fn, opt_useCapture) {
+ if (typeof node.removeEventListener == 'function') {
+ node.removeEventListener(event, fn, opt_useCapture || false);
+ }
+ else if (typeof node.detatchEvent == 'function') {
+ node.detatchEvent('on' + event, fn);
+ }
+ }
+
+
+ /**
+ * Returns the intersection between two rect objects.
+ * @param {Object} rect1 The first rect.
+ * @param {Object} rect2 The second rect.
+ * @return {?Object} The intersection rect or undefined if no intersection
+ * is found.
+ */
+ function computeRectIntersection(rect1, rect2) {
+ var top = Math.max(rect1.top, rect2.top);
+ var bottom = Math.min(rect1.bottom, rect2.bottom);
+ var left = Math.max(rect1.left, rect2.left);
+ var right = Math.min(rect1.right, rect2.right);
+ var width = right - left;
+ var height = bottom - top;
+
+ return (width >= 0 && height >= 0) && {
+ top: top,
+ bottom: bottom,
+ left: left,
+ right: right,
+ width: width,
+ height: height
+ };
+ }
+
+
+ /**
+ * Shims the native getBoundingClientRect for compatibility with older IE.
+ * @param {Element} el The element whose bounding rect to get.
+ * @return {Object} The (possibly shimmed) rect of the element.
+ */
+ function getBoundingClientRect(el) {
+ var rect;
+
+ try {
+ rect = el.getBoundingClientRect();
+ } catch (err) {
+ // Ignore Windows 7 IE11 "Unspecified error"
+ // https://github.com/w3c/IntersectionObserver/pull/205
+ }
+
+ if (!rect) return getEmptyRect();
+
+ // Older IE
+ if (!(rect.width && rect.height)) {
+ rect = {
+ top: rect.top,
+ right: rect.right,
+ bottom: rect.bottom,
+ left: rect.left,
+ width: rect.right - rect.left,
+ height: rect.bottom - rect.top
+ };
+ }
+ return rect;
+ }
+
+
+ /**
+ * Returns an empty rect object. An empty rect is returned when an element
+ * is not in the DOM.
+ * @return {Object} The empty rect.
+ */
+ function getEmptyRect() {
+ return {
+ top: 0,
+ bottom: 0,
+ left: 0,
+ right: 0,
+ width: 0,
+ height: 0
+ };
+ }
+
+ /**
+ * Checks to see if a parent element contains a child elemnt (including inside
+ * shadow DOM).
+ * @param {Node} parent The parent element.
+ * @param {Node} child The child element.
+ * @return {boolean} True if the parent node contains the child node.
+ */
+ function containsDeep(parent, child) {
+ var node = child;
+ while (node) {
+ if (node == parent) return true;
+
+ node = getParentNode(node);
+ }
+ return false;
+ }
+
+
+ /**
+ * Gets the parent node of an element or its host element if the parent node
+ * is a shadow root.
+ * @param {Node} node The node whose parent to get.
+ * @return {Node|null} The parent node or null if no parent exists.
+ */
+ function getParentNode(node) {
+ var parent = node.parentNode;
+
+ if (parent && parent.nodeType == 11 && parent.host) {
+ // If the parent is a shadow root, return the host element.
+ return parent.host;
+ }
+ return parent;
+ }
+
+
+ // Exposes the constructors globally.
+ window.IntersectionObserver = IntersectionObserver;
+ window.IntersectionObserverEntry = IntersectionObserverEntry;
+
+ }(window, document));
diff --git a/vendor/automattic/jetpack-lazy-images/src/js/intersectionobserver-polyfill.min.js b/vendor/automattic/jetpack-lazy-images/src/js/intersectionobserver-polyfill.min.js
new file mode 100644
index 0000000000000..5f34cb86d2716
--- /dev/null
+++ b/vendor/automattic/jetpack-lazy-images/src/js/intersectionobserver-polyfill.min.js
@@ -0,0 +1 @@
+!function(t){var e={};function n(o){if(e[o])return e[o].exports;var r=e[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=t,n.c=e,n.d=function(t,e,o){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:o})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)n.d(o,r,function(e){return t[e]}.bind(null,r));return o},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=0)}([function(t,e){!function(t,e){"use strict";if("IntersectionObserver"in t&&"IntersectionObserverEntry"in t&&"intersectionRatio"in t.IntersectionObserverEntry.prototype)"isIntersecting"in t.IntersectionObserverEntry.prototype||Object.defineProperty(t.IntersectionObserverEntry.prototype,"isIntersecting",{get:function(){return this.intersectionRatio>0}});else{var n=[];r.prototype.THROTTLE_TIMEOUT=100,r.prototype.POLL_INTERVAL=null,r.prototype.USE_MUTATION_OBSERVER=!0,r.prototype.observe=function(t){if(!this._observationTargets.some((function(e){return e.element==t}))){if(!t||1!=t.nodeType)throw new Error("target must be an Element");this._registerInstance(),this._observationTargets.push({element:t,entry:null}),this._monitorIntersections(),this._checkForIntersections()}},r.prototype.unobserve=function(t){this._observationTargets=this._observationTargets.filter((function(e){return e.element!=t})),this._observationTargets.length||(this._unmonitorIntersections(),this._unregisterInstance())},r.prototype.disconnect=function(){this._observationTargets=[],this._unmonitorIntersections(),this._unregisterInstance()},r.prototype.takeRecords=function(){var t=this._queuedEntries.slice();return this._queuedEntries=[],t},r.prototype._initThresholds=function(t){var e=t||[0];return Array.isArray(e)||(e=[e]),e.sort().filter((function(t,e,n){if("number"!=typeof t||isNaN(t)||t<0||t>1)throw new Error("threshold must be a number between 0 and 1 inclusively");return t!==n[e-1]}))},r.prototype._parseRootMargin=function(t){var e=(t||"0px").split(/\s+/).map((function(t){var e=/^(-?\d*\.?\d+)(px|%)$/.exec(t);if(!e)throw new Error("rootMargin must be specified in pixels or percent");return{value:parseFloat(e[1]),unit:e[2]}}));return e[1]=e[1]||e[0],e[2]=e[2]||e[0],e[3]=e[3]||e[1],e},r.prototype._monitorIntersections=function(){this._monitoringIntersections||(this._monitoringIntersections=!0,this.POLL_INTERVAL?this._monitoringInterval=setInterval(this._checkForIntersections,this.POLL_INTERVAL):(i(t,"resize",this._checkForIntersections,!0),i(e,"scroll",this._checkForIntersections,!0),this.USE_MUTATION_OBSERVER&&"MutationObserver"in t&&(this._domObserver=new MutationObserver(this._checkForIntersections),this._domObserver.observe(e,{attributes:!0,childList:!0,characterData:!0,subtree:!0}))))},r.prototype._unmonitorIntersections=function(){this._monitoringIntersections&&(this._monitoringIntersections=!1,clearInterval(this._monitoringInterval),this._monitoringInterval=null,s(t,"resize",this._checkForIntersections,!0),s(e,"scroll",this._checkForIntersections,!0),this._domObserver&&(this._domObserver.disconnect(),this._domObserver=null))},r.prototype._checkForIntersections=function(){var e=this._rootIsInDom(),n=e?this._getRootRect():{top:0,bottom:0,left:0,right:0,width:0,height:0};this._observationTargets.forEach((function(r){var i=r.element,s=h(i),c=this._rootContainsTarget(i),u=r.entry,a=e&&c&&this._computeTargetAndRootIntersection(i,n),l=r.entry=new o({time:t.performance&&performance.now&&performance.now(),target:i,boundingClientRect:s,rootBounds:n,intersectionRect:a});u?e&&c?this._hasCrossedThreshold(u,l)&&this._queuedEntries.push(l):u&&u.isIntersecting&&this._queuedEntries.push(l):this._queuedEntries.push(l)}),this),this._queuedEntries.length&&this._callback(this.takeRecords(),this)},r.prototype._computeTargetAndRootIntersection=function(n,o){if("none"!=t.getComputedStyle(n).display){for(var r,i,s,c,a,l,p,f,d=h(n),g=u(n),_=!1;!_;){var v=null,m=1==g.nodeType?t.getComputedStyle(g):{};if("none"==m.display)return;if(g==this.root||g==e?(_=!0,v=o):g!=e.body&&g!=e.documentElement&&"visible"!=m.overflow&&(v=h(g)),v&&(r=v,i=d,s=void 0,c=void 0,a=void 0,l=void 0,p=void 0,f=void 0,s=Math.max(r.top,i.top),c=Math.min(r.bottom,i.bottom),a=Math.max(r.left,i.left),l=Math.min(r.right,i.right),f=c-s,!(d=(p=l-a)>=0&&f>=0&&{top:s,bottom:c,left:a,right:l,width:p,height:f})))break;g=u(g)}return d}},r.prototype._getRootRect=function(){var t;if(this.root)t=h(this.root);else{var n=e.documentElement,o=e.body;t={top:0,left:0,right:n.clientWidth||o.clientWidth,width:n.clientWidth||o.clientWidth,bottom:n.clientHeight||o.clientHeight,height:n.clientHeight||o.clientHeight}}return this._expandRectByRootMargin(t)},r.prototype._expandRectByRootMargin=function(t){var e=this._rootMarginValues.map((function(e,n){return"px"==e.unit?e.value:e.value*(n%2?t.width:t.height)/100})),n={top:t.top-e[0],right:t.right+e[1],bottom:t.bottom+e[2],left:t.left-e[3]};return n.width=n.right-n.left,n.height=n.bottom-n.top,n},r.prototype._hasCrossedThreshold=function(t,e){var n=t&&t.isIntersecting?t.intersectionRatio||0:-1,o=e.isIntersecting?e.intersectionRatio||0:-1;if(n!==o)for(var r=0;r 0 ) {
+ applyImage( lazyImages[ 0 ] );
+ }
+ }
+
+ /**
+ * On intersection
+ *
+ * @param {Array} entries - List of elements being observed.
+ */
+ function onIntersection( entries ) {
+ // Loop through the entries
+ for ( let i = 0; i < entries.length; i++ ) {
+ const entry = entries[ i ];
+
+ // Are we in viewport?
+ if ( entry.intersectionRatio > 0 ) {
+ // Stop watching and load the image
+ observer.unobserve( entry.target );
+ applyImage( entry.target );
+ }
+ }
+
+ // Disconnect if we've already loaded all of the images
+ if ( lazyImages.length === 0 ) {
+ observer.disconnect();
+ }
+ }
+
+ /**
+ * On print
+ */
+ function onPrint() {
+ if ( ! loadingWarning && ( lazyImages.length > 0 || loadingImages.length > 0 ) ) {
+ // Replace the printed page with a notice that images are still loading.
+ // Hopefully the user won't actually print this, but if they do at least it'll not
+ // waste too much ink.
+ loadingWarning = document.createElement( 'div' );
+ loadingWarning.id = 'loadingWarning';
+ loadingWarning.style.fontWeight = 'bold';
+ loadingWarning.innerText = jetpackLazyImagesL10n.loading_warning;
+
+ const s = document.createElement( 'style' );
+ s.innerHTML =
+ '#loadingWarning { display: none; }\n@media print {\n#loadingWarning { display: block; }\nbody > #loadingWarning ~ * { display: none !important; }\n}';
+ loadingWarning.appendChild( s );
+
+ bodyEl.insertBefore( loadingWarning, bodyEl.firstChild );
+ }
+
+ if ( lazyImages.length > 0 ) {
+ loadAllImages();
+ }
+
+ // May as well try an alert() too. The browser may block it, but if not
+ // it could save them some trouble.
+ if ( loadingWarning ) {
+ alert( jetpackLazyImagesL10n.loading_warning );
+ }
+ }
+
+ /**
+ * Apply the image
+ *
+ * @param {Element} image - The image object.
+ */
+ function applyImage( image ) {
+ let lazyLoadedImageEvent;
+
+ if ( ! ( image instanceof HTMLImageElement ) ) {
+ return;
+ }
+
+ const srcset = image.getAttribute( 'data-lazy-srcset' );
+ const sizes = image.getAttribute( 'data-lazy-sizes' );
+
+ // Remove lazy attributes.
+ image.removeAttribute( 'data-lazy-srcset' );
+ image.removeAttribute( 'data-lazy-sizes' );
+ image.removeAttribute( 'data-lazy-src' );
+
+ // Add the attributes we want.
+ image.classList.add( 'jetpack-lazy-image--handled' );
+ image.setAttribute( 'data-lazy-loaded', 1 );
+
+ if ( sizes ) {
+ image.setAttribute( 'sizes', sizes );
+ }
+
+ if ( ! srcset ) {
+ image.removeAttribute( 'srcset' );
+ } else {
+ image.setAttribute( 'srcset', srcset );
+ }
+
+ // Force eager loading, otherwise the browser-native loading=lazy support will still
+ // prevent the loading.
+ image.setAttribute( 'loading', 'eager' );
+
+ loadingImages.push( image );
+ const idx = lazyImages.indexOf( image );
+ if ( idx >= 0 ) {
+ lazyImages.splice( idx, 1 );
+ }
+
+ if ( image.complete ) {
+ loadedImage.call( image, null );
+ } else {
+ image.addEventListener( 'load', loadedImage, { once: true } );
+ image.addEventListener( 'error', loadedImage, { once: true } );
+ }
+
+ // Fire an event so that third-party code can perform actions after an image is loaded.
+ try {
+ lazyLoadedImageEvent = new Event( 'jetpack-lazy-loaded-image', {
+ bubbles: true,
+ cancelable: true,
+ } );
+ } catch ( e ) {
+ lazyLoadedImageEvent = document.createEvent( 'Event' );
+ lazyLoadedImageEvent.initEvent( 'jetpack-lazy-loaded-image', true, true );
+ }
+
+ image.dispatchEvent( lazyLoadedImageEvent );
+ }
+
+ /**
+ * An image from applyImage() finished loading.
+ */
+ function loadedImage() {
+ const idx = loadingImages.indexOf( this );
+ if ( idx >= 0 ) {
+ loadingImages.splice( idx, 1 );
+ }
+
+ if ( loadingWarning && lazyImages.length === 0 && loadingImages.length === 0 ) {
+ loadingWarning.parentNode.removeChild( loadingWarning );
+ loadingWarning = null;
+ }
+ }
+};
+
+// Let's kick things off now
+if ( document.readyState === 'interactive' || document.readyState === 'complete' ) {
+ jetpackLazyImagesModule();
+} else {
+ document.addEventListener( 'DOMContentLoaded', jetpackLazyImagesModule );
+}
diff --git a/vendor/automattic/jetpack-lazy-images/src/js/lazy-images.min.js b/vendor/automattic/jetpack-lazy-images/src/js/lazy-images.min.js
new file mode 100644
index 0000000000000..5cc3218566357
--- /dev/null
+++ b/vendor/automattic/jetpack-lazy-images/src/js/lazy-images.min.js
@@ -0,0 +1 @@
+!function(e){var t={};function n(r){if(t[r])return t[r].exports;var a=t[r]={i:r,l:!1,exports:{}};return e[r].call(a.exports,a,a.exports,n),a.l=!0,a.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var a in e)n.d(r,a,function(t){return e[t]}.bind(null,a));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t){const n=function(){const e={rootMargin:"200px 0px",threshold:.01},t=[];let n,r,a;i();const o=document.querySelector("body");function i(){n=Array.from(document.querySelectorAll("img.jetpack-lazy-image:not(.jetpack-lazy-image--handled)")),a&&a.disconnect(),"IntersectionObserver"in window?(a=new IntersectionObserver(l,e),n.forEach((function(e){e.getAttribute("data-lazy-loaded")||a.observe(e)})),window.addEventListener("beforeprint",c),window.matchMedia&&window.matchMedia("print").addListener((function(e){e.matches&&c()}))):d()}function d(){for(a&&a.disconnect();n.length>0;)s(n[0])}function l(e){for(let t=0;t0&&(a.unobserve(n.target),s(n.target))}0===n.length&&a.disconnect()}function c(){if(!r&&(n.length>0||t.length>0)){(r=document.createElement("div")).id="loadingWarning",r.style.fontWeight="bold",r.innerText=jetpackLazyImagesL10n.loading_warning;const e=document.createElement("style");e.innerHTML="#loadingWarning { display: none; }\n@media print {\n#loadingWarning { display: block; }\nbody > #loadingWarning ~ * { display: none !important; }\n}",r.appendChild(e),o.insertBefore(r,o.firstChild)}n.length>0&&d(),r&&alert(jetpackLazyImagesL10n.loading_warning)}function s(e){let r;if(!(e instanceof HTMLImageElement))return;const a=e.getAttribute("data-lazy-srcset"),o=e.getAttribute("data-lazy-sizes");e.removeAttribute("data-lazy-srcset"),e.removeAttribute("data-lazy-sizes"),e.removeAttribute("data-lazy-src"),e.classList.add("jetpack-lazy-image--handled"),e.setAttribute("data-lazy-loaded",1),o&&e.setAttribute("sizes",o),a?e.setAttribute("srcset",a):e.removeAttribute("srcset"),e.setAttribute("loading","eager"),t.push(e);const i=n.indexOf(e);i>=0&&n.splice(i,1),e.complete?u.call(e,null):(e.addEventListener("load",u,{once:!0}),e.addEventListener("error",u,{once:!0}));try{r=new Event("jetpack-lazy-loaded-image",{bubbles:!0,cancelable:!0})}catch(e){(r=document.createEvent("Event")).initEvent("jetpack-lazy-loaded-image",!0,!0)}e.dispatchEvent(r)}function u(){const e=t.indexOf(this);e>=0&&t.splice(e,1),r&&0===n.length&&0===t.length&&(r.parentNode.removeChild(r),r=null)}o&&(o.addEventListener("is.post-load",i),o.addEventListener("jetpack-lazy-images-load",i))};"interactive"===document.readyState||"complete"===document.readyState?n():document.addEventListener("DOMContentLoaded",n)}]);
\ No newline at end of file
diff --git a/vendor/automattic/jetpack-lazy-images/src/lazy-images.php b/vendor/automattic/jetpack-lazy-images/src/lazy-images.php
new file mode 100644
index 0000000000000..7b891cb9202e0
--- /dev/null
+++ b/vendor/automattic/jetpack-lazy-images/src/lazy-images.php
@@ -0,0 +1,506 @@
+ since it's mostly all metadata.
+ add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) );
+
+ // Do not lazy load avatar in admin bar.
+ add_action( 'admin_bar_menu', array( $this, 'remove_filters' ), 0 );
+
+ add_filter( 'wp_kses_allowed_html', array( $this, 'allow_lazy_attributes' ) );
+ add_action( 'wp_head', array( $this, 'add_nojs_fallback' ) );
+ }
+
+ /**
+ * Setup filters.
+ *
+ * @since 5.6.0
+ *
+ * @return void
+ */
+ public function setup_filters() {
+ add_filter( 'the_content', array( $this, 'add_image_placeholders' ), PHP_INT_MAX ); // Run this later, so other content filters have run, including image_add_wh on WP.com.
+ add_filter( 'post_thumbnail_html', array( $this, 'add_image_placeholders' ), PHP_INT_MAX );
+ add_filter( 'get_avatar', array( $this, 'add_image_placeholders' ), PHP_INT_MAX );
+ add_filter( 'widget_text', array( $this, 'add_image_placeholders' ), PHP_INT_MAX );
+ add_filter( 'get_image_tag', array( $this, 'add_image_placeholders' ), PHP_INT_MAX );
+ add_filter( 'wp_get_attachment_image_attributes', array( __CLASS__, 'process_image_attributes' ), PHP_INT_MAX );
+ }
+
+ /**
+ * Remove filters.
+ *
+ * @since 5.6.0
+ *
+ * @return void
+ */
+ public function remove_filters() {
+ remove_filter( 'the_content', array( $this, 'add_image_placeholders' ), PHP_INT_MAX );
+ remove_filter( 'post_thumbnail_html', array( $this, 'add_image_placeholders' ), PHP_INT_MAX );
+ remove_filter( 'get_avatar', array( $this, 'add_image_placeholders' ), PHP_INT_MAX );
+ remove_filter( 'widget_text', array( $this, 'add_image_placeholders' ), PHP_INT_MAX );
+ remove_filter( 'get_image_tag', array( $this, 'add_image_placeholders' ), PHP_INT_MAX );
+ remove_filter( 'wp_get_attachment_image_attributes', array( __CLASS__, 'process_image_attributes' ), PHP_INT_MAX );
+ }
+
+ /**
+ * Ensure that our lazy image attributes are not filtered out of image tags.
+ *
+ * @since 5.6.0
+ *
+ * @param array $allowed_tags The allowed tags and their attributes.
+ * @return array
+ */
+ public function allow_lazy_attributes( $allowed_tags ) {
+ if ( ! isset( $allowed_tags['img'] ) ) {
+ return $allowed_tags;
+ }
+
+ // But, if images are allowed, ensure that our attributes are allowed!
+ $img_attributes = array_merge(
+ $allowed_tags['img'],
+ array(
+ 'data-lazy-src' => 1,
+ 'data-lazy-srcset' => 1,
+ 'data-lazy-sizes' => 1,
+ )
+ );
+
+ $allowed_tags['img'] = $img_attributes;
+
+ return $allowed_tags;
+ }
+
+ /**
+ * Add image placeholders.
+ *
+ * @since 5.6.0
+ *
+ * @param string $content Content.
+ * @return string
+ */
+ public function add_image_placeholders( $content ) {
+ // Don't lazy load for feeds, previews.
+ if ( is_feed() || is_preview() ) {
+ return $content;
+ }
+
+ // Don't lazy-load if the content has already been run through previously.
+ if ( false !== strpos( $content, 'data-lazy-src' ) ) {
+ return $content;
+ }
+
+ // This is a pretty simple regex, but it works.
+ $content = preg_replace_callback( '#<(img)([^>]+?)(>(.*?)\\1>|[\/]?>)#si', array( __CLASS__, 'process_image' ), $content );
+
+ return $content;
+ }
+
+ /**
+ * Returns true when a given string of classes contains a class signifying lazy images.
+ * should not process the image.
+ *
+ * @since 5.9.0
+ *
+ * @param string $classes A string of space-separated classes.
+ * @return bool
+ */
+ public static function should_skip_image_with_blocked_class( $classes ) {
+ $blocked_classes = array(
+ 'skip-lazy',
+ 'gazette-featured-content-thumbnail',
+ );
+
+ /**
+ * Allow plugins and themes to tell lazy images to skip an image with a given class.
+ *
+ * @package automattic/jetpack-lazy-images
+ *
+ * @since 5.9.0
+ * @deprecated 8.7.0 Use jetpack_lazy_images_blocked_classes
+ *
+ * @param array An array of strings where each string is a class.
+ */
+ $blocked_classes = apply_filters_deprecated( 'jetpack_lazy_images_blacklisted_classes', array( $blocked_classes ), 'Jetpack 8.7.0', 'jetpack_lazy_images_blocked_classes' );
+
+ /**
+ * Allow plugins and themes to tell lazy images to skip an image with a given class.
+ *
+ * @package automattic/jetpack-lazy-images
+ *
+ * @since 8.7.0
+ *
+ * @param array An array of strings where each string is a class.
+ */
+ $blocked_classes = apply_filters( 'jetpack_lazy_images_blocked_classes', $blocked_classes );
+
+ if ( ! is_array( $blocked_classes ) || empty( $blocked_classes ) ) {
+ return false;
+ }
+
+ foreach ( $blocked_classes as $class ) {
+ if ( false !== strpos( $classes, $class ) ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Processes images in content by acting as the preg_replace_callback.
+ *
+ * @since 5.6.0
+ *
+ * @param array $matches Matches.
+ *
+ * @return string The image with updated lazy attributes.
+ */
+ public static function process_image( $matches ) {
+ $old_attributes_str = $matches[2];
+ $old_attributes_kses_hair = wp_kses_hair( $old_attributes_str, wp_allowed_protocols() );
+
+ if ( empty( $old_attributes_kses_hair['src'] ) ) {
+ return $matches[0];
+ }
+
+ $old_attributes = self::flatten_kses_hair_data( $old_attributes_kses_hair );
+
+ // If we didn't add lazy attributes, just return the original image source.
+ if ( ! empty( $old_attributes['class'] ) && false !== strpos( $old_attributes['class'], 'jetpack-lazy-image' ) ) {
+ return $matches[0];
+ }
+
+ $new_attributes = self::process_image_attributes( $old_attributes );
+ $new_attributes_str = self::build_attributes_string( $new_attributes );
+
+ return sprintf( '%2$s ', $new_attributes_str, $matches[0] );
+ }
+
+ /**
+ * Given an array of image attributes, updates the `src`, `srcset`, and `sizes` attributes so
+ * that they load lazily.
+ *
+ * @since 5.7.0
+ *
+ * @param array $attributes Attributes.
+ *
+ * @return array The updated image attributes array with lazy load attributes.
+ */
+ public static function process_image_attributes( $attributes ) {
+ if ( empty( $attributes['src'] ) ) {
+ return $attributes;
+ }
+
+ if ( ! empty( $attributes['class'] ) && self::should_skip_image_with_blocked_class( $attributes['class'] ) ) {
+ return $attributes;
+ }
+
+ if ( isset( $attributes['data-skip-lazy'] ) ) {
+ return $attributes;
+ }
+
+ /**
+ * Allow plugins and themes to conditionally skip processing an image via its attributes.
+ *
+ * @package automattic/jetpack-lazy-images
+ *
+ * @deprecated 6.5.0 Use jetpack_lazy_images_skip_image_with_attributes instead.
+ * @since 5.9.0
+ *
+ * @param bool Default to not skip processing the current image.
+ * @param array An array of attributes via wp_kses_hair() for the current image.
+ */
+ if ( apply_filters( 'jetpack_lazy_images_skip_image_with_atttributes', false, $attributes ) ) {
+ return $attributes;
+ }
+
+ /**
+ * Allow plugins and themes to conditionally skip processing an image via its attributes.
+ *
+ * @package automattic/jetpack-lazy-images
+ *
+ * @since 6.5.0 Filter name was updated from jetpack_lazy_images_skip_image_with_atttributes to correct typo.
+ * @since 5.9.0
+ *
+ * @param bool Default to not skip processing the current image.
+ * @param array An array of attributes via wp_kses_hair() for the current image.
+ */
+ if ( apply_filters( 'jetpack_lazy_images_skip_image_with_attributes', false, $attributes ) ) {
+ return $attributes;
+ }
+
+ $old_attributes = $attributes;
+
+ // Stash srcset and sizes in data attributes.
+ foreach ( array( 'srcset', 'sizes' ) as $attribute ) {
+ if ( isset( $old_attributes[ $attribute ] ) ) {
+ $attributes[ "data-lazy-$attribute" ] = $old_attributes[ $attribute ];
+ unset( $attributes[ $attribute ] );
+ }
+ }
+
+ // We set this, adding the query arg so that it doesn't exactly equal the src attribute, so that photon JavaScript
+ // will hold off on processing this image.
+ $attributes['data-lazy-src'] = esc_url_raw( add_query_arg( 'is-pending-load', true, $attributes['src'] ) );
+
+ $attributes['srcset'] = self::get_placeholder_image();
+ $attributes['class'] = sprintf(
+ '%s jetpack-lazy-image',
+ empty( $old_attributes['class'] )
+ ? ''
+ : $old_attributes['class']
+ );
+
+ /**
+ * Allow plugins and themes to override the attributes on the image before the content is updated.
+ *
+ * One potential use of this filter is for themes that set `height:auto` on the `img` tag.
+ * With this filter, the theme could get the width and height attributes from the
+ * $attributes array and then add a style tag that sets those values as well, which could
+ * minimize reflow as images load.
+ *
+ * @package automattic/jetpack-lazy-images
+ *
+ * @since 5.6.0
+ *
+ * @param array An array containing the attributes for the image, where the key is the attribute name
+ * and the value is the attribute value.
+ */
+ return apply_filters( 'jetpack_lazy_images_new_attributes', $attributes );
+ }
+
+ /**
+ * Adds JavaScript to check if the current browser supports JavaScript as well as some styles to hide lazy
+ * images when the browser does not support JavaScript.
+ *
+ * @since 5.6.0
+ *
+ * @return void
+ */
+ public function add_nojs_fallback() {
+ ?>
+
+
+ $attribute ) {
+ $flattened_attributes[ $name ] = $attribute['value'];
+ }
+ return $flattened_attributes;
+ }
+
+ /**
+ * Build attributes string.
+ *
+ * @since 5.6.0
+ *
+ * @param array $attributes Attributes.
+ *
+ * @return string
+ */
+ private static function build_attributes_string( $attributes ) {
+ $string = array();
+ foreach ( $attributes as $name => $value ) {
+ if ( '' === $value ) {
+ $string[] = sprintf( '%s', $name );
+ } else {
+ $string[] = sprintf( '%s="%s"', $name, esc_attr( $value ) );
+ }
+ }
+ return implode( ' ', $string );
+ }
+
+ /**
+ * Enqueue assets.
+ *
+ * @since 5.6.0
+ *
+ * @return void
+ */
+ public function enqueue_assets() {
+ wp_enqueue_script(
+ 'jetpack-lazy-images-polyfill-intersectionobserver',
+ Assets::get_file_url_for_environment( 'js/intersectionobserver-polyfill.min.js', 'js/intersectionobserver-polyfill.js', __FILE__ ),
+ array(),
+ self::ASSETS_VERSION,
+ true
+ );
+ wp_enqueue_script(
+ 'jetpack-lazy-images',
+ Assets::get_file_url_for_environment( 'js/lazy-images.min.js', 'js/lazy-images.js', __FILE__ ),
+ array( 'jetpack-lazy-images-polyfill-intersectionobserver' ),
+ self::ASSETS_VERSION,
+ true
+ );
+ wp_localize_script(
+ 'jetpack-lazy-images',
+ 'jetpackLazyImagesL10n',
+ array(
+ 'loading_warning' => __( 'Images are still loading. Please cancel your print and try again.', 'jetpack' ),
+ )
+ );
+ }
+}
diff --git a/vendor/automattic/jetpack-licensing/src/class-licensing.php b/vendor/automattic/jetpack-licensing/src/class-licensing.php
new file mode 100644
index 0000000000000..3e9f7eb4d4741
--- /dev/null
+++ b/vendor/automattic/jetpack-licensing/src/class-licensing.php
@@ -0,0 +1,220 @@
+addCall( 'jetpack.attachLicense', $license );
+ }
+
+ $xml->query();
+
+ return $xml;
+ }
+
+ /**
+ * Attach the given licenses.
+ *
+ * @param string[] $licenses Licenses to attach.
+ * @return array|WP_Error Results for each license (which may include WP_Error instances) or a WP_Error instance.
+ */
+ public function attach_licenses( array $licenses ) {
+ if ( ! $this->connection()->is_active() ) {
+ return new WP_Error( 'not_connected', __( 'Jetpack is not connected.', 'jetpack' ) );
+ }
+
+ if ( empty( $licenses ) ) {
+ return array();
+ }
+
+ $xml = $this->attach_licenses_request( $licenses );
+
+ if ( $xml->isError() ) {
+ $error = new WP_Error( 'request_failed', __( 'License attach request failed.', 'jetpack' ) );
+ $error->add( $xml->getErrorCode(), $xml->getErrorMessage() );
+ return $error;
+ }
+
+ $results = array_map(
+ function ( $response ) {
+ if ( isset( $response['faultCode'] ) || isset( $response['faultString'] ) ) {
+ return new WP_Error( $response['faultCode'], $response['faultString'] );
+ }
+
+ return $response;
+ },
+ (array) $xml->getResponse()
+ );
+
+ return $results;
+ }
+
+ /**
+ * Attach all stored licenses.
+ *
+ * @return array|WP_Error Results for each license (which may include WP_Error instances) or a WP_Error instance.
+ */
+ public function attach_stored_licenses() {
+ $licenses = $this->stored_licenses();
+ $results = $this->attach_licenses( $licenses );
+
+ if ( is_wp_error( $results ) ) {
+ if ( 'request_failed' === $results->get_error_code() ) {
+ $this->log_error(
+ __( 'Failed to attach your Jetpack license(s). Please try reconnecting Jetpack.', 'jetpack' )
+ );
+ }
+
+ return $results;
+ }
+
+ $failed = array();
+
+ foreach ( $results as $index => $result ) {
+ if ( isset( $licenses[ $index ] ) && is_wp_error( $result ) ) {
+ $failed[] = $licenses[ $index ];
+ }
+ }
+
+ if ( ! empty( $failed ) ) {
+ $this->log_error(
+ sprintf(
+ /* translators: %s is a comma-separated list of license keys. */
+ __( 'The following Jetpack licenses are invalid, already in use, or revoked: %s', 'jetpack' ),
+ implode( ', ', $failed )
+ )
+ );
+ }
+
+ return $results;
+ }
+
+ /**
+ * Attach all stored licenses during connection flow for the connection owner.
+ *
+ * @return void
+ */
+ public function attach_stored_licenses_on_connection() {
+ if ( $this->connection()->is_connection_owner() ) {
+ $this->attach_stored_licenses();
+ }
+ }
+}
diff --git a/vendor/automattic/jetpack-logo/src/class-logo.php b/vendor/automattic/jetpack-logo/src/class-logo.php
new file mode 100644
index 0000000000000..02f3c8a80e397
--- /dev/null
+++ b/vendor/automattic/jetpack-logo/src/class-logo.php
@@ -0,0 +1,90 @@
+ tag.
+ * - jetpack-logo__icon-circle: the circle of the Jetpack mark.
+ * - jetpack-logo__icon-triangle: two shapes that correspond to each triangle in the Jetpack mark.
+ * - jetpack-logo__icon-text: the Jetpack lettering.
+ *
+ * @var string
+ */
+const JETPACK_LOGO_SVG = <<<'EOSVG'
+
+
+
+
+
+
+EOSVG;
+
+/**
+ * Create and render a Jetpack logo.
+ */
+class Logo {
+
+ /**
+ * Return the Jetpack logo.
+ *
+ * @return string The Jetpack logo.
+ */
+ public function render() {
+ return JETPACK_LOGO_SVG;
+ }
+
+ /**
+ * Return string containing the Jetpack logo.
+ *
+ * @since 7.5.0
+ *
+ * @param bool $logotype Should we use the full logotype (logo + text). Default to false.
+ *
+ * @return string
+ */
+ public function get_jp_emblem( $logotype = false ) {
+ $logo_text = $this->get_jp_logo_parts();
+ return sprintf(
+ '%2$s ',
+ ( true === $logotype ? '118' : '32' ),
+ ( true === $logotype ? $logo_text['logo'] . $logo_text['text'] : $logo_text['logo'] )
+ );
+ }
+
+ /**
+ * Return string containing the Jetpack logo in a slightly larger format than get_jp_emblem().
+ *
+ * @return string
+ */
+ public function get_jp_emblem_larger() {
+ $logo_text = $this->get_jp_logo_parts();
+ return '' . $logo_text['logo'] . $logo_text['text'] . ' ';
+ }
+
+ /**
+ * Return array containing the Jetpack logo and text
+ *
+ * @return array
+ */
+ private function get_jp_logo_parts() {
+ return array(
+ 'logo' => ' ',
+ 'text' => '
+
+
+
+
+
+ ',
+ );
+ }
+}
diff --git a/vendor/automattic/jetpack-options/legacy/class-jetpack-options.php b/vendor/automattic/jetpack-options/legacy/class-jetpack-options.php
new file mode 100644
index 0000000000000..98c8ea9116026
--- /dev/null
+++ b/vendor/automattic/jetpack-options/legacy/class-jetpack-options.php
@@ -0,0 +1,671 @@
+ 'jetpack_options',
+ 'private' => 'jetpack_private_options',
+ );
+
+ /**
+ * Returns an array of option names for a given type.
+ *
+ * @param string $type The type of option to return. Defaults to 'compact'.
+ *
+ * @return array
+ */
+ public static function get_option_names( $type = 'compact' ) {
+ switch ( $type ) {
+ case 'non-compact':
+ case 'non_compact':
+ return array(
+ 'activated',
+ 'active_modules',
+ 'allowed_xsite_search_ids', // (array) Array of WP.com blog ids that are allowed to search the content of this site
+ 'available_modules',
+ 'do_activate',
+ 'edit_links_calypso_redirect', // (bool) Whether post/page edit links on front end should point to Calypso.
+ 'log',
+ 'slideshow_background_color',
+ 'widget_twitter',
+ 'wpcc_options',
+ 'relatedposts',
+ 'file_data',
+ 'autoupdate_plugins', // (array) An array of plugin ids ( eg. jetpack/jetpack ) that should be autoupdated
+ 'autoupdate_plugins_translations', // (array) An array of plugin ids ( eg. jetpack/jetpack ) that should be autoupdated translation files.
+ 'autoupdate_themes', // (array) An array of theme ids ( eg. twentyfourteen ) that should be autoupdated
+ 'autoupdate_themes_translations', // (array) An array of theme ids ( eg. twentyfourteen ) that should autoupdated translation files.
+ 'autoupdate_core', // (bool) Whether or not to autoupdate core
+ 'autoupdate_translations', // (bool) Whether or not to autoupdate all translations
+ 'json_api_full_management', // (bool) Allow full management (eg. Activate, Upgrade plugins) of the site via the JSON API.
+ 'sync_non_public_post_stati', // (bool) Allow synchronisation of posts and pages with non-public status.
+ 'site_icon_url', // (string) url to the full site icon
+ 'site_icon_id', // (int) Attachment id of the site icon file
+ 'dismissed_manage_banner', // (bool) Dismiss Jetpack manage banner allows the user to dismiss the banner permanently
+ 'unique_connection', // (array) A flag to determine a unique connection to wordpress.com two values "connected" and "disconnected" with values for how many times each has occured
+ 'protect_whitelist', // (array) IP Address for the Protect module to ignore
+ 'sync_error_idc', // (bool|array) false or array containing the site's home and siteurl at time of IDC error
+ 'sync_health_status', // (bool|array) An array of data relating to Jetpack's sync health.
+ 'safe_mode_confirmed', // (bool) True if someone confirms that this site was correctly put into safe mode automatically after an identity crisis is discovered.
+ 'migrate_for_idc', // (bool) True if someone confirms that this site should migrate stats and subscribers from its previous URL
+ 'dismissed_connection_banner', // (bool) True if the connection banner has been dismissed
+ 'ab_connect_banner_green_bar', // (int) Version displayed of the A/B test for the green bar at the top of the connect banner.
+ 'onboarding', // (string) Auth token to be used in the onboarding connection flow
+ 'tos_agreed', // (bool) Whether or not the TOS for connection has been agreed upon.
+ 'static_asset_cdn_files', // (array) An nested array of files that we can swap out for cdn versions.
+ 'mapbox_api_key', // (string) Mapbox API Key, for use with Map block.
+ 'mailchimp', // (string) Mailchimp keyring data, for mailchimp block.
+ 'xmlrpc_errors', // (array) Keys are XML-RPC signature error codes. Values are truthy.
+ 'dismissed_wizard_banner', // (int) True if the Wizard banner has been dismissed.
+ );
+
+ case 'private':
+ return array(
+ 'blog_token', // (string) The Client Secret/Blog Token of this site.
+ 'user_token', // (string) The User Token of this site. (deprecated)
+ 'user_tokens', // (array) User Tokens for each user of this site who has connected to jetpack.wordpress.com.
+ );
+
+ case 'network':
+ return array(
+ 'onboarding', // (string) Auth token to be used in the onboarding connection flow
+ 'file_data', // (array) List of absolute paths to all Jetpack modules
+ );
+ }
+
+ return array(
+ 'id', // (int) The Client ID/WP.com Blog ID of this site.
+ 'publicize_connections', // (array) An array of Publicize connections from WordPress.com.
+ 'master_user', // (int) The local User ID of the user who connected this site to jetpack.wordpress.com.
+ 'version', // (string) Used during upgrade procedure to auto-activate new modules. version:time.
+ 'old_version', // (string) Used to determine which modules are the most recently added. previous_version:time.
+ 'fallback_no_verify_ssl_certs', // (int) Flag for determining if this host must skip SSL Certificate verification due to misconfigured SSL.
+ 'time_diff', // (int) Offset between Jetpack server's clocks and this server's clocks. Jetpack Server Time = time() + (int) Jetpack_Options::get_option( 'time_diff' )
+ 'public', // (int|bool) If we think this site is public or not (1, 0), false if we haven't yet tried to figure it out.
+ 'videopress', // (array) VideoPress options array.
+ 'is_network_site', // (int|bool) If we think this site is a network or a single blog (1, 0), false if we haven't yet tried to figue it out.
+ 'social_links', // (array) The specified links for each social networking site.
+ 'identity_crisis_whitelist', // (array) An array of options, each having an array of the values whitelisted for it.
+ 'gplus_authors', // (array) The Google+ authorship information for connected users.
+ 'last_heartbeat', // (int) The timestamp of the last heartbeat that fired.
+ 'hide_jitm', // (array) A list of just in time messages that we should not show because they have been dismissed by the user.
+ 'custom_css_4.7_migration', // (bool) Whether Custom CSS has scanned for and migrated any legacy CSS CPT entries to the new Core format.
+ 'image_widget_migration', // (bool) Whether any legacy Image Widgets have been converted to the new Core widget.
+ 'gallery_widget_migration', // (bool) Whether any legacy Gallery Widgets have been converted to the new Core widget.
+ 'sso_first_login', // (bool) Is this the first time the user logins via SSO.
+ 'dismissed_hints', // (array) Part of Plugin Search Hints. List of cards that have been dismissed.
+ 'first_admin_view', // (bool) Set to true the first time the user views the admin. Usually after the initial connection.
+ 'setup_wizard_questionnaire', // (array) List of user choices from the setup wizard.
+ 'setup_wizard_status', // (string) Status of the setup wizard.
+ 'licensing_error', // (string) Last error message occurred while attaching licenses that is yet to be surfaced to the user.
+ );
+ }
+
+ /**
+ * Is the option name valid?
+ *
+ * @param string $name The name of the option.
+ * @param string|null $group The name of the group that the option is in. Default to null, which will search non_compact.
+ *
+ * @return bool Is the option name valid?
+ */
+ public static function is_valid( $name, $group = null ) {
+ if ( is_array( $name ) ) {
+ $compact_names = array();
+ foreach ( array_keys( self::$grouped_options ) as $_group ) {
+ $compact_names = array_merge( $compact_names, self::get_option_names( $_group ) );
+ }
+
+ $result = array_diff( $name, self::get_option_names( 'non_compact' ), $compact_names );
+
+ return empty( $result );
+ }
+
+ if ( is_null( $group ) || 'non_compact' === $group ) {
+ if ( in_array( $name, self::get_option_names( $group ), true ) ) {
+ return true;
+ }
+ }
+
+ foreach ( array_keys( self::$grouped_options ) as $_group ) {
+ if ( is_null( $group ) || $group === $_group ) {
+ if ( in_array( $name, self::get_option_names( $_group ), true ) ) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if an option must be saved for the whole network in WP Multisite
+ *
+ * @param string $option_name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
+ *
+ * @return bool
+ */
+ public static function is_network_option( $option_name ) {
+ if ( ! is_multisite() ) {
+ return false;
+ }
+ return in_array( $option_name, self::get_option_names( 'network' ), true );
+ }
+
+ /**
+ * Filters the requested option.
+ * This is a wrapper around `get_option_from_database` so that we can filter the option.
+ *
+ * @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
+ * @param mixed $default (optional).
+ *
+ * @return mixed
+ */
+ public static function get_option( $name, $default = false ) {
+ /**
+ * Filter Jetpack Options.
+ * Can be useful in environments when Jetpack is running with a different setup
+ *
+ * @since 8.8.0
+ *
+ * @param string $value The value from the database.
+ * @param string $name Option name, _without_ `jetpack_%` prefix.
+ * @return string $value, unless the filters modify it.
+ */
+ return apply_filters( 'jetpack_options', self::get_option_from_database( $name, $default ), $name );
+ }
+
+ /**
+ * Returns the requested option. Looks in jetpack_options or jetpack_$name as appropriate.
+ *
+ * @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
+ * @param mixed $default (optional).
+ *
+ * @return mixed
+ */
+ private static function get_option_from_database( $name, $default = false ) {
+ if ( self::is_valid( $name, 'non_compact' ) ) {
+ if ( self::is_network_option( $name ) ) {
+ return get_site_option( "jetpack_$name", $default );
+ }
+
+ return get_option( "jetpack_$name", $default );
+ }
+
+ foreach ( array_keys( self::$grouped_options ) as $group ) {
+ if ( self::is_valid( $name, $group ) ) {
+ return self::get_grouped_option( $group, $name, $default );
+ }
+ }
+
+ trigger_error( sprintf( 'Invalid Jetpack option name: %s', esc_html( $name ) ), E_USER_WARNING ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- Don't wish to change legacy behavior.
+
+ return $default;
+ }
+
+ /**
+ * Returns the requested option, and ensures it's autoloaded in the future.
+ * This does _not_ adjust the prefix in any way (does not prefix jetpack_%)
+ *
+ * @param string $name Option name.
+ * @param mixed $default (optional).
+ *
+ * @return mixed
+ */
+ public static function get_option_and_ensure_autoload( $name, $default ) {
+ // In this function the name is not adjusted by prefixing jetpack_
+ // so if it has already prefixed, we'll replace it and then
+ // check if the option name is a network option or not.
+ $jetpack_name = preg_replace( '/^jetpack_/', '', $name, 1 );
+ $is_network_option = self::is_network_option( $jetpack_name );
+ $value = $is_network_option ? get_site_option( $name ) : get_option( $name );
+
+ if ( false === $value && false !== $default ) {
+ if ( $is_network_option ) {
+ add_site_option( $name, $default );
+ } else {
+ add_option( $name, $default );
+ }
+ $value = $default;
+ }
+
+ return $value;
+ }
+
+ /**
+ * Update grouped option
+ *
+ * @param string $group Options group.
+ * @param string $name Options name.
+ * @param mixed $value Options value.
+ *
+ * @return bool Success or failure.
+ */
+ private static function update_grouped_option( $group, $name, $value ) {
+ $options = get_option( self::$grouped_options[ $group ] );
+ if ( ! is_array( $options ) ) {
+ $options = array();
+ }
+ $options[ $name ] = $value;
+
+ return update_option( self::$grouped_options[ $group ], $options );
+ }
+
+ /**
+ * Updates the single given option. Updates jetpack_options or jetpack_$name as appropriate.
+ *
+ * @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
+ * @param mixed $value Option value.
+ * @param string $autoload If not compact option, allows specifying whether to autoload or not.
+ *
+ * @return bool Was the option successfully updated?
+ */
+ public static function update_option( $name, $value, $autoload = null ) {
+ /**
+ * Fires before Jetpack updates a specific option.
+ *
+ * @since 3.0.0
+ *
+ * @param str $name The name of the option being updated.
+ * @param mixed $value The new value of the option.
+ */
+ do_action( 'pre_update_jetpack_option_' . $name, $name, $value );
+ if ( self::is_valid( $name, 'non_compact' ) ) {
+ if ( self::is_network_option( $name ) ) {
+ return update_site_option( "jetpack_$name", $value );
+ }
+
+ return update_option( "jetpack_$name", $value, $autoload );
+
+ }
+
+ foreach ( array_keys( self::$grouped_options ) as $group ) {
+ if ( self::is_valid( $name, $group ) ) {
+ return self::update_grouped_option( $group, $name, $value );
+ }
+ }
+
+ trigger_error( sprintf( 'Invalid Jetpack option name: %s', esc_html( $name ) ), E_USER_WARNING ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- Don't want to change legacy behavior.
+
+ return false;
+ }
+
+ /**
+ * Updates the multiple given options. Updates jetpack_options and/or jetpack_$name as appropriate.
+ *
+ * @param array $array array( option name => option value, ... ).
+ */
+ public static function update_options( $array ) {
+ $names = array_keys( $array );
+
+ foreach ( array_diff( $names, self::get_option_names(), self::get_option_names( 'non_compact' ), self::get_option_names( 'private' ) ) as $unknown_name ) {
+ trigger_error( sprintf( 'Invalid Jetpack option name: %s', esc_html( $unknown_name ) ), E_USER_WARNING ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- Don't change legacy behavior.
+ unset( $array[ $unknown_name ] );
+ }
+
+ foreach ( $names as $name ) {
+ self::update_option( $name, $array[ $name ] );
+ }
+ }
+
+ /**
+ * Deletes the given option. May be passed multiple option names as an array.
+ * Updates jetpack_options and/or deletes jetpack_$name as appropriate.
+ *
+ * @param string|array $names Option names. They must come _without_ `jetpack_%` prefix. The method will prefix the option names.
+ *
+ * @return bool Was the option successfully deleted?
+ */
+ public static function delete_option( $names ) {
+ $result = true;
+ $names = (array) $names;
+
+ if ( ! self::is_valid( $names ) ) {
+ // phpcs:disable -- This line triggers a handful of errors; ignoring to avoid changing legacy behavior.
+ trigger_error( sprintf( 'Invalid Jetpack option names: %s', print_r( $names, 1 ) ), E_USER_WARNING );
+ // phpcs:enable
+ return false;
+ }
+
+ foreach ( array_intersect( $names, self::get_option_names( 'non_compact' ) ) as $name ) {
+ if ( self::is_network_option( $name ) ) {
+ $result = delete_site_option( "jetpack_$name" );
+ } else {
+ $result = delete_option( "jetpack_$name" );
+ }
+ }
+
+ foreach ( array_keys( self::$grouped_options ) as $group ) {
+ if ( ! self::delete_grouped_option( $group, $names ) ) {
+ $result = false;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Get group option.
+ *
+ * @param string $group Option group name.
+ * @param string $name Option name.
+ * @param mixed $default Default option value.
+ *
+ * @return mixed Option.
+ */
+ private static function get_grouped_option( $group, $name, $default ) {
+ $options = get_option( self::$grouped_options[ $group ] );
+ if ( is_array( $options ) && isset( $options[ $name ] ) ) {
+ return $options[ $name ];
+ }
+
+ return $default;
+ }
+
+ /**
+ * Delete grouped option.
+ *
+ * @param string $group Option group name.
+ * @param array $names Option names.
+ *
+ * @return bool Success or failure.
+ */
+ private static function delete_grouped_option( $group, $names ) {
+ $options = get_option( self::$grouped_options[ $group ], array() );
+
+ $to_delete = array_intersect( $names, self::get_option_names( $group ), array_keys( $options ) );
+ if ( $to_delete ) {
+ foreach ( $to_delete as $name ) {
+ unset( $options[ $name ] );
+ }
+
+ return update_option( self::$grouped_options[ $group ], $options );
+ }
+
+ return true;
+ }
+
+ /*
+ * Raw option methods allow Jetpack to get / update / delete options via direct DB queries, including options
+ * that are not created by the Jetpack plugin. This is helpful only in rare cases when we need to bypass
+ * cache and filters.
+ */
+
+ /**
+ * Deletes an option via $wpdb query.
+ *
+ * @param string $name Option name.
+ *
+ * @return bool Is the option deleted?
+ */
+ public static function delete_raw_option( $name ) {
+ if ( self::bypass_raw_option( $name ) ) {
+ return delete_option( $name );
+ }
+ global $wpdb;
+ $result = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->options WHERE option_name = %s", $name ) );
+ return $result;
+ }
+
+ /**
+ * Updates an option via $wpdb query.
+ *
+ * @param string $name Option name.
+ * @param mixed $value Option value.
+ * @param bool $autoload Specifying whether to autoload or not.
+ *
+ * @return bool Is the option updated?
+ */
+ public static function update_raw_option( $name, $value, $autoload = false ) {
+ if ( self::bypass_raw_option( $name ) ) {
+ return update_option( $name, $value, $autoload );
+ }
+ global $wpdb;
+ $autoload_value = $autoload ? 'yes' : 'no';
+
+ $old_value = $wpdb->get_var(
+ $wpdb->prepare(
+ "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1",
+ $name
+ )
+ );
+ if ( $old_value === $value ) {
+ return false;
+ }
+
+ $serialized_value = maybe_serialize( $value );
+ // below we used "insert ignore" to at least suppress the resulting error.
+ $updated_num = $wpdb->query(
+ $wpdb->prepare(
+ "UPDATE $wpdb->options SET option_value = %s WHERE option_name = %s",
+ $serialized_value,
+ $name
+ )
+ );
+
+ // Try inserting the option if the value doesn't exits.
+ if ( ! $updated_num ) {
+ $updated_num = $wpdb->query(
+ $wpdb->prepare(
+ "INSERT IGNORE INTO $wpdb->options ( option_name, option_value, autoload ) VALUES ( %s, %s, %s )",
+ $name,
+ $serialized_value,
+ $autoload_value
+ )
+ );
+ }
+ return (bool) $updated_num;
+ }
+
+ /**
+ * Gets an option via $wpdb query.
+ *
+ * @since 5.4.0
+ *
+ * @param string $name Option name.
+ * @param mixed $default Default option value if option is not found.
+ *
+ * @return mixed Option value, or null if option is not found and default is not specified.
+ */
+ public static function get_raw_option( $name, $default = null ) {
+ if ( self::bypass_raw_option( $name ) ) {
+ return get_option( $name, $default );
+ }
+
+ global $wpdb;
+ $value = $wpdb->get_var(
+ $wpdb->prepare(
+ "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1",
+ $name
+ )
+ );
+ $value = maybe_unserialize( $value );
+
+ if ( null === $value && null !== $default ) {
+ return $default;
+ }
+
+ return $value;
+ }
+
+ /**
+ * This function checks for a constant that, if present, will disable direct DB queries Jetpack uses to manage certain options and force Jetpack to always use Options API instead.
+ * Options can be selectively managed via a blocklist by filtering option names via the jetpack_disabled_raw_option filter.
+ *
+ * @param string $name Option name.
+ *
+ * @return bool
+ */
+ public static function bypass_raw_option( $name ) {
+
+ if ( Constants::get_constant( 'JETPACK_DISABLE_RAW_OPTIONS' ) ) {
+ return true;
+ }
+ /**
+ * Allows to disable particular raw options.
+ *
+ * @since 5.5.0
+ *
+ * @param array $disabled_raw_options An array of option names that you can selectively blocklist from being managed via direct database queries.
+ */
+ $disabled_raw_options = apply_filters( 'jetpack_disabled_raw_options', array() );
+ return isset( $disabled_raw_options[ $name ] );
+ }
+
+ /**
+ * Gets all known options that are used by Jetpack and managed by Jetpack_Options.
+ *
+ * @since 5.4.0
+ *
+ * @param boolean $strip_unsafe_options If true, and by default, will strip out options necessary for the connection to WordPress.com.
+ * @return array An array of all options managed via the Jetpack_Options class.
+ */
+ public static function get_all_jetpack_options( $strip_unsafe_options = true ) {
+ $jetpack_options = self::get_option_names();
+ $jetpack_options_non_compat = self::get_option_names( 'non_compact' );
+ $jetpack_options_private = self::get_option_names( 'private' );
+
+ $all_jp_options = array_merge( $jetpack_options, $jetpack_options_non_compat, $jetpack_options_private );
+
+ if ( $strip_unsafe_options ) {
+ // Flag some Jetpack options as unsafe.
+ $unsafe_options = array(
+ 'id', // (int) The Client ID/WP.com Blog ID of this site.
+ 'master_user', // (int) The local User ID of the user who connected this site to jetpack.wordpress.com.
+ 'version', // (string) Used during upgrade procedure to auto-activate new modules. version:time
+
+ // non_compact.
+ 'activated',
+
+ // private.
+ 'register',
+ 'blog_token', // (string) The Client Secret/Blog Token of this site.
+ 'user_token', // (string) The User Token of this site. (deprecated)
+ 'user_tokens',
+ );
+
+ // Remove the unsafe Jetpack options.
+ foreach ( $unsafe_options as $unsafe_option ) {
+ $key = array_search( $unsafe_option, $all_jp_options, true );
+ if ( false !== $key ) {
+ unset( $all_jp_options[ $key ] );
+ }
+ }
+ }
+
+ return $all_jp_options;
+ }
+
+ /**
+ * Get all options that are not managed by the Jetpack_Options class that are used by Jetpack.
+ *
+ * @since 5.4.0
+ *
+ * @return array
+ */
+ public static function get_all_wp_options() {
+ // A manual build of the wp options.
+ return array(
+ 'sharing-options',
+ 'disabled_likes',
+ 'disabled_reblogs',
+ 'jetpack_comments_likes_enabled',
+ 'stats_options',
+ 'stats_dashboard_widget',
+ 'safecss_preview_rev',
+ 'safecss_rev',
+ 'safecss_revision_migrated',
+ 'nova_menu_order',
+ 'jetpack_portfolio',
+ 'jetpack_portfolio_posts_per_page',
+ 'jetpack_testimonial',
+ 'jetpack_testimonial_posts_per_page',
+ 'sharedaddy_disable_resources',
+ 'sharing-options',
+ 'sharing-services',
+ 'site_icon_temp_data',
+ 'featured-content',
+ 'site_logo',
+ 'jetpack_dismissed_notices',
+ 'jetpack-twitter-cards-site-tag',
+ 'jetpack-sitemap-state',
+ 'jetpack_sitemap_post_types',
+ 'jetpack_sitemap_location',
+ 'jetpack_protect_key',
+ 'jetpack_protect_blocked_attempts',
+ 'jetpack_protect_activating',
+ 'jetpack_connection_banner_ab',
+ 'jetpack_active_plan',
+ 'jetpack_activation_source',
+ 'jetpack_site_products',
+ 'jetpack_sso_match_by_email',
+ 'jetpack_sso_require_two_step',
+ 'jetpack_sso_remove_login_form',
+ 'jetpack_last_connect_url_check',
+ 'jpo_business_address',
+ 'jpo_site_type',
+ 'jpo_homepage_format',
+ 'jpo_contact_page',
+ 'jetpack_excluded_extensions',
+ );
+ }
+
+ /**
+ * Gets all options that can be safely reset by CLI.
+ *
+ * @since 5.4.0
+ *
+ * @return array array Associative array containing jp_options which are managed by the Jetpack_Options class and wp_options which are not.
+ */
+ public static function get_options_for_reset() {
+ $all_jp_options = self::get_all_jetpack_options();
+
+ $wp_options = self::get_all_wp_options();
+
+ $options = array(
+ 'jp_options' => $all_jp_options,
+ 'wp_options' => $wp_options,
+ );
+
+ return $options;
+ }
+
+ /**
+ * Delete all known options
+ *
+ * @since 5.4.0
+ *
+ * @return void
+ */
+ public static function delete_all_known_options() {
+ // Delete all compact options.
+ foreach ( (array) self::$grouped_options as $option_name ) {
+ delete_option( $option_name );
+ }
+
+ // Delete all non-compact Jetpack options.
+ foreach ( (array) self::get_option_names( 'non-compact' ) as $option_name ) {
+ self::delete_option( $option_name );
+ }
+
+ // Delete all options that can be reset via CLI, that aren't Jetpack options.
+ foreach ( (array) self::get_all_wp_options() as $option_name ) {
+ delete_option( $option_name );
+ }
+ }
+}
diff --git a/vendor/automattic/jetpack-partner/src/class-partner.php b/vendor/automattic/jetpack-partner/src/class-partner.php
new file mode 100644
index 0000000000000..af5bc8f9e72ad
--- /dev/null
+++ b/vendor/automattic/jetpack-partner/src/class-partner.php
@@ -0,0 +1,152 @@
+add_code_as_query_arg( self::SUBSIDIARY_CODE, $url );
+ }
+
+ /**
+ * Adds the affiliate code to the passed URL.
+ *
+ * @param string $url The URL.
+ *
+ * @return string
+ */
+ public function add_affiliate_code_as_query_arg( $url ) {
+ return $this->add_code_as_query_arg( self::AFFILIATE_CODE, $url );
+ }
+
+ /**
+ * Returns the passed URL with the partner code added as a URL query arg.
+ *
+ * @param string $type The partner code.
+ * @param string $url The URL where the partner subsidiary id will be added.
+ *
+ * @return string The passed URL with the partner code added.
+ * @since 8.1.0
+ */
+ public function add_code_as_query_arg( $type, $url ) {
+ switch ( $type ) {
+ case self::AFFILIATE_CODE:
+ $query_arg_name = 'aff';
+ break;
+ case self::SUBSIDIARY_CODE:
+ $query_arg_name = 'subsidiaryId';
+ break;
+ default:
+ return $url;
+ }
+
+ $code = $this->get_partner_code( $type );
+
+ if ( '' === $code ) {
+ return $url;
+ }
+
+ return add_query_arg( $query_arg_name, $code, $url );
+ }
+
+ /**
+ * Returns a partner code.
+ *
+ * @param string $type This can be either 'affiliate' or 'subsidiary'. Returns empty string when code is unknown.
+ *
+ * @return string The partner code.
+ * @since 8.1.0
+ */
+ public function get_partner_code( $type ) {
+ switch ( $type ) {
+ case self::AFFILIATE_CODE:
+ /**
+ * Allow to filter the affiliate code.
+ *
+ * @param string $affiliate_code The affiliate code, blank by default.
+ *
+ * @since 6.9.0
+ */
+ return apply_filters( 'jetpack_affiliate_code', get_option( 'jetpack_affiliate_code', '' ) );
+ case self::SUBSIDIARY_CODE:
+ /**
+ * Allow to filter the partner subsidiary id.
+ *
+ * @param string $subsidiary_id The partner subsidiary id, blank by default.
+ *
+ * @since 8.1.0
+ */
+ return apply_filters(
+ 'jetpack_partner_subsidiary_id',
+ get_option( 'jetpack_partner_subsidiary_id', '' )
+ );
+ default:
+ return '';
+ }
+ }
+
+ /**
+ * Resets the singleton for testing purposes.
+ */
+ public static function reset() {
+ self::$instance = null;
+ }
+}
diff --git a/vendor/automattic/jetpack-redirect/src/class-redirect.php b/vendor/automattic/jetpack-redirect/src/class-redirect.php
new file mode 100644
index 0000000000000..896c2fcd4b72f
--- /dev/null
+++ b/vendor/automattic/jetpack-redirect/src/class-redirect.php
@@ -0,0 +1,91 @@
+get_site_suffix();
+ $args = wp_parse_args( $args, array( 'site' => $site_suffix ) );
+ $accepted_args = array( 'site', 'path', 'query', 'anchor' );
+
+ $source_key = 'source';
+ $is_url = false;
+
+ if ( 0 === strpos( $source, 'https://' ) ) {
+ $source_key = 'url';
+ $is_url = true;
+ $source_url = \wp_parse_url( $source );
+
+ // discard any query and fragments.
+ $source = 'https://' . $source_url['host'] . ( isset( $source_url['path'] ) ? $source_url['path'] : '' );
+ }
+
+ $to_be_added = array(
+ $source_key => rawurlencode( $source ),
+ );
+
+ foreach ( $args as $arg_name => $arg_value ) {
+
+ if ( ! in_array( $arg_name, $accepted_args, true ) || empty( $arg_value ) ) {
+ continue;
+ }
+
+ $to_be_added[ $arg_name ] = rawurlencode( $arg_value );
+
+ }
+
+ if ( ! empty( $to_be_added ) ) {
+ $url = add_query_arg( $to_be_added, $url );
+ }
+
+ /**
+ * Filters the return of the Redirect URL.
+ *
+ * @since 8.6.0
+ *
+ * @param string $url The redirect URL.
+ * @param string $source The $source informed to Redirect::get_url.
+ * @param array $args The arguments informed to Redirect::get_url.
+ * @param boolean $is_url Whether $source is a URL or not.
+ */
+ return \apply_filters( 'jetpack_redirects_get_url', $url, $source, $args, $is_url );
+ }
+}
diff --git a/vendor/automattic/jetpack-roles/src/class-roles.php b/vendor/automattic/jetpack-roles/src/class-roles.php
new file mode 100644
index 0000000000000..7bce34625b2e8
--- /dev/null
+++ b/vendor/automattic/jetpack-roles/src/class-roles.php
@@ -0,0 +1,81 @@
+ 'manage_options',
+ 'editor' => 'edit_others_posts',
+ 'author' => 'publish_posts',
+ 'contributor' => 'edit_posts',
+ 'subscriber' => 'read',
+ );
+
+ /**
+ * Get the role of the current user.
+ *
+ * @access public
+ *
+ * @return string|boolean Current user's role, false if not enough capabilities for any of the roles.
+ */
+ public function translate_current_user_to_role() {
+ foreach ( $this->capability_translations as $role => $cap ) {
+ if ( current_user_can( $role ) || current_user_can( $cap ) ) {
+ return $role;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the role of a particular user.
+ *
+ * @access public
+ *
+ * @param \WP_User $user User object.
+ * @return string|boolean User's role, false if not enough capabilities for any of the roles.
+ */
+ public function translate_user_to_role( $user ) {
+ foreach ( $this->capability_translations as $role => $cap ) {
+ if ( user_can( $user, $role ) || user_can( $user, $cap ) ) {
+ return $role;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the minimum capability for a role.
+ *
+ * @access public
+ *
+ * @param string $role Role name.
+ * @return string|boolean Capability, false if role isn't mapped to any capabilities.
+ */
+ public function translate_role_to_cap( $role ) {
+ if ( ! isset( $this->capability_translations[ $role ] ) ) {
+ return false;
+ }
+
+ return $this->capability_translations[ $role ];
+ }
+}
diff --git a/vendor/automattic/jetpack-status/src/class-status.php b/vendor/automattic/jetpack-status/src/class-status.php
new file mode 100644
index 0000000000000..cf9589565c2a8
--- /dev/null
+++ b/vendor/automattic/jetpack-status/src/class-status.php
@@ -0,0 +1,296 @@
+is_offline_mode().
+ *
+ * @return bool Whether Jetpack's offline mode is active.
+ */
+ public function is_development_mode() {
+ _deprecated_function( __FUNCTION__, 'Jetpack 8.8.0', 'Automattic\Jetpack\Status->is_offline_mode' );
+ return $this->is_offline_mode();
+ }
+
+ /**
+ * Is Jetpack in offline mode?
+ *
+ * This was formerly called "Development Mode", but sites "in development" aren't always offline/localhost.
+ *
+ * @since 8.8.0
+ *
+ * @return bool Whether Jetpack's offline mode is active.
+ */
+ public function is_offline_mode() {
+ $offline_mode = false;
+
+ if ( defined( '\\JETPACK_DEV_DEBUG' ) ) {
+ $offline_mode = constant( '\\JETPACK_DEV_DEBUG' );
+ } elseif ( defined( '\\WP_LOCAL_DEV' ) ) {
+ $offline_mode = constant( '\\WP_LOCAL_DEV' );
+ } elseif ( $this->is_local_site() ) {
+ $offline_mode = true;
+ }
+
+ /**
+ * Filters Jetpack's offline mode.
+ *
+ * @see https://jetpack.com/support/development-mode/
+ * @todo Update documentation ^^.
+ *
+ * @since 2.2.1
+ * @deprecated 8.8.0
+ *
+ * @param bool $offline_mode Is Jetpack's offline mode active.
+ */
+ $offline_mode = (bool) apply_filters_deprecated( 'jetpack_development_mode', array( $offline_mode ), '8.8.0', 'jetpack_offline_mode' );
+
+ /**
+ * Filters Jetpack's offline mode.
+ *
+ * @see https://jetpack.com/support/development-mode/
+ * @todo Update documentation ^^.
+ *
+ * @since 8.8.0
+ *
+ * @param bool $offline_mode Is Jetpack's offline mode active.
+ */
+ $offline_mode = (bool) apply_filters( 'jetpack_offline_mode', $offline_mode );
+
+ return $offline_mode;
+ }
+
+ /**
+ * Is Jetpack in "No User test mode"?
+ *
+ * This will make Jetpack act as if there were no connected users, but only a site connection (aka blog token)
+ *
+ * @since 9.2.0
+ *
+ * @return bool Whether Jetpack's No User Testing Mode is active.
+ */
+ public function is_no_user_testing_mode() {
+ $test_mode = false;
+ if ( defined( 'JETPACK_NO_USER_TEST_MODE' ) ) {
+ $test_mode = JETPACK_NO_USER_TEST_MODE;
+ }
+
+ /**
+ * Filters Jetpack's No User testing mode.
+ *
+ * @since 9.2.0
+ *
+ * @param bool $test_mode Is Jetpack's No User testing mode active.
+ */
+ $test_mode = (bool) apply_filters( 'jetpack_no_user_testing_mode', $test_mode );
+
+ return $test_mode;
+
+ }
+
+ /**
+ * Whether this is a system with a multiple networks.
+ * Implemented since there is no core is_multi_network function.
+ * Right now there is no way to tell which network is the dominant network on the system.
+ *
+ * @return boolean
+ */
+ public function is_multi_network() {
+ global $wpdb;
+
+ // If we don't have a multi site setup no need to do any more.
+ if ( ! is_multisite() ) {
+ return false;
+ }
+
+ $num_sites = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->site}" );
+ if ( $num_sites > 1 ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Whether the current site is single user site.
+ *
+ * @return bool
+ */
+ public function is_single_user_site() {
+ global $wpdb;
+
+ $some_users = get_transient( 'jetpack_is_single_user' );
+ if ( false === $some_users ) {
+ $some_users = $wpdb->get_var( "SELECT COUNT(*) FROM (SELECT user_id FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}capabilities' LIMIT 2) AS someusers" );
+ set_transient( 'jetpack_is_single_user', (int) $some_users, 12 * HOUR_IN_SECONDS );
+ }
+ return 1 === (int) $some_users;
+ }
+
+ /**
+ * If the site is a local site.
+ *
+ * @since 8.8.0
+ *
+ * @return bool
+ */
+ public function is_local_site() {
+ // Check for localhost and sites using an IP only first.
+ $is_local = site_url() && false === strpos( site_url(), '.' );
+
+ // Use Core's environment check, if available. Added in 5.5.0 / 5.5.1 (for `local` return value).
+ if ( 'local' === wp_get_environment_type() ) {
+ $is_local = true;
+ }
+
+ // Then check for usual usual domains used by local dev tools.
+ $known_local = array(
+ '#\.local$#i',
+ '#\.localhost$#i',
+ '#\.test$#i',
+ '#\.docksal$#i', // Docksal.
+ '#\.docksal\.site$#i', // Docksal.
+ '#\.dev\.cc$#i', // ServerPress.
+ '#\.lndo\.site$#i', // Lando.
+ );
+
+ if ( ! $is_local ) {
+ foreach ( $known_local as $url ) {
+ if ( preg_match( $url, site_url() ) ) {
+ $is_local = true;
+ break;
+ }
+ }
+ }
+
+ /**
+ * Filters is_local_site check.
+ *
+ * @since 8.8.0
+ *
+ * @param bool $is_local If the current site is a local site.
+ */
+ return apply_filters( 'jetpack_is_local_site', $is_local );
+ }
+
+ /**
+ * If is a staging site.
+ *
+ * @todo Add IDC detection to a package.
+ *
+ * @return bool
+ */
+ public function is_staging_site() {
+ // Core's wp_get_environment_type allows for a few specific options. We should default to bowing out gracefully for anything other than production or local.
+ $is_staging = ! in_array( \wp_get_environment_type(), array( 'production', 'local' ), true );
+
+ $known_staging = array(
+ 'urls' => array(
+ '#\.staging\.wpengine\.com$#i', // WP Engine.
+ '#\.staging\.kinsta\.com$#i', // Kinsta.com.
+ '#\.kinsta\.cloud$#i', // Kinsta.com.
+ '#\.stage\.site$#i', // DreamPress.
+ '#\.newspackstaging\.com$#i', // Newspack.
+ '#\.pantheonsite\.io$#i', // Pantheon.
+ '#\.flywheelsites\.com$#i', // Flywheel.
+ '#\.flywheelstaging\.com$#i', // Flywheel.
+ '#\.cloudwaysapps\.com$#i', // Cloudways.
+ '#\.azurewebsites\.net$#i', // Azure.
+ '#\.wpserveur\.net$#i', // WPServeur.
+ '#\-liquidwebsites\.com$#i', // Liquidweb.
+ ),
+ 'constants' => array(
+ 'IS_WPE_SNAPSHOT', // WP Engine.
+ 'KINSTA_DEV_ENV', // Kinsta.com.
+ 'WPSTAGECOACH_STAGING', // WP Stagecoach.
+ 'JETPACK_STAGING_MODE', // Generic.
+ 'WP_LOCAL_DEV', // Generic.
+ ),
+ );
+ /**
+ * Filters the flags of known staging sites.
+ *
+ * @since 3.9.0
+ *
+ * @param array $known_staging {
+ * An array of arrays that each are used to check if the current site is staging.
+ * @type array $urls URLs of staging sites in regex to check against site_url.
+ * @type array $constants PHP constants of known staging/developement environments.
+ * }
+ */
+ $known_staging = apply_filters( 'jetpack_known_staging', $known_staging );
+
+ if ( isset( $known_staging['urls'] ) ) {
+ foreach ( $known_staging['urls'] as $url ) {
+ if ( preg_match( $url, wp_parse_url( site_url(), PHP_URL_HOST ) ) ) {
+ $is_staging = true;
+ break;
+ }
+ }
+ }
+
+ if ( isset( $known_staging['constants'] ) ) {
+ foreach ( $known_staging['constants'] as $constant ) {
+ if ( defined( $constant ) && constant( $constant ) ) {
+ $is_staging = true;
+ }
+ }
+ }
+
+ // Last, let's check if sync is erroring due to an IDC. If so, set the site to staging mode.
+ if ( ! $is_staging && method_exists( 'Jetpack', 'validate_sync_error_idc_option' ) && \Jetpack::validate_sync_error_idc_option() ) {
+ $is_staging = true;
+ }
+
+ /**
+ * Filters is_staging_site check.
+ *
+ * @since 3.9.0
+ *
+ * @param bool $is_staging If the current site is a staging site.
+ */
+ return apply_filters( 'jetpack_is_staging_site', $is_staging );
+ }
+
+ /**
+ * Returns the site slug suffix to be used as part of Calypso URLs.
+ *
+ * Strips http:// or https:// from a url, replaces forward slash with ::.
+ *
+ * @since 9.2.0
+ *
+ * @param string $url Optional. URL to build the site suffix from. Default: Home URL.
+ *
+ * @return string
+ */
+ public function get_site_suffix( $url = '' ) {
+ // On WordPress.com, site suffixes are a bit different.
+ if ( method_exists( 'WPCOM_Masterbar', 'get_calypso_site_slug' ) ) {
+ return WPCOM_Masterbar::get_calypso_site_slug( get_current_blog_id() );
+ }
+
+ if ( empty( $url ) ) {
+ $url = \home_url();
+ }
+
+ $url = preg_replace( '#^.*?://#', '', $url );
+ $url = str_replace( '/', '::', $url );
+
+ return $url;
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/class-actions.php b/vendor/automattic/jetpack-sync/src/class-actions.php
new file mode 100644
index 0000000000000..6411bbe93bf54
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/class-actions.php
@@ -0,0 +1,875 @@
+is_offline_mode() ) {
+ return false;
+ }
+
+ if ( ( new Status() )->is_staging_site() ) {
+ return false;
+ }
+
+ $connection = new Jetpack_Connection();
+ if ( ! $connection->is_active() ) {
+ if ( ! doing_action( 'jetpack_user_authorized' ) ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Helper function to get details as to why sync is not allowed, if it is not allowed.
+ *
+ * @return array
+ */
+ public static function get_debug_details() {
+ $debug = array();
+ $debug['debug_details']['sync_allowed'] = self::sync_allowed();
+ $debug['debug_details']['sync_health'] = Health::get_status();
+ if ( false === $debug['debug_details']['sync_allowed'] ) {
+ if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
+ $debug['debug_details']['is_wpcom'] = true;
+ }
+ if ( defined( 'PHPUNIT_JETPACK_TESTSUITE' ) ) {
+ $debug['debug_details']['PHPUNIT_JETPACK_TESTSUITE'] = true;
+ }
+ if ( ! Settings::is_sync_enabled() ) {
+ $debug['debug_details']['is_sync_enabled'] = false;
+ $debug['debug_details']['jetpack_sync_disable'] = Settings::get_setting( 'disable' );
+ $debug['debug_details']['jetpack_sync_network_disable'] = Settings::get_setting( 'network_disable' );
+ }
+ if ( ( new Status() )->is_offline_mode() ) {
+ $debug['debug_details']['is_offline_mode'] = true;
+ }
+ if ( ( new Status() )->is_staging_site() ) {
+ $debug['debug_details']['is_staging_site'] = true;
+ }
+ $connection = new Jetpack_Connection();
+ if ( ! $connection->is_active() ) {
+ $debug['debug_details']['active_connection'] = false;
+ }
+ }
+ return $debug;
+
+ }
+
+ /**
+ * Determines if syncing during a cron job is allowed.
+ *
+ * @access public
+ * @static
+ *
+ * @return bool|int
+ */
+ public static function sync_via_cron_allowed() {
+ return ( Settings::get_setting( 'sync_via_cron' ) );
+ }
+
+ /**
+ * Decides if the given post should be Publicized based on its type.
+ *
+ * @access public
+ * @static
+ *
+ * @param bool $should_publicize Publicize status prior to this filter running.
+ * @param \WP_Post $post The post to test for Publicizability.
+ * @return bool
+ */
+ public static function prevent_publicize_blacklisted_posts( $should_publicize, $post ) {
+ if ( in_array( $post->post_type, Settings::get_setting( 'post_types_blacklist' ), true ) ) {
+ return false;
+ }
+
+ return $should_publicize;
+ }
+
+ /**
+ * Set an importing flag to `true` in sync settings.
+ *
+ * @access public
+ * @static
+ */
+ public static function set_is_importing_true() {
+ Settings::set_importing( true );
+ }
+
+ /**
+ * Sends data to WordPress.com via an XMLRPC request.
+ *
+ * @access public
+ * @static
+ *
+ * @param object $data Data relating to a sync action.
+ * @param string $codec_name The name of the codec that encodes the data.
+ * @param float $sent_timestamp Current server time so we can compensate for clock differences.
+ * @param string $queue_id The queue the action belongs to, sync or full_sync.
+ * @param float $checkout_duration Time spent retrieving queue items from the DB.
+ * @param float $preprocess_duration Time spent converting queue items into data to send.
+ * @param int $queue_size The size of the sync queue at the time of processing.
+ * @param string $buffer_id The ID of the Queue buffer checked out for processing.
+ * @return Jetpack_Error|mixed|WP_Error The result of the sending request.
+ */
+ public static function send_data( $data, $codec_name, $sent_timestamp, $queue_id, $checkout_duration, $preprocess_duration, $queue_size = null, $buffer_id = null ) {
+
+ $query_args = array(
+ 'sync' => '1', // Add an extra parameter to the URL so we can tell it's a sync action.
+ 'codec' => $codec_name,
+ 'timestamp' => $sent_timestamp,
+ 'queue' => $queue_id,
+ 'home' => Functions::home_url(), // Send home url option to check for Identity Crisis server-side.
+ 'siteurl' => Functions::site_url(), // Send siteurl option to check for Identity Crisis server-side.
+ 'cd' => sprintf( '%.4f', $checkout_duration ),
+ 'pd' => sprintf( '%.4f', $preprocess_duration ),
+ 'queue_size' => $queue_size,
+ 'buffer_id' => $buffer_id,
+ );
+
+ // Has the site opted in to IDC mitigation?
+ if ( \Jetpack::sync_idc_optin() ) {
+ $query_args['idc'] = true;
+ }
+
+ if ( \Jetpack_Options::get_option( 'migrate_for_idc', false ) ) {
+ $query_args['migrate_for_idc'] = true;
+ }
+
+ $query_args['timeout'] = Settings::is_doing_cron() ? 30 : 15;
+
+ /**
+ * Filters query parameters appended to the Sync request URL sent to WordPress.com.
+ *
+ * @since 4.7.0
+ *
+ * @param array $query_args associative array of query parameters.
+ */
+ $query_args = apply_filters( 'jetpack_sync_send_data_query_args', $query_args );
+
+ $connection = new Jetpack_Connection();
+ $url = add_query_arg( $query_args, $connection->xmlrpc_api_url() );
+
+ // If we're currently updating to Jetpack 7.7, the IXR client may be missing briefly
+ // because since 7.7 it's being autoloaded with Composer.
+ if ( ! class_exists( '\\Jetpack_IXR_Client' ) ) {
+ return new \WP_Error(
+ 'ixr_client_missing',
+ esc_html__( 'Sync has been aborted because the IXR client is missing.', 'jetpack' )
+ );
+ }
+
+ $rpc = new \Jetpack_IXR_Client(
+ array(
+ 'url' => $url,
+ 'timeout' => $query_args['timeout'],
+ )
+ );
+
+ $result = $rpc->query( 'jetpack.syncActions', $data );
+
+ if ( ! $result ) {
+ return $rpc->get_jetpack_error();
+ }
+
+ $response = $rpc->getResponse();
+
+ // Check if WordPress.com IDC mitigation blocked the sync request.
+ if ( is_array( $response ) && isset( $response['error_code'] ) ) {
+ $error_code = $response['error_code'];
+ $allowed_idc_error_codes = array(
+ 'jetpack_url_mismatch',
+ 'jetpack_home_url_mismatch',
+ 'jetpack_site_url_mismatch',
+ );
+
+ if ( in_array( $error_code, $allowed_idc_error_codes, true ) ) {
+ \Jetpack_Options::update_option(
+ 'sync_error_idc',
+ \Jetpack::get_sync_error_idc_option( $response )
+ );
+ }
+
+ return new \WP_Error(
+ 'sync_error_idc',
+ esc_html__( 'Sync has been blocked from WordPress.com because it would cause an identity crisis', 'jetpack' )
+ );
+ }
+
+ return $response;
+ }
+
+ /**
+ * Kicks off the initial sync.
+ *
+ * @access public
+ * @static
+ *
+ * @return bool|null False if sync is not allowed.
+ */
+ public static function do_initial_sync() {
+ // Lets not sync if we are not suppose to.
+ if ( ! self::sync_allowed() ) {
+ return false;
+ }
+
+ // Don't start new sync if a full sync is in process.
+ $full_sync_module = Modules::get_module( 'full-sync' );
+ if ( $full_sync_module && $full_sync_module->is_started() && ! $full_sync_module->is_finished() ) {
+ return false;
+ }
+
+ $initial_sync_config = array(
+ 'options' => true,
+ 'functions' => true,
+ 'constants' => true,
+ 'users' => array( get_current_user_id() ),
+ 'network_options' => true,
+ );
+
+ self::do_full_sync( $initial_sync_config );
+ }
+
+ /**
+ * Kicks off a full sync.
+ *
+ * @access public
+ * @static
+ *
+ * @param array $modules The sync modules should be included in this full sync. All will be included if null.
+ * @return bool True if full sync was successfully started.
+ */
+ public static function do_full_sync( $modules = null ) {
+ if ( ! self::sync_allowed() ) {
+ return false;
+ }
+
+ $full_sync_module = Modules::get_module( 'full-sync' );
+
+ if ( ! $full_sync_module ) {
+ return false;
+ }
+
+ self::initialize_listener();
+
+ $full_sync_module->start( $modules );
+
+ return true;
+ }
+
+ /**
+ * Adds a cron schedule for regular syncing via cron, unless the schedule already exists.
+ *
+ * @access public
+ * @static
+ *
+ * @param array $schedules The list of WordPress cron schedules prior to this filter.
+ * @return array A list of WordPress cron schedules with the Jetpack sync interval added.
+ */
+ public static function jetpack_cron_schedule( $schedules ) {
+ if ( ! isset( $schedules[ self::DEFAULT_SYNC_CRON_INTERVAL_NAME ] ) ) {
+ $minutes = (int) ( self::DEFAULT_SYNC_CRON_INTERVAL_VALUE / 60 );
+ $display = ( 1 === $minutes ) ?
+ __( 'Every minute', 'jetpack' ) :
+ /* translators: %d is an integer indicating the number of minutes. */
+ sprintf( __( 'Every %d minutes', 'jetpack' ), $minutes );
+ $schedules[ self::DEFAULT_SYNC_CRON_INTERVAL_NAME ] = array(
+ 'interval' => self::DEFAULT_SYNC_CRON_INTERVAL_VALUE,
+ 'display' => $display,
+ );
+ }
+ return $schedules;
+ }
+
+ /**
+ * Starts an incremental sync via cron.
+ *
+ * @access public
+ * @static
+ */
+ public static function do_cron_sync() {
+ self::do_cron_sync_by_type( 'sync' );
+ }
+
+ /**
+ * Starts a full sync via cron.
+ *
+ * @access public
+ * @static
+ */
+ public static function do_cron_full_sync() {
+ self::do_cron_sync_by_type( 'full_sync' );
+ }
+
+ /**
+ * Try to send actions until we run out of things to send,
+ * or have to wait more than 15s before sending again,
+ * or we hit a lock or some other sending issue
+ *
+ * @access public
+ * @static
+ *
+ * @param string $type Sync type. Can be `sync` or `full_sync`.
+ */
+ public static function do_cron_sync_by_type( $type ) {
+ if ( ! self::sync_allowed() || ( 'sync' !== $type && 'full_sync' !== $type ) ) {
+ return;
+ }
+
+ self::initialize_sender();
+
+ $time_limit = Settings::get_setting( 'cron_sync_time_limit' );
+ $start_time = time();
+ $executions = 0;
+
+ do {
+ $next_sync_time = self::$sender->get_next_sync_time( $type );
+
+ if ( $next_sync_time ) {
+ $delay = $next_sync_time - time() + 1;
+ if ( $delay > 15 ) {
+ break;
+ } elseif ( $delay > 0 ) {
+ sleep( $delay );
+ }
+ }
+
+ // Explicitly only allow 1 do_full_sync call until issue with Immediate Full Sync is resolved.
+ // For more context see p1HpG7-9pe-p2.
+ if ( 'full_sync' === $type && $executions >= 1 ) {
+ break;
+ }
+
+ $result = 'full_sync' === $type ? self::$sender->do_full_sync() : self::$sender->do_sync();
+
+ // # of send actions performed.
+ $executions ++;
+
+ } while ( $result && ! is_wp_error( $result ) && ( $start_time + $time_limit ) > time() );
+
+ return $executions;
+ }
+
+ /**
+ * Initialize the sync listener.
+ *
+ * @access public
+ * @static
+ */
+ public static function initialize_listener() {
+ self::$listener = Listener::get_instance();
+ }
+
+ /**
+ * Initializes the sync sender.
+ *
+ * @access public
+ * @static
+ */
+ public static function initialize_sender() {
+ self::$sender = Sender::get_instance();
+ add_filter( 'jetpack_sync_send_data', array( __CLASS__, 'send_data' ), 10, 8 );
+ }
+
+ /**
+ * Initializes sync for WooCommerce.
+ *
+ * @access public
+ * @static
+ */
+ public static function initialize_woocommerce() {
+ if ( false === class_exists( 'WooCommerce' ) ) {
+ return;
+ }
+ add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_woocommerce_sync_module' ) );
+ }
+
+ /**
+ * Adds Woo's sync modules to existing modules for sending.
+ *
+ * @access public
+ * @static
+ *
+ * @param array $sync_modules The list of sync modules declared prior to this filter.
+ * @return array A list of sync modules that now includes Woo's modules.
+ */
+ public static function add_woocommerce_sync_module( $sync_modules ) {
+ $sync_modules[] = 'Automattic\\Jetpack\\Sync\\Modules\\WooCommerce';
+ return $sync_modules;
+ }
+
+ /**
+ * Initializes sync for WP Super Cache.
+ *
+ * @access public
+ * @static
+ */
+ public static function initialize_wp_super_cache() {
+ if ( false === function_exists( 'wp_cache_is_enabled' ) ) {
+ return;
+ }
+ add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_wp_super_cache_sync_module' ) );
+ }
+
+ /**
+ * Adds WP Super Cache's sync modules to existing modules for sending.
+ *
+ * @access public
+ * @static
+ *
+ * @param array $sync_modules The list of sync modules declared prior to this filer.
+ * @return array A list of sync modules that now includes WP Super Cache's modules.
+ */
+ public static function add_wp_super_cache_sync_module( $sync_modules ) {
+ $sync_modules[] = 'Automattic\\Jetpack\\Sync\\Modules\\WP_Super_Cache';
+ return $sync_modules;
+ }
+
+ /**
+ * Sanitizes the name of sync's cron schedule.
+ *
+ * @access public
+ * @static
+ *
+ * @param string $schedule The name of a WordPress cron schedule.
+ * @return string The sanitized name of sync's cron schedule.
+ */
+ public static function sanitize_filtered_sync_cron_schedule( $schedule ) {
+ $schedule = sanitize_key( $schedule );
+ $schedules = wp_get_schedules();
+
+ // Make sure that the schedule has actually been registered using the `cron_intervals` filter.
+ if ( isset( $schedules[ $schedule ] ) ) {
+ return $schedule;
+ }
+
+ return self::DEFAULT_SYNC_CRON_INTERVAL_NAME;
+ }
+
+ /**
+ * Allows offsetting of start times for sync cron jobs.
+ *
+ * @access public
+ * @static
+ *
+ * @param string $schedule The name of a cron schedule.
+ * @param string $hook The hook that this method is responding to.
+ * @return int The offset for the sync cron schedule.
+ */
+ public static function get_start_time_offset( $schedule = '', $hook = '' ) {
+ $start_time_offset = is_multisite()
+ ? wp_rand( 0, ( 2 * self::DEFAULT_SYNC_CRON_INTERVAL_VALUE ) )
+ : 0;
+
+ /**
+ * Allows overriding the offset that the sync cron jobs will first run. This can be useful when scheduling
+ * cron jobs across multiple sites in a network.
+ *
+ * @since 4.5.0
+ *
+ * @param int $start_time_offset
+ * @param string $hook
+ * @param string $schedule
+ */
+ return (int) apply_filters(
+ 'jetpack_sync_cron_start_time_offset',
+ $start_time_offset,
+ $hook,
+ $schedule
+ );
+ }
+
+ /**
+ * Decides if a sync cron should be scheduled.
+ *
+ * @access public
+ * @static
+ *
+ * @param string $schedule The name of a cron schedule.
+ * @param string $hook The hook that this method is responding to.
+ */
+ public static function maybe_schedule_sync_cron( $schedule, $hook ) {
+ if ( ! $hook ) {
+ return;
+ }
+ $schedule = self::sanitize_filtered_sync_cron_schedule( $schedule );
+
+ $start_time = time() + self::get_start_time_offset( $schedule, $hook );
+ if ( ! wp_next_scheduled( $hook ) ) {
+ // Schedule a job to send pending queue items once a minute.
+ wp_schedule_event( $start_time, $schedule, $hook );
+ } elseif ( wp_get_schedule( $hook ) !== $schedule ) {
+ // If the schedule has changed, update the schedule.
+ wp_clear_scheduled_hook( $hook );
+ wp_schedule_event( $start_time, $schedule, $hook );
+ }
+ }
+
+ /**
+ * Clears Jetpack sync cron jobs.
+ *
+ * @access public
+ * @static
+ */
+ public static function clear_sync_cron_jobs() {
+ wp_clear_scheduled_hook( 'jetpack_sync_cron' );
+ wp_clear_scheduled_hook( 'jetpack_sync_full_cron' );
+ }
+
+ /**
+ * Initializes Jetpack sync cron jobs.
+ *
+ * @access public
+ * @static
+ */
+ public static function init_sync_cron_jobs() {
+ add_filter( 'cron_schedules', array( __CLASS__, 'jetpack_cron_schedule' ) ); // phpcs:ignore WordPress.WP.CronInterval.ChangeDetected
+
+ add_action( 'jetpack_sync_cron', array( __CLASS__, 'do_cron_sync' ) );
+ add_action( 'jetpack_sync_full_cron', array( __CLASS__, 'do_cron_full_sync' ) );
+
+ /**
+ * Allows overriding of the default incremental sync cron schedule which defaults to once every 5 minutes.
+ *
+ * @since 4.3.2
+ *
+ * @param string self::DEFAULT_SYNC_CRON_INTERVAL_NAME
+ */
+ $incremental_sync_cron_schedule = apply_filters( 'jetpack_sync_incremental_sync_interval', self::DEFAULT_SYNC_CRON_INTERVAL_NAME );
+ self::maybe_schedule_sync_cron( $incremental_sync_cron_schedule, 'jetpack_sync_cron' );
+
+ /**
+ * Allows overriding of the full sync cron schedule which defaults to once every 5 minutes.
+ *
+ * @since 4.3.2
+ *
+ * @param string self::DEFAULT_SYNC_CRON_INTERVAL_NAME
+ */
+ $full_sync_cron_schedule = apply_filters( 'jetpack_sync_full_sync_interval', self::DEFAULT_SYNC_CRON_INTERVAL_NAME );
+ self::maybe_schedule_sync_cron( $full_sync_cron_schedule, 'jetpack_sync_full_cron' );
+ }
+
+ /**
+ * Perform maintenance when a plugin upgrade occurs.
+ *
+ * @access public
+ * @static
+ *
+ * @param string $new_version New version of the plugin.
+ * @param string $old_version Old version of the plugin.
+ */
+ public static function cleanup_on_upgrade( $new_version = null, $old_version = null ) {
+ if ( wp_next_scheduled( 'jetpack_sync_send_db_checksum' ) ) {
+ wp_clear_scheduled_hook( 'jetpack_sync_send_db_checksum' );
+ }
+
+ $is_new_sync_upgrade = version_compare( $old_version, '4.2', '>=' );
+ if ( ! empty( $old_version ) && $is_new_sync_upgrade && version_compare( $old_version, '4.5', '<' ) ) {
+ self::clear_sync_cron_jobs();
+ Settings::update_settings(
+ array(
+ 'render_filtered_content' => Defaults::$default_render_filtered_content,
+ )
+ );
+ }
+
+ Health::on_jetpack_upgraded();
+ }
+
+ /**
+ * Get syncing status for the given fields.
+ *
+ * @access public
+ * @static
+ *
+ * @param string|null $fields A comma-separated string of the fields to include in the array from the JSON response.
+ * @return array An associative array with the status report.
+ */
+ public static function get_sync_status( $fields = null ) {
+ self::initialize_sender();
+
+ $sync_module = Modules::get_module( 'full-sync' );
+ $queue = self::$sender->get_sync_queue();
+
+ // _get_cron_array can be false
+ $cron_timestamps = ( _get_cron_array() ) ? array_keys( _get_cron_array() ) : array();
+ $next_cron = ( ! empty( $cron_timestamps ) ) ? $cron_timestamps[0] - time() : '';
+
+ $checksums = array();
+ $debug = array();
+
+ if ( ! empty( $fields ) ) {
+ $store = new Replicastore();
+ $fields_params = array_map( 'trim', explode( ',', $fields ) );
+
+ if ( in_array( 'posts_checksum', $fields_params, true ) ) {
+ $checksums['posts_checksum'] = $store->posts_checksum();
+ }
+ if ( in_array( 'comments_checksum', $fields_params, true ) ) {
+ $checksums['comments_checksum'] = $store->comments_checksum();
+ }
+ if ( in_array( 'post_meta_checksum', $fields_params, true ) ) {
+ $checksums['post_meta_checksum'] = $store->post_meta_checksum();
+ }
+ if ( in_array( 'comment_meta_checksum', $fields_params, true ) ) {
+ $checksums['comment_meta_checksum'] = $store->comment_meta_checksum();
+ }
+
+ if ( in_array( 'debug_details', $fields_params, true ) ) {
+ $debug = self::get_debug_details();
+ }
+ }
+
+ $full_sync_status = ( $sync_module ) ? $sync_module->get_status() : array();
+
+ $full_queue = self::$sender->get_full_sync_queue();
+
+ $result = array_merge(
+ $full_sync_status,
+ $checksums,
+ $debug,
+ array(
+ 'cron_size' => count( $cron_timestamps ),
+ 'next_cron' => $next_cron,
+ 'queue_size' => $queue->size(),
+ 'queue_lag' => $queue->lag(),
+ 'queue_next_sync' => ( self::$sender->get_next_sync_time( 'sync' ) - microtime( true ) ),
+ 'full_queue_next_sync' => ( self::$sender->get_next_sync_time( 'full_sync' ) - microtime( true ) ),
+ )
+ );
+
+ // Verify $sync_module is not false.
+ if ( ( $sync_module ) && false === strpos( get_class( $sync_module ), 'Full_Sync_Immediately' ) ) {
+ $result['full_queue_size'] = $full_queue->size();
+ $result['full_queue_lag'] = $full_queue->lag();
+ }
+ return $result;
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/class-defaults.php b/vendor/automattic/jetpack-sync/src/class-defaults.php
new file mode 100644
index 0000000000000..5788baa2e5f74
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/class-defaults.php
@@ -0,0 +1,1269 @@
+ 'wp_max_upload_size',
+ 'is_main_network' => array( __CLASS__, 'is_multi_network' ),
+ 'is_multi_site' => 'is_multisite',
+ 'main_network_site' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'main_network_site_url' ),
+ 'main_network_site_wpcom_id' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'main_network_site_wpcom_id' ),
+ 'site_url' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'site_url' ),
+ 'home_url' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'home_url' ),
+ 'single_user_site' => array( 'Jetpack', 'is_single_user_site' ),
+ 'updates' => array( 'Jetpack', 'get_updates' ),
+ 'has_file_system_write_access' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'file_system_write_access' ),
+ 'is_version_controlled' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'is_version_controlled' ),
+ 'taxonomies' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_taxonomies' ),
+ 'post_types' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_post_types' ),
+ 'post_type_features' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_post_type_features' ),
+ 'shortcodes' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_shortcodes' ),
+ 'rest_api_allowed_post_types' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'rest_api_allowed_post_types' ),
+ 'rest_api_allowed_public_metadata' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'rest_api_allowed_public_metadata' ),
+ 'wp_version' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'wp_version' ),
+ 'get_plugins' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_plugins' ),
+ 'get_plugins_action_links' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_plugins_action_links' ),
+ 'active_modules' => array( 'Jetpack', 'get_active_modules' ),
+ 'hosting_provider' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_hosting_provider' ),
+ 'locale' => 'get_locale',
+ 'site_icon_url' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'site_icon_url' ),
+ 'roles' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'roles' ),
+ 'timezone' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_timezone' ),
+ 'available_jetpack_blocks' => array( 'Jetpack_Gutenberg', 'get_availability' ), // Includes both Gutenberg blocks *and* plugins.
+ 'paused_themes' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_paused_themes' ),
+ 'paused_plugins' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_paused_plugins' ),
+ 'theme_support' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_theme_support' ),
+
+ );
+
+ /**
+ * Array of post type attributes synced.
+ *
+ * @var array Default post type attributes.
+ */
+ public static $default_post_type_attributes = array(
+ 'name' => '',
+ 'label' => '',
+ 'labels' => array(),
+ 'description' => '',
+ 'public' => false,
+ 'hierarchical' => false,
+ 'exclude_from_search' => true,
+ 'publicly_queryable' => null,
+ 'show_ui' => false,
+ 'show_in_menu' => null,
+ 'show_in_nav_menus' => null,
+ 'show_in_admin_bar' => false,
+ 'menu_position' => null,
+ 'menu_icon' => null,
+ 'supports' => array(),
+ 'capability_type' => 'post',
+ 'capabilities' => array(),
+ 'cap' => array(),
+ 'map_meta_cap' => true,
+ 'taxonomies' => array(),
+ 'has_archive' => false,
+ 'rewrite' => true,
+ 'query_var' => true,
+ 'can_export' => true,
+ 'delete_with_user' => null,
+ 'show_in_rest' => false,
+ 'rest_base' => false,
+ '_builtin' => false,
+ '_edit_link' => 'post.php?post=%d',
+ );
+
+ /**
+ * Get the whitelist of callables allowed to be managed via the JSON API.
+ *
+ * @return array Whitelist of callables allowed to be managed via the JSON API.
+ */
+ public static function get_callable_whitelist() {
+ $default = self::$default_callable_whitelist;
+
+ if ( defined( 'JETPACK__PLUGIN_DIR' ) && include_once JETPACK__PLUGIN_DIR . 'modules/sso/class.jetpack-sso-helpers.php' ) {
+ $sso_helpers = array(
+ 'sso_is_two_step_required' => array( 'Jetpack_SSO_Helpers', 'is_two_step_required' ),
+ 'sso_should_hide_login_form' => array( 'Jetpack_SSO_Helpers', 'should_hide_login_form' ),
+ 'sso_match_by_email' => array( 'Jetpack_SSO_Helpers', 'match_by_email' ),
+ 'sso_new_user_override' => array( 'Jetpack_SSO_Helpers', 'new_user_override' ),
+ 'sso_bypass_default_login_form' => array( 'Jetpack_SSO_Helpers', 'bypass_login_forward_wpcom' ),
+ );
+ $default = array_merge( $default, $sso_helpers );
+ }
+
+ /**
+ * Filter the list of callables that are manageable via the JSON API.
+ *
+ * @module sync
+ *
+ * @since 4.8.0
+ *
+ * @param array The default list of callables.
+ */
+ return apply_filters( 'jetpack_sync_callable_whitelist', $default );
+ }
+
+ /**
+ * Post types that will not be synced.
+ *
+ * These are usually automated post types (sitemaps, logs, etc).
+ *
+ * @var array Blacklisted post types.
+ */
+ public static $blacklisted_post_types = array(
+ 'ai_log', // Logger - https://github.com/alleyinteractive/logger.
+ 'ai1ec_event',
+ 'bwg_album',
+ 'bwg_gallery',
+ 'customize_changeset', // WP built-in post type for Customizer changesets.
+ 'dn_wp_yt_log',
+ 'flamingo_contact', // https://wordpress.org/plugins/flamingo/.
+ 'flamingo_inbound',
+ 'flamingo_outbound',
+ 'http',
+ 'idx_page',
+ 'jetpack_migration',
+ 'jp_img_sitemap',
+ 'jp_img_sitemap_index',
+ 'jp_sitemap',
+ 'jp_sitemap_index',
+ 'jp_sitemap_master',
+ 'jp_vid_sitemap',
+ 'jp_vid_sitemap_index',
+ 'msm_sitemap', // Metro Sitemap Plugin.
+ 'postman_sent_mail',
+ 'rssap-feed',
+ 'rssmi_feed_item',
+ 'scheduled-action', // Action Scheduler - Job Queue for WordPress https://github.com/woocommerce/woocommerce/tree/e7762627c37ec1f7590e6cac4218ba0c6a20024d/includes/libraries/action-scheduler .
+ 'secupress_log_action',
+ 'sg_optimizer_jobs',
+ 'snitch',
+ 'vip-legacy-redirect',
+ 'wp_automatic',
+ 'wp_log', // WP Logging Plugin.
+ 'wp-rest-api-log', // https://wordpress.org/plugins/wp-rest-api-log/.
+ 'wpephpcompat_jobs',
+ 'wprss_feed_item',
+ );
+
+ /**
+ * Taxonomies that we're not syncing by default.
+ *
+ * The list is compiled by auditing the dynamic filters and actions that contain taxonomy slugs
+ * and could conflict with other existing filters/actions in WP core, Jetpack and WooCommerce.
+ *
+ * @var array
+ */
+ public static $blacklisted_taxonomies = array(
+ 'ancestors',
+ 'archives_link',
+ 'attached_file',
+ 'attached_media',
+ 'attached_media_args',
+ 'attachment',
+ 'available_languages',
+ 'avatar',
+ 'avatar_comment_types',
+ 'avatar_data',
+ 'avatar_url',
+ 'bloginfo_rss',
+ 'blogs_of_user',
+ 'bookmark_link',
+ 'bookmarks',
+ 'calendar',
+ 'canonical_url',
+ 'categories_per_page',
+ 'categories_taxonomy',
+ 'category_form',
+ 'category_form_fields',
+ 'category_form_pre',
+ 'comment',
+ 'comment_author',
+ 'comment_author_email',
+ 'comment_author_IP',
+ 'comment_author_link',
+ 'comment_author_url',
+ 'comment_author_url_link',
+ 'comment_date',
+ 'comment_excerpt',
+ 'comment_ID',
+ 'comment_link',
+ 'comment_misc_actions',
+ 'comment_text',
+ 'comment_time',
+ 'comment_type',
+ 'comments_link',
+ 'comments_number',
+ 'comments_pagenum_link',
+ 'custom_logo',
+ 'date_sql',
+ 'default_comment_status',
+ 'delete_post_link',
+ 'edit_bookmark_link',
+ 'edit_comment_link',
+ 'edit_post_link',
+ 'edit_tag_link',
+ 'edit_term_link',
+ 'edit_user_link',
+ 'enclosed',
+ 'feed_build_date',
+ 'form_advanced',
+ 'form_after_editor',
+ 'form_after_title',
+ 'form_before_permalink',
+ 'form_top',
+ 'handle_product_cat',
+ 'header_image_tag',
+ 'header_video_url',
+ 'image_tag',
+ 'image_tag_class',
+ 'lastpostdate',
+ 'lastpostmodified',
+ 'link',
+ 'link_category_form',
+ 'link_category_form_fields',
+ 'link_category_form_pre',
+ 'main_network_id',
+ 'media',
+ 'media_item_args',
+ 'ms_user',
+ 'network',
+ 'object_terms',
+ 'option',
+ 'page',
+ 'page_form',
+ 'page_of_comment',
+ 'page_uri',
+ 'pagenum_link',
+ 'pages',
+ 'plugin',
+ 'post',
+ 'post_galleries',
+ 'post_gallery',
+ 'post_link',
+ 'post_modified_time',
+ 'post_status',
+ 'post_time',
+ 'postmeta',
+ 'posts_per_page',
+ 'product_cat',
+ 'product_search_form',
+ 'profile_url',
+ 'pung',
+ 'role_list',
+ 'sample_permalink',
+ 'sample_permalink_html',
+ 'schedule',
+ 'search_form',
+ 'search_query',
+ 'shortlink',
+ 'site',
+ 'site_email_content',
+ 'site_icon_url',
+ 'site_option',
+ 'space_allowed',
+ 'tag',
+ 'tag_form',
+ 'tag_form_fields',
+ 'tag_form_pre',
+ 'tag_link',
+ 'tags',
+ 'tags_per_page',
+ 'term',
+ 'term_link',
+ 'term_relationships',
+ 'term_taxonomies',
+ 'term_taxonomy',
+ 'terms',
+ 'terms_args',
+ 'terms_defaults',
+ 'terms_fields',
+ 'terms_orderby',
+ 'the_archive_description',
+ 'the_archive_title',
+ 'the_categories',
+ 'the_date',
+ 'the_excerpt',
+ 'the_guid',
+ 'the_modified_date',
+ 'the_modified_time',
+ 'the_post_type_description',
+ 'the_tags',
+ 'the_terms',
+ 'the_time',
+ 'theme_starter_content',
+ 'to_ping',
+ 'user',
+ 'user_created_user',
+ 'user_form',
+ 'user_profile',
+ 'user_profile_update',
+ 'usermeta',
+ 'usernumposts',
+ 'users_drafts',
+ 'webhook',
+ 'widget',
+ 'woocommerce_archive',
+ 'wp_title_rss',
+ );
+
+ /**
+ * Default array of post table columns.
+ *
+ * @var array Post table columns.
+ */
+ public static $default_post_checksum_columns = array(
+ 'ID',
+ 'post_modified',
+ );
+
+ /**
+ * Default array of post meta table columns.
+ *
+ * @var array Post meta table columns.
+ */
+ public static $default_post_meta_checksum_columns = array(
+ 'meta_id',
+ 'meta_value',
+ );
+
+ /**
+ * Default array of comment table columns.
+ *
+ * @var array Default comment table columns.
+ */
+ public static $default_comment_checksum_columns = array(
+ 'comment_ID',
+ 'comment_content',
+ );
+
+ /**
+ * Default array of comment meta columns.
+ *
+ * @var array Comment meta table columns.
+ */
+ public static $default_comment_meta_checksum_columns = array(
+ 'meta_id',
+ 'meta_value',
+ );
+
+ /**
+ * Default array of option table columns.
+ *
+ * @var array Default array of option columns.
+ */
+ public static $default_option_checksum_columns = array(
+ 'option_name',
+ 'option_value',
+ );
+
+ /**
+ * Default array of term columns.
+ *
+ * @var array array of term columns.
+ */
+ public static $default_term_checksum_columns = array(
+ 'term_id',
+ 'name',
+ 'slug',
+ );
+
+ /**
+ * Default array of term taxonomy columns.
+ *
+ * @var array Array of term taxonomy columns.
+ */
+ public static $default_term_taxonomy_checksum_columns = array(
+ 'term_taxonomy_id',
+ 'term_id',
+ 'taxonomy',
+ 'parent',
+ 'count',
+ );
+
+ /**
+ * Default term relationship columns.
+ *
+ * @var array Array of term relationship columns.
+ */
+ public static $default_term_relationships_checksum_columns = array(
+ 'object_id',
+ 'term_taxonomy_id',
+ 'term_order',
+ );
+
+ /**
+ * Default multisite callables able to be managed via JSON API.
+ *
+ * @var array multsite callables whitelisted
+ */
+ public static $default_multisite_callable_whitelist = array(
+ 'network_name' => array( 'Jetpack', 'network_name' ),
+ 'network_allow_new_registrations' => array( 'Jetpack', 'network_allow_new_registrations' ),
+ 'network_add_new_users' => array( 'Jetpack', 'network_add_new_users' ),
+ 'network_site_upload_space' => array( 'Jetpack', 'network_site_upload_space' ),
+ 'network_upload_file_types' => array( 'Jetpack', 'network_upload_file_types' ),
+ 'network_enable_administration_menus' => array( 'Jetpack', 'network_enable_administration_menus' ),
+ );
+
+ /**
+ * Get array of multisite callables whitelisted.
+ *
+ * @return array Multisite callables managable via JSON API.
+ */
+ public static function get_multisite_callable_whitelist() {
+ /**
+ * Filter the list of multisite callables that are manageable via the JSON API.
+ *
+ * @module sync
+ *
+ * @since 4.8.0
+ *
+ * @param array The default list of multisite callables.
+ */
+ return apply_filters( 'jetpack_sync_multisite_callable_whitelist', self::$default_multisite_callable_whitelist );
+ }
+
+ /**
+ * Array of post meta keys whitelisted.
+ *
+ * @var array Post meta whitelist.
+ */
+ public static $post_meta_whitelist = array(
+ '_feedback_akismet_values',
+ '_feedback_email',
+ '_feedback_extra_fields',
+ '_g_feedback_shortcode',
+ '_jetpack_post_thumbnail',
+ '_last_editor_used_jetpack',
+ '_menu_item_classes',
+ '_menu_item_menu_item_parent',
+ '_menu_item_object',
+ '_menu_item_object_id',
+ '_menu_item_orphaned',
+ '_menu_item_type',
+ '_menu_item_xfn',
+ '_publicize_facebook_user',
+ '_publicize_twitter_user',
+ '_thumbnail_id',
+ '_wp_attached_file',
+ '_wp_attachment_backup_sizes',
+ '_wp_attachment_context',
+ '_wp_attachment_image_alt',
+ '_wp_attachment_is_custom_background',
+ '_wp_attachment_is_custom_header',
+ '_wp_attachment_metadata',
+ '_wp_page_template',
+ '_wp_trash_meta_comments_status',
+ '_wpas_mess',
+ '_wpas_is_tweetstorm',
+ 'content_width',
+ 'custom_css_add',
+ 'custom_css_preprocessor',
+ 'enclosure',
+ 'imagedata',
+ 'nova_price',
+ 'publicize_results',
+ 'sharing_disabled',
+ 'switch_like_status',
+ 'videopress_guid',
+ 'vimeo_poster_image',
+ 'advanced_seo_description', // Jetpack_SEO_Posts::DESCRIPTION_META_KEY.
+ );
+
+ /**
+ * Get the post meta key whitelist.
+ *
+ * @return array Post meta whitelist.
+ */
+ public static function get_post_meta_whitelist() {
+ /**
+ * Filter the list of post meta data that are manageable via the JSON API.
+ *
+ * @module sync
+ *
+ * @since 4.8.0
+ *
+ * @param array The default list of meta data keys.
+ */
+ return apply_filters( 'jetpack_sync_post_meta_whitelist', self::$post_meta_whitelist );
+ }
+
+ /**
+ * Comment meta whitelist.
+ *
+ * @var array Comment meta whitelist.
+ */
+ public static $comment_meta_whitelist = array(
+ 'hc_avatar',
+ 'hc_post_as',
+ 'hc_wpcom_id_sig',
+ 'hc_foreign_user_id',
+ );
+
+ /**
+ * Get the comment meta whitelist.
+ *
+ * @return array
+ */
+ public static function get_comment_meta_whitelist() {
+ /**
+ * Filter the list of comment meta data that are manageable via the JSON API.
+ *
+ * @module sync
+ *
+ * @since 5.7.0
+ *
+ * @param array The default list of comment meta data keys.
+ */
+ return apply_filters( 'jetpack_sync_comment_meta_whitelist', self::$comment_meta_whitelist );
+ }
+
+ /**
+ * Default theme support whitelist.
+ *
+ * @todo move this to server? - these are theme support values
+ * that should be synced as jetpack_current_theme_supports_foo option values
+ *
+ * @var array Default theme support whitelist.
+ */
+ public static $default_theme_support_whitelist = array(
+ 'post-thumbnails',
+ 'post-formats',
+ 'custom-header',
+ 'custom-background',
+ 'custom-logo',
+ 'menus',
+ 'automatic-feed-links',
+ 'align-wide',
+ 'wp-block-styles',
+ 'responsive-embeds',
+ 'disable-custom-gradients',
+ 'disable-custom-font-sizes',
+ 'disable-custom-colors',
+ 'dark-editor-style',
+ 'customize-selective-refresh-widgets',
+ 'widgets',
+ 'html5',
+ 'title-tag',
+ 'jetpack-social-menu',
+ 'jetpack-responsive-videos',
+ 'infinite-scroll',
+ 'site-logo',
+ 'editor-color-palette',
+ 'editor-gradient-presets',
+ 'editor-font-sizes',
+ 'editor-styles',
+ 'editor-style', // deprecated.
+ );
+
+ /**
+ * Is an option whitelisted?
+ *
+ * @param string $option Option name.
+ * @return bool If option is on the whitelist.
+ */
+ public static function is_whitelisted_option( $option ) {
+ $whitelisted_options = self::get_options_whitelist();
+ foreach ( $whitelisted_options as $whitelisted_option ) {
+ if ( '/' === $whitelisted_option[0] && preg_match( $whitelisted_option, $option ) ) {
+ return true;
+ } elseif ( $whitelisted_option === $option ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Default whitelist of capabilities to sync.
+ *
+ * @var array Array of WordPress capabilities.
+ */
+ public static $default_capabilities_whitelist = array(
+ 'switch_themes',
+ 'edit_themes',
+ 'edit_theme_options',
+ 'install_themes',
+ 'activate_plugins',
+ 'edit_plugins',
+ 'install_plugins',
+ 'edit_users',
+ 'edit_files',
+ 'manage_options',
+ 'moderate_comments',
+ 'manage_categories',
+ 'manage_links',
+ 'upload_files',
+ 'import',
+ 'unfiltered_html',
+ 'edit_posts',
+ 'edit_others_posts',
+ 'edit_published_posts',
+ 'publish_posts',
+ 'edit_pages',
+ 'read',
+ 'publish_pages',
+ 'edit_others_pages',
+ 'edit_published_pages',
+ 'delete_pages',
+ 'delete_others_pages',
+ 'delete_published_pages',
+ 'delete_posts',
+ 'delete_others_posts',
+ 'delete_published_posts',
+ 'delete_private_posts',
+ 'edit_private_posts',
+ 'read_private_posts',
+ 'delete_private_pages',
+ 'edit_private_pages',
+ 'read_private_pages',
+ 'delete_users',
+ 'create_users',
+ 'unfiltered_upload',
+ 'edit_dashboard',
+ 'customize',
+ 'delete_site',
+ 'update_plugins',
+ 'delete_plugins',
+ 'update_themes',
+ 'update_core',
+ 'list_users',
+ 'remove_users',
+ 'add_users',
+ 'promote_users',
+ 'delete_themes',
+ 'export',
+ 'upload_plugins',
+ 'upload_themes',
+ );
+
+ /**
+ * Get default capabilities whitelist.
+ *
+ * @return array
+ */
+ public static function get_capabilities_whitelist() {
+ /**
+ * Filter the list of capabilities that we care about
+ *
+ * @module sync
+ *
+ * @since 5.5.0
+ *
+ * @param array The default list of capabilities.
+ */
+ return apply_filters( 'jetpack_sync_capabilities_whitelist', self::$default_capabilities_whitelist );
+ }
+
+ /**
+ * Get max execution sync time.
+ *
+ * @return float Number of seconds.
+ */
+ public static function get_max_sync_execution_time() {
+ $max_exec_time = (int) ini_get( 'max_execution_time' );
+ if ( 0 === $max_exec_time ) {
+ // 0 actually means "unlimited", but let's not treat it that way.
+ $max_exec_time = 60;
+ }
+ return floor( $max_exec_time / 3 );
+ }
+
+ /**
+ * Get default for a given setting.
+ *
+ * @param string $setting Setting to get.
+ * @return mixed Value will be a string, int, array, based on the particular setting requested.
+ */
+ public static function get_default_setting( $setting ) {
+ $default_name = "default_$setting"; // e.g. default_dequeue_max_bytes.
+ return self::$$default_name;
+ }
+
+ /**
+ * Default list of network options.
+ *
+ * @var array network options
+ */
+ public static $default_network_options_whitelist = array(
+ 'site_name',
+ 'jetpack_protect_key',
+ 'jetpack_protect_global_whitelist',
+ 'active_sitewide_plugins',
+ 'auto_update_plugins', // WordPress 5.5+ auto-updates.
+ );
+
+ /**
+ * A mapping of known importers to friendly names.
+ *
+ * Keys are the class name of the known importer.
+ * Values are the friendly name.
+ *
+ * @since 7.3.0
+ *
+ * @var array
+ */
+ public static $default_known_importers = array(
+ 'Blogger_Importer' => 'blogger',
+ 'LJ_API_Import' => 'livejournal',
+ 'MT_Import' => 'mt',
+ 'RSS_Import' => 'rss',
+ 'WC_Tax_Rate_Importer' => 'woo-tax-rate',
+ 'WP_Import' => 'wordpress',
+ );
+
+ /**
+ * Returns a list of known importers.
+ *
+ * @since 7.3.0
+ *
+ * @return array Known importers with importer class names as keys and friendly names as values.
+ */
+ public static function get_known_importers() {
+ /**
+ * Filter the list of known importers.
+ *
+ * @module sync
+ *
+ * @since 7.3.0
+ *
+ * @param array The default list of known importers.
+ */
+ return apply_filters( 'jetpack_sync_known_importers', self::$default_known_importers );
+ }
+
+ /**
+ * Whether this is a system with a multiple networks.
+ * We currently need this static wrapper because we statically define our default list of callables.
+ *
+ * @since 7.6.0
+ *
+ * @uses Automattic\Jetpack\Status::is_multi_network
+ *
+ * @return boolean
+ */
+ public static function is_multi_network() {
+ $status = new Status();
+ return $status->is_multi_network();
+ }
+
+ /**
+ * Default bytes to dequeue.
+ *
+ * @var int Bytes.
+ */
+ public static $default_dequeue_max_bytes = 500000; // very conservative value, 1/2 MB.
+
+ /**
+ * Default upload bytes.
+ *
+ * This value is a little bigger than the upload limit to account for serialization.
+ *
+ * @var int Bytes.
+ */
+ public static $default_upload_max_bytes = 600000;
+
+ /**
+ * Default number of rows uploaded.
+ *
+ * @var int Number of rows.
+ */
+ public static $default_upload_max_rows = 500;
+
+ /**
+ * Default sync wait time.
+ *
+ * @var int Number of seconds.
+ */
+ public static $default_sync_wait_time = 10; // seconds, between syncs.
+
+ /**
+ * Only wait before next send if the current send took more than this number of seconds.
+ *
+ * @var int Number of seconds.
+ */
+ public static $default_sync_wait_threshold = 10;
+
+ /**
+ * Default wait between attempting to continue a full sync via requests.
+ *
+ * @var int Number of seconds.
+ */
+ public static $default_enqueue_wait_time = 1;
+
+ /**
+ * Maximum queue size.
+ *
+ * Each item is represented with a new row in the wp_options table.
+ *
+ * @var int Number of queue items.
+ */
+ public static $default_max_queue_size = 5000;
+
+ /**
+ * Default maximum lag allowed in the queue.
+ *
+ * @var int Number of seconds
+ */
+ public static $default_max_queue_lag = 7200; // 2 hours.
+
+ /**
+ * Default for default writes per sec.
+ *
+ * @var int Rows per second.
+ */
+ public static $default_queue_max_writes_sec = 100; // 100 rows a second.
+
+ /**
+ * Default for post types blacklist.
+ *
+ * @var array Empty array.
+ */
+ public static $default_post_types_blacklist = array();
+
+ /**
+ * Default for taxonomies blacklist.
+ *
+ * @var array Empty array.
+ */
+ public static $default_taxonomies_blacklist = array();
+
+ /**
+ * Default for taxonomies whitelist.
+ *
+ * @var array Empty array.
+ */
+ public static $default_taxonomy_whitelist = array();
+
+ /**
+ * Default for post meta whitelist.
+ *
+ * @var array Empty array.
+ */
+ public static $default_post_meta_whitelist = array();
+
+ /**
+ * Default for comment meta whitelist.
+ *
+ * @var array Empty array.
+ */
+ public static $default_comment_meta_whitelist = array();
+
+ /**
+ * Default for disabling sync across the site.
+ *
+ * @var int Bool-ish. Default to 0.
+ */
+ public static $default_disable = 0; // completely disable sending data to wpcom.
+
+ /**
+ * Default for disabling sync across the entire network on multisite.
+ *
+ * @var int Bool-ish. Default 0.
+ */
+ public static $default_network_disable = 0;
+
+ /**
+ * Should Sync use cron?
+ *
+ * @var int Bool-ish value. Default 1.
+ */
+ public static $default_sync_via_cron = 1;
+
+ /**
+ * Default if Sync should render content.
+ *
+ * @var int Bool-ish value. Default is 0.
+ */
+ public static $default_render_filtered_content = 0;
+
+ /**
+ * Default number of items to enqueue at a time when running full sync.
+ *
+ * @var int Number of items.
+ */
+ public static $default_max_enqueue_full_sync = 100;
+
+ /**
+ * Default for maximum queue size during a full sync.
+ *
+ * Each item will represent a value in the wp_options table.
+ *
+ * @var int Number of items.
+ */
+ public static $default_max_queue_size_full_sync = 1000; // max number of total items in the full sync queue.
+
+ /**
+ * Default max time for sending in immediate mode.
+ *
+ * @var float Number of Seconds
+ */
+ public static $default_full_sync_send_duration = 9;
+
+ /**
+ * Defaul for time between syncing callables.
+ *
+ * @var int Number of seconds.
+ */
+ public static $default_sync_callables_wait_time = MINUTE_IN_SECONDS; // seconds before sending callables again.
+
+ /**
+ * Default for time between syncing constants.
+ *
+ * @var int Number of seconds.
+ */
+ public static $default_sync_constants_wait_time = HOUR_IN_SECONDS; // seconds before sending constants again.
+ /**
+ * Default for sync queue lock timeout time.
+ *
+ * @var int Number of seconds.
+ */
+ public static $default_sync_queue_lock_timeout = 120; // 2 minutes.
+
+ /**
+ * Default for cron sync time limit.
+ *
+ * @var int Number of seconds.
+ */
+ public static $default_cron_sync_time_limit = 4 * MINUTE_IN_SECONDS;
+
+ /**
+ * Default for number of term relationship items sent in an full sync item.
+ *
+ * @var int Number of items.
+ */
+ public static $default_term_relationships_full_sync_item_size = 100;
+
+ /**
+ * Default for enabling incremental sync.
+ *
+ * @var int 1 for true.
+ */
+ public static $default_sync_sender_enabled = 1; // Should send incremental sync items.
+
+ /**
+ * Default for enabling Full Sync.
+ *
+ * @var int 1 for true.
+ */
+ public static $default_full_sync_sender_enabled = 1; // Should send full sync items.
+
+ /**
+ * Default Full Sync config
+ *
+ * @var array list of module names.
+ */
+ public static $default_full_sync_config = array(
+ 'constants' => 1,
+ 'functions' => 1,
+ 'options' => 1,
+ 'updates' => 1,
+ 'themes' => 1,
+ 'users' => 1,
+ 'terms' => 1,
+ 'posts' => 1,
+ 'comments' => 1,
+ 'term_relationships' => 1,
+ );
+
+ /**
+ * Default Full Sync max objects to send on a single request.
+ *
+ * @var array list of module => max.
+ */
+ public static $default_full_sync_limits = array(
+ 'users' => array(
+ 'chunk_size' => 100,
+ 'max_chunks' => 10,
+ ),
+ 'terms' => array(
+ 'chunk_size' => 1000,
+ 'max_chunks' => 10,
+ ),
+ 'posts' => array(
+ 'chunk_size' => 100,
+ 'max_chunks' => 1,
+ ),
+ 'comments' => array(
+ 'chunk_size' => 100,
+ 'max_chunks' => 10,
+ ),
+ 'term_relationships' => array(
+ 'chunk_size' => 1000,
+ 'max_chunks' => 10,
+ ),
+ );
+
+}
diff --git a/vendor/automattic/jetpack-sync/src/class-functions.php b/vendor/automattic/jetpack-sync/src/class-functions.php
new file mode 100644
index 0000000000000..61a8a8b58ad51
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/class-functions.php
@@ -0,0 +1,638 @@
+get_modules();
+ }
+
+ /**
+ * Return array of taxonomies registered on the site.
+ *
+ * @return array
+ */
+ public static function get_taxonomies() {
+ global $wp_taxonomies;
+ $wp_taxonomies_without_callbacks = array();
+ foreach ( $wp_taxonomies as $taxonomy_name => $taxonomy ) {
+ $sanitized_taxonomy = self::sanitize_taxonomy( $taxonomy );
+ if ( ! empty( $sanitized_taxonomy ) ) {
+ $wp_taxonomies_without_callbacks[ $taxonomy_name ] = $sanitized_taxonomy;
+ }
+ }
+ return $wp_taxonomies_without_callbacks;
+ }
+
+ /**
+ * Return array of registered shortcodes.
+ *
+ * @return array
+ */
+ public static function get_shortcodes() {
+ global $shortcode_tags;
+ return array_keys( $shortcode_tags );
+ }
+
+ /**
+ * Removes any callback data since we will not be able to process it on our side anyways.
+ *
+ * @param \WP_Taxonomy $taxonomy \WP_Taxonomy item.
+ *
+ * @return mixed|null
+ */
+ public static function sanitize_taxonomy( $taxonomy ) {
+
+ // Lets clone the taxonomy object instead of modifing the global one.
+ $cloned_taxonomy = json_decode( wp_json_encode( $taxonomy ) );
+
+ // recursive taxonomies are no fun.
+ if ( is_null( $cloned_taxonomy ) ) {
+ return null;
+ }
+ // Remove any meta_box_cb if they are not the default wp ones.
+ if ( isset( $cloned_taxonomy->meta_box_cb ) &&
+ ! in_array( $cloned_taxonomy->meta_box_cb, array( 'post_tags_meta_box', 'post_categories_meta_box' ), true ) ) {
+ $cloned_taxonomy->meta_box_cb = null;
+ }
+ // Remove update call back.
+ if ( isset( $cloned_taxonomy->update_count_callback ) &&
+ ! is_null( $cloned_taxonomy->update_count_callback ) ) {
+ $cloned_taxonomy->update_count_callback = null;
+ }
+ // Remove rest_controller_class if it something other then the default.
+ if ( isset( $cloned_taxonomy->rest_controller_class ) &&
+ 'WP_REST_Terms_Controller' !== $cloned_taxonomy->rest_controller_class ) {
+ $cloned_taxonomy->rest_controller_class = null;
+ }
+ return $cloned_taxonomy;
+ }
+
+ /**
+ * Return array of registered post types.
+ *
+ * @return array
+ */
+ public static function get_post_types() {
+ global $wp_post_types;
+
+ $post_types_without_callbacks = array();
+ foreach ( $wp_post_types as $post_type_name => $post_type ) {
+ $sanitized_post_type = self::sanitize_post_type( $post_type );
+ if ( ! empty( $sanitized_post_type ) ) {
+ $post_types_without_callbacks[ $post_type_name ] = $sanitized_post_type;
+ }
+ }
+ return $post_types_without_callbacks;
+ }
+
+ /**
+ * Sanitizes by cloning post type object.
+ *
+ * @param object $post_type \WP_Post_Type.
+ *
+ * @return object
+ */
+ public static function sanitize_post_type( $post_type ) {
+ // Lets clone the post type object instead of modifing the global one.
+ $sanitized_post_type = array();
+ foreach ( Defaults::$default_post_type_attributes as $attribute_key => $default_value ) {
+ if ( isset( $post_type->{ $attribute_key } ) ) {
+ $sanitized_post_type[ $attribute_key ] = $post_type->{ $attribute_key };
+ }
+ }
+ return (object) $sanitized_post_type;
+ }
+
+ /**
+ * Return information about a synced post type.
+ *
+ * @param array $sanitized_post_type Array of args used in constructing \WP_Post_Type.
+ * @param string $post_type Post type name.
+ *
+ * @return object \WP_Post_Type
+ */
+ public static function expand_synced_post_type( $sanitized_post_type, $post_type ) {
+ $post_type = sanitize_key( $post_type );
+ $post_type_object = new \WP_Post_Type( $post_type, $sanitized_post_type );
+ $post_type_object->add_supports();
+ $post_type_object->add_rewrite_rules();
+ $post_type_object->add_hooks();
+ $post_type_object->register_taxonomies();
+ return (object) $post_type_object;
+ }
+
+ /**
+ * Returns site's post_type_features.
+ *
+ * @return array
+ */
+ public static function get_post_type_features() {
+ global $_wp_post_type_features;
+
+ return $_wp_post_type_features;
+ }
+
+ /**
+ * Return hosting provider.
+ *
+ * Uses a set of known constants, classes, or functions to help determine the hosting platform.
+ *
+ * @return string Hosting provider.
+ */
+ public static function get_hosting_provider() {
+ $hosting_provider_detection_methods = array(
+ 'get_hosting_provider_by_known_constant',
+ 'get_hosting_provider_by_known_class',
+ 'get_hosting_provider_by_known_function',
+ );
+
+ $functions = new Functions();
+ foreach ( $hosting_provider_detection_methods as $method ) {
+ $hosting_provider = call_user_func( array( $functions, $method ) );
+ if ( false !== $hosting_provider ) {
+ return $hosting_provider;
+ }
+ }
+
+ return 'unknown';
+ }
+
+ /**
+ * Return a hosting provider using a set of known constants.
+ *
+ * @return mixed A host identifier string or false.
+ */
+ public function get_hosting_provider_by_known_constant() {
+ $hosting_provider_constants = array(
+ 'GD_SYSTEM_PLUGIN_DIR' => 'gd-managed-wp',
+ 'MM_BASE_DIR' => 'bh',
+ 'PAGELYBIN' => 'pagely',
+ 'KINSTAMU_VERSION' => 'kinsta',
+ 'FLYWHEEL_CONFIG_DIR' => 'flywheel',
+ 'IS_PRESSABLE' => 'pressable',
+ 'VIP_GO_ENV' => 'vip-go',
+ );
+
+ foreach ( $hosting_provider_constants as $constant => $constant_value ) {
+ if ( Constants::is_defined( $constant ) ) {
+ if ( 'VIP_GO_ENV' === $constant && false === Constants::get_constant( 'VIP_GO_ENV' ) ) {
+ continue;
+ }
+ return $constant_value;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Return a hosting provider using a set of known classes.
+ *
+ * @return mixed A host identifier string or false.
+ */
+ public function get_hosting_provider_by_known_class() {
+ $hosting_provider = false;
+
+ switch ( true ) {
+ case ( class_exists( '\\WPaaS\\Plugin' ) ):
+ $hosting_provider = 'gd-managed-wp';
+ break;
+ }
+
+ return $hosting_provider;
+ }
+
+ /**
+ * Return a hosting provider using a set of known functions.
+ *
+ * @return mixed A host identifier string or false.
+ */
+ public function get_hosting_provider_by_known_function() {
+ $hosting_provider = false;
+
+ switch ( true ) {
+ case ( function_exists( 'is_wpe' ) || function_exists( 'is_wpe_snapshot' ) ):
+ $hosting_provider = 'wpe';
+ break;
+ }
+
+ return $hosting_provider;
+ }
+
+ /**
+ * Return array of allowed REST API post types.
+ *
+ * @return array Array of allowed post types.
+ */
+ public static function rest_api_allowed_post_types() {
+ /** This filter is already documented in class.json-api-endpoints.php */
+ return apply_filters( 'rest_api_allowed_post_types', array( 'post', 'page', 'revision' ) );
+ }
+
+ /**
+ * Return array of allowed REST API public metadata.
+ *
+ * @return array Array of allowed metadata.
+ */
+ public static function rest_api_allowed_public_metadata() {
+ /** This filter is documented in json-endpoints/class.wpcom-json-api-post-endpoint.php */
+ return apply_filters( 'rest_api_allowed_public_metadata', array() );
+ }
+
+ /**
+ * Finds out if a site is using a version control system.
+ *
+ * @return bool
+ **/
+ public static function is_version_controlled() {
+
+ if ( ! class_exists( 'WP_Automatic_Updater' ) ) {
+ require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
+ }
+ $updater = new \WP_Automatic_Updater();
+
+ return (bool) (string) $updater->is_vcs_checkout( ABSPATH );
+ }
+
+ /**
+ * Returns true if the site has file write access false otherwise.
+ *
+ * @return bool
+ **/
+ public static function file_system_write_access() {
+ if ( ! function_exists( 'get_filesystem_method' ) ) {
+ require_once ABSPATH . 'wp-admin/includes/file.php';
+ }
+
+ require_once ABSPATH . 'wp-admin/includes/template.php';
+
+ $filesystem_method = get_filesystem_method();
+ if ( 'direct' === $filesystem_method ) {
+ return true;
+ }
+
+ ob_start();
+
+ if ( ! function_exists( 'request_filesystem_credentials' ) ) {
+ require_once ABSPATH . 'wp-admin/includes/file.php';
+ }
+
+ $filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() );
+ ob_end_clean();
+ if ( $filesystem_credentials_are_stored ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Helper function that is used when getting home or siteurl values. Decides
+ * whether to get the raw or filtered value.
+ *
+ * @param string $url_type URL to get, home or siteurl.
+ * @return string
+ */
+ public static function get_raw_or_filtered_url( $url_type ) {
+ $url_function = ( 'home' === $url_type )
+ ? 'home_url'
+ : 'site_url';
+
+ if (
+ ! Constants::is_defined( 'JETPACK_SYNC_USE_RAW_URL' ) ||
+ Constants::get_constant( 'JETPACK_SYNC_USE_RAW_URL' )
+ ) {
+ $scheme = is_ssl() ? 'https' : 'http';
+ $url = self::get_raw_url( $url_type );
+ $url = set_url_scheme( $url, $scheme );
+ } else {
+ $url = self::normalize_www_in_url( $url_type, $url_function );
+ }
+
+ return self::get_protocol_normalized_url( $url_function, $url );
+ }
+
+ /**
+ * Return the escaped home_url.
+ *
+ * @return string
+ */
+ public static function home_url() {
+ $url = self::get_raw_or_filtered_url( 'home' );
+
+ /**
+ * Allows overriding of the home_url value that is synced back to WordPress.com.
+ *
+ * @since 5.2.0
+ *
+ * @param string $home_url
+ */
+ return esc_url_raw( apply_filters( 'jetpack_sync_home_url', $url ) );
+ }
+
+ /**
+ * Return the escaped siteurl.
+ *
+ * @return string
+ */
+ public static function site_url() {
+ $url = self::get_raw_or_filtered_url( 'siteurl' );
+
+ /**
+ * Allows overriding of the site_url value that is synced back to WordPress.com.
+ *
+ * @since 5.2.0
+ *
+ * @param string $site_url
+ */
+ return esc_url_raw( apply_filters( 'jetpack_sync_site_url', $url ) );
+ }
+
+ /**
+ * Return main site URL with a normalized protocol.
+ *
+ * @return string
+ */
+ public static function main_network_site_url() {
+ return self::get_protocol_normalized_url( 'main_network_site_url', network_site_url() );
+ }
+
+ /**
+ * Return main site WordPress.com site ID.
+ *
+ * @return string
+ */
+ public static function main_network_site_wpcom_id() {
+ /**
+ * Return the current site WPCOM ID for single site installs
+ */
+ if ( ! is_multisite() ) {
+ return \Jetpack_Options::get_option( 'id' );
+ }
+
+ /**
+ * Return the main network site WPCOM ID for multi-site installs
+ */
+ $current_network = get_network();
+ switch_to_blog( $current_network->blog_id );
+ $wpcom_blog_id = \Jetpack_Options::get_option( 'id' );
+ restore_current_blog();
+ return $wpcom_blog_id;
+ }
+
+ /**
+ * Return URL with a normalized protocol.
+ *
+ * @param callable $callable Function to retrieve URL option.
+ * @param string $new_value URL Protocol to set URLs to.
+ * @return string Normalized URL.
+ */
+ public static function get_protocol_normalized_url( $callable, $new_value ) {
+ $option_key = self::HTTPS_CHECK_OPTION_PREFIX . $callable;
+
+ $parsed_url = wp_parse_url( $new_value );
+ if ( ! $parsed_url ) {
+ return $new_value;
+ }
+ if ( array_key_exists( 'scheme', $parsed_url ) ) {
+ $scheme = $parsed_url['scheme'];
+ } else {
+ $scheme = '';
+ }
+ $scheme_history = get_option( $option_key, array() );
+ $scheme_history[] = $scheme;
+
+ // Limit length to self::HTTPS_CHECK_HISTORY.
+ $scheme_history = array_slice( $scheme_history, ( self::HTTPS_CHECK_HISTORY * -1 ) );
+
+ update_option( $option_key, $scheme_history );
+
+ $forced_scheme = in_array( 'https', $scheme_history, true ) ? 'https' : 'http';
+
+ return set_url_scheme( $new_value, $forced_scheme );
+ }
+
+ /**
+ * Return URL from option or PHP constant.
+ *
+ * @param string $option_name (e.g. 'home').
+ *
+ * @return mixed|null URL.
+ */
+ public static function get_raw_url( $option_name ) {
+ $value = null;
+ $constant = ( 'home' === $option_name )
+ ? 'WP_HOME'
+ : 'WP_SITEURL';
+
+ // Since we disregard the constant for multisites in ms-default-filters.php,
+ // let's also use the db value if this is a multisite.
+ if ( ! is_multisite() && Constants::is_defined( $constant ) ) {
+ $value = Constants::get_constant( $constant );
+ } else {
+ // Let's get the option from the database so that we can bypass filters. This will help
+ // ensure that we get more uniform values.
+ $value = \Jetpack_Options::get_raw_option( $option_name );
+ }
+
+ return $value;
+ }
+
+ /**
+ * Normalize domains by removing www unless declared in the site's option.
+ *
+ * @param string $option Option value from the site.
+ * @param callable $url_function Function retrieving the URL to normalize.
+ * @return mixed|string URL.
+ */
+ public static function normalize_www_in_url( $option, $url_function ) {
+ $url = wp_parse_url( call_user_func( $url_function ) );
+ $option_url = wp_parse_url( get_option( $option ) );
+
+ if ( ! $option_url || ! $url ) {
+ return $url;
+ }
+
+ if ( "www.{$option_url[ 'host' ]}" === $url['host'] ) {
+ // remove www if not present in option URL.
+ $url['host'] = $option_url['host'];
+ }
+ if ( "www.{$url[ 'host' ]}" === $option_url['host'] ) {
+ // add www if present in option URL.
+ $url['host'] = $option_url['host'];
+ }
+
+ $normalized_url = "{$url['scheme']}://{$url['host']}";
+ if ( isset( $url['path'] ) ) {
+ $normalized_url .= "{$url['path']}";
+ }
+
+ if ( isset( $url['query'] ) ) {
+ $normalized_url .= "?{$url['query']}";
+ }
+
+ return $normalized_url;
+ }
+
+ /**
+ * Return filtered value of get_plugins.
+ *
+ * @return mixed|void
+ */
+ public static function get_plugins() {
+ if ( ! function_exists( 'get_plugins' ) ) {
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
+ }
+
+ /** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
+ return apply_filters( 'all_plugins', get_plugins() );
+ }
+
+ /**
+ * Get custom action link tags that the plugin is using
+ * Ref: https://codex.wordpress.org/Plugin_API/Filter_Reference/plugin_action_links_(plugin_file_name)
+ *
+ * @param string $plugin_file_singular Particular plugin.
+ * @return array of plugin action links (key: link name value: url)
+ */
+ public static function get_plugins_action_links( $plugin_file_singular = null ) {
+ // Some sites may have DOM disabled in PHP fail early.
+ if ( ! class_exists( 'DOMDocument' ) ) {
+ return array();
+ }
+ $plugins_action_links = get_option( 'jetpack_plugin_api_action_links', array() );
+ if ( ! empty( $plugins_action_links ) ) {
+ if ( is_null( $plugin_file_singular ) ) {
+ return $plugins_action_links;
+ }
+ return ( isset( $plugins_action_links[ $plugin_file_singular ] ) ? $plugins_action_links[ $plugin_file_singular ] : null );
+ }
+ return array();
+ }
+
+ /**
+ * Return the WP version as defined in the $wp_version global.
+ *
+ * @return string
+ */
+ public static function wp_version() {
+ global $wp_version;
+ return $wp_version;
+ }
+
+ /**
+ * Return site icon url used on the site.
+ *
+ * @param int $size Size of requested icon in pixels.
+ * @return mixed|string|void
+ */
+ public static function site_icon_url( $size = 512 ) {
+ $site_icon = get_site_icon_url( $size );
+ return $site_icon ? $site_icon : get_option( 'jetpack_site_icon_url' );
+ }
+
+ /**
+ * Return roles registered on the site.
+ *
+ * @return array
+ */
+ public static function roles() {
+ $wp_roles = wp_roles();
+ return $wp_roles->roles;
+ }
+
+ /**
+ * Determine time zone from WordPress' options "timezone_string"
+ * and "gmt_offset".
+ *
+ * 1. Check if `timezone_string` is set and return it.
+ * 2. Check if `gmt_offset` is set, formats UTC-offset from it and return it.
+ * 3. Default to "UTC+0" if nothing is set.
+ *
+ * Note: This function is specifically not using wp_timezone() to keep consistency with
+ * the existing formatting of the timezone string.
+ *
+ * @return string
+ */
+ public static function get_timezone() {
+ $timezone_string = get_option( 'timezone_string' );
+
+ if ( ! empty( $timezone_string ) ) {
+ return str_replace( '_', ' ', $timezone_string );
+ }
+
+ $gmt_offset = get_option( 'gmt_offset', 0 );
+
+ $formatted_gmt_offset = sprintf( '%+g', (float) $gmt_offset );
+
+ $formatted_gmt_offset = str_replace(
+ array( '.25', '.5', '.75' ),
+ array( ':15', ':30', ':45' ),
+ (string) $formatted_gmt_offset
+ );
+
+ /* translators: %s is UTC offset, e.g. "+1" */
+ return sprintf( __( 'UTC%s', 'jetpack' ), $formatted_gmt_offset );
+ }
+
+ /**
+ * Return list of paused themes.
+ *
+ * @return array|bool Array of paused themes or false if unsupported.
+ */
+ public static function get_paused_themes() {
+ $paused_themes = wp_paused_themes();
+ return $paused_themes->get_all();
+ }
+
+ /**
+ * Return list of paused plugins.
+ *
+ * @return array|bool Array of paused plugins or false if unsupported.
+ */
+ public static function get_paused_plugins() {
+ $paused_plugins = wp_paused_plugins();
+ return $paused_plugins->get_all();
+ }
+
+ /**
+ * Return the theme's supported features.
+ * Used for syncing the supported feature that we care about.
+ *
+ * @return array List of features that the theme supports.
+ */
+ public static function get_theme_support() {
+ global $_wp_theme_features;
+
+ $theme_support = array();
+ foreach ( Defaults::$default_theme_support_whitelist as $theme_feature ) {
+ $has_support = current_theme_supports( $theme_feature );
+ if ( $has_support ) {
+ $theme_support[ $theme_feature ] = $_wp_theme_features[ $theme_feature ];
+ }
+ }
+
+ return $theme_support;
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/class-health.php b/vendor/automattic/jetpack-sync/src/class-health.php
new file mode 100644
index 0000000000000..ea3d7bd4cfd7c
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/class-health.php
@@ -0,0 +1,190 @@
+ self::STATUS_UNKNOWN,
+ self::OPTION_TIMESTAMP_KEY => microtime( true ),
+ );
+
+ switch ( $status ) {
+ case self::STATUS_DISABLED:
+ case self::STATUS_OUT_OF_SYNC:
+ case self::STATUS_IN_SYNC:
+ $new_status[ self::OPTION_STATUS_KEY ] = $status;
+ break;
+ }
+
+ \Jetpack_Options::update_option( self::STATUS_OPTION, $new_status );
+ return true;
+ }
+
+ /**
+ * Check if Status has been previously set.
+ *
+ * @return bool is a Status defined
+ */
+ public static function is_status_defined() {
+ $status = \Jetpack_Options::get_option( self::STATUS_OPTION );
+
+ if ( false === $status || ! is_array( $status ) || empty( $status[ self::OPTION_STATUS_KEY ] ) ) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Update Sync Status if Full Sync ended of Posts
+ *
+ * @param string $checksum The checksum that's currently being processed.
+ * @param array $range The ranges of object types being processed.
+ */
+ public static function full_sync_end_update_status( $checksum, $range ) {
+ if ( isset( $range['posts'] ) ) {
+ self::update_status( self::STATUS_IN_SYNC );
+ }
+ }
+
+}
diff --git a/vendor/automattic/jetpack-sync/src/class-json-deflate-array-codec.php b/vendor/automattic/jetpack-sync/src/class-json-deflate-array-codec.php
new file mode 100644
index 0000000000000..18ea88ab4c69d
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/class-json-deflate-array-codec.php
@@ -0,0 +1,134 @@
+json_serialize( $object ) ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
+ }
+
+ /**
+ * Decode compressed serialized value.
+ *
+ * @param string $input Item to decode.
+ * @return array|mixed|object
+ */
+ public function decode( $input ) {
+ return $this->json_unserialize( gzinflate( base64_decode( $input ) ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
+ }
+
+ /**
+ * Serialize JSON
+ *
+ * @see https://gist.github.com/muhqu/820694
+ *
+ * @param string $any Value to serialize and wrap.
+ *
+ * @return false|string
+ */
+ protected function json_serialize( $any ) {
+ if ( function_exists( 'jetpack_json_wrap' ) ) {
+ return wp_json_encode( jetpack_json_wrap( $any ) );
+ }
+ // This prevents fatal error when updating pre 6.0 via the cli command.
+ return wp_json_encode( $this->json_wrap( $any ) );
+ }
+
+ /**
+ * Unserialize JSON
+ *
+ * @param string $str JSON string.
+ * @return array|object Unwrapped JSON.
+ */
+ protected function json_unserialize( $str ) {
+ return $this->json_unwrap( json_decode( $str, true ) );
+ }
+
+ /**
+ * Wraps JSON
+ *
+ * @param object|array $any Wrapping value.
+ * @param array $seen_nodes Seen nodes.
+ * @return array
+ */
+ private function json_wrap( &$any, $seen_nodes = array() ) {
+ if ( is_object( $any ) ) {
+ $input = get_object_vars( $any );
+ $input['__o'] = 1;
+ } else {
+ $input = &$any;
+ }
+
+ if ( is_array( $input ) ) {
+ $seen_nodes[] = &$any;
+
+ $return = array();
+
+ foreach ( $input as $k => &$v ) {
+ if ( ( is_array( $v ) || is_object( $v ) ) ) {
+ if ( in_array( $v, $seen_nodes, true ) ) {
+ continue;
+ }
+ $return[ $k ] = $this->json_wrap( $v, $seen_nodes );
+ } else {
+ $return[ $k ] = $v;
+ }
+ }
+
+ return $return;
+ }
+
+ return $any;
+ }
+
+ /**
+ * Unwraps a json_decode return.
+ *
+ * @param array|object $any json_decode object.
+ * @return array|object
+ */
+ private function json_unwrap( $any ) {
+ if ( is_array( $any ) ) {
+ foreach ( $any as $k => $v ) {
+ if ( '__o' === $k ) {
+ continue;
+ }
+ $any[ $k ] = $this->json_unwrap( $v );
+ }
+
+ if ( isset( $any['__o'] ) ) {
+ unset( $any['__o'] );
+ $any = (object) $any;
+ }
+ }
+
+ return $any;
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/class-listener.php b/vendor/automattic/jetpack-sync/src/class-listener.php
new file mode 100644
index 0000000000000..ad217f25b1c36
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/class-listener.php
@@ -0,0 +1,470 @@
+:(
+ */
+ protected function __construct() {
+ $this->set_defaults();
+ $this->init();
+ }
+
+ /**
+ * Sync Listener init.
+ */
+ private function init() {
+ $handler = array( $this, 'action_handler' );
+ $full_sync_handler = array( $this, 'full_sync_action_handler' );
+
+ foreach ( Modules::get_modules() as $module ) {
+ $module->init_listeners( $handler );
+ $module->init_full_sync_listeners( $full_sync_handler );
+ }
+
+ // Module Activation.
+ add_action( 'jetpack_activate_module', $handler );
+ add_action( 'jetpack_deactivate_module', $handler );
+
+ // Jetpack Upgrade.
+ add_action( 'updating_jetpack_version', $handler, 10, 2 );
+
+ // Send periodic checksum.
+ add_action( 'jetpack_sync_checksum', $handler );
+ }
+
+ /**
+ * Get incremental sync queue.
+ */
+ public function get_sync_queue() {
+ return $this->sync_queue;
+ }
+
+ /**
+ * Gets the full sync queue.
+ */
+ public function get_full_sync_queue() {
+ return $this->full_sync_queue;
+ }
+
+ /**
+ * Sets queue size limit.
+ *
+ * @param int $limit Queue size limit.
+ */
+ public function set_queue_size_limit( $limit ) {
+ $this->sync_queue_size_limit = $limit;
+ }
+
+ /**
+ * Get queue size limit.
+ */
+ public function get_queue_size_limit() {
+ return $this->sync_queue_size_limit;
+ }
+
+ /**
+ * Sets the queue lag limit.
+ *
+ * @param int $age Queue lag limit.
+ */
+ public function set_queue_lag_limit( $age ) {
+ $this->sync_queue_lag_limit = $age;
+ }
+
+ /**
+ * Return value of queue lag limit.
+ */
+ public function get_queue_lag_limit() {
+ return $this->sync_queue_lag_limit;
+ }
+
+ /**
+ * Force a recheck of the queue limit.
+ */
+ public function force_recheck_queue_limit() {
+ delete_transient( self::QUEUE_STATE_CHECK_TRANSIENT . '_' . $this->sync_queue->id );
+ delete_transient( self::QUEUE_STATE_CHECK_TRANSIENT . '_' . $this->full_sync_queue->id );
+ }
+
+ /**
+ * Determine if an item can be added to the queue.
+ *
+ * Prevent adding items to the queue if it hasn't sent an item for 15 mins
+ * AND the queue is over 1000 items long (by default).
+ *
+ * @param object $queue Sync queue.
+ * @return bool
+ */
+ public function can_add_to_queue( $queue ) {
+ if ( ! Settings::is_sync_enabled() ) {
+ return false;
+ }
+
+ $state_transient_name = self::QUEUE_STATE_CHECK_TRANSIENT . '_' . $queue->id;
+
+ $queue_state = get_transient( $state_transient_name );
+
+ if ( false === $queue_state ) {
+ $queue_state = array( $queue->size(), $queue->lag() );
+ set_transient( $state_transient_name, $queue_state, self::QUEUE_STATE_CHECK_TIMEOUT );
+ }
+
+ list( $queue_size, $queue_age ) = $queue_state;
+
+ return ( $queue_age < $this->sync_queue_lag_limit )
+ ||
+ ( ( $queue_size + 1 ) < $this->sync_queue_size_limit );
+ }
+
+ /**
+ * Full sync action handler.
+ *
+ * @param mixed ...$args Args passed to the action.
+ */
+ public function full_sync_action_handler( ...$args ) {
+ $this->enqueue_action( current_filter(), $args, $this->full_sync_queue );
+ }
+
+ /**
+ * Action handler.
+ *
+ * @param mixed ...$args Args passed to the action.
+ */
+ public function action_handler( ...$args ) {
+ $this->enqueue_action( current_filter(), $args, $this->sync_queue );
+ }
+
+ // add many actions to the queue directly, without invoking them.
+
+ /**
+ * Bulk add action to the queue.
+ *
+ * @param string $action_name The name the full sync action.
+ * @param array $args_array Array of chunked arguments.
+ */
+ public function bulk_enqueue_full_sync_actions( $action_name, $args_array ) {
+ $queue = $this->get_full_sync_queue();
+
+ /*
+ * If we add any items to the queue, we should try to ensure that our script
+ * can't be killed before they are sent.
+ */
+ if ( function_exists( 'ignore_user_abort' ) ) {
+ ignore_user_abort( true );
+ }
+
+ $data_to_enqueue = array();
+ $user_id = get_current_user_id();
+ $currtime = microtime( true );
+ $is_importing = Settings::is_importing();
+
+ foreach ( $args_array as $args ) {
+ $previous_end = isset( $args['previous_end'] ) ? $args['previous_end'] : null;
+ $args = isset( $args['ids'] ) ? $args['ids'] : $args;
+
+ /**
+ * Modify or reject the data within an action before it is enqueued locally.
+ *
+ * @since 4.2.0
+ *
+ * @module sync
+ *
+ * @param array The action parameters
+ */
+ $args = apply_filters( "jetpack_sync_before_enqueue_$action_name", $args );
+ $action_data = array( $args );
+ if ( ! is_null( $previous_end ) ) {
+ $action_data[] = $previous_end;
+ }
+ // allow listeners to abort.
+ if ( false === $args ) {
+ continue;
+ }
+
+ $data_to_enqueue[] = array(
+ $action_name,
+ $action_data,
+ $user_id,
+ $currtime,
+ $is_importing,
+ );
+ }
+
+ $queue->add_all( $data_to_enqueue );
+ }
+
+ /**
+ * Enqueue the action.
+ *
+ * @param string $current_filter Current WordPress filter.
+ * @param object $args Sync args.
+ * @param string $queue Sync queue.
+ */
+ public function enqueue_action( $current_filter, $args, $queue ) {
+ // don't enqueue an action during the outbound http request - this prevents recursion.
+ if ( Settings::is_sending() ) {
+ return;
+ }
+
+ /**
+ * Add an action hook to execute when anything on the whitelist gets sent to the queue to sync.
+ *
+ * @module sync
+ *
+ * @since 5.9.0
+ */
+ do_action( 'jetpack_sync_action_before_enqueue' );
+
+ /**
+ * Modify or reject the data within an action before it is enqueued locally.
+ *
+ * @since 4.2.0
+ *
+ * @param array The action parameters
+ */
+ $args = apply_filters( "jetpack_sync_before_enqueue_$current_filter", $args );
+
+ // allow listeners to abort.
+ if ( false === $args ) {
+ return;
+ }
+
+ /*
+ * Periodically check the size of the queue, and disable adding to it if
+ * it exceeds some limit AND the oldest item exceeds the age limit (i.e. sending has stopped).
+ */
+ if ( ! $this->can_add_to_queue( $queue ) ) {
+ if ( 'sync' === $queue->id ) {
+ $this->sync_data_loss( $queue );
+ }
+ return;
+ }
+
+ /*
+ * If we add any items to the queue, we should try to ensure that our script
+ * can't be killed before they are sent.
+ */
+ if ( function_exists( 'ignore_user_abort' ) ) {
+ ignore_user_abort( true );
+ }
+
+ if (
+ 'sync' === $queue->id ||
+ in_array(
+ $current_filter,
+ array(
+ 'jetpack_full_sync_start',
+ 'jetpack_full_sync_end',
+ 'jetpack_full_sync_cancel',
+ ),
+ true
+ )
+ ) {
+ $queue->add(
+ array(
+ $current_filter,
+ $args,
+ get_current_user_id(),
+ microtime( true ),
+ Settings::is_importing(),
+ $this->get_actor( $current_filter, $args ),
+ )
+ );
+ } else {
+ $queue->add(
+ array(
+ $current_filter,
+ $args,
+ get_current_user_id(),
+ microtime( true ),
+ Settings::is_importing(),
+ )
+ );
+ }
+
+ // since we've added some items, let's try to load the sender so we can send them as quickly as possible.
+ if ( ! Actions::$sender ) {
+ add_filter( 'jetpack_sync_sender_should_load', __NAMESPACE__ . '\Actions::should_initialize_sender_enqueue', 10, 1 );
+ if ( did_action( 'init' ) ) {
+ Actions::add_sender_shutdown();
+ }
+ }
+ }
+
+ /**
+ * Sync Data Loss Handler
+ *
+ * @param Queue $queue Sync queue.
+ * @return boolean was send successful
+ */
+ public function sync_data_loss( $queue ) {
+ if ( ! Settings::is_sync_enabled() ) {
+ return;
+ }
+ $updated = Health::update_status( Health::STATUS_OUT_OF_SYNC );
+
+ if ( ! $updated ) {
+ return;
+ }
+
+ $data = array(
+ 'timestamp' => microtime( true ),
+ 'queue_size' => $queue->size(),
+ 'queue_lag' => $queue->lag(),
+ );
+
+ $sender = Sender::get_instance();
+ return $sender->send_action( 'jetpack_sync_data_loss', $data );
+ }
+
+ /**
+ * Get the event's actor.
+ *
+ * @param string $current_filter Current wp-admin page.
+ * @param object $args Sync event.
+ * @return array Actor information.
+ */
+ public function get_actor( $current_filter, $args ) {
+ if ( 'wp_login' === $current_filter ) {
+ $user = get_user_by( 'ID', $args[1]->data->ID );
+ } else {
+ $user = wp_get_current_user();
+ }
+
+ $roles = new Roles();
+ $translated_role = $roles->translate_user_to_role( $user );
+
+ $actor = array(
+ 'wpcom_user_id' => null,
+ 'external_user_id' => isset( $user->ID ) ? $user->ID : null,
+ 'display_name' => isset( $user->display_name ) ? $user->display_name : null,
+ 'user_email' => isset( $user->user_email ) ? $user->user_email : null,
+ 'user_roles' => isset( $user->roles ) ? $user->roles : null,
+ 'translated_role' => $translated_role ? $translated_role : null,
+ 'is_cron' => defined( 'DOING_CRON' ) ? DOING_CRON : false,
+ 'is_rest' => defined( 'REST_API_REQUEST' ) ? REST_API_REQUEST : false,
+ 'is_xmlrpc' => defined( 'XMLRPC_REQUEST' ) ? XMLRPC_REQUEST : false,
+ 'is_wp_rest' => defined( 'REST_REQUEST' ) ? REST_REQUEST : false,
+ 'is_ajax' => defined( 'DOING_AJAX' ) ? DOING_AJAX : false,
+ 'is_wp_admin' => is_admin(),
+ 'is_cli' => defined( 'WP_CLI' ) ? WP_CLI : false,
+ 'from_url' => $this->get_request_url(),
+ );
+
+ if ( $this->should_send_user_data_with_actor( $current_filter ) ) {
+ require_once JETPACK__PLUGIN_DIR . 'modules/protect/shared-functions.php';
+ $actor['ip'] = jetpack_protect_get_ip();
+ $actor['user_agent'] = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : 'unknown';
+ }
+
+ return $actor;
+ }
+
+ /**
+ * Should user data be sent as the actor?
+ *
+ * @param string $current_filter The current WordPress filter being executed.
+ * @return bool
+ */
+ public function should_send_user_data_with_actor( $current_filter ) {
+ $should_send = in_array( $current_filter, array( 'jetpack_wp_login', 'wp_logout', 'jetpack_valid_failed_login_attempt' ), true );
+ /**
+ * Allow or deny sending actor's user data ( IP and UA ) during a sync event
+ *
+ * @since 5.8.0
+ *
+ * @module sync
+ *
+ * @param bool True if we should send user data
+ * @param string The current filter that is performing the sync action
+ */
+ return apply_filters( 'jetpack_sync_actor_user_data', $should_send, $current_filter );
+ }
+
+ /**
+ * Sets Listener defaults.
+ */
+ public function set_defaults() {
+ $this->sync_queue = new Queue( 'sync' );
+ $this->full_sync_queue = new Queue( 'full_sync' );
+ $this->set_queue_size_limit( Settings::get_setting( 'max_queue_size' ) );
+ $this->set_queue_lag_limit( Settings::get_setting( 'max_queue_lag' ) );
+ }
+
+ /**
+ * Get the request URL.
+ *
+ * @return string Request URL, if known. Otherwise, wp-admin or home_url.
+ */
+ public function get_request_url() {
+ if ( isset( $_SERVER['HTTP_HOST'], $_SERVER['REQUEST_URI'] ) ) {
+ return 'http' . ( isset( $_SERVER['HTTPS'] ) ? 's' : '' ) . '://' . "{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
+ }
+ return is_admin() ? get_admin_url( get_current_blog_id() ) : home_url();
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/class-lock.php b/vendor/automattic/jetpack-sync/src/class-lock.php
new file mode 100644
index 0000000000000..84d87bc87e965
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/class-lock.php
@@ -0,0 +1,65 @@
+set_defaults();
+ }
+ }
+
+ /**
+ * Gets the name of an initialized module. Returns false if given module has not been initialized.
+ *
+ * @access public
+ * @static
+ *
+ * @param string $module_name A module name.
+ *
+ * @return bool|Automattic\Jetpack\Sync\Modules\Module
+ */
+ public static function get_module( $module_name ) {
+ foreach ( self::get_modules() as $module ) {
+ if ( $module->name() === $module_name ) {
+ return $module;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Loads and sets defaults for all declared modules.
+ *
+ * @access public
+ * @static
+ *
+ * @return array
+ */
+ public static function initialize_modules() {
+ /**
+ * Filters the list of class names of sync modules.
+ * If you add to this list, make sure any classes implement the
+ * Jetpack_Sync_Module interface.
+ *
+ * @since 4.2.0
+ */
+ $modules = apply_filters( 'jetpack_sync_modules', self::DEFAULT_SYNC_MODULES );
+
+ $modules = array_map( array( __CLASS__, 'load_module' ), $modules );
+
+ return array_map( array( __CLASS__, 'set_module_defaults' ), $modules );
+ }
+
+ /**
+ * Returns an instance of the given module class.
+ *
+ * @access public
+ * @static
+ *
+ * @param string $module_class The classname of a Jetpack sync module.
+ *
+ * @return Automattic\Jetpack\Sync\Modules\Module
+ */
+ public static function load_module( $module_class ) {
+ return new $module_class();
+ }
+
+ /**
+ * Sets defaults for the given instance of a Jetpack sync module.
+ *
+ * @access public
+ * @static
+ *
+ * @param Automattic\Jetpack\Sync\Modules\Module $module Instance of a Jetpack sync module.
+ *
+ * @return Automattic\Jetpack\Sync\Modules\Module
+ */
+ public static function set_module_defaults( $module ) {
+ $module->set_defaults();
+ if ( method_exists( $module, 'set_late_default' ) ) {
+ add_action( 'init', array( $module, 'set_late_default' ), 90 );
+ }
+ return $module;
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/class-queue-buffer.php b/vendor/automattic/jetpack-sync/src/class-queue-buffer.php
new file mode 100644
index 0000000000000..a9846150cc1e7
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/class-queue-buffer.php
@@ -0,0 +1,78 @@
+id = $id;
+ $this->items_with_ids = $items_with_ids;
+ }
+
+ /**
+ * Retrieve the sync items in the buffer, in an ID => value form.
+ *
+ * @access public
+ *
+ * @return array Sync items in the buffer.
+ */
+ public function get_items() {
+ return array_combine( $this->get_item_ids(), $this->get_item_values() );
+ }
+
+ /**
+ * Retrieve the values of the sync items in the buffer.
+ *
+ * @access public
+ *
+ * @return array Sync items values.
+ */
+ public function get_item_values() {
+ return Utils::get_item_values( $this->items_with_ids );
+ }
+
+ /**
+ * Retrieve the IDs of the sync items in the buffer.
+ *
+ * @access public
+ *
+ * @return array Sync items IDs.
+ */
+ public function get_item_ids() {
+ return Utils::get_item_ids( $this->items_with_ids );
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/class-queue.php b/vendor/automattic/jetpack-sync/src/class-queue.php
new file mode 100644
index 0000000000000..ff1096ca3ef8c
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/class-queue.php
@@ -0,0 +1,716 @@
+id = str_replace( '-', '_', $id ); // Necessary to ensure we don't have ID collisions in the SQL.
+ $this->row_iterator = 0;
+ $this->random_int = wp_rand( 1, 1000000 );
+ }
+
+ /**
+ * Add a single item to the queue.
+ *
+ * @param object $item Event object to add to queue.
+ */
+ public function add( $item ) {
+ global $wpdb;
+ $added = false;
+ // This basically tries to add the option until enough time has elapsed that
+ // it has a unique (microtime-based) option key.
+ while ( ! $added ) {
+ $rows_added = $wpdb->query(
+ $wpdb->prepare(
+ "INSERT INTO $wpdb->options (option_name, option_value, autoload) VALUES (%s, %s,%s)",
+ $this->get_next_data_row_option_name(),
+ serialize( $item ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
+ 'no'
+ )
+ );
+ $added = ( 0 !== $rows_added );
+ }
+ }
+
+ /**
+ * Insert all the items in a single SQL query. May be subject to query size limits!
+ *
+ * @param array $items Array of events to add to the queue.
+ *
+ * @return bool|\WP_Error
+ */
+ public function add_all( $items ) {
+ global $wpdb;
+ $base_option_name = $this->get_next_data_row_option_name();
+
+ $query = "INSERT INTO $wpdb->options (option_name, option_value, autoload) VALUES ";
+
+ $rows = array();
+ $count_items = count( $items );
+ for ( $i = 0; $i < $count_items; ++$i ) {
+ $option_name = esc_sql( $base_option_name . '-' . $i );
+ $option_value = esc_sql( serialize( $items[ $i ] ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
+ $rows[] = "('$option_name', '$option_value', 'no')";
+ }
+
+ $rows_added = $wpdb->query( $query . join( ',', $rows ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+
+ if ( count( $items ) !== $rows_added ) {
+ return new \WP_Error( 'row_count_mismatch', "The number of rows inserted didn't match the size of the input array" );
+ }
+ return true;
+ }
+
+ /**
+ * Get the front-most item on the queue without checking it out.
+ *
+ * @param int $count Number of items to return when looking at the items.
+ *
+ * @return array
+ */
+ public function peek( $count = 1 ) {
+ $items = $this->fetch_items( $count );
+ if ( $items ) {
+ return Utils::get_item_values( $items );
+ }
+
+ return array();
+ }
+
+ /**
+ * Gets items with particular IDs.
+ *
+ * @param array $item_ids Array of item IDs to retrieve.
+ *
+ * @return array
+ */
+ public function peek_by_id( $item_ids ) {
+ $items = $this->fetch_items_by_id( $item_ids );
+ if ( $items ) {
+ return Utils::get_item_values( $items );
+ }
+
+ return array();
+ }
+
+ /**
+ * Gets the queue lag.
+ * Lag is the difference in time between the age of the oldest item
+ * (aka first or frontmost item) and the current time.
+ *
+ * @param microtime $now The current time in microtime.
+ *
+ * @return float|int|mixed|null
+ */
+ public function lag( $now = null ) {
+ global $wpdb;
+
+ $first_item_name = $wpdb->get_var(
+ $wpdb->prepare(
+ "SELECT option_name FROM $wpdb->options WHERE option_name LIKE %s ORDER BY option_name ASC LIMIT 1",
+ "jpsq_{$this->id}-%"
+ )
+ );
+
+ if ( ! $first_item_name ) {
+ return 0;
+ }
+
+ if ( null === $now ) {
+ $now = microtime( true );
+ }
+
+ // Break apart the item name to get the timestamp.
+ $matches = null;
+ if ( preg_match( '/^jpsq_' . $this->id . '-(\d+\.\d+)-/', $first_item_name, $matches ) ) {
+ return $now - (float) $matches[1];
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Resets the queue.
+ */
+ public function reset() {
+ global $wpdb;
+ $this->delete_checkout_id();
+ $wpdb->query(
+ $wpdb->prepare(
+ "DELETE FROM $wpdb->options WHERE option_name LIKE %s",
+ "jpsq_{$this->id}-%"
+ )
+ );
+ }
+
+ /**
+ * Return the size of the queue.
+ *
+ * @return int
+ */
+ public function size() {
+ global $wpdb;
+
+ return (int) $wpdb->get_var(
+ $wpdb->prepare(
+ "SELECT count(*) FROM $wpdb->options WHERE option_name LIKE %s",
+ "jpsq_{$this->id}-%"
+ )
+ );
+ }
+
+ /**
+ * Lets you know if there is any items in the queue.
+ *
+ * We use this peculiar implementation because it's much faster than count(*).
+ *
+ * @return bool
+ */
+ public function has_any_items() {
+ global $wpdb;
+ $value = $wpdb->get_var(
+ $wpdb->prepare(
+ "SELECT exists( SELECT option_name FROM $wpdb->options WHERE option_name LIKE %s )",
+ "jpsq_{$this->id}-%"
+ )
+ );
+
+ return ( '1' === $value );
+ }
+
+ /**
+ * Used to checkout the queue.
+ *
+ * @param int $buffer_size Size of the buffer to checkout.
+ *
+ * @return Automattic\Jetpack\Sync\Queue_Buffer|bool|int|\WP_Error
+ */
+ public function checkout( $buffer_size ) {
+ if ( $this->get_checkout_id() ) {
+ return new \WP_Error( 'unclosed_buffer', 'There is an unclosed buffer' );
+ }
+
+ $buffer_id = uniqid();
+
+ $result = $this->set_checkout_id( $buffer_id );
+
+ if ( ! $result || is_wp_error( $result ) ) {
+ return $result;
+ }
+
+ $items = $this->fetch_items( $buffer_size );
+
+ if ( count( $items ) === 0 ) {
+ return false;
+ }
+
+ $buffer = new Queue_Buffer( $buffer_id, array_slice( $items, 0, $buffer_size ) );
+
+ return $buffer;
+ }
+
+ /**
+ * Given a list of items return the items ids.
+ *
+ * @param array $items List of item objects.
+ *
+ * @return array Ids of the items.
+ */
+ public function get_ids( $items ) {
+ return array_map(
+ function ( $item ) {
+ return $item->id;
+ },
+ $items
+ );
+ }
+
+ /**
+ * Pop elements from the queue.
+ *
+ * @param int $limit Number of items to pop from the queue.
+ *
+ * @return array|object|null
+ */
+ public function pop( $limit ) {
+ $items = $this->fetch_items( $limit );
+
+ $ids = $this->get_ids( $items );
+
+ $this->delete( $ids );
+
+ return $items;
+ }
+
+ /**
+ * Get the items from the queue with a memory limit.
+ *
+ * This checks out rows until it either empties the queue or hits a certain memory limit
+ * it loads the sizes from the DB first so that it doesn't accidentally
+ * load more data into memory than it needs to.
+ * The only way it will load more items than $max_size is if a single queue item
+ * exceeds the memory limit, but in that case it will send that item by itself.
+ *
+ * @param int $max_memory (bytes) Maximum memory threshold.
+ * @param int $max_buffer_size Maximum buffer size (number of items).
+ *
+ * @return Automattic\Jetpack\Sync\Queue_Buffer|bool|int|\WP_Error
+ */
+ public function checkout_with_memory_limit( $max_memory, $max_buffer_size = 500 ) {
+ if ( $this->get_checkout_id() ) {
+ return new \WP_Error( 'unclosed_buffer', 'There is an unclosed buffer' );
+ }
+
+ $buffer_id = uniqid();
+
+ $result = $this->set_checkout_id( $buffer_id );
+
+ if ( ! $result || is_wp_error( $result ) ) {
+ return $result;
+ }
+
+ // Get the map of buffer_id -> memory_size.
+ global $wpdb;
+
+ $items_with_size = $wpdb->get_results(
+ $wpdb->prepare(
+ "SELECT option_name AS id, LENGTH(option_value) AS value_size FROM $wpdb->options WHERE option_name LIKE %s ORDER BY option_name ASC LIMIT %d",
+ "jpsq_{$this->id}-%",
+ $max_buffer_size
+ ),
+ OBJECT
+ );
+
+ if ( count( $items_with_size ) === 0 ) {
+ return false;
+ }
+
+ $total_memory = 0;
+ $max_item_id = $items_with_size[0]->id;
+ $min_item_id = $max_item_id;
+
+ foreach ( $items_with_size as $id => $item_with_size ) {
+ $total_memory += $item_with_size->value_size;
+
+ // If this is the first item and it exceeds memory, allow loop to continue
+ // we will exit on the next iteration instead.
+ if ( $total_memory > $max_memory && $id > 0 ) {
+ break;
+ }
+
+ $max_item_id = $item_with_size->id;
+ }
+
+ $query = $wpdb->prepare(
+ "SELECT option_name AS id, option_value AS value FROM $wpdb->options WHERE option_name >= %s and option_name <= %s ORDER BY option_name ASC",
+ $min_item_id,
+ $max_item_id
+ );
+
+ $items = $wpdb->get_results( $query, OBJECT ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ foreach ( $items as $item ) {
+ $item->value = maybe_unserialize( $item->value );
+ }
+
+ if ( count( $items ) === 0 ) {
+ $this->delete_checkout_id();
+
+ return false;
+ }
+
+ $buffer = new Queue_Buffer( $buffer_id, $items );
+
+ return $buffer;
+ }
+
+ /**
+ * Check in the queue.
+ *
+ * @param Automattic\Jetpack\Sync\Queue_Buffer $buffer Queue_Buffer object.
+ *
+ * @return bool|\WP_Error
+ */
+ public function checkin( $buffer ) {
+ $is_valid = $this->validate_checkout( $buffer );
+
+ if ( is_wp_error( $is_valid ) ) {
+ return $is_valid;
+ }
+
+ $this->delete_checkout_id();
+
+ return true;
+ }
+
+ /**
+ * Close the buffer.
+ *
+ * @param Automattic\Jetpack\Sync\Queue_Buffer $buffer Queue_Buffer object.
+ * @param null|array $ids_to_remove Ids to remove from the queue.
+ *
+ * @return bool|\WP_Error
+ */
+ public function close( $buffer, $ids_to_remove = null ) {
+ $is_valid = $this->validate_checkout( $buffer );
+
+ if ( is_wp_error( $is_valid ) ) {
+ // Always delete ids_to_remove even when buffer is no longer checked-out.
+ // They were processed by WP.com so safe to remove from queue.
+ if ( ! is_null( $ids_to_remove ) ) {
+ $this->delete( $ids_to_remove );
+ }
+ return $is_valid;
+ }
+
+ $this->delete_checkout_id();
+
+ // By default clear all items in the buffer.
+ if ( is_null( $ids_to_remove ) ) {
+ $ids_to_remove = $buffer->get_item_ids();
+ }
+
+ $this->delete( $ids_to_remove );
+
+ return true;
+ }
+
+ /**
+ * Delete elements from the queue.
+ *
+ * @param array $ids Ids to delete.
+ *
+ * @return bool|int
+ */
+ private function delete( $ids ) {
+ if ( 0 === count( $ids ) ) {
+ return 0;
+ }
+ global $wpdb;
+ $sql = "DELETE FROM $wpdb->options WHERE option_name IN (" . implode( ', ', array_fill( 0, count( $ids ), '%s' ) ) . ')';
+ $query = call_user_func_array( array( $wpdb, 'prepare' ), array_merge( array( $sql ), $ids ) );
+
+ return $wpdb->query( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ }
+
+ /**
+ * Flushes all items from the queue.
+ *
+ * @return array
+ */
+ public function flush_all() {
+ $items = Utils::get_item_values( $this->fetch_items() );
+ $this->reset();
+
+ return $items;
+ }
+
+ /**
+ * Get all the items from the queue.
+ *
+ * @return array|object|null
+ */
+ public function get_all() {
+ return $this->fetch_items();
+ }
+
+ /**
+ * Forces Checkin of the queue.
+ * Use with caution, this could allow multiple processes to delete
+ * and send from the queue at the same time
+ */
+ public function force_checkin() {
+ $this->delete_checkout_id();
+ }
+
+ /**
+ * Locks checkouts from the queue
+ * tries to wait up to $timeout seconds for the queue to be empty.
+ *
+ * @param int $timeout The wait time in seconds for the queue to be empty.
+ *
+ * @return bool|int|\WP_Error
+ */
+ public function lock( $timeout = 30 ) {
+ $tries = 0;
+
+ while ( $this->has_any_items() && $tries < $timeout ) {
+ sleep( 1 );
+ ++$tries;
+ }
+
+ if ( 30 === $tries ) {
+ return new \WP_Error( 'lock_timeout', 'Timeout waiting for sync queue to empty' );
+ }
+
+ if ( $this->get_checkout_id() ) {
+ return new \WP_Error( 'unclosed_buffer', 'There is an unclosed buffer' );
+ }
+
+ // Hopefully this means we can acquire a checkout?
+ $result = $this->set_checkout_id( 'lock' );
+
+ if ( ! $result || is_wp_error( $result ) ) {
+ return $result;
+ }
+
+ return true;
+ }
+
+ /**
+ * Unlocks the queue.
+ *
+ * @return bool|int
+ */
+ public function unlock() {
+ return $this->delete_checkout_id();
+ }
+
+ /**
+ * This option is specifically chosen to, as much as possible, preserve time order
+ * and minimise the possibility of collisions between multiple processes working
+ * at the same time.
+ *
+ * @return string
+ */
+ protected function generate_option_name_timestamp() {
+ return sprintf( '%.6f', microtime( true ) );
+ }
+
+ /**
+ * Gets the checkout ID.
+ *
+ * @return bool|string
+ */
+ private function get_checkout_id() {
+ global $wpdb;
+ $checkout_value = $wpdb->get_var(
+ $wpdb->prepare(
+ "SELECT option_value FROM $wpdb->options WHERE option_name = %s",
+ $this->get_lock_option_name()
+ )
+ );
+
+ if ( $checkout_value ) {
+ list( $checkout_id, $timestamp ) = explode( ':', $checkout_value );
+ if ( (int) $timestamp > time() ) {
+ return $checkout_id;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Sets the checkout id.
+ *
+ * @param string $checkout_id The ID of the checkout.
+ *
+ * @return bool|int
+ */
+ private function set_checkout_id( $checkout_id ) {
+ global $wpdb;
+
+ $expires = time() + Defaults::$default_sync_queue_lock_timeout;
+ $updated_num = $wpdb->query(
+ $wpdb->prepare(
+ "UPDATE $wpdb->options SET option_value = %s WHERE option_name = %s",
+ "$checkout_id:$expires",
+ $this->get_lock_option_name()
+ )
+ );
+
+ if ( ! $updated_num ) {
+ $updated_num = $wpdb->query(
+ $wpdb->prepare(
+ "INSERT INTO $wpdb->options ( option_name, option_value, autoload ) VALUES ( %s, %s, 'no' )",
+ $this->get_lock_option_name(),
+ "$checkout_id:$expires"
+ )
+ );
+ }
+
+ return $updated_num;
+ }
+
+ /**
+ * Deletes the checkout ID.
+ *
+ * @return bool|int
+ */
+ private function delete_checkout_id() {
+ global $wpdb;
+ // Rather than delete, which causes fragmentation, we update in place.
+ return $wpdb->query(
+ $wpdb->prepare(
+ "UPDATE $wpdb->options SET option_value = %s WHERE option_name = %s",
+ '0:0',
+ $this->get_lock_option_name()
+ )
+ );
+
+ }
+
+ /**
+ * Return the lock option name.
+ *
+ * @return string
+ */
+ private function get_lock_option_name() {
+ return "jpsq_{$this->id}_checkout";
+ }
+
+ /**
+ * Return the next data row option name.
+ *
+ * @return string
+ */
+ private function get_next_data_row_option_name() {
+ $timestamp = $this->generate_option_name_timestamp();
+
+ // Row iterator is used to avoid collisions where we're writing data waaay fast in a single process.
+ if ( PHP_INT_MAX === $this->row_iterator ) {
+ $this->row_iterator = 0;
+ } else {
+ $this->row_iterator += 1;
+ }
+
+ return 'jpsq_' . $this->id . '-' . $timestamp . '-' . $this->random_int . '-' . $this->row_iterator;
+ }
+
+ /**
+ * Return the items in the queue.
+ *
+ * @param null|int $limit Limit to the number of items we fetch at once.
+ *
+ * @return array|object|null
+ */
+ private function fetch_items( $limit = null ) {
+ global $wpdb;
+
+ if ( $limit ) {
+ $items = $wpdb->get_results(
+ $wpdb->prepare(
+ "SELECT option_name AS id, option_value AS value FROM $wpdb->options WHERE option_name LIKE %s ORDER BY option_name ASC LIMIT %d",
+ "jpsq_{$this->id}-%",
+ $limit
+ ),
+ OBJECT
+ );
+ } else {
+ $items = $wpdb->get_results(
+ $wpdb->prepare(
+ "SELECT option_name AS id, option_value AS value FROM $wpdb->options WHERE option_name LIKE %s ORDER BY option_name ASC",
+ "jpsq_{$this->id}-%"
+ ),
+ OBJECT
+ );
+ }
+
+ return $this->unserialize_values( $items );
+
+ }
+
+ /**
+ * Return items with specific ids.
+ *
+ * @param array $items_ids Array of event ids.
+ *
+ * @return array|object|null
+ */
+ private function fetch_items_by_id( $items_ids ) {
+ global $wpdb;
+
+ // return early if $items_ids is empty or not an array.
+ if ( empty( $items_ids ) || ! is_array( $items_ids ) ) {
+ return null;
+ }
+
+ $ids_placeholders = implode( ', ', array_fill( 0, count( $items_ids ), '%s' ) );
+ $query_with_placeholders = "SELECT option_name AS id, option_value AS value
+ FROM $wpdb->options
+ WHERE option_name IN ( $ids_placeholders )";
+ $items = $wpdb->get_results(
+ $wpdb->prepare(
+ $query_with_placeholders, // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $items_ids
+ ),
+ OBJECT
+ );
+
+ return $this->unserialize_values( $items );
+ }
+
+ /**
+ * Unserialize item values.
+ *
+ * @param array $items Events from the Queue to be serialized.
+ *
+ * @return mixed
+ */
+ private function unserialize_values( $items ) {
+ array_walk(
+ $items,
+ function ( $item ) {
+ $item->value = maybe_unserialize( $item->value );
+ }
+ );
+
+ return $items;
+
+ }
+
+ /**
+ * Return true if the buffer is still valid or an Error other wise.
+ *
+ * @param Automattic\Jetpack\Sync\Queue_Buffer $buffer The Queue_Buffer.
+ *
+ * @return bool|\WP_Error
+ */
+ private function validate_checkout( $buffer ) {
+ if ( ! $buffer instanceof Queue_Buffer ) {
+ return new \WP_Error( 'not_a_buffer', 'You must checkin an instance of Automattic\\Jetpack\\Sync\\Queue_Buffer' );
+ }
+
+ $checkout_id = $this->get_checkout_id();
+
+ if ( ! $checkout_id ) {
+ return new \WP_Error( 'buffer_not_checked_out', 'There are no checked out buffers' );
+ }
+
+ // TODO: change to strict comparison.
+ if ( $checkout_id != $buffer->id ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
+ return new \WP_Error( 'buffer_mismatch', 'The buffer you checked in was not checked out' );
+ }
+
+ return true;
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/class-replicastore.php b/vendor/automattic/jetpack-sync/src/class-replicastore.php
new file mode 100644
index 0000000000000..3d4ba1b94e00b
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/class-replicastore.php
@@ -0,0 +1,1512 @@
+query( "DELETE FROM $wpdb->posts" );
+
+ // Delete comments from cache.
+ $comment_ids = $wpdb->get_col( "SELECT comment_ID FROM $wpdb->comments" );
+ if ( ! empty( $comment_ids ) ) {
+ clean_comment_cache( $comment_ids );
+ }
+ $wpdb->query( "DELETE FROM $wpdb->comments" );
+
+ // Also need to delete terms from cache.
+ $term_ids = $wpdb->get_col( "SELECT term_id FROM $wpdb->terms" );
+ foreach ( $term_ids as $term_id ) {
+ wp_cache_delete( $term_id, 'terms' );
+ }
+
+ $wpdb->query( "DELETE FROM $wpdb->terms" );
+
+ $wpdb->query( "DELETE FROM $wpdb->term_taxonomy" );
+ $wpdb->query( "DELETE FROM $wpdb->term_relationships" );
+
+ // Callables and constants.
+ $wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE 'jetpack_%'" );
+ $wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_key NOT LIKE '\_%'" );
+ }
+
+ /**
+ * Ran when full sync has just started.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ */
+ public function full_sync_start( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ $this->reset();
+ }
+
+ /**
+ * Ran when full sync has just finished.
+ *
+ * @access public
+ *
+ * @param string $checksum Deprecated since 7.3.0.
+ */
+ public function full_sync_end( $checksum ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ // Noop right now.
+ }
+
+ /**
+ * Retrieve the number of terms.
+ *
+ * @access public
+ *
+ * @return int Number of terms.
+ */
+ public function term_count() {
+ global $wpdb;
+ return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->terms" );
+ }
+
+ /**
+ * Retrieve the number of rows in the `term_taxonomy` table.
+ *
+ * @access public
+ *
+ * @return int Number of terms.
+ */
+ public function term_taxonomy_count() {
+ global $wpdb;
+ return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->term_taxonomy" );
+ }
+
+ /**
+ * Retrieve the number of term relationships.
+ *
+ * @access public
+ *
+ * @return int Number of rows in the term relationships table.
+ */
+ public function term_relationship_count() {
+ global $wpdb;
+ return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->term_relationships" );
+ }
+
+ /**
+ * Retrieve the number of posts with a particular post status within a certain range.
+ *
+ * @access public
+ *
+ * @todo Prepare the SQL query before executing it.
+ *
+ * @param string $status Post status.
+ * @param int $min_id Minimum post ID.
+ * @param int $max_id Maximum post ID.
+ * @return int Number of posts.
+ */
+ public function post_count( $status = null, $min_id = null, $max_id = null ) {
+ global $wpdb;
+
+ $where = '';
+
+ if ( $status ) {
+ $where = "post_status = '" . esc_sql( $status ) . "'";
+ } else {
+ $where = '1=1';
+ }
+
+ if ( ! empty( $min_id ) ) {
+ $where .= ' AND ID >= ' . (int) $min_id;
+ }
+
+ if ( ! empty( $max_id ) ) {
+ $where .= ' AND ID <= ' . (int) $max_id;
+ }
+
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts WHERE $where" );
+ }
+
+ /**
+ * Retrieve the posts with a particular post status.
+ *
+ * @access public
+ *
+ * @todo Implement range and actually use max_id/min_id arguments.
+ *
+ * @param string $status Post status.
+ * @param int $min_id Minimum post ID.
+ * @param int $max_id Maximum post ID.
+ * @return array Array of posts.
+ */
+ public function get_posts( $status = null, $min_id = null, $max_id = null ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ $args = array(
+ 'orderby' => 'ID',
+ 'posts_per_page' => -1,
+ );
+
+ if ( $status ) {
+ $args['post_status'] = $status;
+ } else {
+ $args['post_status'] = 'any';
+ }
+
+ return get_posts( $args );
+ }
+
+ /**
+ * Retrieve a post object by the post ID.
+ *
+ * @access public
+ *
+ * @param int $id Post ID.
+ * @return \WP_Post Post object.
+ */
+ public function get_post( $id ) {
+ return get_post( $id );
+ }
+
+ /**
+ * Update or insert a post.
+ *
+ * @access public
+ *
+ * @param \WP_Post $post Post object.
+ * @param bool $silent Whether to perform a silent action. Not used in this implementation.
+ */
+ public function upsert_post( $post, $silent = false ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ global $wpdb;
+
+ // Reject the post if it's not a \WP_Post.
+ if ( ! $post instanceof \WP_Post ) {
+ return;
+ }
+
+ $post = $post->to_array();
+
+ // Reject posts without an ID.
+ if ( ! isset( $post['ID'] ) ) {
+ return;
+ }
+
+ $now = current_time( 'mysql' );
+ $now_gmt = get_gmt_from_date( $now );
+
+ $defaults = array(
+ 'ID' => 0,
+ 'post_author' => '0',
+ 'post_content' => '',
+ 'post_content_filtered' => '',
+ 'post_title' => '',
+ 'post_name' => '',
+ 'post_excerpt' => '',
+ 'post_status' => 'draft',
+ 'post_type' => 'post',
+ 'comment_status' => 'closed',
+ 'comment_count' => '0',
+ 'ping_status' => '',
+ 'post_password' => '',
+ 'to_ping' => '',
+ 'pinged' => '',
+ 'post_parent' => 0,
+ 'menu_order' => 0,
+ 'guid' => '',
+ 'post_date' => $now,
+ 'post_date_gmt' => $now_gmt,
+ 'post_modified' => $now,
+ 'post_modified_gmt' => $now_gmt,
+ );
+
+ $post = array_intersect_key( $post, $defaults );
+
+ $post = sanitize_post( $post, 'db' );
+
+ unset( $post['filter'] );
+
+ $exists = $wpdb->get_var( $wpdb->prepare( "SELECT EXISTS( SELECT 1 FROM $wpdb->posts WHERE ID = %d )", $post['ID'] ) );
+
+ if ( $exists ) {
+ $wpdb->update( $wpdb->posts, $post, array( 'ID' => $post['ID'] ) );
+ } else {
+ $wpdb->insert( $wpdb->posts, $post );
+ }
+
+ clean_post_cache( $post['ID'] );
+ }
+
+ /**
+ * Delete a post by the post ID.
+ *
+ * @access public
+ *
+ * @param int $post_id Post ID.
+ */
+ public function delete_post( $post_id ) {
+ wp_delete_post( $post_id, true );
+ }
+
+ /**
+ * Retrieve the checksum for posts within a range.
+ *
+ * @access public
+ *
+ * @param int $min_id Minimum post ID.
+ * @param int $max_id Maximum post ID.
+ * @return int The checksum.
+ */
+ public function posts_checksum( $min_id = null, $max_id = null ) {
+ global $wpdb;
+ return $this->table_checksum( $wpdb->posts, Defaults::$default_post_checksum_columns, 'ID', Settings::get_blacklisted_post_types_sql(), $min_id, $max_id );
+ }
+
+ /**
+ * Retrieve the checksum for post meta within a range.
+ *
+ * @access public
+ *
+ * @param int $min_id Minimum post meta ID.
+ * @param int $max_id Maximum post meta ID.
+ * @return int The checksum.
+ */
+ public function post_meta_checksum( $min_id = null, $max_id = null ) {
+ global $wpdb;
+ return $this->table_checksum( $wpdb->postmeta, Defaults::$default_post_meta_checksum_columns, 'meta_id', Settings::get_whitelisted_post_meta_sql(), $min_id, $max_id );
+ }
+
+ /**
+ * Retrieve the number of comments with a particular comment status within a certain range.
+ *
+ * @access public
+ *
+ * @todo Prepare the SQL query before executing it.
+ *
+ * @param string $status Comment status.
+ * @param int $min_id Minimum comment ID.
+ * @param int $max_id Maximum comment ID.
+ * @return int Number of comments.
+ */
+ public function comment_count( $status = null, $min_id = null, $max_id = null ) {
+ global $wpdb;
+
+ $comment_approved = $this->comment_status_to_approval_value( $status );
+
+ if ( false !== $comment_approved ) {
+ $where = "comment_approved = '" . esc_sql( $comment_approved ) . "'";
+ } else {
+ $where = '1=1';
+ }
+
+ if ( ! empty( $min_id ) ) {
+ $where .= ' AND comment_ID >= ' . (int) $min_id;
+ }
+
+ if ( ! empty( $max_id ) ) {
+ $where .= ' AND comment_ID <= ' . (int) $max_id;
+ }
+
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments WHERE $where" );
+ }
+
+ /**
+ * Translate a comment status to a value of the comment_approved field.
+ *
+ * @access protected
+ *
+ * @param string $status Comment status.
+ * @return string|bool New comment_approved value, false if the status doesn't affect it.
+ */
+ protected function comment_status_to_approval_value( $status ) {
+ switch ( (string) $status ) {
+ case 'approve':
+ case '1':
+ return '1';
+ case 'hold':
+ case '0':
+ return '0';
+ case 'spam':
+ return 'spam';
+ case 'trash':
+ return 'trash';
+ case 'post-trashed':
+ return 'post-trashed';
+ case 'any':
+ case 'all':
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Retrieve the comments with a particular comment status.
+ *
+ * @access public
+ *
+ * @todo Implement range and actually use max_id/min_id arguments.
+ *
+ * @param string $status Comment status.
+ * @param int $min_id Minimum comment ID.
+ * @param int $max_id Maximum comment ID.
+ * @return array Array of comments.
+ */
+ public function get_comments( $status = null, $min_id = null, $max_id = null ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ $args = array(
+ 'orderby' => 'ID',
+ 'status' => 'all',
+ );
+
+ if ( $status ) {
+ $args['status'] = $status;
+ }
+
+ return get_comments( $args );
+ }
+
+ /**
+ * Retrieve a comment object by the comment ID.
+ *
+ * @access public
+ *
+ * @param int $id Comment ID.
+ * @return \WP_Comment Comment object.
+ */
+ public function get_comment( $id ) {
+ return \WP_Comment::get_instance( $id );
+ }
+
+ /**
+ * Update or insert a comment.
+ *
+ * @access public
+ *
+ * @param \WP_Comment $comment Comment object.
+ */
+ public function upsert_comment( $comment ) {
+ global $wpdb;
+
+ $comment = $comment->to_array();
+
+ // Filter by fields on comment table.
+ $comment_fields_whitelist = array(
+ 'comment_ID',
+ 'comment_post_ID',
+ 'comment_author',
+ 'comment_author_email',
+ 'comment_author_url',
+ 'comment_author_IP',
+ 'comment_date',
+ 'comment_date_gmt',
+ 'comment_content',
+ 'comment_karma',
+ 'comment_approved',
+ 'comment_agent',
+ 'comment_type',
+ 'comment_parent',
+ 'user_id',
+ );
+
+ foreach ( $comment as $key => $value ) {
+ if ( ! in_array( $key, $comment_fields_whitelist, true ) ) {
+ unset( $comment[ $key ] );
+ }
+ }
+
+ $exists = $wpdb->get_var(
+ $wpdb->prepare(
+ "SELECT EXISTS( SELECT 1 FROM $wpdb->comments WHERE comment_ID = %d )",
+ $comment['comment_ID']
+ )
+ );
+
+ if ( $exists ) {
+ $wpdb->update( $wpdb->comments, $comment, array( 'comment_ID' => $comment['comment_ID'] ) );
+ } else {
+ $wpdb->insert( $wpdb->comments, $comment );
+ }
+ // Remove comment from cache.
+ clean_comment_cache( $comment['comment_ID'] );
+
+ wp_update_comment_count( $comment['comment_post_ID'] );
+ }
+
+ /**
+ * Trash a comment by the comment ID.
+ *
+ * @access public
+ *
+ * @param int $comment_id Comment ID.
+ */
+ public function trash_comment( $comment_id ) {
+ wp_delete_comment( $comment_id );
+ }
+
+ /**
+ * Delete a comment by the comment ID.
+ *
+ * @access public
+ *
+ * @param int $comment_id Comment ID.
+ */
+ public function delete_comment( $comment_id ) {
+ wp_delete_comment( $comment_id, true );
+ }
+
+ /**
+ * Mark a comment by the comment ID as spam.
+ *
+ * @access public
+ *
+ * @param int $comment_id Comment ID.
+ */
+ public function spam_comment( $comment_id ) {
+ wp_spam_comment( $comment_id );
+ }
+
+ /**
+ * Trash the comments of a post.
+ *
+ * @access public
+ *
+ * @param int $post_id Post ID.
+ * @param array $statuses Post statuses. Not used in this implementation.
+ */
+ public function trashed_post_comments( $post_id, $statuses ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ wp_trash_post_comments( $post_id );
+ }
+
+ /**
+ * Untrash the comments of a post.
+ *
+ * @access public
+ *
+ * @param int $post_id Post ID.
+ */
+ public function untrashed_post_comments( $post_id ) {
+ wp_untrash_post_comments( $post_id );
+ }
+
+ /**
+ * Retrieve the checksum for comments within a range.
+ *
+ * @access public
+ *
+ * @param int $min_id Minimum comment ID.
+ * @param int $max_id Maximum comment ID.
+ * @return int The checksum.
+ */
+ public function comments_checksum( $min_id = null, $max_id = null ) {
+ global $wpdb;
+ return $this->table_checksum( $wpdb->comments, Defaults::$default_comment_checksum_columns, 'comment_ID', Settings::get_comments_filter_sql(), $min_id, $max_id );
+ }
+
+ /**
+ * Retrieve the checksum for comment meta within a range.
+ *
+ * @access public
+ *
+ * @param int $min_id Minimum comment meta ID.
+ * @param int $max_id Maximum comment meta ID.
+ * @return int The checksum.
+ */
+ public function comment_meta_checksum( $min_id = null, $max_id = null ) {
+ global $wpdb;
+ return $this->table_checksum( $wpdb->commentmeta, Defaults::$default_comment_meta_checksum_columns, 'meta_id', Settings::get_whitelisted_comment_meta_sql(), $min_id, $max_id );
+ }
+
+ /**
+ * Retrieve the checksum for all options.
+ *
+ * @access public
+ *
+ * @return int The checksum.
+ */
+ public function options_checksum() {
+ global $wpdb;
+ $options_whitelist = "'" . implode( "', '", Defaults::$default_options_whitelist ) . "'";
+ $where_sql = "option_name IN ( $options_whitelist )";
+
+ return $this->table_checksum( $wpdb->options, Defaults::$default_option_checksum_columns, null, $where_sql, null, null );
+ }
+
+ /**
+ * Update the value of an option.
+ *
+ * @access public
+ *
+ * @param string $option Option name.
+ * @param mixed $value Option value.
+ * @return bool False if value was not updated and true if value was updated.
+ */
+ public function update_option( $option, $value ) {
+ return update_option( $option, $value );
+ }
+
+ /**
+ * Retrieve an option value based on an option name.
+ *
+ * @access public
+ *
+ * @param string $option Name of option to retrieve.
+ * @param mixed $default Optional. Default value to return if the option does not exist.
+ * @return mixed Value set for the option.
+ */
+ public function get_option( $option, $default = false ) {
+ return get_option( $option, $default );
+ }
+
+ /**
+ * Remove an option by name.
+ *
+ * @access public
+ *
+ * @param string $option Name of option to remove.
+ * @return bool True, if option is successfully deleted. False on failure.
+ */
+ public function delete_option( $option ) {
+ return delete_option( $option );
+ }
+
+ /**
+ * Change the info of the current theme.
+ *
+ * @access public
+ *
+ * @param array $theme_info Theme info array.
+ */
+ public function set_theme_info( $theme_info ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ // Noop.
+ }
+
+ /**
+ * Whether the current theme supports a certain feature.
+ *
+ * @access public
+ *
+ * @param string $feature Name of the feature.
+ */
+ public function current_theme_supports( $feature ) {
+ return current_theme_supports( $feature );
+ }
+
+ /**
+ * Retrieve metadata for the specified object.
+ *
+ * @access public
+ *
+ * @param string $type Meta type.
+ * @param int $object_id ID of the object.
+ * @param string $meta_key Meta key.
+ * @param bool $single If true, return only the first value of the specified meta_key.
+ *
+ * @return mixed Single metadata value, or array of values.
+ */
+ public function get_metadata( $type, $object_id, $meta_key = '', $single = false ) {
+ return get_metadata( $type, $object_id, $meta_key, $single );
+ }
+
+ /**
+ * Stores remote meta key/values alongside an ID mapping key.
+ *
+ * @access public
+ *
+ * @todo Refactor to not use interpolated values when preparing the SQL query.
+ *
+ * @param string $type Meta type.
+ * @param int $object_id ID of the object.
+ * @param string $meta_key Meta key.
+ * @param mixed $meta_value Meta value.
+ * @param int $meta_id ID of the meta.
+ *
+ * @return bool False if meta table does not exist, true otherwise.
+ */
+ public function upsert_metadata( $type, $object_id, $meta_key, $meta_value, $meta_id ) {
+ $table = _get_meta_table( $type );
+ if ( ! $table ) {
+ return false;
+ }
+
+ global $wpdb;
+
+ $exists = $wpdb->get_var(
+ $wpdb->prepare(
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ "SELECT EXISTS( SELECT 1 FROM $table WHERE meta_id = %d )",
+ $meta_id
+ )
+ );
+
+ if ( $exists ) {
+ $wpdb->update(
+ $table,
+ array(
+ 'meta_key' => $meta_key,
+ 'meta_value' => maybe_serialize( $meta_value ),
+ ),
+ array( 'meta_id' => $meta_id )
+ );
+ } else {
+ $object_id_field = $type . '_id';
+ $wpdb->insert(
+ $table,
+ array(
+ 'meta_id' => $meta_id,
+ $object_id_field => $object_id,
+ 'meta_key' => $meta_key,
+ 'meta_value' => maybe_serialize( $meta_value ),
+ )
+ );
+ }
+
+ wp_cache_delete( $object_id, $type . '_meta' );
+
+ return true;
+ }
+
+ /**
+ * Delete metadata for the specified object.
+ *
+ * @access public
+ *
+ * @todo Refactor to not use interpolated values when preparing the SQL query.
+ *
+ * @param string $type Meta type.
+ * @param int $object_id ID of the object.
+ * @param array $meta_ids IDs of the meta objects to delete.
+ */
+ public function delete_metadata( $type, $object_id, $meta_ids ) {
+ global $wpdb;
+
+ $table = _get_meta_table( $type );
+ if ( ! $table ) {
+ return false;
+ }
+
+ foreach ( $meta_ids as $meta_id ) {
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ $wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE meta_id = %d", $meta_id ) );
+ }
+
+ // If we don't have an object ID what do we do - invalidate ALL meta?
+ if ( $object_id ) {
+ wp_cache_delete( $object_id, $type . '_meta' );
+ }
+ }
+
+ /**
+ * Delete metadata with a certain key for the specified objects.
+ *
+ * @access public
+ *
+ * @todo Test this out to make sure it works as expected.
+ * @todo Refactor to not use interpolated values when preparing the SQL query.
+ *
+ * @param string $type Meta type.
+ * @param array $object_ids IDs of the objects.
+ * @param string $meta_key Meta key.
+ */
+ public function delete_batch_metadata( $type, $object_ids, $meta_key ) {
+ global $wpdb;
+
+ $table = _get_meta_table( $type );
+ if ( ! $table ) {
+ return false;
+ }
+ $column = sanitize_key( $type . '_id' );
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ $wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE $column IN (%s) && meta_key = %s", implode( ',', $object_ids ), $meta_key ) );
+
+ // If we don't have an object ID what do we do - invalidate ALL meta?
+ foreach ( $object_ids as $object_id ) {
+ wp_cache_delete( $object_id, $type . '_meta' );
+ }
+ }
+
+ /**
+ * Retrieve value of a constant based on the constant name.
+ *
+ * We explicitly return null instead of false if the constant doesn't exist.
+ *
+ * @access public
+ *
+ * @param string $constant Name of constant to retrieve.
+ * @return mixed Value set for the constant.
+ */
+ public function get_constant( $constant ) {
+ $value = get_option( 'jetpack_constant_' . $constant );
+
+ if ( $value ) {
+ return $value;
+ }
+
+ return null;
+ }
+
+ /**
+ * Set the value of a constant.
+ *
+ * @access public
+ *
+ * @param string $constant Name of constant to retrieve.
+ * @param mixed $value Value set for the constant.
+ */
+ public function set_constant( $constant, $value ) {
+ update_option( 'jetpack_constant_' . $constant, $value );
+ }
+
+ /**
+ * Retrieve the number of the available updates of a certain type.
+ * Type is one of: `plugins`, `themes`, `wordpress`, `translations`, `total`, `wp_update_version`.
+ *
+ * @access public
+ *
+ * @param string $type Type of updates to retrieve.
+ * @return int|null Number of updates available, `null` if type is invalid or missing.
+ */
+ public function get_updates( $type ) {
+ $all_updates = get_option( 'jetpack_updates', array() );
+
+ if ( isset( $all_updates[ $type ] ) ) {
+ return $all_updates[ $type ];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Set the available updates of a certain type.
+ * Type is one of: `plugins`, `themes`, `wordpress`, `translations`, `total`, `wp_update_version`.
+ *
+ * @access public
+ *
+ * @param string $type Type of updates to set.
+ * @param int $updates Total number of updates.
+ */
+ public function set_updates( $type, $updates ) {
+ $all_updates = get_option( 'jetpack_updates', array() );
+ $all_updates[ $type ] = $updates;
+ update_option( 'jetpack_updates', $all_updates );
+ }
+
+ /**
+ * Retrieve a callable value based on its name.
+ *
+ * @access public
+ *
+ * @param string $name Name of the callable to retrieve.
+ * @return mixed Value of the callable.
+ */
+ public function get_callable( $name ) {
+ $value = get_option( 'jetpack_' . $name );
+
+ if ( $value ) {
+ return $value;
+ }
+
+ return null;
+ }
+
+ /**
+ * Update the value of a callable.
+ *
+ * @access public
+ *
+ * @param string $name Callable name.
+ * @param mixed $value Callable value.
+ */
+ public function set_callable( $name, $value ) {
+ update_option( 'jetpack_' . $name, $value );
+ }
+
+ /**
+ * Retrieve a network option value based on a network option name.
+ *
+ * @access public
+ *
+ * @param string $option Name of network option to retrieve.
+ * @return mixed Value set for the network option.
+ */
+ public function get_site_option( $option ) {
+ return get_option( 'jetpack_network_' . $option );
+ }
+
+ /**
+ * Update the value of a network option.
+ *
+ * @access public
+ *
+ * @param string $option Network option name.
+ * @param mixed $value Network option value.
+ * @return bool False if value was not updated and true if value was updated.
+ */
+ public function update_site_option( $option, $value ) {
+ return update_option( 'jetpack_network_' . $option, $value );
+ }
+
+ /**
+ * Remove a network option by name.
+ *
+ * @access public
+ *
+ * @param string $option Name of option to remove.
+ * @return bool True, if option is successfully deleted. False on failure.
+ */
+ public function delete_site_option( $option ) {
+ return delete_option( 'jetpack_network_' . $option );
+ }
+
+ /**
+ * Retrieve the terms from a particular taxonomy.
+ *
+ * @access public
+ *
+ * @param string $taxonomy Taxonomy slug.
+ * @return array|\WP_Error Array of terms or WP_Error object on failure.
+ */
+ public function get_terms( $taxonomy ) {
+ $t = $this->ensure_taxonomy( $taxonomy );
+ if ( ! $t || is_wp_error( $t ) ) {
+ return $t;
+ }
+ return get_terms( $taxonomy );
+ }
+
+ /**
+ * Retrieve a particular term.
+ *
+ * @access public
+ *
+ * @param string $taxonomy Taxonomy slug.
+ * @param int $term_id ID of the term.
+ * @param string $term_key ID Field `term_id` or `term_taxonomy_id`.
+ * @return \WP_Term|\WP_Error Term object on success, \WP_Error object on failure.
+ */
+ public function get_term( $taxonomy, $term_id, $term_key = 'term_id' ) {
+
+ // Full Sync will pass false for the $taxonomy so a check for term_taxonomy_id is needed before ensure_taxonomy.
+ if ( 'term_taxonomy_id' === $term_key ) {
+ return get_term_by( 'term_taxonomy_id', $term_id );
+ }
+
+ $t = $this->ensure_taxonomy( $taxonomy );
+ if ( ! $t || is_wp_error( $t ) ) {
+ return $t;
+ }
+
+ return get_term( $term_id, $taxonomy );
+ }
+
+ /**
+ * Verify a taxonomy is legitimate and register it if necessary.
+ *
+ * @access private
+ *
+ * @param string $taxonomy Taxonomy slug.
+ * @return bool|void|\WP_Error True if already exists; void if it was registered; \WP_Error on error.
+ */
+ private function ensure_taxonomy( $taxonomy ) {
+ if ( ! taxonomy_exists( $taxonomy ) ) {
+ // Try re-registering synced taxonomies.
+ $taxonomies = $this->get_callable( 'taxonomies' );
+ if ( ! isset( $taxonomies[ $taxonomy ] ) ) {
+ // Doesn't exist, or somehow hasn't been synced.
+ return new \WP_Error( 'invalid_taxonomy', "The taxonomy '$taxonomy' doesn't exist" );
+ }
+ $t = $taxonomies[ $taxonomy ];
+
+ return register_taxonomy(
+ $taxonomy,
+ $t->object_type,
+ (array) $t
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * Retrieve all terms from a taxonomy that are related to an object with a particular ID.
+ *
+ * @access public
+ *
+ * @param int $object_id Object ID.
+ * @param string $taxonomy Taxonomy slug.
+ * @return array|bool|\WP_Error Array of terms on success, `false` if no terms or post doesn't exist, \WP_Error on failure.
+ */
+ public function get_the_terms( $object_id, $taxonomy ) {
+ return get_the_terms( $object_id, $taxonomy );
+ }
+
+ /**
+ * Insert or update a term.
+ *
+ * @access public
+ *
+ * @param \WP_Term $term_object Term object.
+ * @return array|bool|\WP_Error Array of term_id and term_taxonomy_id if updated, true if inserted, \WP_Error on failure.
+ */
+ public function update_term( $term_object ) {
+ $taxonomy = $term_object->taxonomy;
+ global $wpdb;
+ $exists = $wpdb->get_var(
+ $wpdb->prepare(
+ "SELECT EXISTS( SELECT 1 FROM $wpdb->terms WHERE term_id = %d )",
+ $term_object->term_id
+ )
+ );
+ if ( ! $exists ) {
+ $term_object = sanitize_term( clone $term_object, $taxonomy, 'db' );
+ $term = array(
+ 'term_id' => $term_object->term_id,
+ 'name' => $term_object->name,
+ 'slug' => $term_object->slug,
+ 'term_group' => $term_object->term_group,
+ );
+ $term_taxonomy = array(
+ 'term_taxonomy_id' => $term_object->term_taxonomy_id,
+ 'term_id' => $term_object->term_id,
+ 'taxonomy' => $term_object->taxonomy,
+ 'description' => $term_object->description,
+ 'parent' => (int) $term_object->parent,
+ 'count' => (int) $term_object->count,
+ );
+ $wpdb->insert( $wpdb->terms, $term );
+ $wpdb->insert( $wpdb->term_taxonomy, $term_taxonomy );
+
+ return true;
+ }
+
+ return wp_update_term( $term_object->term_id, $taxonomy, (array) $term_object );
+ }
+
+ /**
+ * Delete a term by the term ID and its corresponding taxonomy.
+ *
+ * @access public
+ *
+ * @param int $term_id Term ID.
+ * @param string $taxonomy Taxonomy slug.
+ * @return bool|int|\WP_Error True on success, false if term doesn't exist. Zero if trying with default category. \WP_Error on invalid taxonomy.
+ */
+ public function delete_term( $term_id, $taxonomy ) {
+ $this->ensure_taxonomy( $taxonomy );
+ return wp_delete_term( $term_id, $taxonomy );
+ }
+
+ /**
+ * Add/update terms of a particular taxonomy of an object with the specified ID.
+ *
+ * @access public
+ *
+ * @param int $object_id The object to relate to.
+ * @param string $taxonomy The context in which to relate the term to the object.
+ * @param string|int|array $terms A single term slug, single term id, or array of either term slugs or ids.
+ * @param bool $append Optional. If false will delete difference of terms. Default false.
+ */
+ public function update_object_terms( $object_id, $taxonomy, $terms, $append ) {
+ $this->ensure_taxonomy( $taxonomy );
+ wp_set_object_terms( $object_id, $terms, $taxonomy, $append );
+ }
+
+ /**
+ * Remove certain term relationships from the specified object.
+ *
+ * @access public
+ *
+ * @todo Refactor to not use interpolated values when preparing the SQL query.
+ *
+ * @param int $object_id ID of the object.
+ * @param array $tt_ids Term taxonomy IDs.
+ * @return bool True on success, false on failure.
+ */
+ public function delete_object_terms( $object_id, $tt_ids ) {
+ global $wpdb;
+
+ if ( is_array( $tt_ids ) && ! empty( $tt_ids ) ) {
+ // Escape.
+ $tt_ids_sanitized = array_map( 'intval', $tt_ids );
+
+ $taxonomies = array();
+ foreach ( $tt_ids_sanitized as $tt_id ) {
+ $term = get_term_by( 'term_taxonomy_id', $tt_id );
+ $taxonomies[ $term->taxonomy ][] = $tt_id;
+ }
+ $in_tt_ids = implode( ', ', $tt_ids_sanitized );
+
+ /**
+ * Fires immediately before an object-term relationship is deleted.
+ *
+ * @since 2.9.0
+ *
+ * @param int $object_id Object ID.
+ * @param array $tt_ids An array of term taxonomy IDs.
+ */
+ do_action( 'delete_term_relationships', $object_id, $tt_ids_sanitized );
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ $deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_tt_ids)", $object_id ) );
+ foreach ( $taxonomies as $taxonomy => $taxonomy_tt_ids ) {
+ $this->ensure_taxonomy( $taxonomy );
+ wp_cache_delete( $object_id, $taxonomy . '_relationships' );
+ /**
+ * Fires immediately after an object-term relationship is deleted.
+ *
+ * @since 2.9.0
+ *
+ * @param int $object_id Object ID.
+ * @param array $tt_ids An array of term taxonomy IDs.
+ */
+ do_action( 'deleted_term_relationships', $object_id, $taxonomy_tt_ids );
+ wp_update_term_count( $taxonomy_tt_ids, $taxonomy );
+ }
+
+ return (bool) $deleted;
+ }
+
+ return false;
+ }
+
+ /**
+ * Retrieve the number of users.
+ * Not supported in this replicastore.
+ *
+ * @access public
+ */
+ public function user_count() {
+ // Noop.
+ }
+
+ /**
+ * Retrieve a user object by the user ID.
+ *
+ * @access public
+ *
+ * @param int $user_id User ID.
+ * @return \WP_User User object.
+ */
+ public function get_user( $user_id ) {
+ return \WP_User::get_instance( $user_id );
+ }
+
+ /**
+ * Insert or update a user.
+ * Not supported in this replicastore.
+ *
+ * @access public
+ * @throws \Exception If this method is invoked.
+ *
+ * @param \WP_User $user User object.
+ */
+ public function upsert_user( $user ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ $this->invalid_call();
+ }
+
+ /**
+ * Delete a user.
+ * Not supported in this replicastore.
+ *
+ * @access public
+ * @throws \Exception If this method is invoked.
+ *
+ * @param int $user_id User ID.
+ */
+ public function delete_user( $user_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ $this->invalid_call();
+ }
+
+ /**
+ * Update/insert user locale.
+ * Not supported in this replicastore.
+ *
+ * @access public
+ * @throws \Exception If this method is invoked.
+ *
+ * @param int $user_id User ID.
+ * @param string $local The user locale.
+ */
+ public function upsert_user_locale( $user_id, $local ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ $this->invalid_call();
+ }
+
+ /**
+ * Delete user locale.
+ * Not supported in this replicastore.
+ *
+ * @access public
+ * @throws \Exception If this method is invoked.
+ *
+ * @param int $user_id User ID.
+ */
+ public function delete_user_locale( $user_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ $this->invalid_call();
+ }
+
+ /**
+ * Retrieve the user locale.
+ *
+ * @access public
+ *
+ * @param int $user_id User ID.
+ * @return string The user locale.
+ */
+ public function get_user_locale( $user_id ) {
+ return get_user_locale( $user_id );
+ }
+
+ /**
+ * Retrieve the allowed mime types for the user.
+ * Not supported in this replicastore.
+ *
+ * @access public
+ *
+ * @param int $user_id User ID.
+ */
+ public function get_allowed_mime_types( $user_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ // Noop.
+ }
+
+ /**
+ * Retrieve all the checksums we are interested in.
+ * Currently that is posts, comments, post meta and comment meta.
+ *
+ * @access public
+ *
+ * @return array Checksums.
+ */
+ public function checksum_all() {
+ $post_meta_checksum = $this->checksum_histogram( 'post_meta', 1 );
+ $comment_meta_checksum = $this->checksum_histogram( 'comment_meta', 1 );
+
+ return array(
+ 'posts' => $this->posts_checksum(),
+ 'comments' => $this->comments_checksum(),
+ 'post_meta' => reset( $post_meta_checksum ),
+ 'comment_meta' => reset( $comment_meta_checksum ),
+ );
+ }
+
+ /**
+ * Retrieve the columns that are needed to calculate a checksum for an object type.
+ *
+ * @access public
+ *
+ * @todo Refactor to not use interpolated values and prepare the SQL query.
+ *
+ * @param string $object_type Object type.
+ * @return array|bool Columns, or false if invalid object type is specified.
+ */
+ public function get_checksum_columns_for_object_type( $object_type ) {
+ switch ( $object_type ) {
+ case 'posts':
+ return Defaults::$default_post_checksum_columns;
+ case 'post_meta':
+ return Defaults::$default_post_meta_checksum_columns;
+ case 'comments':
+ return Defaults::$default_comment_checksum_columns;
+ case 'comment_meta':
+ return Defaults::$default_post_meta_checksum_columns;
+ case 'terms':
+ return Defaults::$default_term_checksum_columns;
+ case 'term_taxonomy':
+ return Defaults::$default_term_taxonomy_checksum_columns;
+ case 'term_relationships':
+ return Defaults::$default_term_relationships_checksum_columns;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Grabs the minimum and maximum object ids for the given parameters.
+ *
+ * @access public
+ *
+ * @param string $id_field The id column in the table to query.
+ * @param string $object_table The table to query.
+ * @param string $where A sql where clause without 'WHERE'.
+ * @param int $bucket_size The maximum amount of objects to include in the query.
+ * For `term_relationships` table, the bucket size will refer to the amount
+ * of distinct object ids. This will likely include more database rows than
+ * the bucket size implies.
+ *
+ * @return object An object with min_id and max_id properties.
+ */
+ public function get_min_max_object_id( $id_field, $object_table, $where, $bucket_size ) {
+ global $wpdb;
+
+ // The term relationship table's unique key is a combination of 2 columns. `DISTINCT` helps us get a more acurate query.
+ $distinct_sql = ( $wpdb->term_relationships === $object_table ) ? 'DISTINCT' : '';
+ $where_sql = $where ? "WHERE $where" : '';
+
+ // Since MIN() and MAX() do not work with LIMIT, we'll need to adjust the dataset we query if a limit is present.
+ // With a limit present, we'll look at a dataset consisting of object_ids that meet the constructs of the $where clause.
+ // Without a limit, we can use the actual table as a dataset.
+ $from = $bucket_size ?
+ "( SELECT $distinct_sql $id_field FROM $object_table $where_sql ORDER BY $id_field ASC LIMIT $bucket_size ) as ids" :
+ "$object_table $where_sql ORDER BY $id_field ASC";
+
+ return $wpdb->get_row(
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ "SELECT MIN($id_field) as min, MAX($id_field) as max FROM $from"
+ );
+ }
+
+ /**
+ * Retrieve the checksum histogram for a specific object type.
+ *
+ * @access public
+ *
+ * @todo Refactor to not use interpolated values and properly prepare the SQL query.
+ *
+ * @param string $object_type Object type.
+ * @param int $buckets Number of buckets to split the objects to.
+ * @param int $start_id Minimum object ID.
+ * @param int $end_id Maximum object ID.
+ * @param array $columns Table columns to calculate the checksum from.
+ * @param bool $strip_non_ascii Whether to strip non-ASCII characters.
+ * @param string $salt Salt, used for $wpdb->prepare()'s args.
+ * @return array The checksum histogram.
+ */
+ public function checksum_histogram( $object_type, $buckets, $start_id = null, $end_id = null, $columns = null, $strip_non_ascii = true, $salt = '' ) {
+ global $wpdb;
+
+ $wpdb->queries = array();
+
+ if ( empty( $columns ) ) {
+ $columns = $this->get_checksum_columns_for_object_type( $object_type );
+ }
+
+ switch ( $object_type ) {
+ case 'posts':
+ $object_count = $this->post_count( null, $start_id, $end_id );
+ $object_table = $wpdb->posts;
+ $id_field = 'ID';
+ $where_sql = Settings::get_blacklisted_post_types_sql();
+ break;
+ case 'post_meta':
+ $object_table = $wpdb->postmeta;
+ $where_sql = Settings::get_whitelisted_post_meta_sql();
+ $object_count = $this->meta_count( $object_table, $where_sql, $start_id, $end_id );
+ $id_field = 'meta_id';
+ break;
+ case 'comments':
+ $object_count = $this->comment_count( null, $start_id, $end_id );
+ $object_table = $wpdb->comments;
+ $id_field = 'comment_ID';
+ $where_sql = Settings::get_comments_filter_sql();
+ break;
+ case 'comment_meta':
+ $object_table = $wpdb->commentmeta;
+ $where_sql = Settings::get_whitelisted_comment_meta_sql();
+ $object_count = $this->meta_count( $object_table, $where_sql, $start_id, $end_id );
+ $id_field = 'meta_id';
+ break;
+ case 'terms':
+ $object_table = $wpdb->terms;
+ $object_count = $this->term_count();
+ $id_field = 'term_id';
+ $where_sql = '1=1';
+ break;
+ case 'term_taxonomy':
+ $object_table = $wpdb->term_taxonomy;
+ $object_count = $this->term_taxonomy_count();
+ $id_field = 'term_taxonomy_id';
+ $where_sql = '1=1';
+ break;
+ case 'term_relationships':
+ $object_table = $wpdb->term_relationships;
+ $object_count = $this->term_relationship_count();
+ $id_field = 'object_id';
+ $where_sql = '1=1';
+ break;
+ default:
+ return false;
+ }
+
+ $bucket_size = (int) ceil( $object_count / $buckets );
+ $previous_max_id = 0;
+ $histogram = array();
+
+ // This is used for the min / max query, while $where_sql is used for the checksum query.
+ $where = $where_sql;
+
+ if ( $start_id ) {
+ $where .= " AND $id_field >= " . (int) $start_id;
+ }
+
+ if ( $end_id ) {
+ $where .= " AND $id_field <= " . (int) $end_id;
+ }
+
+ do {
+ $result = $this->get_min_max_object_id(
+ $id_field,
+ $object_table,
+ $where . " AND $id_field > $previous_max_id",
+ $bucket_size
+ );
+
+ if ( null === $result->min || null === $result->max ) {
+ // Nothing to checksum here...
+ break;
+ }
+
+ // Get the checksum value.
+ $value = $this->table_checksum( $object_table, $columns, $id_field, $where_sql, $result->min, $result->max, $strip_non_ascii, $salt );
+
+ if ( is_wp_error( $value ) ) {
+ return $value;
+ }
+
+ if ( null === $result->min || null === $result->max ) {
+ break;
+ } elseif ( $result->min === $result->max ) {
+ $histogram[ $result->min ] = $value;
+ } else {
+ $histogram[ "{$result->min}-{$result->max}" ] = $value;
+ }
+
+ $previous_max_id = $result->max;
+ } while ( true );
+
+ return $histogram;
+ }
+
+ /**
+ * Retrieve the checksum for a specific database table.
+ *
+ * @access private
+ *
+ * @todo Refactor to properly prepare the SQL query.
+ *
+ * @param string $table Table name.
+ * @param array $columns Table columns to calculate the checksum from.
+ * @param int $id_column Name of the unique ID column.
+ * @param string $where_sql Additional WHERE clause SQL.
+ * @param int $min_id Minimum object ID.
+ * @param int $max_id Maximum object ID.
+ * @param bool $strip_non_ascii Whether to strip non-ASCII characters.
+ * @param string $salt Salt, used for $wpdb->prepare()'s args.
+ * @return int|\WP_Error The table histogram, or \WP_Error on failure.
+ */
+ private function table_checksum( $table, $columns, $id_column, $where_sql = '1=1', $min_id = null, $max_id = null, $strip_non_ascii = true, $salt = '' ) {
+ global $wpdb;
+
+ // Sanitize to just valid MySQL column names.
+ $sanitized_columns = preg_grep( '/^[0-9,a-z,A-Z$_]+$/i', $columns );
+
+ if ( $strip_non_ascii ) {
+ $columns_sql = implode( ',', array_map( array( $this, 'strip_non_ascii_sql' ), $sanitized_columns ) );
+ } else {
+ $columns_sql = implode( ',', $sanitized_columns );
+ }
+
+ if ( null !== $min_id && null !== $max_id ) {
+ if ( $min_id === $max_id ) {
+ $min_id = (int) $min_id;
+ $where_sql .= " AND $id_column = $min_id LIMIT 1";
+ } else {
+ $min_id = (int) $min_id;
+ $max_id = (int) $max_id;
+ $size = $max_id - $min_id;
+ $where_sql .= " AND $id_column >= $min_id AND $id_column <= $max_id LIMIT $size";
+ }
+ } else {
+ if ( null !== $min_id ) {
+ $min_id = (int) $min_id;
+ $where_sql .= " AND $id_column >= $min_id";
+ }
+
+ if ( null !== $max_id ) {
+ $max_id = (int) $max_id;
+ $where_sql .= " AND $id_column <= $max_id";
+ }
+ }
+
+ $query = <<get_var( $wpdb->prepare( $query, $salt ) );
+ if ( $wpdb->last_error ) {
+ return new \WP_Error( 'database_error', $wpdb->last_error );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Retrieve the type of the checksum.
+ *
+ * @access public
+ *
+ * @return string Type of the checksum.
+ */
+ public function get_checksum_type() {
+ return 'sum';
+ }
+
+ /**
+ * Count the meta values in a table, within a specified range.
+ *
+ * @access private
+ *
+ * @todo Refactor to not use interpolated values when preparing the SQL query.
+ *
+ * @param string $table Table name.
+ * @param string $where_sql Additional WHERE SQL.
+ * @param int $min_id Minimum meta ID.
+ * @param int $max_id Maximum meta ID.
+ * @return int Number of meta values.
+ */
+ private function meta_count( $table, $where_sql, $min_id, $max_id ) {
+ global $wpdb;
+
+ if ( ! empty( $min_id ) ) {
+ $where_sql .= ' AND meta_id >= ' . (int) $min_id;
+ }
+
+ if ( ! empty( $max_id ) ) {
+ $where_sql .= ' AND meta_id <= ' . (int) $max_id;
+ }
+
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ return $wpdb->get_var( "SELECT COUNT(*) FROM $table WHERE $where_sql" );
+ }
+
+ /**
+ * Wraps a column name in SQL which strips non-ASCII chars.
+ * This helps normalize data to avoid checksum differences caused by
+ * badly encoded data in the DB.
+ *
+ * @param string $column_name Name of the column.
+ * @return string Column name, without the non-ASCII chars.
+ */
+ public function strip_non_ascii_sql( $column_name ) {
+ return "REPLACE( CONVERT( $column_name USING ascii ), '?', '' )";
+ }
+
+ /**
+ * Used in methods that are not implemented and shouldn't be invoked.
+ *
+ * @access private
+ * @throws \Exception If this method is invoked.
+ */
+ private function invalid_call() {
+ // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
+ $backtrace = debug_backtrace();
+ $caller = $backtrace[1]['function'];
+ throw new \Exception( "This function $caller is not supported on the WP Replicastore" );
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/class-sender.php b/vendor/automattic/jetpack-sync/src/class-sender.php
new file mode 100644
index 0000000000000..794a168e5ea4a
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/class-sender.php
@@ -0,0 +1,880 @@
+:(
+ *
+ * @access protected
+ * @static
+ */
+ protected function __construct() {
+ $this->set_defaults();
+ $this->init();
+ }
+
+ /**
+ * Initialize the sender.
+ * Prepares the current user and initializes all sync modules.
+ *
+ * @access private
+ */
+ private function init() {
+ add_action( 'jetpack_sync_before_send_queue_sync', array( $this, 'maybe_set_user_from_token' ), 1 );
+ add_action( 'jetpack_sync_before_send_queue_sync', array( $this, 'maybe_clear_user_from_token' ), 20 );
+ add_filter( 'jetpack_xmlrpc_unauthenticated_methods', array( $this, 'register_jetpack_xmlrpc_methods' ) );
+ foreach ( Modules::get_modules() as $module ) {
+ $module->init_before_send();
+ }
+ }
+
+ /**
+ * Detect if this is a XMLRPC request with a valid signature.
+ * If so, changes the user to the new one.
+ *
+ * @access public
+ */
+ public function maybe_set_user_from_token() {
+ $connection = new Manager();
+ $verified_user = $connection->verify_xml_rpc_signature();
+ if ( Constants::is_true( 'XMLRPC_REQUEST' ) &&
+ ! is_wp_error( $verified_user )
+ && $verified_user
+ ) {
+ $old_user = wp_get_current_user();
+ $this->old_user = isset( $old_user->ID ) ? $old_user->ID : 0;
+ wp_set_current_user( $verified_user['user_id'] );
+ }
+ }
+
+ /**
+ * If we used to have a previous current user, revert back to it.
+ *
+ * @access public
+ */
+ public function maybe_clear_user_from_token() {
+ if ( isset( $this->old_user ) ) {
+ wp_set_current_user( $this->old_user );
+ }
+ }
+
+ /**
+ * Retrieve the next sync time.
+ *
+ * @access public
+ *
+ * @param string $queue_name Name of the queue.
+ * @return float Timestamp of the next sync.
+ */
+ public function get_next_sync_time( $queue_name ) {
+ return (float) get_option( self::NEXT_SYNC_TIME_OPTION_NAME . '_' . $queue_name, 0 );
+ }
+
+ /**
+ * Set the next sync time.
+ *
+ * @access public
+ *
+ * @param int $time Timestamp of the next sync.
+ * @param string $queue_name Name of the queue.
+ * @return boolean True if update was successful, false otherwise.
+ */
+ public function set_next_sync_time( $time, $queue_name ) {
+ return update_option( self::NEXT_SYNC_TIME_OPTION_NAME . '_' . $queue_name, $time, true );
+ }
+
+ /**
+ * Trigger a full sync.
+ *
+ * @access public
+ *
+ * @return boolean|\WP_Error True if this sync sending was successful, error object otherwise.
+ */
+ public function do_full_sync() {
+ $sync_module = Modules::get_module( 'full-sync' );
+ if ( ! $sync_module ) {
+ return;
+ }
+ if ( ! Settings::get_setting( 'full_sync_sender_enabled' ) ) {
+ return;
+ }
+ $this->continue_full_sync_enqueue();
+ // immediate full sync sends data in continue_full_sync_enqueue.
+ if ( false === strpos( get_class( $sync_module ), 'Full_Sync_Immediately' ) ) {
+ return $this->do_sync_and_set_delays( $this->full_sync_queue );
+ } else {
+ $status = $sync_module->get_status();
+ // Sync not started or Sync finished.
+ if ( false === $status['started'] || ( ! empty( $status['started'] ) && ! empty( $status['finished'] ) ) ) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ }
+
+ /**
+ * Enqueue the next sync items for sending.
+ * Will not be done if the current request is a WP import one.
+ * Will be delayed until the next sync time comes.
+ *
+ * @access private
+ */
+ private function continue_full_sync_enqueue() {
+ if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
+ return false;
+ }
+
+ if ( $this->get_next_sync_time( 'full-sync-enqueue' ) > microtime( true ) ) {
+ return false;
+ }
+
+ Modules::get_module( 'full-sync' )->continue_enqueuing();
+
+ $this->set_next_sync_time( time() + $this->get_enqueue_wait_time(), 'full-sync-enqueue' );
+ }
+
+ /**
+ * Trigger incremental sync.
+ *
+ * @access public
+ *
+ * @return boolean|\WP_Error True if this sync sending was successful, error object otherwise.
+ */
+ public function do_sync() {
+ return $this->do_sync_and_set_delays( $this->sync_queue );
+ }
+
+ /**
+ * Trigger sync for a certain sync queue.
+ * Responsible for setting next sync time.
+ * Will not be delayed if the current request is a WP import one.
+ * Will be delayed until the next sync time comes.
+ *
+ * @access public
+ *
+ * @param Automattic\Jetpack\Sync\Queue $queue Queue object.
+ * @return boolean|\WP_Error True if this sync sending was successful, error object otherwise.
+ */
+ public function do_sync_and_set_delays( $queue ) {
+ // Don't sync if importing.
+ if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
+ return new \WP_Error( 'is_importing' );
+ }
+
+ // Don't sync if request is marked as read only.
+ if ( Constants::is_true( 'JETPACK_SYNC_READ_ONLY' ) ) {
+ return new \WP_Error( 'jetpack_sync_read_only' );
+ }
+
+ if ( ! Settings::is_sender_enabled( $queue->id ) ) {
+ return new \WP_Error( 'sender_disabled_for_queue_' . $queue->id );
+ }
+
+ // Don't sync if we are throttled.
+ if ( $this->get_next_sync_time( $queue->id ) > microtime( true ) ) {
+ return new \WP_Error( 'sync_throttled' );
+ }
+
+ $start_time = microtime( true );
+
+ Settings::set_is_syncing( true );
+
+ $sync_result = $this->do_sync_for_queue( $queue );
+
+ Settings::set_is_syncing( false );
+
+ $exceeded_sync_wait_threshold = ( microtime( true ) - $start_time ) > (float) $this->get_sync_wait_threshold();
+
+ if ( is_wp_error( $sync_result ) ) {
+ if ( 'unclosed_buffer' === $sync_result->get_error_code() ) {
+ $this->set_next_sync_time( time() + self::QUEUE_LOCKED_SYNC_DELAY, $queue->id );
+ }
+ if ( 'wpcom_error' === $sync_result->get_error_code() ) {
+ $this->set_next_sync_time( time() + self::WPCOM_ERROR_SYNC_DELAY, $queue->id );
+ }
+ } elseif ( $exceeded_sync_wait_threshold ) {
+ // If we actually sent data and it took a while, wait before sending again.
+ $this->set_next_sync_time( time() + $this->get_sync_wait_time(), $queue->id );
+ }
+
+ return $sync_result;
+ }
+
+ /**
+ * Retrieve the next sync items to send.
+ *
+ * @access public
+ *
+ * @param (array|Automattic\Jetpack\Sync\Queue_Buffer) $buffer_or_items Queue buffer or array of objects.
+ * @param boolean $encode Whether to encode the items.
+ * @return array Sync items to send.
+ */
+ public function get_items_to_send( $buffer_or_items, $encode = true ) {
+ // Track how long we've been processing so we can avoid request timeouts.
+ $start_time = microtime( true );
+ $upload_size = 0;
+ $items_to_send = array();
+ $items = is_array( $buffer_or_items ) ? $buffer_or_items : $buffer_or_items->get_items();
+ // Set up current screen to avoid errors rendering content.
+ require_once ABSPATH . 'wp-admin/includes/class-wp-screen.php';
+ require_once ABSPATH . 'wp-admin/includes/screen.php';
+ set_current_screen( 'sync' );
+ $skipped_items_ids = array();
+ /**
+ * We estimate the total encoded size as we go by encoding each item individually.
+ * This is expensive, but the only way to really know :/
+ */
+ foreach ( $items as $key => $item ) {
+ // Suspending cache addition help prevent overloading in memory cache of large sites.
+ wp_suspend_cache_addition( true );
+ /**
+ * Modify the data within an action before it is serialized and sent to the server
+ * For example, during full sync this expands Post ID's into full Post objects,
+ * so that we don't have to serialize the whole object into the queue.
+ *
+ * @since 4.2.0
+ *
+ * @param array The action parameters
+ * @param int The ID of the user who triggered the action
+ */
+ $item[1] = apply_filters( 'jetpack_sync_before_send_' . $item[0], $item[1], $item[2] );
+ wp_suspend_cache_addition( false );
+ if ( false === $item[1] ) {
+ $skipped_items_ids[] = $key;
+ continue;
+ }
+ $encoded_item = $encode ? $this->codec->encode( $item ) : $item;
+ $upload_size += strlen( $encoded_item );
+ if ( $upload_size > $this->upload_max_bytes && count( $items_to_send ) > 0 ) {
+ break;
+ }
+ $items_to_send[ $key ] = $encoded_item;
+ if ( microtime( true ) - $start_time > $this->max_dequeue_time ) {
+ break;
+ }
+ }
+
+ return array( $items_to_send, $skipped_items_ids, $items, microtime( true ) - $start_time );
+ }
+
+ /**
+ * If supported, flush all response data to the client and finish the request.
+ * This allows for time consuming tasks to be performed without leaving the connection open.
+ *
+ * @access private
+ */
+ private function fastcgi_finish_request() {
+ if ( function_exists( 'fastcgi_finish_request' ) && version_compare( phpversion(), '7.0.16', '>=' ) ) {
+ fastcgi_finish_request();
+ }
+ }
+
+ /**
+ * Perform sync for a certain sync queue.
+ *
+ * @access public
+ *
+ * @param Automattic\Jetpack\Sync\Queue $queue Queue object.
+ * @return boolean|\WP_Error True if this sync sending was successful, error object otherwise.
+ */
+ public function do_sync_for_queue( $queue ) {
+ do_action( 'jetpack_sync_before_send_queue_' . $queue->id );
+ if ( $queue->size() === 0 ) {
+ return new \WP_Error( 'empty_queue_' . $queue->id );
+ }
+
+ /**
+ * Now that we're sure we are about to sync, try to ignore user abort
+ * so we can avoid getting into a bad state.
+ */
+ if ( function_exists( 'ignore_user_abort' ) ) {
+ ignore_user_abort( true );
+ }
+
+ /* Don't make the request block till we finish, if possible. */
+ if ( Constants::is_true( 'REST_REQUEST' ) || Constants::is_true( 'XMLRPC_REQUEST' ) ) {
+ $this->fastcgi_finish_request();
+ }
+
+ $checkout_start_time = microtime( true );
+
+ $buffer = $queue->checkout_with_memory_limit( $this->dequeue_max_bytes, $this->upload_max_rows );
+
+ if ( ! $buffer ) {
+ // Buffer has no items.
+ return new \WP_Error( 'empty_buffer' );
+ }
+
+ if ( is_wp_error( $buffer ) ) {
+ return $buffer;
+ }
+
+ $checkout_duration = microtime( true ) - $checkout_start_time;
+
+ list( $items_to_send, $skipped_items_ids, $items, $preprocess_duration ) = $this->get_items_to_send( $buffer, true );
+ if ( ! empty( $items_to_send ) ) {
+ /**
+ * Fires when data is ready to send to the server.
+ * Return false or WP_Error to abort the sync (e.g. if there's an error)
+ * The items will be automatically re-sent later
+ *
+ * @since 4.2.0
+ *
+ * @param array $data The action buffer
+ * @param string $codec The codec name used to encode the data
+ * @param double $time The current time
+ * @param string $queue The queue used to send ('sync' or 'full_sync')
+ * @param float $checkout_duration The duration of the checkout operation.
+ * @param float $preprocess_duration The duration of the pre-process operation.
+ * @param int $queue_size The size of the sync queue at the time of processing.
+ */
+ Settings::set_is_sending( true );
+ $processed_item_ids = apply_filters( 'jetpack_sync_send_data', $items_to_send, $this->codec->name(), microtime( true ), $queue->id, $checkout_duration, $preprocess_duration, $queue->size(), $buffer->id );
+ Settings::set_is_sending( false );
+ } else {
+ $processed_item_ids = $skipped_items_ids;
+ $skipped_items_ids = array();
+ }
+
+ if ( 'non-blocking' !== $processed_item_ids ) {
+ if ( ! $processed_item_ids || is_wp_error( $processed_item_ids ) ) {
+ $checked_in_item_ids = $queue->checkin( $buffer );
+ if ( is_wp_error( $checked_in_item_ids ) ) {
+ // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
+ error_log( 'Error checking in buffer: ' . $checked_in_item_ids->get_error_message() );
+ $queue->force_checkin();
+ }
+ if ( is_wp_error( $processed_item_ids ) ) {
+ return new \WP_Error( 'wpcom_error', $processed_item_ids->get_error_code() );
+ }
+
+ // Returning a wpcom_error is a sign to the caller that we should wait a while before syncing again.
+ return new \WP_Error( 'wpcom_error', 'jetpack_sync_send_data_false' );
+ } else {
+ // Detect if the last item ID was an error.
+ $had_wp_error = is_wp_error( end( $processed_item_ids ) );
+ if ( $had_wp_error ) {
+ $wp_error = array_pop( $processed_item_ids );
+ }
+ // Also checkin any items that were skipped.
+ if ( count( $skipped_items_ids ) > 0 ) {
+ $processed_item_ids = array_merge( $processed_item_ids, $skipped_items_ids );
+ }
+ $processed_items = array_intersect_key( $items, array_flip( $processed_item_ids ) );
+ /**
+ * Allows us to keep track of all the actions that have been sent.
+ * Allows us to calculate the progress of specific actions.
+ *
+ * @since 4.2.0
+ *
+ * @param array $processed_actions The actions that we send successfully.
+ */
+ do_action( 'jetpack_sync_processed_actions', $processed_items );
+ $queue->close( $buffer, $processed_item_ids );
+ // Returning a WP_Error is a sign to the caller that we should wait a while before syncing again.
+ if ( $had_wp_error ) {
+ return new \WP_Error( 'wpcom_error', $wp_error->get_error_code() );
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Immediately sends a single item without firing or enqueuing it
+ *
+ * @param string $action_name The action.
+ * @param array $data The data associated with the action.
+ *
+ * @return Items processed. TODO: this doesn't make much sense anymore, it should probably be just a bool.
+ */
+ public function send_action( $action_name, $data = null ) {
+ if ( ! Settings::is_sender_enabled( 'full_sync' ) ) {
+ return array();
+ }
+
+ // Compose the data to be sent.
+ $action_to_send = $this->create_action_to_send( $action_name, $data );
+
+ list( $items_to_send, $skipped_items_ids, $items, $preprocess_duration ) = $this->get_items_to_send( $action_to_send, true ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ Settings::set_is_sending( true );
+ $processed_item_ids = apply_filters( 'jetpack_sync_send_data', $items_to_send, $this->get_codec()->name(), microtime( true ), 'immediate-send', 0, $preprocess_duration );
+ Settings::set_is_sending( false );
+
+ /**
+ * Allows us to keep track of all the actions that have been sent.
+ * Allows us to calculate the progress of specific actions.
+ *
+ * @param array $processed_actions The actions that we send successfully.
+ *
+ * @since 4.2.0
+ */
+ do_action( 'jetpack_sync_processed_actions', $action_to_send );
+
+ return $processed_item_ids;
+ }
+
+ /**
+ * Create an synthetic action for direct sending to WPCOM during full sync (for example)
+ *
+ * @access private
+ *
+ * @param string $action_name The action.
+ * @param array $data The data associated with the action.
+ * @return array An array of synthetic sync actions keyed by current microtime(true)
+ */
+ private function create_action_to_send( $action_name, $data ) {
+ return array(
+ (string) microtime( true ) => array(
+ $action_name,
+ $data,
+ get_current_user_id(),
+ microtime( true ),
+ Settings::is_importing(),
+ ),
+ );
+ }
+
+ /**
+ * Returns any object that is able to be synced.
+ *
+ * @access public
+ *
+ * @param array $args the synchronized object parameters.
+ * @return string Encoded sync object.
+ */
+ public function sync_object( $args ) {
+ // For example: posts, post, 5.
+ list( $module_name, $object_type, $id ) = $args;
+
+ $sync_module = Modules::get_module( $module_name );
+ $codec = $this->get_codec();
+
+ return $codec->encode( $sync_module->get_object_by_id( $object_type, $id ) );
+ }
+
+ /**
+ * Register additional sync XML-RPC methods available to Jetpack for authenticated users.
+ *
+ * @access public
+ * @since 7.8
+ *
+ * @param array $jetpack_methods XML-RPC methods available to the Jetpack Server.
+ * @return array Filtered XML-RPC methods.
+ */
+ public function register_jetpack_xmlrpc_methods( $jetpack_methods ) {
+ $jetpack_methods['jetpack.syncObject'] = array( $this, 'sync_object' );
+ return $jetpack_methods;
+ }
+
+ /**
+ * Get the incremental sync queue object.
+ *
+ * @access public
+ *
+ * @return Automattic\Jetpack\Sync\Queue Queue object.
+ */
+ public function get_sync_queue() {
+ return $this->sync_queue;
+ }
+
+ /**
+ * Get the full sync queue object.
+ *
+ * @access public
+ *
+ * @return Automattic\Jetpack\Sync\Queue Queue object.
+ */
+ public function get_full_sync_queue() {
+ return $this->full_sync_queue;
+ }
+
+ /**
+ * Get the codec object.
+ *
+ * @access public
+ *
+ * @return Automattic\Jetpack\Sync\Codec_Interface Codec object.
+ */
+ public function get_codec() {
+ return $this->codec;
+ }
+
+ /**
+ * Determine the codec object.
+ * Use gzip deflate if supported.
+ *
+ * @access public
+ */
+ public function set_codec() {
+ if ( function_exists( 'gzinflate' ) ) {
+ $this->codec = new JSON_Deflate_Array_Codec();
+ } else {
+ $this->codec = new Simple_Codec();
+ }
+ }
+
+ /**
+ * Compute and send all the checksums.
+ *
+ * @access public
+ */
+ public function send_checksum() {
+ $store = new Replicastore();
+ do_action( 'jetpack_sync_checksum', $store->checksum_all() );
+ }
+
+ /**
+ * Reset the incremental sync queue.
+ *
+ * @access public
+ */
+ public function reset_sync_queue() {
+ $this->sync_queue->reset();
+ }
+
+ /**
+ * Reset the full sync queue.
+ *
+ * @access public
+ */
+ public function reset_full_sync_queue() {
+ $this->full_sync_queue->reset();
+ }
+
+ /**
+ * Set the maximum bytes to checkout without exceeding the memory limit.
+ *
+ * @access public
+ *
+ * @param int $size Maximum bytes to checkout.
+ */
+ public function set_dequeue_max_bytes( $size ) {
+ $this->dequeue_max_bytes = $size;
+ }
+
+ /**
+ * Set the maximum bytes in a single encoded item.
+ *
+ * @access public
+ *
+ * @param int $max_bytes Maximum bytes in a single encoded item.
+ */
+ public function set_upload_max_bytes( $max_bytes ) {
+ $this->upload_max_bytes = $max_bytes;
+ }
+
+ /**
+ * Set the maximum number of sync items in a single action.
+ *
+ * @access public
+ *
+ * @param int $max_rows Maximum number of sync items.
+ */
+ public function set_upload_max_rows( $max_rows ) {
+ $this->upload_max_rows = $max_rows;
+ }
+
+ /**
+ * Set the sync wait time (in seconds).
+ *
+ * @access public
+ *
+ * @param int $seconds Sync wait time.
+ */
+ public function set_sync_wait_time( $seconds ) {
+ $this->sync_wait_time = $seconds;
+ }
+
+ /**
+ * Get current sync wait time (in seconds).
+ *
+ * @access public
+ *
+ * @return int Sync wait time.
+ */
+ public function get_sync_wait_time() {
+ return $this->sync_wait_time;
+ }
+
+ /**
+ * Set the enqueue wait time (in seconds).
+ *
+ * @access public
+ *
+ * @param int $seconds Enqueue wait time.
+ */
+ public function set_enqueue_wait_time( $seconds ) {
+ $this->enqueue_wait_time = $seconds;
+ }
+
+ /**
+ * Get current enqueue wait time (in seconds).
+ *
+ * @access public
+ *
+ * @return int Enqueue wait time.
+ */
+ public function get_enqueue_wait_time() {
+ return $this->enqueue_wait_time;
+ }
+
+ /**
+ * Set the sync wait threshold (in seconds).
+ *
+ * @access public
+ *
+ * @param int $seconds Sync wait threshold.
+ */
+ public function set_sync_wait_threshold( $seconds ) {
+ $this->sync_wait_threshold = $seconds;
+ }
+
+ /**
+ * Get current sync wait threshold (in seconds).
+ *
+ * @access public
+ *
+ * @return int Sync wait threshold.
+ */
+ public function get_sync_wait_threshold() {
+ return $this->sync_wait_threshold;
+ }
+
+ /**
+ * Set the maximum time for perfirming a checkout of items from the queue (in seconds).
+ *
+ * @access public
+ *
+ * @param int $seconds Maximum dequeue time.
+ */
+ public function set_max_dequeue_time( $seconds ) {
+ $this->max_dequeue_time = $seconds;
+ }
+
+ /**
+ * Initialize the sync queues, codec and set the default settings.
+ *
+ * @access public
+ */
+ public function set_defaults() {
+ $this->sync_queue = new Queue( 'sync' );
+ $this->full_sync_queue = new Queue( 'full_sync' );
+ $this->set_codec();
+
+ // Saved settings.
+ Settings::set_importing( null );
+ $settings = Settings::get_settings();
+ $this->set_dequeue_max_bytes( $settings['dequeue_max_bytes'] );
+ $this->set_upload_max_bytes( $settings['upload_max_bytes'] );
+ $this->set_upload_max_rows( $settings['upload_max_rows'] );
+ $this->set_sync_wait_time( $settings['sync_wait_time'] );
+ $this->set_enqueue_wait_time( $settings['enqueue_wait_time'] );
+ $this->set_sync_wait_threshold( $settings['sync_wait_threshold'] );
+ $this->set_max_dequeue_time( Defaults::get_max_sync_execution_time() );
+ }
+
+ /**
+ * Reset sync queues, modules and settings.
+ *
+ * @access public
+ */
+ public function reset_data() {
+ $this->reset_sync_queue();
+ $this->reset_full_sync_queue();
+
+ foreach ( Modules::get_modules() as $module ) {
+ $module->reset_data();
+ }
+
+ foreach ( array( 'sync', 'full_sync', 'full-sync-enqueue' ) as $queue_name ) {
+ delete_option( self::NEXT_SYNC_TIME_OPTION_NAME . '_' . $queue_name );
+ }
+
+ Settings::reset_data();
+ }
+
+ /**
+ * Perform cleanup at the event of plugin uninstallation.
+ *
+ * @access public
+ */
+ public function uninstall() {
+ // Lets delete all the other fun stuff like transient and option and the sync queue.
+ $this->reset_data();
+
+ // Delete the full sync status.
+ delete_option( 'jetpack_full_sync_status' );
+
+ // Clear the sync cron.
+ wp_clear_scheduled_hook( 'jetpack_sync_cron' );
+ wp_clear_scheduled_hook( 'jetpack_sync_full_cron' );
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/class-server.php b/vendor/automattic/jetpack-sync/src/class-server.php
new file mode 100644
index 0000000000000..2f97fd13fb5c8
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/class-server.php
@@ -0,0 +1,190 @@
+:(
+ *
+ * @access public
+ */
+ public function __construct() {
+ $this->codec = new JSON_Deflate_Array_Codec();
+ }
+
+ /**
+ * Set the codec instance.
+ *
+ * @access public
+ *
+ * @param Automattic\Jetpack\Sync\Codec_Interface $codec Codec instance.
+ */
+ public function set_codec( Codec_Interface $codec ) {
+ $this->codec = $codec;
+ }
+
+ /**
+ * Attempt to lock the request when the server receives concurrent requests from the same blog.
+ *
+ * @access public
+ *
+ * @param int $blog_id ID of the blog.
+ * @param int $expiry Blog lock transient lifetime.
+ * @return boolean True if succeeded, false otherwise.
+ */
+ public function attempt_request_lock( $blog_id, $expiry = self::BLOG_LOCK_TRANSIENT_EXPIRY ) {
+ $transient_name = $this->get_concurrent_request_transient_name( $blog_id );
+ $locked_time = get_site_transient( $transient_name );
+ if ( $locked_time ) {
+ return false;
+ }
+ set_site_transient( $transient_name, microtime( true ), $expiry );
+
+ return true;
+ }
+
+ /**
+ * Retrieve the blog lock transient name for a particular blog.
+ *
+ * @access public
+ *
+ * @param int $blog_id ID of the blog.
+ * @return string Name of the blog lock transient.
+ */
+ private function get_concurrent_request_transient_name( $blog_id ) {
+ return self::BLOG_LOCK_TRANSIENT_PREFIX . $blog_id;
+ }
+
+ /**
+ * Remove the request lock from a particular blog ID.
+ *
+ * @access public
+ *
+ * @param int $blog_id ID of the blog.
+ */
+ public function remove_request_lock( $blog_id ) {
+ delete_site_transient( $this->get_concurrent_request_transient_name( $blog_id ) );
+ }
+
+ /**
+ * Receive and process sync events.
+ *
+ * @access public
+ *
+ * @param array $data Sync events.
+ * @param object $token The auth token used to invoke the API.
+ * @param int $sent_timestamp Timestamp (in seconds) when the actions were transmitted.
+ * @param string $queue_id ID of the queue from which the event was sent (`sync` or `full_sync`).
+ * @return array Processed sync events.
+ */
+ public function receive( $data, $token = null, $sent_timestamp = null, $queue_id = null ) {
+ $start_time = microtime( true );
+ if ( ! is_array( $data ) ) {
+ return new \WP_Error( 'action_decoder_error', 'Events must be an array' );
+ }
+
+ if ( $token && ! $this->attempt_request_lock( $token->blog_id ) ) {
+ /**
+ * Fires when the server receives two concurrent requests from the same blog
+ *
+ * @since 4.2.0
+ *
+ * @param token The token object of the misbehaving site
+ */
+ do_action( 'jetpack_sync_multi_request_fail', $token );
+
+ return new \WP_Error( 'concurrent_request_error', 'There is another request running for the same blog ID' );
+ }
+
+ $events = wp_unslash( array_map( array( $this->codec, 'decode' ), $data ) );
+ $events_processed = array();
+
+ /**
+ * Fires when an array of actions are received from a remote Jetpack site
+ *
+ * @since 4.2.0
+ *
+ * @param array Array of actions received from the remote site
+ */
+ do_action( 'jetpack_sync_remote_actions', $events, $token );
+
+ foreach ( $events as $key => $event ) {
+ list( $action_name, $args, $user_id, $timestamp, $silent ) = $event;
+
+ /**
+ * Fires when an action is received from a remote Jetpack site
+ *
+ * @since 4.2.0
+ *
+ * @param string $action_name The name of the action executed on the remote site
+ * @param array $args The arguments passed to the action
+ * @param int $user_id The external_user_id who did the action
+ * @param bool $silent Whether the item was created via import
+ * @param double $timestamp Timestamp (in seconds) when the action occurred
+ * @param double $sent_timestamp Timestamp (in seconds) when the action was transmitted
+ * @param string $queue_id ID of the queue from which the event was sent (sync or full_sync)
+ * @param array $token The auth token used to invoke the API
+ */
+ do_action( 'jetpack_sync_remote_action', $action_name, $args, $user_id, $silent, $timestamp, $sent_timestamp, $queue_id, $token );
+
+ $events_processed[] = $key;
+
+ if ( microtime( true ) - $start_time > self::MAX_TIME_PER_REQUEST_IN_SECONDS ) {
+ break;
+ }
+ }
+
+ if ( $token ) {
+ $this->remove_request_lock( $token->blog_id );
+ }
+
+ return $events_processed;
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/class-settings.php b/vendor/automattic/jetpack-sync/src/class-settings.php
new file mode 100644
index 0000000000000..8a5e4d8eb4b7e
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/class-settings.php
@@ -0,0 +1,442 @@
+ true,
+ 'upload_max_bytes' => true,
+ 'upload_max_rows' => true,
+ 'sync_wait_time' => true,
+ 'sync_wait_threshold' => true,
+ 'enqueue_wait_time' => true,
+ 'max_queue_size' => true,
+ 'max_queue_lag' => true,
+ 'queue_max_writes_sec' => true,
+ 'post_types_blacklist' => true,
+ 'taxonomies_blacklist' => true,
+ 'disable' => true,
+ 'network_disable' => true,
+ 'render_filtered_content' => true,
+ 'post_meta_whitelist' => true,
+ 'comment_meta_whitelist' => true,
+ 'max_enqueue_full_sync' => true,
+ 'max_queue_size_full_sync' => true,
+ 'sync_via_cron' => true,
+ 'cron_sync_time_limit' => true,
+ 'known_importers' => true,
+ 'term_relationships_full_sync_item_size' => true,
+ 'sync_sender_enabled' => true,
+ 'full_sync_sender_enabled' => true,
+ 'full_sync_send_duration' => true,
+ 'full_sync_limits' => true,
+ );
+
+ /**
+ * Whether WordPress is currently running an import.
+ *
+ * @access public
+ * @static
+ *
+ * @var null|boolean
+ */
+ public static $is_importing;
+
+ /**
+ * Whether WordPress is currently running a WP cron request.
+ *
+ * @access public
+ * @static
+ *
+ * @var null|boolean
+ */
+ public static $is_doing_cron;
+
+ /**
+ * Whether we're currently syncing.
+ *
+ * @access public
+ * @static
+ *
+ * @var null|boolean
+ */
+ public static $is_syncing;
+
+ /**
+ * Whether we're currently sending sync items.
+ *
+ * @access public
+ * @static
+ *
+ * @var null|boolean
+ */
+ public static $is_sending;
+
+ /**
+ * Retrieve all settings with their current values.
+ *
+ * @access public
+ * @static
+ *
+ * @return array All current settings.
+ */
+ public static function get_settings() {
+ $settings = array();
+ foreach ( array_keys( self::$valid_settings ) as $setting ) {
+ $settings[ $setting ] = self::get_setting( $setting );
+ }
+
+ return $settings;
+ }
+
+ /**
+ * Fetches the setting. It saves it if the setting doesn't exist, so that it gets
+ * autoloaded on page load rather than re-queried every time.
+ *
+ * @access public
+ * @static
+ *
+ * @param string $setting The setting name.
+ * @return mixed The setting value.
+ */
+ public static function get_setting( $setting ) {
+ if ( ! isset( self::$valid_settings[ $setting ] ) ) {
+ return false;
+ }
+
+ if ( self::is_network_setting( $setting ) ) {
+ if ( is_multisite() ) {
+ $value = get_site_option( self::SETTINGS_OPTION_PREFIX . $setting );
+ } else {
+ // On single sites just return the default setting.
+ return Defaults::get_default_setting( $setting );
+ }
+ } else {
+ $value = get_option( self::SETTINGS_OPTION_PREFIX . $setting );
+ }
+
+ if ( false === $value ) { // No default value is set.
+ $value = Defaults::get_default_setting( $setting );
+ if ( self::is_network_setting( $setting ) ) {
+ update_site_option( self::SETTINGS_OPTION_PREFIX . $setting, $value );
+ } else {
+ // We set one so that it gets autoloaded.
+ update_option( self::SETTINGS_OPTION_PREFIX . $setting, $value, true );
+ }
+ }
+
+ if ( is_numeric( $value ) ) {
+ $value = (int) $value;
+ }
+ $default_array_value = null;
+ switch ( $setting ) {
+ case 'post_types_blacklist':
+ $default_array_value = Defaults::$blacklisted_post_types;
+ break;
+ case 'taxonomies_blacklist':
+ $default_array_value = Defaults::$blacklisted_taxonomies;
+ break;
+ case 'post_meta_whitelist':
+ $default_array_value = Defaults::get_post_meta_whitelist();
+ break;
+ case 'comment_meta_whitelist':
+ $default_array_value = Defaults::get_comment_meta_whitelist();
+ break;
+ case 'known_importers':
+ $default_array_value = Defaults::get_known_importers();
+ break;
+ }
+
+ if ( $default_array_value ) {
+ if ( is_array( $value ) ) {
+ $value = array_unique( array_merge( $value, $default_array_value ) );
+ } else {
+ $value = $default_array_value;
+ }
+ }
+
+ return $value;
+ }
+
+ /**
+ * Change multiple settings in the same time.
+ *
+ * @access public
+ * @static
+ *
+ * @param array $new_settings The new settings.
+ */
+ public static function update_settings( $new_settings ) {
+ $validated_settings = array_intersect_key( $new_settings, self::$valid_settings );
+ foreach ( $validated_settings as $setting => $value ) {
+
+ if ( self::is_network_setting( $setting ) ) {
+ if ( is_multisite() && is_main_site() ) {
+ update_site_option( self::SETTINGS_OPTION_PREFIX . $setting, $value );
+ }
+ } else {
+ update_option( self::SETTINGS_OPTION_PREFIX . $setting, $value, true );
+ }
+
+ // If we set the disabled option to true, clear the queues.
+ if ( ( 'disable' === $setting || 'network_disable' === $setting ) && (bool) $value ) {
+ $listener = Listener::get_instance();
+ $listener->get_sync_queue()->reset();
+ $listener->get_full_sync_queue()->reset();
+ }
+ }
+ }
+
+ /**
+ * Whether the specified setting is a network setting.
+ *
+ * @access public
+ * @static
+ *
+ * @param string $setting Setting name.
+ * @return boolean Whether the setting is a network setting.
+ */
+ public static function is_network_setting( $setting ) {
+ return strpos( $setting, 'network_' ) === 0;
+ }
+
+ /**
+ * Returns escaped SQL for blacklisted post types.
+ * Can be injected directly into a WHERE clause.
+ *
+ * @access public
+ * @static
+ *
+ * @return string SQL WHERE clause.
+ */
+ public static function get_blacklisted_post_types_sql() {
+ return 'post_type NOT IN (\'' . join( '\', \'', array_map( 'esc_sql', self::get_setting( 'post_types_blacklist' ) ) ) . '\')';
+ }
+
+ /**
+ * Returns escaped SQL for blacklisted taxonomies.
+ * Can be injected directly into a WHERE clause.
+ *
+ * @access public
+ * @static
+ *
+ * @return string SQL WHERE clause.
+ */
+ public static function get_blacklisted_taxonomies_sql() {
+ return "taxonomy NOT IN ('" . join( "', '", array_map( 'esc_sql', self::get_setting( 'taxonomies_blacklist' ) ) ) . "')";
+ }
+
+ /**
+ * Returns escaped SQL for blacklisted post meta.
+ * Can be injected directly into a WHERE clause.
+ *
+ * @access public
+ * @static
+ *
+ * @return string SQL WHERE clause.
+ */
+ public static function get_whitelisted_post_meta_sql() {
+ return 'meta_key IN (\'' . join( '\', \'', array_map( 'esc_sql', self::get_setting( 'post_meta_whitelist' ) ) ) . '\')';
+ }
+
+ /**
+ * Returns escaped SQL for blacklisted comment meta.
+ * Can be injected directly into a WHERE clause.
+ *
+ * @access public
+ * @static
+ *
+ * @return string SQL WHERE clause.
+ */
+ public static function get_whitelisted_comment_meta_sql() {
+ return 'meta_key IN (\'' . join( '\', \'', array_map( 'esc_sql', self::get_setting( 'comment_meta_whitelist' ) ) ) . '\')';
+ }
+
+ /**
+ * Returns escaped SQL for comments, excluding any spam comments.
+ * Can be injected directly into a WHERE clause.
+ *
+ * @access public
+ * @static
+ *
+ * @return string SQL WHERE clause.
+ */
+ public static function get_comments_filter_sql() {
+ return "comment_approved <> 'spam'";
+ }
+
+ /**
+ * Delete any settings options and clean up the current settings state.
+ *
+ * @access public
+ * @static
+ */
+ public static function reset_data() {
+ $valid_settings = self::$valid_settings;
+ foreach ( $valid_settings as $option => $value ) {
+ delete_option( self::SETTINGS_OPTION_PREFIX . $option );
+ }
+ self::set_importing( null );
+ self::set_doing_cron( null );
+ self::set_is_syncing( null );
+ self::set_is_sending( null );
+ }
+
+ /**
+ * Set the importing state.
+ *
+ * @access public
+ * @static
+ *
+ * @param boolean $is_importing Whether WordPress is currently importing.
+ */
+ public static function set_importing( $is_importing ) {
+ // Set to NULL to revert to WP_IMPORTING, the standard behavior.
+ self::$is_importing = $is_importing;
+ }
+
+ /**
+ * Whether WordPress is currently importing.
+ *
+ * @access public
+ * @static
+ *
+ * @return boolean Whether WordPress is currently importing.
+ */
+ public static function is_importing() {
+ if ( ! is_null( self::$is_importing ) ) {
+ return self::$is_importing;
+ }
+
+ return defined( 'WP_IMPORTING' ) && WP_IMPORTING;
+ }
+
+ /**
+ * Whether sync is enabled.
+ *
+ * @access public
+ * @static
+ *
+ * @return boolean Whether sync is enabled.
+ */
+ public static function is_sync_enabled() {
+ return ! ( self::get_setting( 'disable' ) || self::get_setting( 'network_disable' ) );
+ }
+
+ /**
+ * Set the WP cron state.
+ *
+ * @access public
+ * @static
+ *
+ * @param boolean $is_doing_cron Whether WordPress is currently doing WP cron.
+ */
+ public static function set_doing_cron( $is_doing_cron ) {
+ // Set to NULL to revert to WP_IMPORTING, the standard behavior.
+ self::$is_doing_cron = $is_doing_cron;
+ }
+
+ /**
+ * Whether WordPress is currently doing WP cron.
+ *
+ * @access public
+ * @static
+ *
+ * @return boolean Whether WordPress is currently doing WP cron.
+ */
+ public static function is_doing_cron() {
+ if ( ! is_null( self::$is_doing_cron ) ) {
+ return self::$is_doing_cron;
+ }
+
+ return defined( 'DOING_CRON' ) && DOING_CRON;
+ }
+
+ /**
+ * Whether we are currently syncing.
+ *
+ * @access public
+ * @static
+ *
+ * @return boolean Whether we are currently syncing.
+ */
+ public static function is_syncing() {
+ return (bool) self::$is_syncing || ( defined( 'REST_API_REQUEST' ) && REST_API_REQUEST );
+ }
+
+ /**
+ * Set the syncing state.
+ *
+ * @access public
+ * @static
+ *
+ * @param boolean $is_syncing Whether we are currently syncing.
+ */
+ public static function set_is_syncing( $is_syncing ) {
+ self::$is_syncing = $is_syncing;
+ }
+
+ /**
+ * Whether we are currently sending sync items.
+ *
+ * @access public
+ * @static
+ *
+ * @return boolean Whether we are currently sending sync items.
+ */
+ public static function is_sending() {
+ return (bool) self::$is_sending;
+ }
+
+ /**
+ * Set the sending state.
+ *
+ * @access public
+ * @static
+ *
+ * @param boolean $is_sending Whether we are currently sending sync items.
+ */
+ public static function set_is_sending( $is_sending ) {
+ self::$is_sending = $is_sending;
+ }
+
+ /**
+ * Whether should send from the queue
+ *
+ * @access public
+ * @static
+ *
+ * @param string $queue_id The queue identifier.
+ *
+ * @return boolean Whether sync is enabled.
+ */
+ public static function is_sender_enabled( $queue_id ) {
+ return (bool) self::get_setting( $queue_id . '_sender_enabled' );
+ }
+
+}
diff --git a/vendor/automattic/jetpack-sync/src/class-simple-codec.php b/vendor/automattic/jetpack-sync/src/class-simple-codec.php
new file mode 100644
index 0000000000000..613323fdada2f
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/class-simple-codec.php
@@ -0,0 +1,63 @@
+json_serialize( $object ) );
+ }
+
+ /**
+ * Encode a sync object.
+ *
+ * @access public
+ *
+ * @param string $input Encoded sync object to decode.
+ * @return mixed Decoded sync object.
+ */
+ public function decode( $input ) {
+ // This is intentionally using base64_decode().
+ // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
+ return $this->json_unserialize( base64_decode( $input ) );
+ }
+
+}
diff --git a/vendor/automattic/jetpack-sync/src/class-users.php b/vendor/automattic/jetpack-sync/src/class-users.php
new file mode 100644
index 0000000000000..0477fcc3f8685
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/class-users.php
@@ -0,0 +1,151 @@
+is_active() ) {
+ // Kick off synchronization of user role when it changes.
+ add_action( 'set_user_role', array( __CLASS__, 'user_role_change' ) );
+ }
+ }
+
+ /**
+ * Synchronize connected user role changes.
+ *
+ * @access public
+ * @static
+ *
+ * @param int $user_id ID of the user.
+ */
+ public static function user_role_change( $user_id ) {
+ $connection = new Jetpack_Connection();
+ if ( $connection->is_user_connected( $user_id ) ) {
+ self::update_role_on_com( $user_id );
+ // Try to choose a new master if we're demoting the current one.
+ self::maybe_demote_master_user( $user_id );
+ }
+ }
+
+ /**
+ * Retrieve the role of a user by their ID.
+ *
+ * @access public
+ * @static
+ *
+ * @param int $user_id ID of the user.
+ * @return string Role of the user.
+ */
+ public static function get_role( $user_id ) {
+ if ( isset( self::$user_roles[ $user_id ] ) ) {
+ return self::$user_roles[ $user_id ];
+ }
+
+ $current_user_id = get_current_user_id();
+ wp_set_current_user( $user_id );
+ $roles = new Roles();
+ $role = $roles->translate_current_user_to_role();
+ wp_set_current_user( $current_user_id );
+ self::$user_roles[ $user_id ] = $role;
+
+ return $role;
+ }
+
+ /**
+ * Retrieve the signed role of a user by their ID.
+ *
+ * @access public
+ * @static
+ *
+ * @param int $user_id ID of the user.
+ * @return string Signed role of the user.
+ */
+ public static function get_signed_role( $user_id ) {
+ $connection = new Jetpack_Connection();
+ return $connection->sign_role( self::get_role( $user_id ), $user_id );
+ }
+
+ /**
+ * Retrieve the signed role and update it in WP.com for that user.
+ *
+ * @access public
+ * @static
+ *
+ * @param int $user_id ID of the user.
+ */
+ public static function update_role_on_com( $user_id ) {
+ $signed_role = self::get_signed_role( $user_id );
+ XMLRPC_Async_Call::add_call( 'jetpack.updateRole', get_current_user_id(), $user_id, $signed_role );
+ }
+
+ /**
+ * Choose a new master user if we're demoting the current one.
+ *
+ * @access public
+ * @static
+ * @todo Disconnect if there is no user with enough capabilities to be the master user.
+ * @uses \WP_User_Query
+ *
+ * @param int $user_id ID of the user.
+ */
+ public static function maybe_demote_master_user( $user_id ) {
+ $master_user_id = (int) \Jetpack_Options::get_option( 'master_user' );
+ $role = self::get_role( $user_id );
+ if ( $user_id === $master_user_id && 'administrator' !== $role ) {
+ $query = new \WP_User_Query(
+ array(
+ 'fields' => array( 'id' ),
+ 'role' => 'administrator',
+ 'orderby' => 'id',
+ 'exclude' => array( $master_user_id ),
+ )
+ );
+ $new_master = false;
+ $connection = new Jetpack_Connection();
+ foreach ( $query->results as $result ) {
+ $found_user_id = absint( $result->id );
+ if ( $found_user_id && $connection->is_user_connected( $found_user_id ) ) {
+ $new_master = $found_user_id;
+ break;
+ }
+ }
+
+ if ( $new_master ) {
+ \Jetpack_Options::update_option( 'master_user', $new_master );
+ }
+ // TODO: else disconnect..?
+ }
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/class-utils.php b/vendor/automattic/jetpack-sync/src/class-utils.php
new file mode 100644
index 0000000000000..23f24e95d36a2
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/class-utils.php
@@ -0,0 +1,65 @@
+value;
+ }
+
+ /**
+ * Get the ID of a sync item.
+ *
+ * @access private
+ * @static
+ *
+ * @param array $item Sync item.
+ * @return int Sync item ID.
+ */
+ private static function get_item_id( $item ) {
+ return $item->id;
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/interface-codec.php b/vendor/automattic/jetpack-sync/src/interface-codec.php
new file mode 100644
index 0000000000000..7653f26de7e10
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/interface-codec.php
@@ -0,0 +1,44 @@
+post_parent && 0 !== $attachment_after->post_parent ) {
+ /**
+ * Fires when an existing attachment is added to a post for the first time
+ *
+ * @since 6.6.0
+ *
+ * @param int $attachment_id Attachment ID.
+ * @param \WP_Post $attachment_after Attachment post object after the update.
+ */
+ do_action( 'jetpack_sync_save_attach_attachment', $attachment_id, $attachment_after );
+ } else {
+ /**
+ * Fires when the client needs to sync an updated attachment
+ *
+ * @since 4.9.0
+ *
+ * @param int $attachment_id Attachment ID.
+ * @param \WP_Post $attachment_after Attachment post object after the update.
+ *
+ * Previously this action was synced using jetpack_sync_save_add_attachment action.
+ */
+ do_action( 'jetpack_sync_save_update_attachment', $attachment_id, $attachment_after );
+ }
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/modules/class-callables.php b/vendor/automattic/jetpack-sync/src/modules/class-callables.php
new file mode 100644
index 0000000000000..e5f2f01337722
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/modules/class-callables.php
@@ -0,0 +1,571 @@
+ 'home_url',
+ 'siteurl' => 'site_url',
+ );
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'functions';
+ }
+
+ /**
+ * Set module defaults.
+ * Define the callable whitelist based on whether this is a single site or a multisite installation.
+ *
+ * @access public
+ */
+ public function set_defaults() {
+ if ( is_multisite() ) {
+ $this->callable_whitelist = array_merge( Defaults::get_callable_whitelist(), Defaults::get_multisite_callable_whitelist() );
+ } else {
+ $this->callable_whitelist = Defaults::get_callable_whitelist();
+ }
+ $this->force_send_callables_on_next_tick = false; // Resets here as well mostly for tests.
+ }
+
+ /**
+ * Initialize callables action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ add_action( 'jetpack_sync_callable', $callable, 10, 2 );
+ add_action( 'current_screen', array( $this, 'set_plugin_action_links' ), 9999 ); // Should happen very late.
+
+ foreach ( self::ALWAYS_SEND_UPDATES_TO_THESE_OPTIONS as $option ) {
+ add_action( "update_option_{$option}", array( $this, 'unlock_sync_callable' ) );
+ add_action( "delete_option_{$option}", array( $this, 'unlock_sync_callable' ) );
+ }
+
+ foreach ( self::ALWAYS_SEND_UPDATES_TO_THESE_OPTIONS_NEXT_TICK as $option ) {
+ add_action( "update_option_{$option}", array( $this, 'unlock_sync_callable_next_tick' ) );
+ add_action( "delete_option_{$option}", array( $this, 'unlock_sync_callable_next_tick' ) );
+ }
+
+ // Provide a hook so that hosts can send changes to certain callables right away.
+ // Especially useful when a host uses constants to change home and siteurl.
+ add_action( 'jetpack_sync_unlock_sync_callable', array( $this, 'unlock_sync_callable' ) );
+
+ // get_plugins and wp_version
+ // gets fired when new code gets installed, updates etc.
+ add_action( 'upgrader_process_complete', array( $this, 'unlock_plugin_action_link_and_callables' ) );
+ add_action( 'update_option_active_plugins', array( $this, 'unlock_plugin_action_link_and_callables' ) );
+ }
+
+ /**
+ * Initialize callables action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ add_action( 'jetpack_full_sync_callables', $callable );
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ add_action( 'jetpack_sync_before_send_queue_sync', array( $this, 'maybe_sync_callables' ) );
+
+ // Full sync.
+ add_filter( 'jetpack_sync_before_send_jetpack_full_sync_callables', array( $this, 'expand_callables' ) );
+ }
+
+ /**
+ * Perform module cleanup.
+ * Deletes any transients and options that this module uses.
+ * Usually triggered when uninstalling the plugin.
+ *
+ * @access public
+ */
+ public function reset_data() {
+ delete_option( self::CALLABLES_CHECKSUM_OPTION_NAME );
+ delete_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME );
+
+ $url_callables = array( 'home_url', 'site_url', 'main_network_site_url' );
+ foreach ( $url_callables as $callable ) {
+ delete_option( Functions::HTTPS_CHECK_OPTION_PREFIX . $callable );
+ }
+ }
+
+ /**
+ * Set the callable whitelist.
+ *
+ * @access public
+ *
+ * @param array $callables The new callables whitelist.
+ */
+ public function set_callable_whitelist( $callables ) {
+ $this->callable_whitelist = $callables;
+ }
+
+ /**
+ * Get the callable whitelist.
+ *
+ * @access public
+ *
+ * @return array The callables whitelist.
+ */
+ public function get_callable_whitelist() {
+ return $this->callable_whitelist;
+ }
+
+ /**
+ * Retrieve all callables as per the current callables whitelist.
+ *
+ * @access public
+ *
+ * @return array All callables.
+ */
+ public function get_all_callables() {
+ // get_all_callables should run as the master user always.
+ $current_user_id = get_current_user_id();
+ wp_set_current_user( \Jetpack_Options::get_option( 'master_user' ) );
+ $callables = array_combine(
+ array_keys( $this->get_callable_whitelist() ),
+ array_map( array( $this, 'get_callable' ), array_values( $this->get_callable_whitelist() ) )
+ );
+ wp_set_current_user( $current_user_id );
+ return $callables;
+ }
+
+ /**
+ * Invoke a particular callable.
+ * Used as a wrapper to standartize invocation.
+ *
+ * @access private
+ *
+ * @param callable $callable Callable to invoke.
+ * @return mixed Return value of the callable.
+ */
+ private function get_callable( $callable ) {
+ return call_user_func( $callable );
+ }
+
+ /**
+ * Enqueue the callable actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ /**
+ * Tells the client to sync all callables to the server
+ *
+ * @since 4.2.0
+ *
+ * @param boolean Whether to expand callables (should always be true)
+ */
+ do_action( 'jetpack_full_sync_callables', true );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 1, true );
+ }
+
+ /**
+ * Send the callable actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $send_until The timestamp until the current request can send.
+ * @param array $status This Module Full Sync Status.
+ *
+ * @return array This Module Full Sync Status.
+ */
+ public function send_full_sync_actions( $config, $send_until, $status ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ // we call this instead of do_action when sending immediately.
+ $this->send_action( 'jetpack_full_sync_callables', array( true ) );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 'finished' => true );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return array Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return 1;
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_callables' );
+ }
+
+ /**
+ * Unlock callables so they would be available for syncing again.
+ *
+ * @access public
+ */
+ public function unlock_sync_callable() {
+ delete_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME );
+ }
+
+ /**
+ * Unlock callables on the next tick.
+ * Sometime the true callable values are only present on the next tick.
+ * When switching themes for example.
+ *
+ * @access public
+ */
+ public function unlock_sync_callable_next_tick() {
+ $this->force_send_callables_on_next_tick = true;
+ }
+
+ /**
+ * Unlock callables and plugin action links.
+ *
+ * @access public
+ */
+ public function unlock_plugin_action_link_and_callables() {
+ delete_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME );
+ delete_transient( 'jetpack_plugin_api_action_links_refresh' );
+ add_filter( 'jetpack_check_and_send_callables', '__return_true' );
+ }
+
+ /**
+ * Parse and store the plugin action links if on the plugins page.
+ *
+ * @uses \DOMDocument
+ * @uses libxml_use_internal_errors
+ * @uses mb_convert_encoding
+ *
+ * @access public
+ */
+ public function set_plugin_action_links() {
+ if (
+ ! class_exists( '\DOMDocument' ) ||
+ ! function_exists( 'libxml_use_internal_errors' ) ||
+ ! function_exists( 'mb_convert_encoding' )
+ ) {
+ return;
+ }
+
+ $current_screeen = get_current_screen();
+
+ $plugins_action_links = array();
+ // Is the transient lock in place?
+ $plugins_lock = get_transient( 'jetpack_plugin_api_action_links_refresh', false );
+ if ( ! empty( $plugins_lock ) && ( isset( $current_screeen->id ) && 'plugins' !== $current_screeen->id ) ) {
+ return;
+ }
+ $plugins = array_keys( Functions::get_plugins() );
+ foreach ( $plugins as $plugin_file ) {
+ /**
+ * Plugins often like to unset things but things break if they are not able to.
+ */
+ $action_links = array(
+ 'deactivate' => '',
+ 'activate' => '',
+ 'details' => '',
+ 'delete' => '',
+ 'edit' => '',
+ );
+ /** This filter is documented in src/wp-admin/includes/class-wp-plugins-list-table.php */
+ $action_links = apply_filters( 'plugin_action_links', $action_links, $plugin_file, null, 'all' );
+ /** This filter is documented in src/wp-admin/includes/class-wp-plugins-list-table.php */
+ $action_links = apply_filters( "plugin_action_links_{$plugin_file}", $action_links, $plugin_file, null, 'all' );
+ // Verify $action_links is still an array to resolve warnings from filters not returning an array.
+ if ( is_array( $action_links ) ) {
+ $action_links = array_filter( $action_links );
+ } else {
+ $action_links = array();
+ }
+ $formatted_action_links = null;
+ if ( ! empty( $action_links ) && count( $action_links ) > 0 ) {
+ $dom_doc = new \DOMDocument();
+ foreach ( $action_links as $action_link ) {
+ // The @ is not enough to suppress errors when dealing with libxml,
+ // we have to tell it directly how we want to handle errors.
+ libxml_use_internal_errors( true );
+ $dom_doc->loadHTML( mb_convert_encoding( $action_link, 'HTML-ENTITIES', 'UTF-8' ) );
+ libxml_use_internal_errors( false );
+
+ $link_elements = $dom_doc->getElementsByTagName( 'a' );
+ if ( 0 === $link_elements->length ) {
+ continue;
+ }
+
+ $link_element = $link_elements->item( 0 );
+ // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
+ if ( $link_element->hasAttribute( 'href' ) && $link_element->nodeValue ) {
+ $link_url = trim( $link_element->getAttribute( 'href' ) );
+
+ // Add the full admin path to the url if the plugin did not provide it.
+ $link_url_scheme = wp_parse_url( $link_url, PHP_URL_SCHEME );
+ if ( empty( $link_url_scheme ) ) {
+ $link_url = admin_url( $link_url );
+ }
+
+ // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
+ $formatted_action_links[ $link_element->nodeValue ] = $link_url;
+ }
+ }
+ }
+ if ( $formatted_action_links ) {
+ $plugins_action_links[ $plugin_file ] = $formatted_action_links;
+ }
+ }
+ // Cache things for a long time.
+ set_transient( 'jetpack_plugin_api_action_links_refresh', time(), DAY_IN_SECONDS );
+ update_option( 'jetpack_plugin_api_action_links', $plugins_action_links );
+ }
+
+ /**
+ * Whether a certain callable should be sent.
+ *
+ * @access public
+ *
+ * @param array $callable_checksums Callable checksums.
+ * @param string $name Name of the callable.
+ * @param string $checksum A checksum of the callable.
+ * @return boolean Whether to send the callable.
+ */
+ public function should_send_callable( $callable_checksums, $name, $checksum ) {
+ $idc_override_callables = array(
+ 'main_network_site',
+ 'home_url',
+ 'site_url',
+ );
+ if ( in_array( $name, $idc_override_callables, true ) && \Jetpack_Options::get_option( 'migrate_for_idc' ) ) {
+ return true;
+ }
+
+ return ! $this->still_valid_checksum( $callable_checksums, $name, $checksum );
+ }
+
+ /**
+ * Sync the callables if we're supposed to.
+ *
+ * @access public
+ */
+ public function maybe_sync_callables() {
+ $callables = $this->get_all_callables();
+ if ( ! apply_filters( 'jetpack_check_and_send_callables', false ) ) {
+ if ( ! is_admin() ) {
+ // If we're not an admin and we're not doing cron and this isn't WP_CLI, don't sync anything.
+ if ( ! Settings::is_doing_cron() && ! Jetpack_Constants::get_constant( 'WP_CLI' ) ) {
+ return;
+ }
+ // If we're not an admin and we are doing cron, sync the Callables that are always supposed to sync ( See https://github.com/Automattic/jetpack/issues/12924 ).
+ $callables = $this->get_always_sent_callables();
+ }
+ if ( get_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME ) ) {
+ if ( $this->force_send_callables_on_next_tick ) {
+ $this->unlock_sync_callable();
+ }
+ return;
+ }
+ }
+
+ if ( empty( $callables ) ) {
+ return;
+ }
+ // No need to set the transiant we are trying to remove it anyways.
+ if ( ! $this->force_send_callables_on_next_tick ) {
+ set_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME, microtime( true ), Defaults::$default_sync_callables_wait_time );
+ }
+
+ $callable_checksums = (array) \Jetpack_Options::get_raw_option( self::CALLABLES_CHECKSUM_OPTION_NAME, array() );
+ $has_changed = false;
+ // Only send the callables that have changed.
+ foreach ( $callables as $name => $value ) {
+ $checksum = $this->get_check_sum( $value );
+
+ // Explicitly not using Identical comparison as get_option returns a string.
+ if ( ! is_null( $value ) && $this->should_send_callable( $callable_checksums, $name, $checksum ) ) {
+
+ // Only send callable if the non sorted checksum also does not match.
+ if ( $this->should_send_callable( $callable_checksums, $name, $this->get_check_sum( $value, false ) ) ) {
+
+ /**
+ * Tells the client to sync a callable (aka function) to the server
+ *
+ * @param string The name of the callable
+ * @param mixed The value of the callable
+ *
+ * @since 4.2.0
+ */
+ do_action( 'jetpack_sync_callable', $name, $value );
+ }
+
+ $callable_checksums[ $name ] = $checksum;
+ $has_changed = true;
+ } else {
+ $callable_checksums[ $name ] = $checksum;
+ }
+ }
+ if ( $has_changed ) {
+ \Jetpack_Options::update_raw_option( self::CALLABLES_CHECKSUM_OPTION_NAME, $callable_checksums );
+ }
+
+ if ( $this->force_send_callables_on_next_tick ) {
+ $this->unlock_sync_callable();
+ }
+ }
+
+ /**
+ * Get the callables that should always be sent, e.g. on cron.
+ *
+ * @return array Callables that should always be sent
+ */
+ protected function get_always_sent_callables() {
+ $callables = $this->get_all_callables();
+ $cron_callables = array();
+ foreach ( self::ALWAYS_SEND_UPDATES_TO_THESE_OPTIONS as $option_name ) {
+ if ( array_key_exists( $option_name, $callables ) ) {
+ $cron_callables[ $option_name ] = $callables[ $option_name ];
+ continue;
+ }
+
+ // Check for the Callable name/key for the option, if different from option name.
+ if ( array_key_exists( $option_name, self::OPTION_NAMES_TO_CALLABLE_NAMES ) ) {
+ $callable_name = self::OPTION_NAMES_TO_CALLABLE_NAMES[ $option_name ];
+ if ( array_key_exists( $callable_name, $callables ) ) {
+ $cron_callables[ $callable_name ] = $callables[ $callable_name ];
+ }
+ }
+ }
+ return $cron_callables;
+ }
+
+ /**
+ * Expand the callables within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array $args The hook parameters.
+ */
+ public function expand_callables( $args ) {
+ if ( $args[0] ) {
+ $callables = $this->get_all_callables();
+ $callables_checksums = array();
+ foreach ( $callables as $name => $value ) {
+ $callables_checksums[ $name ] = $this->get_check_sum( $value );
+ }
+ \Jetpack_Options::update_raw_option( self::CALLABLES_CHECKSUM_OPTION_NAME, $callables_checksums );
+ return $callables;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Return Total number of objects.
+ *
+ * @param array $config Full Sync config.
+ *
+ * @return int total
+ */
+ public function total( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return count( $this->get_callable_whitelist() );
+ }
+
+}
diff --git a/vendor/automattic/jetpack-sync/src/modules/class-comments.php b/vendor/automattic/jetpack-sync/src/modules/class-comments.php
new file mode 100644
index 0000000000000..8678f7456a5a9
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/modules/class-comments.php
@@ -0,0 +1,489 @@
+filter_comment( $comment );
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Initialize comments action listeners.
+ * Also responsible for initializing comment meta listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ add_action( 'wp_insert_comment', $callable, 10, 2 );
+ add_action( 'deleted_comment', $callable );
+ add_action( 'trashed_comment', $callable );
+ add_action( 'spammed_comment', $callable );
+ add_action( 'trashed_post_comments', $callable, 10, 2 );
+ add_action( 'untrash_post_comments', $callable );
+ add_action( 'comment_approved_to_unapproved', $callable );
+ add_action( 'comment_unapproved_to_approved', $callable );
+ add_action( 'jetpack_modified_comment_contents', $callable, 10, 2 );
+ add_action( 'untrashed_comment', $callable, 10, 2 );
+ add_action( 'unspammed_comment', $callable, 10, 2 );
+ add_filter( 'wp_update_comment_data', array( $this, 'handle_comment_contents_modification' ), 10, 3 );
+
+ // comment actions.
+ add_filter( 'jetpack_sync_before_enqueue_wp_insert_comment', array( $this, 'only_allow_white_listed_comment_types' ) );
+ add_filter( 'jetpack_sync_before_enqueue_deleted_comment', array( $this, 'only_allow_white_listed_comment_types' ) );
+ add_filter( 'jetpack_sync_before_enqueue_trashed_comment', array( $this, 'only_allow_white_listed_comment_types' ) );
+ add_filter( 'jetpack_sync_before_enqueue_untrashed_comment', array( $this, 'only_allow_white_listed_comment_types' ) );
+ add_filter( 'jetpack_sync_before_enqueue_spammed_comment', array( $this, 'only_allow_white_listed_comment_types' ) );
+ add_filter( 'jetpack_sync_before_enqueue_unspammed_comment', array( $this, 'only_allow_white_listed_comment_types' ) );
+
+ // comment status transitions.
+ add_filter( 'jetpack_sync_before_enqueue_comment_approved_to_unapproved', array( $this, 'only_allow_white_listed_comment_type_transitions' ) );
+ add_filter( 'jetpack_sync_before_enqueue_comment_unapproved_to_approved', array( $this, 'only_allow_white_listed_comment_type_transitions' ) );
+
+ // Post Actions.
+ add_filter( 'jetpack_sync_before_enqueue_trashed_post_comments', array( $this, 'filter_blacklisted_post_types' ) );
+ add_filter( 'jetpack_sync_before_enqueue_untrash_post_comments', array( $this, 'filter_blacklisted_post_types' ) );
+
+ /**
+ * Even though it's messy, we implement these hooks because
+ * the edit_comment hook doesn't include the data
+ * so this saves us a DB read for every comment event.
+ */
+ foreach ( $this->get_whitelisted_comment_types() as $comment_type ) {
+ foreach ( array( 'unapproved', 'approved' ) as $comment_status ) {
+ $comment_action_name = "comment_{$comment_status}_{$comment_type}";
+ add_action( $comment_action_name, $callable, 10, 2 );
+ }
+ }
+
+ // Listen for meta changes.
+ $this->init_listeners_for_meta_type( 'comment', $callable );
+ $this->init_meta_whitelist_handler( 'comment', array( $this, 'filter_meta' ) );
+ }
+
+ /**
+ * Handler for any comment content updates.
+ *
+ * @access public
+ *
+ * @param array $new_comment The new, processed comment data.
+ * @param array $old_comment The old, unslashed comment data.
+ * @param array $new_comment_with_slashes The new, raw comment data.
+ * @return array The new, processed comment data.
+ */
+ public function handle_comment_contents_modification( $new_comment, $old_comment, $new_comment_with_slashes ) {
+ $changes = array();
+ $content_fields = array(
+ 'comment_author',
+ 'comment_author_email',
+ 'comment_author_url',
+ 'comment_content',
+ );
+ foreach ( $content_fields as $field ) {
+ if ( $new_comment_with_slashes[ $field ] !== $old_comment[ $field ] ) {
+ $changes[ $field ] = array( $new_comment[ $field ], $old_comment[ $field ] );
+ }
+ }
+
+ if ( ! empty( $changes ) ) {
+ /**
+ * Signals to the sync listener that this comment's contents were modified and a sync action
+ * reflecting the change(s) to the content should be sent
+ *
+ * @since 4.9.0
+ *
+ * @param int $new_comment['comment_ID'] ID of comment whose content was modified
+ * @param mixed $changes Array of changed comment fields with before and after values
+ */
+ do_action( 'jetpack_modified_comment_contents', $new_comment['comment_ID'], $changes );
+ }
+ return $new_comment;
+ }
+
+ /**
+ * Initialize comments action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ add_action( 'jetpack_full_sync_comments', $callable ); // Also send comments meta.
+ }
+
+ /**
+ * Gets a filtered list of comment types that sync can hook into.
+ *
+ * @access public
+ *
+ * @return array Defaults to [ '', 'trackback', 'pingback' ].
+ */
+ public function get_whitelisted_comment_types() {
+ /**
+ * Comment types present in this list will sync their status changes to WordPress.com.
+ *
+ * @since 7.6.0
+ *
+ * @param array A list of comment types.
+ */
+ return apply_filters(
+ 'jetpack_sync_whitelisted_comment_types',
+ array( '', 'comment', 'trackback', 'pingback', 'review' )
+ );
+ }
+
+ /**
+ * Prevents any comment types that are not in the whitelist from being enqueued and sent to WordPress.com.
+ *
+ * @param array $args Arguments passed to wp_insert_comment, deleted_comment, spammed_comment, etc.
+ *
+ * @return bool or array $args Arguments passed to wp_insert_comment, deleted_comment, spammed_comment, etc.
+ */
+ public function only_allow_white_listed_comment_types( $args ) {
+ $comment = false;
+
+ if ( isset( $args[1] ) ) {
+ // comment object is available.
+ $comment = $args[1];
+ } elseif ( is_numeric( $args[0] ) ) {
+ // comment_id is available.
+ $comment = get_comment( $args[0] );
+ }
+
+ if ( false !== $comment && ! in_array( $comment->comment_type, $this->get_whitelisted_comment_types(), true ) ) {
+ return false;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Filter all blacklisted post types.
+ *
+ * @param array $args Hook arguments.
+ * @return array|false Hook arguments, or false if the post type is a blacklisted one.
+ */
+ public function filter_blacklisted_post_types( $args ) {
+ $post_id = $args[0];
+ $posts_module = Modules::get_module( 'posts' );
+
+ if ( false !== $posts_module && ! $posts_module->is_post_type_allowed( $post_id ) ) {
+ return false;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Prevents any comment types that are not in the whitelist from being enqueued and sent to WordPress.com.
+ *
+ * @param array $args Arguments passed to wp_{old_status}_to_{new_status}.
+ *
+ * @return bool or array $args Arguments passed to wp_{old_status}_to_{new_status}
+ */
+ public function only_allow_white_listed_comment_type_transitions( $args ) {
+ $comment = $args[0];
+
+ if ( ! in_array( $comment->comment_type, $this->get_whitelisted_comment_types(), true ) ) {
+ return false;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Whether a comment type is allowed.
+ * A comment type is allowed if it's present in the comment type whitelist.
+ *
+ * @param int $comment_id ID of the comment.
+ * @return boolean Whether the comment type is allowed.
+ */
+ public function is_comment_type_allowed( $comment_id ) {
+ $comment = get_comment( $comment_id );
+
+ if ( isset( $comment->comment_type ) ) {
+ return in_array( $comment->comment_type, $this->get_whitelisted_comment_types(), true );
+ }
+ return false;
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ add_filter( 'jetpack_sync_before_send_wp_insert_comment', array( $this, 'expand_wp_insert_comment' ) );
+
+ foreach ( $this->get_whitelisted_comment_types() as $comment_type ) {
+ foreach ( array( 'unapproved', 'approved' ) as $comment_status ) {
+ $comment_action_name = "comment_{$comment_status}_{$comment_type}";
+ add_filter(
+ 'jetpack_sync_before_send_' . $comment_action_name,
+ array(
+ $this,
+ 'expand_wp_insert_comment',
+ )
+ );
+ }
+ }
+
+ // Full sync.
+ add_filter( 'jetpack_sync_before_send_jetpack_full_sync_comments', array( $this, 'expand_comment_ids' ) );
+ }
+
+ /**
+ * Enqueue the comments actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
+ global $wpdb;
+ return $this->enqueue_all_ids_as_action( 'jetpack_full_sync_comments', $wpdb->comments, 'comment_ID', $this->get_where_sql( $config ), $max_items_to_enqueue, $state );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return int Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) {
+ global $wpdb;
+
+ $query = "SELECT count(*) FROM $wpdb->comments";
+
+ $where_sql = $this->get_where_sql( $config );
+ if ( $where_sql ) {
+ $query .= ' WHERE ' . $where_sql;
+ }
+
+ // TODO: Call $wpdb->prepare on the following query.
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $count = $wpdb->get_var( $query );
+
+ return (int) ceil( $count / self::ARRAY_CHUNK_SIZE );
+ }
+
+ /**
+ * Retrieve the WHERE SQL clause based on the module config.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return string WHERE SQL clause, or `null` if no comments are specified in the module config.
+ */
+ public function get_where_sql( $config ) {
+ if ( is_array( $config ) ) {
+ return 'comment_ID IN (' . implode( ',', array_map( 'intval', $config ) ) . ')';
+ }
+
+ return '1=1';
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_comments' );
+ }
+
+ /**
+ * Count all the actions that are going to be sent.
+ *
+ * @access public
+ *
+ * @param array $action_names Names of all the actions that will be sent.
+ * @return int Number of actions.
+ */
+ public function count_full_sync_actions( $action_names ) {
+ return $this->count_actions( $action_names, array( 'jetpack_full_sync_comments' ) );
+ }
+
+ /**
+ * Expand the comment status change before the data is serialized and sent to the server.
+ *
+ * @access public
+ * @todo This is not used currently - let's implement it.
+ *
+ * @param array $args The hook parameters.
+ * @return array The expanded hook parameters.
+ */
+ public function expand_wp_comment_status_change( $args ) {
+ return array( $args[0], $this->filter_comment( $args[1] ) );
+ }
+
+ /**
+ * Expand the comment creation before the data is serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array The expanded hook parameters.
+ */
+ public function expand_wp_insert_comment( $args ) {
+ return array( $args[0], $this->filter_comment( $args[1] ) );
+ }
+
+ /**
+ * Filter a comment object to the fields we need.
+ *
+ * @access public
+ *
+ * @param \WP_Comment $comment The unfiltered comment object.
+ * @return \WP_Comment Filtered comment object.
+ */
+ public function filter_comment( $comment ) {
+ /**
+ * Filters whether to prevent sending comment data to .com
+ *
+ * Passing true to the filter will prevent the comment data from being sent
+ * to the WordPress.com.
+ * Instead we pass data that will still enable us to do a checksum against the
+ * Jetpacks data but will prevent us from displaying the data on in the API as well as
+ * other services.
+ *
+ * @since 4.2.0
+ *
+ * @param boolean false prevent post data from bing synced to WordPress.com
+ * @param mixed $comment WP_COMMENT object
+ */
+ if ( apply_filters( 'jetpack_sync_prevent_sending_comment_data', false, $comment ) ) {
+ $blocked_comment = new \stdClass();
+ $blocked_comment->comment_ID = $comment->comment_ID;
+ $blocked_comment->comment_date = $comment->comment_date;
+ $blocked_comment->comment_date_gmt = $comment->comment_date_gmt;
+ $blocked_comment->comment_approved = 'jetpack_sync_blocked';
+ return $blocked_comment;
+ }
+
+ return $comment;
+ }
+
+ /**
+ * Whether a certain comment meta key is whitelisted for sync.
+ *
+ * @access public
+ *
+ * @param string $meta_key Comment meta key.
+ * @return boolean Whether the meta key is whitelisted.
+ */
+ public function is_whitelisted_comment_meta( $meta_key ) {
+ return in_array( $meta_key, Settings::get_setting( 'comment_meta_whitelist' ), true );
+ }
+
+ /**
+ * Handler for filtering out non-whitelisted comment meta.
+ *
+ * @access public
+ *
+ * @param array $args Hook args.
+ * @return array|boolean False if not whitelisted, the original hook args otherwise.
+ */
+ public function filter_meta( $args ) {
+ if ( $this->is_comment_type_allowed( $args[1] ) && $this->is_whitelisted_comment_meta( $args[2] ) ) {
+ return $args;
+ }
+
+ return false;
+ }
+
+ /**
+ * Expand the comment IDs to comment objects and meta before being serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array The expanded hook parameters.
+ */
+ public function expand_comment_ids( $args ) {
+ list( $comment_ids, $previous_interval_end ) = $args;
+ $comments = get_comments(
+ array(
+ 'include_unapproved' => true,
+ 'comment__in' => $comment_ids,
+ 'orderby' => 'comment_ID',
+ 'order' => 'DESC',
+ )
+ );
+
+ return array(
+ $comments,
+ $this->get_metadata( $comment_ids, 'comment', Settings::get_setting( 'comment_meta_whitelist' ) ),
+ $previous_interval_end,
+ );
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/modules/class-constants.php b/vendor/automattic/jetpack-sync/src/modules/class-constants.php
new file mode 100644
index 0000000000000..4e0d29902da33
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/modules/class-constants.php
@@ -0,0 +1,285 @@
+constants_whitelist = $constants;
+ }
+
+ /**
+ * Get the constants whitelist.
+ *
+ * @access public
+ *
+ * @return array The constants whitelist.
+ */
+ public function get_constants_whitelist() {
+ return Defaults::get_constants_whitelist();
+ }
+
+ /**
+ * Enqueue the constants actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ *
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ /**
+ * Tells the client to sync all constants to the server
+ *
+ * @param boolean Whether to expand constants (should always be true)
+ *
+ * @since 4.2.0
+ */
+ do_action( 'jetpack_full_sync_constants', true );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 1, true );
+ }
+
+ /**
+ * Send the constants actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $send_until The timestamp until the current request can send.
+ * @param array $state This module Full Sync status.
+ *
+ * @return array This module Full Sync status.
+ */
+ public function send_full_sync_actions( $config, $send_until, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ // we call this instead of do_action when sending immediately.
+ $this->send_action( 'jetpack_full_sync_constants', array( true ) );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 'finished' => true );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ *
+ * @return array Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return 1;
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_constants' );
+ }
+
+ /**
+ * Sync the constants if we're supposed to.
+ *
+ * @access public
+ */
+ public function maybe_sync_constants() {
+ if ( get_transient( self::CONSTANTS_AWAIT_TRANSIENT_NAME ) ) {
+ return;
+ }
+
+ set_transient( self::CONSTANTS_AWAIT_TRANSIENT_NAME, microtime( true ), Defaults::$default_sync_constants_wait_time );
+
+ $constants = $this->get_all_constants();
+ if ( empty( $constants ) ) {
+ return;
+ }
+
+ $constants_checksums = (array) get_option( self::CONSTANTS_CHECKSUM_OPTION_NAME, array() );
+
+ foreach ( $constants as $name => $value ) {
+ $checksum = $this->get_check_sum( $value );
+ // Explicitly not using Identical comparison as get_option returns a string.
+ if ( ! $this->still_valid_checksum( $constants_checksums, $name, $checksum ) && ! is_null( $value ) ) {
+ /**
+ * Tells the client to sync a constant to the server
+ *
+ * @param string The name of the constant
+ * @param mixed The value of the constant
+ *
+ * @since 4.2.0
+ */
+ do_action( 'jetpack_sync_constant', $name, $value );
+ $constants_checksums[ $name ] = $checksum;
+ } else {
+ $constants_checksums[ $name ] = $checksum;
+ }
+ }
+ update_option( self::CONSTANTS_CHECKSUM_OPTION_NAME, $constants_checksums );
+ }
+
+ /**
+ * Retrieve all constants as per the current constants whitelist.
+ * Public so that we don't have to store an option for each constant.
+ *
+ * @access public
+ *
+ * @return array All constants.
+ */
+ public function get_all_constants() {
+ $constants_whitelist = $this->get_constants_whitelist();
+
+ return array_combine(
+ $constants_whitelist,
+ array_map( array( $this, 'get_constant' ), $constants_whitelist )
+ );
+ }
+
+ /**
+ * Retrieve the value of a constant.
+ * Used as a wrapper to standartize access to constants.
+ *
+ * @access private
+ *
+ * @param string $constant Constant name.
+ *
+ * @return mixed Return value of the constant.
+ */
+ private function get_constant( $constant ) {
+ return ( defined( $constant ) ) ?
+ constant( $constant )
+ : null;
+ }
+
+ /**
+ * Expand the constants within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ *
+ * @return array $args The hook parameters.
+ */
+ public function expand_constants( $args ) {
+ if ( $args[0] ) {
+ $constants = $this->get_all_constants();
+ $constants_checksums = array();
+ foreach ( $constants as $name => $value ) {
+ $constants_checksums[ $name ] = $this->get_check_sum( $value );
+ }
+ update_option( self::CONSTANTS_CHECKSUM_OPTION_NAME, $constants_checksums );
+
+ return $constants;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Return Total number of objects.
+ *
+ * @param array $config Full Sync config.
+ *
+ * @return int total
+ */
+ public function total( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return count( $this->get_constants_whitelist() );
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/modules/class-full-sync-immediately.php b/vendor/automattic/jetpack-sync/src/modules/class-full-sync-immediately.php
new file mode 100644
index 0000000000000..487470806ceec
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/modules/class-full-sync-immediately.php
@@ -0,0 +1,435 @@
+is_started() && ! $this->is_finished() ) {
+ /**
+ * Fires when a full sync is cancelled.
+ *
+ * @since 4.2.0
+ */
+ do_action( 'jetpack_full_sync_cancelled' );
+ $this->send_action( 'jetpack_full_sync_cancelled' );
+ }
+
+ // Remove all evidence of previous full sync items and status.
+ $this->reset_data();
+
+ if ( ! is_array( $full_sync_config ) ) {
+ $full_sync_config = Defaults::$default_full_sync_config;
+ if ( is_multisite() ) {
+ $full_sync_config['network_options'] = 1;
+ }
+ }
+
+ if ( isset( $full_sync_config['users'] ) && 'initial' === $full_sync_config['users'] ) {
+ $full_sync_config['users'] = Modules::get_module( 'users' )->get_initial_sync_user_config();
+ }
+
+ $this->update_status(
+ array(
+ 'started' => time(),
+ 'config' => $full_sync_config,
+ 'progress' => $this->get_initial_progress( $full_sync_config ),
+ )
+ );
+
+ $range = $this->get_content_range( $full_sync_config );
+ /**
+ * Fires when a full sync begins. This action is serialized
+ * and sent to the server so that it knows a full sync is coming.
+ *
+ * @param array $full_sync_config Sync configuration for all sync modules.
+ * @param array $range Range of the sync items, containing min and max IDs for some item types.
+ * @param array $empty The modules with no items to sync during a full sync.
+ *
+ * @since 4.2.0
+ * @since 7.3.0 Added $range arg.
+ * @since 7.4.0 Added $empty arg.
+ */
+ do_action( 'jetpack_full_sync_start', $full_sync_config, $range );
+ $this->send_action( 'jetpack_full_sync_start', array( $full_sync_config, $range ) );
+
+ return true;
+ }
+
+ /**
+ * Whether full sync has started.
+ *
+ * @access public
+ *
+ * @return boolean
+ */
+ public function is_started() {
+ return (bool) $this->get_status()['started'];
+ }
+
+ /**
+ * Retrieve the status of the current full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync status.
+ */
+ public function get_status() {
+ $default = array(
+ 'started' => false,
+ 'finished' => false,
+ 'progress' => array(),
+ 'config' => array(),
+ );
+
+ return wp_parse_args( \Jetpack_Options::get_raw_option( self::STATUS_OPTION ), $default );
+ }
+
+ /**
+ * Returns the progress percentage of a full sync.
+ *
+ * @access public
+ *
+ * @return int|null
+ */
+ public function get_sync_progress_percentage() {
+ if ( ! $this->is_started() || $this->is_finished() ) {
+ return null;
+ }
+ $status = $this->get_status();
+ if ( empty( $status['progress'] ) ) {
+ return null;
+ }
+ $total_items = array_reduce(
+ array_values( $status['progress'] ),
+ function ( $sum, $sync_item ) {
+ return isset( $sync_item['total'] ) ? ( $sum + (int) $sync_item['total'] ) : $sum;
+ },
+ 0
+ );
+ $total_sent = array_reduce(
+ array_values( $status['progress'] ),
+ function ( $sum, $sync_item ) {
+ return isset( $sync_item['sent'] ) ? ( $sum + (int) $sync_item['sent'] ) : $sum;
+ },
+ 0
+ );
+ return floor( ( $total_sent / $total_items ) * 100 );
+ }
+
+ /**
+ * Whether full sync has finished.
+ *
+ * @access public
+ *
+ * @return boolean
+ */
+ public function is_finished() {
+ return (bool) $this->get_status()['finished'];
+ }
+
+ /**
+ * Clear all the full sync data.
+ *
+ * @access public
+ */
+ public function reset_data() {
+ $this->clear_status();
+ ( new Lock() )->remove( self::LOCK_NAME );
+ }
+
+ /**
+ * Clear all the full sync status options.
+ *
+ * @access public
+ */
+ public function clear_status() {
+ \Jetpack_Options::delete_raw_option( self::STATUS_OPTION );
+ }
+
+ /**
+ * Updates the status of the current full sync.
+ *
+ * @access public
+ *
+ * @param array $values New values to set.
+ *
+ * @return bool True if success.
+ */
+ public function update_status( $values ) {
+ return $this->set_status( wp_parse_args( $values, $this->get_status() ) );
+ }
+
+ /**
+ * Retrieve the status of the current full sync.
+ *
+ * @param array $values New values to set.
+ *
+ * @access public
+ *
+ * @return boolean Full sync status.
+ */
+ public function set_status( $values ) {
+ return \Jetpack_Options::update_raw_option( self::STATUS_OPTION, $values );
+ }
+
+ /**
+ * Given an initial Full Sync configuration get the initial status.
+ *
+ * @param array $full_sync_config Full sync configuration.
+ *
+ * @return array Initial Sent status.
+ */
+ public function get_initial_progress( $full_sync_config ) {
+ // Set default configuration, calculate totals, and save configuration if totals > 0.
+ $status = array();
+ foreach ( $full_sync_config as $name => $config ) {
+ $module = Modules::get_module( $name );
+ $status[ $name ] = array(
+ 'total' => $module->total( $config ),
+ 'sent' => 0,
+ 'finished' => false,
+ );
+ }
+
+ return $status;
+ }
+
+ /**
+ * Get the range for content (posts and comments) to sync.
+ *
+ * @access private
+ *
+ * @return array Array of range (min ID, max ID, total items) for all content types.
+ */
+ private function get_content_range() {
+ $range = array();
+ $config = $this->get_status()['config'];
+ // Add range only when syncing all objects.
+ if ( true === isset( $config['posts'] ) && $config['posts'] ) {
+ $range['posts'] = $this->get_range( 'posts' );
+ }
+
+ if ( true === isset( $config['comments'] ) && $config['comments'] ) {
+ $range['comments'] = $this->get_range( 'comments' );
+ }
+
+ return $range;
+ }
+
+ /**
+ * Get the range (min ID, max ID and total items) of items to sync.
+ *
+ * @access public
+ *
+ * @param string $type Type of sync item to get the range for.
+ *
+ * @return array Array of min ID, max ID and total items in the range.
+ */
+ public function get_range( $type ) {
+ global $wpdb;
+ if ( ! in_array( $type, array( 'comments', 'posts' ), true ) ) {
+ return array();
+ }
+
+ switch ( $type ) {
+ case 'posts':
+ $table = $wpdb->posts;
+ $id = 'ID';
+ $where_sql = Settings::get_blacklisted_post_types_sql();
+
+ break;
+ case 'comments':
+ $table = $wpdb->comments;
+ $id = 'comment_ID';
+ $where_sql = Settings::get_comments_filter_sql();
+ break;
+ }
+
+ // TODO: Call $wpdb->prepare on the following query.
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ $results = $wpdb->get_results( "SELECT MAX({$id}) as max, MIN({$id}) as min, COUNT({$id}) as count FROM {$table} WHERE {$where_sql}" );
+ if ( isset( $results[0] ) ) {
+ return $results[0];
+ }
+
+ return array();
+ }
+
+ /**
+ * Continue sending instead of enqueueing.
+ *
+ * @access public
+ */
+ public function continue_enqueuing() {
+ $this->continue_sending();
+ }
+
+ /**
+ * Continue sending.
+ *
+ * @access public
+ */
+ public function continue_sending() {
+ if ( ! ( new Lock() )->attempt( self::LOCK_NAME ) || ! $this->is_started() || $this->get_status()['finished'] ) {
+ return;
+ }
+
+ $this->send();
+
+ ( new Lock() )->remove( self::LOCK_NAME );
+ }
+
+ /**
+ * Immediately send the next items to full sync.
+ *
+ * @access public
+ */
+ public function send() {
+ $config = $this->get_status()['config'];
+
+ $max_duration = Settings::get_setting( 'full_sync_send_duration' );
+ $send_until = microtime( true ) + $max_duration;
+
+ $progress = $this->get_status()['progress'];
+
+ foreach ( $this->get_remaining_modules_to_send() as $module ) {
+ $progress[ $module->name() ] = $module->send_full_sync_actions( $config[ $module->name() ], $progress[ $module->name() ], $send_until );
+ if ( ! $progress[ $module->name() ]['finished'] ) {
+ $this->update_status( array( 'progress' => $progress ) );
+
+ return;
+ }
+ }
+
+ $this->send_full_sync_end();
+ $this->update_status( array( 'progress' => $progress ) );
+ }
+
+ /**
+ * Get Modules that are configured to Full Sync and haven't finished sending
+ *
+ * @return array
+ */
+ public function get_remaining_modules_to_send() {
+ $status = $this->get_status();
+
+ return array_filter(
+ Modules::get_modules(),
+ /**
+ * Select configured and not finished modules.
+ *
+ * @return bool
+ * @var $module Module
+ */
+ function ( $module ) use ( $status ) {
+ // Skip module if not configured for this sync or module is done.
+ if ( ! isset( $status['config'][ $module->name() ] ) ) {
+ return false;
+ }
+ if ( ! $status['config'][ $module->name() ] ) {
+ return false;
+ }
+ if ( isset( $status['progress'][ $module->name() ]['finished'] ) ) {
+ if ( true === $status['progress'][ $module->name() ]['finished'] ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ );
+ }
+
+ /**
+ * Send 'jetpack_full_sync_end' and update 'finished' status.
+ *
+ * @access public
+ */
+ public function send_full_sync_end() {
+ $range = $this->get_content_range();
+
+ /**
+ * Fires when a full sync ends. This action is serialized
+ * and sent to the server.
+ *
+ * @param string $checksum Deprecated since 7.3.0 - @see https://github.com/Automattic/jetpack/pull/11945/
+ * @param array $range Range of the sync items, containing min and max IDs for some item types.
+ *
+ * @since 4.2.0
+ * @since 7.3.0 Added $range arg.
+ */
+ do_action( 'jetpack_full_sync_end', '', $range );
+ $this->send_action( 'jetpack_full_sync_end', array( '', $range ) );
+
+ // Setting autoload to true means that it's faster to check whether we should continue enqueuing.
+ $this->update_status( array( 'finished' => time() ) );
+ }
+
+ /**
+ * Empty Function as we don't close buffers on Immediate Full Sync.
+ *
+ * @param array $actions an array of actions, ignored for queueless sync.
+ */
+ public function update_sent_progress_action( $actions ) { } // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+
+}
diff --git a/vendor/automattic/jetpack-sync/src/modules/class-full-sync.php b/vendor/automattic/jetpack-sync/src/modules/class-full-sync.php
new file mode 100644
index 0000000000000..732411c34d60a
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/modules/class-full-sync.php
@@ -0,0 +1,715 @@
+is_started() && ! $this->is_finished();
+
+ // Remove all evidence of previous full sync items and status.
+ $this->reset_data();
+
+ if ( $was_already_running ) {
+ /**
+ * Fires when a full sync is cancelled.
+ *
+ * @since 4.2.0
+ */
+ do_action( 'jetpack_full_sync_cancelled' );
+ }
+
+ $this->update_status_option( 'started', time() );
+ $this->update_status_option( 'params', $module_configs );
+
+ $enqueue_status = array();
+ $full_sync_config = array();
+ $include_empty = false;
+ $empty = array();
+
+ // Default value is full sync.
+ if ( ! is_array( $module_configs ) ) {
+ $module_configs = array();
+ $include_empty = true;
+ foreach ( Modules::get_modules() as $module ) {
+ $module_configs[ $module->name() ] = true;
+ }
+ }
+
+ // Set default configuration, calculate totals, and save configuration if totals > 0.
+ foreach ( Modules::get_modules() as $module ) {
+ $module_name = $module->name();
+ $module_config = isset( $module_configs[ $module_name ] ) ? $module_configs[ $module_name ] : false;
+
+ if ( ! $module_config ) {
+ continue;
+ }
+
+ if ( 'users' === $module_name && 'initial' === $module_config ) {
+ $module_config = $module->get_initial_sync_user_config();
+ }
+
+ $enqueue_status[ $module_name ] = false;
+
+ $total_items = $module->estimate_full_sync_actions( $module_config );
+
+ // If there's information to process, configure this module.
+ if ( ! is_null( $total_items ) && $total_items > 0 ) {
+ $full_sync_config[ $module_name ] = $module_config;
+ $enqueue_status[ $module_name ] = array(
+ $total_items, // Total.
+ 0, // Queued.
+ false, // Current state.
+ );
+ } elseif ( $include_empty && 0 === $total_items ) {
+ $empty[ $module_name ] = true;
+ }
+ }
+
+ $this->set_config( $full_sync_config );
+ $this->set_enqueue_status( $enqueue_status );
+
+ $range = $this->get_content_range( $full_sync_config );
+ /**
+ * Fires when a full sync begins. This action is serialized
+ * and sent to the server so that it knows a full sync is coming.
+ *
+ * @since 4.2.0
+ * @since 7.3.0 Added $range arg.
+ * @since 7.4.0 Added $empty arg.
+ *
+ * @param array $full_sync_config Sync configuration for all sync modules.
+ * @param array $range Range of the sync items, containing min and max IDs for some item types.
+ * @param array $empty The modules with no items to sync during a full sync.
+ */
+ do_action( 'jetpack_full_sync_start', $full_sync_config, $range, $empty );
+
+ $this->continue_enqueuing( $full_sync_config );
+
+ return true;
+ }
+
+ /**
+ * Enqueue the next items to sync.
+ *
+ * @access public
+ *
+ * @param array $configs Full sync configuration for all sync modules.
+ */
+ public function continue_enqueuing( $configs = null ) {
+ if ( ! $this->is_started() || ! ( new Lock() )->attempt( self::ENQUEUE_LOCK_NAME ) || $this->get_status_option( 'queue_finished' ) ) {
+ return;
+ }
+
+ $this->enqueue( $configs );
+
+ ( new Lock() )->remove( self::ENQUEUE_LOCK_NAME );
+ }
+
+ /**
+ * Get Modules that are configured to Full Sync and haven't finished enqueuing
+ *
+ * @param array $configs Full sync configuration for all sync modules.
+ *
+ * @return array
+ */
+ public function get_remaining_modules_to_enqueue( $configs ) {
+ $enqueue_status = $this->get_enqueue_status();
+ return array_filter(
+ Modules::get_modules(),
+ /**
+ * Select configured and not finished modules.
+ *
+ * @var $module Module
+ * @return bool
+ */
+ function ( $module ) use ( $configs, $enqueue_status ) {
+ // Skip module if not configured for this sync or module is done.
+ if ( ! isset( $configs[ $module->name() ] ) ) {
+ return false;
+ }
+ if ( ! $configs[ $module->name() ] ) {
+ return false;
+ }
+ if ( isset( $enqueue_status[ $module->name() ][2] ) ) {
+ if ( true === $enqueue_status[ $module->name() ][2] ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ );
+ }
+
+ /**
+ * Enqueue the next items to sync.
+ *
+ * @access public
+ *
+ * @param array $configs Full sync configuration for all sync modules.
+ */
+ public function enqueue( $configs = null ) {
+ if ( ! $configs ) {
+ $configs = $this->get_config();
+ }
+
+ $enqueue_status = $this->get_enqueue_status();
+ $full_sync_queue = new Queue( 'full_sync' );
+ $available_queue_slots = Settings::get_setting( 'max_queue_size_full_sync' ) - $full_sync_queue->size();
+
+ if ( $available_queue_slots <= 0 ) {
+ return;
+ }
+
+ $remaining_items_to_enqueue = min( Settings::get_setting( 'max_enqueue_full_sync' ), $available_queue_slots );
+
+ /**
+ * If a module exits early (e.g. because it ran out of full sync queue slots, or we ran out of request time)
+ * then it should exit early
+ */
+ foreach ( $this->get_remaining_modules_to_enqueue( $configs ) as $module ) {
+ list( $items_enqueued, $next_enqueue_state ) = $module->enqueue_full_sync_actions( $configs[ $module->name() ], $remaining_items_to_enqueue, $enqueue_status[ $module->name() ][2] );
+
+ $enqueue_status[ $module->name() ][2] = $next_enqueue_state;
+
+ // If items were processed, subtract them from the limit.
+ if ( ! is_null( $items_enqueued ) && $items_enqueued > 0 ) {
+ $enqueue_status[ $module->name() ][1] += $items_enqueued;
+ $remaining_items_to_enqueue -= $items_enqueued;
+ }
+
+ if ( 0 >= $remaining_items_to_enqueue || true !== $next_enqueue_state ) {
+ $this->set_enqueue_status( $enqueue_status );
+ return;
+ }
+ }
+
+ $this->queue_full_sync_end( $configs );
+ $this->set_enqueue_status( $enqueue_status );
+ }
+
+ /**
+ * Enqueue 'jetpack_full_sync_end' and update 'queue_finished' status.
+ *
+ * @access public
+ *
+ * @param array $configs Full sync configuration for all sync modules.
+ */
+ public function queue_full_sync_end( $configs ) {
+ $range = $this->get_content_range( $configs );
+
+ /**
+ * Fires when a full sync ends. This action is serialized
+ * and sent to the server.
+ *
+ * @since 4.2.0
+ * @since 7.3.0 Added $range arg.
+ *
+ * @param string $checksum Deprecated since 7.3.0 - @see https://github.com/Automattic/jetpack/pull/11945/
+ * @param array $range Range of the sync items, containing min and max IDs for some item types.
+ */
+ do_action( 'jetpack_full_sync_end', '', $range );
+
+ // Setting autoload to true means that it's faster to check whether we should continue enqueuing.
+ $this->update_status_option( 'queue_finished', time(), true );
+ }
+
+ /**
+ * Get the range (min ID, max ID and total items) of items to sync.
+ *
+ * @access public
+ *
+ * @param string $type Type of sync item to get the range for.
+ * @return array Array of min ID, max ID and total items in the range.
+ */
+ public function get_range( $type ) {
+ global $wpdb;
+ if ( ! in_array( $type, array( 'comments', 'posts' ), true ) ) {
+ return array();
+ }
+
+ switch ( $type ) {
+ case 'posts':
+ $table = $wpdb->posts;
+ $id = 'ID';
+ $where_sql = Settings::get_blacklisted_post_types_sql();
+
+ break;
+ case 'comments':
+ $table = $wpdb->comments;
+ $id = 'comment_ID';
+ $where_sql = Settings::get_comments_filter_sql();
+ break;
+ }
+
+ // TODO: Call $wpdb->prepare on the following query.
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ $results = $wpdb->get_results( "SELECT MAX({$id}) as max, MIN({$id}) as min, COUNT({$id}) as count FROM {$table} WHERE {$where_sql}" );
+ if ( isset( $results[0] ) ) {
+ return $results[0];
+ }
+
+ return array();
+ }
+
+ /**
+ * Get the range for content (posts and comments) to sync.
+ *
+ * @access private
+ *
+ * @param array $config Full sync configuration for this all sync modules.
+ * @return array Array of range (min ID, max ID, total items) for all content types.
+ */
+ private function get_content_range( $config ) {
+ $range = array();
+ // Only when we are sending the whole range do we want to send also the range.
+ if ( true === isset( $config['posts'] ) && $config['posts'] ) {
+ $range['posts'] = $this->get_range( 'posts' );
+ }
+
+ if ( true === isset( $config['comments'] ) && $config['comments'] ) {
+ $range['comments'] = $this->get_range( 'comments' );
+ }
+ return $range;
+ }
+
+ /**
+ * Update the progress after sync modules actions have been processed on the server.
+ *
+ * @access public
+ *
+ * @param array $actions Actions that have been processed on the server.
+ */
+ public function update_sent_progress_action( $actions ) {
+ // Quick way to map to first items with an array of arrays.
+ $actions_with_counts = array_count_values( array_filter( array_map( array( $this, 'get_action_name' ), $actions ) ) );
+
+ // Total item counts for each action.
+ $actions_with_total_counts = $this->get_actions_totals( $actions );
+
+ if ( ! $this->is_started() || $this->is_finished() ) {
+ return;
+ }
+
+ if ( isset( $actions_with_counts['jetpack_full_sync_start'] ) ) {
+ $this->update_status_option( 'send_started', time() );
+ }
+
+ foreach ( Modules::get_modules() as $module ) {
+ $module_actions = $module->get_full_sync_actions();
+ $status_option_name = "{$module->name()}_sent";
+ $total_option_name = "{$status_option_name}_total";
+ $items_sent = $this->get_status_option( $status_option_name, 0 );
+ $items_sent_total = $this->get_status_option( $total_option_name, 0 );
+
+ foreach ( $module_actions as $module_action ) {
+ if ( isset( $actions_with_counts[ $module_action ] ) ) {
+ $items_sent += $actions_with_counts[ $module_action ];
+ }
+
+ if ( ! empty( $actions_with_total_counts[ $module_action ] ) ) {
+ $items_sent_total += $actions_with_total_counts[ $module_action ];
+ }
+ }
+
+ if ( $items_sent > 0 ) {
+ $this->update_status_option( $status_option_name, $items_sent );
+ }
+
+ if ( 0 !== $items_sent_total ) {
+ $this->update_status_option( $total_option_name, $items_sent_total );
+ }
+ }
+
+ if ( isset( $actions_with_counts['jetpack_full_sync_end'] ) ) {
+ $this->update_status_option( 'finished', time() );
+ }
+ }
+
+ /**
+ * Returns the progress percentage of a full sync.
+ *
+ * @access public
+ *
+ * @return int|null
+ */
+ public function get_sync_progress_percentage() {
+ if ( ! $this->is_started() || $this->is_finished() ) {
+ return null;
+ }
+ $status = $this->get_status();
+ if ( ! $status['queue'] || ! $status['sent'] || ! $status['total'] ) {
+ return null;
+ }
+ $queued_multiplier = 0.1;
+ $sent_multiplier = 0.9;
+ $count_queued = array_reduce(
+ $status['queue'],
+ function ( $sum, $value ) {
+ return $sum + $value;
+ },
+ 0
+ );
+ $count_sent = array_reduce(
+ $status['sent'],
+ function ( $sum, $value ) {
+ return $sum + $value;
+ },
+ 0
+ );
+ $count_total = array_reduce(
+ $status['total'],
+ function ( $sum, $value ) {
+ return $sum + $value;
+ },
+ 0
+ );
+ $percent_queued = ( $count_queued / $count_total ) * $queued_multiplier * 100;
+ $percent_sent = ( $count_sent / $count_total ) * $sent_multiplier * 100;
+ return ceil( $percent_queued + $percent_sent );
+ }
+
+ /**
+ * Get the name of the action for an item in the sync queue.
+ *
+ * @access public
+ *
+ * @param array $queue_item Item of the sync queue.
+ * @return string|boolean Name of the action, false if queue item is invalid.
+ */
+ public function get_action_name( $queue_item ) {
+ if ( is_array( $queue_item ) && isset( $queue_item[0] ) ) {
+ return $queue_item[0];
+ }
+ return false;
+ }
+
+ /**
+ * Retrieve the total number of items we're syncing in a particular queue item (action).
+ * `$queue_item[1]` is expected to contain chunks of items, and `$queue_item[1][0]`
+ * represents the first (and only) chunk of items to sync in that action.
+ *
+ * @access public
+ *
+ * @param array $queue_item Item of the sync queue that corresponds to a particular action.
+ * @return int Total number of items in the action.
+ */
+ public function get_action_totals( $queue_item ) {
+ if ( is_array( $queue_item ) && isset( $queue_item[1][0] ) ) {
+ if ( is_array( $queue_item[1][0] ) ) {
+ // Let's count the items we sync in this action.
+ return count( $queue_item[1][0] );
+ }
+ // -1 indicates that this action syncs all items by design.
+ return -1;
+ }
+ return 0;
+ }
+
+ /**
+ * Retrieve the total number of items for a set of actions, grouped by action name.
+ *
+ * @access public
+ *
+ * @param array $actions An array of actions.
+ * @return array An array, representing the total number of items, grouped per action.
+ */
+ public function get_actions_totals( $actions ) {
+ $totals = array();
+
+ foreach ( $actions as $action ) {
+ $name = $this->get_action_name( $action );
+ $action_totals = $this->get_action_totals( $action );
+ if ( ! isset( $totals[ $name ] ) ) {
+ $totals[ $name ] = 0;
+ }
+ $totals[ $name ] += $action_totals;
+ }
+
+ return $totals;
+ }
+
+ /**
+ * Whether full sync has started.
+ *
+ * @access public
+ *
+ * @return boolean
+ */
+ public function is_started() {
+ return (bool) $this->get_status_option( 'started' );
+ }
+
+ /**
+ * Whether full sync has finished.
+ *
+ * @access public
+ *
+ * @return boolean
+ */
+ public function is_finished() {
+ return (bool) $this->get_status_option( 'finished' );
+ }
+
+ /**
+ * Retrieve the status of the current full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync status.
+ */
+ public function get_status() {
+ $status = array(
+ 'started' => $this->get_status_option( 'started' ),
+ 'queue_finished' => $this->get_status_option( 'queue_finished' ),
+ 'send_started' => $this->get_status_option( 'send_started' ),
+ 'finished' => $this->get_status_option( 'finished' ),
+ 'sent' => array(),
+ 'sent_total' => array(),
+ 'queue' => array(),
+ 'config' => $this->get_status_option( 'params' ),
+ 'total' => array(),
+ );
+
+ $enqueue_status = $this->get_enqueue_status();
+
+ foreach ( Modules::get_modules() as $module ) {
+ $name = $module->name();
+
+ if ( ! isset( $enqueue_status[ $name ] ) ) {
+ continue;
+ }
+
+ list( $total, $queued ) = $enqueue_status[ $name ];
+
+ if ( $total ) {
+ $status['total'][ $name ] = $total;
+ }
+
+ if ( $queued ) {
+ $status['queue'][ $name ] = $queued;
+ }
+
+ $sent = $this->get_status_option( "{$name}_sent" );
+ if ( $sent ) {
+ $status['sent'][ $name ] = $sent;
+ }
+
+ $sent_total = $this->get_status_option( "{$name}_sent_total" );
+ if ( $sent_total ) {
+ $status['sent_total'][ $name ] = $sent_total;
+ }
+ }
+
+ return $status;
+ }
+
+ /**
+ * Clear all the full sync status options.
+ *
+ * @access public
+ */
+ public function clear_status() {
+ $prefix = self::STATUS_OPTION_PREFIX;
+ \Jetpack_Options::delete_raw_option( "{$prefix}_started" );
+ \Jetpack_Options::delete_raw_option( "{$prefix}_params" );
+ \Jetpack_Options::delete_raw_option( "{$prefix}_queue_finished" );
+ \Jetpack_Options::delete_raw_option( "{$prefix}_send_started" );
+ \Jetpack_Options::delete_raw_option( "{$prefix}_finished" );
+
+ $this->delete_enqueue_status();
+
+ foreach ( Modules::get_modules() as $module ) {
+ \Jetpack_Options::delete_raw_option( "{$prefix}_{$module->name()}_sent" );
+ \Jetpack_Options::delete_raw_option( "{$prefix}_{$module->name()}_sent_total" );
+ }
+ }
+
+ /**
+ * Clear all the full sync data.
+ *
+ * @access public
+ */
+ public function reset_data() {
+ $this->clear_status();
+ $this->delete_config();
+ ( new Lock() )->remove( self::ENQUEUE_LOCK_NAME );
+
+ $listener = Listener::get_instance();
+ $listener->get_full_sync_queue()->reset();
+ }
+
+ /**
+ * Get the value of a full sync status option.
+ *
+ * @access private
+ *
+ * @param string $name Name of the option.
+ * @param mixed $default Default value of the option.
+ * @return mixed Option value.
+ */
+ private function get_status_option( $name, $default = null ) {
+ $value = \Jetpack_Options::get_raw_option( self::STATUS_OPTION_PREFIX . "_$name", $default );
+
+ return is_numeric( $value ) ? (int) $value : $value;
+ }
+
+ /**
+ * Update the value of a full sync status option.
+ *
+ * @access private
+ *
+ * @param string $name Name of the option.
+ * @param mixed $value Value of the option.
+ * @param boolean $autoload Whether the option should be autoloaded at the beginning of the request.
+ */
+ private function update_status_option( $name, $value, $autoload = false ) {
+ \Jetpack_Options::update_raw_option( self::STATUS_OPTION_PREFIX . "_$name", $value, $autoload );
+ }
+
+ /**
+ * Set the full sync enqueue status.
+ *
+ * @access private
+ *
+ * @param array $new_status The new full sync enqueue status.
+ */
+ private function set_enqueue_status( $new_status ) {
+ \Jetpack_Options::update_raw_option( 'jetpack_sync_full_enqueue_status', $new_status );
+ }
+
+ /**
+ * Delete full sync enqueue status.
+ *
+ * @access private
+ *
+ * @return boolean Whether the status was deleted.
+ */
+ private function delete_enqueue_status() {
+ return \Jetpack_Options::delete_raw_option( 'jetpack_sync_full_enqueue_status' );
+ }
+
+ /**
+ * Retrieve the current full sync enqueue status.
+ *
+ * @access private
+ *
+ * @return array Full sync enqueue status.
+ */
+ public function get_enqueue_status() {
+ return \Jetpack_Options::get_raw_option( 'jetpack_sync_full_enqueue_status' );
+ }
+
+ /**
+ * Set the full sync enqueue configuration.
+ *
+ * @access private
+ *
+ * @param array $config The new full sync enqueue configuration.
+ */
+ private function set_config( $config ) {
+ \Jetpack_Options::update_raw_option( 'jetpack_sync_full_config', $config );
+ }
+
+ /**
+ * Delete full sync configuration.
+ *
+ * @access private
+ *
+ * @return boolean Whether the configuration was deleted.
+ */
+ private function delete_config() {
+ return \Jetpack_Options::delete_raw_option( 'jetpack_sync_full_config' );
+ }
+
+ /**
+ * Retrieve the current full sync enqueue config.
+ *
+ * @access private
+ *
+ * @return array Full sync enqueue config.
+ */
+ private function get_config() {
+ return \Jetpack_Options::get_raw_option( 'jetpack_sync_full_config' );
+ }
+
+}
diff --git a/vendor/automattic/jetpack-sync/src/modules/class-import.php b/vendor/automattic/jetpack-sync/src/modules/class-import.php
new file mode 100644
index 0000000000000..99afd74b11e7b
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/modules/class-import.php
@@ -0,0 +1,218 @@
+ 'jetpack_sync_import_start',
+ 'import_done' => 'jetpack_sync_import_end',
+ 'import_end' => 'jetpack_sync_import_end',
+ );
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'import';
+ }
+
+ /**
+ * Initialize imports action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ add_action( 'export_wp', $callable );
+ add_action( 'jetpack_sync_import_start', $callable, 10, 2 );
+ add_action( 'jetpack_sync_import_end', $callable, 10, 2 );
+
+ // WordPress.
+ add_action( 'import_start', array( $this, 'sync_import_action' ) );
+
+ // Movable type, RSS, Livejournal.
+ add_action( 'import_done', array( $this, 'sync_import_action' ) );
+
+ // WordPress, Blogger, Livejournal, woo tax rate.
+ add_action( 'import_end', array( $this, 'sync_import_action' ) );
+ }
+
+ /**
+ * Set module defaults.
+ * Define an empty list of synced actions for us to fill later.
+ *
+ * @access public
+ */
+ public function set_defaults() {
+ $this->synced_actions = array();
+ }
+
+ /**
+ * Generic handler for import actions.
+ *
+ * @access public
+ *
+ * @param string $importer Either a string reported by the importer, the class name of the importer, or 'unknown'.
+ */
+ public function sync_import_action( $importer ) {
+ $import_action = current_filter();
+ // Map action to event name.
+ $sync_action = self::$import_sync_action_map[ $import_action ];
+
+ // Only sync each action once per import.
+ if ( array_key_exists( $sync_action, $this->synced_actions ) && $this->synced_actions[ $sync_action ] ) {
+ return;
+ }
+
+ // Mark this action as synced.
+ $this->synced_actions[ $sync_action ] = true;
+
+ // Prefer self-reported $importer value.
+ if ( ! $importer ) {
+ // Fall back to inferring by calling class name.
+ $importer = self::get_calling_importer_class();
+ }
+
+ // Get $importer from known_importers.
+ $known_importers = Settings::get_setting( 'known_importers' );
+ if ( isset( $known_importers[ $importer ] ) ) {
+ $importer = $known_importers[ $importer ];
+ }
+
+ $importer_name = $this->get_importer_name( $importer );
+
+ switch ( $sync_action ) {
+ case 'jetpack_sync_import_start':
+ /**
+ * Used for syncing the start of an import
+ *
+ * @since 7.3.0
+ *
+ * @module sync
+ *
+ * @param string $importer Either a string reported by the importer, the class name of the importer, or 'unknown'.
+ * @param string $importer_name The name reported by the importer, or 'Unknown Importer'.
+ */
+ do_action( 'jetpack_sync_import_start', $importer, $importer_name );
+ break;
+
+ case 'jetpack_sync_import_end':
+ /**
+ * Used for syncing the end of an import
+ *
+ * @since 7.3.0
+ *
+ * @module sync
+ *
+ * @param string $importer Either a string reported by the importer, the class name of the importer, or 'unknown'.
+ * @param string $importer_name The name reported by the importer, or 'Unknown Importer'.
+ */
+ do_action( 'jetpack_sync_import_end', $importer, $importer_name );
+ break;
+ }
+ }
+
+ /**
+ * Retrieve the name of the importer.
+ *
+ * @access private
+ *
+ * @param string $importer Either a string reported by the importer, the class name of the importer, or 'unknown'.
+ * @return string Name of the importer, or "Unknown Importer" if importer is unknown.
+ */
+ private function get_importer_name( $importer ) {
+ $importers = get_importers();
+ return isset( $importers[ $importer ] ) ? $importers[ $importer ][0] : 'Unknown Importer';
+ }
+
+ /**
+ * Determine the class that extends `WP_Importer` which is responsible for
+ * the current action. Designed to be used within an action handler.
+ *
+ * @access private
+ * @static
+ *
+ * @return string The name of the calling class, or 'unknown'.
+ */
+ private static function get_calling_importer_class() {
+ // If WP_Importer doesn't exist, neither will any importer that extends it.
+ if ( ! class_exists( 'WP_Importer', false ) ) {
+ return 'unknown';
+ }
+
+ $action = current_filter();
+ $backtrace = debug_backtrace( false ); //phpcs:ignore PHPCompatibility.FunctionUse.NewFunctionParameters.debug_backtrace_optionsFound,WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
+
+ $do_action_pos = -1;
+ $backtrace_len = count( $backtrace );
+ for ( $i = 0; $i < $backtrace_len; $i++ ) {
+ // Find the location in the stack of the calling action.
+ if ( 'do_action' === $backtrace[ $i ]['function'] && $action === $backtrace[ $i ]['args'][0] ) {
+ $do_action_pos = $i;
+ break;
+ }
+ }
+
+ // If the action wasn't called, the calling class is unknown.
+ if ( -1 === $do_action_pos ) {
+ return 'unknown';
+ }
+
+ // Continue iterating the stack looking for a caller that extends WP_Importer.
+ for ( $i = $do_action_pos + 1; $i < $backtrace_len; $i++ ) {
+ // If there is no class on the trace, continue.
+ if ( ! isset( $backtrace[ $i ]['class'] ) ) {
+ continue;
+ }
+
+ $class_name = $backtrace[ $i ]['class'];
+
+ // Check if the class extends WP_Importer.
+ if ( class_exists( $class_name, false ) ) {
+ $parents = class_parents( $class_name, false );
+ if ( $parents && in_array( 'WP_Importer', $parents, true ) ) {
+ return $class_name;
+ }
+ }
+ }
+
+ // If we've exhausted the stack without a match, the calling class is unknown.
+ return 'unknown';
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/modules/class-menus.php b/vendor/automattic/jetpack-sync/src/modules/class-menus.php
new file mode 100644
index 0000000000000..69faa9b57f8c2
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/modules/class-menus.php
@@ -0,0 +1,143 @@
+nav_items_just_added[] = $nav_item_id;
+ /**
+ * Helps sync log that a new menu item was added.
+ *
+ * @since 5.0.0
+ *
+ * @param int $menu_id ID of the menu.
+ * @param array $menu_data An array of menu data.
+ * @param int $nav_item_id ID of the new menu item.
+ * @param array $nav_item_args Arguments used to add the menu item.
+ */
+ do_action( 'jetpack_sync_updated_nav_menu_add_item', $menu_id, $menu_data, $nav_item_id, $nav_item_args );
+ }
+
+ /**
+ * Nav menu item update handler.
+ *
+ * @access public
+ *
+ * @param int $menu_id ID of the menu.
+ * @param int $nav_item_id ID of the new menu item.
+ * @param array $nav_item_args Arguments used to update the menu item.
+ */
+ public function update_nav_menu_update_item( $menu_id, $nav_item_id, $nav_item_args ) {
+ if ( in_array( $nav_item_id, $this->nav_items_just_added, true ) ) {
+ return;
+ }
+ $menu_data = wp_get_nav_menu_object( $menu_id );
+ /**
+ * Helps sync log that an update to the menu item happened.
+ *
+ * @since 5.0.0
+ *
+ * @param int $menu_id ID of the menu.
+ * @param array $menu_data An array of menu data.
+ * @param int $nav_item_id ID of the new menu item.
+ * @param array $nav_item_args Arguments used to update the menu item.
+ */
+ do_action( 'jetpack_sync_updated_nav_menu_update_item', $menu_id, $menu_data, $nav_item_id, $nav_item_args );
+ }
+
+ /**
+ * Remove menu items that have already been saved from the "just added" list.
+ *
+ * @access public
+ *
+ * @param int $nav_item_id ID of the new menu item.
+ * @param \WP_Post $post_after Nav menu item post object after the update.
+ */
+ public function remove_just_added_menu_item( $nav_item_id, $post_after ) {
+ if ( 'nav_menu_item' !== $post_after->post_type ) {
+ return;
+ }
+ $this->nav_items_just_added = array_diff( $this->nav_items_just_added, array( $nav_item_id ) );
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/modules/class-meta.php b/vendor/automattic/jetpack-sync/src/modules/class-meta.php
new file mode 100644
index 0000000000000..1d30c72ee73d5
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/modules/class-meta.php
@@ -0,0 +1,81 @@
+prepare call to use placeholders.
+ *
+ * @param string $object_type The type of object for which we retrieve meta. Either 'post' or 'comment'.
+ * @param array $config Must include 'meta_key' and 'ids' keys.
+ *
+ * @return array
+ */
+ public function get_objects_by_id( $object_type, $config ) {
+ global $wpdb;
+
+ $table = _get_meta_table( $object_type );
+
+ if ( ! $table ) {
+ return array();
+ }
+
+ if ( ! isset( $config['meta_key'] ) || ! isset( $config['ids'] ) || ! is_array( $config['ids'] ) ) {
+ return array();
+ }
+
+ $meta_key = $config['meta_key'];
+ $ids = $config['ids'];
+ $object_id_column = $object_type . '_id';
+
+ // Sanitize so that the array only has integer values.
+ $ids_string = implode( ', ', array_map( 'intval', $ids ) );
+ $metas = $wpdb->get_results(
+ $wpdb->prepare(
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ "SELECT * FROM {$table} WHERE {$object_id_column} IN ( {$ids_string} ) AND meta_key = %s",
+ $meta_key
+ )
+ );
+
+ $meta_objects = array();
+ foreach ( (array) $metas as $meta_object ) {
+ $meta_object = (array) $meta_object;
+ $meta_objects[ $meta_object[ $object_id_column ] ] = array(
+ 'meta_type' => $object_type,
+ 'meta_id' => $meta_object['meta_id'],
+ 'meta_key' => $meta_key,
+ 'meta_value' => $meta_object['meta_value'],
+ 'object_id' => $meta_object[ $object_id_column ],
+ );
+ }
+
+ return $meta_objects;
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/modules/class-module.php b/vendor/automattic/jetpack-sync/src/modules/class-module.php
new file mode 100644
index 0000000000000..bedb95a3e5456
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/modules/class-module.php
@@ -0,0 +1,602 @@
+recursive_ksort( $values );
+ }
+ return crc32( wp_json_encode( jetpack_json_wrap( $values ) ) );
+ }
+
+ /**
+ * Recursively call ksort on an Array
+ *
+ * @param array $values Array.
+ */
+ private function recursive_ksort( &$values ) {
+ ksort( $values );
+ foreach ( $values as &$value ) {
+ if ( is_array( $value ) ) {
+ $this->recursive_ksort( $value );
+ }
+ }
+ }
+
+ /**
+ * Whether a particular checksum in a set of checksums is valid.
+ *
+ * @access protected
+ *
+ * @param array $sums_to_check Array of checksums.
+ * @param string $name Name of the checksum.
+ * @param int $new_sum Checksum to compare against.
+ * @return boolean Whether the checksum is valid.
+ */
+ protected function still_valid_checksum( $sums_to_check, $name, $new_sum ) {
+ if ( isset( $sums_to_check[ $name ] ) && $sums_to_check[ $name ] === $new_sum ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Enqueue all items of a sync type as an action.
+ *
+ * @access protected
+ *
+ * @param string $action_name Name of the action.
+ * @param string $table_name Name of the database table.
+ * @param string $id_field Name of the ID field in the database.
+ * @param string $where_sql The SQL WHERE clause to filter to the desired items.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue in the same time.
+ * @param boolean $state Whether enqueueing has finished.
+ * @return array Array, containing the number of chunks and TRUE, indicating enqueueing has finished.
+ */
+ protected function enqueue_all_ids_as_action( $action_name, $table_name, $id_field, $where_sql, $max_items_to_enqueue, $state ) {
+ global $wpdb;
+
+ if ( ! $where_sql ) {
+ $where_sql = '1 = 1';
+ }
+
+ $items_per_page = 1000;
+ $page = 1;
+ $chunk_count = 0;
+ $previous_interval_end = $state ? $state : '~0';
+ $listener = Listener::get_instance();
+
+ // Count down from max_id to min_id so we get newest posts/comments/etc first.
+ // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ while ( $ids = $wpdb->get_col( "SELECT {$id_field} FROM {$table_name} WHERE {$where_sql} AND {$id_field} < {$previous_interval_end} ORDER BY {$id_field} DESC LIMIT {$items_per_page}" ) ) {
+ // Request posts in groups of N for efficiency.
+ $chunked_ids = array_chunk( $ids, self::ARRAY_CHUNK_SIZE );
+
+ // If we hit our row limit, process and return.
+ if ( $chunk_count + count( $chunked_ids ) >= $max_items_to_enqueue ) {
+ $remaining_items_count = $max_items_to_enqueue - $chunk_count;
+ $remaining_items = array_slice( $chunked_ids, 0, $remaining_items_count );
+ $remaining_items_with_previous_interval_end = $this->get_chunks_with_preceding_end( $remaining_items, $previous_interval_end );
+ $listener->bulk_enqueue_full_sync_actions( $action_name, $remaining_items_with_previous_interval_end );
+
+ $last_chunk = end( $remaining_items );
+ return array( $remaining_items_count + $chunk_count, end( $last_chunk ) );
+ }
+ $chunked_ids_with_previous_end = $this->get_chunks_with_preceding_end( $chunked_ids, $previous_interval_end );
+
+ $listener->bulk_enqueue_full_sync_actions( $action_name, $chunked_ids_with_previous_end );
+
+ $chunk_count += count( $chunked_ids );
+ $page++;
+ // The $ids are ordered in descending order.
+ $previous_interval_end = end( $ids );
+ }
+
+ if ( $wpdb->last_error ) {
+ // return the values that were passed in so all these chunks get retried.
+ return array( $max_items_to_enqueue, $state );
+ }
+
+ return array( $chunk_count, true );
+ }
+
+ /**
+ * Given the Module Full Sync Configuration and Status return the next chunk of items to send.
+ *
+ * @param array $config This module Full Sync configuration.
+ * @param array $status This module Full Sync status.
+ * @param int $chunk_size Chunk size.
+ *
+ * @return array|object|null
+ */
+ public function get_next_chunk( $config, $status, $chunk_size ) {
+ // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ global $wpdb;
+ return $wpdb->get_col(
+ <<id_field()}
+FROM {$wpdb->{$this->table_name()}}
+WHERE {$this->get_where_sql( $config )}
+AND {$this->id_field()} < {$status['last_sent']}
+ORDER BY {$this->id_field()}
+DESC LIMIT {$chunk_size}
+SQL
+ );
+ // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ }
+
+ /**
+ * Return the initial last sent object.
+ *
+ * @return string|array initial status.
+ */
+ public function get_initial_last_sent() {
+ return '~0';
+ }
+
+ /**
+ * Immediately send all items of a sync type as an action.
+ *
+ * @access protected
+ *
+ * @param string $config Full sync configuration for this module.
+ * @param array $status the current module full sync status.
+ * @param float $send_until timestamp until we want this request to send full sync events.
+ *
+ * @return array Status, the module full sync status updated.
+ */
+ public function send_full_sync_actions( $config, $status, $send_until ) {
+ global $wpdb;
+
+ if ( empty( $status['last_sent'] ) ) {
+ $status['last_sent'] = $this->get_initial_last_sent();
+ }
+
+ $limits = Settings::get_setting( 'full_sync_limits' )[ $this->name() ];
+
+ $chunks_sent = 0;
+ // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
+ while ( $objects = $this->get_next_chunk( $config, $status, $limits['chunk_size'] ) ) {
+ if ( $chunks_sent++ === $limits['max_chunks'] || microtime( true ) >= $send_until ) {
+ return $status;
+ }
+
+ $result = $this->send_action( 'jetpack_full_sync_' . $this->name(), array( $objects, $status['last_sent'] ) );
+
+ if ( is_wp_error( $result ) || $wpdb->last_error ) {
+ return $status;
+ }
+ // The $ids are ordered in descending order.
+ $status['last_sent'] = end( $objects );
+ $status['sent'] += count( $objects );
+ }
+
+ if ( ! $wpdb->last_error ) {
+ $status['finished'] = true;
+ }
+
+ return $status;
+ }
+
+ /**
+ * Immediately sends a single item without firing or enqueuing it
+ *
+ * @param string $action_name The action.
+ * @param array $data The data associated with the action.
+ */
+ public function send_action( $action_name, $data = null ) {
+ $sender = Sender::get_instance();
+ return $sender->send_action( $action_name, $data );
+ }
+
+ /**
+ * Retrieve chunk IDs with previous interval end.
+ *
+ * @access protected
+ *
+ * @param array $chunks All remaining items.
+ * @param int $previous_interval_end The last item from the previous interval.
+ * @return array Chunk IDs with the previous interval end.
+ */
+ protected function get_chunks_with_preceding_end( $chunks, $previous_interval_end ) {
+ $chunks_with_ends = array();
+ foreach ( $chunks as $chunk ) {
+ $chunks_with_ends[] = array(
+ 'ids' => $chunk,
+ 'previous_end' => $previous_interval_end,
+ );
+ // Chunks are ordered in descending order.
+ $previous_interval_end = end( $chunk );
+ }
+ return $chunks_with_ends;
+ }
+
+ /**
+ * Get metadata of a particular object type within the designated meta key whitelist.
+ *
+ * @access protected
+ *
+ * @todo Refactor to use $wpdb->prepare() on the SQL query.
+ *
+ * @param array $ids Object IDs.
+ * @param string $meta_type Meta type.
+ * @param array $meta_key_whitelist Meta key whitelist.
+ * @return array Unserialized meta values.
+ */
+ protected function get_metadata( $ids, $meta_type, $meta_key_whitelist ) {
+ global $wpdb;
+ $table = _get_meta_table( $meta_type );
+ $id = $meta_type . '_id';
+ if ( ! $table ) {
+ return array();
+ }
+
+ $private_meta_whitelist_sql = "'" . implode( "','", array_map( 'esc_sql', $meta_key_whitelist ) ) . "'";
+
+ return array_map(
+ array( $this, 'unserialize_meta' ),
+ $wpdb->get_results(
+ // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared
+ "SELECT $id, meta_key, meta_value, meta_id FROM $table WHERE $id IN ( " . implode( ',', wp_parse_id_list( $ids ) ) . ' )' .
+ " AND meta_key IN ( $private_meta_whitelist_sql ) ",
+ // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared
+ OBJECT
+ )
+ );
+ }
+
+ /**
+ * Initialize listeners for the particular meta type.
+ *
+ * @access public
+ *
+ * @param string $meta_type Meta type.
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners_for_meta_type( $meta_type, $callable ) {
+ add_action( "added_{$meta_type}_meta", $callable, 10, 4 );
+ add_action( "updated_{$meta_type}_meta", $callable, 10, 4 );
+ add_action( "deleted_{$meta_type}_meta", $callable, 10, 4 );
+ }
+
+ /**
+ * Initialize meta whitelist handler for the particular meta type.
+ *
+ * @access public
+ *
+ * @param string $meta_type Meta type.
+ * @param callable $whitelist_handler Action handler callable.
+ */
+ public function init_meta_whitelist_handler( $meta_type, $whitelist_handler ) {
+ add_filter( "jetpack_sync_before_enqueue_added_{$meta_type}_meta", $whitelist_handler );
+ add_filter( "jetpack_sync_before_enqueue_updated_{$meta_type}_meta", $whitelist_handler );
+ add_filter( "jetpack_sync_before_enqueue_deleted_{$meta_type}_meta", $whitelist_handler );
+ }
+
+ /**
+ * Retrieve the term relationships for the specified object IDs.
+ *
+ * @access protected
+ *
+ * @todo This feels too specific to be in the abstract sync Module class. Move it?
+ *
+ * @param array $ids Object IDs.
+ * @return array Term relationships - object ID and term taxonomy ID pairs.
+ */
+ protected function get_term_relationships( $ids ) {
+ global $wpdb;
+
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ return $wpdb->get_results( "SELECT object_id, term_taxonomy_id FROM $wpdb->term_relationships WHERE object_id IN ( " . implode( ',', wp_parse_id_list( $ids ) ) . ' )', OBJECT );
+ }
+
+ /**
+ * Unserialize the value of a meta object, if necessary.
+ *
+ * @access public
+ *
+ * @param object $meta Meta object.
+ * @return object Meta object with possibly unserialized value.
+ */
+ public function unserialize_meta( $meta ) {
+ $meta->meta_value = maybe_unserialize( $meta->meta_value );
+ return $meta;
+ }
+
+ /**
+ * Retrieve a set of objects by their IDs.
+ *
+ * @access public
+ *
+ * @param string $object_type Object type.
+ * @param array $ids Object IDs.
+ * @return array Array of objects.
+ */
+ public function get_objects_by_id( $object_type, $ids ) {
+ if ( empty( $ids ) || empty( $object_type ) ) {
+ return array();
+ }
+
+ $objects = array();
+ foreach ( (array) $ids as $id ) {
+ $object = $this->get_object_by_id( $object_type, $id );
+
+ // Only add object if we have the object.
+ if ( $object ) {
+ $objects[ $id ] = $object;
+ }
+ }
+
+ return $objects;
+ }
+
+ /**
+ * Gets a list of minimum and maximum object ids for each batch based on the given batch size.
+ *
+ * @access public
+ *
+ * @param int $batch_size The batch size for objects.
+ * @param string|bool $where_sql The sql where clause minus 'WHERE', or false if no where clause is needed.
+ *
+ * @return array|bool An array of min and max ids for each batch. FALSE if no table can be found.
+ */
+ public function get_min_max_object_ids_for_batches( $batch_size, $where_sql = false ) {
+ global $wpdb;
+
+ if ( ! $this->table_name() ) {
+ return false;
+ }
+
+ $results = array();
+ $table = $wpdb->{$this->table_name()};
+ $current_max = 0;
+ $current_min = 1;
+ $id_field = $this->id_field();
+ $replicastore = new Replicastore();
+
+ $total = $replicastore->get_min_max_object_id(
+ $id_field,
+ $table,
+ $where_sql,
+ false
+ );
+
+ while ( $total->max > $current_max ) {
+ $where = $where_sql ?
+ $where_sql . " AND $id_field > $current_max" :
+ "$id_field > $current_max";
+ $result = $replicastore->get_min_max_object_id(
+ $id_field,
+ $table,
+ $where,
+ $batch_size
+ );
+ if ( empty( $result->min ) && empty( $result->max ) ) {
+ // Our query produced no min and max. We can assume the min from the previous query,
+ // and the total max we found in the initial query.
+ $current_max = (int) $total->max;
+ $result = (object) array(
+ 'min' => $current_min,
+ 'max' => $current_max,
+ );
+ } else {
+ $current_min = (int) $result->min;
+ $current_max = (int) $result->max;
+ }
+ $results[] = $result;
+ }
+
+ return $results;
+ }
+
+ /**
+ * Return Total number of objects.
+ *
+ * @param array $config Full Sync config.
+ *
+ * @return int total
+ */
+ public function total( $config ) {
+ global $wpdb;
+ $table = $wpdb->{$this->table_name()};
+ $where = $this->get_where_sql( $config );
+
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ return $wpdb->get_var( "SELECT COUNT(*) FROM $table WHERE $where" );
+ }
+
+ /**
+ * Retrieve the WHERE SQL clause based on the module config.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return string WHERE SQL clause, or `null` if no comments are specified in the module config.
+ */
+ public function get_where_sql( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return '1=1';
+ }
+
+}
diff --git a/vendor/automattic/jetpack-sync/src/modules/class-network-options.php b/vendor/automattic/jetpack-sync/src/modules/class-network-options.php
new file mode 100644
index 0000000000000..681282ce19d35
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/modules/class-network-options.php
@@ -0,0 +1,251 @@
+network_options_whitelist = Defaults::$default_network_options_whitelist;
+ }
+
+ /**
+ * Enqueue the network options actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ /**
+ * Tells the client to sync all options to the server
+ *
+ * @since 4.2.0
+ *
+ * @param boolean Whether to expand options (should always be true)
+ */
+ do_action( 'jetpack_full_sync_network_options', true );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 1, true );
+ }
+
+ /**
+ * Send the network options actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $send_until The timestamp until the current request can send.
+ * @param array $state This module Full Sync status.
+ *
+ * @return array This module Full Sync status.
+ */
+ public function send_full_sync_actions( $config, $send_until, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ // we call this instead of do_action when sending immediately.
+ $this->send_action( 'jetpack_full_sync_network_options', array( true ) );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 'finished' => true );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return array Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return 1;
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_network_options' );
+ }
+
+ /**
+ * Retrieve all network options as per the current network options whitelist.
+ *
+ * @access public
+ *
+ * @return array All network options.
+ */
+ public function get_all_network_options() {
+ $options = array();
+ foreach ( $this->network_options_whitelist as $option ) {
+ $options[ $option ] = get_site_option( $option );
+ }
+
+ return $options;
+ }
+
+ /**
+ * Set the network options whitelist.
+ *
+ * @access public
+ *
+ * @param array $options The new network options whitelist.
+ */
+ public function set_network_options_whitelist( $options ) {
+ $this->network_options_whitelist = $options;
+ }
+
+ /**
+ * Get the network options whitelist.
+ *
+ * @access public
+ *
+ * @return array The network options whitelist.
+ */
+ public function get_network_options_whitelist() {
+ return $this->network_options_whitelist;
+ }
+
+ /**
+ * Reject non-whitelisted network options.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array|false $args The hook parameters, false if not a whitelisted network option.
+ */
+ public function whitelist_network_options( $args ) {
+ if ( ! $this->is_whitelisted_network_option( $args[0] ) ) {
+ return false;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Whether the option is a whitelisted network option.
+ *
+ * @access public
+ *
+ * @param string $option Option name.
+ * @return boolean True if this is a whitelisted network option.
+ */
+ public function is_whitelisted_network_option( $option ) {
+ return in_array( $option, $this->network_options_whitelist, true );
+ }
+
+ /**
+ * Expand the network options within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array $args The hook parameters.
+ */
+ public function expand_network_options( $args ) {
+ if ( $args[0] ) {
+ return $this->get_all_network_options();
+ }
+
+ return $args;
+ }
+
+ /**
+ * Return Total number of objects.
+ *
+ * @param array $config Full Sync config.
+ *
+ * @return int total
+ */
+ public function total( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return count( $this->network_options_whitelist );
+ }
+
+}
diff --git a/vendor/automattic/jetpack-sync/src/modules/class-options.php b/vendor/automattic/jetpack-sync/src/modules/class-options.php
new file mode 100644
index 0000000000000..329036316f88e
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/modules/class-options.php
@@ -0,0 +1,404 @@
+update_options_whitelist();
+ $this->update_options_contentless();
+ }
+
+ /**
+ * Set module defaults at a later time.
+ *
+ * @access public
+ */
+ public function set_late_default() {
+ /** This filter is already documented in json-endpoints/jetpack/class.wpcom-json-api-get-option-endpoint.php */
+ $late_options = apply_filters( 'jetpack_options_whitelist', array() );
+ if ( ! empty( $late_options ) && is_array( $late_options ) ) {
+ $this->options_whitelist = array_merge( $this->options_whitelist, $late_options );
+ }
+ }
+
+ /**
+ * Add old deprecated options to the list of options to keep in sync.
+ *
+ * @since 8.8.0
+ *
+ * @access public
+ *
+ * @param array $options The default list of site options.
+ */
+ public function add_deprecated_options( $options ) {
+ global $wp_version;
+
+ $deprecated_options = array(
+ 'blacklist_keys' => '5.5-alpha', // Replaced by disallowed_keys.
+ 'comment_whitelist' => '5.5-alpha', // Replaced by comment_previously_approved.
+ );
+
+ foreach ( $deprecated_options as $option => $version ) {
+ if ( version_compare( $wp_version, $version, '<=' ) ) {
+ $options[] = $option;
+ }
+ }
+
+ return $options;
+ }
+
+ /**
+ * Enqueue the options actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ /**
+ * Tells the client to sync all options to the server
+ *
+ * @since 4.2.0
+ *
+ * @param boolean Whether to expand options (should always be true)
+ */
+ do_action( 'jetpack_full_sync_options', true );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 1, true );
+ }
+
+ /**
+ * Send the options actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $send_until The timestamp until the current request can send.
+ * @param array $state This module Full Sync status.
+ *
+ * @return array This module Full Sync status.
+ */
+ public function send_full_sync_actions( $config, $send_until, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ // we call this instead of do_action when sending immediately.
+ $this->send_action( 'jetpack_full_sync_options', array( true ) );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 'finished' => true );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return int Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return 1;
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_options' );
+ }
+
+ /**
+ * Retrieve all options as per the current options whitelist.
+ * Public so that we don't have to store so much data all the options twice.
+ *
+ * @access public
+ *
+ * @return array All options.
+ */
+ public function get_all_options() {
+ $options = array();
+ $random_string = wp_generate_password();
+ foreach ( $this->options_whitelist as $option ) {
+ $option_value = get_option( $option, $random_string );
+ if ( $option_value !== $random_string ) {
+ $options[ $option ] = $option_value;
+ }
+ }
+
+ // Add theme mods.
+ $theme_mods_option = 'theme_mods_' . get_option( 'stylesheet' );
+ $theme_mods_value = get_option( $theme_mods_option, $random_string );
+ if ( $theme_mods_value === $random_string ) {
+ return $options;
+ }
+ $this->filter_theme_mods( $theme_mods_value );
+ $options[ $theme_mods_option ] = $theme_mods_value;
+ return $options;
+ }
+
+ /**
+ * Update the options whitelist to the default one.
+ *
+ * @access public
+ */
+ public function update_options_whitelist() {
+ $this->options_whitelist = Defaults::get_options_whitelist();
+ }
+
+ /**
+ * Set the options whitelist.
+ *
+ * @access public
+ *
+ * @param array $options The new options whitelist.
+ */
+ public function set_options_whitelist( $options ) {
+ $this->options_whitelist = $options;
+ }
+
+ /**
+ * Get the options whitelist.
+ *
+ * @access public
+ *
+ * @return array The options whitelist.
+ */
+ public function get_options_whitelist() {
+ return $this->options_whitelist;
+ }
+
+ /**
+ * Update the contentless options to the defaults.
+ *
+ * @access public
+ */
+ public function update_options_contentless() {
+ $this->options_contentless = Defaults::get_options_contentless();
+ }
+
+ /**
+ * Get the contentless options.
+ *
+ * @access public
+ *
+ * @return array Array of the contentless options.
+ */
+ public function get_options_contentless() {
+ return $this->options_contentless;
+ }
+
+ /**
+ * Reject any options that aren't whitelisted or contentless.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array $args The hook parameters.
+ */
+ public function whitelist_options( $args ) {
+ // Reject non-whitelisted options.
+ if ( ! $this->is_whitelisted_option( $args[0] ) ) {
+ return false;
+ }
+
+ // Filter our weird array( false ) value for theme_mods_*.
+ if ( 'theme_mods_' === substr( $args[0], 0, 11 ) ) {
+ $this->filter_theme_mods( $args[1] );
+ if ( isset( $args[2] ) ) {
+ $this->filter_theme_mods( $args[2] );
+ }
+ }
+
+ // Set value(s) of contentless option to empty string(s).
+ if ( $this->is_contentless_option( $args[0] ) ) {
+ // Create a new array matching length of $args, containing empty strings.
+ $empty = array_fill( 0, count( $args ), '' );
+ $empty[0] = $args[0];
+ return $empty;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Whether a certain option is whitelisted for sync.
+ *
+ * @access public
+ *
+ * @param string $option Option name.
+ * @return boolean Whether the option is whitelisted.
+ */
+ public function is_whitelisted_option( $option ) {
+ return in_array( $option, $this->options_whitelist, true ) || 'theme_mods_' === substr( $option, 0, 11 );
+ }
+
+ /**
+ * Whether a certain option is a contentless one.
+ *
+ * @access private
+ *
+ * @param string $option Option name.
+ * @return boolean Whether the option is contentless.
+ */
+ private function is_contentless_option( $option ) {
+ return in_array( $option, $this->options_contentless, true );
+ }
+
+ /**
+ * Filters out falsy values from theme mod options.
+ *
+ * @access private
+ *
+ * @param array $value Option value.
+ */
+ private function filter_theme_mods( &$value ) {
+ if ( is_array( $value ) && isset( $value[0] ) ) {
+ unset( $value[0] );
+ }
+ }
+
+ /**
+ * Handle changes in the core site icon and sync them.
+ *
+ * @access public
+ */
+ public function jetpack_sync_core_icon() {
+ $url = get_site_icon_url();
+
+ require_once JETPACK__PLUGIN_DIR . 'modules/site-icon/site-icon-functions.php';
+ // If there's a core icon, maybe update the option. If not, fall back to Jetpack's.
+ if ( ! empty( $url ) && jetpack_site_icon_url() !== $url ) {
+ // This is the option that is synced with dotcom.
+ \Jetpack_Options::update_option( 'site_icon_url', $url );
+ } elseif ( empty( $url ) ) {
+ \Jetpack_Options::delete_option( 'site_icon_url' );
+ }
+ }
+
+ /**
+ * Expand all options within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array $args The hook parameters.
+ */
+ public function expand_options( $args ) {
+ if ( $args[0] ) {
+ return $this->get_all_options();
+ }
+
+ return $args;
+ }
+
+ /**
+ * Return Total number of objects.
+ *
+ * @param array $config Full Sync config.
+ *
+ * @return int total
+ */
+ public function total( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return count( Defaults::get_options_whitelist() );
+ }
+
+}
diff --git a/vendor/automattic/jetpack-sync/src/modules/class-plugins.php b/vendor/automattic/jetpack-sync/src/modules/class-plugins.php
new file mode 100644
index 0000000000000..2a0b538f9d83f
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/modules/class-plugins.php
@@ -0,0 +1,416 @@
+action_handler = $callable;
+
+ add_action( 'deleted_plugin', array( $this, 'deleted_plugin' ), 10, 2 );
+ add_action( 'activated_plugin', $callable, 10, 2 );
+ add_action( 'deactivated_plugin', $callable, 10, 2 );
+ add_action( 'delete_plugin', array( $this, 'delete_plugin' ) );
+ add_filter( 'upgrader_pre_install', array( $this, 'populate_plugins' ), 10, 1 );
+ add_action( 'upgrader_process_complete', array( $this, 'on_upgrader_completion' ), 10, 2 );
+ add_action( 'jetpack_plugin_installed', $callable, 10, 1 );
+ add_action( 'jetpack_plugin_update_failed', $callable, 10, 4 );
+ add_action( 'jetpack_plugins_updated', $callable, 10, 2 );
+ add_action( 'admin_action_update', array( $this, 'check_plugin_edit' ) );
+ add_action( 'jetpack_edited_plugin', $callable, 10, 2 );
+ add_action( 'wp_ajax_edit-theme-plugin-file', array( $this, 'plugin_edit_ajax' ), 0 );
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ add_filter( 'jetpack_sync_before_send_activated_plugin', array( $this, 'expand_plugin_data' ) );
+ add_filter( 'jetpack_sync_before_send_deactivated_plugin', array( $this, 'expand_plugin_data' ) );
+ // Note that we don't simply 'expand_plugin_data' on the 'delete_plugin' action here because the plugin file is deleted when that action finishes.
+ }
+
+ /**
+ * Fetch and populate all current plugins before upgrader installation.
+ *
+ * @access public
+ *
+ * @param bool|WP_Error $response Install response, true if successful, WP_Error if not.
+ */
+ public function populate_plugins( $response ) {
+ if ( ! function_exists( 'get_plugins' ) ) {
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
+ }
+ $this->plugins = get_plugins();
+ return $response;
+ }
+
+ /**
+ * Handler for the upgrader success finishes.
+ *
+ * @access public
+ *
+ * @param \WP_Upgrader $upgrader Upgrader instance.
+ * @param array $details Array of bulk item update data.
+ */
+ public function on_upgrader_completion( $upgrader, $details ) {
+ if ( ! isset( $details['type'] ) ) {
+ return;
+ }
+ if ( 'plugin' !== $details['type'] ) {
+ return;
+ }
+
+ if ( ! isset( $details['action'] ) ) {
+ return;
+ }
+
+ $plugins = ( isset( $details['plugins'] ) ? $details['plugins'] : null );
+ if ( empty( $plugins ) ) {
+ $plugins = ( isset( $details['plugin'] ) ? array( $details['plugin'] ) : null );
+ }
+
+ // For plugin installer.
+ if ( empty( $plugins ) && method_exists( $upgrader, 'plugin_info' ) ) {
+ $plugins = array( $upgrader->plugin_info() );
+ }
+
+ if ( empty( $plugins ) ) {
+ return; // We shouldn't be here.
+ }
+
+ switch ( $details['action'] ) {
+ case 'update':
+ $state = array(
+ 'is_autoupdate' => Jetpack_Constants::is_true( 'JETPACK_PLUGIN_AUTOUPDATE' ),
+ );
+ $errors = $this->get_errors( $upgrader->skin );
+ if ( $errors ) {
+ foreach ( $plugins as $slug ) {
+ /**
+ * Sync that a plugin update failed
+ *
+ * @since 5.8.0
+ *
+ * @module sync
+ *
+ * @param string $plugin , Plugin slug
+ * @param string Error code
+ * @param string Error message
+ */
+ do_action( 'jetpack_plugin_update_failed', $this->get_plugin_info( $slug ), $errors['code'], $errors['message'], $state );
+ }
+
+ return;
+ }
+ /**
+ * Sync that a plugin update
+ *
+ * @since 5.8.0
+ *
+ * @module sync
+ *
+ * @param array () $plugin, Plugin Data
+ */
+ do_action( 'jetpack_plugins_updated', array_map( array( $this, 'get_plugin_info' ), $plugins ), $state );
+ break;
+ case 'install':
+ }
+
+ if ( 'install' === $details['action'] ) {
+ /**
+ * Signals to the sync listener that a plugin was installed and a sync action
+ * reflecting the installation and the plugin info should be sent
+ *
+ * @since 5.8.0
+ *
+ * @module sync
+ *
+ * @param array () $plugin, Plugin Data
+ */
+ do_action( 'jetpack_plugin_installed', array_map( array( $this, 'get_plugin_info' ), $plugins ) );
+
+ return;
+ }
+ }
+
+ /**
+ * Retrieve the plugin information by a plugin slug.
+ *
+ * @access private
+ *
+ * @param string $slug Plugin slug.
+ * @return array Plugin information.
+ */
+ private function get_plugin_info( $slug ) {
+ $plugins = get_plugins(); // Get the most up to date info.
+ if ( isset( $plugins[ $slug ] ) ) {
+ return array_merge( array( 'slug' => $slug ), $plugins[ $slug ] );
+ };
+ // Try grabbing the info from before the update.
+ return isset( $this->plugins[ $slug ] ) ? array_merge( array( 'slug' => $slug ), $this->plugins[ $slug ] ) : array( 'slug' => $slug );
+ }
+
+ /**
+ * Retrieve upgrade errors.
+ *
+ * @access private
+ *
+ * @param \Automatic_Upgrader_Skin|\WP_Upgrader_Skin $skin The upgrader skin being used.
+ * @return array|boolean Error on error, false otherwise.
+ */
+ private function get_errors( $skin ) {
+ $errors = method_exists( $skin, 'get_errors' ) ? $skin->get_errors() : null;
+ if ( is_wp_error( $errors ) ) {
+ $error_code = $errors->get_error_code();
+ if ( ! empty( $error_code ) ) {
+ return array(
+ 'code' => $error_code,
+ 'message' => $errors->get_error_message(),
+ );
+ }
+ }
+
+ if ( isset( $skin->result ) ) {
+ $errors = $skin->result;
+ if ( is_wp_error( $errors ) ) {
+ return array(
+ 'code' => $errors->get_error_code(),
+ 'message' => $errors->get_error_message(),
+ );
+ }
+
+ if ( empty( $skin->result ) ) {
+ return array(
+ 'code' => 'unknown',
+ 'message' => __( 'Unknown Plugin Update Failure', 'jetpack' ),
+ );
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Handle plugin edit in the administration.
+ *
+ * @access public
+ *
+ * @todo The `admin_action_update` hook is called only for logged in users, but maybe implement nonce verification?
+ */
+ public function check_plugin_edit() {
+ $screen = get_current_screen();
+ // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ if ( 'plugin-editor' !== $screen->base || ! isset( $_POST['newcontent'] ) || ! isset( $_POST['plugin'] ) ) {
+ return;
+ }
+
+ // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ $plugin = $_POST['plugin'];
+ $plugins = get_plugins();
+ if ( ! isset( $plugins[ $plugin ] ) ) {
+ return;
+ }
+
+ /**
+ * Helps Sync log that a plugin was edited
+ *
+ * @since 4.9.0
+ *
+ * @param string $plugin, Plugin slug
+ * @param mixed $plugins[ $plugin ], Array of plugin data
+ */
+ do_action( 'jetpack_edited_plugin', $plugin, $plugins[ $plugin ] );
+ }
+
+ /**
+ * Handle plugin ajax edit in the administration.
+ *
+ * @access public
+ *
+ * @todo Update this method to use WP_Filesystem instead of fopen/fclose.
+ */
+ public function plugin_edit_ajax() {
+ // This validation is based on wp_edit_theme_plugin_file().
+ $args = wp_unslash( $_POST );
+ if ( empty( $args['file'] ) ) {
+ return;
+ }
+
+ $file = $args['file'];
+ if ( 0 !== validate_file( $file ) ) {
+ return;
+ }
+
+ if ( ! isset( $args['newcontent'] ) ) {
+ return;
+ }
+
+ if ( ! isset( $args['nonce'] ) ) {
+ return;
+ }
+
+ if ( empty( $args['plugin'] ) ) {
+ return;
+ }
+
+ $plugin = $args['plugin'];
+ if ( ! current_user_can( 'edit_plugins' ) ) {
+ return;
+ }
+
+ if ( ! wp_verify_nonce( $args['nonce'], 'edit-plugin_' . $file ) ) {
+ return;
+ }
+ $plugins = get_plugins();
+ if ( ! array_key_exists( $plugin, $plugins ) ) {
+ return;
+ }
+
+ if ( 0 !== validate_file( $file, get_plugin_files( $plugin ) ) ) {
+ return;
+ }
+
+ $real_file = WP_PLUGIN_DIR . '/' . $file;
+
+ if ( ! is_writeable( $real_file ) ) {
+ return;
+ }
+
+ // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fopen
+ $file_pointer = fopen( $real_file, 'w+' );
+ if ( false === $file_pointer ) {
+ return;
+ }
+ // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose
+ fclose( $file_pointer );
+ /**
+ * This action is documented already in this file
+ */
+ do_action( 'jetpack_edited_plugin', $plugin, $plugins[ $plugin ] );
+ }
+
+ /**
+ * Handle plugin deletion.
+ *
+ * @access public
+ *
+ * @param string $plugin_path Path to the plugin main file.
+ */
+ public function delete_plugin( $plugin_path ) {
+ $full_plugin_path = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . $plugin_path;
+
+ // Checking for file existence because some sync plugin module tests simulate plugin installation and deletion without putting file on disk.
+ if ( file_exists( $full_plugin_path ) ) {
+ $all_plugin_data = get_plugin_data( $full_plugin_path );
+ $data = array(
+ 'name' => $all_plugin_data['Name'],
+ 'version' => $all_plugin_data['Version'],
+ );
+ } else {
+ $data = array(
+ 'name' => $plugin_path,
+ 'version' => 'unknown',
+ );
+ }
+
+ $this->plugin_info[ $plugin_path ] = $data;
+ }
+
+ /**
+ * Invoked after plugin deletion.
+ *
+ * @access public
+ *
+ * @param string $plugin_path Path to the plugin main file.
+ * @param boolean $is_deleted Whether the plugin was deleted successfully.
+ */
+ public function deleted_plugin( $plugin_path, $is_deleted ) {
+ call_user_func( $this->action_handler, $plugin_path, $is_deleted, $this->plugin_info[ $plugin_path ] );
+ unset( $this->plugin_info[ $plugin_path ] );
+ }
+
+ /**
+ * Expand the plugins within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array $args The expanded hook parameters.
+ */
+ public function expand_plugin_data( $args ) {
+ $plugin_path = $args[0];
+ $plugin_data = array();
+
+ if ( ! function_exists( 'get_plugins' ) ) {
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
+ }
+ $all_plugins = get_plugins();
+ if ( isset( $all_plugins[ $plugin_path ] ) ) {
+ $all_plugin_data = $all_plugins[ $plugin_path ];
+ $plugin_data['name'] = $all_plugin_data['Name'];
+ $plugin_data['version'] = $all_plugin_data['Version'];
+ }
+
+ return array(
+ $args[0],
+ $args[1],
+ $plugin_data,
+ );
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/modules/class-posts.php b/vendor/automattic/jetpack-sync/src/modules/class-posts.php
new file mode 100644
index 0000000000000..b50c3909fdda8
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/modules/class-posts.php
@@ -0,0 +1,726 @@
+filter_post_content_and_add_links( $post );
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Initialize posts action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ $this->action_handler = $callable;
+
+ add_action( 'wp_insert_post', array( $this, 'wp_insert_post' ), 11, 3 );
+ add_action( 'wp_after_insert_post', array( $this, 'wp_after_insert_post' ), 11, 2 );
+ add_action( 'jetpack_sync_save_post', $callable, 10, 4 );
+
+ add_action( 'deleted_post', $callable, 10 );
+ add_action( 'jetpack_published_post', $callable, 10, 2 );
+ add_filter( 'jetpack_sync_before_enqueue_deleted_post', array( $this, 'filter_blacklisted_post_types_deleted' ) );
+
+ add_action( 'transition_post_status', array( $this, 'save_published' ), 10, 3 );
+ add_filter( 'jetpack_sync_before_enqueue_jetpack_sync_save_post', array( $this, 'filter_blacklisted_post_types' ) );
+
+ // Listen for meta changes.
+ $this->init_listeners_for_meta_type( 'post', $callable );
+ $this->init_meta_whitelist_handler( 'post', array( $this, 'filter_meta' ) );
+
+ add_action( 'jetpack_daily_akismet_meta_cleanup_before', array( $this, 'daily_akismet_meta_cleanup_before' ) );
+ add_action( 'jetpack_daily_akismet_meta_cleanup_after', array( $this, 'daily_akismet_meta_cleanup_after' ) );
+ add_action( 'jetpack_post_meta_batch_delete', $callable, 10, 2 );
+ }
+
+ /**
+ * Before Akismet's daily cleanup of spam detection metadata.
+ *
+ * @access public
+ *
+ * @param array $feedback_ids IDs of feedback posts.
+ */
+ public function daily_akismet_meta_cleanup_before( $feedback_ids ) {
+ remove_action( 'deleted_post_meta', $this->action_handler );
+
+ if ( ! is_array( $feedback_ids ) || count( $feedback_ids ) < 1 ) {
+ return;
+ }
+
+ $ids_chunks = array_chunk( $feedback_ids, 100, false );
+ foreach ( $ids_chunks as $chunk ) {
+ /**
+ * Used for syncing deletion of batch post meta
+ *
+ * @since 6.1.0
+ *
+ * @module sync
+ *
+ * @param array $feedback_ids feedback post IDs
+ * @param string $meta_key to be deleted
+ */
+ do_action( 'jetpack_post_meta_batch_delete', $chunk, '_feedback_akismet_values' );
+ }
+ }
+
+ /**
+ * After Akismet's daily cleanup of spam detection metadata.
+ *
+ * @access public
+ *
+ * @param array $feedback_ids IDs of feedback posts.
+ */
+ public function daily_akismet_meta_cleanup_after( $feedback_ids ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ add_action( 'deleted_post_meta', $this->action_handler );
+ }
+
+ /**
+ * Initialize posts action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ add_action( 'jetpack_full_sync_posts', $callable ); // Also sends post meta.
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ add_filter( 'jetpack_sync_before_send_jetpack_sync_save_post', array( $this, 'expand_jetpack_sync_save_post' ) );
+
+ // Full sync.
+ add_filter( 'jetpack_sync_before_send_jetpack_full_sync_posts', array( $this, 'expand_post_ids' ) );
+ }
+
+ /**
+ * Enqueue the posts actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
+ global $wpdb;
+
+ return $this->enqueue_all_ids_as_action( 'jetpack_full_sync_posts', $wpdb->posts, 'ID', $this->get_where_sql( $config ), $max_items_to_enqueue, $state );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @todo Use $wpdb->prepare for the SQL query.
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return array Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) {
+ global $wpdb;
+
+ $query = "SELECT count(*) FROM $wpdb->posts WHERE " . $this->get_where_sql( $config );
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $count = $wpdb->get_var( $query );
+
+ return (int) ceil( $count / self::ARRAY_CHUNK_SIZE );
+ }
+
+ /**
+ * Retrieve the WHERE SQL clause based on the module config.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return string WHERE SQL clause, or `null` if no comments are specified in the module config.
+ */
+ public function get_where_sql( $config ) {
+ $where_sql = Settings::get_blacklisted_post_types_sql();
+
+ // Config is a list of post IDs to sync.
+ if ( is_array( $config ) ) {
+ $where_sql .= ' AND ID IN (' . implode( ',', array_map( 'intval', $config ) ) . ')';
+ }
+
+ return $where_sql;
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_posts' );
+ }
+
+ /**
+ * Process content before send.
+ *
+ * @param array $args Arguments of the `wp_insert_post` hook.
+ *
+ * @return array
+ */
+ public function expand_jetpack_sync_save_post( $args ) {
+ list( $post_id, $post, $update, $previous_state ) = $args;
+ return array( $post_id, $this->filter_post_content_and_add_links( $post ), $update, $previous_state );
+ }
+
+ /**
+ * Filter all blacklisted post types.
+ *
+ * @param array $args Hook arguments.
+ * @return array|false Hook arguments, or false if the post type is a blacklisted one.
+ */
+ public function filter_blacklisted_post_types_deleted( $args ) {
+
+ // deleted_post is called after the SQL delete but before cache cleanup.
+ // There is the potential we can't detect post_type at this point.
+ if ( ! $this->is_post_type_allowed( $args[0] ) ) {
+ return false;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Filter all blacklisted post types.
+ *
+ * @param array $args Hook arguments.
+ * @return array|false Hook arguments, or false if the post type is a blacklisted one.
+ */
+ public function filter_blacklisted_post_types( $args ) {
+ $post = $args[1];
+
+ if ( in_array( $post->post_type, Settings::get_setting( 'post_types_blacklist' ), true ) ) {
+ return false;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Filter all meta that is not blacklisted, or is stored for a disallowed post type.
+ *
+ * @param array $args Hook arguments.
+ * @return array|false Hook arguments, or false if meta was filtered.
+ */
+ public function filter_meta( $args ) {
+ if ( $this->is_post_type_allowed( $args[1] ) && $this->is_whitelisted_post_meta( $args[2] ) ) {
+ return $args;
+ }
+
+ return false;
+ }
+
+ /**
+ * Whether a post meta key is whitelisted.
+ *
+ * @param string $meta_key Meta key.
+ * @return boolean Whether the post meta key is whitelisted.
+ */
+ public function is_whitelisted_post_meta( $meta_key ) {
+ // The _wpas_skip_ meta key is used by Publicize.
+ return in_array( $meta_key, Settings::get_setting( 'post_meta_whitelist' ), true ) || wp_startswith( $meta_key, '_wpas_skip_' );
+ }
+
+ /**
+ * Whether a post type is allowed.
+ * A post type will be disallowed if it's present in the post type blacklist.
+ *
+ * @param int $post_id ID of the post.
+ * @return boolean Whether the post type is allowed.
+ */
+ public function is_post_type_allowed( $post_id ) {
+ $post = get_post( (int) $post_id );
+
+ if ( isset( $post->post_type ) ) {
+ return ! in_array( $post->post_type, Settings::get_setting( 'post_types_blacklist' ), true );
+ }
+ return false;
+ }
+
+ /**
+ * Remove the embed shortcode.
+ *
+ * @global $wp_embed
+ */
+ public function remove_embed() {
+ global $wp_embed;
+ remove_filter( 'the_content', array( $wp_embed, 'run_shortcode' ), 8 );
+ // remove the embed shortcode since we would do the part later.
+ remove_shortcode( 'embed' );
+ // Attempts to embed all URLs in a post.
+ remove_filter( 'the_content', array( $wp_embed, 'autoembed' ), 8 );
+ }
+
+ /**
+ * Add the embed shortcode.
+ *
+ * @global $wp_embed
+ */
+ public function add_embed() {
+ global $wp_embed;
+ add_filter( 'the_content', array( $wp_embed, 'run_shortcode' ), 8 );
+ // Shortcode placeholder for strip_shortcodes().
+ add_shortcode( 'embed', '__return_false' );
+ // Attempts to embed all URLs in a post.
+ add_filter( 'the_content', array( $wp_embed, 'autoembed' ), 8 );
+ }
+
+ /**
+ * Expands wp_insert_post to include filtered content
+ *
+ * @param \WP_Post $post_object Post object.
+ */
+ public function filter_post_content_and_add_links( $post_object ) {
+ global $post;
+ // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+ $post = $post_object;
+
+ // Return non existant post.
+ $post_type = get_post_type_object( $post->post_type );
+ if ( empty( $post_type ) || ! is_object( $post_type ) ) {
+ $non_existant_post = new \stdClass();
+ $non_existant_post->ID = $post->ID;
+ $non_existant_post->post_modified = $post->post_modified;
+ $non_existant_post->post_modified_gmt = $post->post_modified_gmt;
+ $non_existant_post->post_status = 'jetpack_sync_non_registered_post_type';
+ $non_existant_post->post_type = $post->post_type;
+
+ return $non_existant_post;
+ }
+ /**
+ * Filters whether to prevent sending post data to .com
+ *
+ * Passing true to the filter will prevent the post data from being sent
+ * to the WordPress.com.
+ * Instead we pass data that will still enable us to do a checksum against the
+ * Jetpacks data but will prevent us from displaying the data on in the API as well as
+ * other services.
+ *
+ * @since 4.2.0
+ *
+ * @param boolean false prevent post data from being synced to WordPress.com
+ * @param mixed $post \WP_Post object
+ */
+ if ( apply_filters( 'jetpack_sync_prevent_sending_post_data', false, $post ) ) {
+ // We only send the bare necessary object to be able to create a checksum.
+ $blocked_post = new \stdClass();
+ $blocked_post->ID = $post->ID;
+ $blocked_post->post_modified = $post->post_modified;
+ $blocked_post->post_modified_gmt = $post->post_modified_gmt;
+ $blocked_post->post_status = 'jetpack_sync_blocked';
+ $blocked_post->post_type = $post->post_type;
+
+ return $blocked_post;
+ }
+
+ // lets not do oembed just yet.
+ $this->remove_embed();
+
+ if ( 0 < strlen( $post->post_password ) ) {
+ $post->post_password = 'auto-' . wp_generate_password( 10, false );
+ }
+
+ /** This filter is already documented in core. wp-includes/post-template.php */
+ if ( Settings::get_setting( 'render_filtered_content' ) && $post_type->public ) {
+ global $shortcode_tags;
+ /**
+ * Filter prevents some shortcodes from expanding.
+ *
+ * Since we can can expand some type of shortcode better on the .com side and make the
+ * expansion more relevant to contexts. For example [galleries] and subscription emails
+ *
+ * @since 4.5.0
+ *
+ * @param array of shortcode tags to remove.
+ */
+ $shortcodes_to_remove = apply_filters(
+ 'jetpack_sync_do_not_expand_shortcodes',
+ array(
+ 'gallery',
+ 'slideshow',
+ )
+ );
+ $removed_shortcode_callbacks = array();
+ foreach ( $shortcodes_to_remove as $shortcode ) {
+ if ( isset( $shortcode_tags[ $shortcode ] ) ) {
+ $removed_shortcode_callbacks[ $shortcode ] = $shortcode_tags[ $shortcode ];
+ }
+ }
+
+ array_map( 'remove_shortcode', array_keys( $removed_shortcode_callbacks ) );
+
+ $post->post_content_filtered = apply_filters( 'the_content', $post->post_content );
+ $post->post_excerpt_filtered = apply_filters( 'the_excerpt', $post->post_excerpt );
+
+ foreach ( $removed_shortcode_callbacks as $shortcode => $callback ) {
+ add_shortcode( $shortcode, $callback );
+ }
+ }
+
+ $this->add_embed();
+
+ if ( has_post_thumbnail( $post->ID ) ) {
+ $image_attributes = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'full' );
+ if ( is_array( $image_attributes ) && isset( $image_attributes[0] ) ) {
+ $post->featured_image = $image_attributes[0];
+ }
+ }
+
+ $post->permalink = get_permalink( $post->ID );
+ $post->shortlink = wp_get_shortlink( $post->ID );
+
+ if ( function_exists( 'amp_get_permalink' ) ) {
+ $post->amp_permalink = amp_get_permalink( $post->ID );
+ }
+
+ return $post;
+ }
+
+ /**
+ * Handle transition from another post status to a published one.
+ *
+ * @param string $new_status New post status.
+ * @param string $old_status Old post status.
+ * @param \WP_Post $post Post object.
+ */
+ public function save_published( $new_status, $old_status, $post ) {
+ if ( 'publish' === $new_status && 'publish' !== $old_status ) {
+ $this->just_published[ $post->ID ] = true;
+ }
+
+ $this->previous_status[ $post->ID ] = $old_status;
+ }
+
+ /**
+ * When publishing or updating a post, the Gutenberg editor sends two requests:
+ * 1. sent to WP REST API endpoint `wp-json/wp/v2/posts/$id`
+ * 2. sent to wp-admin/post.php `?post=$id&action=edit&classic-editor=1&meta_box=1`
+ *
+ * The 2nd request is to update post meta, which is not supported on WP REST API.
+ * When syncing post data, we will include if this was a meta box update.
+ *
+ * @todo Implement nonce verification.
+ *
+ * @return boolean Whether this is a Gutenberg meta box update.
+ */
+ public function is_gutenberg_meta_box_update() {
+ // phpcs:disable WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
+ return (
+ isset( $_POST['action'], $_GET['classic-editor'], $_GET['meta_box'] ) &&
+ 'editpost' === $_POST['action'] &&
+ '1' === $_GET['classic-editor'] &&
+ '1' === $_GET['meta_box']
+ // phpcs:enable WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
+ );
+ }
+
+ /**
+ * Handler for the wp_insert_post hook.
+ * Called upon creation of a new post.
+ *
+ * @param int $post_ID Post ID.
+ * @param \WP_Post $post Post object.
+ * @param boolean $update Whether this is an existing post being updated or not.
+ */
+ public function wp_insert_post( $post_ID, $post = null, $update = null ) {
+ if ( ! is_numeric( $post_ID ) || is_null( $post ) ) {
+ return;
+ }
+
+ // Workaround for https://github.com/woocommerce/woocommerce/issues/18007.
+ if ( $post && 'shop_order' === $post->post_type ) {
+ $post = get_post( $post_ID );
+ }
+
+ $previous_status = isset( $this->previous_status[ $post_ID ] ) ? $this->previous_status[ $post_ID ] : self::DEFAULT_PREVIOUS_STATE;
+
+ $just_published = isset( $this->just_published[ $post_ID ] ) ? $this->just_published[ $post_ID ] : false;
+
+ $state = array(
+ 'is_auto_save' => (bool) Jetpack_Constants::get_constant( 'DOING_AUTOSAVE' ),
+ 'previous_status' => $previous_status,
+ 'just_published' => $just_published,
+ 'is_gutenberg_meta_box_update' => $this->is_gutenberg_meta_box_update(),
+ );
+ /**
+ * Filter that is used to add to the post flags ( meta data ) when a post gets published
+ *
+ * @since 5.8.0
+ *
+ * @param int $post_ID the post ID
+ * @param mixed $post \WP_Post object
+ * @param bool $update Whether this is an existing post being updated or not.
+ * @param mixed $state state
+ *
+ * @module sync
+ */
+ do_action( 'jetpack_sync_save_post', $post_ID, $post, $update, $state );
+ unset( $this->previous_status[ $post_ID ] );
+
+ /*
+ * WP 5.6 introduced the wp_after_insert_post hook that triggers when
+ * the post, meta and terms has been updated. We are migrating send_published
+ * function to this hook to ensure we have all data for WP.com functionality.
+ * @todo: remove full if statement when WordPress 5.6 is the minimum required version.
+ */
+ if ( ! function_exists( 'wp_after_insert_post' ) ) {
+ $this->send_published( $post_ID, $post );
+ }
+ }
+
+ /**
+ * Handler for the wp_after_insert_post hook.
+ * Called after creation/update of a new post.
+ *
+ * @param int $post_ID Post ID.
+ * @param \WP_Post $post Post object.
+ **/
+ public function wp_after_insert_post( $post_ID, $post ) {
+ if ( ! is_numeric( $post_ID ) || is_null( $post ) ) {
+ return;
+ }
+
+ // Workaround for https://github.com/woocommerce/woocommerce/issues/18007.
+ if ( $post && 'shop_order' === $post->post_type ) {
+ $post = get_post( $post_ID );
+ }
+
+ $this->send_published( $post_ID, $post );
+ }
+
+ /**
+ * Send a published post for sync.
+ *
+ * @param int $post_ID Post ID.
+ * @param \WP_Post $post Post object.
+ */
+ public function send_published( $post_ID, $post ) {
+ if ( ! isset( $this->just_published[ $post_ID ] ) ) {
+ return;
+ }
+
+ // Post revisions cause race conditions where this send_published add the action before the actual post gets synced.
+ if ( wp_is_post_autosave( $post ) || wp_is_post_revision( $post ) ) {
+ return;
+ }
+
+ $post_flags = array(
+ 'post_type' => $post->post_type,
+ );
+
+ $author_user_object = get_user_by( 'id', $post->post_author );
+ if ( $author_user_object ) {
+ $roles = new Roles();
+
+ $post_flags['author'] = array(
+ 'id' => $post->post_author,
+ 'wpcom_user_id' => get_user_meta( $post->post_author, 'wpcom_user_id', true ),
+ 'display_name' => $author_user_object->display_name,
+ 'email' => $author_user_object->user_email,
+ 'translated_role' => $roles->translate_user_to_role( $author_user_object ),
+ );
+ }
+
+ /**
+ * Filter that is used to add to the post flags ( meta data ) when a post gets published
+ *
+ * @since 4.4.0
+ *
+ * @param mixed array post flags that are added to the post
+ * @param mixed $post \WP_Post object
+ */
+ $flags = apply_filters( 'jetpack_published_post_flags', $post_flags, $post );
+
+ // Only Send Pulished Post event if post_type is not blacklisted.
+ if ( ! in_array( $post->post_type, Settings::get_setting( 'post_types_blacklist' ), true ) ) {
+ /**
+ * Action that gets synced when a post type gets published.
+ *
+ * @since 4.4.0
+ *
+ * @param int $post_ID
+ * @param mixed array $flags post flags that are added to the post
+ */
+ do_action( 'jetpack_published_post', $post_ID, $flags );
+ }
+ unset( $this->just_published[ $post_ID ] );
+
+ /**
+ * Send additional sync action for Activity Log when post is a Customizer publish
+ */
+ if ( 'customize_changeset' === $post->post_type ) {
+ $post_content = json_decode( $post->post_content, true );
+ foreach ( $post_content as $key => $value ) {
+ // Skip if it isn't a widget.
+ if ( 'widget_' !== substr( $key, 0, strlen( 'widget_' ) ) ) {
+ continue;
+ }
+ // Change key from "widget_archives[2]" to "archives-2".
+ $key = str_replace( 'widget_', '', $key );
+ $key = str_replace( '[', '-', $key );
+ $key = str_replace( ']', '', $key );
+
+ global $wp_registered_widgets;
+ if ( isset( $wp_registered_widgets[ $key ] ) ) {
+ $widget_data = array(
+ 'name' => $wp_registered_widgets[ $key ]['name'],
+ 'id' => $key,
+ 'title' => $value['value']['title'],
+ );
+ do_action( 'jetpack_widget_edited', $widget_data );
+ }
+ }
+ }
+ }
+
+ /**
+ * Expand post IDs to post objects within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array $args The expanded hook parameters.
+ */
+ public function expand_post_ids( $args ) {
+ list( $post_ids, $previous_interval_end) = $args;
+
+ $posts = array_filter( array_map( array( 'WP_Post', 'get_instance' ), $post_ids ) );
+ $posts = array_map( array( $this, 'filter_post_content_and_add_links' ), $posts );
+ $posts = array_values( $posts ); // Reindex in case posts were deleted.
+
+ return array(
+ $posts,
+ $this->get_metadata( $post_ids, 'post', Settings::get_setting( 'post_meta_whitelist' ) ),
+ $this->get_term_relationships( $post_ids ),
+ $previous_interval_end,
+ );
+ }
+
+ /**
+ * Gets a list of minimum and maximum object ids for each batch based on the given batch size.
+ *
+ * @access public
+ *
+ * @param int $batch_size The batch size for objects.
+ * @param string|bool $where_sql The sql where clause minus 'WHERE', or false if no where clause is needed.
+ *
+ * @return array|bool An array of min and max ids for each batch. FALSE if no table can be found.
+ */
+ public function get_min_max_object_ids_for_batches( $batch_size, $where_sql = false ) {
+ return parent::get_min_max_object_ids_for_batches( $batch_size, $this->get_where_sql( $where_sql ) );
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/modules/class-protect.php b/vendor/automattic/jetpack-sync/src/modules/class-protect.php
new file mode 100644
index 0000000000000..ebd62ff8f608c
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/modules/class-protect.php
@@ -0,0 +1,53 @@
+has_login_ability() && ! Jetpack_Constants::is_true( 'XMLRPC_REQUEST' ) ) {
+ do_action( 'jetpack_valid_failed_login_attempt', $failed_attempt );
+ }
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/modules/class-stats.php b/vendor/automattic/jetpack-sync/src/modules/class-stats.php
new file mode 100644
index 0000000000000..bbd4cae605028
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/modules/class-stats.php
@@ -0,0 +1,66 @@
+ self::MAX_INT,
+ 'term_taxonomy_id' => self::MAX_INT,
+ );
+
+ while ( $limit > 0 ) {
+ /*
+ * SELECT object_id, term_taxonomy_id
+ * FROM $wpdb->term_relationships
+ * WHERE ( object_id = 11 AND term_taxonomy_id < 14 ) OR ( object_id < 11 )
+ * ORDER BY object_id DESC, term_taxonomy_id DESC LIMIT 1000
+ */
+ $objects = $wpdb->get_results( $wpdb->prepare( "SELECT object_id, term_taxonomy_id FROM $wpdb->term_relationships WHERE ( object_id = %d AND term_taxonomy_id < %d ) OR ( object_id < %d ) ORDER BY object_id DESC, term_taxonomy_id DESC LIMIT %d", $last_object_enqueued['object_id'], $last_object_enqueued['term_taxonomy_id'], $last_object_enqueued['object_id'], $limit ), ARRAY_A );
+ // Request term relationships in groups of N for efficiency.
+ $objects_count = count( $objects );
+ if ( ! count( $objects ) ) {
+ return array( $items_enqueued_count, true );
+ }
+ $items = array_chunk( $objects, $term_relationships_full_sync_item_size );
+ $last_object_enqueued = $this->bulk_enqueue_full_sync_term_relationships( $items, $last_object_enqueued );
+ $items_enqueued_count += count( $items );
+ $limit = min( $limit - $objects_count, self::QUERY_LIMIT );
+ }
+
+ // We need to do this extra check in case $max_items_to_enqueue * $term_relationships_full_sync_item_size == relationships objects left.
+ $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships WHERE ( object_id = %d AND term_taxonomy_id < %d ) OR ( object_id < %d ) ORDER BY object_id DESC, term_taxonomy_id DESC LIMIT %d", $last_object_enqueued['object_id'], $last_object_enqueued['term_taxonomy_id'], $last_object_enqueued['object_id'], 1 ) );
+ if ( 0 === (int) $count ) {
+ return array( $items_enqueued_count, true );
+ }
+
+ return array( $items_enqueued_count, $last_object_enqueued );
+ }
+
+ /**
+ * Return the initial last sent object.
+ *
+ * @return string|array initial status.
+ */
+ public function get_initial_last_sent() {
+ return array(
+ 'object_id' => self::MAX_INT,
+ 'term_taxonomy_id' => self::MAX_INT,
+ );
+ }
+
+ /**
+ * Given the Module Full Sync Configuration and Status return the next chunk of items to send.
+ *
+ * @param array $config This module Full Sync configuration.
+ * @param array $status This module Full Sync status.
+ * @param int $chunk_size Chunk size.
+ *
+ * @return array|object|null
+ */
+ public function get_next_chunk( $config, $status, $chunk_size ) {
+ global $wpdb;
+
+ return $wpdb->get_results(
+ $wpdb->prepare(
+ "SELECT object_id, term_taxonomy_id
+ FROM $wpdb->term_relationships
+ WHERE ( object_id = %d AND term_taxonomy_id < %d ) OR ( object_id < %d )
+ ORDER BY object_id DESC, term_taxonomy_id
+ DESC LIMIT %d",
+ $status['last_sent']['object_id'],
+ $status['last_sent']['term_taxonomy_id'],
+ $status['last_sent']['object_id'],
+ $chunk_size
+ ),
+ ARRAY_A
+ );
+ }
+
+ /**
+ *
+ * Enqueue all $items within `jetpack_full_sync_term_relationships` actions.
+ *
+ * @param array $items Groups of objects to sync.
+ * @param array $previous_interval_end Last item enqueued.
+ *
+ * @return array Last enqueued object.
+ */
+ public function bulk_enqueue_full_sync_term_relationships( $items, $previous_interval_end ) {
+ $listener = Listener::get_instance();
+ $items_with_previous_interval_end = $this->get_chunks_with_preceding_end( $items, $previous_interval_end );
+ $listener->bulk_enqueue_full_sync_actions( 'jetpack_full_sync_term_relationships', $items_with_previous_interval_end );
+ $last_item = end( $items );
+ return end( $last_item );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return int Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ global $wpdb;
+
+ $query = "SELECT COUNT(*) FROM $wpdb->term_relationships";
+
+ // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
+ $count = $wpdb->get_var( $query );
+
+ return (int) ceil( $count / Settings::get_setting( 'term_relationships_full_sync_item_size' ) );
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_term_relationships' );
+ }
+
+ /**
+ * Expand the term relationships within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array $args The expanded hook parameters.
+ */
+ public function expand_term_relationships( $args ) {
+ list( $term_relationships, $previous_end ) = $args;
+
+ return array(
+ 'term_relationships' => $term_relationships,
+ 'previous_end' => $previous_end,
+ );
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/modules/class-terms.php b/vendor/automattic/jetpack-sync/src/modules/class-terms.php
new file mode 100644
index 0000000000000..5989239e3c7bf
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/modules/class-terms.php
@@ -0,0 +1,312 @@
+get_error_code() === 'invalid_taxonomy' ) {
+ // Fetch raw term.
+ $columns = implode( ', ', array_unique( array_merge( Defaults::$default_term_checksum_columns, array( 'term_group' ) ) ) );
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ $object = $wpdb->get_row( $wpdb->prepare( "SELECT $columns FROM $wpdb->terms WHERE term_id = %d", $id ) );
+ }
+ }
+
+ if ( 'term_taxonomy' === $object_type ) {
+ $columns = implode( ', ', array_unique( array_merge( Defaults::$default_term_taxonomy_checksum_columns, array( 'description' ) ) ) );
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ $object = $wpdb->get_row( $wpdb->prepare( "SELECT $columns FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %d", $id ) );
+ }
+
+ if ( 'term_relationships' === $object_type ) {
+ $columns = implode( ', ', Defaults::$default_term_relationships_checksum_columns );
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ $objects = $wpdb->get_results( $wpdb->prepare( "SELECT $columns FROM $wpdb->term_relationships WHERE object_id = %d", $id ) );
+ $object = (object) array(
+ 'object_id' => $id,
+ 'relationships' => array_map( array( $this, 'expand_terms_for_relationship' ), $objects ),
+ );
+ }
+
+ return $object ? $object : false;
+ }
+
+ /**
+ * Initialize terms action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ add_action( 'created_term', array( $this, 'save_term_handler' ), 10, 3 );
+ add_action( 'edited_term', array( $this, 'save_term_handler' ), 10, 3 );
+ add_action( 'jetpack_sync_save_term', $callable );
+ add_action( 'jetpack_sync_add_term', $callable );
+ add_action( 'delete_term', $callable, 10, 4 );
+ add_action( 'set_object_terms', $callable, 10, 6 );
+ add_action( 'deleted_term_relationships', $callable, 10, 2 );
+ add_filter( 'jetpack_sync_before_enqueue_set_object_terms', array( $this, 'filter_set_object_terms_no_update' ) );
+ add_filter( 'jetpack_sync_before_enqueue_jetpack_sync_save_term', array( $this, 'filter_blacklisted_taxonomies' ) );
+ add_filter( 'jetpack_sync_before_enqueue_jetpack_sync_add_term', array( $this, 'filter_blacklisted_taxonomies' ) );
+ }
+
+ /**
+ * Initialize terms action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ add_action( 'jetpack_full_sync_terms', $callable, 10, 2 );
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ // Full sync.
+ add_filter( 'jetpack_sync_before_send_jetpack_full_sync_terms', array( $this, 'expand_term_taxonomy_id' ) );
+ }
+
+ /**
+ * Enqueue the terms actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
+ global $wpdb;
+ return $this->enqueue_all_ids_as_action( 'jetpack_full_sync_terms', $wpdb->term_taxonomy, 'term_taxonomy_id', $this->get_where_sql( $config ), $max_items_to_enqueue, $state );
+ }
+
+ /**
+ * Retrieve the WHERE SQL clause based on the module config.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return string WHERE SQL clause, or `null` if no comments are specified in the module config.
+ */
+ public function get_where_sql( $config ) {
+ $where_sql = Settings::get_blacklisted_taxonomies_sql();
+
+ if ( is_array( $config ) ) {
+ $where_sql .= ' AND term_taxonomy_id IN (' . implode( ',', array_map( 'intval', $config ) ) . ')';
+ }
+
+ return $where_sql;
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return int Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) {
+ global $wpdb;
+
+ $query = "SELECT count(*) FROM $wpdb->term_taxonomy";
+
+ $where_sql = $this->get_where_sql( $config );
+ if ( $where_sql ) {
+ $query .= ' WHERE ' . $where_sql;
+ }
+
+ // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
+ $count = $wpdb->get_var( $query );
+
+ return (int) ceil( $count / self::ARRAY_CHUNK_SIZE );
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_terms' );
+ }
+
+ /**
+ * Handler for creating and updating terms.
+ *
+ * @access public
+ *
+ * @param int $term_id Term ID.
+ * @param int $tt_id Term taxonomy ID.
+ * @param string $taxonomy Taxonomy slug.
+ */
+ public function save_term_handler( $term_id, $tt_id, $taxonomy ) {
+ if ( class_exists( '\\WP_Term' ) ) {
+ $term_object = \WP_Term::get_instance( $term_id, $taxonomy );
+ } else {
+ $term_object = get_term_by( 'id', $term_id, $taxonomy );
+ }
+
+ $current_filter = current_filter();
+
+ if ( 'created_term' === $current_filter ) {
+ /**
+ * Fires when the client needs to add a new term
+ *
+ * @since 5.0.0
+ *
+ * @param object the Term object
+ */
+ do_action( 'jetpack_sync_add_term', $term_object );
+ return;
+ }
+
+ /**
+ * Fires when the client needs to update a term
+ *
+ * @since 4.2.0
+ *
+ * @param object the Term object
+ */
+ do_action( 'jetpack_sync_save_term', $term_object );
+ }
+
+ /**
+ * Filter blacklisted taxonomies.
+ *
+ * @access public
+ *
+ * @param array $args Hook args.
+ * @return array|boolean False if not whitelisted, the original hook args otherwise.
+ */
+ public function filter_blacklisted_taxonomies( $args ) {
+ $term = $args[0];
+
+ if ( in_array( $term->taxonomy, Settings::get_setting( 'taxonomies_blacklist' ), true ) ) {
+ return false;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Filter out set_object_terms actions where the terms have not changed.
+ *
+ * @param array $args Hook args.
+ * @return array|boolean False if no change in terms, the original hook args otherwise.
+ */
+ public function filter_set_object_terms_no_update( $args ) {
+ // There is potential for other plugins to modify args, therefore lets validate # of and types.
+ // $args[2] is $tt_ids, $args[5] is $old_tt_ids see wp-includes/taxonomy.php L2740.
+ if ( 6 === count( $args ) && is_array( $args[2] ) && is_array( $args[5] ) ) {
+ if ( empty( array_diff( $args[2], $args[5] ) ) && empty( array_diff( $args[5], $args[2] ) ) ) {
+ return false;
+ }
+ }
+ return $args;
+ }
+
+ /**
+ * Expand the term taxonomy IDs to terms within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array $args The expanded hook parameters.
+ */
+ public function expand_term_taxonomy_id( $args ) {
+ list( $term_taxonomy_ids, $previous_end ) = $args;
+
+ return array(
+ 'terms' => get_terms(
+ array(
+ 'hide_empty' => false,
+ 'term_taxonomy_id' => $term_taxonomy_ids,
+ 'orderby' => 'term_taxonomy_id',
+ 'order' => 'DESC',
+ )
+ ),
+ 'previous_end' => $previous_end,
+ );
+ }
+
+ /**
+ * Gets a term object based on a given row from the term_relationships database table.
+ *
+ * @access public
+ *
+ * @param object $relationship A row object from the term_relationships table.
+ * @return object|bool A term object, or false if term taxonomy doesn't exist.
+ */
+ public function expand_terms_for_relationship( $relationship ) {
+ return get_term_by( 'term_taxonomy_id', $relationship->term_taxonomy_id );
+ }
+
+}
diff --git a/vendor/automattic/jetpack-sync/src/modules/class-themes.php b/vendor/automattic/jetpack-sync/src/modules/class-themes.php
new file mode 100644
index 0000000000000..458bbb6e836c5
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/modules/class-themes.php
@@ -0,0 +1,845 @@
+ $widget_object->name,
+ 'id' => $widget_object->id,
+ 'title' => isset( $new_instance['title'] ) ? $new_instance['title'] : '',
+ );
+ /**
+ * Trigger action to alert $callable sync listener that a widget was edited.
+ *
+ * @since 5.0.0
+ *
+ * @param string $widget_name , Name of edited widget
+ */
+ do_action( 'jetpack_widget_edited', $widget );
+
+ return $instance;
+ }
+
+ /**
+ * Sync handler for network allowed themes change.
+ *
+ * @access public
+ *
+ * @param string $option Name of the network option.
+ * @param mixed $value Current value of the network option.
+ * @param mixed $old_value Old value of the network option.
+ * @param int $network_id ID of the network.
+ */
+ public function sync_network_allowed_themes_change( $option, $value, $old_value, $network_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ $all_enabled_theme_slugs = array_keys( $value );
+
+ if ( count( $old_value ) > count( $value ) ) {
+
+ // Suppress jetpack_network_disabled_themes sync action when theme is deleted.
+ $delete_theme_call = $this->get_delete_theme_call();
+ if ( ! empty( $delete_theme_call ) ) {
+ return;
+ }
+
+ $newly_disabled_theme_names = array_keys( array_diff_key( $old_value, $value ) );
+ $newly_disabled_themes = $this->get_theme_details_for_slugs( $newly_disabled_theme_names );
+ /**
+ * Trigger action to alert $callable sync listener that network themes were disabled.
+ *
+ * @since 5.0.0
+ *
+ * @param mixed $newly_disabled_themes, Array of info about network disabled themes
+ * @param mixed $all_enabled_theme_slugs, Array of slugs of all enabled themes
+ */
+ do_action( 'jetpack_network_disabled_themes', $newly_disabled_themes, $all_enabled_theme_slugs );
+ return;
+ }
+
+ $newly_enabled_theme_names = array_keys( array_diff_key( $value, $old_value ) );
+ $newly_enabled_themes = $this->get_theme_details_for_slugs( $newly_enabled_theme_names );
+ /**
+ * Trigger action to alert $callable sync listener that network themes were enabled
+ *
+ * @since 5.0.0
+ *
+ * @param mixed $newly_enabled_themes , Array of info about network enabled themes
+ * @param mixed $all_enabled_theme_slugs, Array of slugs of all enabled themes
+ */
+ do_action( 'jetpack_network_enabled_themes', $newly_enabled_themes, $all_enabled_theme_slugs );
+ }
+
+ /**
+ * Retrieve details for one or more themes by their slugs.
+ *
+ * @access private
+ *
+ * @param array $theme_slugs Theme slugs.
+ * @return array Details for the themes.
+ */
+ private function get_theme_details_for_slugs( $theme_slugs ) {
+ $theme_data = array();
+ foreach ( $theme_slugs as $slug ) {
+ $theme = wp_get_theme( $slug );
+ $theme_data[ $slug ] = array(
+ 'name' => $theme->get( 'Name' ),
+ 'version' => $theme->get( 'Version' ),
+ 'uri' => $theme->get( 'ThemeURI' ),
+ 'slug' => $slug,
+ );
+ }
+ return $theme_data;
+ }
+
+ /**
+ * Detect a theme edit during a redirect.
+ *
+ * @access public
+ *
+ * @param string $redirect_url Redirect URL.
+ * @return string Redirect URL.
+ */
+ public function detect_theme_edit( $redirect_url ) {
+ $url = wp_parse_url( admin_url( $redirect_url ) );
+ $theme_editor_url = wp_parse_url( admin_url( 'theme-editor.php' ) );
+
+ if ( $theme_editor_url['path'] !== $url['path'] ) {
+ return $redirect_url;
+ }
+
+ $query_params = array();
+ wp_parse_str( $url['query'], $query_params );
+ if (
+ ! isset( $_POST['newcontent'] ) ||
+ ! isset( $query_params['file'] ) ||
+ ! isset( $query_params['theme'] ) ||
+ ! isset( $query_params['updated'] )
+ ) {
+ return $redirect_url;
+ }
+ $theme = wp_get_theme( $query_params['theme'] );
+ $theme_data = array(
+ 'name' => $theme->get( 'Name' ),
+ 'version' => $theme->get( 'Version' ),
+ 'uri' => $theme->get( 'ThemeURI' ),
+ );
+
+ /**
+ * Trigger action to alert $callable sync listener that a theme was edited.
+ *
+ * @since 5.0.0
+ *
+ * @param string $query_params['theme'], Slug of edited theme
+ * @param string $theme_data, Information about edited them
+ */
+ do_action( 'jetpack_edited_theme', $query_params['theme'], $theme_data );
+
+ return $redirect_url;
+ }
+
+ /**
+ * Handler for AJAX theme editing.
+ *
+ * @todo Refactor to use WP_Filesystem instead of fopen()/fclose().
+ */
+ public function theme_edit_ajax() {
+ $args = wp_unslash( $_POST );
+
+ if ( empty( $args['theme'] ) ) {
+ return;
+ }
+
+ if ( empty( $args['file'] ) ) {
+ return;
+ }
+ $file = $args['file'];
+ if ( 0 !== validate_file( $file ) ) {
+ return;
+ }
+
+ if ( ! isset( $args['newcontent'] ) ) {
+ return;
+ }
+
+ if ( ! isset( $args['nonce'] ) ) {
+ return;
+ }
+
+ $stylesheet = $args['theme'];
+ if ( 0 !== validate_file( $stylesheet ) ) {
+ return;
+ }
+
+ if ( ! current_user_can( 'edit_themes' ) ) {
+ return;
+ }
+
+ $theme = wp_get_theme( $stylesheet );
+ if ( ! $theme->exists() ) {
+ return;
+ }
+
+ if ( ! wp_verify_nonce( $args['nonce'], 'edit-theme_' . $stylesheet . '_' . $file ) ) {
+ return;
+ }
+
+ if ( $theme->errors() && 'theme_no_stylesheet' === $theme->errors()->get_error_code() ) {
+ return;
+ }
+
+ $editable_extensions = wp_get_theme_file_editable_extensions( $theme );
+
+ $allowed_files = array();
+ foreach ( $editable_extensions as $type ) {
+ switch ( $type ) {
+ case 'php':
+ $allowed_files = array_merge( $allowed_files, $theme->get_files( 'php', -1 ) );
+ break;
+ case 'css':
+ $style_files = $theme->get_files( 'css', -1 );
+ $allowed_files['style.css'] = $style_files['style.css'];
+ $allowed_files = array_merge( $allowed_files, $style_files );
+ break;
+ default:
+ $allowed_files = array_merge( $allowed_files, $theme->get_files( $type, -1 ) );
+ break;
+ }
+ }
+
+ $real_file = $theme->get_stylesheet_directory() . '/' . $file;
+ if ( 0 !== validate_file( $real_file, $allowed_files ) ) {
+ return;
+ }
+
+ // Ensure file is real.
+ if ( ! is_file( $real_file ) ) {
+ return;
+ }
+
+ // Ensure file extension is allowed.
+ $extension = null;
+ if ( preg_match( '/\.([^.]+)$/', $real_file, $matches ) ) {
+ $extension = strtolower( $matches[1] );
+ if ( ! in_array( $extension, $editable_extensions, true ) ) {
+ return;
+ }
+ }
+
+ if ( ! is_writeable( $real_file ) ) {
+ return;
+ }
+
+ // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fopen
+ $file_pointer = fopen( $real_file, 'w+' );
+ if ( false === $file_pointer ) {
+ return;
+ }
+ // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose
+ fclose( $file_pointer );
+
+ $theme_data = array(
+ 'name' => $theme->get( 'Name' ),
+ 'version' => $theme->get( 'Version' ),
+ 'uri' => $theme->get( 'ThemeURI' ),
+ );
+
+ /**
+ * This action is documented already in this file.
+ */
+ do_action( 'jetpack_edited_theme', $stylesheet, $theme_data );
+ }
+
+ /**
+ * Detect a theme deletion.
+ *
+ * @access public
+ */
+ public function detect_theme_deletion() {
+ $delete_theme_call = $this->get_delete_theme_call();
+ if ( empty( $delete_theme_call ) ) {
+ return;
+ }
+
+ $slug = $delete_theme_call['args'][0];
+ $theme = wp_get_theme( $slug );
+ $theme_data = array(
+ 'name' => $theme->get( 'Name' ),
+ 'version' => $theme->get( 'Version' ),
+ 'uri' => $theme->get( 'ThemeURI' ),
+ 'slug' => $slug,
+ );
+
+ /**
+ * Signals to the sync listener that a theme was deleted and a sync action
+ * reflecting the deletion and theme slug should be sent
+ *
+ * @since 5.0.0
+ *
+ * @param string $slug Theme slug
+ * @param array $theme_data Theme info Since 5.3
+ */
+ do_action( 'jetpack_deleted_theme', $slug, $theme_data );
+ }
+
+ /**
+ * Handle an upgrader completion action.
+ *
+ * @access public
+ *
+ * @param \WP_Upgrader $upgrader The upgrader instance.
+ * @param array $details Array of bulk item update data.
+ */
+ public function check_upgrader( $upgrader, $details ) {
+ if ( ! isset( $details['type'] ) ||
+ 'theme' !== $details['type'] ||
+ is_wp_error( $upgrader->skin->result ) ||
+ ! method_exists( $upgrader, 'theme_info' )
+ ) {
+ return;
+ }
+
+ if ( 'install' === $details['action'] ) {
+ $theme = $upgrader->theme_info();
+ if ( ! $theme instanceof \WP_Theme ) {
+ return;
+ }
+ $theme_info = array(
+ 'name' => $theme->get( 'Name' ),
+ 'version' => $theme->get( 'Version' ),
+ 'uri' => $theme->get( 'ThemeURI' ),
+ );
+
+ /**
+ * Signals to the sync listener that a theme was installed and a sync action
+ * reflecting the installation and the theme info should be sent
+ *
+ * @since 4.9.0
+ *
+ * @param string $theme->theme_root Text domain of the theme
+ * @param mixed $theme_info Array of abbreviated theme info
+ */
+ do_action( 'jetpack_installed_theme', $theme->stylesheet, $theme_info );
+ }
+
+ if ( 'update' === $details['action'] ) {
+ $themes = array();
+
+ if ( empty( $details['themes'] ) && isset( $details['theme'] ) ) {
+ $details['themes'] = array( $details['theme'] );
+ }
+
+ foreach ( $details['themes'] as $theme_slug ) {
+ $theme = wp_get_theme( $theme_slug );
+
+ if ( ! $theme instanceof \WP_Theme ) {
+ continue;
+ }
+
+ $themes[ $theme_slug ] = array(
+ 'name' => $theme->get( 'Name' ),
+ 'version' => $theme->get( 'Version' ),
+ 'uri' => $theme->get( 'ThemeURI' ),
+ 'stylesheet' => $theme->stylesheet,
+ );
+ }
+
+ if ( empty( $themes ) ) {
+ return;
+ }
+
+ /**
+ * Signals to the sync listener that one or more themes was updated and a sync action
+ * reflecting the update and the theme info should be sent
+ *
+ * @since 6.2.0
+ *
+ * @param mixed $themes Array of abbreviated theme info
+ */
+ do_action( 'jetpack_updated_themes', $themes );
+ }
+ }
+
+ /**
+ * Initialize themes action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ add_action( 'jetpack_full_sync_theme_data', $callable );
+ }
+
+ /**
+ * Handle a theme switch.
+ *
+ * @access public
+ *
+ * @param string $new_name Name of the new theme.
+ * @param \WP_Theme $new_theme The new theme.
+ * @param \WP_Theme $old_theme The previous theme.
+ */
+ public function sync_theme_support( $new_name, $new_theme = null, $old_theme = null ) {
+ $previous_theme = $this->get_theme_info( $old_theme );
+
+ /**
+ * Fires when the client needs to sync theme support info
+ *
+ * @since 4.2.0
+ *
+ * @param array the theme support array
+ * @param array the previous theme since Jetpack 6.5.0
+ */
+ do_action( 'jetpack_sync_current_theme_support', $this->get_theme_info(), $previous_theme );
+ }
+
+ /**
+ * Enqueue the themes actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ /**
+ * Tells the client to sync all theme data to the server
+ *
+ * @since 4.2.0
+ *
+ * @param boolean Whether to expand theme data (should always be true)
+ */
+ do_action( 'jetpack_full_sync_theme_data', true );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 1, true );
+ }
+
+ /**
+ * Send the themes actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $send_until The timestamp until the current request can send.
+ * @param array $state This module Full Sync status.
+ *
+ * @return array This module Full Sync status.
+ */
+ public function send_full_sync_actions( $config, $send_until, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ // we call this instead of do_action when sending immediately.
+ $this->send_action( 'jetpack_full_sync_theme_data', array( true ) );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 'finished' => true );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return array Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return 1;
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ add_filter( 'jetpack_sync_before_send_jetpack_full_sync_theme_data', array( $this, 'expand_theme_data' ) );
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_theme_data' );
+ }
+
+ /**
+ * Expand the theme within a hook before it is serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @return array Theme data.
+ */
+ public function expand_theme_data() {
+ return array( $this->get_theme_info() );
+ }
+
+ /**
+ * Retrieve the name of the widget by the widget ID.
+ *
+ * @access public
+ * @global $wp_registered_widgets
+ *
+ * @param string $widget_id Widget ID.
+ * @return string Name of the widget, or null if not found.
+ */
+ public function get_widget_name( $widget_id ) {
+ global $wp_registered_widgets;
+ return ( isset( $wp_registered_widgets[ $widget_id ] ) ? $wp_registered_widgets[ $widget_id ]['name'] : null );
+ }
+
+ /**
+ * Retrieve the name of the sidebar by the sidebar ID.
+ *
+ * @access public
+ * @global $wp_registered_sidebars
+ *
+ * @param string $sidebar_id Sidebar ID.
+ * @return string Name of the sidebar, or null if not found.
+ */
+ public function get_sidebar_name( $sidebar_id ) {
+ global $wp_registered_sidebars;
+ return ( isset( $wp_registered_sidebars[ $sidebar_id ] ) ? $wp_registered_sidebars[ $sidebar_id ]['name'] : null );
+ }
+
+ /**
+ * Sync addition of widgets to a sidebar.
+ *
+ * @access public
+ *
+ * @param array $new_widgets New widgets.
+ * @param array $old_widgets Old widgets.
+ * @param string $sidebar Sidebar ID.
+ * @return array All widgets that have been moved to the sidebar.
+ */
+ public function sync_add_widgets_to_sidebar( $new_widgets, $old_widgets, $sidebar ) {
+ $added_widgets = array_diff( $new_widgets, $old_widgets );
+ if ( empty( $added_widgets ) ) {
+ return array();
+ }
+ $moved_to_sidebar = array();
+ $sidebar_name = $this->get_sidebar_name( $sidebar );
+
+ // Don't sync jetpack_widget_added if theme was switched.
+ if ( $this->is_theme_switch() ) {
+ return array();
+ }
+
+ foreach ( $added_widgets as $added_widget ) {
+ $moved_to_sidebar[] = $added_widget;
+ $added_widget_name = $this->get_widget_name( $added_widget );
+ /**
+ * Helps Sync log that a widget got added
+ *
+ * @since 4.9.0
+ *
+ * @param string $sidebar, Sidebar id got changed
+ * @param string $added_widget, Widget id got added
+ * @param string $sidebar_name, Sidebar id got changed Since 5.0.0
+ * @param string $added_widget_name, Widget id got added Since 5.0.0
+ */
+ do_action( 'jetpack_widget_added', $sidebar, $added_widget, $sidebar_name, $added_widget_name );
+ }
+ return $moved_to_sidebar;
+ }
+
+ /**
+ * Sync removal of widgets from a sidebar.
+ *
+ * @access public
+ *
+ * @param array $new_widgets New widgets.
+ * @param array $old_widgets Old widgets.
+ * @param string $sidebar Sidebar ID.
+ * @param array $inactive_widgets Current inactive widgets.
+ * @return array All widgets that have been moved to inactive.
+ */
+ public function sync_remove_widgets_from_sidebar( $new_widgets, $old_widgets, $sidebar, $inactive_widgets ) {
+ $removed_widgets = array_diff( $old_widgets, $new_widgets );
+
+ if ( empty( $removed_widgets ) ) {
+ return array();
+ }
+
+ $moved_to_inactive = array();
+ $sidebar_name = $this->get_sidebar_name( $sidebar );
+
+ foreach ( $removed_widgets as $removed_widget ) {
+ // Lets check if we didn't move the widget to in_active_widgets.
+ if ( isset( $inactive_widgets ) && ! in_array( $removed_widget, $inactive_widgets, true ) ) {
+ $removed_widget_name = $this->get_widget_name( $removed_widget );
+ /**
+ * Helps Sync log that a widgte got removed
+ *
+ * @since 4.9.0
+ *
+ * @param string $sidebar, Sidebar id got changed
+ * @param string $removed_widget, Widget id got removed
+ * @param string $sidebar_name, Name of the sidebar that changed Since 5.0.0
+ * @param string $removed_widget_name, Name of the widget that got removed Since 5.0.0
+ */
+ do_action( 'jetpack_widget_removed', $sidebar, $removed_widget, $sidebar_name, $removed_widget_name );
+ } else {
+ $moved_to_inactive[] = $removed_widget;
+ }
+ }
+ return $moved_to_inactive;
+
+ }
+
+ /**
+ * Sync a reorder of widgets within a sidebar.
+ *
+ * @access public
+ *
+ * @todo Refactor serialize() to a json_encode().
+ *
+ * @param array $new_widgets New widgets.
+ * @param array $old_widgets Old widgets.
+ * @param string $sidebar Sidebar ID.
+ */
+ public function sync_widgets_reordered( $new_widgets, $old_widgets, $sidebar ) {
+ $added_widgets = array_diff( $new_widgets, $old_widgets );
+ if ( ! empty( $added_widgets ) ) {
+ return;
+ }
+ $removed_widgets = array_diff( $old_widgets, $new_widgets );
+ if ( ! empty( $removed_widgets ) ) {
+ return;
+ }
+
+ // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
+ if ( serialize( $old_widgets ) !== serialize( $new_widgets ) ) {
+ $sidebar_name = $this->get_sidebar_name( $sidebar );
+ /**
+ * Helps Sync log that a sidebar id got reordered
+ *
+ * @since 4.9.0
+ *
+ * @param string $sidebar, Sidebar id got changed
+ * @param string $sidebar_name, Name of the sidebar that changed Since 5.0.0
+ */
+ do_action( 'jetpack_widget_reordered', $sidebar, $sidebar_name );
+ }
+
+ }
+
+ /**
+ * Handle the update of the sidebars and widgets mapping option.
+ *
+ * @access public
+ *
+ * @param mixed $old_value The old option value.
+ * @param mixed $new_value The new option value.
+ */
+ public function sync_sidebar_widgets_actions( $old_value, $new_value ) {
+ // Don't really know how to deal with different array_values yet.
+ if (
+ ( isset( $old_value['array_version'] ) && 3 !== $old_value['array_version'] ) ||
+ ( isset( $new_value['array_version'] ) && 3 !== $new_value['array_version'] )
+ ) {
+ return;
+ }
+
+ $moved_to_inactive_ids = array();
+ $moved_to_sidebar = array();
+
+ foreach ( $new_value as $sidebar => $new_widgets ) {
+ if ( in_array( $sidebar, array( 'array_version', 'wp_inactive_widgets' ), true ) ) {
+ continue;
+ }
+ $old_widgets = isset( $old_value[ $sidebar ] )
+ ? $old_value[ $sidebar ]
+ : array();
+
+ if ( ! is_array( $new_widgets ) ) {
+ $new_widgets = array();
+ }
+
+ $moved_to_inactive_recently = $this->sync_remove_widgets_from_sidebar( $new_widgets, $old_widgets, $sidebar, $new_value['wp_inactive_widgets'] );
+ $moved_to_inactive_ids = array_merge( $moved_to_inactive_ids, $moved_to_inactive_recently );
+
+ $moved_to_sidebar_recently = $this->sync_add_widgets_to_sidebar( $new_widgets, $old_widgets, $sidebar );
+ $moved_to_sidebar = array_merge( $moved_to_sidebar, $moved_to_sidebar_recently );
+
+ $this->sync_widgets_reordered( $new_widgets, $old_widgets, $sidebar );
+
+ }
+
+ // Don't sync either jetpack_widget_moved_to_inactive or jetpack_cleared_inactive_widgets if theme was switched.
+ if ( $this->is_theme_switch() ) {
+ return;
+ }
+
+ // Treat inactive sidebar a bit differently.
+ if ( ! empty( $moved_to_inactive_ids ) ) {
+ $moved_to_inactive_name = array_map( array( $this, 'get_widget_name' ), $moved_to_inactive_ids );
+ /**
+ * Helps Sync log that a widgets IDs got moved to in active
+ *
+ * @since 4.9.0
+ *
+ * @param array $moved_to_inactive_ids, Array of widgets id that moved to inactive id got changed
+ * @param array $moved_to_inactive_names, Array of widgets names that moved to inactive id got changed Since 5.0.0
+ */
+ do_action( 'jetpack_widget_moved_to_inactive', $moved_to_inactive_ids, $moved_to_inactive_name );
+ } elseif ( empty( $moved_to_sidebar ) && empty( $new_value['wp_inactive_widgets'] ) && ! empty( $old_value['wp_inactive_widgets'] ) ) {
+ /**
+ * Helps Sync log that a got cleared from inactive.
+ *
+ * @since 4.9.0
+ */
+ do_action( 'jetpack_cleared_inactive_widgets' );
+ }
+ }
+
+ /**
+ * Retrieve the theme data for the current or a specific theme.
+ *
+ * @access private
+ *
+ * @param \WP_Theme $theme Theme object. Optional, will default to the current theme.
+ *
+ * @return array Theme data.
+ */
+ private function get_theme_info( $theme = null ) {
+ $theme_support = array();
+
+ // We are trying to get the current theme info.
+ if ( null === $theme ) {
+ $theme = wp_get_theme();
+ }
+
+ $theme_support['name'] = $theme->get( 'Name' );
+ $theme_support['version'] = $theme->get( 'Version' );
+ $theme_support['slug'] = $theme->get_stylesheet();
+ $theme_support['uri'] = $theme->get( 'ThemeURI' );
+
+ return $theme_support;
+ }
+
+ /**
+ * Whether we've deleted a theme in the current request.
+ *
+ * @access private
+ *
+ * @return boolean True if this is a theme deletion request, false otherwise.
+ */
+ private function get_delete_theme_call() {
+ // Intentional usage of `debug_backtrace()` for production needs.
+ // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
+ $backtrace = debug_backtrace();
+ $delete_theme_call = null;
+ foreach ( $backtrace as $call ) {
+ if ( isset( $call['function'] ) && 'delete_theme' === $call['function'] ) {
+ $delete_theme_call = $call;
+ break;
+ }
+ }
+ return $delete_theme_call;
+ }
+
+ /**
+ * Whether we've switched to another theme in the current request.
+ *
+ * @access private
+ *
+ * @return boolean True if this is a theme switch request, false otherwise.
+ */
+ private function is_theme_switch() {
+ return did_action( 'after_switch_theme' );
+ }
+
+ /**
+ * Return Total number of objects.
+ *
+ * @param array $config Full Sync config.
+ *
+ * @return int total
+ */
+ public function total( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return 1;
+ }
+
+}
diff --git a/vendor/automattic/jetpack-sync/src/modules/class-updates.php b/vendor/automattic/jetpack-sync/src/modules/class-updates.php
new file mode 100644
index 0000000000000..fa51eef75c7e1
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/modules/class-updates.php
@@ -0,0 +1,527 @@
+updates = array();
+ }
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'updates';
+ }
+
+ /**
+ * Initialize updates action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ global $wp_version;
+ $this->old_wp_version = $wp_version;
+ add_action( 'set_site_transient_update_plugins', array( $this, 'validate_update_change' ), 10, 3 );
+ add_action( 'set_site_transient_update_themes', array( $this, 'validate_update_change' ), 10, 3 );
+ add_action( 'set_site_transient_update_core', array( $this, 'validate_update_change' ), 10, 3 );
+
+ add_action( 'jetpack_update_plugins_change', $callable );
+ add_action( 'jetpack_update_themes_change', $callable );
+ add_action( 'jetpack_update_core_change', $callable );
+
+ add_filter(
+ 'jetpack_sync_before_enqueue_jetpack_update_plugins_change',
+ array(
+ $this,
+ 'filter_update_keys',
+ ),
+ 10,
+ 2
+ );
+ add_filter(
+ 'jetpack_sync_before_enqueue_upgrader_process_complete',
+ array(
+ $this,
+ 'filter_upgrader_process_complete',
+ ),
+ 10,
+ 2
+ );
+
+ add_action( 'automatic_updates_complete', $callable );
+
+ if ( is_multisite() ) {
+ add_filter( 'pre_update_site_option_wpmu_upgrade_site', array( $this, 'update_core_network_event' ), 10, 2 );
+ add_action( 'jetpack_sync_core_update_network', $callable, 10, 3 );
+ }
+
+ // Send data when update completes.
+ add_action( '_core_updated_successfully', array( $this, 'update_core' ) );
+ add_action( 'jetpack_sync_core_reinstalled_successfully', $callable );
+ add_action( 'jetpack_sync_core_autoupdated_successfully', $callable, 10, 2 );
+ add_action( 'jetpack_sync_core_updated_successfully', $callable, 10, 2 );
+
+ }
+
+ /**
+ * Initialize updates action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ add_action( 'jetpack_full_sync_updates', $callable );
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ add_filter( 'jetpack_sync_before_send_jetpack_full_sync_updates', array( $this, 'expand_updates' ) );
+ add_filter( 'jetpack_sync_before_send_jetpack_update_themes_change', array( $this, 'expand_themes' ) );
+ }
+
+ /**
+ * Handle a core network update.
+ *
+ * @access public
+ *
+ * @param int $wp_db_version Current version of the WordPress database.
+ * @param int $old_wp_db_version Old version of the WordPress database.
+ * @return int Current version of the WordPress database.
+ */
+ public function update_core_network_event( $wp_db_version, $old_wp_db_version ) {
+ global $wp_version;
+ /**
+ * Sync event for when core wp network updates to a new db version
+ *
+ * @since 5.0.0
+ *
+ * @param int $wp_db_version the latest wp_db_version
+ * @param int $old_wp_db_version previous wp_db_version
+ * @param string $wp_version the latest wp_version
+ */
+ do_action( 'jetpack_sync_core_update_network', $wp_db_version, $old_wp_db_version, $wp_version );
+ return $wp_db_version;
+ }
+
+ /**
+ * Handle a core update.
+ *
+ * @access public
+ *
+ * @todo Implement nonce or refactor to use `admin_post_{$action}` hooks instead.
+ *
+ * @param string $new_wp_version The new WP core version.
+ */
+ public function update_core( $new_wp_version ) {
+ global $pagenow;
+
+ // // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ if ( isset( $_GET['action'] ) && 'do-core-reinstall' === $_GET['action'] ) {
+ /**
+ * Sync event that fires when core reinstall was successful
+ *
+ * @since 5.0.0
+ *
+ * @param string $new_wp_version the updated WordPress version
+ */
+ do_action( 'jetpack_sync_core_reinstalled_successfully', $new_wp_version );
+ return;
+ }
+
+ // Core was autoupdated.
+ if (
+ 'update-core.php' !== $pagenow &&
+ ! Jetpack_Constants::is_true( 'REST_API_REQUEST' ) // WP.com rest api calls should never be marked as a core autoupdate.
+ ) {
+ /**
+ * Sync event that fires when core autoupdate was successful
+ *
+ * @since 5.0.0
+ *
+ * @param string $new_wp_version the updated WordPress version
+ * @param string $old_wp_version the previous WordPress version
+ */
+ do_action( 'jetpack_sync_core_autoupdated_successfully', $new_wp_version, $this->old_wp_version );
+ return;
+ }
+ /**
+ * Sync event that fires when core update was successful
+ *
+ * @since 5.0.0
+ *
+ * @param string $new_wp_version the updated WordPress version
+ * @param string $old_wp_version the previous WordPress version
+ */
+ do_action( 'jetpack_sync_core_updated_successfully', $new_wp_version, $this->old_wp_version );
+ }
+
+ /**
+ * Retrieve the checksum for an update.
+ *
+ * @access public
+ *
+ * @param object $update The update object.
+ * @param string $transient The transient we're retrieving a checksum for.
+ * @return int The checksum.
+ */
+ public function get_update_checksum( $update, $transient ) {
+ $updates = array();
+ $no_updated = array();
+ switch ( $transient ) {
+ case 'update_plugins':
+ if ( ! empty( $update->response ) && is_array( $update->response ) ) {
+ foreach ( $update->response as $plugin_slug => $response ) {
+ if ( ! empty( $plugin_slug ) && isset( $response->new_version ) ) {
+ $updates[] = array( $plugin_slug => $response->new_version );
+ }
+ }
+ }
+ if ( ! empty( $update->no_update ) ) {
+ $no_updated = array_keys( $update->no_update );
+ }
+
+ if ( ! isset( $no_updated['jetpack/jetpack.php'] ) && isset( $updates['jetpack/jetpack.php'] ) ) {
+ return false;
+ }
+
+ break;
+ case 'update_themes':
+ if ( ! empty( $update->response ) && is_array( $update->response ) ) {
+ foreach ( $update->response as $theme_slug => $response ) {
+ if ( ! empty( $theme_slug ) && isset( $response['new_version'] ) ) {
+ $updates[] = array( $theme_slug => $response['new_version'] );
+ }
+ }
+ }
+
+ if ( ! empty( $update->checked ) ) {
+ $no_updated = $update->checked;
+ }
+
+ break;
+ case 'update_core':
+ if ( ! empty( $update->updates ) && is_array( $update->updates ) ) {
+ foreach ( $update->updates as $response ) {
+ if ( ! empty( $response->response ) && 'latest' === $response->response ) {
+ continue;
+ }
+ if ( ! empty( $response->response ) && isset( $response->packages->full ) ) {
+ $updates[] = array( $response->response => $response->packages->full );
+ }
+ }
+ }
+
+ if ( ! empty( $update->version_checked ) ) {
+ $no_updated = $update->version_checked;
+ }
+
+ if ( empty( $updates ) ) {
+ return false;
+ }
+ break;
+
+ }
+ if ( empty( $updates ) && empty( $no_updated ) ) {
+ return false;
+ }
+ return $this->get_check_sum( array( $no_updated, $updates ) );
+ }
+
+ /**
+ * Validate a change coming from an update before sending for sync.
+ *
+ * @access public
+ *
+ * @param mixed $value Site transient value.
+ * @param int $expiration Time until transient expiration in seconds.
+ * @param string $transient Transient name.
+ */
+ public function validate_update_change( $value, $expiration, $transient ) {
+ $new_checksum = $this->get_update_checksum( $value, $transient );
+
+ if ( false === $new_checksum ) {
+ return;
+ }
+
+ $checksums = get_option( self::UPDATES_CHECKSUM_OPTION_NAME, array() );
+
+ if ( isset( $checksums[ $transient ] ) && $checksums[ $transient ] === $new_checksum ) {
+ return;
+ }
+
+ $checksums[ $transient ] = $new_checksum;
+
+ update_option( self::UPDATES_CHECKSUM_OPTION_NAME, $checksums );
+ if ( 'update_core' === $transient ) {
+ /**
+ * Trigger a change to core update that we want to sync.
+ *
+ * @since 5.1.0
+ *
+ * @param array $value Contains info that tells us what needs updating.
+ */
+ do_action( 'jetpack_update_core_change', $value );
+ return;
+ }
+ if ( empty( $this->updates ) ) {
+ // Lets add the shutdown method once and only when the updates move from empty to filled with something.
+ add_action( 'shutdown', array( $this, 'sync_last_event' ), 9 );
+ }
+ if ( ! isset( $this->updates[ $transient ] ) ) {
+ $this->updates[ $transient ] = array();
+ }
+ $this->updates[ $transient ][] = $value;
+ }
+
+ /**
+ * Sync the last update only.
+ *
+ * @access public
+ */
+ public function sync_last_event() {
+ foreach ( $this->updates as $transient => $values ) {
+ $value = end( $values ); // Only send over the last value.
+ /**
+ * Trigger a change to a specific update that we want to sync.
+ * Triggers one of the following actions:
+ * - jetpack_{$transient}_change
+ * - jetpack_update_plugins_change
+ * - jetpack_update_themes_change
+ *
+ * @since 5.1.0
+ *
+ * @param array $value Contains info that tells us what needs updating.
+ */
+ do_action( "jetpack_{$transient}_change", $value );
+ }
+
+ }
+
+ /**
+ * Enqueue the updates actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ /**
+ * Tells the client to sync all updates to the server
+ *
+ * @since 4.2.0
+ *
+ * @param boolean Whether to expand updates (should always be true)
+ */
+ do_action( 'jetpack_full_sync_updates', true );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 1, true );
+ }
+
+ /**
+ * Send the updates actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $send_until The timestamp until the current request can send.
+ * @param array $state This module Full Sync status.
+ *
+ * @return array This module Full Sync status.
+ */
+ public function send_full_sync_actions( $config, $send_until, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ // we call this instead of do_action when sending immediately.
+ $this->send_action( 'jetpack_full_sync_updates', array( true ) );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 'finished' => true );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return array Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return 1;
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_updates' );
+ }
+
+ /**
+ * Retrieve all updates that we're interested in.
+ *
+ * @access public
+ *
+ * @return array All updates.
+ */
+ public function get_all_updates() {
+ return array(
+ 'core' => get_site_transient( 'update_core' ),
+ 'plugins' => get_site_transient( 'update_plugins' ),
+ 'themes' => get_site_transient( 'update_themes' ),
+ );
+ }
+
+ /**
+ * Remove unnecessary keys from synced updates data.
+ *
+ * @access public
+ *
+ * @param array $args Hook arguments.
+ * @return array $args Hook arguments.
+ */
+ public function filter_update_keys( $args ) {
+ $updates = $args[0];
+
+ if ( isset( $updates->no_update ) ) {
+ unset( $updates->no_update );
+ }
+
+ return $args;
+ }
+
+ /**
+ * Filter out upgrader object from the completed upgrader action args.
+ *
+ * @access public
+ *
+ * @param array $args Hook arguments.
+ * @return array $args Filtered hook arguments.
+ */
+ public function filter_upgrader_process_complete( $args ) {
+ array_shift( $args );
+
+ return $args;
+ }
+
+ /**
+ * Expand the updates within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array $args The hook parameters.
+ */
+ public function expand_updates( $args ) {
+ if ( $args[0] ) {
+ return $this->get_all_updates();
+ }
+
+ return $args;
+ }
+
+ /**
+ * Expand the themes within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array $args The hook parameters.
+ */
+ public function expand_themes( $args ) {
+ if ( ! isset( $args[0], $args[0]->response ) ) {
+ return $args;
+ }
+ if ( ! is_array( $args[0]->response ) ) {
+ // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
+ trigger_error( 'Warning: Not an Array as expected but -> ' . wp_json_encode( $args[0]->response ) . ' instead', E_USER_WARNING );
+ return $args;
+ }
+ foreach ( $args[0]->response as $stylesheet => &$theme_data ) {
+ $theme = wp_get_theme( $stylesheet );
+ $theme_data['name'] = $theme->name;
+ }
+ return $args;
+ }
+
+ /**
+ * Perform module cleanup.
+ * Deletes any transients and options that this module uses.
+ * Usually triggered when uninstalling the plugin.
+ *
+ * @access public
+ */
+ public function reset_data() {
+ delete_option( self::UPDATES_CHECKSUM_OPTION_NAME );
+ }
+
+ /**
+ * Return Total number of objects.
+ *
+ * @param array $config Full Sync config.
+ *
+ * @return int total
+ */
+ public function total( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return 3;
+ }
+
+}
diff --git a/vendor/automattic/jetpack-sync/src/modules/class-users.php b/vendor/automattic/jetpack-sync/src/modules/class-users.php
new file mode 100644
index 0000000000000..ece6590dc5307
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/modules/class-users.php
@@ -0,0 +1,865 @@
+sanitize_user_and_expand( $user );
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Initialize users action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ // Users.
+ add_action( 'user_register', array( $this, 'user_register_handler' ) );
+ add_action( 'profile_update', array( $this, 'save_user_handler' ), 10, 2 );
+
+ add_action( 'add_user_to_blog', array( $this, 'add_user_to_blog_handler' ) );
+ add_action( 'jetpack_sync_add_user', $callable, 10, 2 );
+
+ add_action( 'jetpack_sync_register_user', $callable, 10, 2 );
+ add_action( 'jetpack_sync_save_user', $callable, 10, 2 );
+
+ add_action( 'jetpack_sync_user_locale', $callable, 10, 2 );
+ add_action( 'jetpack_sync_user_locale_delete', $callable, 10, 1 );
+
+ add_action( 'deleted_user', array( $this, 'deleted_user_handler' ), 10, 2 );
+ add_action( 'jetpack_deleted_user', $callable, 10, 3 );
+ add_action( 'remove_user_from_blog', array( $this, 'remove_user_from_blog_handler' ), 10, 2 );
+ add_action( 'jetpack_removed_user_from_blog', $callable, 10, 2 );
+
+ // User roles.
+ add_action( 'add_user_role', array( $this, 'save_user_role_handler' ), 10, 2 );
+ add_action( 'set_user_role', array( $this, 'save_user_role_handler' ), 10, 3 );
+ add_action( 'remove_user_role', array( $this, 'save_user_role_handler' ), 10, 2 );
+
+ // User capabilities.
+ add_action( 'added_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
+ add_action( 'updated_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
+ add_action( 'deleted_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
+
+ // User authentication.
+ add_filter( 'authenticate', array( $this, 'authenticate_handler' ), 1000, 3 );
+ add_action( 'wp_login', array( $this, 'wp_login_handler' ), 10, 2 );
+
+ add_action( 'jetpack_wp_login', $callable, 10, 3 );
+
+ add_action( 'wp_logout', $callable, 10, 0 );
+ add_action( 'wp_masterbar_logout', $callable, 10, 1 );
+
+ // Add on init.
+ add_filter( 'jetpack_sync_before_enqueue_jetpack_sync_add_user', array( $this, 'expand_action' ) );
+ add_filter( 'jetpack_sync_before_enqueue_jetpack_sync_register_user', array( $this, 'expand_action' ) );
+ add_filter( 'jetpack_sync_before_enqueue_jetpack_sync_save_user', array( $this, 'expand_action' ) );
+ }
+
+ /**
+ * Initialize users action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ add_action( 'jetpack_full_sync_users', $callable );
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ add_filter( 'jetpack_sync_before_send_jetpack_wp_login', array( $this, 'expand_login_username' ), 10, 1 );
+ add_filter( 'jetpack_sync_before_send_wp_logout', array( $this, 'expand_logout_username' ), 10, 2 );
+
+ // Full sync.
+ add_filter( 'jetpack_sync_before_send_jetpack_full_sync_users', array( $this, 'expand_users' ) );
+ }
+
+ /**
+ * Retrieve a user by a user ID or object.
+ *
+ * @access private
+ *
+ * @param mixed $user User object or ID.
+ * @return \WP_User User object, or `null` if user invalid/not found.
+ */
+ private function get_user( $user ) {
+ if ( is_numeric( $user ) ) {
+ $user = get_user_by( 'id', $user );
+ }
+ if ( $user instanceof \WP_User ) {
+ return $user;
+ }
+ return null;
+ }
+
+ /**
+ * Sanitize a user object.
+ * Removes the password from the user object because we don't want to sync it.
+ *
+ * @access public
+ *
+ * @todo Refactor `serialize`/`unserialize` to `wp_json_encode`/`wp_json_decode`.
+ *
+ * @param \WP_User $user User object.
+ * @return \WP_User Sanitized user object.
+ */
+ public function sanitize_user( $user ) {
+ $user = $this->get_user( $user );
+ // This creates a new user object and stops the passing of the object by reference.
+ // // phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize, WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize
+ $user = unserialize( serialize( $user ) );
+
+ if ( is_object( $user ) && is_object( $user->data ) ) {
+ unset( $user->data->user_pass );
+ }
+ return $user;
+ }
+
+ /**
+ * Expand a particular user.
+ *
+ * @access public
+ *
+ * @param \WP_User $user User object.
+ * @return \WP_User Expanded user object.
+ */
+ public function expand_user( $user ) {
+ if ( ! is_object( $user ) ) {
+ return null;
+ }
+ $user->allowed_mime_types = get_allowed_mime_types( $user );
+ $user->allcaps = $this->get_real_user_capabilities( $user );
+
+ // Only set the user locale if it is different from the site locale.
+ if ( get_locale() !== get_user_locale( $user->ID ) ) {
+ $user->locale = get_user_locale( $user->ID );
+ }
+
+ return $user;
+ }
+
+ /**
+ * Retrieve capabilities we care about for a particular user.
+ *
+ * @access public
+ *
+ * @param \WP_User $user User object.
+ * @return array User capabilities.
+ */
+ public function get_real_user_capabilities( $user ) {
+ $user_capabilities = array();
+ if ( is_wp_error( $user ) ) {
+ return $user_capabilities;
+ }
+ foreach ( Defaults::get_capabilities_whitelist() as $capability ) {
+ if ( user_can( $user, $capability ) ) {
+ $user_capabilities[ $capability ] = true;
+ }
+ }
+ return $user_capabilities;
+ }
+
+ /**
+ * Retrieve, expand and sanitize a user.
+ * Can be directly used in the sync user action handlers.
+ *
+ * @access public
+ *
+ * @param mixed $user User ID or user object.
+ * @return \WP_User Expanded and sanitized user object.
+ */
+ public function sanitize_user_and_expand( $user ) {
+ $user = $this->get_user( $user );
+ $user = $this->expand_user( $user );
+ return $this->sanitize_user( $user );
+ }
+
+ /**
+ * Expand the user within a hook before it is serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook arguments.
+ * @return array $args The hook arguments.
+ */
+ public function expand_action( $args ) {
+ // The first argument is always the user.
+ list( $user ) = $args;
+ if ( $user ) {
+ $args[0] = $this->sanitize_user_and_expand( $user );
+ return $args;
+ }
+
+ return false;
+ }
+
+ /**
+ * Expand the user username at login before being sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook arguments.
+ * @return array $args Expanded hook arguments.
+ */
+ public function expand_login_username( $args ) {
+ list( $login, $user, $flags ) = $args;
+ $user = $this->sanitize_user( $user );
+
+ return array( $login, $user, $flags );
+ }
+
+ /**
+ * Expand the user username at logout before being sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook arguments.
+ * @param int $user_id ID of the user.
+ * @return array $args Expanded hook arguments.
+ */
+ public function expand_logout_username( $args, $user_id ) {
+ $user = get_userdata( $user_id );
+ $user = $this->sanitize_user( $user );
+
+ $login = '';
+ if ( is_object( $user ) && is_object( $user->data ) ) {
+ $login = $user->data->user_login;
+ }
+
+ // If we don't have a user here lets not send anything.
+ if ( empty( $login ) ) {
+ return false;
+ }
+
+ return array( $login, $user );
+ }
+
+ /**
+ * Additional processing is needed for wp_login so we introduce this wrapper handler.
+ *
+ * @access public
+ *
+ * @param string $user_login The user login.
+ * @param \WP_User $user The user object.
+ */
+ public function wp_login_handler( $user_login, $user ) {
+ /**
+ * Fires when a user is logged into a site.
+ *
+ * @since 7.2.0
+ *
+ * @param int $user_id The user ID.
+ * @param \WP_User $user The User Object of the user that currently logged in.
+ * @param array $params Any Flags that have been added during login.
+ */
+ do_action( 'jetpack_wp_login', $user->ID, $user, $this->get_flags( $user->ID ) );
+ $this->clear_flags( $user->ID );
+ }
+
+ /**
+ * A hook for the authenticate event that checks the password strength.
+ *
+ * @access public
+ *
+ * @param \WP_Error|\WP_User $user The user object, or an error.
+ * @param string $username The username.
+ * @param string $password The password used to authenticate.
+ * @return \WP_Error|\WP_User the same object that was passed into the function.
+ */
+ public function authenticate_handler( $user, $username, $password ) {
+ // In case of cookie authentication we don't do anything here.
+ if ( empty( $password ) ) {
+ return $user;
+ }
+
+ // We are only interested in successful authentication events.
+ if ( is_wp_error( $user ) || ! ( $user instanceof \WP_User ) ) {
+ return $user;
+ }
+
+ jetpack_require_lib( 'class.jetpack-password-checker' );
+ $password_checker = new \Jetpack_Password_Checker( $user->ID );
+
+ $test_results = $password_checker->test( $password, true );
+
+ // If the password passes tests, we don't do anything.
+ if ( empty( $test_results['test_results']['failed'] ) ) {
+ return $user;
+ }
+
+ $this->add_flags(
+ $user->ID,
+ array(
+ 'warning' => 'The password failed at least one strength test.',
+ 'failures' => $test_results['test_results']['failed'],
+ )
+ );
+
+ return $user;
+ }
+
+ /**
+ * Handler for after the user is deleted.
+ *
+ * @access public
+ *
+ * @param int $deleted_user_id ID of the deleted user.
+ * @param int $reassigned_user_id ID of the user the deleted user's posts are reassigned to (if any).
+ */
+ public function deleted_user_handler( $deleted_user_id, $reassigned_user_id = '' ) {
+ $is_multisite = is_multisite();
+ /**
+ * Fires when a user is deleted on a site
+ *
+ * @since 5.4.0
+ *
+ * @param int $deleted_user_id - ID of the deleted user.
+ * @param int $reassigned_user_id - ID of the user the deleted user's posts are reassigned to (if any).
+ * @param bool $is_multisite - Whether this site is a multisite installation.
+ */
+ do_action( 'jetpack_deleted_user', $deleted_user_id, $reassigned_user_id, $is_multisite );
+ }
+
+ /**
+ * Handler for user registration.
+ *
+ * @access public
+ *
+ * @param int $user_id ID of the deleted user.
+ */
+ public function user_register_handler( $user_id ) {
+ // Ensure we only sync users who are members of the current blog.
+ if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) {
+ return;
+ }
+
+ if ( Jetpack_Constants::is_true( 'JETPACK_INVITE_ACCEPTED' ) ) {
+ $this->add_flags( $user_id, array( 'invitation_accepted' => true ) );
+ }
+ /**
+ * Fires when a new user is registered on a site
+ *
+ * @since 4.9.0
+ *
+ * @param object The WP_User object
+ */
+ do_action( 'jetpack_sync_register_user', $user_id, $this->get_flags( $user_id ) );
+ $this->clear_flags( $user_id );
+
+ }
+
+ /**
+ * Handler for user addition to the current blog.
+ *
+ * @access public
+ *
+ * @param int $user_id ID of the user.
+ */
+ public function add_user_to_blog_handler( $user_id ) {
+ // Ensure we only sync users who are members of the current blog.
+ if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) {
+ return;
+ }
+
+ if ( Jetpack_Constants::is_true( 'JETPACK_INVITE_ACCEPTED' ) ) {
+ $this->add_flags( $user_id, array( 'invitation_accepted' => true ) );
+ }
+
+ /**
+ * Fires when a user is added on a site
+ *
+ * @since 4.9.0
+ *
+ * @param object The WP_User object
+ */
+ do_action( 'jetpack_sync_add_user', $user_id, $this->get_flags( $user_id ) );
+ $this->clear_flags( $user_id );
+ }
+
+ /**
+ * Handler for user save.
+ *
+ * @access public
+ *
+ * @param int $user_id ID of the user.
+ * @param \WP_User $old_user_data User object before the changes.
+ */
+ public function save_user_handler( $user_id, $old_user_data = null ) {
+ // Ensure we only sync users who are members of the current blog.
+ if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) {
+ return;
+ }
+
+ $user = get_user_by( 'id', $user_id );
+
+ // Older versions of WP don't pass the old_user_data in ->data.
+ if ( isset( $old_user_data->data ) ) {
+ $old_user = $old_user_data->data;
+ } else {
+ $old_user = $old_user_data;
+ }
+
+ if ( null !== $old_user && $user->user_pass !== $old_user->user_pass ) {
+ $this->flags[ $user_id ]['password_changed'] = true;
+ }
+ if ( null !== $old_user && $user->data->user_email !== $old_user->user_email ) {
+ /**
+ * The '_new_email' user meta is deleted right after the call to wp_update_user
+ * that got us to this point so if it's still set then this was a user confirming
+ * their new email address.
+ */
+ if ( 1 === (int) get_user_meta( $user->ID, '_new_email', true ) ) {
+ $this->flags[ $user_id ]['email_changed'] = true;
+ }
+ }
+
+ /**
+ * Fires when the client needs to sync an updated user.
+ *
+ * @since 4.2.0
+ *
+ * @param \WP_User The WP_User object
+ * @param array State - New since 5.8.0
+ */
+ do_action( 'jetpack_sync_save_user', $user_id, $this->get_flags( $user_id ) );
+ $this->clear_flags( $user_id );
+ }
+
+ /**
+ * Handler for user role change.
+ *
+ * @access public
+ *
+ * @param int $user_id ID of the user.
+ * @param string $role New user role.
+ * @param array $old_roles Previous user roles.
+ */
+ public function save_user_role_handler( $user_id, $role, $old_roles = null ) {
+ $this->add_flags(
+ $user_id,
+ array(
+ 'role_changed' => true,
+ 'previous_role' => $old_roles,
+ )
+ );
+
+ // The jetpack_sync_register_user payload is identical to jetpack_sync_save_user, don't send both.
+ if ( $this->is_create_user() || $this->is_add_user_to_blog() ) {
+ return;
+ }
+ /**
+ * This action is documented already in this file
+ */
+ do_action( 'jetpack_sync_save_user', $user_id, $this->get_flags( $user_id ) );
+ $this->clear_flags( $user_id );
+ }
+
+ /**
+ * Retrieve current flags for a particular user.
+ *
+ * @access public
+ *
+ * @param int $user_id ID of the user.
+ * @return array Current flags of the user.
+ */
+ public function get_flags( $user_id ) {
+ if ( isset( $this->flags[ $user_id ] ) ) {
+ return $this->flags[ $user_id ];
+ }
+ return array();
+ }
+
+ /**
+ * Clear the flags of a particular user.
+ *
+ * @access public
+ *
+ * @param int $user_id ID of the user.
+ */
+ public function clear_flags( $user_id ) {
+ if ( isset( $this->flags[ $user_id ] ) ) {
+ unset( $this->flags[ $user_id ] );
+ }
+ }
+
+ /**
+ * Add flags to a particular user.
+ *
+ * @access public
+ *
+ * @param int $user_id ID of the user.
+ * @param array $flags New flags to add for the user.
+ */
+ public function add_flags( $user_id, $flags ) {
+ $this->flags[ $user_id ] = wp_parse_args( $flags, $this->get_flags( $user_id ) );
+ }
+
+ /**
+ * Save the user meta, if we're interested in it.
+ * Also uses the time to add flags for the user.
+ *
+ * @access public
+ *
+ * @param int $meta_id ID of the meta object.
+ * @param int $user_id ID of the user.
+ * @param string $meta_key Meta key.
+ * @param mixed $value Meta value.
+ */
+ public function maybe_save_user_meta( $meta_id, $user_id, $meta_key, $value ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ if ( 'locale' === $meta_key ) {
+ $this->add_flags( $user_id, array( 'locale_changed' => true ) );
+ }
+
+ $user = get_user_by( 'id', $user_id );
+ if ( isset( $user->cap_key ) && $meta_key === $user->cap_key ) {
+ $this->add_flags( $user_id, array( 'capabilities_changed' => true ) );
+ }
+
+ if ( $this->is_create_user() || $this->is_add_user_to_blog() || $this->is_delete_user() ) {
+ return;
+ }
+
+ if ( isset( $this->flags[ $user_id ] ) ) {
+ /**
+ * This action is documented already in this file
+ */
+ do_action( 'jetpack_sync_save_user', $user_id, $this->get_flags( $user_id ) );
+ }
+ }
+
+ /**
+ * Enqueue the users actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
+ global $wpdb;
+
+ return $this->enqueue_all_ids_as_action( 'jetpack_full_sync_users', $wpdb->usermeta, 'user_id', $this->get_where_sql( $config ), $max_items_to_enqueue, $state );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @todo Refactor to prepare the SQL query before executing it.
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return array Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) {
+ global $wpdb;
+
+ $query = "SELECT count(*) FROM $wpdb->usermeta";
+
+ $where_sql = $this->get_where_sql( $config );
+ if ( $where_sql ) {
+ $query .= ' WHERE ' . $where_sql;
+ }
+
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $count = $wpdb->get_var( $query );
+
+ return (int) ceil( $count / self::ARRAY_CHUNK_SIZE );
+ }
+
+ /**
+ * Retrieve the WHERE SQL clause based on the module config.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return string WHERE SQL clause, or `null` if no comments are specified in the module config.
+ */
+ public function get_where_sql( $config ) {
+ global $wpdb;
+
+ $query = "meta_key = '{$wpdb->prefix}capabilities'";
+
+ // The $config variable is a list of user IDs to sync.
+ if ( is_array( $config ) ) {
+ $query .= ' AND user_id IN (' . implode( ',', array_map( 'intval', $config ) ) . ')';
+ }
+
+ return $query;
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_users' );
+ }
+
+ /**
+ * Retrieve initial sync user config.
+ *
+ * @access public
+ *
+ * @todo Refactor the SQL query to call $wpdb->prepare() before execution.
+ *
+ * @return array|boolean IDs of users to initially sync, or false if tbe number of users exceed the maximum.
+ */
+ public function get_initial_sync_user_config() {
+ global $wpdb;
+
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $user_ids = $wpdb->get_col( "SELECT user_id FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}user_level' AND meta_value > 0 LIMIT " . ( self::MAX_INITIAL_SYNC_USERS + 1 ) );
+
+ if ( count( $user_ids ) <= self::MAX_INITIAL_SYNC_USERS ) {
+ return $user_ids;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Expand the users within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook arguments.
+ * @return array $args The hook arguments.
+ */
+ public function expand_users( $args ) {
+ list( $user_ids, $previous_end ) = $args;
+
+ return array(
+ 'users' => array_map(
+ array( $this, 'sanitize_user_and_expand' ),
+ get_users(
+ array(
+ 'include' => $user_ids,
+ 'orderby' => 'ID',
+ 'order' => 'DESC',
+ )
+ )
+ ),
+ 'previous_end' => $previous_end,
+ );
+ }
+
+ /**
+ * Handler for user removal from a particular blog.
+ *
+ * @access public
+ *
+ * @param int $user_id ID of the user.
+ * @param int $blog_id ID of the blog.
+ */
+ public function remove_user_from_blog_handler( $user_id, $blog_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ // User is removed on add, see https://github.com/WordPress/WordPress/blob/0401cee8b36df3def8e807dd766adc02b359dfaf/wp-includes/ms-functions.php#L2114.
+ if ( $this->is_add_new_user_to_blog() ) {
+ return;
+ }
+
+ $reassigned_user_id = $this->get_reassigned_network_user_id();
+
+ // Note that we are in the context of the blog the user is removed from, see https://github.com/WordPress/WordPress/blob/473e1ba73bc5c18c72d7f288447503713d518790/wp-includes/ms-functions.php#L233.
+ /**
+ * Fires when a user is removed from a blog on a multisite installation
+ *
+ * @since 5.4.0
+ *
+ * @param int $user_id - ID of the removed user
+ * @param int $reassigned_user_id - ID of the user the removed user's posts are reassigned to (if any).
+ */
+ do_action( 'jetpack_removed_user_from_blog', $user_id, $reassigned_user_id );
+ }
+
+ /**
+ * Whether we're adding a new user to a blog in this request.
+ *
+ * @access protected
+ *
+ * @return boolean
+ */
+ protected function is_add_new_user_to_blog() {
+ return $this->is_function_in_backtrace( 'add_new_user_to_blog' );
+ }
+
+ /**
+ * Whether we're adding an existing user to a blog in this request.
+ *
+ * @access protected
+ *
+ * @return boolean
+ */
+ protected function is_add_user_to_blog() {
+ return $this->is_function_in_backtrace( 'add_user_to_blog' );
+ }
+
+ /**
+ * Whether we're removing a user from a blog in this request.
+ *
+ * @access protected
+ *
+ * @return boolean
+ */
+ protected function is_delete_user() {
+ return $this->is_function_in_backtrace( array( 'wp_delete_user', 'remove_user_from_blog' ) );
+ }
+
+ /**
+ * Whether we're creating a user or adding a new user to a blog.
+ *
+ * @access protected
+ *
+ * @return boolean
+ */
+ protected function is_create_user() {
+ $functions = array(
+ 'add_new_user_to_blog', // Used to suppress jetpack_sync_save_user in save_user_cap_handler when user registered on multi site.
+ 'wp_create_user', // Used to suppress jetpack_sync_save_user in save_user_role_handler when user registered on multi site.
+ 'wp_insert_user', // Used to suppress jetpack_sync_save_user in save_user_cap_handler and save_user_role_handler when user registered on single site.
+ );
+
+ return $this->is_function_in_backtrace( $functions );
+ }
+
+ /**
+ * Retrieve the ID of the user the removed user's posts are reassigned to (if any).
+ *
+ * @return int ID of the user that got reassigned as the author of the posts.
+ */
+ protected function get_reassigned_network_user_id() {
+ $backtrace = debug_backtrace( false ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
+ foreach ( $backtrace as $call ) {
+ if (
+ 'remove_user_from_blog' === $call['function'] &&
+ 3 === count( $call['args'] )
+ ) {
+ return $call['args'][2];
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if one or more function names is in debug_backtrace.
+ *
+ * @access protected
+ *
+ * @param array|string $names Mixed string name of function or array of string names of functions.
+ * @return bool
+ */
+ protected function is_function_in_backtrace( $names ) {
+ $backtrace = debug_backtrace( false ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
+ if ( ! is_array( $names ) ) {
+ $names = array( $names );
+ }
+ $names_as_keys = array_flip( $names );
+
+ // Do check in constant O(1) time for PHP5.5+.
+ if ( function_exists( 'array_column' ) ) {
+ $backtrace_functions = array_column( $backtrace, 'function' ); // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.array_columnFound
+ $backtrace_functions_as_keys = array_flip( $backtrace_functions );
+ $intersection = array_intersect_key( $backtrace_functions_as_keys, $names_as_keys );
+ return ! empty( $intersection );
+ }
+
+ // Do check in linear O(n) time for < PHP5.5 ( using isset at least prevents O(n^2) ).
+ foreach ( $backtrace as $call ) {
+ if ( isset( $names_as_keys[ $call['function'] ] ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/vendor/automattic/jetpack-sync/src/modules/class-woocommerce.php b/vendor/automattic/jetpack-sync/src/modules/class-woocommerce.php
new file mode 100644
index 0000000000000..f6c37b96dea09
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/modules/class-woocommerce.php
@@ -0,0 +1,559 @@
+order_item_table_name;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @global $wpdb
+ *
+ * @todo Should we refactor this to use $this->set_defaults() instead?
+ */
+ public function __construct() {
+ global $wpdb;
+ $this->order_item_table_name = $wpdb->prefix . 'woocommerce_order_items';
+
+ // Options, constants and post meta whitelists.
+ add_filter( 'jetpack_sync_options_whitelist', array( $this, 'add_woocommerce_options_whitelist' ), 10 );
+ add_filter( 'jetpack_sync_constants_whitelist', array( $this, 'add_woocommerce_constants_whitelist' ), 10 );
+ add_filter( 'jetpack_sync_post_meta_whitelist', array( $this, 'add_woocommerce_post_meta_whitelist' ), 10 );
+ add_filter( 'jetpack_sync_comment_meta_whitelist', array( $this, 'add_woocommerce_comment_meta_whitelist' ), 10 );
+
+ add_filter( 'jetpack_sync_before_enqueue_woocommerce_new_order_item', array( $this, 'filter_order_item' ) );
+ add_filter( 'jetpack_sync_before_enqueue_woocommerce_update_order_item', array( $this, 'filter_order_item' ) );
+ add_filter( 'jetpack_sync_whitelisted_comment_types', array( $this, 'add_review_comment_types' ) );
+
+ // Blacklist Action Scheduler comment types.
+ add_filter( 'jetpack_sync_prevent_sending_comment_data', array( $this, 'filter_action_scheduler_comments' ), 10, 2 );
+ }
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'woocommerce';
+ }
+
+ /**
+ * Initialize WooCommerce action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ // Attributes.
+ add_action( 'woocommerce_attribute_added', $callable, 10, 2 );
+ add_action( 'woocommerce_attribute_updated', $callable, 10, 3 );
+ add_action( 'woocommerce_attribute_deleted', $callable, 10, 3 );
+
+ // Orders.
+ add_action( 'woocommerce_new_order', $callable, 10, 1 );
+ add_action( 'woocommerce_order_status_changed', $callable, 10, 3 );
+ add_action( 'woocommerce_payment_complete', $callable, 10, 1 );
+
+ // Order items.
+ add_action( 'woocommerce_new_order_item', $callable, 10, 4 );
+ add_action( 'woocommerce_update_order_item', $callable, 10, 4 );
+ add_action( 'woocommerce_delete_order_item', $callable, 10, 1 );
+ $this->init_listeners_for_meta_type( 'order_item', $callable );
+
+ // Payment tokens.
+ add_action( 'woocommerce_new_payment_token', $callable, 10, 1 );
+ add_action( 'woocommerce_payment_token_deleted', $callable, 10, 2 );
+ add_action( 'woocommerce_payment_token_updated', $callable, 10, 1 );
+ $this->init_listeners_for_meta_type( 'payment_token', $callable );
+
+ // Product downloads.
+ add_action( 'woocommerce_downloadable_product_download_log_insert', $callable, 10, 1 );
+ add_action( 'woocommerce_grant_product_download_access', $callable, 10, 1 );
+
+ // Tax rates.
+ add_action( 'woocommerce_tax_rate_added', $callable, 10, 2 );
+ add_action( 'woocommerce_tax_rate_updated', $callable, 10, 2 );
+ add_action( 'woocommerce_tax_rate_deleted', $callable, 10, 1 );
+
+ // Webhooks.
+ add_action( 'woocommerce_new_webhook', $callable, 10, 1 );
+ add_action( 'woocommerce_webhook_deleted', $callable, 10, 2 );
+ add_action( 'woocommerce_webhook_updated', $callable, 10, 1 );
+ }
+
+ /**
+ * Initialize WooCommerce action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ add_action( 'jetpack_full_sync_woocommerce_order_items', $callable ); // Also sends post meta.
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_woocommerce_order_items' );
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ // Full sync.
+ add_filter( 'jetpack_sync_before_send_jetpack_full_sync_woocommerce_order_items', array( $this, 'expand_order_item_ids' ) );
+ }
+
+ /**
+ * Expand the order items properly.
+ *
+ * @access public
+ *
+ * @param array $args The hook arguments.
+ * @return array $args The hook arguments.
+ */
+ public function filter_order_item( $args ) {
+ // Make sure we always have all the data - prior to WooCommerce 3.0 we only have the user supplied data in the second argument and not the full details.
+ $args[1] = $this->build_order_item( $args[0] );
+ return $args;
+ }
+
+ /**
+ * Expand order item IDs to order items and their meta.
+ *
+ * @access public
+ *
+ * @todo Refactor table name to use a $wpdb->prepare placeholder.
+ *
+ * @param array $args The hook arguments.
+ * @return array $args Expanded order items with meta.
+ */
+ public function expand_order_item_ids( $args ) {
+ $order_item_ids = $args[0];
+
+ global $wpdb;
+
+ $order_item_ids_sql = implode( ', ', array_map( 'intval', $order_item_ids ) );
+
+ $order_items = $wpdb->get_results(
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ "SELECT * FROM $this->order_item_table_name WHERE order_item_id IN ( $order_item_ids_sql )"
+ );
+
+ return array(
+ $order_items,
+ $this->get_metadata( $order_item_ids, 'order_item', $this->order_item_meta_whitelist ),
+ );
+ }
+
+ /**
+ * Extract the full order item from the database by its ID.
+ *
+ * @access public
+ *
+ * @todo Refactor table name to use a $wpdb->prepare placeholder.
+ *
+ * @param int $order_item_id Order item ID.
+ * @return object Order item.
+ */
+ public function build_order_item( $order_item_id ) {
+ global $wpdb;
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ return $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $this->order_item_table_name WHERE order_item_id = %d", $order_item_id ) );
+ }
+
+ /**
+ * Enqueue the WooCommerce actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
+ return $this->enqueue_all_ids_as_action( 'jetpack_full_sync_woocommerce_order_items', $this->order_item_table_name, 'order_item_id', $this->get_where_sql( $config ), $max_items_to_enqueue, $state );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @todo Refactor the SQL query to use $wpdb->prepare().
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return array Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) {
+ global $wpdb;
+
+ $query = "SELECT count(*) FROM $this->order_item_table_name WHERE " . $this->get_where_sql( $config );
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $count = $wpdb->get_var( $query );
+
+ return (int) ceil( $count / self::ARRAY_CHUNK_SIZE );
+ }
+
+ /**
+ * Retrieve the WHERE SQL clause based on the module config.
+ *
+ * @access private
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return string WHERE SQL clause.
+ */
+ public function get_where_sql( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return '1=1';
+ }
+
+ /**
+ * Add WooCommerce options to the options whitelist.
+ *
+ * @param array $list Existing options whitelist.
+ * @return array Updated options whitelist.
+ */
+ public function add_woocommerce_options_whitelist( $list ) {
+ return array_merge( $list, self::$wc_options_whitelist );
+ }
+
+ /**
+ * Add WooCommerce constants to the constants whitelist.
+ *
+ * @param array $list Existing constants whitelist.
+ * @return array Updated constants whitelist.
+ */
+ public function add_woocommerce_constants_whitelist( $list ) {
+ return array_merge( $list, self::$wc_constants_whitelist );
+ }
+
+ /**
+ * Add WooCommerce post meta to the post meta whitelist.
+ *
+ * @param array $list Existing post meta whitelist.
+ * @return array Updated post meta whitelist.
+ */
+ public function add_woocommerce_post_meta_whitelist( $list ) {
+ return array_merge( $list, self::$wc_post_meta_whitelist );
+ }
+
+ /**
+ * Add WooCommerce comment meta to the comment meta whitelist.
+ *
+ * @param array $list Existing comment meta whitelist.
+ * @return array Updated comment meta whitelist.
+ */
+ public function add_woocommerce_comment_meta_whitelist( $list ) {
+ return array_merge( $list, self::$wc_comment_meta_whitelist );
+ }
+
+ /**
+ * Adds 'revew' to the list of comment types so Sync will listen for status changes on 'reviews'.
+ *
+ * @access public
+ *
+ * @param array $comment_types The list of comment types prior to this filter.
+ * return array The list of comment types with 'review' added.
+ */
+ public function add_review_comment_types( $comment_types ) {
+ if ( is_array( $comment_types ) ) {
+ $comment_types[] = 'review';
+ }
+ return $comment_types;
+ }
+
+ /**
+ * Stop comments from the Action Scheduler from being synced.
+ * https://github.com/woocommerce/woocommerce/tree/e7762627c37ec1f7590e6cac4218ba0c6a20024d/includes/libraries/action-scheduler
+ *
+ * @since 7.7.0
+ *
+ * @param boolean $can_sync Should we prevent comment data from bing synced to WordPress.com.
+ * @param mixed $comment WP_COMMENT object.
+ *
+ * @return bool
+ */
+ public function filter_action_scheduler_comments( $can_sync, $comment ) {
+ if ( isset( $comment->comment_agent ) && 'ActionScheduler' === $comment->comment_agent ) {
+ return true;
+ }
+ return $can_sync;
+ }
+
+ /**
+ * Whitelist for options we are interested to sync.
+ *
+ * @access private
+ * @static
+ *
+ * @var array
+ */
+ private static $wc_options_whitelist = array(
+ 'woocommerce_currency',
+ 'woocommerce_db_version',
+ 'woocommerce_weight_unit',
+ 'woocommerce_version',
+ 'woocommerce_unforce_ssl_checkout',
+ 'woocommerce_tax_total_display',
+ 'woocommerce_tax_round_at_subtotal',
+ 'woocommerce_tax_display_shop',
+ 'woocommerce_tax_display_cart',
+ 'woocommerce_prices_include_tax',
+ 'woocommerce_price_thousand_sep',
+ 'woocommerce_price_num_decimals',
+ 'woocommerce_price_decimal_sep',
+ 'woocommerce_notify_low_stock',
+ 'woocommerce_notify_low_stock_amount',
+ 'woocommerce_notify_no_stock',
+ 'woocommerce_notify_no_stock_amount',
+ 'woocommerce_manage_stock',
+ 'woocommerce_force_ssl_checkout',
+ 'woocommerce_hide_out_of_stock_items',
+ 'woocommerce_file_download_method',
+ 'woocommerce_enable_signup_and_login_from_checkout',
+ 'woocommerce_enable_shipping_calc',
+ 'woocommerce_enable_review_rating',
+ 'woocommerce_enable_guest_checkout',
+ 'woocommerce_enable_coupons',
+ 'woocommerce_enable_checkout_login_reminder',
+ 'woocommerce_enable_ajax_add_to_cart',
+ 'woocommerce_dimension_unit',
+ 'woocommerce_default_country',
+ 'woocommerce_default_customer_address',
+ 'woocommerce_currency_pos',
+ 'woocommerce_api_enabled',
+ 'woocommerce_allow_tracking',
+ 'woocommerce_task_list_hidden',
+ 'woocommerce_onboarding_profile',
+ );
+
+ /**
+ * Whitelist for constants we are interested to sync.
+ *
+ * @access private
+ * @static
+ *
+ * @var array
+ */
+ private static $wc_constants_whitelist = array(
+ // WooCommerce constants.
+ 'WC_PLUGIN_FILE',
+ 'WC_ABSPATH',
+ 'WC_PLUGIN_BASENAME',
+ 'WC_VERSION',
+ 'WOOCOMMERCE_VERSION',
+ 'WC_ROUNDING_PRECISION',
+ 'WC_DISCOUNT_ROUNDING_MODE',
+ 'WC_TAX_ROUNDING_MODE',
+ 'WC_DELIMITER',
+ 'WC_LOG_DIR',
+ 'WC_SESSION_CACHE_GROUP',
+ 'WC_TEMPLATE_DEBUG_MODE',
+ );
+
+ /**
+ * Whitelist for post meta we are interested to sync.
+ *
+ * @access private
+ * @static
+ *
+ * @var array
+ */
+ private static $wc_post_meta_whitelist = array(
+ // WooCommerce products.
+ // See https://github.com/woocommerce/woocommerce/blob/8ed6e7436ff87c2153ed30edd83c1ab8abbdd3e9/includes/data-stores/class-wc-product-data-store-cpt.php#L21 .
+ '_visibility',
+ '_sku',
+ '_price',
+ '_regular_price',
+ '_sale_price',
+ '_sale_price_dates_from',
+ '_sale_price_dates_to',
+ 'total_sales',
+ '_tax_status',
+ '_tax_class',
+ '_manage_stock',
+ '_backorders',
+ '_sold_individually',
+ '_weight',
+ '_length',
+ '_width',
+ '_height',
+ '_upsell_ids',
+ '_crosssell_ids',
+ '_purchase_note',
+ '_default_attributes',
+ '_product_attributes',
+ '_virtual',
+ '_downloadable',
+ '_download_limit',
+ '_download_expiry',
+ '_featured',
+ '_downloadable_files',
+ '_wc_rating_count',
+ '_wc_average_rating',
+ '_wc_review_count',
+ '_variation_description',
+ '_thumbnail_id',
+ '_file_paths',
+ '_product_image_gallery',
+ '_product_version',
+ '_wp_old_slug',
+
+ // Woocommerce orders.
+ // See https://github.com/woocommerce/woocommerce/blob/8ed6e7436ff87c2153ed30edd83c1ab8abbdd3e9/includes/data-stores/class-wc-order-data-store-cpt.php#L27 .
+ '_order_key',
+ '_order_currency',
+ // '_billing_first_name', do not sync these as they contain personal data
+ // '_billing_last_name',
+ // '_billing_company',
+ // '_billing_address_1',
+ // '_billing_address_2',
+ '_billing_city',
+ '_billing_state',
+ '_billing_postcode',
+ '_billing_country',
+ // '_billing_email', do not sync these as they contain personal data.
+ // '_billing_phone',
+ // '_shipping_first_name',
+ // '_shipping_last_name',
+ // '_shipping_company',
+ // '_shipping_address_1',
+ // '_shipping_address_2',
+ '_shipping_city',
+ '_shipping_state',
+ '_shipping_postcode',
+ '_shipping_country',
+ '_completed_date',
+ '_paid_date',
+ '_cart_discount',
+ '_cart_discount_tax',
+ '_order_shipping',
+ '_order_shipping_tax',
+ '_order_tax',
+ '_order_total',
+ '_payment_method',
+ '_payment_method_title',
+ // '_transaction_id', do not sync these as they contain personal data.
+ // '_customer_ip_address',
+ // '_customer_user_agent',
+ '_created_via',
+ '_order_version',
+ '_prices_include_tax',
+ '_date_completed',
+ '_date_paid',
+ '_payment_tokens',
+ '_billing_address_index',
+ '_shipping_address_index',
+ '_recorded_sales',
+ '_recorded_coupon_usage_counts',
+ // See https://github.com/woocommerce/woocommerce/blob/8ed6e7436ff87c2153ed30edd83c1ab8abbdd3e9/includes/data-stores/class-wc-order-data-store-cpt.php#L539 .
+ '_download_permissions_granted',
+ // See https://github.com/woocommerce/woocommerce/blob/8ed6e7436ff87c2153ed30edd83c1ab8abbdd3e9/includes/data-stores/class-wc-order-data-store-cpt.php#L594 .
+ '_order_stock_reduced',
+
+ // Woocommerce order refunds.
+ // See https://github.com/woocommerce/woocommerce/blob/b8a2815ae546c836467008739e7ff5150cb08e93/includes/data-stores/class-wc-order-refund-data-store-cpt.php#L20 .
+ '_order_currency',
+ '_refund_amount',
+ '_refunded_by',
+ '_refund_reason',
+ '_order_shipping',
+ '_order_shipping_tax',
+ '_order_tax',
+ '_order_total',
+ '_order_version',
+ '_prices_include_tax',
+ '_payment_tokens',
+ );
+
+ /**
+ * Whitelist for comment meta we are interested to sync.
+ *
+ * @access private
+ * @static
+ *
+ * @var array
+ */
+ private static $wc_comment_meta_whitelist = array(
+ 'rating',
+ );
+}
diff --git a/vendor/automattic/jetpack-sync/src/modules/class-wp-super-cache.php b/vendor/automattic/jetpack-sync/src/modules/class-wp-super-cache.php
new file mode 100644
index 0000000000000..af4aec419456d
--- /dev/null
+++ b/vendor/automattic/jetpack-sync/src/modules/class-wp-super-cache.php
@@ -0,0 +1,156 @@
+set_defaults() instead?
+ */
+ public function __construct() {
+ add_filter( 'jetpack_sync_constants_whitelist', array( $this, 'add_wp_super_cache_constants_whitelist' ), 10 );
+ add_filter( 'jetpack_sync_callable_whitelist', array( $this, 'add_wp_super_cache_callable_whitelist' ), 10 );
+ }
+
+ /**
+ * Whitelist for constants we are interested to sync.
+ *
+ * @access public
+ * @static
+ *
+ * @var array
+ */
+ public static $wp_super_cache_constants = array(
+ 'WPLOCKDOWN',
+ 'WPSC_DISABLE_COMPRESSION',
+ 'WPSC_DISABLE_LOCKING',
+ 'WPSC_DISABLE_HTACCESS_UPDATE',
+ 'ADVANCEDCACHEPROBLEM',
+ );
+
+ /**
+ * Container for the whitelist for WP_Super_Cache callables we are interested to sync.
+ *
+ * @access public
+ * @static
+ *
+ * @var array
+ */
+ public static $wp_super_cache_callables = array(
+ 'wp_super_cache_globals' => array( __CLASS__, 'get_wp_super_cache_globals' ),
+ );
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'wp-super-cache';
+ }
+
+ /**
+ * Retrieve all WP_Super_Cache callables we are interested to sync.
+ *
+ * @access public
+ *
+ * @global $wp_cache_mod_rewrite;
+ * @global $cache_enabled;
+ * @global $super_cache_enabled;
+ * @global $ossdlcdn;
+ * @global $cache_rebuild_files;
+ * @global $wp_cache_mobile;
+ * @global $wp_super_cache_late_init;
+ * @global $wp_cache_anon_only;
+ * @global $wp_cache_not_logged_in;
+ * @global $wp_cache_clear_on_post_edit;
+ * @global $wp_cache_mobile_enabled;
+ * @global $wp_super_cache_debug;
+ * @global $cache_max_time;
+ * @global $wp_cache_refresh_single_only;
+ * @global $wp_cache_mfunc_enabled;
+ * @global $wp_supercache_304;
+ * @global $wp_cache_no_cache_for_get;
+ * @global $wp_cache_mutex_disabled;
+ * @global $cache_jetpack;
+ * @global $cache_domain_mapping;
+ *
+ * @return array All WP_Super_Cache callables.
+ */
+ public static function get_wp_super_cache_globals() {
+ global $wp_cache_mod_rewrite;
+ global $cache_enabled;
+ global $super_cache_enabled;
+ global $ossdlcdn;
+ global $cache_rebuild_files;
+ global $wp_cache_mobile;
+ global $wp_super_cache_late_init;
+ global $wp_cache_anon_only;
+ global $wp_cache_not_logged_in;
+ global $wp_cache_clear_on_post_edit;
+ global $wp_cache_mobile_enabled;
+ global $wp_super_cache_debug;
+ global $cache_max_time;
+ global $wp_cache_refresh_single_only;
+ global $wp_cache_mfunc_enabled;
+ global $wp_supercache_304;
+ global $wp_cache_no_cache_for_get;
+ global $wp_cache_mutex_disabled;
+ global $cache_jetpack;
+ global $cache_domain_mapping;
+
+ return array(
+ 'wp_cache_mod_rewrite' => $wp_cache_mod_rewrite,
+ 'cache_enabled' => $cache_enabled,
+ 'super_cache_enabled' => $super_cache_enabled,
+ 'ossdlcdn' => $ossdlcdn,
+ 'cache_rebuild_files' => $cache_rebuild_files,
+ 'wp_cache_mobile' => $wp_cache_mobile,
+ 'wp_super_cache_late_init' => $wp_super_cache_late_init,
+ 'wp_cache_anon_only' => $wp_cache_anon_only,
+ 'wp_cache_not_logged_in' => $wp_cache_not_logged_in,
+ 'wp_cache_clear_on_post_edit' => $wp_cache_clear_on_post_edit,
+ 'wp_cache_mobile_enabled' => $wp_cache_mobile_enabled,
+ 'wp_super_cache_debug' => $wp_super_cache_debug,
+ 'cache_max_time' => $cache_max_time,
+ 'wp_cache_refresh_single_only' => $wp_cache_refresh_single_only,
+ 'wp_cache_mfunc_enabled' => $wp_cache_mfunc_enabled,
+ 'wp_supercache_304' => $wp_supercache_304,
+ 'wp_cache_no_cache_for_get' => $wp_cache_no_cache_for_get,
+ 'wp_cache_mutex_disabled' => $wp_cache_mutex_disabled,
+ 'cache_jetpack' => $cache_jetpack,
+ 'cache_domain_mapping' => $cache_domain_mapping,
+ );
+ }
+
+ /**
+ * Add WP_Super_Cache constants to the constants whitelist.
+ *
+ * @param array $list Existing constants whitelist.
+ * @return array Updated constants whitelist.
+ */
+ public function add_wp_super_cache_constants_whitelist( $list ) {
+ return array_merge( $list, self::$wp_super_cache_constants );
+ }
+
+ /**
+ * Add WP_Super_Cache callables to the callables whitelist.
+ *
+ * @param array $list Existing callables whitelist.
+ * @return array Updated callables whitelist.
+ */
+ public function add_wp_super_cache_callable_whitelist( $list ) {
+ return array_merge( $list, self::$wp_super_cache_callables );
+ }
+}
diff --git a/vendor/automattic/jetpack-terms-of-service/src/class-terms-of-service.php b/vendor/automattic/jetpack-terms-of-service/src/class-terms-of-service.php
new file mode 100644
index 0000000000000..ec6d566e43447
--- /dev/null
+++ b/vendor/automattic/jetpack-terms-of-service/src/class-terms-of-service.php
@@ -0,0 +1,110 @@
+set_agree();
+ /**
+ * Acton fired when the master user has agreed to the terms of service.
+ *
+ * @since 7.9.0
+ */
+ do_action( 'jetpack_agreed_to_terms_of_service' );
+ }
+
+ /**
+ * Allow the site to reject to the terms of service.
+ */
+ public function reject() {
+ $this->set_reject();
+ /**
+ * Acton fired when the master user has revoked their agreement to the terms of service.
+ *
+ * @since 7.9.1
+ */
+ do_action( 'jetpack_reject_terms_of_service' );
+ }
+
+ /**
+ * Returns whether the master user has agreed to the terms of service.
+ *
+ * The following conditions have to be met in order to agree to the terms of service.
+ * 1. The master user has gone though the connect flow.
+ * 2. The site is not in dev mode.
+ * 3. The master user of the site is still connected (deprecated @since 8.9.0).
+ *
+ * @return bool
+ */
+ public function has_agreed() {
+ if ( $this->is_offline_mode() ) {
+ return false;
+ }
+ /**
+ * Before 8.9.0 we used to also check if the master user of the site is connected
+ * by calling the Connection related `is_active` method.
+ * As of 8.9.0 we have removed this check in order to resolve the
+ * circular dependencies it was introducing to composer packages.
+ *
+ * @since 8.9.0
+ */
+ return $this->get_raw_has_agreed();
+ }
+
+ /**
+ * Abstracted for testing purposes.
+ * Tells us if the site is in dev mode.
+ *
+ * @return bool
+ */
+ protected function is_offline_mode() {
+ return ( new Status() )->is_offline_mode();
+ }
+
+ /**
+ * Gets just the Jetpack Option that contains the terms of service state.
+ * Abstracted for testing purposes.
+ *
+ * @return bool
+ */
+ protected function get_raw_has_agreed() {
+ return \Jetpack_Options::get_option( self::OPTION_NAME, false );
+ }
+
+ /**
+ * Sets the correct Jetpack Option to mark the that the site has agreed to the terms of service.
+ * Abstracted for testing purposes.
+ */
+ protected function set_agree() {
+ \Jetpack_Options::update_option( self::OPTION_NAME, true );
+ }
+
+ /**
+ * Sets the correct Jetpack Option to mark that the site has rejected the terms of service.
+ * Abstracted for testing purposes.
+ */
+ protected function set_reject() {
+ \Jetpack_Options::update_option( self::OPTION_NAME, false );
+ }
+
+}
diff --git a/vendor/automattic/jetpack-tracking/legacy/class-jetpack-tracks-client.php b/vendor/automattic/jetpack-tracking/legacy/class-jetpack-tracks-client.php
new file mode 100644
index 0000000000000..0ea251842655e
--- /dev/null
+++ b/vendor/automattic/jetpack-tracking/legacy/class-jetpack-tracks-client.php
@@ -0,0 +1,228 @@
+ $event_name, // required
+ '_ui' => $user_id, // required unless _ul is provided
+ '_ul' => $user_login, // required unless _ui is provided
+
+ // Optional, but recommended
+ '_ts' => $ts_in_ms, // Default: now
+ '_via_ip' => $client_ip, // we use it for geo, etc.
+
+ // Possibly useful to set some context for the event
+ '_via_ua' => $client_user_agent,
+ '_via_url' => $client_url,
+ '_via_ref' => $client_referrer,
+
+ // For user-targeted tests
+ 'abtest_name' => $abtest_name,
+ 'abtest_variation' => $abtest_variation,
+
+ // Your application-specific properties
+ 'custom_property' => $some_value,
+ ) );
+
+ if ( is_wp_error( $result ) ) {
+ // Handle the error in your app
+ }
+```
+ */
+class Jetpack_Tracks_Client {
+ const PIXEL = 'https://pixel.wp.com/t.gif';
+ const BROWSER_TYPE = 'php-agent';
+ const USER_AGENT_SLUG = 'tracks-client';
+ const VERSION = '0.3';
+
+ /**
+ * Stores the Terms of Service Object Reference.
+ *
+ * @var null
+ */
+ private static $terms_of_service = null;
+
+ /**
+ * Record an event.
+ *
+ * @param mixed $event Event object to send to Tracks. An array will be cast to object. Required.
+ * Properties are included directly in the pixel query string after light validation.
+ * @return mixed True on success, WP_Error on failure
+ */
+ public static function record_event( $event ) {
+ if ( ! self::$terms_of_service ) {
+ self::$terms_of_service = new \Automattic\Jetpack\Terms_Of_Service();
+ }
+
+ // Don't track users who have opted out or not agreed to our TOS, or are not running an active Jetpack.
+ if ( ! self::$terms_of_service->has_agreed() || ! empty( $_COOKIE['tk_opt-out'] ) ) {
+ return false;
+ }
+
+ if ( ! $event instanceof Jetpack_Tracks_Event ) {
+ $event = new Jetpack_Tracks_Event( $event );
+ }
+ if ( is_wp_error( $event ) ) {
+ return $event;
+ }
+
+ $pixel = $event->build_pixel_url( $event );
+
+ if ( ! $pixel ) {
+ return new WP_Error( 'invalid_pixel', 'cannot generate tracks pixel for given input', 400 );
+ }
+
+ return self::record_pixel( $pixel );
+ }
+
+ /**
+ * Synchronously request the pixel.
+ *
+ * @param string $pixel The wp.com tracking pixel.
+ * @return array|bool|WP_Error True if successful. wp_remote_get response or WP_Error if not.
+ */
+ public static function record_pixel( $pixel ) {
+ // Add the Request Timestamp and URL terminator just before the HTTP request.
+ $pixel .= '&_rt=' . self::build_timestamp() . '&_=_';
+
+ $response = wp_remote_get(
+ $pixel,
+ array(
+ 'blocking' => true, // The default, but being explicit here :).
+ 'timeout' => 1,
+ 'redirection' => 2,
+ 'httpversion' => '1.1',
+ 'user-agent' => self::get_user_agent(),
+ )
+ );
+
+ if ( is_wp_error( $response ) ) {
+ return $response;
+ }
+
+ $code = isset( $response['response']['code'] ) ? $response['response']['code'] : 0;
+
+ if ( 200 !== $code ) {
+ return new WP_Error( 'request_failed', 'Tracks pixel request failed', $code );
+ }
+
+ return true;
+ }
+
+ /**
+ * Get the user agent.
+ *
+ * @return string The user agent.
+ */
+ public static function get_user_agent() {
+ return self::USER_AGENT_SLUG . '-v' . self::VERSION;
+ }
+
+ /**
+ * Build an event and return its tracking URL
+ *
+ * @deprecated Call the `build_pixel_url` method on a Jetpack_Tracks_Event object instead.
+ * @param array $event Event keys and values.
+ * @return string URL of a tracking pixel.
+ */
+ public static function build_pixel_url( $event ) {
+ $_event = new Jetpack_Tracks_Event( $event );
+ return $_event->build_pixel_url();
+ }
+
+ /**
+ * Validate input for a tracks event.
+ *
+ * @deprecated Instantiate a Jetpack_Tracks_Event object instead
+ * @param array $event Event keys and values.
+ * @return mixed Validated keys and values or WP_Error on failure
+ */
+ private static function validate_and_sanitize( $event ) {
+ $_event = new Jetpack_Tracks_Event( $event );
+ if ( is_wp_error( $_event ) ) {
+ return $_event;
+ }
+ return get_object_vars( $_event );
+ }
+
+ /**
+ * Builds a timestamp.
+ *
+ * Milliseconds since 1970-01-01.
+ *
+ * @return string
+ */
+ public static function build_timestamp() {
+ $ts = round( microtime( true ) * 1000 );
+ return number_format( $ts, 0, '', '' );
+ }
+
+ /**
+ * Grabs the user's anon id from cookies, or generates and sets a new one
+ *
+ * @return string An anon id for the user
+ */
+ public static function get_anon_id() {
+ static $anon_id = null;
+
+ if ( ! isset( $anon_id ) ) {
+
+ // Did the browser send us a cookie?
+ if ( isset( $_COOKIE['tk_ai'] ) && preg_match( '#^[A-Za-z0-9+/=]{24}$#', $_COOKIE['tk_ai'] ) ) {
+ $anon_id = $_COOKIE['tk_ai'];
+ } else {
+
+ $binary = '';
+
+ // Generate a new anonId and try to save it in the browser's cookies.
+ // Note that base64-encoding an 18 character string generates a 24-character anon id.
+ for ( $i = 0; $i < 18; ++$i ) {
+ $binary .= chr( wp_rand( 0, 255 ) );
+ }
+
+ $anon_id = 'jetpack:' . base64_encode( $binary ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
+
+ if ( ! headers_sent()
+ && ! ( defined( 'REST_REQUEST' ) && REST_REQUEST )
+ && ! ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST )
+ ) {
+ setcookie( 'tk_ai', $anon_id );
+ }
+ }
+ }
+
+ return $anon_id;
+ }
+
+ /**
+ * Gets the WordPress.com user's Tracks identity, if connected.
+ *
+ * @return array|bool
+ */
+ public static function get_connected_user_tracks_identity() {
+ $user_data = Jetpack::get_connected_user_data();
+ if ( ! $user_data ) {
+ return false;
+ }
+
+ return array(
+ 'blogid' => Jetpack_Options::get_option( 'id', 0 ),
+ 'userid' => $user_data['ID'],
+ 'username' => $user_data['login'],
+ );
+ }
+}
diff --git a/vendor/automattic/jetpack-tracking/legacy/class-jetpack-tracks-event.php b/vendor/automattic/jetpack-tracking/legacy/class-jetpack-tracks-event.php
new file mode 100644
index 0000000000000..be77a397b03fc
--- /dev/null
+++ b/vendor/automattic/jetpack-tracking/legacy/class-jetpack-tracks-event.php
@@ -0,0 +1,189 @@
+ $event_name, // required
+ '_ui' => $user_id, // required unless _ul is provided
+ '_ul' => $user_login, // required unless _ui is provided
+
+ // Optional, but recommended
+ '_via_ip' => $client_ip, // for geo, etc.
+
+ // Possibly useful to set some context for the event
+ '_via_ua' => $client_user_agent,
+ '_via_url' => $client_url,
+ '_via_ref' => $client_referrer,
+
+ // For user-targeted tests
+ 'abtest_name' => $abtest_name,
+ 'abtest_variation' => $abtest_variation,
+
+ // Your application-specific properties
+ 'custom_property' => $some_value,
+ ) );
+
+ if ( is_wp_error( $event->error ) ) {
+ // Handle the error in your app
+ }
+
+ $bump_and_redirect_pixel = $event->build_signed_pixel_url();
+```
+ */
+
+/**
+ * Class Jetpack_Tracks_Event
+ */
+class Jetpack_Tracks_Event {
+ const EVENT_NAME_REGEX = '/^(([a-z0-9]+)_){2}([a-z0-9_]+)$/';
+ const PROP_NAME_REGEX = '/^[a-z_][a-z0-9_]*$/';
+
+ /**
+ * Tracks Event Error.
+ *
+ * @var mixed Error.
+ */
+ public $error;
+
+ /**
+ * Jetpack_Tracks_Event constructor.
+ *
+ * @param object $event Tracks event.
+ */
+ public function __construct( $event ) {
+ $_event = self::validate_and_sanitize( $event );
+ if ( is_wp_error( $_event ) ) {
+ $this->error = $_event;
+ return;
+ }
+
+ foreach ( $_event as $key => $value ) {
+ $this->{$key} = $value;
+ }
+ }
+
+ /**
+ * Record a track event.
+ */
+ public function record() {
+ return Jetpack_Tracks_Client::record_event( $this );
+ }
+
+ /**
+ * Annotate the event with all relevant info.
+ *
+ * @param mixed $event Object or (flat) array.
+ * @return mixed The transformed event array or WP_Error on failure.
+ */
+ public static function validate_and_sanitize( $event ) {
+ $event = (object) $event;
+
+ // Required.
+ if ( ! $event->_en ) {
+ return new WP_Error( 'invalid_event', 'A valid event must be specified via `_en`', 400 );
+ }
+
+ // delete non-routable addresses otherwise geoip will discard the record entirely.
+ if ( property_exists( $event, '_via_ip' ) && preg_match( '/^192\.168|^10\./', $event->_via_ip ) ) {
+ unset( $event->_via_ip );
+ }
+
+ $validated = array(
+ 'browser_type' => Jetpack_Tracks_Client::BROWSER_TYPE,
+ '_aua' => Jetpack_Tracks_Client::get_user_agent(),
+ );
+
+ $_event = (object) array_merge( (array) $event, $validated );
+
+ // If you want to block property names, do it here.
+
+ // Make sure we have an event timestamp.
+ if ( ! isset( $_event->_ts ) ) {
+ $_event->_ts = Jetpack_Tracks_Client::build_timestamp();
+ }
+
+ return $_event;
+ }
+
+ /**
+ * Build a pixel URL that will send a Tracks event when fired.
+ * On error, returns an empty string ('').
+ *
+ * @return string A pixel URL or empty string ('') if there were invalid args.
+ */
+ public function build_pixel_url() {
+ if ( $this->error ) {
+ return '';
+ }
+
+ $args = get_object_vars( $this );
+
+ // Request Timestamp and URL Terminator must be added just before the HTTP request or not at all.
+ unset( $args['_rt'] );
+ unset( $args['_'] );
+
+ $validated = self::validate_and_sanitize( $args );
+
+ if ( is_wp_error( $validated ) ) {
+ return '';
+ }
+
+ return Jetpack_Tracks_Client::PIXEL . '?' . http_build_query( $validated );
+ }
+
+ /**
+ * Validate the event name.
+ *
+ * @param string $name Event name.
+ * @return false|int
+ */
+ public static function event_name_is_valid( $name ) {
+ return preg_match( self::EVENT_NAME_REGEX, $name );
+ }
+
+ /**
+ * Validates prop name
+ *
+ * @param string $name Property name.
+ *
+ * @return false|int Truthy value.
+ */
+ public static function prop_name_is_valid( $name ) {
+ return preg_match( self::PROP_NAME_REGEX, $name );
+ }
+
+ /**
+ * Scrutinize event name.
+ *
+ * @param object $event Tracks event.
+ */
+ public static function scrutinize_event_names( $event ) {
+ if ( ! self::event_name_is_valid( $event->_en ) ) {
+ return;
+ }
+
+ $whitelisted_key_names = array(
+ 'anonId',
+ 'Browser_Type',
+ );
+
+ foreach ( array_keys( (array) $event ) as $key ) {
+ if ( in_array( $key, $whitelisted_key_names, true ) ) {
+ continue;
+ }
+ if ( ! self::prop_name_is_valid( $key ) ) {
+ return;
+ }
+ }
+ }
+}
diff --git a/vendor/automattic/jetpack-tracking/src/class-tracking.php b/vendor/automattic/jetpack-tracking/src/class-tracking.php
new file mode 100644
index 0000000000000..71464e70c034f
--- /dev/null
+++ b/vendor/automattic/jetpack-tracking/src/class-tracking.php
@@ -0,0 +1,217 @@
+product_name = $product_name;
+ $this->connection = $connection;
+ if ( is_null( $this->connection ) ) {
+ // TODO We should always pass a Connection.
+ $this->connection = new Connection\Manager();
+ }
+ }
+
+ /**
+ * Enqueue script necessary for tracking.
+ */
+ public function enqueue_tracks_scripts() {
+ wp_enqueue_script( 'jptracks', plugins_url( '_inc/lib/tracks/tracks-ajax.js', JETPACK__PLUGIN_FILE ), array(), JETPACK__VERSION, true );
+ wp_localize_script(
+ 'jptracks',
+ 'jpTracksAJAX',
+ array(
+ 'ajaxurl' => admin_url( 'admin-ajax.php' ),
+ 'jpTracksAJAX_nonce' => wp_create_nonce( 'jp-tracks-ajax-nonce' ),
+ )
+ );
+ }
+
+ /**
+ * Send an event in Tracks.
+ *
+ * @param string $event_type Type of the event.
+ * @param array $data Data to send with the event.
+ * @param mixed $user username, user_id, or WP_user object.
+ */
+ public function record_user_event( $event_type, $data = array(), $user = null ) {
+ if ( ! $user ) {
+ $user = wp_get_current_user();
+ }
+ $site_url = get_option( 'siteurl' );
+
+ $data['_via_ua'] = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '';
+ $data['_via_ip'] = isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
+ $data['_lg'] = isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : '';
+ $data['blog_url'] = $site_url;
+ $data['blog_id'] = \Jetpack_Options::get_option( 'id' );
+
+ // Top level events should not be namespaced.
+ if ( '_aliasUser' !== $event_type ) {
+ $event_type = $this->product_name . '_' . $event_type;
+ }
+
+ $data['jetpack_version'] = defined( 'JETPACK__VERSION' ) ? JETPACK__VERSION : '0';
+
+ return $this->tracks_record_event( $user, $event_type, $data );
+ }
+
+ /**
+ * Record an event in Tracks - this is the preferred way to record events from PHP.
+ *
+ * @param mixed $user username, user_id, or WP_user object.
+ * @param string $event_name The name of the event.
+ * @param array $properties Custom properties to send with the event.
+ * @param int $event_timestamp_millis The time in millis since 1970-01-01 00:00:00 when the event occurred.
+ *
+ * @return bool true for success | \WP_Error if the event pixel could not be fired
+ */
+ public function tracks_record_event( $user, $event_name, $properties = array(), $event_timestamp_millis = false ) {
+
+ // We don't want to track user events during unit tests/CI runs.
+ if ( $user instanceof \WP_User && 'wptests_capabilities' === $user->cap_key ) {
+ return false;
+ }
+ $terms_of_service = new Terms_Of_Service();
+ $status = new Status();
+ // Don't track users who have not agreed to our TOS.
+ if ( ! $this->should_enable_tracking( $terms_of_service, $status ) ) {
+ return false;
+ }
+
+ $event_obj = $this->tracks_build_event_obj( $user, $event_name, $properties, $event_timestamp_millis );
+
+ if ( is_wp_error( $event_obj->error ) ) {
+ return $event_obj->error;
+ }
+
+ return $event_obj->record();
+ }
+
+ /**
+ * Determines whether tracking should be enabled.
+ *
+ * @param Automattic\Jetpack\Terms_Of_Service $terms_of_service A Terms_Of_Service object.
+ * @param Automattic\Jetpack\Status $status A Status object.
+ *
+ * @return boolean True if tracking should be enabled, else false.
+ */
+ public function should_enable_tracking( $terms_of_service, $status ) {
+ if ( $status->is_offline_mode() ) {
+ return false;
+ }
+
+ return $terms_of_service->has_agreed() || $this->connection->is_user_connected();
+ }
+
+ /**
+ * Procedurally build a Tracks Event Object.
+ * NOTE: Use this only when the simpler Automattic\Jetpack\Tracking->jetpack_tracks_record_event() function won't work for you.
+ *
+ * @param WP_user $user WP_user object.
+ * @param string $event_name The name of the event.
+ * @param array $properties Custom properties to send with the event.
+ * @param int $event_timestamp_millis The time in millis since 1970-01-01 00:00:00 when the event occurred.
+ *
+ * @return \Jetpack_Tracks_Event|\WP_Error
+ */
+ private function tracks_build_event_obj( $user, $event_name, $properties = array(), $event_timestamp_millis = false ) {
+ $identity = $this->tracks_get_identity( $user->ID );
+
+ $properties['user_lang'] = $user->get( 'WPLANG' );
+
+ $blog_details = array(
+ 'blog_lang' => isset( $properties['blog_lang'] ) ? $properties['blog_lang'] : get_bloginfo( 'language' ),
+ );
+
+ $timestamp = ( false !== $event_timestamp_millis ) ? $event_timestamp_millis : round( microtime( true ) * 1000 );
+ $timestamp_string = is_string( $timestamp ) ? $timestamp : number_format( $timestamp, 0, '', '' );
+
+ return new \Jetpack_Tracks_Event(
+ array_merge(
+ $blog_details,
+ (array) $properties,
+ $identity,
+ array(
+ '_en' => $event_name,
+ '_ts' => $timestamp_string,
+ )
+ )
+ );
+ }
+
+ /**
+ * Get the identity to send to tracks.
+ *
+ * @param int $user_id The user id of the local user.
+ *
+ * @return array $identity
+ */
+ public function tracks_get_identity( $user_id ) {
+
+ // Meta is set, and user is still connected. Use WPCOM ID.
+ $wpcom_id = get_user_meta( $user_id, 'jetpack_tracks_wpcom_id', true );
+ if ( $wpcom_id && $this->connection->is_user_connected( $user_id ) ) {
+ return array(
+ '_ut' => 'wpcom:user_id',
+ '_ui' => $wpcom_id,
+ );
+ }
+
+ // User is connected, but no meta is set yet. Use WPCOM ID and set meta.
+ if ( $this->connection->is_user_connected( $user_id ) ) {
+ $wpcom_user_data = $this->connection->get_connected_user_data( $user_id );
+ update_user_meta( $user_id, 'jetpack_tracks_wpcom_id', $wpcom_user_data['ID'] );
+
+ return array(
+ '_ut' => 'wpcom:user_id',
+ '_ui' => $wpcom_user_data['ID'],
+ );
+ }
+
+ // User isn't linked at all. Fall back to anonymous ID.
+ $anon_id = get_user_meta( $user_id, 'jetpack_tracks_anon_id', true );
+ if ( ! $anon_id ) {
+ $anon_id = \Jetpack_Tracks_Client::get_anon_id();
+ add_user_meta( $user_id, 'jetpack_tracks_anon_id', $anon_id, false );
+ }
+
+ if ( ! isset( $_COOKIE['tk_ai'] ) && ! headers_sent() ) {
+ setcookie( 'tk_ai', $anon_id );
+ }
+
+ return array(
+ '_ut' => 'anon',
+ '_ui' => $anon_id,
+ );
+
+ }
+}
diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php
new file mode 100644
index 0000000000000..03b9bb9c40cb8
--- /dev/null
+++ b/vendor/composer/ClassLoader.php
@@ -0,0 +1,445 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ * $loader = new \Composer\Autoload\ClassLoader();
+ *
+ * // register classes with namespaces
+ * $loader->add('Symfony\Component', __DIR__.'/component');
+ * $loader->add('Symfony', __DIR__.'/framework');
+ *
+ * // activate the autoloader
+ * $loader->register();
+ *
+ * // to enable searching the include path (eg. for PEAR packages)
+ * $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier
+ * @author Jordi Boggiano
+ * @see http://www.php-fig.org/psr/psr-0/
+ * @see http://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+ // PSR-4
+ private $prefixLengthsPsr4 = array();
+ private $prefixDirsPsr4 = array();
+ private $fallbackDirsPsr4 = array();
+
+ // PSR-0
+ private $prefixesPsr0 = array();
+ private $fallbackDirsPsr0 = array();
+
+ private $useIncludePath = false;
+ private $classMap = array();
+ private $classMapAuthoritative = false;
+ private $missingClasses = array();
+ private $apcuPrefix;
+
+ public function getPrefixes()
+ {
+ if (!empty($this->prefixesPsr0)) {
+ return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
+ }
+
+ return array();
+ }
+
+ public function getPrefixesPsr4()
+ {
+ return $this->prefixDirsPsr4;
+ }
+
+ public function getFallbackDirs()
+ {
+ return $this->fallbackDirsPsr0;
+ }
+
+ public function getFallbackDirsPsr4()
+ {
+ return $this->fallbackDirsPsr4;
+ }
+
+ public function getClassMap()
+ {
+ return $this->classMap;
+ }
+
+ /**
+ * @param array $classMap Class to filename map
+ */
+ public function addClassMap(array $classMap)
+ {
+ if ($this->classMap) {
+ $this->classMap = array_merge($this->classMap, $classMap);
+ } else {
+ $this->classMap = $classMap;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix, either
+ * appending or prepending to the ones previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param array|string $paths The PSR-0 root directories
+ * @param bool $prepend Whether to prepend the directories
+ */
+ public function add($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ if ($prepend) {
+ $this->fallbackDirsPsr0 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr0
+ );
+ } else {
+ $this->fallbackDirsPsr0 = array_merge(
+ $this->fallbackDirsPsr0,
+ (array) $paths
+ );
+ }
+
+ return;
+ }
+
+ $first = $prefix[0];
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+ return;
+ }
+ if ($prepend) {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixesPsr0[$first][$prefix]
+ );
+ } else {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $this->prefixesPsr0[$first][$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace, either
+ * appending or prepending to the ones previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param array|string $paths The PSR-4 base directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function addPsr4($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ // Register directories for the root namespace.
+ if ($prepend) {
+ $this->fallbackDirsPsr4 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr4
+ );
+ } else {
+ $this->fallbackDirsPsr4 = array_merge(
+ $this->fallbackDirsPsr4,
+ (array) $paths
+ );
+ }
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+ // Register directories for a new namespace.
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ } elseif ($prepend) {
+ // Prepend directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixDirsPsr4[$prefix]
+ );
+ } else {
+ // Append directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $this->prefixDirsPsr4[$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix,
+ * replacing any others previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param array|string $paths The PSR-0 base directories
+ */
+ public function set($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr0 = (array) $paths;
+ } else {
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace,
+ * replacing any others previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param array|string $paths The PSR-4 base directories
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setPsr4($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr4 = (array) $paths;
+ } else {
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Turns on searching the include path for class files.
+ *
+ * @param bool $useIncludePath
+ */
+ public function setUseIncludePath($useIncludePath)
+ {
+ $this->useIncludePath = $useIncludePath;
+ }
+
+ /**
+ * Can be used to check if the autoloader uses the include path to check
+ * for classes.
+ *
+ * @return bool
+ */
+ public function getUseIncludePath()
+ {
+ return $this->useIncludePath;
+ }
+
+ /**
+ * Turns off searching the prefix and fallback directories for classes
+ * that have not been registered with the class map.
+ *
+ * @param bool $classMapAuthoritative
+ */
+ public function setClassMapAuthoritative($classMapAuthoritative)
+ {
+ $this->classMapAuthoritative = $classMapAuthoritative;
+ }
+
+ /**
+ * Should class lookup fail if not found in the current class map?
+ *
+ * @return bool
+ */
+ public function isClassMapAuthoritative()
+ {
+ return $this->classMapAuthoritative;
+ }
+
+ /**
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+ *
+ * @param string|null $apcuPrefix
+ */
+ public function setApcuPrefix($apcuPrefix)
+ {
+ $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
+ }
+
+ /**
+ * The APCu prefix in use, or null if APCu caching is not enabled.
+ *
+ * @return string|null
+ */
+ public function getApcuPrefix()
+ {
+ return $this->apcuPrefix;
+ }
+
+ /**
+ * Registers this instance as an autoloader.
+ *
+ * @param bool $prepend Whether to prepend the autoloader or not
+ */
+ public function register($prepend = false)
+ {
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+ }
+
+ /**
+ * Unregisters this instance as an autoloader.
+ */
+ public function unregister()
+ {
+ spl_autoload_unregister(array($this, 'loadClass'));
+ }
+
+ /**
+ * Loads the given class or interface.
+ *
+ * @param string $class The name of the class
+ * @return bool|null True if loaded, null otherwise
+ */
+ public function loadClass($class)
+ {
+ if ($file = $this->findFile($class)) {
+ includeFile($file);
+
+ return true;
+ }
+ }
+
+ /**
+ * Finds the path to the file where the class is defined.
+ *
+ * @param string $class The name of the class
+ *
+ * @return string|false The path if found, false otherwise
+ */
+ public function findFile($class)
+ {
+ // class map lookup
+ if (isset($this->classMap[$class])) {
+ return $this->classMap[$class];
+ }
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+ return false;
+ }
+ if (null !== $this->apcuPrefix) {
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+ if ($hit) {
+ return $file;
+ }
+ }
+
+ $file = $this->findFileWithExtension($class, '.php');
+
+ // Search for Hack files if we are running on HHVM
+ if (false === $file && defined('HHVM_VERSION')) {
+ $file = $this->findFileWithExtension($class, '.hh');
+ }
+
+ if (null !== $this->apcuPrefix) {
+ apcu_add($this->apcuPrefix.$class, $file);
+ }
+
+ if (false === $file) {
+ // Remember that this class does not exist.
+ $this->missingClasses[$class] = true;
+ }
+
+ return $file;
+ }
+
+ private function findFileWithExtension($class, $ext)
+ {
+ // PSR-4 lookup
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+ $first = $class[0];
+ if (isset($this->prefixLengthsPsr4[$first])) {
+ $subPath = $class;
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
+ $subPath = substr($subPath, 0, $lastPos);
+ $search = $subPath . '\\';
+ if (isset($this->prefixDirsPsr4[$search])) {
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
+ if (file_exists($file = $dir . $pathEnd)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-4 fallback dirs
+ foreach ($this->fallbackDirsPsr4 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 lookup
+ if (false !== $pos = strrpos($class, '\\')) {
+ // namespaced class name
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+ } else {
+ // PEAR-like class name
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+ }
+
+ if (isset($this->prefixesPsr0[$first])) {
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($dirs as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-0 fallback dirs
+ foreach ($this->fallbackDirsPsr0 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 include paths.
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+ return $file;
+ }
+
+ return false;
+ }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ */
+function includeFile($file)
+{
+ include $file;
+}
diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE
new file mode 100644
index 0000000000000..62ecfd8d0046b
--- /dev/null
+++ b/vendor/composer/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php
new file mode 100644
index 0000000000000..69956407d7cae
--- /dev/null
+++ b/vendor/composer/autoload_classmap.php
@@ -0,0 +1,114 @@
+ $vendorDir . '/automattic/jetpack-a8c-mc-stats/src/class-a8c-mc-stats.php',
+ 'Automattic\\Jetpack\\Abtest' => $vendorDir . '/automattic/jetpack-abtest/src/class-abtest.php',
+ 'Automattic\\Jetpack\\Assets' => $vendorDir . '/automattic/jetpack-assets/src/class-assets.php',
+ 'Automattic\\Jetpack\\Assets\\Logo' => $vendorDir . '/automattic/jetpack-logo/src/class-logo.php',
+ 'Automattic\\Jetpack\\Autoloader\\AutoloadGenerator' => $vendorDir . '/automattic/jetpack-autoloader/src/AutoloadGenerator.php',
+ 'Automattic\\Jetpack\\Autoloader\\AutoloadProcessor' => $vendorDir . '/automattic/jetpack-autoloader/src/AutoloadProcessor.php',
+ 'Automattic\\Jetpack\\Autoloader\\CustomAutoloaderPlugin' => $vendorDir . '/automattic/jetpack-autoloader/src/CustomAutoloaderPlugin.php',
+ 'Automattic\\Jetpack\\Autoloader\\ManifestGenerator' => $vendorDir . '/automattic/jetpack-autoloader/src/ManifestGenerator.php',
+ 'Automattic\\Jetpack\\Backup\\Helper_Script_Manager' => $vendorDir . '/automattic/jetpack-backup/src/class-helper-script-manager.php',
+ 'Automattic\\Jetpack\\Blocks' => $vendorDir . '/automattic/jetpack-blocks/src/class-blocks.php',
+ 'Automattic\\Jetpack\\Config' => $vendorDir . '/automattic/jetpack-config/src/class-config.php',
+ 'Automattic\\Jetpack\\Connection\\Client' => $vendorDir . '/automattic/jetpack-connection/src/class-client.php',
+ 'Automattic\\Jetpack\\Connection\\Error_Handler' => $vendorDir . '/automattic/jetpack-connection/src/class-error-handler.php',
+ 'Automattic\\Jetpack\\Connection\\Manager' => $vendorDir . '/automattic/jetpack-connection/src/class-manager.php',
+ 'Automattic\\Jetpack\\Connection\\Manager_Interface' => $vendorDir . '/automattic/jetpack-connection/src/interface-manager.php',
+ 'Automattic\\Jetpack\\Connection\\Plugin' => $vendorDir . '/automattic/jetpack-connection/src/class-plugin.php',
+ 'Automattic\\Jetpack\\Connection\\Plugin_Storage' => $vendorDir . '/automattic/jetpack-connection/src/class-plugin-storage.php',
+ 'Automattic\\Jetpack\\Connection\\REST_Connector' => $vendorDir . '/automattic/jetpack-connection/src/class-rest-connector.php',
+ 'Automattic\\Jetpack\\Connection\\Rest_Authentication' => $vendorDir . '/automattic/jetpack-connection/src/class-rest-authentication.php',
+ 'Automattic\\Jetpack\\Connection\\Utils' => $vendorDir . '/automattic/jetpack-connection/src/class-utils.php',
+ 'Automattic\\Jetpack\\Connection\\XMLRPC_Async_Call' => $vendorDir . '/automattic/jetpack-connection/src/class-xmlrpc-async-call.php',
+ 'Automattic\\Jetpack\\Connection\\XMLRPC_Connector' => $vendorDir . '/automattic/jetpack-connection/src/class-xmlrpc-connector.php',
+ 'Automattic\\Jetpack\\Constants' => $vendorDir . '/automattic/jetpack-constants/src/class-constants.php',
+ 'Automattic\\Jetpack\\Device_Detection' => $vendorDir . '/automattic/jetpack-device-detection/src/class-device-detection.php',
+ 'Automattic\\Jetpack\\Device_Detection\\User_Agent_Info' => $vendorDir . '/automattic/jetpack-device-detection/src/class-user-agent-info.php',
+ 'Automattic\\Jetpack\\Error' => $vendorDir . '/automattic/jetpack-error/src/class-error.php',
+ 'Automattic\\Jetpack\\Heartbeat' => $vendorDir . '/automattic/jetpack-heartbeat/src/class-heartbeat.php',
+ 'Automattic\\Jetpack\\JITMS\\JITM' => $vendorDir . '/automattic/jetpack-jitm/src/class-jitm.php',
+ 'Automattic\\Jetpack\\JITMS\\Post_Connection_JITM' => $vendorDir . '/automattic/jetpack-jitm/src/class-post-connection-jitm.php',
+ 'Automattic\\Jetpack\\JITMS\\Pre_Connection_JITM' => $vendorDir . '/automattic/jetpack-jitm/src/class-pre-connection-jitm.php',
+ 'Automattic\\Jetpack\\Jetpack_Lazy_Images' => $vendorDir . '/automattic/jetpack-lazy-images/src/lazy-images.php',
+ 'Automattic\\Jetpack\\Licensing' => $vendorDir . '/automattic/jetpack-licensing/src/class-licensing.php',
+ 'Automattic\\Jetpack\\Partner' => $vendorDir . '/automattic/jetpack-partner/src/class-partner.php',
+ 'Automattic\\Jetpack\\Plugin\\Tracking' => $baseDir . '/src/class-tracking.php',
+ 'Automattic\\Jetpack\\Redirect' => $vendorDir . '/automattic/jetpack-redirect/src/class-redirect.php',
+ 'Automattic\\Jetpack\\Roles' => $vendorDir . '/automattic/jetpack-roles/src/class-roles.php',
+ 'Automattic\\Jetpack\\Status' => $vendorDir . '/automattic/jetpack-status/src/class-status.php',
+ 'Automattic\\Jetpack\\Sync\\Actions' => $vendorDir . '/automattic/jetpack-sync/src/class-actions.php',
+ 'Automattic\\Jetpack\\Sync\\Codec_Interface' => $vendorDir . '/automattic/jetpack-sync/src/interface-codec.php',
+ 'Automattic\\Jetpack\\Sync\\Defaults' => $vendorDir . '/automattic/jetpack-sync/src/class-defaults.php',
+ 'Automattic\\Jetpack\\Sync\\Functions' => $vendorDir . '/automattic/jetpack-sync/src/class-functions.php',
+ 'Automattic\\Jetpack\\Sync\\Health' => $vendorDir . '/automattic/jetpack-sync/src/class-health.php',
+ 'Automattic\\Jetpack\\Sync\\JSON_Deflate_Array_Codec' => $vendorDir . '/automattic/jetpack-sync/src/class-json-deflate-array-codec.php',
+ 'Automattic\\Jetpack\\Sync\\Listener' => $vendorDir . '/automattic/jetpack-sync/src/class-listener.php',
+ 'Automattic\\Jetpack\\Sync\\Lock' => $vendorDir . '/automattic/jetpack-sync/src/class-lock.php',
+ 'Automattic\\Jetpack\\Sync\\Main' => $vendorDir . '/automattic/jetpack-sync/src/class-main.php',
+ 'Automattic\\Jetpack\\Sync\\Modules' => $vendorDir . '/automattic/jetpack-sync/src/class-modules.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Attachments' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-attachments.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Callables' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-callables.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Comments' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-comments.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Constants' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-constants.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Full_Sync' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-full-sync.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Full_Sync_Immediately' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-full-sync-immediately.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Import' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-import.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Menus' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-menus.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Meta' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-meta.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Module' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-module.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Network_Options' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-network-options.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Options' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-options.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Plugins' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-plugins.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Posts' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-posts.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Protect' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-protect.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Stats' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-stats.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Term_Relationships' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-term-relationships.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Terms' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-terms.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Themes' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-themes.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Updates' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-updates.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Users' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-users.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\WP_Super_Cache' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-wp-super-cache.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\WooCommerce' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-woocommerce.php',
+ 'Automattic\\Jetpack\\Sync\\Queue' => $vendorDir . '/automattic/jetpack-sync/src/class-queue.php',
+ 'Automattic\\Jetpack\\Sync\\Queue_Buffer' => $vendorDir . '/automattic/jetpack-sync/src/class-queue-buffer.php',
+ 'Automattic\\Jetpack\\Sync\\Replicastore' => $vendorDir . '/automattic/jetpack-sync/src/class-replicastore.php',
+ 'Automattic\\Jetpack\\Sync\\Replicastore_Interface' => $vendorDir . '/automattic/jetpack-sync/src/interface-replicastore.php',
+ 'Automattic\\Jetpack\\Sync\\Sender' => $vendorDir . '/automattic/jetpack-sync/src/class-sender.php',
+ 'Automattic\\Jetpack\\Sync\\Server' => $vendorDir . '/automattic/jetpack-sync/src/class-server.php',
+ 'Automattic\\Jetpack\\Sync\\Settings' => $vendorDir . '/automattic/jetpack-sync/src/class-settings.php',
+ 'Automattic\\Jetpack\\Sync\\Simple_Codec' => $vendorDir . '/automattic/jetpack-sync/src/class-simple-codec.php',
+ 'Automattic\\Jetpack\\Sync\\Users' => $vendorDir . '/automattic/jetpack-sync/src/class-users.php',
+ 'Automattic\\Jetpack\\Sync\\Utils' => $vendorDir . '/automattic/jetpack-sync/src/class-utils.php',
+ 'Automattic\\Jetpack\\Terms_Of_Service' => $vendorDir . '/automattic/jetpack-terms-of-service/src/class-terms-of-service.php',
+ 'Automattic\\Jetpack\\Tracking' => $vendorDir . '/automattic/jetpack-tracking/src/class-tracking.php',
+ 'JetpackTracking' => $vendorDir . '/automattic/jetpack-compat/legacy/class-jetpacktracking.php',
+ 'Jetpack_Client' => $vendorDir . '/automattic/jetpack-compat/legacy/class-jetpack-client.php',
+ 'Jetpack_IXR_Client' => $vendorDir . '/automattic/jetpack-connection/legacy/class-jetpack-ixr-client.php',
+ 'Jetpack_IXR_ClientMulticall' => $vendorDir . '/automattic/jetpack-connection/legacy/class-jetpack-ixr-clientmulticall.php',
+ 'Jetpack_Options' => $vendorDir . '/automattic/jetpack-options/legacy/class-jetpack-options.php',
+ 'Jetpack_Signature' => $vendorDir . '/automattic/jetpack-connection/legacy/class-jetpack-signature.php',
+ 'Jetpack_Sync_Actions' => $vendorDir . '/automattic/jetpack-compat/legacy/class-jetpack-sync-actions.php',
+ 'Jetpack_Sync_Modules' => $vendorDir . '/automattic/jetpack-compat/legacy/class-jetpack-sync-modules.php',
+ 'Jetpack_Sync_Settings' => $vendorDir . '/automattic/jetpack-compat/legacy/class-jetpack-sync-settings.php',
+ 'Jetpack_Tracks_Client' => $vendorDir . '/automattic/jetpack-tracking/legacy/class-jetpack-tracks-client.php',
+ 'Jetpack_Tracks_Event' => $vendorDir . '/automattic/jetpack-tracking/legacy/class-jetpack-tracks-event.php',
+ 'Jetpack_XMLRPC_Server' => $vendorDir . '/automattic/jetpack-connection/legacy/class-jetpack-xmlrpc-server.php',
+ 'Twitter\\Text\\Autolink' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/Autolink.php',
+ 'Twitter\\Text\\Configuration' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/Configuration.php',
+ 'Twitter\\Text\\EmojiRegex' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/EmojiRegex.php',
+ 'Twitter\\Text\\Extractor' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/Extractor.php',
+ 'Twitter\\Text\\HitHighlighter' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/HitHighlighter.php',
+ 'Twitter\\Text\\ParseResults' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/ParseResults.php',
+ 'Twitter\\Text\\Parser' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/Parser.php',
+ 'Twitter\\Text\\Regex' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/Regex.php',
+ 'Twitter\\Text\\StringUtils' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/StringUtils.php',
+ 'Twitter\\Text\\TldLists' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/TldLists.php',
+ 'Twitter\\Text\\Validator' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/Validator.php',
+);
diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php
new file mode 100644
index 0000000000000..0d7f6fcaceba1
--- /dev/null
+++ b/vendor/composer/autoload_files.php
@@ -0,0 +1,12 @@
+ $vendorDir . '/automattic/jetpack-connection/legacy/load-ixr.php',
+ 'd4eb94df91a729802d18373ee8cdc79f' => $vendorDir . '/automattic/jetpack-backup/actions.php',
+ '009de6aaa0d497eacea41fab13fc05f1' => $vendorDir . '/automattic/jetpack-compat/functions.php',
+);
diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php
new file mode 100644
index 0000000000000..53e84e22386ea
--- /dev/null
+++ b/vendor/composer/autoload_namespaces.php
@@ -0,0 +1,10 @@
+ array($vendorDir . '/nojimage/twitter-text-php/lib'),
+);
diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php
new file mode 100644
index 0000000000000..bef81985556b9
--- /dev/null
+++ b/vendor/composer/autoload_psr4.php
@@ -0,0 +1,10 @@
+ array($vendorDir . '/automattic/jetpack-autoloader/src'),
+);
diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php
new file mode 100644
index 0000000000000..50a473717a62b
--- /dev/null
+++ b/vendor/composer/autoload_real.php
@@ -0,0 +1,64 @@
+= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
+ if ($useStaticLoader) {
+ require_once __DIR__ . '/autoload_static.php';
+
+ call_user_func(\Composer\Autoload\ComposerStaticInitf4b7b70b114e3d14b34ab30f33a9a084::getInitializer($loader));
+ } else {
+ $classMap = require __DIR__ . '/autoload_classmap.php';
+ if ($classMap) {
+ $loader->addClassMap($classMap);
+ }
+ }
+
+ $loader->setClassMapAuthoritative(true);
+ $loader->register(true);
+
+ if ($useStaticLoader) {
+ $includeFiles = Composer\Autoload\ComposerStaticInitf4b7b70b114e3d14b34ab30f33a9a084::$files;
+ } else {
+ $includeFiles = require __DIR__ . '/autoload_files.php';
+ }
+ foreach ($includeFiles as $fileIdentifier => $file) {
+ composerRequiref4b7b70b114e3d14b34ab30f33a9a084($fileIdentifier, $file);
+ }
+
+ return $loader;
+ }
+}
+
+function composerRequiref4b7b70b114e3d14b34ab30f33a9a084($fileIdentifier, $file)
+{
+ if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
+ require $file;
+
+ $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
+ }
+}
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
new file mode 100644
index 0000000000000..da6da7d4218ab
--- /dev/null
+++ b/vendor/composer/autoload_static.php
@@ -0,0 +1,157 @@
+ __DIR__ . '/..' . '/automattic/jetpack-connection/legacy/load-ixr.php',
+ 'd4eb94df91a729802d18373ee8cdc79f' => __DIR__ . '/..' . '/automattic/jetpack-backup/actions.php',
+ '009de6aaa0d497eacea41fab13fc05f1' => __DIR__ . '/..' . '/automattic/jetpack-compat/functions.php',
+ );
+
+ public static $prefixLengthsPsr4 = array (
+ 'A' =>
+ array (
+ 'Automattic\\Jetpack\\Autoloader\\' => 30,
+ ),
+ );
+
+ public static $prefixDirsPsr4 = array (
+ 'Automattic\\Jetpack\\Autoloader\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/automattic/jetpack-autoloader/src',
+ ),
+ );
+
+ public static $prefixesPsr0 = array (
+ 'T' =>
+ array (
+ 'Twitter\\Text\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/nojimage/twitter-text-php/lib',
+ ),
+ ),
+ );
+
+ public static $classMap = array (
+ 'Automattic\\Jetpack\\A8c_Mc_Stats' => __DIR__ . '/..' . '/automattic/jetpack-a8c-mc-stats/src/class-a8c-mc-stats.php',
+ 'Automattic\\Jetpack\\Abtest' => __DIR__ . '/..' . '/automattic/jetpack-abtest/src/class-abtest.php',
+ 'Automattic\\Jetpack\\Assets' => __DIR__ . '/..' . '/automattic/jetpack-assets/src/class-assets.php',
+ 'Automattic\\Jetpack\\Assets\\Logo' => __DIR__ . '/..' . '/automattic/jetpack-logo/src/class-logo.php',
+ 'Automattic\\Jetpack\\Autoloader\\AutoloadGenerator' => __DIR__ . '/..' . '/automattic/jetpack-autoloader/src/AutoloadGenerator.php',
+ 'Automattic\\Jetpack\\Autoloader\\AutoloadProcessor' => __DIR__ . '/..' . '/automattic/jetpack-autoloader/src/AutoloadProcessor.php',
+ 'Automattic\\Jetpack\\Autoloader\\CustomAutoloaderPlugin' => __DIR__ . '/..' . '/automattic/jetpack-autoloader/src/CustomAutoloaderPlugin.php',
+ 'Automattic\\Jetpack\\Autoloader\\ManifestGenerator' => __DIR__ . '/..' . '/automattic/jetpack-autoloader/src/ManifestGenerator.php',
+ 'Automattic\\Jetpack\\Backup\\Helper_Script_Manager' => __DIR__ . '/..' . '/automattic/jetpack-backup/src/class-helper-script-manager.php',
+ 'Automattic\\Jetpack\\Blocks' => __DIR__ . '/..' . '/automattic/jetpack-blocks/src/class-blocks.php',
+ 'Automattic\\Jetpack\\Config' => __DIR__ . '/..' . '/automattic/jetpack-config/src/class-config.php',
+ 'Automattic\\Jetpack\\Connection\\Client' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/class-client.php',
+ 'Automattic\\Jetpack\\Connection\\Error_Handler' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/class-error-handler.php',
+ 'Automattic\\Jetpack\\Connection\\Manager' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/class-manager.php',
+ 'Automattic\\Jetpack\\Connection\\Manager_Interface' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/interface-manager.php',
+ 'Automattic\\Jetpack\\Connection\\Plugin' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/class-plugin.php',
+ 'Automattic\\Jetpack\\Connection\\Plugin_Storage' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/class-plugin-storage.php',
+ 'Automattic\\Jetpack\\Connection\\REST_Connector' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/class-rest-connector.php',
+ 'Automattic\\Jetpack\\Connection\\Rest_Authentication' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/class-rest-authentication.php',
+ 'Automattic\\Jetpack\\Connection\\Utils' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/class-utils.php',
+ 'Automattic\\Jetpack\\Connection\\XMLRPC_Async_Call' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/class-xmlrpc-async-call.php',
+ 'Automattic\\Jetpack\\Connection\\XMLRPC_Connector' => __DIR__ . '/..' . '/automattic/jetpack-connection/src/class-xmlrpc-connector.php',
+ 'Automattic\\Jetpack\\Constants' => __DIR__ . '/..' . '/automattic/jetpack-constants/src/class-constants.php',
+ 'Automattic\\Jetpack\\Device_Detection' => __DIR__ . '/..' . '/automattic/jetpack-device-detection/src/class-device-detection.php',
+ 'Automattic\\Jetpack\\Device_Detection\\User_Agent_Info' => __DIR__ . '/..' . '/automattic/jetpack-device-detection/src/class-user-agent-info.php',
+ 'Automattic\\Jetpack\\Error' => __DIR__ . '/..' . '/automattic/jetpack-error/src/class-error.php',
+ 'Automattic\\Jetpack\\Heartbeat' => __DIR__ . '/..' . '/automattic/jetpack-heartbeat/src/class-heartbeat.php',
+ 'Automattic\\Jetpack\\JITMS\\JITM' => __DIR__ . '/..' . '/automattic/jetpack-jitm/src/class-jitm.php',
+ 'Automattic\\Jetpack\\JITMS\\Post_Connection_JITM' => __DIR__ . '/..' . '/automattic/jetpack-jitm/src/class-post-connection-jitm.php',
+ 'Automattic\\Jetpack\\JITMS\\Pre_Connection_JITM' => __DIR__ . '/..' . '/automattic/jetpack-jitm/src/class-pre-connection-jitm.php',
+ 'Automattic\\Jetpack\\Jetpack_Lazy_Images' => __DIR__ . '/..' . '/automattic/jetpack-lazy-images/src/lazy-images.php',
+ 'Automattic\\Jetpack\\Licensing' => __DIR__ . '/..' . '/automattic/jetpack-licensing/src/class-licensing.php',
+ 'Automattic\\Jetpack\\Partner' => __DIR__ . '/..' . '/automattic/jetpack-partner/src/class-partner.php',
+ 'Automattic\\Jetpack\\Plugin\\Tracking' => __DIR__ . '/../..' . '/src/class-tracking.php',
+ 'Automattic\\Jetpack\\Redirect' => __DIR__ . '/..' . '/automattic/jetpack-redirect/src/class-redirect.php',
+ 'Automattic\\Jetpack\\Roles' => __DIR__ . '/..' . '/automattic/jetpack-roles/src/class-roles.php',
+ 'Automattic\\Jetpack\\Status' => __DIR__ . '/..' . '/automattic/jetpack-status/src/class-status.php',
+ 'Automattic\\Jetpack\\Sync\\Actions' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/class-actions.php',
+ 'Automattic\\Jetpack\\Sync\\Codec_Interface' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/interface-codec.php',
+ 'Automattic\\Jetpack\\Sync\\Defaults' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/class-defaults.php',
+ 'Automattic\\Jetpack\\Sync\\Functions' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/class-functions.php',
+ 'Automattic\\Jetpack\\Sync\\Health' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/class-health.php',
+ 'Automattic\\Jetpack\\Sync\\JSON_Deflate_Array_Codec' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/class-json-deflate-array-codec.php',
+ 'Automattic\\Jetpack\\Sync\\Listener' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/class-listener.php',
+ 'Automattic\\Jetpack\\Sync\\Lock' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/class-lock.php',
+ 'Automattic\\Jetpack\\Sync\\Main' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/class-main.php',
+ 'Automattic\\Jetpack\\Sync\\Modules' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/class-modules.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Attachments' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-attachments.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Callables' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-callables.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Comments' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-comments.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Constants' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-constants.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Full_Sync' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-full-sync.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Full_Sync_Immediately' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-full-sync-immediately.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Import' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-import.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Menus' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-menus.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Meta' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-meta.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Module' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-module.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Network_Options' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-network-options.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Options' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-options.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Plugins' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-plugins.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Posts' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-posts.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Protect' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-protect.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Stats' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-stats.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Term_Relationships' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-term-relationships.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Terms' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-terms.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Themes' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-themes.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Updates' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-updates.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Users' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-users.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\WP_Super_Cache' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-wp-super-cache.php',
+ 'Automattic\\Jetpack\\Sync\\Modules\\WooCommerce' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/modules/class-woocommerce.php',
+ 'Automattic\\Jetpack\\Sync\\Queue' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/class-queue.php',
+ 'Automattic\\Jetpack\\Sync\\Queue_Buffer' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/class-queue-buffer.php',
+ 'Automattic\\Jetpack\\Sync\\Replicastore' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/class-replicastore.php',
+ 'Automattic\\Jetpack\\Sync\\Replicastore_Interface' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/interface-replicastore.php',
+ 'Automattic\\Jetpack\\Sync\\Sender' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/class-sender.php',
+ 'Automattic\\Jetpack\\Sync\\Server' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/class-server.php',
+ 'Automattic\\Jetpack\\Sync\\Settings' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/class-settings.php',
+ 'Automattic\\Jetpack\\Sync\\Simple_Codec' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/class-simple-codec.php',
+ 'Automattic\\Jetpack\\Sync\\Users' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/class-users.php',
+ 'Automattic\\Jetpack\\Sync\\Utils' => __DIR__ . '/..' . '/automattic/jetpack-sync/src/class-utils.php',
+ 'Automattic\\Jetpack\\Terms_Of_Service' => __DIR__ . '/..' . '/automattic/jetpack-terms-of-service/src/class-terms-of-service.php',
+ 'Automattic\\Jetpack\\Tracking' => __DIR__ . '/..' . '/automattic/jetpack-tracking/src/class-tracking.php',
+ 'JetpackTracking' => __DIR__ . '/..' . '/automattic/jetpack-compat/legacy/class-jetpacktracking.php',
+ 'Jetpack_Client' => __DIR__ . '/..' . '/automattic/jetpack-compat/legacy/class-jetpack-client.php',
+ 'Jetpack_IXR_Client' => __DIR__ . '/..' . '/automattic/jetpack-connection/legacy/class-jetpack-ixr-client.php',
+ 'Jetpack_IXR_ClientMulticall' => __DIR__ . '/..' . '/automattic/jetpack-connection/legacy/class-jetpack-ixr-clientmulticall.php',
+ 'Jetpack_Options' => __DIR__ . '/..' . '/automattic/jetpack-options/legacy/class-jetpack-options.php',
+ 'Jetpack_Signature' => __DIR__ . '/..' . '/automattic/jetpack-connection/legacy/class-jetpack-signature.php',
+ 'Jetpack_Sync_Actions' => __DIR__ . '/..' . '/automattic/jetpack-compat/legacy/class-jetpack-sync-actions.php',
+ 'Jetpack_Sync_Modules' => __DIR__ . '/..' . '/automattic/jetpack-compat/legacy/class-jetpack-sync-modules.php',
+ 'Jetpack_Sync_Settings' => __DIR__ . '/..' . '/automattic/jetpack-compat/legacy/class-jetpack-sync-settings.php',
+ 'Jetpack_Tracks_Client' => __DIR__ . '/..' . '/automattic/jetpack-tracking/legacy/class-jetpack-tracks-client.php',
+ 'Jetpack_Tracks_Event' => __DIR__ . '/..' . '/automattic/jetpack-tracking/legacy/class-jetpack-tracks-event.php',
+ 'Jetpack_XMLRPC_Server' => __DIR__ . '/..' . '/automattic/jetpack-connection/legacy/class-jetpack-xmlrpc-server.php',
+ 'Twitter\\Text\\Autolink' => __DIR__ . '/..' . '/nojimage/twitter-text-php/lib/Twitter/Text/Autolink.php',
+ 'Twitter\\Text\\Configuration' => __DIR__ . '/..' . '/nojimage/twitter-text-php/lib/Twitter/Text/Configuration.php',
+ 'Twitter\\Text\\EmojiRegex' => __DIR__ . '/..' . '/nojimage/twitter-text-php/lib/Twitter/Text/EmojiRegex.php',
+ 'Twitter\\Text\\Extractor' => __DIR__ . '/..' . '/nojimage/twitter-text-php/lib/Twitter/Text/Extractor.php',
+ 'Twitter\\Text\\HitHighlighter' => __DIR__ . '/..' . '/nojimage/twitter-text-php/lib/Twitter/Text/HitHighlighter.php',
+ 'Twitter\\Text\\ParseResults' => __DIR__ . '/..' . '/nojimage/twitter-text-php/lib/Twitter/Text/ParseResults.php',
+ 'Twitter\\Text\\Parser' => __DIR__ . '/..' . '/nojimage/twitter-text-php/lib/Twitter/Text/Parser.php',
+ 'Twitter\\Text\\Regex' => __DIR__ . '/..' . '/nojimage/twitter-text-php/lib/Twitter/Text/Regex.php',
+ 'Twitter\\Text\\StringUtils' => __DIR__ . '/..' . '/nojimage/twitter-text-php/lib/Twitter/Text/StringUtils.php',
+ 'Twitter\\Text\\TldLists' => __DIR__ . '/..' . '/nojimage/twitter-text-php/lib/Twitter/Text/TldLists.php',
+ 'Twitter\\Text\\Validator' => __DIR__ . '/..' . '/nojimage/twitter-text-php/lib/Twitter/Text/Validator.php',
+ );
+
+ public static function getInitializer(ClassLoader $loader)
+ {
+ return \Closure::bind(function () use ($loader) {
+ $loader->prefixLengthsPsr4 = ComposerStaticInitf4b7b70b114e3d14b34ab30f33a9a084::$prefixLengthsPsr4;
+ $loader->prefixDirsPsr4 = ComposerStaticInitf4b7b70b114e3d14b34ab30f33a9a084::$prefixDirsPsr4;
+ $loader->prefixesPsr0 = ComposerStaticInitf4b7b70b114e3d14b34ab30f33a9a084::$prefixesPsr0;
+ $loader->classMap = ComposerStaticInitf4b7b70b114e3d14b34ab30f33a9a084::$classMap;
+
+ }, null, ClassLoader::class);
+ }
+}
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
new file mode 100644
index 0000000000000..eb35f4372f50c
--- /dev/null
+++ b/vendor/composer/installed.json
@@ -0,0 +1,973 @@
+[
+ {
+ "name": "automattic/jetpack-a8c-mc-stats",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/a8c-mc-stats",
+ "reference": "72e8464c053658a730113aa3cfb6d8b292cd16e2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "scripts": {
+ "phpunit": [
+ "@composer install",
+ "./vendor/phpunit/phpunit/phpunit --colors=always"
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Used to record internal usage stats for Automattic. Not visible to site owners.",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-abtest",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/abtest",
+ "reference": "8295c4e5fe6e61ad3f2848f294763398f966e8e9"
+ },
+ "require": {
+ "automattic/jetpack-connection": "@dev",
+ "automattic/jetpack-error": "@dev"
+ },
+ "require-dev": {
+ "php-mock/php-mock": "^2.1",
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "scripts": {
+ "phpunit": [
+ "@composer install",
+ "./vendor/phpunit/phpunit/phpunit --colors=always"
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Provides an interface to the WP.com A/B tests.",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-assets",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/assets",
+ "reference": "2e25ad7667693395602e6a1962357901c842b067"
+ },
+ "require": {
+ "automattic/jetpack-constants": "@dev"
+ },
+ "require-dev": {
+ "brain/monkey": "2.4.0",
+ "php-mock/php-mock": "^2.1",
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "scripts": {
+ "phpunit": [
+ "@composer install",
+ "./vendor/phpunit/phpunit/phpunit --colors=always"
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Asset management utilities for Jetpack ecosystem packages",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-autoloader",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/autoloader",
+ "reference": "557195e755ff951fe3f8544056c5696eecc4d2cb"
+ },
+ "require": {
+ "composer-plugin-api": "^1.1 || ^2.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "Automattic\\Jetpack\\Autoloader\\CustomAutoloaderPlugin"
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/AutoloadGenerator.php"
+ ],
+ "psr-4": {
+ "Automattic\\Jetpack\\Autoloader\\": "src"
+ }
+ },
+ "scripts": {
+ "phpunit": [
+ "@composer install",
+ "./vendor/phpunit/phpunit/phpunit --colors=always"
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Creates a custom autoloader for a plugin or theme.",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-backup",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/backup",
+ "reference": "69733464fcefcb51342908dcdd6db6feb285c180"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "files": [
+ "actions.php"
+ ],
+ "classmap": [
+ "src/"
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Tools to assist with backing up Jetpack sites.",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-blocks",
+ "version": "dev-add/nonce-removal",
+ "version_normalized": "dev-add/nonce-removal",
+ "dist": {
+ "type": "path",
+ "url": "./packages/blocks",
+ "reference": "057e0ecae8c03cbc335bb5ea488f67b51603fd7a"
+ },
+ "require-dev": {
+ "automattic/wordbless": "dev-master",
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "scripts": {
+ "phpunit": [
+ "@composer install",
+ "./vendor/phpunit/phpunit/phpunit --colors=always"
+ ],
+ "post-update-cmd": [
+ "php -r \"copy('vendor/automattic/wordbless/src/dbless-wpdb.php', 'wordpress/wp-content/db.php');\""
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Register and manage blocks within a plugin. Used to manage block registration, enqueues, and more.",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-compat",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/compat",
+ "reference": "68d64fff654a688bab6db5d79d1609c9ec288701"
+ },
+ "require-dev": {
+ "php-mock/php-mock": "^2.1",
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "files": [
+ "functions.php"
+ ],
+ "classmap": [
+ "legacy"
+ ]
+ },
+ "scripts": {
+ "phpunit": [
+ "@composer install",
+ "./vendor/phpunit/phpunit/phpunit --colors=always"
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Compatibility layer with previous versions of Jetpack",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-config",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/config",
+ "reference": "2276f5efe5e29fff5c373e9b7a272d7d007005f4"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Jetpack configuration package that initializes other packages and configures Jetpack's functionality. Can be used as a base for all variants of Jetpack package usage.",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-connection",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/connection",
+ "reference": "bb68aeb045a837fe2fe4784ca41ac667fe5b6fa5"
+ },
+ "require": {
+ "automattic/jetpack-constants": "@dev",
+ "automattic/jetpack-options": "@dev",
+ "automattic/jetpack-roles": "@dev",
+ "automattic/jetpack-status": "@dev",
+ "automattic/jetpack-tracking": "@dev"
+ },
+ "require-dev": {
+ "automattic/wordbless": "@dev",
+ "php-mock/php-mock": "^2.1",
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "files": [
+ "legacy/load-ixr.php"
+ ],
+ "classmap": [
+ "legacy",
+ "src/"
+ ]
+ },
+ "scripts": {
+ "phpunit": [
+ "@composer install",
+ "./vendor/phpunit/phpunit/phpunit --colors=always"
+ ],
+ "post-update-cmd": [
+ "php -r \"copy('vendor/automattic/wordbless/src/dbless-wpdb.php', 'wordpress/wp-content/db.php');\""
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Everything needed to connect to the Jetpack infrastructure",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-constants",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/constants",
+ "reference": "d8c1e77b35ab7d05f228ddd1bef563fabeb0628b"
+ },
+ "require-dev": {
+ "php-mock/php-mock": "^2.1",
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "scripts": {
+ "phpunit": [
+ "@composer install",
+ "./vendor/phpunit/phpunit/phpunit --colors=always"
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "A wrapper for defining constants in a more testable way.",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-device-detection",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/device-detection",
+ "reference": "235a1225b76554ca04daece6ca94bcf28dd23399"
+ },
+ "require-dev": {
+ "php-mock/php-mock": "^2.1",
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "scripts": {
+ "phpunit": [
+ "@composer install",
+ "./vendor/phpunit/phpunit/phpunit --colors=always"
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "A way to detect device types based on User-Agent header.",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-error",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/error",
+ "reference": "a4c523b50f710b69579f95b2c9de8a4f97d976d6"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "scripts": {
+ "phpunit": [
+ "@composer install",
+ "./vendor/phpunit/phpunit/phpunit --colors=always"
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Jetpack Error - a wrapper around WP_Error.",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-heartbeat",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/heartbeat",
+ "reference": "ad4ce7ae64a1881dea8f09a7f94c789a02956f00"
+ },
+ "require": {
+ "automattic/jetpack-a8c-mc-stats": "@dev",
+ "automattic/jetpack-options": "@dev"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "scripts": {
+ "phpunit": [
+ "@composer install",
+ "./vendor/phpunit/phpunit/phpunit --colors=always"
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "This adds a cronjob that sends a batch of internal automattic stats to wp.com once a day",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-jitm",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/jitm",
+ "reference": "705eb87a82f661570e546e65671dddf6f80035fa"
+ },
+ "require": {
+ "automattic/jetpack-assets": "@dev",
+ "automattic/jetpack-connection": "@dev",
+ "automattic/jetpack-constants": "@dev",
+ "automattic/jetpack-logo": "@dev",
+ "automattic/jetpack-options": "@dev",
+ "automattic/jetpack-partner": "@dev",
+ "automattic/jetpack-redirect": "@dev",
+ "automattic/jetpack-status": "@dev",
+ "automattic/jetpack-tracking": "@dev"
+ },
+ "require-dev": {
+ "mockery/mockery": "^1.2",
+ "php-mock/php-mock": "^2.1",
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "scripts": {
+ "phpunit": [
+ "@composer install",
+ "./vendor/phpunit/phpunit/phpunit --colors=always"
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Just in time messages for Jetpack",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-lazy-images",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/lazy-images",
+ "reference": "16409fc61cce65c8b914b08c80cae2cc0662ca2d"
+ },
+ "require": {
+ "automattic/jetpack-constants": "@dev"
+ },
+ "require-dev": {
+ "automattic/wordbless": "dev-master",
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "scripts": {
+ "phpunit": [
+ "@composer install",
+ "./vendor/phpunit/phpunit/phpunit --colors=always"
+ ],
+ "post-update-cmd": [
+ "php -r \"copy('vendor/automattic/wordbless/src/dbless-wpdb.php', 'wordpress/wp-content/db.php');\""
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Speed up your site and create a smoother viewing experience by loading images as visitors scroll down the screen, instead of all at once.",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-licensing",
+ "version": "dev-add/license-support",
+ "version_normalized": "dev-add/license-support",
+ "dist": {
+ "type": "path",
+ "url": "./packages/licensing",
+ "reference": "0b3150892c93b9bdb252933500b80db692a1d1e9"
+ },
+ "require": {
+ "automattic/jetpack-connection": "@dev",
+ "automattic/jetpack-options": "@dev"
+ },
+ "require-dev": {
+ "automattic/wordbless": "@dev",
+ "php-mock/php-mock": "^2.1",
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "scripts": {
+ "phpunit": [
+ "@composer install",
+ "./vendor/phpunit/phpunit/phpunit --colors=always"
+ ],
+ "post-update-cmd": [
+ "php -r \"copy('vendor/automattic/wordbless/src/dbless-wpdb.php', 'wordpress/wp-content/db.php');\""
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Everything needed to manage Jetpack licenses client-side.",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-logo",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/logo",
+ "reference": "c62a9f8e0d4a881097cc7c65500badd5065f80b6"
+ },
+ "require-dev": {
+ "php-mock/php-mock": "^2.1",
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "scripts": {
+ "phpunit": [
+ "@composer install",
+ "./vendor/phpunit/phpunit/phpunit --colors=always"
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "A logo for Jetpack",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-options",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/options",
+ "reference": "6602d93381bb43d69627484fb8d7af5ce939d880"
+ },
+ "require": {
+ "automattic/jetpack-constants": "@dev"
+ },
+ "require-dev": {
+ "10up/wp_mock": "0.4.2",
+ "phpunit/phpunit": "7.*.*"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "legacy"
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "A wrapper for wp-options to manage specific Jetpack options.",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-partner",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/partner",
+ "reference": "14332b26281567bc6691096875176862fba01300"
+ },
+ "require-dev": {
+ "brain/monkey": "2.4.0",
+ "mockery/mockery": "^1.2",
+ "php-mock/php-mock": "^2.1",
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "scripts": {
+ "phpunit": [
+ "@composer install",
+ "./vendor/phpunit/phpunit/phpunit --colors=always"
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Support functions for Jetpack hosting partners.",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-redirect",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/redirect",
+ "reference": "84327fef3be2e9321aa99d081b71ac3013f06dc7"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "scripts": {
+ "phpunit": [
+ "@composer install",
+ "./vendor/phpunit/phpunit/phpunit --colors=always"
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Utilities to build URLs to the jetpack.com/redirect/ service",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-roles",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/roles",
+ "reference": "139dfbbf05e2f6c45682a9c8bf13ef29c9f9bd83"
+ },
+ "require-dev": {
+ "php-mock/php-mock": "^2.1",
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "scripts": {
+ "phpunit": [
+ "@composer install",
+ "./vendor/phpunit/phpunit/phpunit --colors=always"
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Utilities, related with user roles and capabilities.",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-status",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/status",
+ "reference": "65e70cc54dc8141a328e6d43fbe9bb4286281afb"
+ },
+ "require-dev": {
+ "brain/monkey": "2.4.0",
+ "php-mock/php-mock": "^2.1",
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "scripts": {
+ "phpunit": [
+ "@composer install",
+ "./vendor/phpunit/phpunit/phpunit --colors=always"
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Used to retrieve information about the current status of Jetpack and the site overall.",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-sync",
+ "version": "dev-add/publishing-tweetstorms",
+ "version_normalized": "dev-add/publishing-tweetstorms",
+ "dist": {
+ "type": "path",
+ "url": "./packages/sync",
+ "reference": "f163052f84a9656349985737f75c0e20c9a3c8e0"
+ },
+ "require": {
+ "automattic/jetpack-connection": "@dev",
+ "automattic/jetpack-constants": "@dev",
+ "automattic/jetpack-options": "@dev",
+ "automattic/jetpack-roles": "@dev",
+ "automattic/jetpack-status": "@dev"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Everything needed to allow syncing to the WP.com infrastructure.",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-terms-of-service",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/terms-of-service",
+ "reference": "f97241d09352c4ca44eab26520cac4c1903d08d3"
+ },
+ "require": {
+ "automattic/jetpack-connection": "@dev",
+ "automattic/jetpack-options": "@dev",
+ "automattic/jetpack-status": "@dev"
+ },
+ "require-dev": {
+ "php-mock/php-mock": "^2.1",
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "scripts": {
+ "phpunit": [
+ "@composer install",
+ "./vendor/phpunit/phpunit/phpunit --colors=always"
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Everything need to manage the terms of service state",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "automattic/jetpack-tracking",
+ "version": "dev-add/jetpack-lazy-images-package",
+ "version_normalized": "dev-add/jetpack-lazy-images-package",
+ "dist": {
+ "type": "path",
+ "url": "./packages/tracking",
+ "reference": "b2686165f1137a485f5f923c170d9d5b00871fe0"
+ },
+ "require": {
+ "automattic/jetpack-options": "@dev",
+ "automattic/jetpack-terms-of-service": "@dev"
+ },
+ "require-dev": {
+ "php-mock/php-mock": "^2.1",
+ "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5"
+ },
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "legacy",
+ "src/"
+ ]
+ },
+ "scripts": {
+ "phpunit": [
+ "@composer install",
+ "./vendor/phpunit/phpunit/phpunit --colors=always"
+ ]
+ },
+ "license": [
+ "GPL-2.0-or-later"
+ ],
+ "description": "Tracking for Jetpack",
+ "transport-options": {
+ "relative": true
+ }
+ },
+ {
+ "name": "nojimage/twitter-text-php",
+ "version": "v3.1.1",
+ "version_normalized": "3.1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nojimage/twitter-text-php.git",
+ "reference": "7f466b331cebfdd00e3568acaf45f2e90a39a320"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nojimage/twitter-text-php/zipball/7f466b331cebfdd00e3568acaf45f2e90a39a320",
+ "reference": "7f466b331cebfdd00e3568acaf45f2e90a39a320",
+ "shasum": ""
+ },
+ "require": {
+ "ext-intl": "*",
+ "ext-mbstring": "*",
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "ext-json": "*",
+ "phpunit/phpunit": "4.8.*|5.7.*|6.5.*",
+ "symfony/yaml": "^2.6.0|^3.4.0|^4.4.0|^5.0.0",
+ "twitter/twitter-text": "^3.0.0"
+ },
+ "time": "2020-09-30T13:30:59+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-0": {
+ "Twitter\\Text\\": "lib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Matt Sanford",
+ "email": "matt@mzsanford.com",
+ "homepage": "http://mzsanford.com"
+ },
+ {
+ "name": "Mike Cochrane",
+ "email": "mikec@mikenz.geek.nz",
+ "homepage": "http://mikenz.geek.nz"
+ },
+ {
+ "name": "Nick Pope",
+ "email": "git@nickpope.me.uk",
+ "homepage": "http://www.nickpope.me.uk"
+ },
+ {
+ "name": "Takashi Nojima",
+ "homepage": "http://php-tips.com"
+ }
+ ],
+ "description": "A library of PHP classes that provide auto-linking and extraction of usernames, lists, hashtags and URLs from tweets.",
+ "homepage": "https://github.com/nojimage/twitter-text-php",
+ "keywords": [
+ "autolink",
+ "extract",
+ "text",
+ "twitter"
+ ]
+ }
+]
diff --git a/vendor/composer/jetpack_autoload_classmap.php b/vendor/composer/jetpack_autoload_classmap.php
new file mode 100644
index 0000000000000..544b638070284
--- /dev/null
+++ b/vendor/composer/jetpack_autoload_classmap.php
@@ -0,0 +1,429 @@
+ array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-autoloader/src/AutoloadGenerator.php'
+ ),
+ 'Automattic\\Jetpack\\Autoloader\\CustomAutoloaderPlugin' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-autoloader/src/CustomAutoloaderPlugin.php'
+ ),
+ 'Automattic\\Jetpack\\Autoloader\\AutoloadProcessor' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-autoloader/src/AutoloadProcessor.php'
+ ),
+ 'Automattic\\Jetpack\\Autoloader\\ManifestGenerator' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-autoloader/src/ManifestGenerator.php'
+ ),
+ 'Twitter\\Text\\Regex' => array(
+ 'version' => '3.1.1.0',
+ 'path' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/Regex.php'
+ ),
+ 'Twitter\\Text\\StringUtils' => array(
+ 'version' => '3.1.1.0',
+ 'path' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/StringUtils.php'
+ ),
+ 'Twitter\\Text\\Configuration' => array(
+ 'version' => '3.1.1.0',
+ 'path' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/Configuration.php'
+ ),
+ 'Twitter\\Text\\Validator' => array(
+ 'version' => '3.1.1.0',
+ 'path' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/Validator.php'
+ ),
+ 'Twitter\\Text\\Extractor' => array(
+ 'version' => '3.1.1.0',
+ 'path' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/Extractor.php'
+ ),
+ 'Twitter\\Text\\Autolink' => array(
+ 'version' => '3.1.1.0',
+ 'path' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/Autolink.php'
+ ),
+ 'Twitter\\Text\\Parser' => array(
+ 'version' => '3.1.1.0',
+ 'path' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/Parser.php'
+ ),
+ 'Twitter\\Text\\EmojiRegex' => array(
+ 'version' => '3.1.1.0',
+ 'path' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/EmojiRegex.php'
+ ),
+ 'Twitter\\Text\\HitHighlighter' => array(
+ 'version' => '3.1.1.0',
+ 'path' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/HitHighlighter.php'
+ ),
+ 'Twitter\\Text\\ParseResults' => array(
+ 'version' => '3.1.1.0',
+ 'path' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/ParseResults.php'
+ ),
+ 'Twitter\\Text\\TldLists' => array(
+ 'version' => '3.1.1.0',
+ 'path' => $vendorDir . '/nojimage/twitter-text-php/lib/Twitter/Text/TldLists.php'
+ ),
+ 'Automattic\\Jetpack\\Plugin\\Tracking' => array(
+ 'version' => 'dev-branch-9.2',
+ 'path' => $baseDir . '/src/class-tracking.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Defaults' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/class-defaults.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Replicastore' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/class-replicastore.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Actions' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/class-actions.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Settings' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/class-settings.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\JSON_Deflate_Array_Codec' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/class-json-deflate-array-codec.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Health' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/class-health.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Functions' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/class-functions.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Server' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/class-server.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Simple_Codec' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/class-simple-codec.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Sender' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/class-sender.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Lock' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/class-lock.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Utils' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/class-utils.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Replicastore_Interface' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/interface-replicastore.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Queue_Buffer' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/class-queue-buffer.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/class-modules.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Listener' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/class-listener.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Main' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/class-main.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\Callables' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-callables.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\Network_Options' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-network-options.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\Updates' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-updates.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\Full_Sync' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-full-sync.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\Term_Relationships' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-term-relationships.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\Stats' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-stats.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\WP_Super_Cache' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-wp-super-cache.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\Comments' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-comments.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\WooCommerce' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-woocommerce.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\Protect' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-protect.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\Terms' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-terms.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\Full_Sync_Immediately' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-full-sync-immediately.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\Plugins' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-plugins.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\Meta' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-meta.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\Themes' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-themes.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\Constants' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-constants.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\Posts' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-posts.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\Attachments' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-attachments.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\Options' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-options.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\Import' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-import.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\Users' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-users.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\Module' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-module.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Modules\\Menus' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/modules/class-menus.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Queue' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/class-queue.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Users' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/class-users.php'
+ ),
+ 'Automattic\\Jetpack\\Sync\\Codec_Interface' => array(
+ 'version' => 'dev-add/publishing-tweetstorms',
+ 'path' => $vendorDir . '/automattic/jetpack-sync/src/interface-codec.php'
+ ),
+ 'Automattic\\Jetpack\\Licensing' => array(
+ 'version' => 'dev-add/license-support',
+ 'path' => $vendorDir . '/automattic/jetpack-licensing/src/class-licensing.php'
+ ),
+ 'Automattic\\Jetpack\\Jetpack_Lazy_Images' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-lazy-images/src/lazy-images.php'
+ ),
+ 'Automattic\\Jetpack\\JITMS\\Pre_Connection_JITM' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-jitm/src/class-pre-connection-jitm.php'
+ ),
+ 'Automattic\\Jetpack\\JITMS\\JITM' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-jitm/src/class-jitm.php'
+ ),
+ 'Automattic\\Jetpack\\JITMS\\Post_Connection_JITM' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-jitm/src/class-post-connection-jitm.php'
+ ),
+ 'Automattic\\Jetpack\\Heartbeat' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-heartbeat/src/class-heartbeat.php'
+ ),
+ 'Automattic\\Jetpack\\Device_Detection' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-device-detection/src/class-device-detection.php'
+ ),
+ 'Automattic\\Jetpack\\Device_Detection\\User_Agent_Info' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-device-detection/src/class-user-agent-info.php'
+ ),
+ 'Automattic\\Jetpack\\Config' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-config/src/class-config.php'
+ ),
+ 'JetpackTracking' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-compat/legacy/class-jetpacktracking.php'
+ ),
+ 'Jetpack_Sync_Actions' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-compat/legacy/class-jetpack-sync-actions.php'
+ ),
+ 'Jetpack_Sync_Modules' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-compat/legacy/class-jetpack-sync-modules.php'
+ ),
+ 'Jetpack_Client' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-compat/legacy/class-jetpack-client.php'
+ ),
+ 'Jetpack_Sync_Settings' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-compat/legacy/class-jetpack-sync-settings.php'
+ ),
+ 'Automattic\\Jetpack\\Blocks' => array(
+ 'version' => 'dev-add/nonce-removal',
+ 'path' => $vendorDir . '/automattic/jetpack-blocks/src/class-blocks.php'
+ ),
+ 'Automattic\\Jetpack\\Backup\\Helper_Script_Manager' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-backup/src/class-helper-script-manager.php'
+ ),
+ 'Automattic\\Jetpack\\Abtest' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-abtest/src/class-abtest.php'
+ ),
+ 'Automattic\\Jetpack\\Assets' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-assets/src/class-assets.php'
+ ),
+ 'Automattic\\Jetpack\\Assets\\Logo' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-logo/src/class-logo.php'
+ ),
+ 'Automattic\\Jetpack\\Partner' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-partner/src/class-partner.php'
+ ),
+ 'Automattic\\Jetpack\\Redirect' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-redirect/src/class-redirect.php'
+ ),
+ 'Automattic\\Jetpack\\A8c_Mc_Stats' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-a8c-mc-stats/src/class-a8c-mc-stats.php'
+ ),
+ 'Automattic\\Jetpack\\Error' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-error/src/class-error.php'
+ ),
+ 'Jetpack_Tracks_Client' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-tracking/legacy/class-jetpack-tracks-client.php'
+ ),
+ 'Jetpack_Tracks_Event' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-tracking/legacy/class-jetpack-tracks-event.php'
+ ),
+ 'Automattic\\Jetpack\\Tracking' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-tracking/src/class-tracking.php'
+ ),
+ 'Automattic\\Jetpack\\Terms_Of_Service' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-terms-of-service/src/class-terms-of-service.php'
+ ),
+ 'Jetpack_IXR_Client' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-connection/legacy/class-jetpack-ixr-client.php'
+ ),
+ 'Jetpack_IXR_ClientMulticall' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-connection/legacy/class-jetpack-ixr-clientmulticall.php'
+ ),
+ 'Jetpack_XMLRPC_Server' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-connection/legacy/class-jetpack-xmlrpc-server.php'
+ ),
+ 'Jetpack_Signature' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-connection/legacy/class-jetpack-signature.php'
+ ),
+ 'Automattic\\Jetpack\\Connection\\Manager' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-connection/src/class-manager.php'
+ ),
+ 'Automattic\\Jetpack\\Connection\\Client' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-connection/src/class-client.php'
+ ),
+ 'Automattic\\Jetpack\\Connection\\Plugin' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-connection/src/class-plugin.php'
+ ),
+ 'Automattic\\Jetpack\\Connection\\XMLRPC_Connector' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-connection/src/class-xmlrpc-connector.php'
+ ),
+ 'Automattic\\Jetpack\\Connection\\Rest_Authentication' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-connection/src/class-rest-authentication.php'
+ ),
+ 'Automattic\\Jetpack\\Connection\\XMLRPC_Async_Call' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-connection/src/class-xmlrpc-async-call.php'
+ ),
+ 'Automattic\\Jetpack\\Connection\\Manager_Interface' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-connection/src/interface-manager.php'
+ ),
+ 'Automattic\\Jetpack\\Connection\\REST_Connector' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-connection/src/class-rest-connector.php'
+ ),
+ 'Automattic\\Jetpack\\Connection\\Utils' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-connection/src/class-utils.php'
+ ),
+ 'Automattic\\Jetpack\\Connection\\Plugin_Storage' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-connection/src/class-plugin-storage.php'
+ ),
+ 'Automattic\\Jetpack\\Connection\\Error_Handler' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-connection/src/class-error-handler.php'
+ ),
+ 'Automattic\\Jetpack\\Roles' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-roles/src/class-roles.php'
+ ),
+ 'Automattic\\Jetpack\\Status' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-status/src/class-status.php'
+ ),
+ 'Jetpack_Options' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-options/legacy/class-jetpack-options.php'
+ ),
+ 'Automattic\\Jetpack\\Constants' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-constants/src/class-constants.php'
+ ),
+);
diff --git a/vendor/composer/jetpack_autoload_filemap.php b/vendor/composer/jetpack_autoload_filemap.php
new file mode 100644
index 0000000000000..9f634976dbdfa
--- /dev/null
+++ b/vendor/composer/jetpack_autoload_filemap.php
@@ -0,0 +1,21 @@
+ array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-connection/legacy/load-ixr.php'
+ ),
+ 'd4eb94df91a729802d18373ee8cdc79f' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-backup/actions.php'
+ ),
+ '009de6aaa0d497eacea41fab13fc05f1' => array(
+ 'version' => 'dev-add/jetpack-lazy-images-package',
+ 'path' => $vendorDir . '/automattic/jetpack-compat/functions.php'
+ ),
+);
diff --git a/vendor/jetpack-autoloader/autoload_functions.php b/vendor/jetpack-autoloader/autoload_functions.php
new file mode 100644
index 0000000000000..bc76a8eb78512
--- /dev/null
+++ b/vendor/jetpack-autoloader/autoload_functions.php
@@ -0,0 +1,74 @@
+find_class_file( $class_name );
+ if ( ! isset( $file ) ) {
+ return false;
+ }
+
+ require_once $file;
+ return true;
+}
+
+/**
+ * Finds the latest installed autoloader. If this is the latest autoloader, sets
+ * up the classmap and filemap.
+ */
+function set_up_autoloader() {
+ global $jetpack_autoloader_latest_version;
+ global $jetpack_autoloader_loader;
+
+ require_once __DIR__ . '/class-plugins-handler.php';
+ require_once __DIR__ . '/class-version-selector.php';
+ require_once __DIR__ . '/class-autoloader-locator.php';
+ require_once __DIR__ . '/class-autoloader-handler.php';
+
+ $plugins_handler = new Plugins_Handler();
+ $version_selector = new Version_Selector();
+ $autoloader_handler = new Autoloader_Handler(
+ $plugins_handler->get_current_plugin_path(),
+ $plugins_handler->get_all_active_plugins_paths(),
+ new Autoloader_Locator( $version_selector ),
+ $version_selector
+ );
+
+ // The autoloader must be reset when a plugin that was previously unknown is detected.
+ if ( $autoloader_handler->should_autoloader_reset() ) {
+ $jetpack_autoloader_latest_version = null;
+ $jetpack_autoloader_loader = null;
+ }
+
+ if ( ! $autoloader_handler->is_latest_autoloader() || isset( $jetpack_autoloader_loader ) ) {
+ return;
+ }
+
+ require_once __DIR__ . '/class-manifest-handler.php';
+ require_once __DIR__ . '/class-version-loader.php';
+
+ $jetpack_autoloader_loader = $autoloader_handler->build_autoloader();
+ $autoloader_handler->update_autoloader_chain();
+
+ // Now that the autoloader is ready we can load the files in the filemap safely.
+ $jetpack_autoloader_loader->load_filemap();
+}
diff --git a/vendor/jetpack-autoloader/class-autoloader-handler.php b/vendor/jetpack-autoloader/class-autoloader-handler.php
new file mode 100644
index 0000000000000..bc0e3989ad1b2
--- /dev/null
+++ b/vendor/jetpack-autoloader/class-autoloader-handler.php
@@ -0,0 +1,198 @@
+current_plugin_path = $current_plugin_path;
+ $this->active_plugin_paths = $active_plugin_paths;
+ $this->autoloader_locator = $autoloader_locator;
+ $this->version_selector = $version_selector;
+ }
+
+ /**
+ * Finds the latest installed autoloader.
+ *
+ * @return bool True if this autoloader is the latest, false otherwise.
+ */
+ public function is_latest_autoloader() {
+ global $jetpack_autoloader_latest_version;
+
+ if ( isset( $jetpack_autoloader_latest_version ) ) {
+ return $jetpack_autoloader_latest_version === $this->autoloader_locator->get_autoloader_version( $this->current_plugin_path );
+ }
+
+ $latest_plugin = $this->autoloader_locator->find_latest_autoloader( $this->active_plugin_paths, $jetpack_autoloader_latest_version );
+ if ( ! isset( $latest_plugin ) ) {
+ return true;
+ }
+
+ if ( $latest_plugin !== $this->current_plugin_path ) {
+ require $this->autoloader_locator->get_autoloader_path( $latest_plugin );
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Checks whether the autoloader should be reset. The autoloader should be reset:
+ *
+ * - When a plugin is activated via a method other than a request, for example using WP-CLI.
+ * - When the active plugins list changes between autoloader checks, for example when filtered by a plugin.
+ *
+ * We perform this reset because the manifest files for the plugin will have been initially unknown when
+ * selecting versions for classes and files.
+ *
+ * If the current plugin is not already known, this method will add it to the
+ * $jetpack_autoloader_activating_plugins_paths global.
+ * The $jetpack_autoloader_cached_plugin_paths global will store a cache of the
+ * active plugin paths when last changed.
+ *
+ * @return boolean True if the autoloader must be reset, else false.
+ */
+ public function should_autoloader_reset() {
+ global $jetpack_autoloader_activating_plugins_paths;
+ global $jetpack_autoloader_cached_plugin_paths;
+
+ $plugin_unknown = ! in_array( $this->current_plugin_path, $this->active_plugin_paths, true );
+ if ( $plugin_unknown ) {
+ if ( ! isset( $jetpack_autoloader_activating_plugins_paths ) ) {
+ $jetpack_autoloader_activating_plugins_paths = array();
+ }
+
+ // If the current plugin isn't known, add it to the activating plugins list.
+ $jetpack_autoloader_activating_plugins_paths[] = $this->current_plugin_path;
+ $this->active_plugin_paths[] = $this->current_plugin_path;
+ }
+
+ $cache_invalidated = $jetpack_autoloader_cached_plugin_paths !== $this->active_plugin_paths;
+ if ( $cache_invalidated ) {
+ $jetpack_autoloader_cached_plugin_paths = $this->active_plugin_paths;
+ }
+
+ return $plugin_unknown || $cache_invalidated;
+ }
+
+ /**
+ * Builds the Version_Autoloader class that is used for autoloading.
+ *
+ * @return Version_Loader
+ */
+ public function build_autoloader() {
+ $manifest_handler = new Manifest_Handler( $this->active_plugin_paths, $this->version_selector );
+
+ global $jetpack_packages_psr4;
+ $jetpack_packages_psr4 = array();
+ $manifest_handler->register_plugin_manifests( 'vendor/composer/jetpack_autoload_psr4.php', $jetpack_packages_psr4 );
+
+ global $jetpack_packages_classmap;
+ $jetpack_packages_classmap = array();
+ $manifest_handler->register_plugin_manifests( 'vendor/composer/jetpack_autoload_classmap.php', $jetpack_packages_classmap );
+
+ global $jetpack_packages_filemap;
+ $jetpack_packages_filemap = array();
+ $manifest_handler->register_plugin_manifests( 'vendor/composer/jetpack_autoload_filemap.php', $jetpack_packages_filemap );
+
+ // Store the generated autoloader data in the loader so we can use it.
+ return new Version_Loader(
+ $this->version_selector,
+ $jetpack_packages_classmap,
+ $jetpack_packages_psr4,
+ $jetpack_packages_filemap
+ );
+ }
+
+ /**
+ * Updates the spl autoloader chain:
+ * - Registers this namespace's autoloader function.
+ * - If a v1 autoloader function is registered, moves it to the end of the chain.
+ * - Removes any other v2 autoloader functions that have already been registered. This
+ * can occur when the autoloader is being reset by an activating plugin.
+ */
+ public function update_autoloader_chain() {
+ spl_autoload_register( __NAMESPACE__ . '\autoloader' );
+
+ $autoload_chain = spl_autoload_functions();
+
+ foreach ( $autoload_chain as $autoloader ) {
+ if ( ! is_string( $autoloader ) ) {
+ /*
+ * The Jetpack Autoloader functions are registered as strings, so
+ * just continue if $autoloader isn't a string.
+ */
+ continue;
+ }
+
+ if ( self::V1_AUTOLOADER_NAME === $autoloader ) {
+ // Move the v1.* autoloader function to the end of the spl autoloader chain.
+ spl_autoload_unregister( $autoloader );
+ spl_autoload_register( $autoloader );
+
+ } elseif (
+ self::V2_AUTOLOADER_BASE === substr( $autoloader, 0, strlen( self::V2_AUTOLOADER_BASE ) )
+ && __NAMESPACE__ !== substr( $autoloader, 0, strlen( __NAMESPACE__ ) )
+ ) {
+ // Unregister any other v2.* autoloader functions if they're in the chain.
+ spl_autoload_unregister( $autoloader );
+ }
+ }
+ }
+}
diff --git a/vendor/jetpack-autoloader/class-autoloader-locator.php b/vendor/jetpack-autoloader/class-autoloader-locator.php
new file mode 100644
index 0000000000000..3cd7724b32882
--- /dev/null
+++ b/vendor/jetpack-autoloader/class-autoloader-locator.php
@@ -0,0 +1,90 @@
+version_selector = $version_selector;
+ }
+
+ /**
+ * Finds the path to the plugin with the latest autoloader.
+ *
+ * @param array $plugin_paths An array of plugin paths.
+ * @param string $latest_version The latest version reference.
+ *
+ * @return string|null
+ */
+ public function find_latest_autoloader( $plugin_paths, &$latest_version ) {
+ $latest_plugin = null;
+
+ foreach ( $plugin_paths as $plugin_path ) {
+ $version = $this->get_autoloader_version( $plugin_path );
+ if ( ! $this->version_selector->is_version_update_required( $latest_version, $version ) ) {
+ continue;
+ }
+
+ $latest_version = $version;
+ $latest_plugin = $plugin_path;
+ }
+
+ return $latest_plugin;
+ }
+
+ /**
+ * Gets the path to the autoloader.
+ *
+ * @param string $plugin_path The path to the plugin.
+ *
+ * @return string
+ */
+ public function get_autoloader_path( $plugin_path ) {
+ return trailingslashit( $plugin_path ) . 'vendor/autoload_packages.php';
+ }
+
+ /**
+ * Gets the version for the autoloader.
+ *
+ * @param string $plugin_path The path to the plugin.
+ *
+ * @return string|null
+ */
+ public function get_autoloader_version( $plugin_path ) {
+ $classmap = trailingslashit( $plugin_path ) . 'vendor/composer/jetpack_autoload_classmap.php';
+ if ( ! file_exists( $classmap ) ) {
+ return null;
+ }
+
+ $classmap = require $classmap;
+ if ( isset( $classmap[ AutoloadGenerator::class ] ) ) {
+ return $classmap[ AutoloadGenerator::class ]['version'];
+ }
+
+ return null;
+ }
+}
diff --git a/vendor/jetpack-autoloader/class-manifest-handler.php b/vendor/jetpack-autoloader/class-manifest-handler.php
new file mode 100644
index 0000000000000..9757d01717330
--- /dev/null
+++ b/vendor/jetpack-autoloader/class-manifest-handler.php
@@ -0,0 +1,107 @@
+active_plugin_paths = $active_plugin_paths;
+ $this->version_selector = $version_selector;
+ }
+
+ /**
+ * Registers all of the paths in a given manifest.
+ *
+ * @param string $manifest_path The path that we're loading the manifest from in each plugin.
+ * @param array $path_map The path map to add the contents of the manifests to.
+ *
+ * @return array $path_map The path map we've built using the manifests in each plugin.
+ */
+ public function register_plugin_manifests( $manifest_path, &$path_map ) {
+ $file_paths = array_map(
+ function ( $path ) use ( $manifest_path ) {
+ return trailingslashit( $path ) . $manifest_path;
+ },
+ $this->active_plugin_paths
+ );
+
+ foreach ( $file_paths as $path ) {
+ $this->register_manifest( $path, $path_map );
+ }
+
+ return $path_map;
+ }
+
+ /**
+ * Registers a plugin's manifest file with the path map.
+ *
+ * @param string $manifest_path The absolute path to the manifest that we're loading.
+ * @param array $path_map The path map to add the contents of the manifest to.
+ */
+ protected function register_manifest( $manifest_path, &$path_map ) {
+ if ( ! is_readable( $manifest_path ) ) {
+ return;
+ }
+
+ $manifest = require $manifest_path;
+ if ( ! is_array( $manifest ) ) {
+ return;
+ }
+
+ foreach ( $manifest as $key => $data ) {
+ $this->register_record( $key, $data, $path_map );
+ }
+ }
+
+ /**
+ * Registers an entry from the manifest in the path map.
+ *
+ * @param string $key The identifier for the entry we're registering.
+ * @param array $data The data for the entry we're registering.
+ * @param array $path_map The path map to add the contents of the manifest to.
+ */
+ protected function register_record( $key, $data, &$path_map ) {
+ if ( isset( $path_map[ $key ]['version'] ) ) {
+ $selected_version = $path_map[ $key ]['version'];
+ } else {
+ $selected_version = null;
+ }
+
+ if ( $this->version_selector->is_version_update_required( $selected_version, $data['version'] ) ) {
+ $path_map[ $key ] = array(
+ 'version' => $data['version'],
+ 'path' => $data['path'],
+ );
+ }
+ }
+}
diff --git a/vendor/jetpack-autoloader/class-plugins-handler.php b/vendor/jetpack-autoloader/class-plugins-handler.php
new file mode 100644
index 0000000000000..a67a871eb4b6c
--- /dev/null
+++ b/vendor/jetpack-autoloader/class-plugins-handler.php
@@ -0,0 +1,137 @@
+get_active_plugins_paths();
+ $multisite_plugins_paths = $this->get_multisite_plugins_paths();
+ $activating_plugins_paths = $this->get_plugins_activating_via_request();
+
+ return array_unique(
+ array_merge(
+ $active_plugins_paths,
+ $activating_plugins_paths,
+ $multisite_plugins_paths,
+ (array) $jetpack_autoloader_activating_plugins_paths
+ )
+ );
+ }
+
+ /**
+ * Returns an array containing the paths of the active sitewide plugins in a multisite environment.
+ *
+ * @return array The paths of the active sitewide plugins or an empty array.
+ */
+ protected function get_multisite_plugins_paths() {
+ $plugin_slugs = is_multisite()
+ ? array_keys( get_site_option( 'active_sitewide_plugins', array() ) )
+ : array();
+
+ $plugin_slugs = array_filter( $plugin_slugs, array( $this, 'is_directory_plugin' ) );
+ return array_map( array( $this, 'create_plugin_path' ), $plugin_slugs );
+ }
+
+ /**
+ * Returns an array containing the paths of the currently active plugins.
+ *
+ * @return array The active plugins' paths or an empty array.
+ */
+ protected function get_active_plugins_paths() {
+ $plugin_slugs = (array) get_option( 'active_plugins', array() );
+ $plugin_slugs = array_filter( $plugin_slugs, array( $this, 'is_directory_plugin' ) );
+ return array_map( array( $this, 'create_plugin_path' ), $plugin_slugs );
+ }
+
+ /**
+ * Adds the plugin directory from the WP_PLUGIN_DIR constant to the plugin slug.
+ *
+ * @param string $plugin_slug The plugin slug.
+ */
+ private function create_plugin_path( $plugin_slug ) {
+ $plugin_dir = str_replace( '\\', '/', WP_PLUGIN_DIR );
+ return trailingslashit( $plugin_dir ) . substr( $plugin_slug, 0, strrpos( $plugin_slug, '/' ) );
+ }
+
+ /**
+ * Ensure the plugin has its own directory and not a single-file plugin.
+ *
+ * @param string $plugin Plugin name, may be prefixed with "/".
+ *
+ * @return bool
+ */
+ public function is_directory_plugin( $plugin ) {
+ return strlen( $plugin ) > 1 && false !== strpos( $plugin, '/', 1 );
+ }
+
+ /**
+ * Returns an array containing the names of plugins that are activating via a request.
+ *
+ * @return array An array of names of the activating plugins or an empty array.
+ */
+ private function get_plugins_activating_via_request() {
+
+ // phpcs:disable WordPress.Security.NonceVerification.Recommended
+ // phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash
+ // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+
+ $action = isset( $_REQUEST['action'] ) ? $_REQUEST['action'] : false;
+ $plugin = isset( $_REQUEST['plugin'] ) ? $_REQUEST['plugin'] : false;
+ $nonce = isset( $_REQUEST['_wpnonce'] ) ? $_REQUEST['_wpnonce'] : false;
+
+ /**
+ * Note: we're not actually checking the nonce here becase it's too early
+ * in the execution. The pluggable functions are not yet loaded to give
+ * plugins a chance to plug their versions. Therefore we're doing the bare
+ * minimum: checking whether the nonce exists and it's in the right place.
+ * The request will fail later if the nonce doesn't pass the check.
+ */
+
+ // In case of a single plugin activation there will be a plugin slug.
+ if ( 'activate' === $action && ! empty( $nonce ) ) {
+ return array( $this->create_plugin_path( wp_unslash( $plugin ) ) );
+ }
+
+ $plugins = isset( $_REQUEST['checked'] ) ? $_REQUEST['checked'] : array();
+
+ // In case of bulk activation there will be an array of plugins.
+ if ( 'activate-selected' === $action && ! empty( $nonce ) ) {
+ $plugin_slugs = array_map( 'wp_unslash', $plugins );
+ return array_map( array( $this, 'create_plugin_path' ), $plugin_slugs );
+ }
+
+ // phpcs:enable WordPress.Security.NonceVerification.Recommended
+ // phpcs:enable WordPress.Security.ValidatedSanitizedInput.MissingUnslash
+ // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+ return array();
+ }
+
+ /**
+ * Returns the path of the current plugin.
+ *
+ * @return string The path of the current plugin.
+ */
+ public function get_current_plugin_path() {
+ $vendor_path = str_replace( '\\', '/', dirname( __DIR__ ) );
+ // Path to the plugin's folder (the parent of the vendor/jetpack-autoloader folder).
+ return substr( $vendor_path, 0, strrpos( $vendor_path, '/' ) );
+ }
+}
diff --git a/vendor/jetpack-autoloader/class-version-loader.php b/vendor/jetpack-autoloader/class-version-loader.php
new file mode 100644
index 0000000000000..db0544cf64639
--- /dev/null
+++ b/vendor/jetpack-autoloader/class-version-loader.php
@@ -0,0 +1,164 @@
+version_selector = $version_selector;
+ $this->classmap = $classmap;
+ $this->psr4_map = $psr4_map;
+ $this->filemap = $filemap;
+ }
+
+ /**
+ * Finds the file path for the given class.
+ *
+ * @param string $class_name The class to find.
+ *
+ * @return string|null $file_path The path to the file if found, null if no class was found.
+ */
+ public function find_class_file( $class_name ) {
+ $data = $this->select_newest_file(
+ isset( $this->classmap[ $class_name ] ) ? $this->classmap[ $class_name ] : null,
+ $this->find_psr4_file( $class_name )
+ );
+ if ( ! isset( $data ) ) {
+ return null;
+ }
+
+ return $data['path'];
+ }
+
+ /**
+ * Load all of the files in the filemap.
+ */
+ public function load_filemap() {
+ if ( empty( $this->filemap ) ) {
+ return;
+ }
+
+ foreach ( $this->filemap as $file_identifier => $file_data ) {
+ if ( empty( $GLOBALS['__composer_autoload_files'][ $file_identifier ] ) ) {
+ require_once $file_data['path'];
+
+ $GLOBALS['__composer_autoload_files'][ $file_identifier ] = true;
+ }
+ }
+ }
+
+ /**
+ * Compares different class sources and returns the newest.
+ *
+ * @param array|null $classmap_data The classmap class data.
+ * @param array|null $psr4_data The PSR-4 class data.
+ *
+ * @return array|null $data
+ */
+ private function select_newest_file( $classmap_data, $psr4_data ) {
+ if ( ! isset( $classmap_data ) ) {
+ return $psr4_data;
+ } elseif ( ! isset( $psr4_data ) ) {
+ return $classmap_data;
+ }
+
+ if ( $this->version_selector->is_version_update_required( $classmap_data['version'], $psr4_data['version'] ) ) {
+ return $psr4_data;
+ }
+
+ return $classmap_data;
+ }
+
+ /**
+ * Finds the file for a given class in a PSR-4 namespace.
+ *
+ * @param string $class_name The class to find.
+ *
+ * @return array|null $data The version and path path to the file if found, null otherwise.
+ */
+ private function find_psr4_file( $class_name ) {
+ if ( ! isset( $this->psr4_map ) ) {
+ return null;
+ }
+
+ // Don't bother with classes that have no namespace.
+ $class_index = strrpos( $class_name, '\\' );
+ if ( ! $class_index ) {
+ return null;
+ }
+ $class_for_path = str_replace( '\\', '/', $class_name );
+
+ // Search for the namespace by iteratively cutting off the last segment until
+ // we find a match. This allows us to check the most-specific namespaces
+ // first as well as minimize the amount of time spent looking.
+ for (
+ $class_namespace = substr( $class_name, 0, $class_index );
+ ! empty( $class_namespace );
+ $class_namespace = substr( $class_namespace, 0, strrpos( $class_namespace, '\\' ) )
+ ) {
+ $namespace = $class_namespace . '\\';
+ if ( ! isset( $this->psr4_map[ $namespace ] ) ) {
+ continue;
+ }
+ $data = $this->psr4_map[ $namespace ];
+
+ foreach ( $data['path'] as $path ) {
+ $path .= '/' . substr( $class_for_path, strlen( $namespace ) ) . '.php';
+ if ( file_exists( $path ) ) {
+ return array(
+ 'version' => $data['version'],
+ 'path' => $path,
+ );
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/vendor/jetpack-autoloader/class-version-selector.php b/vendor/jetpack-autoloader/class-version-selector.php
new file mode 100644
index 0000000000000..d68d891d37307
--- /dev/null
+++ b/vendor/jetpack-autoloader/class-version-selector.php
@@ -0,0 +1,69 @@
+is_package_version_dev( $selected_version ) ) {
+ return false;
+ }
+
+ if ( $this->is_package_version_dev( $compare_version ) ) {
+ if ( $use_dev_versions ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ if ( version_compare( $selected_version, $compare_version, '<' ) ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether the given package version is a development version.
+ *
+ * @param String $version The package version.
+ *
+ * @return Boolean True if the version is a dev version, else false.
+ */
+ private function is_package_version_dev( $version ) {
+ if ( 'dev-' === substr( $version, 0, 4 ) || '9999999-dev' === $version ) {
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/vendor/nojimage/twitter-text-php/Changes.md b/vendor/nojimage/twitter-text-php/Changes.md
new file mode 100755
index 0000000000000..08dc29e926d70
--- /dev/null
+++ b/vendor/nojimage/twitter-text-php/Changes.md
@@ -0,0 +1,27 @@
+# Changes
+
+## 3.0.0
+
+- Pass twitter-text conformance 3.0.0
+- Change default configuration to v3 (emojiParsingEnabled=true)
+- Add t.co with query string support
+- Add Directional Characters support
+
+## 2.0.2
+
+- Pass twitter-text conformance 2.0.5
+- Change default configuration to v2 in `Validator`.
+
+## 2.0.1
+
+- Fixes wrong method call in Extractor::extract() #19
+
+## 2.0.0
+
+- Pass twitter-text conformance 2.0.0
+- Add to required php extension, `mbstring` and `intl`.
+- Add `Parser`, `ParseResults`, `Configuration` class for twitter-text 2.0 "weighted" tweets.
+- Twtter\Text classes no longer extended Regex class.
+- Deprecated `Validator::isValidTweetText()`, `Validator::getTweetLength()`.
+- `Extractor` constractor no longer accepts `$tweet`
+- `Validator` constractor no longer accepts `$tweet` and `$config`. `Validator` constractor only accepts `Configuration` incetance.
diff --git a/vendor/nojimage/twitter-text-php/LICENSE b/vendor/nojimage/twitter-text-php/LICENSE
new file mode 100644
index 0000000000000..f9dd53d7604b6
--- /dev/null
+++ b/vendor/nojimage/twitter-text-php/LICENSE
@@ -0,0 +1,13 @@
+Copyright 2010 Mike Cochrane
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may not
+use this file except in compliance with the License. You may obtain a copy of
+the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+License for the specific language governing permissions and limitations under
+the License.
diff --git a/vendor/nojimage/twitter-text-php/README.md b/vendor/nojimage/twitter-text-php/README.md
new file mode 100755
index 0000000000000..499dd8866138e
--- /dev/null
+++ b/vendor/nojimage/twitter-text-php/README.md
@@ -0,0 +1,148 @@
+# Twitter Text (PHP Edition) #
+
+A library of PHP classes that provide auto-linking and extraction of usernames,
+lists, hashtags and URLs from tweets. Originally created from twitter-text-rb
+and twitter-text-java projects by Matt Sanford and ported to PHP by Mike
+Cochrane, this library has been improved and made more complete by Nick Pope.
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Requirements ##
+
+- PHP 5.3 or higher
+- ext-mbstring
+- ext-intl
+
+If the necessary extensions are not installed on the server, please install it additionally or use [symfony/polyfill](https://github.com/symfony/polyfill).
+
+## Install ##
+
+You can install this library into your application using [Composer](https://getcomposer.org/).
+
+```
+composer require nojimage/twitter-text-php
+```
+
+### Note for Older Server ###
+
+This library use intl/libICU.
+Some older server and PHP 7.2+ combinations may have deprecated warnings due to older ICU versions. [refs #32](https://github.com/nojimage/twitter-text-php/issues/32)
+
+If you are using RHEL/CentOS 6, installing PHP using [the remi repository](https://rpms.remirepo.net/) is the best choice.
+If you use remi, you can use the new ICU.
+
+## Features ##
+
+### Autolink ##
+
+ - Add links to all matching Twitter usernames (no account verification).
+ - Add links to all user lists (of the form @username/list-name).
+ - Add links to all valid hashtags.
+ - Add links to all URLs.
+ - Support for international character sets.
+
+### Extractor ###
+
+ - Extract mentioned Twitter usernames (from anywhere in the tweet).
+ - Extract replied to Twitter usernames (from start of the tweet).
+ - Extract all user lists (of the form @username/list-name).
+ - Extract all valid hashtags.
+ - Extract all URLs.
+ - Support for international character sets.
+
+### Hit Highlighter ###
+
+ - Highlight text specifed by a range by surrounding with a tag.
+ - Support for highlighting when tweet has already been autolinked.
+ - Support for international character sets.
+
+### Validation ###
+
+ - Validate different twitter text elements.
+ - Support for international character sets.
+
+### Parser ###
+
+- Parses a given tweet text with the weighted character count configuration.
+
+## Length validation ##
+
+twitter-text 3.0 updates the config file with `emojiParsingEnabled` config option.
+When true, twitter-text-php will parse and discount emoji supported by the [Unicode Emoji 11.0](http://www.unicode.org/emoji/charts-11.0) (NOTE: Original [twitter-text](https://github.com/twitter/twitter-text) supported [twemoji library](https://github.com/twitter/twemoji)).
+The length of these emoji will be the default weight (200 or two characters) even if they contain multiple code points combined by zero-width joiners.
+This means that emoji with skin tone and gender modifiers no longer count as more characters than those without such modifiers.
+
+twitter-text 2.0 introduced configuration files that define how Tweets are parsed for length. This allows for backwards compatibility and flexibility going forward.
+Old-style traditional 140-character parsing is defined by the v1.json configuration file, whereas v2.json is updated for "weighted" Tweets where ranges of Unicode code points can have independent weights aside from the default weight.
+The sum of all code points, each weighted appropriately, should not exceed the max weighted length.
+
+Some old methods from twitter-text-php 1.0 have been marked deprecated, such as the `Twitter\Text\Validator::isValidTweetText()`, `Twitter\Text\Validator::getTweetLength()` method. The new API is based on the following method, `Twitter\Text\Parser::parseTweet()`
+
+```(php)
+use Twitter\Text\Parser;
+$parser = new Parser();
+$result = $parser->parseTweet($text);
+```
+
+This method takes a string as input and returns a results object that contains information about the string. `Twitter\Text\ParseResults` object includes:
+
+- `weightedLength`: the overall length of the tweet with code points
+weighted per the ranges defined in the configuration file.
+
+- `permillage`: indicates the proportion (per thousand) of the weighted
+length in comparison to the max weighted length. A value > 1000
+indicates input text that is longer than the allowable maximum.
+
+- `valid`: indicates if input text length corresponds to a valid
+result.
+
+- `displayRangeStart, displayRangeEnd`: An array of two unicode code point
+indices identifying the inclusive start and exclusive end of the
+displayable content of the Tweet. For more information, see
+the description of `display_text_range` here:
+[Tweet updates](https://developer.twitter.com/en/docs/tweets/tweet-updates)
+
+- `validRangeStart, validRangeRnd`: An array of two unicode code point
+indices identifying the inclusive start and exclusive end of the valid
+content of the Tweet. For more information on the extended Tweet
+payload see [Tweet updates](https://developer.twitter.com/en/docs/tweets/tweet-updates)
+
+## Examples ##
+
+For examples, please see `tests/example.php` which you can view in a browser or
+run from the command line.
+
+## Conformance ##
+
+You'll need the test data which is in YAML format from the following
+repository:
+
+ https://github.com/twitter/twitter-text
+
+`twitter/twitter-text` already included in `composer.json`, so you should just need to run:
+
+ curl -s https://getcomposer.org/installer | php
+ php composer.phar install
+
+There are a couple of options for testing conformance:
+
+- Run `phpunit` in from the root folder of the project.
+
+## Thanks & Contributions ##
+
+The bulk of this library is from the heroic efforts of:
+
+ - Matt Sanford (https://github.com/mzsanford): For the orignal Ruby and Java implementions.
+ - Mike Cochrane (https://github.com/mikenz): For the initial PHP code.
+ - Nick Pope (https://github.com/ngnpope): For the bulk of the maintenance work to date.
+ - Takashi Nojima (https://github.com/nojimage): For ongoing maintenance work.
diff --git a/vendor/nojimage/twitter-text-php/build/build-emoji-regex.php b/vendor/nojimage/twitter-text-php/build/build-emoji-regex.php
new file mode 100644
index 0000000000000..67ce3c88cc8bf
--- /dev/null
+++ b/vendor/nojimage/twitter-text-php/build/build-emoji-regex.php
@@ -0,0 +1,47 @@
+
+
+/**
+ * @author Takashi Nojima
+ * @copyright Copyright = date('Y') ?>, Takashi Nojima
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
+ * @package Twitter.Text
+ */
+
+namespace Twitter\Text;
+
+/**
+ * TLD Lists
+ */
+final class TldLists
+{
+ /**
+ * gTLDs
+ *
+ * @var array
+ */
+ private static $gTLDs = array(
+
+ '= $tld ?>',
+
+ );
+
+ /**
+ * gTLDs
+ *
+ * @var array
+ */
+ private static $ccTLDs = array(
+
+ '= $tld ?>',
+
+ );
+
+ /**
+ * get valid gTLD regexp
+ *
+ * @staticvar string $regex
+ * @return string
+ */
+ final public static function getValidGTLD()
+ {
+ static $regex;
+
+ if (!empty($regex)) {
+ return $regex;
+ }
+
+ $gTLD = implode('|', static::$gTLDs);
+ $regex = '(?:(?:' . $gTLD . ')(?=[^0-9a-z@]|$))';
+
+ return $regex;
+ }
+
+ /**
+ * get valid ccTLD regexp
+ *
+ * @staticvar string $regex
+ * @return string
+ */
+ final public static function getValidCcTLD()
+ {
+ static $regex;
+
+ if (!empty($regex)) {
+ return $regex;
+ }
+
+ $ccTLD = implode('|', static::$ccTLDs);
+ $regex = '(?:(?:' . $ccTLD . ')(?=[^0-9a-z@]|$))';
+
+ return $regex;
+ }
+}
+=5.3.3",
+ "ext-mbstring": "*",
+ "ext-intl": "*"
+ },
+ "require-dev": {
+ "ext-json": "*",
+ "symfony/yaml": "^2.6.0|^3.4.0|^4.4.0|^5.0.0",
+ "phpunit/phpunit": "4.8.*|5.7.*|6.5.*",
+ "twitter/twitter-text": "^3.0.0"
+ },
+ "autoload": {
+ "psr-0": {
+ "Twitter\\Text\\": "lib/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Twitter\\Text\\TestCase\\": "tests/TestCase/"
+ }
+ },
+ "scripts": {
+ "check": [
+ "@cs",
+ "@test",
+ "@tld-test"
+ ],
+ "test": "phpunit --exclude-group deprecated,tld",
+ "deprecated-test": "phpunit --group deprecated",
+ "tld-test": "phpunit --group tld",
+ "cs": "phpcs -psn --standard=PSR2 --extensions=php ./lib ./tests",
+ "cs-fixer": "phpcbf --standard=PSR2 ./lib ./tests"
+ }
+}
diff --git a/vendor/nojimage/twitter-text-php/lib/Twitter/Text/Autolink.php b/vendor/nojimage/twitter-text-php/lib/Twitter/Text/Autolink.php
new file mode 100644
index 0000000000000..f77d99a349e96
--- /dev/null
+++ b/vendor/nojimage/twitter-text-php/lib/Twitter/Text/Autolink.php
@@ -0,0 +1,934 @@
+
+ * @author Nick Pope
+ * @copyright Copyright © 2010, Mike Cochrane, Nick Pope
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
+ * @package Twitter.Text
+ */
+
+namespace Twitter\Text;
+
+/**
+ * Twitter Autolink Class
+ *
+ * Parses tweets and generates HTML anchor tags around URLs, usernames,
+ * username/list pairs and hashtags.
+ *
+ * Originally written by {@link http://github.com/mikenz Mike Cochrane}, this
+ * is based on code by {@link http://github.com/mzsanford Matt Sanford} and
+ * heavily modified by {@link http://github.com/ngnpope Nick Pope}.
+ *
+ * @author Mike Cochrane
+ * @author Nick Pope
+ * @copyright Copyright © 2010, Mike Cochrane, Nick Pope
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
+ * @package Twitter.Text
+ */
+class Autolink
+{
+
+ /**
+ * CSS class for auto-linked URLs.
+ *
+ * @var string
+ */
+ protected $class_url = '';
+
+ /**
+ * CSS class for auto-linked username URLs.
+ *
+ * @var string
+ */
+ protected $class_user = 'tweet-url username';
+
+ /**
+ * CSS class for auto-linked list URLs.
+ *
+ * @var string
+ */
+ protected $class_list = 'tweet-url list-slug';
+
+ /**
+ * CSS class for auto-linked hashtag URLs.
+ *
+ * @var string
+ */
+ protected $class_hash = 'tweet-url hashtag';
+
+ /**
+ * CSS class for auto-linked cashtag URLs.
+ *
+ * @var string
+ */
+ protected $class_cash = 'tweet-url cashtag';
+
+ /**
+ * URL base for username links (the username without the @ will be appended).
+ *
+ * @var string
+ */
+ protected $url_base_user = 'https://twitter.com/';
+
+ /**
+ * URL base for list links (the username/list without the @ will be appended).
+ *
+ * @var string
+ */
+ protected $url_base_list = 'https://twitter.com/';
+
+ /**
+ * URL base for hashtag links (the hashtag without the # will be appended).
+ *
+ * @var string
+ */
+ protected $url_base_hash = 'https://twitter.com/search?q=%23';
+
+ /**
+ * URL base for cashtag links (the hashtag without the $ will be appended).
+ *
+ * @var string
+ */
+ protected $url_base_cash = 'https://twitter.com/search?q=%24';
+
+ /**
+ * the 'rel' attribute values.
+ *
+ * @var array
+ */
+ protected $rel = array('external', 'nofollow');
+
+ /**
+ * The scope to open the link in.
+ *
+ * Support for the 'target' attribute was deprecated in HTML 4.01 but has
+ * since been reinstated in HTML 5. To output the 'target' attribute you
+ * must disable the adding of the string 'external' to the 'rel' attribute.
+ *
+ * @var string
+ */
+ protected $target = '_blank';
+
+ /**
+ * attribute for invisible span tag
+ *
+ * @var string
+ */
+ protected $invisibleTagAttrs = "style='position:absolute;left:-9999px;'";
+
+ /**
+ * If the at mark '@' should be included in the link (false by default)
+ *
+ * @var bool
+ * @since 3.0.1
+ */
+ protected $usernameIncludeSymbol = false;
+
+ /**
+ * HTML tag to be applied around #/@/# symbols in hashtags/usernames/lists/cashtag
+ *
+ * @var string
+ * @since 3.0.1
+ */
+ protected $symbolTag = '';
+
+ /**
+ * HTML tag to be applied around text part of hashtags/usernames/lists/cashtag
+ *
+ * @var string
+ * @since 3.0.1
+ */
+ protected $textWithSymbolTag = '';
+
+ /**
+ *
+ * @var Extractor
+ */
+ protected $extractor = null;
+
+ /**
+ * The tweet to be used in parsing.
+ *
+ * @var string
+ * @deprecated will be removed
+ */
+ protected $tweet = '';
+
+ /**
+ * Provides fluent method chaining.
+ *
+ * @param string $tweet [deprecated] The tweet to be converted.
+ * @param bool $full_encode [deprecated] Whether to encode all special characters.
+ *
+ * @see __construct()
+ *
+ * @return Autolink
+ */
+ public static function create($tweet = null, $full_encode = false)
+ {
+ return new static($tweet, $full_encode);
+ }
+
+ /**
+ * Reads in a tweet to be parsed and converted to contain links.
+ *
+ * As the intent is to produce links and output the modified tweet to the
+ * user, we take this opportunity to ensure that we escape user input.
+ *
+ * @see htmlspecialchars()
+ *
+ * @param string $tweet [deprecated] The tweet to be converted.
+ * @param bool $escape [deprecated] Whether to escape the tweet (default: true).
+ * @param bool $full_encode [deprecated] Whether to encode all special characters.
+ */
+ public function __construct($tweet = null, $escape = true, $full_encode = false)
+ {
+ if ($escape && !empty($tweet)) {
+ if ($full_encode) {
+ $this->tweet = htmlentities($tweet, ENT_QUOTES, 'UTF-8', false);
+ } else {
+ $this->tweet = htmlspecialchars($tweet, ENT_QUOTES, 'UTF-8', false);
+ }
+ } else {
+ $this->tweet = $tweet;
+ }
+
+ $this->extractor = Extractor::create();
+ }
+
+ /**
+ * Set CSS class to all link types.
+ *
+ * @param string $v CSS class for links.
+ *
+ * @return Autolink Fluid method chaining.
+ */
+ public function setToAllLinkClasses($v)
+ {
+ $this->setURLClass($v);
+ $this->setUsernameClass($v);
+ $this->setListClass($v);
+ $this->setHashtagClass($v);
+ $this->setCashtagClass($v);
+
+ return $this;
+ }
+
+ /**
+ * CSS class for auto-linked URLs.
+ *
+ * @return string CSS class for URL links.
+ */
+ public function getURLClass()
+ {
+ return $this->class_url;
+ }
+
+ /**
+ * CSS class for auto-linked URLs.
+ *
+ * @param string $v CSS class for URL links.
+ *
+ * @return Autolink Fluid method chaining.
+ */
+ public function setURLClass($v)
+ {
+ $this->class_url = trim($v);
+ return $this;
+ }
+
+ /**
+ * CSS class for auto-linked username URLs.
+ *
+ * @return string CSS class for username links.
+ */
+ public function getUsernameClass()
+ {
+ return $this->class_user;
+ }
+
+ /**
+ * CSS class for auto-linked username URLs.
+ *
+ * @param string $v CSS class for username links.
+ *
+ * @return Autolink Fluid method chaining.
+ */
+ public function setUsernameClass($v)
+ {
+ $this->class_user = trim($v);
+ return $this;
+ }
+
+ /**
+ * CSS class for auto-linked username/list URLs.
+ *
+ * @return string CSS class for username/list links.
+ */
+ public function getListClass()
+ {
+ return $this->class_list;
+ }
+
+ /**
+ * CSS class for auto-linked username/list URLs.
+ *
+ * @param string $v CSS class for username/list links.
+ *
+ * @return Autolink Fluid method chaining.
+ */
+ public function setListClass($v)
+ {
+ $this->class_list = trim($v);
+ return $this;
+ }
+
+ /**
+ * CSS class for auto-linked hashtag URLs.
+ *
+ * @return string CSS class for hashtag links.
+ */
+ public function getHashtagClass()
+ {
+ return $this->class_hash;
+ }
+
+ /**
+ * CSS class for auto-linked hashtag URLs.
+ *
+ * @param string $v CSS class for hashtag links.
+ *
+ * @return Autolink Fluid method chaining.
+ */
+ public function setHashtagClass($v)
+ {
+ $this->class_hash = trim($v);
+ return $this;
+ }
+
+ /**
+ * CSS class for auto-linked cashtag URLs.
+ *
+ * @return string CSS class for cashtag links.
+ */
+ public function getCashtagClass()
+ {
+ return $this->class_cash;
+ }
+
+ /**
+ * CSS class for auto-linked cashtag URLs.
+ *
+ * @param string $v CSS class for cashtag links.
+ *
+ * @return Autolink Fluid method chaining.
+ */
+ public function setCashtagClass($v)
+ {
+ $this->class_cash = trim($v);
+ return $this;
+ }
+
+ /**
+ * Whether to include the value 'nofollow' in the 'rel' attribute.
+ *
+ * @return bool Whether to add 'nofollow' to the 'rel' attribute.
+ */
+ public function getNoFollow()
+ {
+ return in_array('nofollow', $this->rel, true);
+ }
+
+ /**
+ * Whether to include the value 'nofollow' in the 'rel' attribute.
+ *
+ * @param bool $v The value to add to the 'target' attribute.
+ *
+ * @return Autolink Fluid method chaining.
+ */
+ public function setNoFollow($v)
+ {
+ if ($v && !$this->getNoFollow()) {
+ $this->setRel('nofollow', true);
+ }
+ if (!$v && $this->getNoFollow()) {
+ $this->rel = array_filter($this->rel, function ($r) {
+ return $r !== 'nofollow';
+ });
+ }
+
+ return $this;
+ }
+
+ /**
+ * Whether to include the value 'external' in the 'rel' attribute.
+ *
+ * Often this is used to be matched on in JavaScript for dynamically adding
+ * the 'target' attribute which is deprecated in HTML 4.01. In HTML 5 it has
+ * been undeprecated and thus the 'target' attribute can be used. If this is
+ * set to false then the 'target' attribute will be output.
+ *
+ * @return bool Whether to add 'external' to the 'rel' attribute.
+ */
+ public function getExternal()
+ {
+ return in_array('external', $this->rel, true);
+ }
+
+ /**
+ * Whether to include the value 'external' in the 'rel' attribute.
+ *
+ * Often this is used to be matched on in JavaScript for dynamically adding
+ * the 'target' attribute which is deprecated in HTML 4.01. In HTML 5 it has
+ * been undeprecated and thus the 'target' attribute can be used. If this is
+ * set to false then the 'target' attribute will be output.
+ *
+ * @param bool $v The value to add to the 'target' attribute.
+ *
+ * @return Autolink Fluid method chaining.
+ */
+ public function setExternal($v)
+ {
+ if ($v && !$this->getExternal()) {
+ $this->setRel('external', true);
+ }
+ if (!$v && $this->getExternal()) {
+ $this->rel = array_filter($this->rel, function ($r) {
+ return $r !== 'external';
+ });
+ }
+
+ return $this;
+ }
+
+ /**
+ * The scope to open the link in.
+ *
+ * Support for the 'target' attribute was deprecated in HTML 4.01 but has
+ * since been reinstated in HTML 5. To output the 'target' attribute you
+ * must disable the adding of the string 'external' to the 'rel' attribute.
+ *
+ * @return string The value to add to the 'target' attribute.
+ */
+ public function getTarget()
+ {
+ return $this->target;
+ }
+
+ /**
+ * The scope to open the link in.
+ *
+ * Support for the 'target' attribute was deprecated in HTML 4.01 but has
+ * since been reinstated in HTML 5. To output the 'target' attribute you
+ * must disable the adding of the string 'external' to the 'rel' attribute.
+ *
+ * @param string $v The value to add to the 'target' attribute.
+ *
+ * @return Autolink Fluid method chaining.
+ */
+ public function setTarget($v)
+ {
+ $this->target = trim($v);
+ return $this;
+ }
+
+ /**
+ * @return bool
+ * @since 3.0.1
+ */
+ public function isUsernameIncludeSymbol()
+ {
+ return $this->usernameIncludeSymbol;
+ }
+
+ /**
+ * Set if the at mark '@' should be included in the link (false by default)
+ *
+ * @param bool $usernameIncludeSymbol if username includes symbol
+ * @return Autolink
+ * @since 3.0.1
+ */
+ public function setUsernameIncludeSymbol($usernameIncludeSymbol)
+ {
+ $this->usernameIncludeSymbol = $usernameIncludeSymbol;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ * @since 3.0.1
+ */
+ public function getSymbolTag()
+ {
+ return $this->symbolTag;
+ }
+
+ /**
+ * Set HTML tag to be applied around #/@/# symbols in hashtags/usernames/lists/cashtag
+ *
+ * @param string $symbolTag HTML tag without bracket. e.g., 'b' or 's'
+ * @return Autolink
+ * @since 3.0.1
+ */
+ public function setSymbolTag($symbolTag)
+ {
+ $this->symbolTag = $symbolTag;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ * @since 3.0.1
+ */
+ public function getTextWithSymbolTag()
+ {
+ return $this->textWithSymbolTag;
+ }
+
+ /**
+ * Set HTML tag to be applied around text part of hashtags/usernames/lists/cashtag
+ *
+ * @param string $textWithSymbolTag HTML tag without bracket. e.g., 'b' or 's'
+ * @return Autolink
+ * @since 3.0.1
+ */
+ public function setTextWithSymbolTag($textWithSymbolTag)
+ {
+ $this->textWithSymbolTag = $textWithSymbolTag;
+
+ return $this;
+ }
+
+ /**
+ * Autolink with entities
+ *
+ * @param string $tweet
+ * @param array $entities
+ * @return string
+ * @since 1.1.0
+ */
+ public function autoLinkEntities($tweet = null, $entities = null)
+ {
+ if ($tweet === null) {
+ $tweet = $this->tweet;
+ }
+
+ $text = '';
+ $beginIndex = 0;
+ foreach ($entities as $entity) {
+ $text .= StringUtils::substr($tweet, $beginIndex, $entity['indices'][0] - $beginIndex);
+
+ if (isset($entity['url'])) {
+ $text .= $this->linkToUrl($entity);
+ } elseif (isset($entity['hashtag'])) {
+ $text .= $this->linkToHashtag($entity, $tweet);
+ } elseif (isset($entity['screen_name'])) {
+ $text .= $this->linkToMentionAndList($entity, $tweet);
+ } elseif (isset($entity['cashtag'])) {
+ $text .= $this->linkToCashtag($entity, $tweet);
+ }
+ $beginIndex = $entity['indices'][1];
+ }
+ $text .= StringUtils::substr($tweet, $beginIndex, StringUtils::strlen($tweet));
+ return $text;
+ }
+
+ /**
+ * Auto-link hashtags, URLs, usernames and lists, with JSON entities.
+ *
+ * @param string The tweet to be converted
+ * @param mixed The entities info
+ * @return string that auto-link HTML added
+ * @since 1.1.0
+ */
+ public function autoLinkWithJson($tweet = null, $json = null)
+ {
+ // concatenate entities
+ $entities = array();
+ if (is_object($json)) {
+ $json = $this->object2array($json);
+ }
+ if (is_array($json)) {
+ $entities = call_user_func_array('array_merge', $json);
+ }
+
+ // map JSON entity to twitter-text entity
+ foreach ($entities as $idx => $entity) {
+ if (!empty($entity['text'])) {
+ $entities[$idx]['hashtag'] = $entity['text'];
+ }
+ }
+
+ $entities = $this->extractor->removeOverlappingEntities($entities);
+ return $this->autoLinkEntities($tweet, $entities);
+ }
+
+ /**
+ * convert Object to Array
+ *
+ * @param mixed $obj
+ * @return array
+ */
+ protected function object2array($obj)
+ {
+ $array = (array) $obj;
+ foreach ($array as $key => $var) {
+ if (is_object($var) || is_array($var)) {
+ $array[$key] = $this->object2array($var);
+ }
+ }
+ return $array;
+ }
+
+ /**
+ * Auto-link hashtags, URLs, usernames and lists.
+ *
+ * @param string The tweet to be converted
+ * @return string that auto-link HTML added
+ * @since 1.1.0
+ */
+ public function autoLink($tweet = null)
+ {
+ if ($tweet === null) {
+ $tweet = $this->tweet;
+ }
+ $entities = $this->extractor->extractURLWithoutProtocol(false)->extractEntitiesWithIndices($tweet);
+ return $this->autoLinkEntities($tweet, $entities);
+ }
+
+ /**
+ * Auto-link the @username and @username/list references in the provided text. Links to @username references will
+ * have the usernameClass CSS classes added. Links to @username/list references will have the listClass CSS class
+ * added.
+ *
+ * @return string that auto-link HTML added
+ * @since 1.1.0
+ */
+ public function autoLinkUsernamesAndLists($tweet = null)
+ {
+ if ($tweet === null) {
+ $tweet = $this->tweet;
+ }
+ $entities = $this->extractor->extractMentionsOrListsWithIndices($tweet);
+ return $this->autoLinkEntities($tweet, $entities);
+ }
+
+ /**
+ * Auto-link #hashtag references in the provided Tweet text. The #hashtag links will have the hashtagClass CSS class
+ * added.
+ *
+ * @return string that auto-link HTML added
+ * @since 1.1.0
+ */
+ public function autoLinkHashtags($tweet = null)
+ {
+ if ($tweet === null) {
+ $tweet = $this->tweet;
+ }
+ $entities = $this->extractor->extractHashtagsWithIndices($tweet);
+ return $this->autoLinkEntities($tweet, $entities);
+ }
+
+ /**
+ * Auto-link URLs in the Tweet text provided.
+ *
+ * This only auto-links URLs with protocol.
+ *
+ * @return string that auto-link HTML added
+ * @since 1.1.0
+ */
+ public function autoLinkURLs($tweet = null)
+ {
+ if ($tweet === null) {
+ $tweet = $this->tweet;
+ }
+ $entities = $this->extractor->extractURLWithoutProtocol(false)->extractURLsWithIndices($tweet);
+ return $this->autoLinkEntities($tweet, $entities);
+ }
+
+ /**
+ * Auto-link $cashtag references in the provided Tweet text. The $cashtag links will have the cashtagClass CSS class
+ * added.
+ *
+ * @return string that auto-link HTML added
+ * @since 1.1.0
+ */
+ public function autoLinkCashtags($tweet = null)
+ {
+ if ($tweet === null) {
+ $tweet = $this->tweet;
+ }
+ $entities = $this->extractor->extractCashtagsWithIndices($tweet);
+ return $this->autoLinkEntities($tweet, $entities);
+ }
+
+ public function linkToUrl($entity)
+ {
+ if (!empty($this->class_url)) {
+ $attributes['class'] = $this->class_url;
+ }
+ $attributes['href'] = $entity['url'];
+ $linkText = $this->escapeHTML($entity['url']);
+
+ if (!empty($entity['display_url']) && !empty($entity['expanded_url'])) {
+ // Goal: If a user copies and pastes a tweet containing t.co'ed link, the resulting paste
+ // should contain the full original URL (expanded_url), not the display URL.
+ //
+ // Method: Whenever possible, we actually emit HTML that contains expanded_url, and use
+ // font-size:0 to hide those parts that should not be displayed (because they are not part of display_url).
+ // Elements with font-size:0 get copied even though they are not visible.
+ // Note that display:none doesn't work here. Elements with display:none don't get copied.
+ //
+ // Additionally, we want to *display* ellipses, but we don't want them copied. To make this happen we
+ // wrap the ellipses in a tco-ellipsis class and provide an onCopy handler that sets display:none on
+ // everything with the tco-ellipsis class.
+ //
+ // As an example: The user tweets "hi http://longdomainname.com/foo"
+ // This gets shortened to "hi http://t.co/xyzabc", with display_url = "…nname.com/foo"
+ // This will get rendered as:
+ //
+ // …
+ //
+ // http://longdomai
+ //
+ //
+ // nname.com/foo
+ //
+ //
+ //
+ // …
+ //
+ //
+ // Exception: pic.twitter.com images, for which
+ // expandedUrl = "https://twitter.com/#!/username/status/1234/photo/1
+ // For those URLs, display_url is not a substring of expanded_url, so we don't do anything
+ //special to render the elided parts.
+ // For a pic.twitter.com URL, the only elided part will be the "https://", so this is fine.
+ $displayURL = $entity['display_url'];
+ $expandedURL = $entity['expanded_url'];
+ $displayURLSansEllipses = preg_replace('/…/u', '', $displayURL);
+ $diplayURLIndexInExpandedURL = mb_strpos($expandedURL, $displayURLSansEllipses);
+
+ if ($diplayURLIndexInExpandedURL !== false) {
+ $beforeDisplayURL = mb_substr($expandedURL, 0, $diplayURLIndexInExpandedURL);
+ $afterDisplayURL = mb_substr(
+ $expandedURL,
+ $diplayURLIndexInExpandedURL + mb_strlen($displayURLSansEllipses)
+ );
+ $precedingEllipsis = (preg_match('/\A…/u', $displayURL)) ? '…' : '';
+ $followingEllipsis = (preg_match('/…\z/u', $displayURL)) ? '…' : '';
+
+ $invisibleSpan = "invisibleTagAttrs}>";
+
+ $linkText = "{$precedingEllipsis}{$invisibleSpan} ";
+ $linkText .= "{$invisibleSpan}{$this->escapeHTML($beforeDisplayURL)}";
+ $linkText .= "{$this->escapeHTML($displayURLSansEllipses)} ";
+ $linkText .= "{$invisibleSpan}{$this->escapeHTML($afterDisplayURL)}";
+ $linkText .= "{$invisibleSpan} {$followingEllipsis}";
+ } else {
+ $linkText = $entity['display_url'];
+ }
+ $attributes['title'] = $entity['expanded_url'];
+ } elseif (!empty($entity['display_url'])) {
+ $linkText = $entity['display_url'];
+ }
+
+ return $this->linkToText($entity, $linkText, $attributes);
+ }
+
+ /**
+ *
+ * @param array $entity
+ * @param string $tweet
+ * @return string
+ * @since 1.1.0
+ */
+ public function linkToHashtag($entity, $tweet = null)
+ {
+ if ($tweet === null) {
+ $tweet = $this->tweet;
+ }
+
+ $attributes = array();
+ $class = array();
+ $hash = StringUtils::substr($tweet, $entity['indices'][0], 1);
+ $linkText = $entity['hashtag'];
+
+ $attributes['href'] = $this->url_base_hash . $entity['hashtag'];
+ $attributes['title'] = '#' . $entity['hashtag'];
+ if (!empty($this->class_hash)) {
+ $class[] = $this->class_hash;
+ }
+ if (preg_match(Regex::getRtlCharsMatcher(), $linkText)) {
+ $class[] = 'rtl';
+ }
+ if (!empty($class)) {
+ $attributes['class'] = implode(' ', $class);
+ }
+
+ return $this->linkToTextWithSymbol($entity, $hash, $linkText, $attributes);
+ }
+
+ /**
+ *
+ * @param array $entity
+ * @param string $tweet
+ * @return string
+ * @since 1.1.0
+ */
+ public function linkToMentionAndList($entity, $tweet)
+ {
+ $attributes = array();
+ $symbol = StringUtils::substr($tweet, $entity['indices'][0], 1);
+
+ if (!empty($entity['list_slug'])) {
+ # Replace the list and username
+ $linkText = $entity['screen_name'] . $entity['list_slug'];
+ $class = $this->class_list;
+ $url = $this->url_base_list . $linkText;
+ } else {
+ # Replace the username
+ $linkText = $entity['screen_name'];
+ $class = $this->class_user;
+ $url = $this->url_base_user . $linkText;
+ }
+ if (!empty($class)) {
+ $attributes['class'] = $class;
+ }
+ $attributes['href'] = $url;
+
+ return $this->linkToTextWithSymbol($entity, $symbol, $linkText, $attributes);
+ }
+
+ /**
+ *
+ * @param array $entity
+ * @param string $tweet
+ * @return string
+ * @since 1.1.0
+ */
+ public function linkToCashtag($entity, $tweet = null)
+ {
+ if ($tweet === null) {
+ $tweet = $this->tweet;
+ }
+ $attributes = array();
+ $dollar = StringUtils::substr($tweet, $entity['indices'][0], 1);
+ $linkText = $entity['cashtag'];
+ $attributes['href'] = $this->url_base_cash . $entity['cashtag'];
+ $attributes['title'] = '$' . $linkText;
+ if (!empty($this->class_cash)) {
+ $attributes['class'] = $this->class_cash;
+ }
+
+ return $this->linkToTextWithSymbol($entity, $dollar, $linkText, $attributes);
+ }
+
+ /**
+ *
+ * @param array $entity
+ * @param string $text
+ * @param array $attributes
+ * @return string
+ * @since 1.1.0
+ */
+ public function linkToText(array $entity, $text, $attributes = array())
+ {
+ $rel = $this->getRel();
+ if ($rel !== '') {
+ $attributes['rel'] = $rel;
+ }
+ if ($this->target) {
+ $attributes['target'] = $this->target;
+ }
+ $link = ' $val) {
+ $link .= ' ' . $key . '="' . $this->escapeHTML($val) . '"';
+ }
+ $link .= '>' . $text . ' ';
+ return $link;
+ }
+
+ /**
+ *
+ * @param array $entity
+ * @param string $symbol
+ * @param string $linkText
+ * @param array $attributes
+ * @return string
+ * @since 3.0.1
+ */
+ protected function linkToTextWithSymbol(array $entity, $symbol, $linkText, array $attributes)
+ {
+ $includeSymbol = $this->usernameIncludeSymbol || !preg_match('/[@@]/u', $symbol);
+
+ if (!empty($this->symbolTag)) {
+ $symbol = sprintf('<%1$s>%2$s%1$s>', $this->symbolTag, $symbol);
+ }
+ if (!empty($this->textWithSymbolTag)) {
+ $linkText = sprintf('<%1$s>%2$s%1$s>', $this->textWithSymbolTag, $linkText);
+ }
+
+ if (!$includeSymbol) {
+ return $symbol . $this->linkToText($entity, $linkText, $attributes);
+ }
+
+ $linkText = $symbol . $linkText;
+
+ return $this->linkToText($entity, $linkText, $attributes);
+ }
+
+ /**
+ * get rel attribute
+ *
+ * @return string
+ */
+ public function getRel()
+ {
+ $rel = $this->rel;
+ $rel = array_unique($rel);
+
+ return implode(' ', $rel);
+ }
+
+ /**
+ * Set rel attribute.
+ *
+ * This method override setExternal/setNoFollow setting.
+ *
+ * @param string[]|string $rel the rel attribute
+ * @param bool $merge if true, merge rel attributes instead replace.
+ * @return $this
+ */
+ public function setRel($rel, $merge = false)
+ {
+ if (is_string($rel)) {
+ $rel = explode(' ', $rel);
+ }
+
+ $this->rel = $merge ? array_unique(array_merge($this->rel, $rel)) : $rel;
+
+ return $this;
+ }
+
+ /**
+ * html escape
+ *
+ * @param string $text
+ * @return string
+ */
+ protected function escapeHTML($text)
+ {
+ return htmlspecialchars($text, ENT_QUOTES, 'UTF-8', false);
+ }
+}
diff --git a/vendor/nojimage/twitter-text-php/lib/Twitter/Text/Configuration.php b/vendor/nojimage/twitter-text-php/lib/Twitter/Text/Configuration.php
new file mode 100644
index 0000000000000..d1073026b9976
--- /dev/null
+++ b/vendor/nojimage/twitter-text-php/lib/Twitter/Text/Configuration.php
@@ -0,0 +1,215 @@
+ 3,
+ 'maxWeightedTweetLength' => 280,
+ 'scale' => 100,
+ 'defaultWeight' => 200,
+ 'emojiParsingEnabled' => true,
+ 'transformedURLLength' => 23,
+ 'ranges' => array(
+ array(
+ 'start' => 0,
+ 'end' => 4351,
+ 'weight' => 100,
+ ),
+ array(
+ 'start' => 8192,
+ 'end' => 8205,
+ 'weight' => 100,
+ ),
+ array(
+ 'start' => 8208,
+ 'end' => 8223,
+ 'weight' => 100,
+ ),
+ array(
+ 'start' => 8242,
+ 'end' => 8247,
+ 'weight' => 100,
+ ),
+ ),
+ );
+
+ /**
+ * configuration from v2.json
+ *
+ * @var array
+ */
+ private static $v2Config = array(
+ 'version' => 2,
+ 'maxWeightedTweetLength' => 280,
+ 'scale' => 100,
+ 'defaultWeight' => 200,
+ 'transformedURLLength' => 23,
+ 'ranges' => array(
+ array(
+ 'start' => 0,
+ 'end' => 4351,
+ 'weight' => 100,
+ ),
+ array(
+ 'start' => 8192,
+ 'end' => 8205,
+ 'weight' => 100,
+ ),
+ array(
+ 'start' => 8208,
+ 'end' => 8223,
+ 'weight' => 100,
+ ),
+ array(
+ 'start' => 8242,
+ 'end' => 8247,
+ 'weight' => 100,
+ ),
+ ),
+ );
+
+ /**
+ * configuration from v1.json
+ *
+ * @var array
+ */
+ private static $v1Config = array(
+ 'version' => 1,
+ 'maxWeightedTweetLength' => 140,
+ 'scale' => 1,
+ 'defaultWeight' => 1,
+ 'transformedURLLength' => 23,
+ 'ranges' => array(),
+ );
+
+ /**
+ * @var array
+ */
+ private $config;
+
+ /**
+ * construct
+ *
+ * @param array $config
+ */
+ public function __construct(array $config = null)
+ {
+ if ($config === null) {
+ $config = static::$v3Config;
+ }
+
+ $this->config = $config;
+ }
+
+ /**
+ * property accessor
+ *
+ * @param string $name
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ return isset($this->config[$name]) ? $this->config[$name] : null;
+ }
+
+ /**
+ * convert to array
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return $this->config;
+ }
+
+ /**
+ * Create configuration from json string
+ *
+ * @param string $json as configuration
+ * @return Configuration
+ */
+ public static function fromJson($json)
+ {
+ return new Configuration(json_decode($json, true));
+ }
+
+ /**
+ * Get twitter-text 1.x configuration
+ *
+ * @return Configuration
+ */
+ public static function v1()
+ {
+ return new self(static::$v1Config);
+ }
+
+ /**
+ * Get twitter-text 2.x configuration
+ *
+ * @return Configuration
+ */
+ public static function v2()
+ {
+ return new self(static::$v2Config);
+ }
+
+ /**
+ * maxWeightedTweetLength * scale
+ *
+ * @return int
+ */
+ public function getScaledMaxWeightedTweetLength()
+ {
+ return $this->maxWeightedTweetLength * $this->scale;
+ }
+
+ /**
+ * transformedURLLength * scale
+ *
+ * @return int
+ */
+ public function getScaledTransformedURLLength()
+ {
+ return $this->transformedURLLength * $this->scale;
+ }
+
+ /**
+ * Get whether emoji parsing is enabled.
+ *
+ * @return bool `true` if emoji parsing is enabled, otherwise `false`.
+ */
+ public function getEmojiParsingEnabled()
+ {
+ return (bool)$this->emojiParsingEnabled;
+ }
+}
diff --git a/vendor/nojimage/twitter-text-php/lib/Twitter/Text/EmojiRegex.php b/vendor/nojimage/twitter-text-php/lib/Twitter/Text/EmojiRegex.php
new file mode 100644
index 0000000000000..95ae4c4667b86
--- /dev/null
+++ b/vendor/nojimage/twitter-text-php/lib/Twitter/Text/EmojiRegex.php
@@ -0,0 +1,29 @@
+
+ * @author Nick Pope
+ * @copyright Copyright © 2010, Mike Cochrane, Nick Pope
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
+ * @package Twitter.Text
+ */
+
+namespace Twitter\Text;
+
+/**
+ * Twitter Extractor Class
+ *
+ * Parses tweets and extracts URLs, usernames, username/list pairs and
+ * hashtags.
+ *
+ * Originally written by {@link http://github.com/mikenz Mike Cochrane}, this
+ * is based on code by {@link http://github.com/mzsanford Matt Sanford} and
+ * heavily modified by {@link http://github.com/ngnpope Nick Pope}.
+ *
+ * @author Mike Cochrane
+ * @author Nick Pope
+ * @copyright Copyright © 2010, Mike Cochrane, Nick Pope
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
+ * @package Twitter.Text
+ */
+class Extractor
+{
+
+ /**
+ * The maximum url length that the Twitter backend supports.
+ */
+ const MAX_URL_LENGTH = 4096;
+
+ /**
+ * The backend adds http:// for normal links and https to *.twitter.com URLs (it also rewrites http to https for
+ * URLs matching *.twitter.com). We're better off adding https:// all the time.
+ * By making the assumption that URL_GROUP_PROTOCOL_LENGTH is https, the trade off is we'll disallow a http URL
+ * that is 4096 characters.
+ */
+ const URL_GROUP_PROTOCOL_LENGTH = 4104; // https:// + MAX_URL_LENGTH
+
+ /**
+ * The maximum t.co path length that the Twitter backend supports.
+ */
+ const MAX_TCO_SLUG_LENGTH = 40;
+
+ /**
+ * The maximum hostname length that the ASCII domain.
+ */
+ const MAX_ASCII_HOSTNAME_LENGTH = 63;
+
+ /**
+ * @var boolean
+ */
+ protected $extractURLWithoutProtocol = true;
+
+ /**
+ * Provides fluent method chaining.
+ *
+ * @see __construct()
+ *
+ * @return Extractor
+ */
+ public static function create()
+ {
+ return new self();
+ }
+
+ /**
+ * Reads in a tweet to be parsed and extracts elements from it.
+ *
+ * Extracts various parts of a tweet including URLs, usernames, hashtags...
+ */
+ public function __construct()
+ {
+ }
+
+ /**
+ * Extracts all parts of a tweet and returns an associative array containing
+ * the extracted elements.
+ *
+ * @param string $tweet The tweet to extract.
+ * @return array The elements in the tweet.
+ */
+ public function extract($tweet)
+ {
+ return array(
+ 'hashtags' => $this->extractHashtags($tweet),
+ 'cashtags' => $this->extractCashtags($tweet),
+ 'urls' => $this->extractURLs($tweet),
+ 'mentions' => $this->extractMentionedScreennames($tweet),
+ 'replyto' => $this->extractReplyScreenname($tweet),
+ 'hashtags_with_indices' => $this->extractHashtagsWithIndices($tweet),
+ 'urls_with_indices' => $this->extractURLsWithIndices($tweet),
+ 'mentions_with_indices' => $this->extractMentionedScreennamesWithIndices($tweet),
+ );
+ }
+
+ /**
+ * Extract URLs, @mentions, lists and #hashtag from a given text/tweet.
+ *
+ * @param string $tweet The tweet to extract.
+ * @return array list of extracted entities
+ */
+ public function extractEntitiesWithIndices($tweet)
+ {
+ $entities = array();
+ $entities = array_merge($entities, $this->extractURLsWithIndices($tweet));
+ $entities = array_merge($entities, $this->extractHashtagsWithIndices($tweet, false));
+ $entities = array_merge($entities, $this->extractMentionsOrListsWithIndices($tweet));
+ $entities = array_merge($entities, $this->extractCashtagsWithIndices($tweet));
+ $entities = $this->removeOverlappingEntities($entities);
+ return $entities;
+ }
+
+ /**
+ * Extracts all the hashtags from the tweet.
+ *
+ * @param string $tweet The tweet to extract.
+ * @return array The hashtag elements in the tweet.
+ */
+ public function extractHashtags($tweet)
+ {
+ $hashtagsOnly = array();
+ $hashtagsWithIndices = $this->extractHashtagsWithIndices($tweet);
+
+ foreach ($hashtagsWithIndices as $hashtagWithIndex) {
+ $hashtagsOnly[] = $hashtagWithIndex['hashtag'];
+ }
+ return $hashtagsOnly;
+ }
+
+ /**
+ * Extracts all the cashtags from the tweet.
+ *
+ * @param string $tweet The tweet to extract.
+ * @return array The cashtag elements in the tweet.
+ */
+ public function extractCashtags($tweet)
+ {
+ $cashtagsOnly = array();
+ $cashtagsWithIndices = $this->extractCashtagsWithIndices($tweet);
+
+ foreach ($cashtagsWithIndices as $cashtagWithIndex) {
+ $cashtagsOnly[] = $cashtagWithIndex['cashtag'];
+ }
+ return $cashtagsOnly;
+ }
+
+ /**
+ * Extracts all the URLs from the tweet.
+ *
+ * @param string $tweet The tweet to extract.
+ * @return array The URL elements in the tweet.
+ */
+ public function extractURLs($tweet)
+ {
+ $urlsOnly = array();
+ $urlsWithIndices = $this->extractURLsWithIndices($tweet);
+
+ foreach ($urlsWithIndices as $urlWithIndex) {
+ $urlsOnly[] = $urlWithIndex['url'];
+ }
+ return $urlsOnly;
+ }
+
+ /**
+ * Extract all the usernames from the tweet.
+ *
+ * A mention is an occurrence of a username anywhere in a tweet.
+ *
+ * @param string $tweet The tweet to extract.
+ * @return array The usernames elements in the tweet.
+ */
+ public function extractMentionedScreennames($tweet)
+ {
+ $usernamesOnly = array();
+ $mentionsWithIndices = $this->extractMentionsOrListsWithIndices($tweet);
+
+ foreach ($mentionsWithIndices as $mentionWithIndex) {
+ if (empty($mentionWithIndex['screen_name'])) {
+ continue;
+ }
+ $usernamesOnly[] = $mentionWithIndex['screen_name'];
+ }
+ return $usernamesOnly;
+ }
+
+ /**
+ * Extract all the usernames replied to from the tweet.
+ *
+ * A reply is an occurrence of a username at the beginning of a tweet.
+ *
+ * @param string $tweet The tweet to extract.
+ * @return array The usernames replied to in a tweet.
+ */
+ public function extractReplyScreenname($tweet)
+ {
+ $matched = preg_match(Regex::getValidReplyMatcher(), $tweet, $matches);
+ # Check username ending in
+ if ($matched && preg_match(Regex::getEndMentionMatcher(), $matches[2])) {
+ $matched = false;
+ }
+ return $matched ? $matches[1] : null;
+ }
+
+ /**
+ * Extracts all the emoji and the indices they occur at from the tweet.
+ *
+ * @param string $tweet The tweet to extract.
+ * @return array The emoji chars in the tweet.
+ */
+ public function extractEmojiWithIndices($tweet)
+ {
+ preg_match_all(EmojiRegex::VALID_EMOJI_PATTERN, $tweet, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
+ $entities = array();
+
+ foreach ($matches as $match) {
+ list($emoji) = $match;
+ list($emojiChar, $offset) = $emoji;
+ $startPosition = StringUtils::strlen(substr($tweet, 0, $offset));
+ $endPosition = $startPosition + StringUtils::strlen($emojiChar) - 1;
+
+ $entities[] = array(
+ 'emoji' => $emoji[0],
+ 'indices' => array($startPosition, $endPosition)
+ );
+ }
+
+ return $entities;
+ }
+
+ /**
+ * Extracts all the hashtags and the indices they occur at from the tweet.
+ *
+ * @param string $tweet The tweet to extract.
+ * @param boolean $checkUrlOverlap if true, check if extracted hashtags overlap URLs and remove overlapping ones
+ * @return array The hashtag elements in the tweet.
+ */
+ public function extractHashtagsWithIndices($tweet, $checkUrlOverlap = true)
+ {
+ if (!preg_match('/[##]/u', $tweet)) {
+ return array();
+ }
+
+ preg_match_all(Regex::getValidHashtagMatcher(), $tweet, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
+ $tags = array();
+
+ foreach ($matches as $match) {
+ list($all, $before, $hash, $hashtag, $outer) = array_pad($match, 3, array('', 0));
+ $start_position = $hash[1] > 0 ? StringUtils::strlen(substr($tweet, 0, $hash[1])) : $hash[1];
+ $end_position = $start_position + StringUtils::strlen($hash[0] . $hashtag[0]);
+
+ if (preg_match(Regex::getEndHashtagMatcher(), $outer[0])) {
+ continue;
+ }
+
+ $tags[] = array(
+ 'hashtag' => $hashtag[0],
+ 'indices' => array($start_position, $end_position)
+ );
+ }
+
+ if (!$checkUrlOverlap) {
+ return $tags;
+ }
+
+ # check url overlap
+ $urls = $this->extractURLsWithIndices($tweet);
+ $entities = $this->removeOverlappingEntities(array_merge($tags, $urls));
+
+ $validTags = array();
+ foreach ($entities as $entity) {
+ if (empty($entity['hashtag'])) {
+ continue;
+ }
+ $validTags[] = $entity;
+ }
+
+ return $validTags;
+ }
+
+ /**
+ * Extracts all the cashtags and the indices they occur at from the tweet.
+ *
+ * @param string $tweet The tweet to extract.
+ * @return array The cashtag elements in the tweet.
+ */
+ public function extractCashtagsWithIndices($tweet)
+ {
+ if (!preg_match('/\$/u', $tweet)) {
+ return array();
+ }
+
+ preg_match_all(Regex::getValidCashtagMatcher(), $tweet, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
+ $tags = array();
+
+ foreach ($matches as $match) {
+ list($all, $before, $dollar, $cash_text, $outer) = array_pad($match, 3, array('', 0));
+ $start_position = $dollar[1] > 0 ? StringUtils::strlen(substr($tweet, 0, $dollar[1])) : $dollar[1];
+ $end_position = $start_position + StringUtils::strlen($dollar[0] . $cash_text[0]);
+
+ if (preg_match(Regex::getEndHashtagMatcher(), $outer[0])) {
+ continue;
+ }
+
+ $tags[] = array(
+ 'cashtag' => $cash_text[0],
+ 'indices' => array($start_position, $end_position)
+ );
+ }
+
+ return $tags;
+ }
+
+ /**
+ * Extracts all the URLs and the indices they occur at from the tweet.
+ *
+ * @param string $tweet The tweet to extract.
+ * @return array The URLs elements in the tweet.
+ */
+ public function extractURLsWithIndices($tweet)
+ {
+ $needle = $this->extractURLWithoutProtocol() ? '.' : ':';
+ if (strpos($tweet, $needle) === false) {
+ return array();
+ }
+
+ $urls = array();
+ preg_match_all(Regex::getValidUrlMatcher(), $tweet, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
+
+ foreach ($matches as $match) {
+ list($all, $before, $url, $protocol, $domain, $port, $path, $query) = array_pad($match, 8, array(''));
+ $start_position = $url[1] > 0 ? StringUtils::strlen(substr($tweet, 0, $url[1])) : $url[1];
+ $end_position = $start_position + StringUtils::strlen($url[0]);
+
+ $all = $all[0];
+ $before = $before[0];
+ $url = $url[0];
+ $protocol = $protocol[0];
+ $domain = $domain[0];
+ $port = $port[0];
+ $path = $path[0];
+ $query = $query[0];
+
+ // If protocol is missing and domain contains non-ASCII characters,
+ // extract ASCII-only domains.
+ if (empty($protocol)) {
+ if (
+ !$this->extractURLWithoutProtocol
+ || preg_match(Regex::getInvalidUrlWithoutProtocolPrecedingCharsMatcher(), $before)
+ ) {
+ continue;
+ }
+
+ $last_url = null;
+ $ascii_end_position = 0;
+
+ if (preg_match(Regex::getValidAsciiDomainMatcher(), $domain, $asciiDomain)) {
+ // check hostname length
+ if (
+ isset($asciiDomain[1])
+ && strlen(rtrim($asciiDomain[1], '.')) > static::MAX_ASCII_HOSTNAME_LENGTH
+ ) {
+ continue;
+ }
+
+ $asciiDomain[0] = preg_replace('/' . preg_quote($domain, '/') . '/u', $asciiDomain[0], $url);
+ $ascii_start_position = StringUtils::strpos($domain, $asciiDomain[0], $ascii_end_position);
+ $ascii_end_position = $ascii_start_position + StringUtils::strlen($asciiDomain[0]);
+ $last_url = array(
+ 'url' => $asciiDomain[0],
+ 'indices' => array(
+ $start_position + $ascii_start_position,
+ $start_position + $ascii_end_position
+ ),
+ );
+ if (
+ !empty($path)
+ || preg_match(Regex::getValidSpecialShortDomainMatcher(), $asciiDomain[0])
+ || !preg_match(Regex::getInvalidCharactersMatcher(), $asciiDomain[0])
+ ) {
+ $urls[] = $last_url;
+ }
+ }
+
+ // no ASCII-only domain found. Skip the entire URL
+ if (empty($last_url)) {
+ continue;
+ }
+
+ // $last_url only contains domain. Need to add path and query if they exist.
+ if (!empty($path)) {
+ // last_url was not added. Add it to urls here.
+ $last_url['url'] = preg_replace('/' . preg_quote($domain, '/') . '/u', $last_url['url'], $url);
+ $last_url['indices'][1] = $end_position;
+ }
+ } else {
+ // In the case of t.co URLs, don't allow additional path characters
+ if (preg_match(Regex::getValidTcoUrlMatcher(), $url, $tcoUrlMatches)) {
+ list($url, $tcoUrlSlug) = $tcoUrlMatches;
+ $end_position = $start_position + StringUtils::strlen($url);
+
+ // In the case of t.co URLs, don't allow additional path characters and
+ // ensure that the slug is under 40 chars.
+ if (strlen($tcoUrlSlug) > static::MAX_TCO_SLUG_LENGTH) {
+ continue;
+ }
+ }
+ if ($this->isValidHostAndLength(StringUtils::strlen($url), $protocol, $domain)) {
+ $urls[] = array(
+ 'url' => $url,
+ 'indices' => array($start_position, $end_position),
+ );
+ }
+ }
+ }
+
+ return $urls;
+ }
+
+ /**
+ * Verifies that the host name adheres to RFC 3490 and 1035
+ * Also, verifies that the entire url (including protocol) doesn't exceed MAX_URL_LENGTH
+ *
+ * @param int $originalUrlLength The length of the entire URL, including protocol if any
+ * @param string $protocol The protocol used
+ * @param string $host The hostname to check validity of
+ * @return bool true if the host is valid
+ */
+ public function isValidHostAndLength($originalUrlLength, $protocol, $host)
+ {
+ if (empty($host)) {
+ return false;
+ }
+
+ $originalHostLength = StringUtils::strlen($host);
+
+ // Use IDN for all host names, if the host is all ASCII, it returns unchanged.
+ // It comes with an added benefit of checking the host length to be between 1 to 63 characters.
+ $encodedHost = StringUtils::idnToAscii($host);
+ if ($encodedHost === false || empty($encodedHost)) {
+ return false;
+ }
+
+ $punycodeEncodedHostLength = StringUtils::strlen($encodedHost);
+ if ($punycodeEncodedHostLength === 0) {
+ return false;
+ }
+
+ // The punycodeEncoded host length might be different now, offset that length from the URL.
+ $encodedUrlLength = $originalUrlLength + $punycodeEncodedHostLength - $originalHostLength;
+ // Add the protocol to our length check, if there isn't one, to ensure it doesn't go over the limit.
+ $urlLengthWithProtocol = $encodedUrlLength + (empty($protocol) ? self::URL_GROUP_PROTOCOL_LENGTH : 0);
+
+ return $urlLengthWithProtocol <= self::MAX_URL_LENGTH;
+ }
+
+ /**
+ * Extracts all the usernames and the indices they occur at from the tweet.
+ *
+ * @param string $tweet The tweet to extract.
+ * @return array The username elements in the tweet.
+ */
+ public function extractMentionedScreennamesWithIndices($tweet)
+ {
+ $usernamesOnly = array();
+ $mentions = $this->extractMentionsOrListsWithIndices($tweet);
+ foreach ($mentions as $mention) {
+ if (isset($mention['list_slug'])) {
+ unset($mention['list_slug']);
+ }
+ $usernamesOnly[] = $mention;
+ }
+ return $usernamesOnly;
+ }
+
+ /**
+ * Extracts all the usernames and the indices they occur at from the tweet.
+ *
+ * @param string $tweet The tweet to extract.
+ * @return array The username elements in the tweet.
+ */
+ public function extractMentionsOrListsWithIndices($tweet)
+ {
+ if (!preg_match('/[@@]/u', $tweet)) {
+ return array();
+ }
+
+ preg_match_all(Regex::getValidMentionsOrListsMatcher(), $tweet, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);
+ $results = array();
+
+ foreach ($matches as $match) {
+ list($all, $before, $at, $username, $list_slug, $outer) = array_pad($match, 6, array('', 0));
+ $start_position = $at[1] > 0 ? StringUtils::strlen(substr($tweet, 0, $at[1])) : $at[1];
+ $end_position = $start_position + StringUtils::strlen($at[0]) + StringUtils::strlen($username[0]);
+ $entity = array(
+ 'screen_name' => $username[0],
+ 'list_slug' => $list_slug[0],
+ 'indices' => array($start_position, $end_position),
+ );
+
+ if (preg_match(Regex::getEndMentionMatcher(), $outer[0])) {
+ continue;
+ }
+
+ if (!empty($list_slug[0])) {
+ $entity['indices'][1] = $end_position + StringUtils::strlen($list_slug[0]);
+ }
+
+ $results[] = $entity;
+ }
+
+ return $results;
+ }
+
+ /**
+ * setter/getter for extractURLWithoutProtocol
+ *
+ * @param boolean $flag
+ * @return bool|Extractor
+ */
+ public function extractURLWithoutProtocol($flag = null)
+ {
+ if ($flag === null) {
+ return $this->extractURLWithoutProtocol;
+ }
+ $this->extractURLWithoutProtocol = (bool) $flag;
+ return $this;
+ }
+
+ /**
+ * Remove overlapping entities.
+ * This returns a new array with no overlapping entities.
+ *
+ * @param array $entities
+ * @return array
+ */
+ public function removeOverlappingEntities($entities)
+ {
+ $result = array();
+ usort($entities, array($this, 'sortEntities'));
+
+ $prev = null;
+ foreach ($entities as $entity) {
+ if ($prev !== null && $entity['indices'][0] < $prev['indices'][1]) {
+ continue;
+ }
+ $prev = $entity;
+ $result[] = $entity;
+ }
+ return $result;
+ }
+
+ /**
+ * sort by entity start index
+ *
+ * @param array $a
+ * @param array $b
+ * @return int
+ */
+ protected function sortEntities($a, $b)
+ {
+ if ($a['indices'][0] === $b['indices'][0]) {
+ return 0;
+ }
+ return ($a['indices'][0] < $b['indices'][0]) ? -1 : 1;
+ }
+}
diff --git a/vendor/nojimage/twitter-text-php/lib/Twitter/Text/HitHighlighter.php b/vendor/nojimage/twitter-text-php/lib/Twitter/Text/HitHighlighter.php
new file mode 100644
index 0000000000000..3008ad66f5f09
--- /dev/null
+++ b/vendor/nojimage/twitter-text-php/lib/Twitter/Text/HitHighlighter.php
@@ -0,0 +1,190 @@
+
+ * @copyright Copyright © 2010, Nick Pope
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
+ * @package Twitter.Text
+ */
+
+namespace Twitter\Text;
+
+/**
+ * Twitter HitHighlighter Class
+ *
+ * Performs "hit highlighting" on tweets that have been auto-linked already.
+ * Useful with the results returned from the search API.
+ *
+ * Originally written by {@link http://github.com/mikenz Mike Cochrane}, this
+ * is based on code by {@link http://github.com/mzsanford Matt Sanford} and
+ * heavily modified by {@link http://github.com/ngnpope Nick Pope}.
+ *
+ * @author Nick Pope
+ * @copyright Copyright © 2010, Nick Pope
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
+ * @package Twitter.Text
+ */
+class HitHighlighter
+{
+
+ /**
+ * The tag to surround hits with.
+ *
+ * @var string
+ */
+ protected $tag = 'em';
+
+ /**
+ * The tweet to be used in parsing.
+ *
+ * @var string
+ * @deprecated will be removed
+ */
+ protected $tweet = '';
+
+ /**
+ * Provides fluent method chaining.
+ *
+ * @param string $tweet [deprecated] The tweet to be hit highlighted.
+ * @param bool $full_encode [deprecated] Whether to encode all special characters.
+ *
+ * @see __construct()
+ *
+ * @return HitHighlighter
+ */
+ public static function create($tweet = null, $full_encode = false)
+ {
+ return new self($tweet, $full_encode);
+ }
+
+ /**
+ * Reads in a tweet to be parsed and hit highlighted.
+ *
+ * We take this opportunity to ensure that we escape user input.
+ *
+ * @see htmlspecialchars()
+ *
+ * @param string $tweet [deprecated] The tweet to be hit highlighted.
+ * @param bool $escape [deprecated] Whether to escape the tweet (default: true).
+ * @param bool $full_encode [deprecated] Whether to encode all special characters.
+ */
+ public function __construct($tweet = null, $escape = true, $full_encode = false)
+ {
+ if (!empty($tweet) && $escape) {
+ if ($full_encode) {
+ $this->tweet = htmlentities($tweet, ENT_QUOTES, 'UTF-8', false);
+ } else {
+ $this->tweet = htmlspecialchars($tweet, ENT_QUOTES, 'UTF-8', false);
+ }
+ } else {
+ $this->tweet = $tweet;
+ }
+ }
+
+ /**
+ * Set the highlighting tag to surround hits with. The default tag is 'em'.
+ *
+ * @return string The tag name.
+ */
+ public function getTag()
+ {
+ return $this->tag;
+ }
+
+ /**
+ * Set the highlighting tag to surround hits with. The default tag is 'em'.
+ *
+ * @param string $v The tag name.
+ *
+ * @return HitHighlighter Fluid method chaining.
+ */
+ public function setTag($v)
+ {
+ $this->tag = $v;
+ return $this;
+ }
+
+ /**
+ * Hit highlights the tweet.
+ *
+ * @param string $tweet The tweet to be hit highlighted.
+ * @param array $hits An array containing the start and end index pairs
+ * for the highlighting.
+ *
+ * @return string The hit highlighted tweet.
+ */
+ public function highlight($tweet = null, array $hits = null)
+ {
+ if ($tweet === null) {
+ $tweet = $this->tweet;
+ }
+ if (empty($hits)) {
+ return $tweet;
+ }
+ $highlightTweet = '';
+ $tags = array('<' . $this->tag . '>', '' . $this->tag . '>');
+ # Check whether we can simply replace or whether we need to chunk...
+ if (strpos($tweet, '<') === false) {
+ $ti = 0; // tag increment (for added tags)
+ $highlightTweet = $tweet;
+ foreach ($hits as $hit) {
+ $highlightTweet = StringUtils::substrReplace($highlightTweet, $tags[0], $hit[0] + $ti, 0);
+ $ti += StringUtils::strlen($tags[0]);
+ $highlightTweet = StringUtils::substrReplace($highlightTweet, $tags[1], $hit[1] + $ti, 0);
+ $ti += StringUtils::strlen($tags[1]);
+ }
+ } else {
+ $chunks = preg_split('/[<>]/iu', $tweet);
+ $chunk = $chunks[0];
+ $chunk_index = 0;
+ $chunk_cursor = 0;
+ $offset = 0;
+ $start_in_chunk = false;
+ # Flatten the multidimensional hits array:
+ $hits_flat = call_user_func_array('array_merge', $hits);
+ $hits_flat_count = count($hits_flat);
+ # Loop over the hit indices:
+ for ($index = 0; $index < $hits_flat_count; $index++) {
+ $hit = $hits_flat[$index];
+ $tag = $tags[$index % 2];
+ $placed = false;
+ while ($chunk !== null && $hit >= ($i = $offset + StringUtils::strlen($chunk))) {
+ $highlightTweet .= StringUtils::substr($chunk, $chunk_cursor);
+ if ($start_in_chunk && $hit === $i) {
+ $highlightTweet .= $tag;
+ $placed = true;
+ }
+ if (isset($chunks[$chunk_index + 1])) {
+ $highlightTweet .= '<' . $chunks[$chunk_index + 1] . '>';
+ }
+ $offset += StringUtils::strlen($chunk);
+ $chunk_cursor = 0;
+ $chunk_index += 2;
+ $chunk = (isset($chunks[$chunk_index]) ? $chunks[$chunk_index] : null);
+ $start_in_chunk = false;
+ }
+ if (!$placed && $chunk !== null) {
+ $hit_spot = $hit - $offset;
+ $highlightTweet .= StringUtils::substr($chunk, $chunk_cursor, $hit_spot - $chunk_cursor) . $tag;
+ $chunk_cursor = $hit_spot;
+ $start_in_chunk = ($index % 2 === 0);
+ $placed = true;
+ }
+ # Ultimate fallback - hits that run off the end get a closing tag:
+ if (!$placed) {
+ $highlightTweet .= $tag;
+ }
+ }
+ if ($chunk !== null) {
+ if ($chunk_cursor < StringUtils::strlen($chunk)) {
+ $highlightTweet .= StringUtils::substr($chunk, $chunk_cursor);
+ }
+ $chunks_count = count($chunks);
+ for ($index = $chunk_index + 1; $index < $chunks_count; $index++) {
+ $highlightTweet .= ($index % 2 === 0 ? $chunks[$index] : '<' . $chunks[$index] . '>');
+ }
+ }
+ }
+ return $highlightTweet;
+ }
+}
diff --git a/vendor/nojimage/twitter-text-php/lib/Twitter/Text/ParseResults.php b/vendor/nojimage/twitter-text-php/lib/Twitter/Text/ParseResults.php
new file mode 100644
index 0000000000000..c24f985c19d7c
--- /dev/null
+++ b/vendor/nojimage/twitter-text-php/lib/Twitter/Text/ParseResults.php
@@ -0,0 +1,203 @@
+ 1000 indicates input text that is longer than
+ * the allowable maximum.
+ * @property bool $valid Indicates if input text length corresponds to a valid result.
+ * @property int $displayRangeStart
+ * @property int $displayRangeEnd
+ * @property int $validRangeStart
+ * @property int $validRangeEnd
+ */
+class ParseResults
+{
+
+ /**
+ * A pair of unicode code point indices identifying the inclusive start and exclusive end of
+ * the displayable content of the Tweet.
+ *
+ * @var array
+ * @link https://developer.twitter.com/en/docs/tweets/tweet-updates
+ */
+ protected $displayTextRange = array(0, 0);
+
+ /**
+ * A pair of unicode code point indices identifying the inclusive start and exclusive end of
+ * the valid content of the Tweet.
+ *
+ * @var array
+ * @link https://developer.twitter.com/en/docs/tweets/tweet-updates
+ */
+ protected $validTextRange = array(0, 0);
+
+ /**
+ * @var array
+ */
+ protected $result = array(
+ 'weightedLength' => 0,
+ 'valid' => false,
+ 'permillage' => 0,
+ );
+
+ /**
+ * Tweet parsed results
+ *
+ * @param int $weightedLength
+ * @param int $permillage
+ * @param bool $isValid
+ * @param array $displayTextRange
+ * @param array $validTextRange
+ */
+ public function __construct(
+ $weightedLength = 0,
+ $permillage = 0,
+ $isValid = false,
+ array $displayTextRange = array(0, 0),
+ array $validTextRange = array(0, 0)
+ ) {
+ $this->weightedLength = $weightedLength;
+ $this->permillage = $permillage;
+ $this->valid = $isValid;
+ $this->displayRangeEnd = $displayTextRange[1];
+ $this->displayRangeStart = $displayTextRange[0];
+ $this->validRangeEnd = $validTextRange[1];
+ $this->validRangeStart = $validTextRange[0];
+ }
+
+ /**
+ * property accessor
+ *
+ * @param string $name
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ if ($name === 'displayRangeStart') {
+ return $this->displayTextRange[0];
+ }
+
+ if ($name === 'displayRangeEnd') {
+ return $this->displayTextRange[1];
+ }
+
+ if ($name === 'validRangeStart') {
+ return $this->validTextRange[0];
+ }
+
+ if ($name === 'validRangeEnd') {
+ return $this->validTextRange[1];
+ }
+
+ return isset($this->result[$name]) ? $this->result[$name] : null;
+ }
+
+ /**
+ * property setter
+ *
+ * @param string $name
+ * @param mixed $value
+ * @return void
+ */
+ public function __set($name, $value)
+ {
+ if (
+ $name === 'displayRangeStart'
+ && $this->lte($value, $this->displayTextRange[1], $name, 'displayRangeEnd')
+ ) {
+ $this->displayTextRange[0] = (int)$value;
+ } elseif (
+ $name === 'displayRangeEnd'
+ && $this->gte($value, $this->displayTextRange[0], $name, 'displayRangeStart')
+ ) {
+ $this->displayTextRange[1] = (int)$value;
+ } elseif (
+ $name === 'validRangeStart'
+ && $this->lte($value, $this->validTextRange[1], $name, 'validRangeEnd')
+ ) {
+ $this->validTextRange[0] = (int)$value;
+ } elseif (
+ $name === 'validRangeEnd'
+ && $this->gte($value, $this->validTextRange[0], $name, 'validRangeStart')
+ ) {
+ $this->validTextRange[1] = (int)$value;
+ } elseif ($name === 'valid') {
+ $this->result[$name] = (bool)$value;
+ } elseif (isset($this->result[$name])) {
+ $this->result[$name] = (int)$value;
+ }
+ }
+
+ /**
+ * check value less than equals
+ *
+ * @param int $lessValue
+ * @param int $greaterValue
+ * @param string $lessValueLabel
+ * @param string $greaterValueLabel
+ * @return bool
+ * @throws \RangeException
+ */
+ private function lte($lessValue, $greaterValue, $lessValueLabel, $greaterValueLabel)
+ {
+ if ($lessValue > $greaterValue) {
+ throw new \RangeException("$lessValueLabel should be less than equals $greaterValueLabel: "
+ . "[$lessValue, $greaterValue]");
+ }
+
+ return true;
+ }
+
+ /**
+ * check value less than equals
+ *
+ * @param int $greaterValue
+ * @param int $lessValue
+ * @param string $greaterValueLabel
+ * @param string $lessValueLabel
+ * @return bool
+ * @throws \RangeException
+ */
+ private function gte($greaterValue, $lessValue, $greaterValueLabel, $lessValueLabel)
+ {
+ if ($lessValue > $greaterValue) {
+ throw new \RangeException("$greaterValueLabel should be greater than equals $lessValueLabel: "
+ . "[$lessValue, $greaterValue]");
+ }
+
+ return true;
+ }
+
+ /**
+ * convert to array
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return array_merge($this->result, array(
+ 'displayRangeStart' => $this->displayRangeStart,
+ 'displayRangeEnd' => $this->displayRangeEnd,
+ 'validRangeStart' => $this->validRangeStart,
+ 'validRangeEnd' => $this->validRangeEnd,
+ ));
+ }
+}
diff --git a/vendor/nojimage/twitter-text-php/lib/Twitter/Text/Parser.php b/vendor/nojimage/twitter-text-php/lib/Twitter/Text/Parser.php
new file mode 100644
index 0000000000000..34ead6c6b78ee
--- /dev/null
+++ b/vendor/nojimage/twitter-text-php/lib/Twitter/Text/Parser.php
@@ -0,0 +1,190 @@
+config = $config;
+ }
+
+ /**
+ * Parses a given tweet text with the weighted character count configuration
+ *
+ * @param string $tweet which is to be parsed
+ * @return ParseResults
+ */
+ public function parseTweet($tweet)
+ {
+ if ($tweet === null || '' === $tweet) {
+ return new ParseResults();
+ }
+
+ $normalizedTweet = StringUtils::normalizeFromNFC($tweet);
+ $normalizedTweetLength = StringUtils::strlen($normalizedTweet);
+
+ $emojiParsingEnabled = $this->config->getEmojiParsingEnabled();
+ $maxWeightedTweetLength = $this->config->getScaledMaxWeightedTweetLength();
+ $transformedUrlWeight = $this->config->getScaledTransformedURLLength();
+
+ $extractor = new Extractor();
+ $urlEntitiesMap = $this->transformEntitiesToHash($extractor->extractURLsWithIndices($normalizedTweet));
+ $emojiEntitiesMap = $emojiParsingEnabled
+ ? $this->transformEntitiesToHash($extractor->extractEmojiWithIndices($normalizedTweet))
+ : array();
+
+ $hasInvalidCharacters = false;
+ $weightedCount = 0;
+ $offset = 0;
+ $displayOffset = 0;
+ $validOffset = 0;
+
+ while ($offset < $normalizedTweetLength) {
+ if (isset($urlEntitiesMap[$offset])) {
+ list($urlStart, $urlEnd) = $urlEntitiesMap[$offset]['indices'];
+ $urlLength = $urlEnd - $urlStart;
+
+ $weightedCount += $transformedUrlWeight;
+ $offset += $urlLength;
+ $displayOffset += $urlLength;
+ if ($weightedCount <= $maxWeightedTweetLength) {
+ $validOffset += $urlLength;
+ }
+ } elseif ($emojiParsingEnabled && isset($emojiEntitiesMap[$offset])) {
+ $emoji = $emojiEntitiesMap[$offset]['emoji'];
+ $emojiLength = StringUtils::strlen($emoji);
+ $charCount = StringUtils::charCount($emoji);
+
+ $weightedCount += $this->config->defaultWeight;
+ $offset += $emojiLength;
+ $displayOffset += $charCount;
+ if ($weightedCount <= $maxWeightedTweetLength) {
+ $validOffset += $charCount;
+ }
+ } else {
+ $char = StringUtils::substr($normalizedTweet, $offset, 1);
+
+ $hasInvalidCharacters = $hasInvalidCharacters || $this->hasInvalidCharacters($char);
+ $charCount = StringUtils::strlen($char);
+ $charWidth = StringUtils::isSurrogatePair($char) ? 2 : 1;
+
+ $weightedCount += $this->getCharacterWeight($char, $this->config);
+ $offset += $charCount;
+ $displayOffset += $charWidth;
+
+ if (!$hasInvalidCharacters && $weightedCount <= $maxWeightedTweetLength) {
+ $validOffset += $charWidth;
+ }
+ }
+ }
+
+ $scaledWeightedLength = $weightedCount / $this->config->scale;
+ $permillage = $scaledWeightedLength * 1000 / $this->config->maxWeightedTweetLength;
+ $isValid = !$hasInvalidCharacters && $weightedCount <= $maxWeightedTweetLength;
+
+ $normalizedTweetOffset = StringUtils::strlen($tweet) - $normalizedTweetLength;
+ $displayTextRange = array(0, $displayOffset + $normalizedTweetOffset - 1);
+ $validTextRange = array(0, $validOffset + $normalizedTweetOffset - 1);
+
+ return new ParseResults($scaledWeightedLength, $permillage, $isValid, $displayTextRange, $validTextRange);
+ }
+
+ /**
+ * Convert to Hash by indices start
+ *
+ * @param array $entities
+ * @return array
+ */
+ private function transformEntitiesToHash(array $entities)
+ {
+ return array_reduce($entities, function ($map, $entity) {
+ $map[$entity['indices'][0]] = $entity;
+
+ return $map;
+ }, array());
+ }
+
+ /**
+ * Get the character weight from ranges
+ *
+ * @param string $char the Character
+ * @param Configuration $config the parse configuration
+ * @return int
+ */
+ private function getCharacterWeight($char, Configuration $config)
+ {
+ $codePoint = StringUtils::ord($char);
+
+ foreach ($config->ranges as $range) {
+ if ($this->inRange($codePoint, $range)) {
+ return $range['weight'];
+ }
+ }
+
+ return $config->defaultWeight;
+ }
+
+ /**
+ * check codepoint in range
+ *
+ * @param int $codePoint
+ * @param array $range
+ * @return boolean
+ */
+ private function inRange($codePoint, array $range)
+ {
+ return ($codePoint >= $range['start'] && $codePoint <= $range['end']);
+ }
+
+ /**
+ * check has invalid characters
+ *
+ * @param string $char
+ * @return bool
+ */
+ private function hasInvalidCharacters($char)
+ {
+ return preg_match(Regex::getInvalidCharactersMatcher(), $char);
+ }
+}
diff --git a/vendor/nojimage/twitter-text-php/lib/Twitter/Text/Regex.php b/vendor/nojimage/twitter-text-php/lib/Twitter/Text/Regex.php
new file mode 100644
index 0000000000000..8ac6683213362
--- /dev/null
+++ b/vendor/nojimage/twitter-text-php/lib/Twitter/Text/Regex.php
@@ -0,0 +1,838 @@
+
+ * @author Nick Pope
+ * @copyright Copyright © 2010, Mike Cochrane, Nick Pope
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
+ * @package Twitter.Text
+ */
+
+namespace Twitter\Text;
+
+/**
+ * Twitter Regex Abstract Class
+ *
+ * Used by subclasses that need to parse tweets.
+ *
+ * Originally written by {@link http://github.com/mikenz Mike Cochrane}, this
+ * is based on code by {@link http://github.com/mzsanford Matt Sanford} and
+ * heavily modified by {@link http://github.com/ngnpope Nick Pope}.
+ *
+ * @author Mike Cochrane
+ * @author Nick Pope
+ * @copyright Copyright © 2010, Mike Cochrane, Nick Pope
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
+ * @package Twitter
+ */
+class Regex
+{
+ /**
+ * Expression to match whitespace characters.
+ *
+ * 0x0009-0x000D Cc # ..
+ * 0x0020 Zs # SPACE
+ * 0x0085 Cc #
+ * 0x00A0 Zs # NO-BREAK SPACE
+ * 0x1680 Zs # OGHAM SPACE MARK
+ * 0x180E Zs # MONGOLIAN VOWEL SEPARATOR
+ * 0x2000-0x200A Zs # EN QUAD..HAIR SPACE
+ * 0x2028 Zl # LINE SEPARATOR
+ * 0x2029 Zp # PARAGRAPH SEPARATOR
+ * 0x202F Zs # NARROW NO-BREAK SPACE
+ * 0x205F Zs # MEDIUM MATHEMATICAL SPACE
+ * 0x3000 Zs # IDEOGRAPHIC SPACE
+ *
+ * @var string
+ */
+ // @codingStandardsIgnoreStart
+ private static $spaces = '\x{0009}-\x{000D}\x{0020}\x{0085}\x{00a0}\x{1680}\x{180E}\x{2000}-\x{200a}\x{2028}\x{2029}\x{202f}\x{205f}\x{3000}'; // @codingStandardsIgnoreEnd
+
+ /**
+ * Expression to match latin accented characters.
+ *
+ * 0x00C0-0x00D6
+ * 0x00D8-0x00F6
+ * 0x00F8-0x00FF
+ * 0x0100-0x024f
+ * 0x0253-0x0254
+ * 0x0256-0x0257
+ * 0x0259
+ * 0x025b
+ * 0x0263
+ * 0x0268
+ * 0x026f
+ * 0x0272
+ * 0x0289
+ * 0x028b
+ * 0x02bb
+ * 0x0300-0x036f
+ * 0x1e00-0x1eff
+ *
+ * Excludes 0x00D7 - multiplication sign (confusable with 'x').
+ * Excludes 0x00F7 - division sign.
+ *
+ * @var string
+ */
+ // @codingStandardsIgnoreStart
+ private static $latinAccents = '\x{00c0}-\x{00d6}\x{00d8}-\x{00f6}\x{00f8}-\x{00ff}\x{0100}-\x{024f}\x{0253}-\x{0254}\x{0256}-\x{0257}\x{0259}\x{025b}\x{0263}\x{0268}\x{026f}\x{0272}\x{0289}\x{028b}\x{02bb}\x{0300}-\x{036f}\x{1e00}-\x{1eff}'; // @codingStandardsIgnoreEnd
+
+ /**
+ * Invalid Characters
+ *
+ * 0xFFFE,0xFEFF # BOM
+ * 0xFFFF # Special
+ * 0x202A-0x202E # Directional change
+ */
+ private static $invalidCharacters = '\x{202a}-\x{202e}\x{feff}\x{fffe}\x{ffff}';
+
+ /**
+ * Directional Characters
+ *
+ * 0x061C ARABIC LETTER MARK (ALM)
+ * 0x200E LEFT-TO-RIGHT MARK (LRM)
+ * 0x200F RIGHT-TO-LEFT MARK (RLM)
+ * 0x202A LEFT-TO-RIGHT EMBEDDING (LRE)
+ * 0x202B RIGHT-TO-LEFT EMBEDDING (RLE)
+ * 0x202C POP DIRECTIONAL FORMATTING (PDF)
+ * 0x202D LEFT-TO-RIGHT OVERRIDE (LRO)
+ * 0x202E RIGHT-TO-LEFT OVERRIDE (RLO)
+ * 0x2066 LEFT-TO-RIGHT ISOLATE (LRI)
+ * 0x2067 RIGHT-TO-LEFT ISOLATE (RLI)
+ * 0x2068 FIRST STRONG ISOLATE (FSI)
+ * 0x2069 POP DIRECTIONAL ISOLATE (PDI)
+ */
+ private static $directionalCharacters = '\x{061c}\x{200e}\x{200f}\x{202a}\x{202e}\x{2066}\x{2069}';
+
+ /**
+ * Expression to match RTL characters.
+ *
+ * 0x0600-0x06FF Arabic
+ * 0x0750-0x077F Arabic Supplement
+ * 0x08A0-0x08FF Arabic Extended-A
+ * 0x0590-0x05FF Hebrew
+ * 0xFB50-0xFDFF Arabic Presentation Forms-A
+ * 0xFE70-0xFEFF Arabic Presentation Forms-B
+ *
+ * @var string
+ */
+ // @codingStandardsIgnoreStart
+ private static $rtlChars = '\x{0600}-\x{06ff}\x{0750}-\x{077f}\x{08a0}-\x{08ff}\x{0590}-\x{05ff}\x{fb50}-\x{fdff}\x{fe70}-\x{feff}'; // @codingStandardsIgnoreEnd
+
+ # Expression to match at and hash sign characters:
+ private static $atSigns = '@@';
+
+ private static $hashSigns = '##';
+
+ # cash tags
+ private static $cashSigns = '\$';
+
+ private static $cashtag = '[a-z]{1,6}(?:[._][a-z]{1,2})?';
+
+ # These URL validation pattern strings are based on the ABNF from RFC 3986
+ private static $validateUrlUnreserved = '[a-z\p{Cyrillic}0-9\-._~]';
+
+ private static $validateUrlPctEncoded = '(?:%[0-9a-f]{2})';
+
+ private static $validateUrlSubDelims = '[!$&\'()*+,;=]';
+
+ private static $validUrlQueryChars = '[a-z0-9!?\*\'\(\);:&=\+\$\/%#\[\]\-_\.,~|@]';
+
+ private static $validUrlQueryEndingChars = '[a-z0-9_&=#\/\-]';
+
+ // @codingStandardsIgnoreStart
+ private static $validateUrlIpv4 = '(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(?:\.(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])){3})'; // @codingStandardsIgnoreEnd
+
+ private static $validateUrlIpv6 = '(?:\[[a-f0-9:\.]+\])';
+
+ private static $validateUrlPort = '[0-9]{1,5}';
+
+ # URL related hash regex collection
+ private static $validSpecialCcTLD = '(?:(?:co|tv)(?=[^0-9a-z@]|$))';
+
+ private static $validPunycode = '(?:xn--[0-9a-z]+)';
+
+ /**
+ * Get invalid characters matcher
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getInvalidCharactersMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ $regexp = '/[' . static::$invalidCharacters . ']/u';
+ }
+
+ return $regexp;
+ }
+
+ /**
+ * Get RTL characters matcher
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getRtlCharsMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ $regexp = '/[' . static::$rtlChars . ']/iu';
+ }
+
+ return $regexp;
+ }
+
+ // =================================================================================================================
+
+ /**
+ * Get valid ascii domain matcher
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getValidAsciiDomainMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ $regexp = '/' . static::getValidSubdomain() . '*(' . static::getValidDomainName()
+ . ')(?:' . TldLists::getValidGTLD() . '|' . TldLists::getValidCcTLD()
+ . '|' . static::$validPunycode . ')/iu';
+ }
+
+ return $regexp;
+ }
+
+ /**
+ * Get valid tco url matcher
+ *
+ * Used by the extractor for stricter t.co URL extraction
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getValidTcoUrlMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ $regexp = '/^https?:\/\/t\.co\/([a-z0-9]+)'
+ . '(?:\?' . static::$validUrlQueryChars . '*' . static::$validUrlQueryEndingChars . ')?/iu';
+ }
+
+ return $regexp;
+ }
+
+ /**
+ * Get invalid short domain matcher
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getInvalidShortDomainMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ $regexp = '/\A' . static::getValidDomainName() . TldLists::getValidCcTLD() . '\Z/iu';
+ }
+
+ return $regexp;
+ }
+
+ /**
+ * Get valid special short domain matcher
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getValidSpecialShortDomainMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ $regexp = '/\A' . static::getValidDomainName() . static::$validSpecialCcTLD . '\Z/iu';
+ }
+
+ return $regexp;
+ }
+
+ /**
+ * Get invalid url without protocol preceding chars matcher
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getInvalidUrlWithoutProtocolPrecedingCharsMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ $regexp = '/[\-_.\/]\z/iu';
+ }
+
+ return $regexp;
+ }
+
+ /**
+ * Get valid url
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getValidUrlMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ $validUrlPrecedingChars = '(?:[^a-z0-9_@@\$##' . static::$invalidCharacters . ']|[' . static::$directionalCharacters . ']|^)';
+ $validPortNumber = '[0-9]+';
+
+ $regexp = '/(?:' # $1 Complete match (preg_match() already matches everything.)
+ . '(' . $validUrlPrecedingChars . ')' # $2 Preceding characters
+ . '(' # $3 Complete URL
+ . '(https?:\/\/)?' # $4 Protocol (optional)
+ . '(' . static::getValidDomain() . ')' # $5 Domain(s)
+ . '(?::(' . $validPortNumber . '))?' # $6 Port number (optional)
+ . '(\/' . static::getValidUrlPath() . '*)?' # $7 URL Path
+ . '(\?' . static::$validUrlQueryChars . '*' . static::$validUrlQueryEndingChars . ')?' # $8 Query String
+ . ')'
+ . ')/iux';
+ }
+
+ return $regexp;
+ }
+
+ /**
+ * Get valid domain chars
+ *
+ * @return string
+ */
+ private static function getValidDomainChars()
+ {
+ return '0-9a-z' . static::$latinAccents;
+ }
+
+ /**
+ * Get valid subdomain
+ *
+ * @return string
+ */
+ private static function getValidSubdomain()
+ {
+ $domainValidChars = static::getValidDomainChars();
+
+ return '(?>(?:[' . $domainValidChars . '][' . $domainValidChars . '\-_]*)?[' . $domainValidChars . ']\.)';
+ }
+
+ /**
+ * Get valid domain name
+ *
+ * @return string
+ */
+ private static function getValidDomainName()
+ {
+ $domainValidChars = static::getValidDomainChars();
+
+ return '(?:(?:[' . $domainValidChars . '][' . $domainValidChars . '\-]*)?[' . $domainValidChars . ']\.)';
+ }
+
+ /**
+ * Get valid unicode domain chars
+ *
+ * @return string
+ */
+ private static function getValidUnicodeDomainChars()
+ {
+ return '[^\p{P}\p{Z}\p{C}' . static::$invalidCharacters . static::$spaces . ']';
+ }
+
+ /**
+ * Get valid unicode domain name
+ *
+ * @return string
+ */
+ private static function getValidUnicodeDomainName()
+ {
+ $domainValidChars = static::getValidUnicodeDomainChars();
+
+ return '(?:(?:' . $domainValidChars . '(?:' . $domainValidChars . '|[\-])*)?' . $domainValidChars . '\.)';
+ }
+
+ /**
+ * Get valid domain
+ *
+ * @return string
+ */
+ private static function getValidDomain()
+ {
+ $validSubdomain = static::getValidSubdomain();
+ $validDomainName = static::getValidDomainName();
+ $validUnicodeDomainName = static::getValidUnicodeDomainName();
+ $validGTLD = TldLists::getValidGTLD();
+ $validCcTLD = TldLists::getValidCcTLD();
+
+ return ''
+ // optional sub-domain + domain + TLD
+ // e.g. twitter.com, foo.co.jp, bar.co.uk
+ . '(?:' . $validSubdomain . '*' . $validDomainName
+ . '(?:' . $validGTLD . '|' . $validCcTLD . '|' . static::$validPunycode . '))'
+ // domain + gTLD | protocol + unicode domain + gTLD
+ . '|(?:'
+ . '(?:' . $validSubdomain . '+' . $validDomainName
+ . '|' . $validDomainName
+ . '|(?:(?<=http:\/\/|https:\/\/)' . $validUnicodeDomainName . ')'
+ . ')'
+ . $validGTLD
+ . ')'
+ // protocol + (domain | unicode domain) + ccTLD
+ . '|(?:(?<=http:\/\/|https:\/\/)'
+ . '(?:' . $validDomainName . '|' . $validUnicodeDomainName . ')'
+ . $validCcTLD . ')'
+ // domain + ccTLD + '/'
+ // e.g. t.co/
+ . '|(?:' . $validDomainName . $validCcTLD . '(?=\/))';
+ }
+
+ /**
+ * Get valid url path
+ *
+ * @return string
+ */
+ private static function getValidUrlPath()
+ {
+ $validGeneralUrlPathChars = '[a-z0-9' . preg_quote("!*';:=+,.$/%#[]–\x{2013}_~", '/')
+ . '|&@' . static::$latinAccents . '\p{Cyrillic}]';
+
+ # Allow URL paths to contain up to two nested levels of balanced parentheses:
+ # 1. Used in Wikipedia URLs, e.g. /Primer_(film)
+ # 2. Used in IIS sessions, e.g. /S(dfd346)/
+ # 3. Used in Rdio URLs like /track/We_Up_(Album_Version_(Edited))/
+ $validUrlBalancedParens = '(?:\('
+ . '(?:' . $validGeneralUrlPathChars . '+'
+ . '|'
+ // allow one nested level of balanced parentheses
+ . '(?:'
+ . $validGeneralUrlPathChars . '*'
+ . '\(' . $validGeneralUrlPathChars . '+' . '\)'
+ . $validGeneralUrlPathChars . '*'
+ . ')'
+ . ')'
+ . '\))';
+ # Valid end-of-path characters (so /foo. does not gobble the period).
+ # 1. Allow = for empty URL parameters and other URL-join artifacts.
+ $validUrlPathEndingChars = '[a-z0-9=_#\/\+\-' . static::$latinAccents . '\p{Cyrillic}]'
+ . '|(?:' . $validUrlBalancedParens . ')';
+
+ return '(?:(?:'
+ . $validGeneralUrlPathChars . '*(?:'
+ . $validUrlBalancedParens . ' '
+ . $validGeneralUrlPathChars . '*)*'
+ . $validUrlPathEndingChars . ')|(?:@'
+ . $validGeneralUrlPathChars . '+\/))';
+ }
+
+ // =================================================================================================================
+
+ # NOTE: PHP doesn't have Ruby's $' (dollar apostrophe) so we have to capture
+ # $after in the following regular expression. Note that we only use a
+ # look-ahead capture here and don't append $after when we return.
+
+ /**
+ * Get valid mentions or lists matcher
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getValidMentionsOrListsMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ $mentionPrecedingChars = '([^a-z0-9_!#\$%&*@@\/]|^|(?:^|[^a-z0-9_+~.-])RT:?)';
+ $regexp = '/' . $mentionPrecedingChars
+ . '([' . static::$atSigns . '])([a-z0-9_]{1,20})(\/[a-z][a-z0-9_\-]{0,24})?(?=(.*|$))/iu';
+ }
+
+ return $regexp;
+ }
+
+ /**
+ * Get valid hashtag matcher
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getValidReplyMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ $regexp = '/^(?:[' . static::$spaces . static::$directionalCharacters . '])*[' . static::$atSigns . ']([a-z0-9_]{1,20})(?=(.*|$))/iu';
+ }
+
+ return $regexp;
+ }
+
+ /**
+ * Get end of hashtag matcher
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getEndMentionMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ $regexp = '/\A(?:[' . static::$atSigns . ']|[' . static::$latinAccents . ']|:\/\/)/iu';
+ }
+
+ return $regexp;
+ }
+
+ // =================================================================================================================
+
+ /**
+ * Get hashtag matcher
+ *
+ * @return string matcher
+ */
+ private static function getHashtagPattern()
+ {
+ $hashtag_letters = '\p{L}\p{M}';
+ $hashtag_numerals = '\p{Nd}';
+ # Hashtag special chars
+ #
+ # _ underscore
+ # 0x200c ZERO WIDTH NON-JOINER (ZWNJ)
+ # 0x200d ZERO WIDTH JOINER (ZWJ)
+ # 0xa67e CYRILLIC KAVYKA
+ # 0x05be HEBREW PUNCTUATION MAQAF
+ # 0x05f3 HEBREW PUNCTUATION GERESH
+ # 0x05f4 HEBREW PUNCTUATION GERSHAYIM
+ # 0xff5e FULLWIDTH TILDE
+ # 0x301c WAVE DASH
+ # 0x309b KATAKANA-HIRAGANA VOICED SOUND MARK
+ # 0x309c KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+ # 0x30a0 KATAKANA-HIRAGANA DOUBLE HYPHEN
+ # 0x30fb KATAKANA MIDDLE DOT
+ # 0x3003 DITTO MARK
+ # 0x0f0b TIBETAN MARK INTERSYLLABIC TSHEG
+ # 0x0f0c TIBETAN MARK DELIMITER TSHEG BSTAR
+ # 0x00b7 MIDDLE DOT
+ $hashtag_special_chars = '_\x{200c}\x{200d}\x{a67e}\x{05be}\x{05f3}\x{05f4}'
+ . '\x{ff5e}\x{301c}\x{309b}\x{309c}\x{30a0}\x{30fb}\x{3003}\x{0f0b}\x{0f0c}\x{00b7}';
+ $hashtag_letters_numerals_set = '[' . $hashtag_letters . $hashtag_numerals . $hashtag_special_chars . ']';
+ $hashtag_letters_set = '[' . $hashtag_letters . ']';
+ $hashtag_boundary = '(?:\A|\x{fe0e}|\x{fe0f}|[^&'
+ . $hashtag_letters . $hashtag_numerals . $hashtag_special_chars . '])';
+
+ return '(' . $hashtag_boundary . ')(#|\x{ff03})(?!\x{fe0f}|\x{20e3})('
+ . $hashtag_letters_numerals_set . '*' . $hashtag_letters_set . $hashtag_letters_numerals_set . '*)';
+ }
+
+ /**
+ * Get valid hashtag matcher
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getValidHashtagMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ $regexp = '/' . static::getHashtagPattern() . '(?=(.*|$))/iu';
+ }
+
+ return $regexp;
+ }
+
+ /**
+ * Get end of hashtag matcher
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getEndHashtagMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ $regexp = '/\A(?:[' . static::$hashSigns . ']|:\/\/)/u';
+ }
+
+ return $regexp;
+ }
+
+ // =================================================================================================================
+
+ /**
+ * Get valid cachtag matcher
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getValidCashtagMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ $regexp = '/(^|[' . static::$spaces . static::$directionalCharacters . '])([' . static::$cashSigns . '])'
+ . '(' . static::$cashtag . ')(?=($|\s|[[:punct:]]))/iu';
+ }
+
+ return $regexp;
+ }
+
+ /**
+ * Get end of cachtag matcher
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getEndCashtagMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ $regexp = '/\A(?:[' . static::$cashSigns . ']|:\/\/)/u';
+ }
+
+ return $regexp;
+ }
+
+ // =================================================================================================================
+
+ /**
+ * Get url matcher
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getValidateUrlUnencodedMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ # Modified version of RFC 3986 Appendix B
+ $regexp = '/\A' # Full URL
+ . '(?:'
+ . '([^:\/?#]+):\/\/' # $1 Scheme
+ . ')?'
+ . '([^\/?#]*)' # $2 Authority
+ . '([^?#]*)' # $3 Path
+ . '(?:'
+ . '\?([^#]*)' # $4 Query
+ . ')?'
+ . '(?:'
+ . '\#(.*)' # $5 Fragment
+ . ')?\z/iux';
+ }
+
+ return $regexp;
+ }
+
+ /**
+ * Get valid url ip
+ *
+ * @return string matcher
+ */
+ private static function getValidateUrlIp()
+ {
+ return '(?:' . static::$validateUrlIpv4 . '|' . static::$validateUrlIpv6 . ')'; #/iox
+ }
+
+ /**
+ * Get valid url domain
+ *
+ * @return string matcher
+ */
+ private static function getValidateUrlDomain()
+ {
+ $subdomain = '(?:[a-z0-9](?:[a-z0-9_\-]*[a-z0-9])?)'; #/i
+ $domain = '(?:[a-z0-9](?:[a-z0-9\-]*[a-z0-9])?)'; #/i
+ $tld = '(?:[a-z](?:[a-z0-9\-]*[a-z0-9])?)'; #/i
+
+ return '(?:(?:' . $subdomain . '\.)*(?:' . $domain . '\.)' . $tld . ')'; #/iox
+ }
+
+ /**
+ * Get valid url host
+ *
+ * @return string matcher
+ */
+ private static function getValidateUrlHost()
+ {
+ return '(?:' . static::getValidateUrlIp() . '|' . static::getValidateUrlDomain() . ')'; #/iox
+ }
+
+ /**
+ * Get valid url unicode domain
+ *
+ * @return string matcher
+ */
+ private static function getValidateUrlUnicodeDomain()
+ {
+ $subdomain = '(?:(?:[a-z0-9]|[^\x00-\x7f])(?:(?:[a-z0-9_\-]|[^\x00-\x7f])*(?:[a-z0-9]|[^\x00-\x7f]))?)'; #/ix
+ $domain = '(?:(?:[a-z0-9]|[^\x00-\x7f])(?:(?:[a-z0-9\-]|[^\x00-\x7f])*(?:[a-z0-9]|[^\x00-\x7f]))?)'; #/ix
+ $tld = '(?:(?:[a-z]|[^\x00-\x7f])(?:(?:[a-z0-9\-]|[^\x00-\x7f])*(?:[a-z0-9]|[^\x00-\x7f]))?)'; #/ix
+
+ return '(?:(?:' . $subdomain . '\.)*(?:' . $domain . '\.)' . $tld . ')'; #/iox
+ }
+
+ /**
+ * Get valid url unicode host
+ *
+ * @return string matcher
+ */
+ private static function getValidateUrlUnicodeHost()
+ {
+ return '(?:' . static::getValidateUrlIp() . '|' . static::getValidateUrlUnicodeDomain() . ')'; #/iox
+ }
+
+ /**
+ * Get valid url userinfo
+ *
+ * @return string matcher
+ */
+ private static function getValidateUrlUserinfo()
+ {
+ return '(?:' . static::$validateUrlUnreserved
+ . '|' . static::$validateUrlPctEncoded
+ . '|' . static::$validateUrlSubDelims
+ . '|:)*'; #/iox
+ }
+
+ /**
+ * Get url unicode authority matcher
+ *
+ * Unencoded internationalized domains - this doesn't check for invalid UTF-8 sequences
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getValidateUrlUnicodeAuthorityMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ $regexp = '/'
+ . '(?:(' . static::getValidateUrlUserinfo() . ')@)?' # $1 userinfo
+ . '(' . static::getValidateUrlUnicodeHost() . ')' # $2 host
+ . '(?::(' . static::$validateUrlPort . '))?' # $3 port
+ . '/iux';
+ }
+
+ return $regexp;
+ }
+
+ /**
+ * Get url authority matcher
+ *
+ * This is more strict than the rfc specifies
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getValidateUrlAuthorityMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ $regexp = '/'
+ . '(?:(' . static::getValidateUrlUserinfo() . ')@)?' # $1 userinfo
+ . '(' . static::getValidateUrlHost() . ')' # $2 host
+ . '(?::(' . static::$validateUrlPort . '))?' # $3 port
+ . '/ix';
+ }
+
+ return $regexp;
+ }
+
+ /**
+ * Get url scheme matcher
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getValidateUrlSchemeMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ $regexp = '/(?:[a-z][a-z0-9+\-.]*)/i';
+ }
+
+ return $regexp;
+ }
+
+ /**
+ * Get valid url charactors
+ *
+ * @return string matcher
+ */
+ private static function getValidateUrlPchar()
+ {
+ return '(?:' . static::$validateUrlUnreserved
+ . '|' . static::$validateUrlPctEncoded
+ . '|' . static::$validateUrlSubDelims
+ . '|[:\|@])'; #/iox
+ }
+
+ /**
+ * Get url path matcher
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getValidateUrlPathMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ $regexp = '/(\/' . static::getValidateUrlPchar() . '*)*/iu';
+ }
+
+ return $regexp;
+ }
+
+ /**
+ * Get url query matcher
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getValidateUrlQueryMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ $regexp = '/(' . static::getValidateUrlPchar() . '|\/|\?)*/iu';
+ }
+
+ return $regexp;
+ }
+
+ /**
+ * Get url flagment matcher
+ *
+ * @staticvar string $regexp
+ * @return string
+ */
+ public static function getValidateUrlFragmentMatcher()
+ {
+ static $regexp = null;
+
+ if ($regexp === null) {
+ $regexp = '/(' . static::getValidateUrlPchar() . '|\/|\?)*/iu';
+ }
+
+ return $regexp;
+ }
+}
diff --git a/vendor/nojimage/twitter-text-php/lib/Twitter/Text/StringUtils.php b/vendor/nojimage/twitter-text-php/lib/Twitter/Text/StringUtils.php
new file mode 100644
index 0000000000000..b7af156c8ff6d
--- /dev/null
+++ b/vendor/nojimage/twitter-text-php/lib/Twitter/Text/StringUtils.php
@@ -0,0 +1,191 @@
+ $string_length) {
+ $start = $string_length;
+ }
+ if ($length < 0) {
+ $length = max(0, $string_length - $start + $length);
+ } elseif (($length === null) || ($length > $string_length)) {
+ $length = $string_length;
+ }
+ if (($start + $length) > $string_length) {
+ $length = $string_length - $start;
+ }
+
+ $suffixOffset = $start + $length;
+ $suffixLength = $string_length - $start - $length;
+
+ return static::substr($string, 0, $start, $encoding)
+ . $replacement
+ . static::substr($string, $suffixOffset, $suffixLength, $encoding);
+ }
+
+ /**
+ * idn_to_ascii wrapper
+ *
+ * @param string $domain as utf8
+ * @return string
+ */
+ public static function idnToAscii($domain)
+ {
+ // INTL_IDNA_VARIANT_UTS46 defined PHP 5.4.0 or later
+ if (defined('INTL_IDNA_VARIANT_UTS46')) {
+ return idn_to_ascii($domain, IDNA_ALLOW_UNASSIGNED, INTL_IDNA_VARIANT_UTS46);
+ }
+
+ return idn_to_ascii($domain, IDNA_ALLOW_UNASSIGNED);
+ }
+
+ /**
+ * normalize text from NFC
+ *
+ * @param string $text
+ * @return string
+ */
+ public static function normalizeFromNFC($text)
+ {
+ return normalizer_normalize($text);
+ }
+
+ /**
+ * get code point
+ *
+ * @param string $char
+ * @param string $encoding
+ * @return int
+ */
+ public static function ord($char, $encoding = 'UTF-8')
+ {
+ if (mb_strlen($char, $encoding) > 1) {
+ $char = mb_substr($char, 0, 1, $encoding);
+ }
+
+ return current(unpack('N', mb_convert_encoding($char, 'UCS-4BE', $encoding)));
+ }
+
+ /**
+ * get code point at
+ *
+ * @param string $str
+ * @param int $offset
+ * @param string $encoding
+ * @return int
+ */
+ public static function codePointAt($str, $offset, $encoding = 'UTF-8')
+ {
+ return static::ord(mb_substr($str, $offset, 1, $encoding), $encoding);
+ }
+
+ /**
+ * is surrogate pair char
+ *
+ * @param string $char
+ * @return bool
+ */
+ public static function isSurrogatePair($char)
+ {
+ return preg_match('/[\\x{10000}-\\x{10FFFF}]/u', $char);
+ }
+
+ /**
+ * get the character code count
+ *
+ * @param $string
+ * @param string $encoding
+ * @return int
+ */
+ public static function charCount($string, $encoding = 'UTF-8')
+ {
+ $count = 0;
+ $strlen = static::strlen($string);
+
+ for ($offset = 0; $offset < $strlen; $offset++) {
+ $char = static::substr($string, $offset, 1, $encoding);
+ $count += static::isSurrogatePair($char) ? 2 : 1;
+ }
+
+ return $count;
+ }
+}
diff --git a/vendor/nojimage/twitter-text-php/lib/Twitter/Text/TldLists.php b/vendor/nojimage/twitter-text-php/lib/Twitter/Text/TldLists.php
new file mode 100644
index 0000000000000..276bc17f4e2d7
--- /dev/null
+++ b/vendor/nojimage/twitter-text-php/lib/Twitter/Text/TldLists.php
@@ -0,0 +1,1646 @@
+
+ * @copyright Copyright © 2010, Nick Pope
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
+ * @package Twitter.Text
+ */
+
+namespace Twitter\Text;
+
+/**
+ * Twitter Validator Class
+ *
+ * Performs "validation" on tweets.
+ *
+ * Originally written by {@link http://github.com/mikenz Mike Cochrane}, this
+ * is based on code by {@link http://github.com/mzsanford Matt Sanford} and
+ * heavily modified by {@link http://github.com/ngnpope Nick Pope}.
+ *
+ * @author Nick Pope
+ * @copyright Copyright © 2010, Nick Pope
+ * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0
+ * @package Twitter.Text
+ */
+class Validator
+{
+ /**
+ *
+ * @var Extractor
+ */
+ protected $extractor;
+
+ /**
+ *
+ * @var Configuration
+ */
+ protected $config;
+
+ /**
+ * Provides fluent method chaining.
+ *
+ * @param Configuration $config A Twitter Text Configuration
+ *
+ * @see __construct()
+ *
+ * @return Validator
+ */
+ public static function create(Configuration $config = null)
+ {
+ return new self($config);
+ }
+
+ /**
+ * Reads in a tweet to be parsed and validates it.
+ *
+ * @param Configuration $config A Twitter Text Configuration
+ */
+ public function __construct(Configuration $config = null)
+ {
+ $this->setConfiguration($config);
+ $this->extractor = Extractor::create();
+ }
+
+ /**
+ * Setup configuration
+ *
+ * @see Configuration
+ *
+ * @param Configuration $config
+ * @return Validator
+ * @throws \InvalidArgumentException
+ */
+ public function setConfiguration(Configuration $config = null)
+ {
+ if ($config === null) {
+ // default use v2 config
+ $this->config = new Configuration();
+ } elseif (is_a($config, '\Twitter\Text\Configuration')) {
+ $this->config = $config;
+ } else {
+ throw new \InvalidArgumentException('Invalid Configuration');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Get current configuration
+ *
+ * @return Configuration
+ */
+ public function getConfiguration()
+ {
+ return $this->config;
+ }
+
+ /**
+ * Check whether a tweet is valid.
+ *
+ * @param string $tweet The tweet to validate.
+ * @param Configuration $config using configuration
+ * @return boolean Whether the tweet is valid.
+ * @deprecated instead use \Twitter\Text\Parser::parseText()
+ */
+ public function isValidTweetText($tweet, Configuration $config = null)
+ {
+
+ return $this->parseTweet($tweet, $config)->valid;
+ }
+
+ /**
+ * Check whether a username is valid.
+ *
+ * @param string $username The username to validate.
+ * @return boolean Whether the username is valid.
+ */
+ public function isValidUsername($username)
+ {
+ $length = StringUtils::strlen($username);
+ if (empty($username) || !$length) {
+ return false;
+ }
+ $extracted = $this->extractor->extractMentionedScreennames($username);
+ return count($extracted) === 1 && $extracted[0] === substr($username, 1);
+ }
+
+ /**
+ * Check whether a list is valid.
+ *
+ * @param string $list The list name to validate.
+ * @return boolean Whether the list is valid.
+ */
+ public function isValidList($list)
+ {
+ $length = StringUtils::strlen($list);
+ if (empty($list) || !$length) {
+ return false;
+ }
+
+ if (preg_match(Regex::getValidMentionsOrListsMatcher(), $list, $matches)) {
+ $matches = array_pad($matches, 5, '');
+
+ return $matches[1] === '' && !empty($matches[4]) && $matches[4] && $matches[5] === '';
+ }
+
+ return false;
+ }
+
+ /**
+ * Check whether a hashtag is valid.
+ *
+ * @param string $hashtag The hashtag to validate.
+ * @return boolean Whether the hashtag is valid.
+ */
+ public function isValidHashtag($hashtag)
+ {
+ $length = StringUtils::strlen($hashtag);
+ if (empty($hashtag) || !$length) {
+ return false;
+ }
+ $extracted = $this->extractor->extractHashtags($hashtag);
+ return count($extracted) === 1 && $extracted[0] === substr($hashtag, 1);
+ }
+
+ /**
+ * Check whether a URL is valid.
+ *
+ * @param string $url The url to validate.
+ * @param boolean $unicode_domains Consider the domain to be unicode.
+ * @param boolean $require_protocol Require a protocol for valid domain?
+ *
+ * @return boolean Whether the URL is valid.
+ */
+ public function isValidURL($url, $unicode_domains = true, $require_protocol = true)
+ {
+ $length = StringUtils::strlen($url);
+ if (empty($url) || !$length) {
+ return false;
+ }
+
+ preg_match(Regex::getValidateUrlUnencodedMatcher(), $url, $matches);
+ $match = array_shift($matches);
+ if (!$matches || $match !== $url) {
+ return false;
+ }
+
+ list($scheme, $authority, $path, $query, $fragment) = array_pad($matches, 5, '');
+
+ # Check scheme, path, query, fragment:
+ if (
+ ($require_protocol && !(
+ self::isValidMatch($scheme, Regex::getValidateUrlSchemeMatcher())
+ && preg_match('/^https?$/i', $scheme)
+ ))
+ || !self::isValidMatch($path, Regex::getValidateUrlPathMatcher())
+ || !self::isValidMatch($query, Regex::getValidateUrlQueryMatcher(), true)
+ || !self::isValidMatch($fragment, Regex::getValidateUrlFragmentMatcher(), true)
+ ) {
+ return false;
+ }
+
+ # Check authority:
+ $authorityPattern = $unicode_domains ?
+ Regex::getValidateUrlUnicodeAuthorityMatcher() :
+ Regex::getValidateUrlAuthorityMatcher();
+
+ return self::isValidMatch($authority, $authorityPattern);
+ }
+
+ /**
+ * Determines the length of a tweet. Takes shortening of URLs into account.
+ *
+ * @param string $tweet The tweet to validate.
+ * @param Configuration $config using configuration
+ * @return int the length of a tweet.
+ * @deprecated instead use \Twitter\Text\Parser::parseTweet()
+ */
+ public function getTweetLength($tweet, Configuration $config = null)
+ {
+ return $this->parseTweet($tweet, $config)->weightedLength;
+ }
+
+ /**
+ * A helper function to check for a valid match. Used in URL validation.
+ *
+ * @param string $string The subject string to test.
+ * @param string $pattern The pattern to match against.
+ * @param boolean $optional Whether a match is compulsory or not.
+ *
+ * @return boolean Whether an exact match was found.
+ */
+ protected static function isValidMatch($string, $pattern, $optional = false)
+ {
+ $found = preg_match($pattern, $string, $matches);
+ if (!$optional) {
+ return (($string || $string === '') && $found && $matches[0] === $string);
+ }
+
+ return !(($string || $string === '') && (!$found || $matches[0] !== $string));
+ }
+
+ /**
+ * Parse tweet
+ *
+ * @param string $tweet The tweet to parse.
+ * @param Configuration|null $config using configuration
+ * @return ParseResults
+ */
+ private function parseTweet($tweet, Configuration $config = null)
+ {
+ if ($config === null) {
+ $config = $this->config;
+ }
+
+ return Parser::create($config)->parseTweet($tweet);
+ }
+}