| @@ -1,2 +1,3 @@ | ||
| 1 | -(()=>{"use strict";const e={n:t=>{const s=t&&t.__esModule?()=>t.default:()=>t;return e.d(s,{a:s}),s},d:(t,s)=>{if(Array.isArray(s))for(var r=0;r<s.length;){var n=s[r++],a=s[r++];e.o(t,n)?0===a&&r++:0===a?Object.defineProperty(t,n,{enumerable:!0,value:s[r++]}):Object.defineProperty(t,n,{enumerable:!0,get:a})}else for(var n in s)e.o(s,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:s[n]})},o:(e,t)=>Object.hasOwn(e,t)},t=window.wp.element,s=window.wp.i18n,r=window.wp.apiFetch;var n=e.n(r);const a=window.wp.components,l=window.ReactJSXRuntime;function i({tabs:e,active:s,onSelect:r}){const n=(0,t.useRef)(null),[a,i]=(0,t.useState)(!1);return(0,t.useEffect)(()=>{const e=n.current;if(!e)return;const t=e.querySelector(".is-active");if(t){const s=t.offsetLeft+t.offsetWidth-(e.scrollLeft+e.clientWidth),r=e.scrollLeft-t.offsetLeft;s>0?e.scrollLeft+=s+16:r>0&&(e.scrollLeft-=r+16)}const s=()=>{const t=e.scrollWidth-e.scrollLeft-e.clientWidth;i(t<=1)};return s(),e.addEventListener("scroll",s,{passive:!0}),window.addEventListener("resize",s),()=>{e.removeEventListener("scroll",s),window.removeEventListener("resize",s)}},[s,e]),(0,l.jsx)("div",{className:"cryptx-tabs"+(a?" is-scrolled-end":""),role:"tablist",ref:n,children:e.map((t,a)=>{const i=t.id===s;return(0,l.jsx)("button",{type:"button",role:"tab",id:`cryptx-tab-${t.id}`,"aria-selected":i,"aria-controls":`cryptx-panel-${t.id}`,tabIndex:i?0:-1,className:"cryptx-tabs__tab"+(i?" is-active":""),onClick:()=>r(t.id),onKeyDown:t=>((t,s)=>{let a=null;"ArrowRight"===t.key?a=(s+1)%e.length:"ArrowLeft"===t.key?a=(s-1+e.length)%e.length:"Home"===t.key?a=0:"End"===t.key&&(a=e.length-1),null!==a&&(t.preventDefault(),r(e[a].id),n.current?.querySelectorAll('[role="tab"]')[a]?.focus())})(t,a),children:t.label},t.id)})})}function c({field:e,value:r,onChange:n}){const[i,c]=(0,t.useState)(null),o=`cryptx-field-${e.key}`;switch(e.type){case"boolean":return(0,l.jsx)(a.ToggleControl,{__nextHasNoMarginBottom:!0,label:e.label,help:e.help,checked:!!Number(r),onChange:t=>n(e.key,t?1:0)});case"choice":{const t=e.choices.map(e=>({label:e.label,value:String(e.value)}));return t.length<=4?(0,l.jsx)(a.RadioControl,{label:e.label,help:e.help,selected:String(r),options:t,onChange:t=>n(e.key,t)}):(0,l.jsx)(a.SelectControl,{__nextHasNoMarginBottom:!0,__next40pxDefaultSize:!0,label:e.label,help:e.help,value:String(r),options:t,onChange:t=>n(e.key,t)})}case"integer":return(0,l.jsx)(a.TextControl,{__nextHasNoMarginBottom:!0,__next40pxDefaultSize:!0,type:"number",min:e.min,max:e.max,label:e.label,help:e.help,value:r,onChange:t=>n(e.key,t)});case"idlist":return(0,l.jsx)(a.TextareaControl,{__nextHasNoMarginBottom:!0,label:e.label,help:e.help,value:r,rows:3,onChange:t=>n(e.key,t)});case"color":return(0,l.jsx)(a.BaseControl,{__nextHasNoMarginBottom:!0,id:o,label:e.label,help:e.help,children:(0,l.jsxs)("div",{className:"cryptx-color",children:[(0,l.jsx)("input",{id:o,type:"color",value:r||"#000000",onChange:t=>n(e.key,t.target.value)}),(0,l.jsx)(a.TextControl,{__nextHasNoMarginBottom:!0,__next40pxDefaultSize:!0,label:(0,s.__)("Hex value","cryptx"),hideLabelFromVision:!0,value:r,onChange:t=>n(e.key,t)})]})});case"media":return(0,l.jsx)(a.BaseControl,{__nextHasNoMarginBottom:!0,id:o,label:e.label,help:e.help,children:(0,l.jsxs)(a.__experimentalVStack,{spacing:2,className:"cryptx-media",children:[i&&(0,l.jsx)("img",{src:i,alt:"",className:"cryptx-media__preview"}),(0,l.jsxs)("div",{className:"cryptx-media__buttons",children:[(0,l.jsx)(a.Button,{variant:"secondary",__next40pxDefaultSize:!0,onClick:()=>function(){if(!window.wp?.media)return;const t=window.wp.media({title:(0,s.__)("Choose an image","cryptx"),multiple:!1,library:{type:"image"}});t.on("select",()=>{const s=t.state().get("selection").first().toJSON();var r,a;r=s.id,a=s.url,n(e.key,r),c(a)}),t.open()}(),children:Number(r)?(0,s.__)("Replace image","cryptx"):(0,s.__)("Choose image","cryptx")}),!!Number(r)&&(0,l.jsx)(a.Button,{variant:"tertiary",isDestructive:!0,__next40pxDefaultSize:!0,onClick:()=>{n(e.key,0),c(null)},children:(0,s.__)("Remove","cryptx")})]})]})});case"url":return(0,l.jsx)(a.TextControl,{__nextHasNoMarginBottom:!0,__next40pxDefaultSize:!0,type:"url",label:e.label,help:e.help,value:r,onChange:t=>n(e.key,t)});default:return(0,l.jsx)(a.TextControl,{__nextHasNoMarginBottom:!0,__next40pxDefaultSize:!0,label:e.label,help:e.help,value:r,onChange:t=>n(e.key,t)})}}const o={none:{tone:"good",text:()=>(0,s.__)("No readable address is left in the source.","cryptx")},encoded:{tone:"warning",text:()=>(0,s.__)("The address is in the source as HTML entities. That defeats a simple scanner, but any spam bot that decodes entities will read it.","cryptx")},plain:{tone:"bad",text:()=>(0,s.__)("An address is still plainly readable in the source with these settings.","cryptx")}};function d({values:e}){const[r,i]=(0,t.useState)(null),[c,d]=(0,t.useState)(!1),[p,x]=(0,t.useState)(null),h=(0,t.useRef)(0);return(0,t.useEffect)(()=>{const t=setTimeout(()=>{const t=++h.current;d(!0),n()({path:"/cryptx/v1/preview",method:"POST",data:{values:e}}).then(e=>{t===h.current&&(i(e),x(null))}).catch(e=>{t===h.current&&x(e?.message||(0,s.__)("The preview could not be generated.","cryptx"))}).finally(()=>{t===h.current&&d(!1)})},400);return()=>clearTimeout(t)},[e]),(0,l.jsxs)(a.Card,{className:"cryptx-preview",children:[(0,l.jsxs)(a.CardHeader,{children:[(0,l.jsx)("h2",{className:"cryptx-section__title",children:(0,s.__)("Preview","cryptx")}),c&&(0,l.jsx)(a.Spinner,{})]}),(0,l.jsxs)(a.CardBody,{children:[p&&(0,l.jsx)("p",{className:"cryptx-preview__error",children:p}),r&&(0,l.jsxs)(l.Fragment,{children:[(0,l.jsxs)("div",{className:"cryptx-preview__block",children:[(0,l.jsx)("h3",{children:(0,s.__)("What visitors see","cryptx")}),(0,l.jsx)("div",{className:"cryptx-preview__rendered",dangerouslySetInnerHTML:{__html:r.markup}})]}),(0,l.jsxs)("div",{className:"cryptx-preview__block",children:[(0,l.jsx)("h3",{children:(0,s.__)("What a spam bot finds in the source","cryptx")}),(0,l.jsx)("pre",{className:"cryptx-preview__source",children:(0,l.jsx)("code",{children:r.markup})})]}),(0,l.jsx)("p",{className:"cryptx-preview__verdict is-"+(o[r.exposure]||o.none).tone,children:(o[r.exposure]||o.none).text()})]})]})]})}function p({tab:e,fields:t,values:s,onChange:r}){const n=t.filter(t=>t.tab===e&&function(e,t){return!e.depends||Object.entries(e.depends).every(([e,s])=>t[e]==s)}(t,s)),i=[];return n.forEach(e=>{const t=i.find(t=>t.title===e.section);t?t.fields.push(e):i.push({title:e.section,fields:[e]})}),(0,l.jsxs)("div",{className:"cryptx-tab-panel",role:"tabpanel",id:`cryptx-panel-${e}`,"aria-labelledby":`cryptx-tab-${e}`,children:["appearance"===e&&(0,l.jsx)(d,{values:s}),i.map(e=>(0,l.jsxs)(a.Card,{className:"cryptx-section",children:[(0,l.jsx)(a.CardHeader,{children:(0,l.jsx)("h2",{className:"cryptx-section__title",children:e.title})}),(0,l.jsx)(a.CardBody,{children:(0,l.jsx)("div",{className:"cryptx-section__fields",children:e.fields.map(e=>(0,l.jsx)("div",{className:"cryptx-field",children:(0,l.jsx)(c,{field:e,value:s[e.key],onChange:r})},e.key))})})]},e.title))]})}function x({children:e}){return(0,l.jsx)("pre",{className:"cryptx-help__snippet",children:(0,l.jsx)("code",{children:e})})}function h({release:e,isCurrent:t}){return(0,l.jsxs)("section",{className:"cryptx-help__release",children:[(0,l.jsxs)("h3",{children:[e.version,t&&(0,l.jsx)("span",{className:"cryptx-help__badge",children:(0,s.__)("installed","cryptx")})]}),(0,l.jsx)("ul",{children:e.items.map((e,t)=>(0,l.jsx)("li",{dangerouslySetInnerHTML:{__html:e}},t))})]})}function u(){const[e,r]=(0,t.useState)(null);if((0,t.useEffect)(()=>{n()({path:"/cryptx/v1/changelog"}).then(e=>r(e)).catch(()=>r({releases:[],current:""}))},[]),null===e)return(0,l.jsx)("p",{children:(0,s.__)("Loading…","cryptx")});const{releases:a,current:i}=e;if(!a||0===a.length)return(0,l.jsx)("p",{children:(0,s.__)("No changelog available.","cryptx")});const[c,...o]=a;return(0,l.jsxs)("div",{className:"cryptx-help__changelog",children:[(0,l.jsx)(h,{release:c,isCurrent:c.version===i}),o.length>0&&(0,l.jsxs)("details",{className:"cryptx-help__older",children:[(0,l.jsx)("summary",{children:(0,s.sprintf)(/* translators: %d: number of older releases */ /* translators: %d: number of older releases */ | |
| 2 | -(0,s._n)("Show %d earlier release","Show %d earlier releases",o.length,"cryptx"),o.length)}),o.map(e=>(0,l.jsx)(h,{release:e,isCurrent:e.version===i},e.version))]})]})}function y(){return(0,l.jsxs)("div",{className:"cryptx-tab-panel",role:"tabpanel",id:"cryptx-panel-help","aria-labelledby":"cryptx-tab-help",children:[(0,l.jsxs)(a.Card,{className:"cryptx-section",children:[(0,l.jsx)(a.CardHeader,{children:(0,l.jsx)("h2",{className:"cryptx-section__title",children:(0,s.__)("Protect a single address","cryptx")})}),(0,l.jsxs)(a.CardBody,{children:[(0,l.jsx)("p",{children:(0,s.__)("The shortcode works even in posts you excluded from CryptX, which makes it the way to protect one address in otherwise untouched content.","cryptx")}),(0,l.jsx)(x,{children:"[cryptx]info@example.com[/cryptx]"}),(0,l.jsx)("p",{children:(0,s.__)("Any setting from this screen can be overridden for a single shortcode by using its option name, written in lower case:","cryptx")}),(0,l.jsx)(x,{children:'[cryptx opt_linktext="1" alt_linktext="Contact us"]info@example.com[/cryptx]'}),(0,l.jsx)("p",{children:(0,s.__)("On top of the settings, four attributes describe the mail itself. They end up inside the encrypted link, so they stay hidden from spam bots just like the address:","cryptx")}),(0,l.jsx)(x,{children:'[cryptx subject="Price enquiry" cc="sales@example.com"]info@example.com[/cryptx]'}),(0,l.jsx)("p",{children:(0,s.__)('Available are subject, body, cc and bcc — the headers RFC 6068 allows in a mailto link. Anything else is dropped. A link you wrote yourself with its own "?subject=" keeps it; the attribute only fills in where nothing is set.',"cryptx")}),(0,l.jsx)("p",{className:"cryptx-help__note",children:(0,s.__)('The shortcode needs an address between its tags. Written self-closing, as [cryptx subject="…" /], there is nothing to protect and nothing is output — the attributes go nowhere.',"cryptx")}),(0,l.jsx)("p",{className:"cryptx-help__note",children:(0,s.__)('Until 4.1.0 the attribute "subject" was accepted and then silently discarded, and an address written as "info@example.com?subject=…" lost its subject on the way as well. Both work now. The attribute "linktext", also listed by older versions of this page, never existed — use alt_linktext.',"cryptx")})]})]}),(0,l.jsxs)(a.Card,{className:"cryptx-section",children:[(0,l.jsx)(a.CardHeader,{children:(0,l.jsx)("h2",{className:"cryptx-section__title",children:(0,s.__)("In a theme","cryptx")})}),(0,l.jsxs)(a.CardBody,{children:[(0,l.jsx)(x,{children:"<?php echo cryptx_encrypt( 'info@example.com' ); ?>"}),(0,l.jsx)("p",{children:(0,s.__)("Takes an optional array of settings as its second argument, using the same option names as the shortcode.","cryptx")}),(0,l.jsx)("p",{className:"cryptx-help__note",children:(0,s.__)("The older function encryptx() still exists but is deprecated and will be removed in a future major release.","cryptx")})]})]}),(0,l.jsxs)(a.Card,{className:"cryptx-section",children:[(0,l.jsx)(a.CardHeader,{children:(0,l.jsx)("h2",{className:"cryptx-section__title",children:(0,s.__)("In your own JavaScript","cryptx")})}),(0,l.jsxs)(a.CardBody,{children:[(0,l.jsx)("p",{children:(0,s.__)("For addresses that are added to the page after it loaded, and therefore never passed through the server side filters.","cryptx")}),(0,l.jsx)(x,{children:"const link = document.createElement( 'a' );\nlink.href = generateDeCryptXHandler( 'info@example.com' );\nlink.textContent = 'Contact us';"})]})]}),(0,l.jsxs)(a.Card,{className:"cryptx-section",children:[(0,l.jsx)(a.CardHeader,{children:(0,l.jsx)("h2",{className:"cryptx-section__title",children:(0,s.__)("What changed","cryptx")})}),(0,l.jsxs)(a.CardBody,{children:[(0,l.jsx)(u,{}),(0,l.jsx)("p",{children:(0,l.jsx)(a.ExternalLink,{href:"https://wordpress.org/plugins/cryptx/#developers",children:(0,s.__)("Full history on wordpress.org","cryptx")})})]})]})]})}const _="help";function m(e){const t=new URLSearchParams(window.location.search).get("tab");return[...e.map(e=>e.id),_].includes(t)?t:e[0]?.id}function f(){const[e,r]=(0,t.useState)(null),[c,o]=(0,t.useState)(null),[d,x]=(0,t.useState)(null),[h,u]=(0,t.useState)(null),[f,j]=(0,t.useState)(null),[b,g]=(0,t.useState)(!1),[v,w]=(0,t.useState)(null),[C,N]=(0,t.useState)(!1);(0,t.useEffect)(()=>{n()({path:"/cryptx/v1/settings"}).then(e=>{r(e.schema),o(e.values),x(e.values),u(m(e.schema.tabs))}).catch(e=>{w(e?.message||(0,s.__)("The settings could not be loaded.","cryptx"))})},[]);const k=(0,t.useMemo)(()=>!(!c||!d)&&Object.keys(c).some(e=>String(c[e])!==String(d[e])),[c,d]);(0,t.useEffect)(()=>{if(!k)return;const e=e=>{e.preventDefault(),e.returnValue=""};return window.addEventListener("beforeunload",e),()=>window.removeEventListener("beforeunload",e)},[k]),(0,t.useEffect)(()=>{const t=()=>{e&&u(m(e.tabs))};return window.addEventListener("popstate",t),()=>window.removeEventListener("popstate",t)},[e]);const S=(0,t.useCallback)(e=>{u(e);const t=new URL(window.location.href);t.searchParams.set("tab",e),window.history.pushState({},"",t)},[]),T=(0,t.useCallback)((e,t)=>{o(s=>({...s,[e]:t}))},[]),L=(0,t.useCallback)(()=>{g(!0),j(null),n()({path:"/cryptx/v1/settings",method:"POST",data:{values:c}}).then(e=>{o(e.values),x(e.values),j({status:"success",text:e.message})}).catch(e=>{j({status:"error",text:e?.message||(0,s.__)("The settings could not be saved.","cryptx")})}).finally(()=>g(!1))},[c]),B=(0,t.useCallback)(()=>{N(!1),g(!0),n()({path:"/cryptx/v1/settings/reset",method:"POST"}).then(e=>{o(e.values),x(e.values),j({status:"success",text:e.message})}).catch(e=>{j({status:"error",text:e?.message||(0,s.__)("The settings could not be reset.","cryptx")})}).finally(()=>g(!1))},[]);if(v)return(0,l.jsx)(a.Notice,{status:"error",isDismissible:!1,children:v});if(!e||!c)return(0,l.jsxs)("div",{className:"cryptx-loading",children:[(0,l.jsx)(a.Spinner,{}),(0,l.jsx)("span",{children:(0,s.__)("Loading the settings…","cryptx")})]});const H=[...e.tabs,{id:_,label:(0,s.__)("Help","cryptx"),description:(0,s.__)("Shortcode, template functions and what changed in each release.","cryptx")}],E=H.find(e=>e.id===h)||H[0];return(0,l.jsxs)("div",{className:"cryptx-settings",children:[(0,l.jsxs)("header",{className:"cryptx-settings__header",children:[(0,l.jsx)("h1",{children:(0,s.__)("CryptX","cryptx")}),(0,l.jsx)("p",{className:"cryptx-settings__intro",children:(0,s.__)("CryptX hides email addresses in your pages from spam bots while keeping them usable for your visitors.","cryptx")})]}),(0,l.jsx)(i,{tabs:H,active:E.id,onSelect:S}),f&&(0,l.jsx)(a.Notice,{status:f.status,onRemove:()=>j(null),children:f.text}),(0,l.jsx)("p",{className:"cryptx-settings__tab-description",children:E.description}),(0,l.jsx)("main",{className:"cryptx-settings__body",children:E.id===_?(0,l.jsx)(y,{}):(0,l.jsx)(p,{tab:E.id,fields:e.fields,values:c,onChange:T})}),E.id!==_&&(0,l.jsxs)("div",{className:"cryptx-settings__actions"+(k?" is-dirty":""),children:[(0,l.jsxs)(a.Flex,{justify:"space-between",wrap:!0,children:[(0,l.jsx)(a.FlexItem,{children:(0,l.jsx)(a.Button,{variant:"primary",onClick:L,isBusy:b,disabled:b||!k,__next40pxDefaultSize:!0,children:(0,s.__)("Save changes","cryptx")})}),(0,l.jsx)(a.FlexItem,{children:(0,l.jsx)(a.Button,{variant:"tertiary",isDestructive:!0,onClick:()=>N(!0),disabled:b,__next40pxDefaultSize:!0,children:(0,s.__)("Restore defaults","cryptx")})})]}),(0,l.jsx)("p",{className:"cryptx-settings__status"+(k?" is-dirty":""),"aria-live":"polite",children:k?(0,s.__)("You have unsaved changes.","cryptx"):(0,s.__)("All changes saved.","cryptx")})]}),(0,l.jsx)(a.__experimentalConfirmDialog,{isOpen:C,onConfirm:B,onCancel:()=>N(!1),confirmButtonText:(0,s.__)("Restore defaults","cryptx"),children:(0,s.__)("This puts every CryptX setting back to its default, on every tab. Addresses already delivered in cached pages keep working. Continue?","cryptx")})]})}document.addEventListener("DOMContentLoaded",()=>{const e=document.getElementById("cryptx-settings-root");e&&(e.textContent="",e.setAttribute("aria-busy","false"),e.setAttribute("aria-label",(0,s.__)("CryptX settings","cryptx")),(0,t.createRoot)(e).render((0,l.jsx)(t.StrictMode,{children:(0,l.jsx)(f,{})})))})})(); | |
| 1 | +(()=>{"use strict";const e={n:t=>{const s=t&&t.__esModule?()=>t.default:()=>t;return e.d(s,{a:s}),s},d:(t,s)=>{if(Array.isArray(s))for(var r=0;r<s.length;){var n=s[r++],a=s[r++];e.o(t,n)?0===a&&r++:0===a?Object.defineProperty(t,n,{enumerable:!0,value:s[r++]}):Object.defineProperty(t,n,{enumerable:!0,get:a})}else for(var n in s)e.o(s,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:s[n]})},o:(e,t)=>Object.hasOwn(e,t)},t=window.wp.element,s=window.wp.i18n,r=window.wp.apiFetch;var n=e.n(r);const a=window.wp.components,i=window.ReactJSXRuntime;function l({tabs:e,active:s,onSelect:r}){const n=(0,t.useRef)(null),[a,l]=(0,t.useState)(!1);return(0,t.useEffect)(()=>{const e=n.current;if(!e)return;const t=e.querySelector(".is-active");if(t){const s=t.offsetLeft+t.offsetWidth-(e.scrollLeft+e.clientWidth),r=e.scrollLeft-t.offsetLeft;s>0?e.scrollLeft+=s+16:r>0&&(e.scrollLeft-=r+16)}const s=()=>{const t=e.scrollWidth-e.scrollLeft-e.clientWidth;l(t<=1)};return s(),e.addEventListener("scroll",s,{passive:!0}),window.addEventListener("resize",s),()=>{e.removeEventListener("scroll",s),window.removeEventListener("resize",s)}},[s,e]),(0,i.jsx)("div",{className:"cryptx-tabs"+(a?" is-scrolled-end":""),role:"tablist",ref:n,children:e.map((t,a)=>{const l=t.id===s;return(0,i.jsx)("button",{type:"button",role:"tab",id:`cryptx-tab-${t.id}`,"aria-selected":l,"aria-controls":`cryptx-panel-${t.id}`,tabIndex:l?0:-1,className:"cryptx-tabs__tab"+(l?" is-active":""),onClick:()=>r(t.id),onKeyDown:t=>((t,s)=>{let a=null;"ArrowRight"===t.key?a=(s+1)%e.length:"ArrowLeft"===t.key?a=(s-1+e.length)%e.length:"Home"===t.key?a=0:"End"===t.key&&(a=e.length-1),null!==a&&(t.preventDefault(),r(e[a].id),n.current?.querySelectorAll('[role="tab"]')[a]?.focus())})(t,a),children:t.label},t.id)})})}function c({field:e,value:r,onChange:n}){const[l,c]=(0,t.useState)(null),o=`cryptx-field-${e.key}`;switch(e.type){case"boolean":return(0,i.jsx)(a.ToggleControl,{__nextHasNoMarginBottom:!0,label:e.label,help:e.help,checked:!!Number(r),onChange:t=>n(e.key,t?1:0)});case"choice":{const t=e.choices.map(e=>({label:e.label,value:String(e.value)}));return t.length<=4?(0,i.jsx)(a.RadioControl,{label:e.label,help:e.help,selected:String(r),options:t,onChange:t=>n(e.key,t)}):(0,i.jsx)(a.SelectControl,{__nextHasNoMarginBottom:!0,__next40pxDefaultSize:!0,label:e.label,help:e.help,value:String(r),options:t,onChange:t=>n(e.key,t)})}case"integer":return(0,i.jsx)(a.TextControl,{__nextHasNoMarginBottom:!0,__next40pxDefaultSize:!0,type:"number",min:e.min,max:e.max,label:e.label,help:e.help,value:r,onChange:t=>n(e.key,t)});case"idlist":case"addresslist":return(0,i.jsx)(a.TextareaControl,{__nextHasNoMarginBottom:!0,label:e.label,help:e.help,value:r,rows:3,onChange:t=>n(e.key,t)});case"color":return(0,i.jsx)(a.BaseControl,{__nextHasNoMarginBottom:!0,id:o,label:e.label,help:e.help,children:(0,i.jsxs)("div",{className:"cryptx-color",children:[(0,i.jsx)("input",{id:o,type:"color",value:r||"#000000",onChange:t=>n(e.key,t.target.value)}),(0,i.jsx)(a.TextControl,{__nextHasNoMarginBottom:!0,__next40pxDefaultSize:!0,label:(0,s.__)("Hex value","cryptx"),hideLabelFromVision:!0,value:r,onChange:t=>n(e.key,t)})]})});case"media":return(0,i.jsx)(a.BaseControl,{__nextHasNoMarginBottom:!0,id:o,label:e.label,help:e.help,children:(0,i.jsxs)(a.__experimentalVStack,{spacing:2,className:"cryptx-media",children:[l&&(0,i.jsx)("img",{src:l,alt:"",className:"cryptx-media__preview"}),(0,i.jsxs)("div",{className:"cryptx-media__buttons",children:[(0,i.jsx)(a.Button,{variant:"secondary",__next40pxDefaultSize:!0,onClick:()=>function(){if(!window.wp?.media)return;const t=window.wp.media({title:(0,s.__)("Choose an image","cryptx"),multiple:!1,library:{type:"image"}});t.on("select",()=>{const s=t.state().get("selection").first().toJSON();var r,a;r=s.id,a=s.url,n(e.key,r),c(a)}),t.open()}(),children:Number(r)?(0,s.__)("Replace image","cryptx"):(0,s.__)("Choose image","cryptx")}),!!Number(r)&&(0,i.jsx)(a.Button,{variant:"tertiary",isDestructive:!0,__next40pxDefaultSize:!0,onClick:()=>{n(e.key,0),c(null)},children:(0,s.__)("Remove","cryptx")})]})]})});case"url":return(0,i.jsx)(a.TextControl,{__nextHasNoMarginBottom:!0,__next40pxDefaultSize:!0,type:"url",label:e.label,help:e.help,value:r,onChange:t=>n(e.key,t)});default:return(0,i.jsx)(a.TextControl,{__nextHasNoMarginBottom:!0,__next40pxDefaultSize:!0,label:e.label,help:e.help,value:r,onChange:t=>n(e.key,t)})}}const o={none:{tone:"good",text:()=>(0,s.__)("No readable address is left in the source.","cryptx")},encoded:{tone:"warning",text:()=>(0,s.__)("The address is in the source as HTML entities. That defeats a simple scanner, but any spam bot that decodes entities will read it.","cryptx")},plain:{tone:"bad",text:()=>(0,s.__)("An address is still plainly readable in the source with these settings.","cryptx")}};function d({values:e}){const[r,l]=(0,t.useState)(null),[c,d]=(0,t.useState)(!1),[h,p]=(0,t.useState)(null),x=(0,t.useRef)(0);return(0,t.useEffect)(()=>{const t=setTimeout(()=>{const t=++x.current;d(!0),n()({path:"/cryptx/v1/preview",method:"POST",data:{values:e}}).then(e=>{t===x.current&&(l(e),p(null))}).catch(e=>{t===x.current&&p(e?.message||(0,s.__)("The preview could not be generated.","cryptx"))}).finally(()=>{t===x.current&&d(!1)})},400);return()=>clearTimeout(t)},[e]),(0,i.jsxs)(a.Card,{className:"cryptx-preview",children:[(0,i.jsxs)(a.CardHeader,{children:[(0,i.jsx)("h2",{className:"cryptx-section__title",children:(0,s.__)("Preview","cryptx")}),c&&(0,i.jsx)(a.Spinner,{})]}),(0,i.jsxs)(a.CardBody,{children:[h&&(0,i.jsx)("p",{className:"cryptx-preview__error",children:h}),r&&(0,i.jsxs)(i.Fragment,{children:[(0,i.jsxs)("div",{className:"cryptx-preview__block",children:[(0,i.jsx)("h3",{children:(0,s.__)("What visitors see","cryptx")}),(0,i.jsx)("div",{className:"cryptx-preview__rendered",dangerouslySetInnerHTML:{__html:r.markup}})]}),(0,i.jsxs)("div",{className:"cryptx-preview__block",children:[(0,i.jsx)("h3",{children:(0,s.__)("What a spam bot finds in the source","cryptx")}),(0,i.jsx)("pre",{className:"cryptx-preview__source",children:(0,i.jsx)("code",{children:r.markup})})]}),(0,i.jsx)("p",{className:"cryptx-preview__verdict is-"+(o[r.exposure]||o.none).tone,children:(o[r.exposure]||o.none).text()})]})]})]})}function h({rotatedAt:e}){const[r,l]=(0,t.useState)(!1),[c,o]=(0,t.useState)(null),[d,h]=(0,t.useState)(e||""),[p,x]=(0,t.useState)(!1);return(0,i.jsxs)(a.Card,{className:"cryptx-section",children:[(0,i.jsx)(a.CardHeader,{children:(0,i.jsx)("h2",{className:"cryptx-section__title",children:(0,s.__)("Encryption secrets","cryptx")})}),(0,i.jsxs)(a.CardBody,{children:[(0,i.jsx)("p",{children:(0,s.__)("CryptX keeps two secrets. One is used to encrypt the links and travels inside each of them, so that a visitor's browser can open it. The other never leaves your server and stands in for the address when one is drawn as a picture.","cryptx")}),(0,i.jsx)("p",{children:(0,s.__)("Replacing them is safe and rarely necessary — after restoring a backup that may have been seen by someone else, for instance. Links that are already published keep working, because each one carries the key it was made with. Pictures are opened on the server, so the old secret is kept for 30 days and the pictures in pages that are still cached go on working until then. Only one old secret is kept, so replacing them a second time inside those 30 days ends the grace period for the first — which is also how you cut it short deliberately, if the old secret needs to stop working now.","cryptx")}),d&&(0,i.jsx)("p",{className:"cryptx-secrets__last",children:(0,s.sprintf)(/* translators: %s: a date */ /* translators: %s: a date */ | |
| 2 | +(0,s.__)("Last replaced on %s.","cryptx"),d)}),c&&(0,i.jsx)(a.Notice,{status:c.status,isDismissible:!1,className:"cryptx-secrets__notice",children:c.text}),p?(0,i.jsxs)("div",{className:"cryptx-secrets__confirm",children:[(0,i.jsx)("p",{children:(0,s.__)("Replace both secrets now?","cryptx")}),(0,i.jsx)(a.Button,{variant:"primary",isDestructive:!0,onClick:()=>{l(!0),x(!1),n()({path:"/cryptx/v1/secrets/rotate",method:"POST"}).then(e=>{o({status:"success",text:e.message}),h(e.secretsRotatedAt||"")}).catch(e=>o({status:"error",text:e?.message||(0,s.__)("The secrets could not be replaced.","cryptx")})).finally(()=>l(!1))},isBusy:r,disabled:r,__next40pxDefaultSize:!0,children:(0,s.__)("Yes, replace them","cryptx")}),(0,i.jsx)(a.Button,{variant:"tertiary",onClick:()=>x(!1),disabled:r,__next40pxDefaultSize:!0,children:(0,s.__)("Cancel","cryptx")})]}):(0,i.jsx)(a.Button,{variant:"secondary",onClick:()=>x(!0),disabled:r,__next40pxDefaultSize:!0,children:(0,s.__)("Create new secrets","cryptx")})]})]})}function p({tab:e,fields:t,values:s,onChange:r,rotatedAt:n,isNetwork:l=!1}){const o=t.filter(t=>t.tab===e&&function(e,t){return!e.depends||Object.entries(e.depends).every(([e,s])=>t[e]==s)}(t,s)),p=[];return o.forEach(e=>{const t=p.find(t=>t.title===e.section);t?t.fields.push(e):p.push({title:e.section,fields:[e]})}),(0,i.jsxs)("div",{className:"cryptx-tab-panel",role:"tabpanel",id:`cryptx-panel-${e}`,"aria-labelledby":`cryptx-tab-${e}`,children:["appearance"===e&&!l&&(0,i.jsx)(d,{values:s}),p.map(e=>(0,i.jsxs)(a.Card,{className:"cryptx-section",children:[(0,i.jsx)(a.CardHeader,{children:(0,i.jsx)("h2",{className:"cryptx-section__title",children:e.title})}),(0,i.jsx)(a.CardBody,{children:(0,i.jsx)("div",{className:"cryptx-section__fields",children:e.fields.map(e=>(0,i.jsx)("div",{className:"cryptx-field",children:(0,i.jsx)(c,{field:e,value:s[e.key],onChange:r})},e.key))})})]},e.title)),"advanced"===e&&!l&&(0,i.jsx)(h,{rotatedAt:n})]})}function x({children:e}){return(0,i.jsx)("pre",{className:"cryptx-help__snippet",children:(0,i.jsx)("code",{children:e})})}function u({release:e,isCurrent:t}){return(0,i.jsxs)("section",{className:"cryptx-help__release",children:[(0,i.jsxs)("h3",{children:[e.version,t&&(0,i.jsx)("span",{className:"cryptx-help__badge",children:(0,s.__)("installed","cryptx")})]}),(0,i.jsx)("ul",{children:e.items.map((e,t)=>(0,i.jsx)("li",{dangerouslySetInnerHTML:{__html:e}},t))})]})}function y(){const[e,r]=(0,t.useState)(null);if((0,t.useEffect)(()=>{n()({path:"/cryptx/v1/changelog"}).then(e=>r(e)).catch(()=>r({releases:[],current:""}))},[]),null===e)return(0,i.jsx)("p",{children:(0,s.__)("Loading…","cryptx")});const{releases:a,current:l}=e;if(!a||0===a.length)return(0,i.jsx)("p",{children:(0,s.__)("No changelog available.","cryptx")});const[c,...o]=a;return(0,i.jsxs)("div",{className:"cryptx-help__changelog",children:[(0,i.jsx)(u,{release:c,isCurrent:c.version===l}),o.length>0&&(0,i.jsxs)("details",{className:"cryptx-help__older",children:[(0,i.jsx)("summary",{children:(0,s.sprintf)(/* translators: %d: number of older releases */ /* translators: %d: number of older releases */ | |
| 3 | +(0,s._n)("Show %d earlier release","Show %d earlier releases",o.length,"cryptx"),o.length)}),o.map(e=>(0,i.jsx)(u,{release:e,isCurrent:e.version===l},e.version))]})]})}function _(){return(0,i.jsxs)("div",{className:"cryptx-tab-panel",role:"tabpanel",id:"cryptx-panel-help","aria-labelledby":"cryptx-tab-help",children:[(0,i.jsxs)(a.Card,{className:"cryptx-section",children:[(0,i.jsx)(a.CardHeader,{children:(0,i.jsx)("h2",{className:"cryptx-section__title",children:(0,s.__)("Protect a single address","cryptx")})}),(0,i.jsxs)(a.CardBody,{children:[(0,i.jsx)("p",{children:(0,s.__)('In the editor, look for the "Protected email address" block — it does the same thing with fields instead of syntax, and it is the easier way in. The shortcode below is for everywhere a block cannot go: a widget, a custom field, a theme template.',"cryptx")}),(0,i.jsx)("p",{children:(0,s.__)("Both outrank the two ways of switching CryptX off: they work in posts you excluded, and inside them no address is exempt, however the list under Exceptions reads. That makes them the way to protect one address in otherwise untouched content.","cryptx")}),(0,i.jsx)(x,{children:"[cryptx]info@example.com[/cryptx]"}),(0,i.jsx)("p",{children:(0,s.__)('Almost any setting from this screen can be overridden for a single shortcode by using its option name, written in lower case. The list of addresses to leave alone is the exception, and deliberately so: a shortcode says "protect this one", so nothing inside it is ever exempt.',"cryptx")}),(0,i.jsx)(x,{children:'[cryptx opt_linktext="1" alt_linktext="Contact us"]info@example.com[/cryptx]'}),(0,i.jsx)("p",{children:(0,s.__)("On top of the settings, four attributes describe the mail itself. They end up inside the encrypted link, so they stay hidden from spam bots just like the address:","cryptx")}),(0,i.jsx)(x,{children:'[cryptx subject="Price enquiry" cc="sales@example.com"]info@example.com[/cryptx]'}),(0,i.jsx)("p",{children:(0,s.__)('Available are subject, body, cc and bcc — the headers RFC 6068 allows in a mailto link. Anything else is dropped. A link you wrote yourself with its own "?subject=" keeps it; the attribute only fills in where nothing is set.',"cryptx")}),(0,i.jsx)("p",{className:"cryptx-help__note",children:(0,s.__)('The shortcode needs an address between its tags. Written self-closing, as [cryptx subject="…" /], there is nothing to protect and nothing is output — the attributes go nowhere.',"cryptx")}),(0,i.jsx)("p",{className:"cryptx-help__note",children:(0,s.__)('Until 4.1.0 the attribute "subject" was accepted and then silently discarded, and an address written as "info@example.com?subject=…" lost its subject on the way as well. Both work now. The attribute "linktext", also listed by older versions of this page, never existed — use alt_linktext.',"cryptx")})]})]}),(0,i.jsxs)(a.Card,{className:"cryptx-section",children:[(0,i.jsx)(a.CardHeader,{children:(0,i.jsx)("h2",{className:"cryptx-section__title",children:(0,s.__)("In a theme","cryptx")})}),(0,i.jsxs)(a.CardBody,{children:[(0,i.jsx)(x,{children:"<?php echo cryptx_encrypt( 'info@example.com' ); ?>"}),(0,i.jsx)("p",{children:(0,s.__)("Takes an optional array of settings as its second argument, using the same option names as the shortcode.","cryptx")}),(0,i.jsx)("p",{className:"cryptx-help__note",children:(0,s.__)("The older function encryptx() still exists but is deprecated and will be removed in a future major release.","cryptx")})]})]}),(0,i.jsxs)(a.Card,{className:"cryptx-section",children:[(0,i.jsx)(a.CardHeader,{children:(0,i.jsx)("h2",{className:"cryptx-section__title",children:(0,s.__)("In your own JavaScript","cryptx")})}),(0,i.jsxs)(a.CardBody,{children:[(0,i.jsx)("p",{children:(0,s.__)("For addresses that are added to the page after it loaded, and therefore never passed through the server side filters.","cryptx")}),(0,i.jsx)(x,{children:"const link = document.createElement( 'a' );\nlink.href = generateDeCryptXHandler( 'info@example.com' );\nlink.textContent = 'Contact us';"})]})]}),(0,i.jsxs)(a.Card,{className:"cryptx-section",children:[(0,i.jsx)(a.CardHeader,{children:(0,i.jsx)("h2",{className:"cryptx-section__title",children:(0,s.__)("On the command line","cryptx")})}),(0,i.jsxs)(a.CardBody,{children:[(0,i.jsx)("p",{children:(0,s.__)("With WP-CLI installed. The first two read and write the settings, through the same validation this screen uses.","cryptx")}),(0,i.jsx)(x,{children:"wp cryptx settings\nwp cryptx settings opt_linktext\nwp cryptx settings disable_rss 0"}),(0,i.jsx)("p",{children:(0,s.__)("The third is the one worth knowing about: it runs the body and the title of every published post through the filters that render it, and reports the ones that still carry a readable address. That is the question you have after changing a setting, and the preview above cannot answer it — it only ever renders a single sample.","cryptx")}),(0,i.jsx)(x,{children:"wp cryptx scan"}),(0,i.jsx)("p",{className:"cryptx-help__note",children:(0,s.__)('A verdict of "encoded" means the address is in the page as HTML entities. That is invisible to a naive scanner and plain to anything that decodes them, which is most things — it is not the same as "hidden".',"cryptx")}),(0,i.jsx)("p",{className:"cryptx-help__note",children:(0,s.__)("An address in a post title is always reported, because CryptX cannot protect one: the title reaches the document head through WordPress itself, along a path no plugin filter touches. Take it out of the title. And what the scan does not cover: widgets, comments, feeds, and whatever a theme prints on its own.","cryptx")}),(0,i.jsx)("p",{children:(0,s.__)("On a multisite network both take --url, so one shell loop covers every site:","cryptx")}),(0,i.jsx)(x,{children:"wp site list --field=url | xargs -I{} wp cryptx scan --url={}"})]})]}),(0,i.jsxs)(a.Card,{className:"cryptx-section",children:[(0,i.jsx)(a.CardHeader,{children:(0,i.jsx)("h2",{className:"cryptx-section__title",children:(0,s.__)("What changed","cryptx")})}),(0,i.jsxs)(a.CardBody,{children:[(0,i.jsx)(y,{}),(0,i.jsx)("p",{children:(0,i.jsx)(a.ExternalLink,{href:"https://wordpress.org/plugins/cryptx/#developers",children:(0,s.__)("Full history on wordpress.org","cryptx")})})]})]})]})}const m="help";function f(e){const t=new URLSearchParams(window.location.search).get("tab");return[...e.map(e=>e.id),m].includes(t)?t:e[0]?.id}function g({scope:e="site"}){const r="network"===e,c=r?"/cryptx/v1/network-defaults":"/cryptx/v1/settings",[o,d]=(0,t.useState)(null),[h,x]=(0,t.useState)(null),[u,y]=(0,t.useState)(""),[g,j]=(0,t.useState)(null),[b,v]=(0,t.useState)(null),[w,k]=(0,t.useState)(null),[C,N]=(0,t.useState)(!1),[S,T]=(0,t.useState)(null),[B,L]=(0,t.useState)(!1);(0,t.useEffect)(()=>{n()({path:c}).then(e=>{d(e.schema),x(e.values),j(e.values),y(e.secretsRotatedAt||""),v(f(e.schema.tabs))}).catch(e=>{T(e?.message||(0,s.__)("The settings could not be loaded.","cryptx"))})},[c]);const H=(0,t.useMemo)(()=>!(!h||!g)&&Object.keys(h).some(e=>String(h[e])!==String(g[e])),[h,g]);(0,t.useEffect)(()=>{if(!H)return;const e=e=>{e.preventDefault(),e.returnValue=""};return window.addEventListener("beforeunload",e),()=>window.removeEventListener("beforeunload",e)},[H]),(0,t.useEffect)(()=>{const e=()=>{o&&v(f(o.tabs))};return window.addEventListener("popstate",e),()=>window.removeEventListener("popstate",e)},[o]);const A=(0,t.useCallback)(e=>{v(e);const t=new URL(window.location.href);t.searchParams.set("tab",e),window.history.pushState({},"",t)},[]),D=(0,t.useCallback)((e,t)=>{x(s=>({...s,[e]:t}))},[]),E=(0,t.useCallback)(()=>{N(!0),k(null),n()({path:c,method:"POST",data:{values:h}}).then(e=>{x(e.values),j(e.values),k({status:"success",text:e.message})}).catch(e=>{k({status:"error",text:e?.message||(0,s.__)("The settings could not be saved.","cryptx")})}).finally(()=>N(!1))},[h,c]),O=(0,t.useCallback)(()=>{L(!1),N(!0),n()({path:"/cryptx/v1/settings/reset",method:"POST"}).then(e=>{x(e.values),j(e.values),k({status:"success",text:e.message})}).catch(e=>{k({status:"error",text:e?.message||(0,s.__)("The settings could not be reset.","cryptx")})}).finally(()=>N(!1))},[]);if(S)return(0,i.jsx)(a.Notice,{status:"error",isDismissible:!1,children:S});if(!o||!h)return(0,i.jsxs)("div",{className:"cryptx-loading",children:[(0,i.jsx)(a.Spinner,{}),(0,i.jsx)("span",{children:(0,s.__)("Loading the settings…","cryptx")})]});const R=[...o.tabs,{id:m,label:(0,s.__)("Help","cryptx"),description:(0,s.__)("Shortcode, template functions and what changed in each release.","cryptx")}],M=R.find(e=>e.id===b)||R[0];return(0,i.jsxs)("div",{className:"cryptx-settings",children:[(0,i.jsxs)("header",{className:"cryptx-settings__header",children:[(0,i.jsx)("h1",{children:r?(0,s.__)("CryptX network defaults","cryptx"):(0,s.__)("CryptX","cryptx")}),(0,i.jsx)("p",{className:"cryptx-settings__intro",children:r?(0,s.__)("What a newly created site starts with. Sites that already exist are never changed by this — every site keeps its own settings, and a site administrator can change theirs at any time. Two settings are missing here on purpose: excluded posts and the uploaded image refer to things that exist on one site only. One deserves a second look before you set it: an address on the list under Exceptions is left readable, and a network default puts it on every site created from now on.","cryptx"):(0,s.__)("CryptX hides email addresses in your pages from spam bots while keeping them usable for your visitors.","cryptx")})]}),(0,i.jsx)(l,{tabs:R,active:M.id,onSelect:A}),w&&(0,i.jsx)(a.Notice,{status:w.status,onRemove:()=>k(null),children:w.text}),(0,i.jsx)("p",{className:"cryptx-settings__tab-description",children:M.description}),(0,i.jsx)("main",{className:"cryptx-settings__body",children:M.id===m?(0,i.jsx)(_,{}):(0,i.jsx)(p,{tab:M.id,fields:o.fields,values:h,onChange:D,rotatedAt:u,isNetwork:r})}),M.id!==m&&(0,i.jsxs)("div",{className:"cryptx-settings__actions"+(H?" is-dirty":""),children:[(0,i.jsxs)(a.Flex,{justify:"space-between",wrap:!0,children:[(0,i.jsx)(a.FlexItem,{children:(0,i.jsx)(a.Button,{variant:"primary",onClick:E,isBusy:C,disabled:C||!H,__next40pxDefaultSize:!0,children:(0,s.__)("Save changes","cryptx")})}),!r&&(0,i.jsx)(a.FlexItem,{children:(0,i.jsx)(a.Button,{variant:"tertiary",isDestructive:!0,onClick:()=>L(!0),disabled:C,__next40pxDefaultSize:!0,children:(0,s.__)("Restore defaults","cryptx")})})]}),(0,i.jsx)("p",{className:"cryptx-settings__status"+(H?" is-dirty":""),"aria-live":"polite",children:H?(0,s.__)("You have unsaved changes.","cryptx"):(0,s.__)("All changes saved.","cryptx")})]}),(0,i.jsx)(a.__experimentalConfirmDialog,{isOpen:B,onConfirm:O,onCancel:()=>L(!1),confirmButtonText:(0,s.__)("Restore defaults","cryptx"),children:(0,s.__)("This puts every CryptX setting back to its default, on every tab. Addresses already delivered in cached pages keep working. Continue?","cryptx")})]})}document.addEventListener("DOMContentLoaded",()=>{const e=document.getElementById("cryptx-settings-root");if(!e)return;e.textContent="",e.setAttribute("aria-busy","false"),e.setAttribute("aria-label",(0,s.__)("CryptX settings","cryptx"));const r="network"===e.getAttribute("data-cryptx-scope")?"network":"site";(0,t.createRoot)(e).render((0,i.jsx)(t.StrictMode,{children:(0,i.jsx)(g,{scope:r})}))})})(); | |