Plugin Directory

Changeset 3381631


Ignore:
Timestamp:
10/21/2025 05:59:08 AM (3 months ago)
Author:
kotakdigitalcom
Message:

v1.0.9

Location:
kotaqx-poster/trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • kotaqx-poster/trunk/Readme.txt

    r3380670 r3381631  
    55Tested up to: 6.8
    66Requires PHP: 7.2
    7 Stable tag: 1.0.8
     7Stable tag: 1.0.9
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    105105== Changelog ==
    106106
     107= 1.0.9 =
     108* Refactor: Removed redundant nonce field from the “Exchange” button since a data-nonce attribute is already present.
     109* Refactor: Removed double arrows in select box.
     110* Improve: Added clear action detection and improved scheduling logic to ensure first publish always triggers repost.
     111* Fix: New posts were not being reposted when repost_on_update was disabled.
     112* Fix: Invalid nonce error when clicking the eye icon to uncensor field values.
     113
    107114= 1.0.8 =
    108115* Fix: Prevented fatal error when upgrading the Free plugin without updating the Pro version by adding a fallback if the instance() method does not exist.
     
    164171* Initial release
    165172
    166 == Upgrade Notice ==
    167 
    168 = 1.0.0 =
    169 Initial stable version. Recommended for all users.
    170 
    171173== License ==
    172174
  • kotaqx-poster/trunk/admin/settings.php

    r3351682 r3381631  
    206206                } else if ($args['action'] == 'exchange') {
    207207                    $nonce = wp_create_nonce('kotaqx_poster_exchange_button');
    208                     echo '<div class="control is-flex is-align-items-center">';
    209                     wp_nonce_field('kotaqx_poster_exchange_button', 'kotaqx_poster_exchange_button_nonce');
    210                     echo '<button type="button"
     208                    echo '<div class="control is-flex is-align-items-center">
     209                        <button type="button"
    211210                        class="button kotaqx-poster-exchange-button '.($args['value'] ? 'is-success' : 'is-warning').
    212211                        '" data-platform="'. esc_attr($platform) .'" data-nonce="'. esc_attr($nonce) .'">' . ($args['value'] ? 'Connected' : 'Exchange Token') .
     
    216215                break;
    217216   
    218             default:
    219                 if (!$mustCensor && $args['value']) echo '<div class="control has-icons-right">';
     217             default:
     218                if (!$mustCensor && $args['value'])
     219                    echo '<div class="control has-icons-right">';
     220
     221                $nonce = wp_create_nonce('kotaqx_poster_uncensor_field');
    220222                echo '<input type="' . esc_attr($args['type']) . '"
    221223                    name="' . esc_attr($name) . '"
     
    224226                    class="' . esc_attr($classes) . '"
    225227                    placeholder="' . esc_attr($args['placeholder']) . '"
     228                    data-nonce="'. esc_attr($nonce) .'"
    226229                    data-censored-value="' . esc_attr($args['value']) . '"
    227230                    />';
     231                   
    228232                if (!$mustCensor && $args['value'])
    229233                    echo '<span class="icon is-small is-right is-clickable" onclick="kotaqxPosterToggleVisibility(this)">
  • kotaqx-poster/trunk/assets/css/style.css

    r3351682 r3381631  
    7373.input::placeholder {
    7474    color: #808080;
     75}
     76.select:not(.is-multiple):not(.is-loading):after {
     77    display: none;
    7578}
    7679
  • kotaqx-poster/trunk/assets/css/style.min.css

    r3351682 r3381631  
    1 body{padding-right:0 !important;background-color:#000 !important}div#wpwrap{background-color:#000 !important}ul#adminmenu > li.current > a.current:after,ul#adminmenu a.wp-has-current-submenu:after{border-right-color:#040404 !important}.wp-core-ui select{background-color:#000 !important}#wpfooter{display:none !important}.poster-tab-content{display:none}.poster-tab-content.is-active{display:block}.swal2-modal{background:#14161a !important}.swal2-html-container,.swal2-title{color:#fff!important}#loading-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0, 0, 0, 0.5);z-index:9999;display:flex;justify-content:center;align-items:center}.loading-content{width:60px;height:60px}.loading-content img{width:100%;height:100%}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.loading-content img{animation:spin 1s linear infinite}.button,.button:hover{color:hsl(var(--bulma-button-h),var(--bulma-button-s),var(--bulma-button-color-l)) !important;background-color:hsl(var(--bulma-button-h),var(--bulma-button-s),calc(var(--bulma-button-background-l) + var(--bulma-button-background-l-delta))) !important;border-color:hsl(var(--bulma-button-h),var(--bulma-button-s),calc(var(--bulma-button-border-l) + var(--bulma-button-border-l-delta))) !important;border-style:var(--bulma-button-border-style) !important;font-weight:var(--bulma-button-weight) !important;box-shadow:0 0.0625em 0.125em hsla(var(--bulma-button-outer-shadow-h),var(--bulma-button-outer-shadow-s),var(--bulma-button-outer-shadow-l),var(--bulma-button-outer-shadow-a)),0 0.125em 0.25em hsla(var(--bulma-button-outer-shadow-h),var(--bulma-button-outer-shadow-s),var(--bulma-button-outer-shadow-l),var(--bulma-button-outer-shadow-a)) !important}select{color:#fff !important}label.checkbox{font-size:1.1em;margin-right:10px}input.input.is-dark{color:#fff;background:#14161a}.input::placeholder{color:#808080}.is-disabled{opacity:0.5;pointer-events:none;position:relative}.status-message{padding:1em;margin-bottom:1em;background-color:#fcebea;border-left:4px solid #e3342f;color:#cc1f1a;font-weight:bold;border-radius:4px}.is-hidden{display:none}.checkbox-wrapper-51 input[type="checkbox"]{visibility:hidden;display:none}.checkbox-wrapper-51 .toggle{position:relative;display:block;width:42px;height:24px;cursor:pointer;-webkit-tap-highlight-color:transparent;transform:translate3d(0, 0, 0)}.checkbox-wrapper-51 .toggle:before{content:"";position:relative;top:1px;left:1px;width:40px;height:22px;display:block;background:#c8ccd4;border-radius:12px;transition:background 0.2s ease}.checkbox-wrapper-51 .toggle span{position:absolute;top:0;left:0;width:24px;height:24px;display:block;background:#fff;border-radius:50%;box-shadow:0 2px 6px rgba(154,153,153,0.75);transition:all 0.2s ease}.checkbox-wrapper-51 .toggle span svg{margin:7px;fill:none}.checkbox-wrapper-51 .toggle span svg path{stroke:#c8ccd4;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:24;stroke-dashoffset:0;transition:all 0.5s linear}.checkbox-wrapper-51 input[type="checkbox"]:checked + .toggle:before{background:#1175c7}.checkbox-wrapper-51 input[type="checkbox"]:disabled + .toggle:before{background:#3c434a}.checkbox-wrapper-51 input[type="checkbox"]:checked + .toggle span{transform:translateX(18px)}.checkbox-wrapper-51 input[type="checkbox"]:checked + .toggle span path{stroke:#000000;stroke-dasharray:25;stroke-dashoffset:25}.kotaqx_poster_settings_group{transition:all 0.3s ease;padding:20px;background-color:#000;border-radius:12px}.platform-status-dot{width:8px;height:8px;border-radius:50%;display:inline-block;margin-right:8px;background-color:#7f7f7f}.platform-status-dot.enabled{background-color:#55ff90}.content-preview {text-align: left;font-size: 14px !important;background: #fff;color: #000;padding: 10px;border-radius: 5px;overflow-y: auto;height: 300px;}.content-preview strong {color: #000 !important;}.content-total-chars {text-align: left;font-size: 14px;}
     1body{padding-right: 0 !important;background-color: #000 !important;}div#wpwrap {background-color: #000 !important;}ul#adminmenu a.wp-has-current-submenu:after, ul#adminmenu>li.current>a.current:after {border-right-color: #040404 !important;}.wp-core-ui select {background-color: #000 !important;}#wpfooter {display: none !important;}.poster-tab-content {display: none;}.poster-tab-content.is-active {display: block;}.swal2-modal {background: #14161a !important;}.swal2-title, .swal2-html-container {color: #fff!important;}#loading-overlay {position: fixed;top: 0;left: 0;width: 100%;height: 100%;background: rgba(0, 0, 0, 0.5);z-index: 9999;display: flex;justify-content: center;align-items: center;}.loading-content {width: 60px;height: 60px;}.loading-content img {width: 100%;height: 100%;}@keyframes spin {0% {transform: rotate(0deg);}100% {transform: rotate(360deg);}}.loading-content img {animation: spin 1s linear infinite;}.button, .button:hover {color: hsl(var(--bulma-button-h),var(--bulma-button-s),var(--bulma-button-color-l)) !important;background-color: hsl(var(--bulma-button-h),var(--bulma-button-s),calc(var(--bulma-button-background-l) + var(--bulma-button-background-l-delta))) !important;border-color: hsl(var(--bulma-button-h),var(--bulma-button-s),calc(var(--bulma-button-border-l) + var(--bulma-button-border-l-delta))) !important;border-style: var(--bulma-button-border-style) !important;font-weight: var(--bulma-button-weight) !important;box-shadow: 0 .0625em .125em hsla(var(--bulma-button-outer-shadow-h),var(--bulma-button-outer-shadow-s),var(--bulma-button-outer-shadow-l),var(--bulma-button-outer-shadow-a)),0 .125em .25em hsla(var(--bulma-button-outer-shadow-h),var(--bulma-button-outer-shadow-s),var(--bulma-button-outer-shadow-l),var(--bulma-button-outer-shadow-a)) !important;}select {color: #fff !important;}label.checkbox {font-size: 1.1em;margin-right: 10px;}input.input.is-dark {color: #fff;background: #14161a;}.input::placeholder {color: #808080;}.select:not(.is-multiple):not(.is-loading):after {display: none;}.is-disabled {opacity: 0.5;pointer-events: none;position: relative;}.status-message {padding: 1em;margin-bottom: 1em;background-color: #fcebea;border-left: 4px solid #e3342f;color: #cc1f1a;font-weight: bold;border-radius: 4px;}.is-hidden {display: none;}.checkbox-wrapper-51 input[type="checkbox"] {visibility: hidden;display: none;}.checkbox-wrapper-51 .toggle {position: relative;display: block;width: 42px;height: 24px;cursor: pointer;-webkit-tap-highlight-color: transparent;transform: translate3d(0, 0, 0);}.checkbox-wrapper-51 .toggle:before {content: "";position: relative;top: 1px;left: 1px;width: 40px;height: 22px;display: block;background: #c8ccd4;border-radius: 12px;transition: background 0.2s ease;}.checkbox-wrapper-51 .toggle span {position: absolute;top: 0;left: 0;width: 24px;height: 24px;display: block;background: #fff;border-radius: 50%;box-shadow: 0 2px 6px rgba(154,153,153,0.75);transition: all 0.2s ease;}.checkbox-wrapper-51 .toggle span svg {margin: 7px;fill: none;}.checkbox-wrapper-51 .toggle span svg path {stroke: #c8ccd4;stroke-width: 2;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 24;stroke-dashoffset: 0;transition: all 0.5s linear;}.checkbox-wrapper-51 input[type="checkbox"]:checked + .toggle:before {background: #1175c7;}.checkbox-wrapper-51 input[type="checkbox"]:disabled + .toggle:before {background: #3c434a;}.checkbox-wrapper-51 input[type="checkbox"]:checked + .toggle span {transform: translateX(18px);}.checkbox-wrapper-51 input[type="checkbox"]:checked + .toggle span path {stroke: #000000;stroke-dasharray: 25;stroke-dashoffset: 25;}.kotaqx_poster_settings_group {transition: all 0.3s ease;padding: 20px;background-color: #000;border-radius: 12px;}.platform-status-dot {width: 8px;height: 8px;border-radius: 50%;display: inline-block;margin-right: 8px;background-color: #7f7f7f;}.platform-status-dot.enabled {background-color: #55ff90;}.content-preview {text-align: left;font-size: 14px !important;background: #fff;color: #000;padding: 10px;border-radius: 5px;overflow-y: auto;height: 300px;}.content-preview strong {color: #000 !important;}.content-total-chars {text-align: left;font-size: 14px;}
  • kotaqx-poster/trunk/assets/js/script.js

    r3364882 r3381631  
    815815   
    816816            const fieldId = input.getAttribute('id') || '';
     817            const nonce = input.getAttribute('data-nonce');
    817818   
    818819            fetchWithLoading(ajaxurl, {
     
    821822                body: new URLSearchParams({
    822823                    action: 'kotaqx_poster_field_uncensor',
     824                    nonce: nonce,
    823825                    field: fieldId
    824826                })
  • kotaqx-poster/trunk/assets/js/script.min.js

    r3364882 r3381631  
    1 let kotaqxFormSnapshot={};function initFormSnapshot(e){kotaqxFormSnapshot={};const t=e.querySelectorAll("input, select, textarea"),n={};t.forEach((e=>{e.name&&(n[e.name]||(n[e.name]=[]),n[e.name].push(e))}));for(const e in n){const t=n[e];if(2===t.length&&t.some((e=>"hidden"===e.type))&&t.some((e=>"checkbox"===e.type))){const n=t.find((e=>"checkbox"===e.type));kotaqxFormSnapshot[e]=n.checked?"1":""}else if(1===t.length){const n=t[0];"checkbox"===n.type||"radio"===n.type?kotaqxFormSnapshot[e]=n.checked?n.value||"on":"":"file"===n.type?kotaqxFormSnapshot[e]="":kotaqxFormSnapshot[e]=n.value}else if("checkbox"===t[0].type){const n=t.filter((e=>e.checked)).map((e=>e.value||"on"));kotaqxFormSnapshot[e]=n.sort()}else kotaqxFormSnapshot[e]=t.map((e=>e.value)).join(",")}}function getChangedFormData(e,t=""){const n=new FormData,a=e.querySelectorAll("input, select, textarea"),o=Array.from(a).filter((e=>e.name&&"license_key"!==e.name)),s={};o.forEach((e=>{s[e.name]||(s[e.name]=[]),s[e.name].push(e)}));let r=!1;for(const e in s){const t=s[e];if(2===t.length&&t.some((e=>"hidden"===e.type))&&t.some((e=>"checkbox"===e.type))){const a=t.find((e=>"checkbox"===e.type)),o=(t.find((e=>"hidden"===e.type)),a.checked?"1":"");o!==(kotaqxFormSnapshot[e]||"")&&(r=!0),n.append(e,o)}else if(1===t.length){const a=t[0];let o;if("checkbox"===a.type){o=a.checked?a.value||"on":"";o!==(kotaqxFormSnapshot[e]||"")&&(r=!0),n.append(e,o)}else if("radio"===a.type){o=a.checked?a.value||"on":"";o!==(kotaqxFormSnapshot[e]||"")&&(r=!0,n.append(e,o))}else"file"===a.type?a.files.length>0&&(r=!0,n.append(a.name,a.files[0])):(o=a.value,o!==(kotaqxFormSnapshot[e]||"")&&(r=!0,n.append(e,o)))}else if("checkbox"===t[0].type){const a=t.filter((e=>e.checked)).map((e=>e.value||"on")).sort();arraysHaveSameItems(a,kotaqxFormSnapshot[e]||[])||(r=!0),a.length>0?a.forEach((t=>n.append(e,t))):n.append(e,"")}else{t.map((e=>e.value)).join(",")!==(kotaqxFormSnapshot[e]||"")&&(r=!0,t.forEach((t=>n.append(e,t.value))))}}if(!r)return null;const i=e.querySelector('input[name="kotaqx_poster_nonce"]');return i&&i.value&&n.append("nonce",i.value),t&&n.append("action",t),n}function arraysHaveSameItems(e,t){if(!Array.isArray(e)||!Array.isArray(t))return!1;if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(e[n]!==t[n])return!1;return!0}const checkboxDivPairs={"cbx-log-enabled":"log_enabled","cbx-facebook":"facebook","cbx-facebook_group":"facebook_group","cbx-instagram":"instagram","cbx-threads":"threads","cbx-twitter":"twitter","cbx-linkedin":"linkedin","cbx-reddit":"reddit","cbx-medium":"medium","cbx-mastodon":"mastodon","cbx-slack":"slack","cbx-vk":"vk","cbx-pinterest":"pinterest","cbx-tumblr":"tumblr","cbx-discord":"discord","cbx-telegram":"telegram","cbx-grupo":"grupo"};function toggleDivState(e,t){const n=e.checked,a=document.getElementById("kotaqx_poster_settings_"+t),o=document.querySelector(".platform-status-dot."+t);a&&(a.style.opacity=n?"1":"0.5",a.style.pointerEvents=n?"auto":"none",a.style.cursor=n?"auto":"default"),o&&o.classList.toggle("enabled",n)}function toggleCustomDelay(e){const t=document.getElementById("custom_delay_section");t.style.display="custom"===e?"flex":"none"}document.addEventListener("DOMContentLoaded",(function(){if(!window.location.href.includes("?page=kotaqx-poster-settings"))return;for(const[e,t]of Object.entries(checkboxDivPairs)){const n=document.getElementById(e);n&&(toggleDivState(n,t),n.addEventListener("change",(()=>{toggleDivState(n,t)})))}const e=document.getElementById("loading-overlay"),t=t=>{e&&(e.style.display=t?"flex":"none")},n=(e,n)=>(t(!0),fetch(e,n).finally((()=>t(!1)))),a=document.querySelectorAll("#poster-tabs li"),o=document.querySelectorAll(".poster-tab-content");function s(e){a.forEach((t=>{t.classList.toggle("is-active",t.dataset.tab===e)})),o.forEach((t=>{t.classList.toggle("is-active",t.id==="tab-"+e)}))}var r;s((r="tab",new URLSearchParams(window.location.search).get(r)||"general")),a.forEach((e=>{e.addEventListener("click",(t=>{t.preventDefault();const n=e.dataset.tab;s(n);const a=new URL(window.location.href);a.searchParams.set("tab",n),window.history.replaceState(null,"",a)}))})),document.querySelectorAll(".image-source-selector").forEach((e=>{e.addEventListener("change",(function(){!function(e,t){const n=document.getElementById("manual-image-"+e),a=document.getElementById("custom-field-"+e);n&&(n.style.display="none"),a&&(a.style.display="none"),"manual"===t&&n?n.style.display="block":"custom-field"===t&&a&&(a.style.display="block")}(this.getAttribute("data-platform"),this.value)}))})),document.querySelectorAll(".select-media-button").forEach((e=>{e.addEventListener("click",(function(e){e.preventDefault();const t=this.getAttribute("data-platform"),n=document.querySelector(`input[name="kotaqx_poster_settings[${t}_manual_image_url]"]`),a=document.querySelector(`input[name="kotaqx_poster_settings[${t}_manual_image_id]"]`),o=document.querySelector(`#manual-image-${t} .selected-image-preview`),s=wp.media({title:"Select or Upload Image",button:{text:"Use this image"},multiple:!1});s.on("select",(function(){const e=s.state().get("selection").first().toJSON();n.value=e.url,a.value=e.id,o.innerHTML=`<img src="${e.url}" style="max-width: 200px;">`})),s.open()}))}));const i=document.getElementById("kotaqx-poster-settings-form"),c=document.getElementById("kotaqx-poster-save-settings-button");async function l(e,t="",a=!1){if(e&&e instanceof FormData)return t||e.append("platform",t),e.has("license_key")&&e.delete("license_key"),n(ajaxurl,{method:"POST",credentials:"same-origin",body:e}).then((e=>{if(!e.ok)throw new Error("Network response was not ok");return e.json()})).then((e=>(e.success?(initFormSnapshot(i),a||Swal.fire("Success",e.message,"success")):Swal.fire("Error",e.message||"Something went wrong.","error"),e))).catch((e=>{throw Swal.fire("Error","AJAX request failed.","error"),console.error(e),e}));console.error("Invalid formData provided")}c&&i&&c.addEventListener("click",(function(e){e.preventDefault();const t=getChangedFormData(i,"kotaqx_poster_save_settings");t?l(t):Swal.fire("Info","No changes to save.","info")}));const d=document.querySelector("#kotaqx-poster-manual-trigger .manual-trigger");d&&d.addEventListener("click",(function(e){e.preventDefault(),Swal.fire({title:"Run Manual Check?",text:"This will attempt to publish any missed scheduled posts now.",icon:"warning",showCancelButton:!0,confirmButtonText:"Yes, run it!",cancelButtonText:"Cancel"}).then((e=>{if(!e.isConfirmed)return;const t=document.getElementById("kotaqx-poster-manual-trigger"),a=new FormData;t.querySelectorAll('input[type="hidden"]').forEach((e=>{a.append(e.name,e.value)})),a.append("action","kotaqx_poster_manual_trigger"),n(ajaxurl,{method:"POST",credentials:"same-origin",body:a}).then((e=>e.json())).then((e=>{e.success?Swal.fire("Done",e.message||"Manual check completed.","success"):Swal.fire("Failed",e.message||"Manual check failed.","error")})).catch((e=>{console.error(e),Swal.fire("Error","AJAX request failed.","error")}))}))}));const u=document.getElementById("kotaqx-poster-logs-table");if(u){const e=u.querySelector(".clear-logs");e&&e.addEventListener("click",(function(e){e.preventDefault(),Swal.fire({title:"Are you sure?",text:"This will permanently delete all logs.",icon:"warning",showCancelButton:!0,confirmButtonText:"Yes, clear them!"}).then((e=>{if(e.isConfirmed){const e=new FormData;e.append("action","kotaqx_poster_clear_logs");const t=u.querySelector('input[name="kotaqx_poster_logs_nonce"]');t&&e.append("nonce",t.value),n(ajaxurl,{method:"POST",credentials:"same-origin",body:e}).then((e=>e.json())).then((e=>{e.success?Swal.fire("Success",e.message,"success").then((()=>window.location.reload())):Swal.fire("Error",e.message,"error")})).catch((()=>{Swal.fire("Error","AJAX request failed.","error")}))}}))}))}const p=document.getElementById("kotaqx-poster-logs-pagination");if(p){const e=document.querySelector("#kotaqx-poster-logs-table table tbody");p.addEventListener("click",(function(t){const a=t.target;if(!a.matches(".pagination-link, .pagination-previous, .pagination-next"))return;t.preventDefault();const o=p.querySelector(".pagination-link.is-current"),s=parseInt(o?.dataset.page||"1"),r=parseInt(p.dataset.totalPages||"1");let i=null;if(a.classList.contains("pagination-previous"))s>1&&(i=s-1);else if(a.classList.contains("pagination-next"))s<r&&(i=s+1);else if(a.classList.contains("pagination-link")){const e=parseInt(a.dataset.page);e!==s&&(i=e)}if(!i)return;const c=new FormData;c.append("action","kotaqx_poster_get_logs_page"),c.append("nonce",kotaqx_poster_vars.nonce),c.append("page",i),n(ajaxurl,{method:"POST",credentials:"same-origin",body:c}).then((e=>e.json())).then((t=>{if(t.success&&e){e.innerHTML=t.data.html,p.querySelectorAll(".pagination-link").forEach((e=>{e.classList.toggle("is-current",parseInt(e.dataset.page)===i)}));const n=p.querySelector(".pagination-previous"),a=p.querySelector(".pagination-next");n&&n.classList.toggle("is-disabled",i<=1),a&&a.classList.toggle("is-disabled",i>=r)}})).catch((()=>{Swal.fire("Error","Failed to fetch log data.","error")}))}))}document.querySelectorAll(".kotaqx-poster-authorize-button").forEach((e=>{e.addEventListener("click",(async function(e){e.preventDefault();const t=getChangedFormData(i,"kotaqx_poster_save_settings"),a=this.dataset.platform,o=this.dataset.nonce,s=this.classList.contains("is-success"),r=this.textContent;if(s){if(!(await Swal.fire({title:"Reauthorize Platform",text:"You’ll be redirected to sign in and grant permissions again. The current connection will be replaced.",icon:"info",showCancelButton:!0,confirmButtonText:"Reauthorize",cancelButtonText:"Cancel"})).isConfirmed)return}this.disabled=!0,this.textContent="Authorizing…";const c=new FormData;if(t)for(const[e,n]of t.entries())if(e.includes(a)){const t=/\[(.*?)\]/.exec(e)?.[1]??e;c.append(t,n)}const d=new URLSearchParams(c);d.append("action","kotaqx_poster_authorize"),d.append("platform",a),d.append("nonce",o),t&&await l(t,a,!0);try{const e=await n(ajaxurl,{method:"POST",credentials:"same-origin",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:d.toString()}),t=await e.json();if(t.success){const e=t.message;e&&(e.startsWith("http://")||e.startsWith("https://"))?window.location.href=e:Swal.fire("Error","Redirect URL is invalid.","error")}else{const e=t.message||"Unknown error";Swal.fire("Error",e,"error")}}catch(e){console.error(e),Swal.fire("Error","Request failed or Unauthorized","error")}finally{this.disabled=!1,this.textContent=r}}))}));document.querySelectorAll(".kotaqx-poster-exchange-button").forEach((e=>{e.addEventListener("click",(async function(e){e.preventDefault();const t=getChangedFormData(i,"kotaqx_poster_save_settings"),n=this.getAttribute("data-platform"),a=this.dataset.nonce,o=this.classList.contains("is-success");let s=this.textContent;if(o){if(!(await Swal.fire({title:"Renew Access Token",text:"This will reauthenticate and issue a new token. If successful, the current token will be revoked and replaced.",icon:"info",showCancelButton:!0,confirmButtonText:"Refresh Token",cancelButtonText:"Cancel"})).isConfirmed)return}this.disabled=!0,this.textContent="Loading...";const r=new FormData;if(t)for(const[e,a]of t.entries())if(e.includes(n)){const t=e.match(/\[(.*?)\]/),n=t?t[1]:e;r.append(n,a)}const c=new URLSearchParams(r);c.append("action","kotaqx_poster_exchange"),c.append("platform",n),c.append("nonce",a);try{const e=await fetch(ajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:c}),t=await e.json();t.success?(Swal.fire("Success",t.message||"Success","success"),s="Connected",this.classList.remove("is-warning"),this.classList.add("is-success")):Swal.fire("Error",t.message||"Unknown error","error")}catch(e){console.error(e),Swal.fire("Error","Request failed or Unauthorized","error")}finally{this.disabled=!1,this.textContent=s}}))})),document.querySelectorAll("input.is-censored, textarea.is-censored").forEach((e=>{let t=e.value,n=!1,a=!1;e.addEventListener("focus",(function(){if(!this.classList.contains("is-censored"))return;const e=this.getAttribute("data-real-value"),o=this.parentElement?.querySelector(".toggle-icon");!e||o?(n||(t=this.value,this.value="",n=!0),a=!1):this.value=e})),e.addEventListener("input",(function(){if(!this.classList.contains("is-censored"))return;a=!0,this.classList.remove("is-censored");const e=this.parentElement?.querySelector(".icon");e&&e.remove()})),e.addEventListener("blur",(function(){if(!this.classList.contains("is-censored"))return;const e=this.getAttribute("data-real-value"),o=this.parentElement?.querySelector(".toggle-icon");e&&!o||(""!==this.value&&this.value!==t&&(a=!0),a||(this.value=t,n=!1))}))})),window.kotaqxPosterToggleVisibility=async function(e){const t=e.closest(".control").querySelector("input, textarea"),a=e.querySelector(".toggle-icon");if(!t||!a)return;if(a.classList.contains("dashicons-visibility")){if(!(await Swal.fire({title:"Show Value?",text:"Are you sure you want to reveal this value?",icon:"warning",showCancelButton:!0,confirmButtonText:"Yes, show it",cancelButtonText:"Cancel"})).isConfirmed)return;const a=t.getAttribute("id")||"";n(ajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({action:"kotaqx_poster_field_uncensor",field:a})}).then((e=>e.json())).then((n=>{n.success?(t.setAttribute("data-real-value",n.value),t.value=n.value,t.classList.remove("is-censored"),e.remove()):Swal.fire({icon:"error",title:"Failed to reveal field",text:n.message})})).catch((e=>{Swal.fire({icon:"error",title:"Error revealing field",text:e.toString()})}))}},window.onbeforeunload=function(){if(getChangedFormData(i,"kotaqx_poster_save_settings"))return"You have unsaved changes. Are you sure you want to close this tab/window?"},document.querySelectorAll("[data-required-id][data-required-compare]").forEach((e=>{const t=e.dataset.requiredCompare;let n=[];try{n=JSON.parse(e.dataset.requiredValue||"[]"),Array.isArray(n)||(n=[n])}catch{n=[e.dataset.requiredValue]}let a=[];try{a=JSON.parse(e.dataset.requiredId),Array.isArray(a)||(a=[a])}catch{a=[e.dataset.requiredId]}const o=()=>{let o=[];a.forEach((e=>{const n=`kotaqx_poster_settings[${e}${["in","not_in","min_checked","max_checked"].includes(t)?"[]":""}]`;document.querySelectorAll(`[name="${n}"]`).forEach((e=>{"checkbox"===e.type||"radio"===e.type?e.checked&&o.push(e.value):o.push(e.value)}))}));let s=!1;const r=o[0]??"",i=n[0]??"";switch(t){case"in":s=o.some((e=>n.includes(e)));break;case"not_in":s=o.every((e=>!n.includes(e)));break;case"=":s=o.includes(i);break;case"<>":s=!o.includes(i);break;case"isset":s=o.length===a.length&&o.every((e=>""!==e&&"none"!==e));break;case"empty":s=o.every((e=>""===e||"none"===e));break;case"min_checked":s=o.length>=parseInt(i||"1",10);break;case"max_checked":s=o.length<=parseInt(i||"1",10);break;case">":s=parseFloat(r)>parseFloat(i);break;case"<":s=parseFloat(r)<parseFloat(i);break;case">=":s=parseFloat(r)>=parseFloat(i);break;case"<=":s=parseFloat(r)<=parseFloat(i)}e.style.display=s?"":"none"};a.forEach((e=>{const t=`[name="kotaqx_poster_settings[${e}]"], [name="kotaqx_poster_settings[${e}][]"]`;document.querySelectorAll(t).forEach((e=>{e.addEventListener("change",o)}))})),o()}));const h=document.querySelectorAll(".platform-tab"),m=document.querySelectorAll(".platform-content");h.forEach((e=>{e.addEventListener("click",(()=>{const t=e.getAttribute("data-target");h.forEach((e=>e.classList.remove("is-active"))),e.classList.add("is-active"),m.forEach((e=>{e.id===t?e.style.display="block":e.style.display="none"}))}))})),document.querySelectorAll(".kotaqx-poster-content-preview").forEach((function(e){e.addEventListener("click",(async function(e){e.preventDefault();const t=this.getAttribute("data-platform"),a=this.getAttribute("data-nonce"),o=document.querySelector(`textarea[name="kotaqx_poster_settings[${t}_post_content]"]`),s=o?o.value:"",r=await async function(e){let t=null;const a=async(t="")=>{const a=await async function(e,t=""){const a=await n(ajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({action:"kotaqx_poster_get_post_list",search:t,nonce:e})});return await a.json()}(e,t);return Object.entries(a).map((([e,t])=>`<option value="${e}">${t}</option>`)).join("")};return await Swal.fire({title:"Select Post to Preview",html:'\n                <div class="field">\n                    <label class="label" for="post-search">Search Post</label>\n                    <div class="control has-icons-left">\n                        <input id="post-search" class="input" type="text" placeholder="Search post title...">\n                        <span class="icon is-left">\n                        <i class="fas fa-search"></i>\n                        </span>\n                    </div>\n                    </div>\n\n                    <div class="field">\n                    <label class="label" for="post-select">Select Post</label>\n                    <div class="control">\n                        <div class="select is-fullwidth">\n                        <select id="post-select">\n                            <option disabled selected>Loading post...</option>\n                        </select>\n                        </div>\n                    </div>\n                </div>\n            ',focusConfirm:!1,didOpen:async()=>{const e=document.getElementById("post-select"),t=document.getElementById("post-search");e.innerHTML=await a(),t.addEventListener("input",function(e,t){let n;return function(...a){clearTimeout(n),n=setTimeout((()=>e.apply(this,a)),t)}}((async function(){e.innerHTML=await a(this.value)}),400))},preConfirm:()=>document.getElementById("post-select").value,showCancelButton:!0}).then((e=>{t=e.value})),t}(a);if(!r)return;const i=new FormData;i.append("action","kotaqx_poster_preview_content"),i.append("platform",t),i.append("nonce",a),i.append("post_id",r),i.append("content",s),n(ajaxurl,{method:"POST",body:i}).then((e=>e.text())).then((e=>{Swal.fire({title:"Preview Content",html:e,width:"60%",showCloseButton:!0,focusConfirm:!1,confirmButtonText:"Close"})})).catch((e=>{console.error("Preview error:",e),Swal.fire("Error","Failed to load preview","error")}))}))})),initFormSnapshot(i);const f=document.getElementById("license-submit-btn");f&&f.addEventListener("click",(function(e){e.preventDefault();const t=document.getElementById("license_key").value,a=f.getAttribute("data-action"),o=f.getAttribute("data-nonce"),s=new FormData;s.append("action","kotaqx_poster_handle_license"),s.append("license_key",t),s.append("license_action",a),s.append("license_nonce",o);const r=()=>{n(ajaxurl,{method:"POST",credentials:"same-origin",body:s}).then((e=>e.json())).then((e=>{e.success?(Swal.fire({icon:"success",title:"Success",text:e.data.message||"License action completed."}),e.data?.reload&&location.reload()):Swal.fire({icon:"error",title:"Failed",text:e.data?.message||"License action failed."})})).catch((e=>{Swal.fire({icon:"error",title:"Oops...",text:"Something went wrong!"}),console.error("AJAX Error:",e)}))};"deactivate"===a?(s.delete("license_key"),Swal.fire({title:"Deactivate License?",text:"Are you sure you want to deactivate your license?",icon:"warning",showCancelButton:!0,confirmButtonText:"Yes, deactivate",cancelButtonText:"Cancel"}).then((e=>{e.isConfirmed&&r()}))):r()}));const g=document.querySelector(".activate-license-button");g&&g.addEventListener("click",(function(e){e.preventDefault();const t="license";s(t);const n=new URL(window.location.href);n.searchParams.set("tab",t),window.history.replaceState(null,"",n)}))}));
     1let kotaqxFormSnapshot={};function initFormSnapshot(e){kotaqxFormSnapshot={};const t=e.querySelectorAll("input, select, textarea"),n={};t.forEach((e=>{e.name&&(n[e.name]||(n[e.name]=[]),n[e.name].push(e))}));for(const e in n){const t=n[e];if(2===t.length&&t.some((e=>"hidden"===e.type))&&t.some((e=>"checkbox"===e.type))){const n=t.find((e=>"checkbox"===e.type));kotaqxFormSnapshot[e]=n.checked?"1":""}else if(1===t.length){const n=t[0];"checkbox"===n.type||"radio"===n.type?kotaqxFormSnapshot[e]=n.checked?n.value||"on":"":"file"===n.type?kotaqxFormSnapshot[e]="":kotaqxFormSnapshot[e]=n.value}else if("checkbox"===t[0].type){const n=t.filter((e=>e.checked)).map((e=>e.value||"on"));kotaqxFormSnapshot[e]=n.sort()}else kotaqxFormSnapshot[e]=t.map((e=>e.value)).join(",")}}function getChangedFormData(e,t=""){const n=new FormData,a=e.querySelectorAll("input, select, textarea"),o=Array.from(a).filter((e=>e.name&&"license_key"!==e.name)),s={};o.forEach((e=>{s[e.name]||(s[e.name]=[]),s[e.name].push(e)}));let r=!1;for(const e in s){const t=s[e];if(2===t.length&&t.some((e=>"hidden"===e.type))&&t.some((e=>"checkbox"===e.type))){const a=t.find((e=>"checkbox"===e.type)),o=(t.find((e=>"hidden"===e.type)),a.checked?"1":"");o!==(kotaqxFormSnapshot[e]||"")&&(r=!0),n.append(e,o)}else if(1===t.length){const a=t[0];let o;if("checkbox"===a.type){o=a.checked?a.value||"on":"";o!==(kotaqxFormSnapshot[e]||"")&&(r=!0),n.append(e,o)}else if("radio"===a.type){o=a.checked?a.value||"on":"";o!==(kotaqxFormSnapshot[e]||"")&&(r=!0,n.append(e,o))}else"file"===a.type?a.files.length>0&&(r=!0,n.append(a.name,a.files[0])):(o=a.value,o!==(kotaqxFormSnapshot[e]||"")&&(r=!0,n.append(e,o)))}else if("checkbox"===t[0].type){const a=t.filter((e=>e.checked)).map((e=>e.value||"on")).sort();arraysHaveSameItems(a,kotaqxFormSnapshot[e]||[])||(r=!0),a.length>0?a.forEach((t=>n.append(e,t))):n.append(e,"")}else{t.map((e=>e.value)).join(",")!==(kotaqxFormSnapshot[e]||"")&&(r=!0,t.forEach((t=>n.append(e,t.value))))}}if(!r)return null;const i=e.querySelector('input[name="kotaqx_poster_nonce"]');return i&&i.value&&n.append("nonce",i.value),t&&n.append("action",t),n}function arraysHaveSameItems(e,t){if(!Array.isArray(e)||!Array.isArray(t))return!1;if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(e[n]!==t[n])return!1;return!0}const checkboxDivPairs={"cbx-log-enabled":"log_enabled","cbx-facebook":"facebook","cbx-facebook_group":"facebook_group","cbx-instagram":"instagram","cbx-threads":"threads","cbx-twitter":"twitter","cbx-linkedin":"linkedin","cbx-reddit":"reddit","cbx-medium":"medium","cbx-mastodon":"mastodon","cbx-slack":"slack","cbx-vk":"vk","cbx-pinterest":"pinterest","cbx-tumblr":"tumblr","cbx-discord":"discord","cbx-telegram":"telegram","cbx-grupo":"grupo"};function toggleDivState(e,t){const n=e.checked,a=document.getElementById("kotaqx_poster_settings_"+t),o=document.querySelector(".platform-status-dot."+t);a&&(a.style.opacity=n?"1":"0.5",a.style.pointerEvents=n?"auto":"none",a.style.cursor=n?"auto":"default"),o&&o.classList.toggle("enabled",n)}function toggleCustomDelay(e){const t=document.getElementById("custom_delay_section");t.style.display="custom"===e?"flex":"none"}document.addEventListener("DOMContentLoaded",(function(){if(!window.location.href.includes("?page=kotaqx-poster-settings"))return;for(const[e,t]of Object.entries(checkboxDivPairs)){const n=document.getElementById(e);n&&(toggleDivState(n,t),n.addEventListener("change",(()=>{toggleDivState(n,t)})))}const e=document.getElementById("loading-overlay"),t=t=>{e&&(e.style.display=t?"flex":"none")},n=(e,n)=>(t(!0),fetch(e,n).finally((()=>t(!1)))),a=document.querySelectorAll("#poster-tabs li"),o=document.querySelectorAll(".poster-tab-content");function s(e){a.forEach((t=>{t.classList.toggle("is-active",t.dataset.tab===e)})),o.forEach((t=>{t.classList.toggle("is-active",t.id==="tab-"+e)}))}var r;s((r="tab",new URLSearchParams(window.location.search).get(r)||"general")),a.forEach((e=>{e.addEventListener("click",(t=>{t.preventDefault();const n=e.dataset.tab;s(n);const a=new URL(window.location.href);a.searchParams.set("tab",n),window.history.replaceState(null,"",a)}))})),document.querySelectorAll(".image-source-selector").forEach((e=>{e.addEventListener("change",(function(){!function(e,t){const n=document.getElementById("manual-image-"+e),a=document.getElementById("custom-field-"+e);n&&(n.style.display="none"),a&&(a.style.display="none"),"manual"===t&&n?n.style.display="block":"custom-field"===t&&a&&(a.style.display="block")}(this.getAttribute("data-platform"),this.value)}))})),document.querySelectorAll(".select-media-button").forEach((e=>{e.addEventListener("click",(function(e){e.preventDefault();const t=this.getAttribute("data-platform"),n=document.querySelector(`input[name="kotaqx_poster_settings[${t}_manual_image_url]"]`),a=document.querySelector(`input[name="kotaqx_poster_settings[${t}_manual_image_id]"]`),o=document.querySelector(`#manual-image-${t} .selected-image-preview`),s=wp.media({title:"Select or Upload Image",button:{text:"Use this image"},multiple:!1});s.on("select",(function(){const e=s.state().get("selection").first().toJSON();n.value=e.url,a.value=e.id,o.innerHTML=`<img src="${e.url}" style="max-width: 200px;">`})),s.open()}))}));const i=document.getElementById("kotaqx-poster-settings-form"),c=document.getElementById("kotaqx-poster-save-settings-button");async function l(e,t="",a=!1){if(e&&e instanceof FormData)return t||e.append("platform",t),e.has("license_key")&&e.delete("license_key"),n(ajaxurl,{method:"POST",credentials:"same-origin",body:e}).then((e=>{if(!e.ok)throw new Error("Network response was not ok");return e.json()})).then((e=>(e.success?(initFormSnapshot(i),a||Swal.fire("Success",e.message,"success")):Swal.fire("Error",e.message||"Something went wrong.","error"),e))).catch((e=>{throw Swal.fire("Error","AJAX request failed.","error"),console.error(e),e}));console.error("Invalid formData provided")}c&&i&&c.addEventListener("click",(function(e){e.preventDefault();const t=getChangedFormData(i,"kotaqx_poster_save_settings");t?l(t):Swal.fire("Info","No changes to save.","info")}));const d=document.querySelector("#kotaqx-poster-manual-trigger .manual-trigger");d&&d.addEventListener("click",(function(e){e.preventDefault(),Swal.fire({title:"Run Manual Check?",text:"This will attempt to publish any missed scheduled posts now.",icon:"warning",showCancelButton:!0,confirmButtonText:"Yes, run it!",cancelButtonText:"Cancel"}).then((e=>{if(!e.isConfirmed)return;const t=document.getElementById("kotaqx-poster-manual-trigger"),a=new FormData;t.querySelectorAll('input[type="hidden"]').forEach((e=>{a.append(e.name,e.value)})),a.append("action","kotaqx_poster_manual_trigger"),n(ajaxurl,{method:"POST",credentials:"same-origin",body:a}).then((e=>e.json())).then((e=>{e.success?Swal.fire("Done",e.message||"Manual check completed.","success"):Swal.fire("Failed",e.message||"Manual check failed.","error")})).catch((e=>{console.error(e),Swal.fire("Error","AJAX request failed.","error")}))}))}));const u=document.getElementById("kotaqx-poster-logs-table");if(u){const e=u.querySelector(".clear-logs");e&&e.addEventListener("click",(function(e){e.preventDefault(),Swal.fire({title:"Are you sure?",text:"This will permanently delete all logs.",icon:"warning",showCancelButton:!0,confirmButtonText:"Yes, clear them!"}).then((e=>{if(e.isConfirmed){const e=new FormData;e.append("action","kotaqx_poster_clear_logs");const t=u.querySelector('input[name="kotaqx_poster_logs_nonce"]');t&&e.append("nonce",t.value),n(ajaxurl,{method:"POST",credentials:"same-origin",body:e}).then((e=>e.json())).then((e=>{e.success?Swal.fire("Success",e.message,"success").then((()=>window.location.reload())):Swal.fire("Error",e.message,"error")})).catch((()=>{Swal.fire("Error","AJAX request failed.","error")}))}}))}))}const p=document.getElementById("kotaqx-poster-logs-pagination");if(p){const e=document.querySelector("#kotaqx-poster-logs-table table tbody");p.addEventListener("click",(function(t){const a=t.target;if(!a.matches(".pagination-link, .pagination-previous, .pagination-next"))return;t.preventDefault();const o=p.querySelector(".pagination-link.is-current"),s=parseInt(o?.dataset.page||"1"),r=parseInt(p.dataset.totalPages||"1");let i=null;if(a.classList.contains("pagination-previous"))s>1&&(i=s-1);else if(a.classList.contains("pagination-next"))s<r&&(i=s+1);else if(a.classList.contains("pagination-link")){const e=parseInt(a.dataset.page);e!==s&&(i=e)}if(!i)return;const c=new FormData;c.append("action","kotaqx_poster_get_logs_page"),c.append("nonce",kotaqx_poster_vars.nonce),c.append("page",i),n(ajaxurl,{method:"POST",credentials:"same-origin",body:c}).then((e=>e.json())).then((t=>{if(t.success&&e){e.innerHTML=t.data.html,p.querySelectorAll(".pagination-link").forEach((e=>{e.classList.toggle("is-current",parseInt(e.dataset.page)===i)}));const n=p.querySelector(".pagination-previous"),a=p.querySelector(".pagination-next");n&&n.classList.toggle("is-disabled",i<=1),a&&a.classList.toggle("is-disabled",i>=r)}})).catch((()=>{Swal.fire("Error","Failed to fetch log data.","error")}))}))}document.querySelectorAll(".kotaqx-poster-authorize-button").forEach((e=>{e.addEventListener("click",(async function(e){e.preventDefault();const t=getChangedFormData(i,"kotaqx_poster_save_settings"),a=this.dataset.platform,o=this.dataset.nonce,s=this.classList.contains("is-success"),r=this.textContent;if(s){if(!(await Swal.fire({title:"Reauthorize Platform",text:"You’ll be redirected to sign in and grant permissions again. The current connection will be replaced.",icon:"info",showCancelButton:!0,confirmButtonText:"Reauthorize",cancelButtonText:"Cancel"})).isConfirmed)return}this.disabled=!0,this.textContent="Authorizing…";const c=new FormData;if(t)for(const[e,n]of t.entries())if(e.includes(a)){const t=/\[(.*?)\]/.exec(e)?.[1]??e;c.append(t,n)}const d=new URLSearchParams(c);d.append("action","kotaqx_poster_authorize"),d.append("platform",a),d.append("nonce",o),t&&await l(t,a,!0);try{const e=await n(ajaxurl,{method:"POST",credentials:"same-origin",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:d.toString()}),t=await e.json();if(t.success){const e=t.message;e&&(e.startsWith("http://")||e.startsWith("https://"))?window.location.href=e:Swal.fire("Error","Redirect URL is invalid.","error")}else{const e=t.message||"Unknown error";Swal.fire("Error",e,"error")}}catch(e){console.error(e),Swal.fire("Error","Request failed or Unauthorized","error")}finally{this.disabled=!1,this.textContent=r}}))}));document.querySelectorAll(".kotaqx-poster-exchange-button").forEach((e=>{e.addEventListener("click",(async function(e){e.preventDefault();const t=getChangedFormData(i,"kotaqx_poster_save_settings"),n=this.getAttribute("data-platform"),a=this.dataset.nonce,o=this.classList.contains("is-success");let s=this.textContent;if(o){if(!(await Swal.fire({title:"Renew Access Token",text:"This will reauthenticate and issue a new token. If successful, the current token will be revoked and replaced.",icon:"info",showCancelButton:!0,confirmButtonText:"Refresh Token",cancelButtonText:"Cancel"})).isConfirmed)return}this.disabled=!0,this.textContent="Loading...";const r=new FormData;if(t)for(const[e,a]of t.entries())if(e.includes(n)){const t=e.match(/\[(.*?)\]/),n=t?t[1]:e;r.append(n,a)}const c=new URLSearchParams(r);c.append("action","kotaqx_poster_exchange"),c.append("platform",n),c.append("nonce",a);try{const e=await fetch(ajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:c}),t=await e.json();t.success?(Swal.fire("Success",t.message||"Success","success"),s="Connected",this.classList.remove("is-warning"),this.classList.add("is-success")):Swal.fire("Error",t.message||"Unknown error","error")}catch(e){console.error(e),Swal.fire("Error","Request failed or Unauthorized","error")}finally{this.disabled=!1,this.textContent=s}}))})),document.querySelectorAll("input.is-censored, textarea.is-censored").forEach((e=>{let t=e.value,n=!1,a=!1;e.addEventListener("focus",(function(){if(!this.classList.contains("is-censored"))return;const e=this.getAttribute("data-real-value"),o=this.parentElement?.querySelector(".toggle-icon");!e||o?(n||(t=this.value,this.value="",n=!0),a=!1):this.value=e})),e.addEventListener("input",(function(){if(!this.classList.contains("is-censored"))return;a=!0,this.classList.remove("is-censored");const e=this.parentElement?.querySelector(".icon");e&&e.remove()})),e.addEventListener("blur",(function(){if(!this.classList.contains("is-censored"))return;const e=this.getAttribute("data-real-value"),o=this.parentElement?.querySelector(".toggle-icon");e&&!o||(""!==this.value&&this.value!==t&&(a=!0),a||(this.value=t,n=!1))}))})),window.kotaqxPosterToggleVisibility=async function(e){const t=e.closest(".control").querySelector("input, textarea"),a=e.querySelector(".toggle-icon");if(!t||!a)return;if(a.classList.contains("dashicons-visibility")){if(!(await Swal.fire({title:"Show Value?",text:"Are you sure you want to reveal this value?",icon:"warning",showCancelButton:!0,confirmButtonText:"Yes, show it",cancelButtonText:"Cancel"})).isConfirmed)return;const a=t.getAttribute("id")||"",o=t.getAttribute("data-nonce");n(ajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({action:"kotaqx_poster_field_uncensor",nonce:o,field:a})}).then((e=>e.json())).then((n=>{n.success?(t.setAttribute("data-real-value",n.value),t.value=n.value,t.classList.remove("is-censored"),e.remove()):Swal.fire({icon:"error",title:"Failed to reveal field",text:n.message})})).catch((e=>{Swal.fire({icon:"error",title:"Error revealing field",text:e.toString()})}))}},window.onbeforeunload=function(){if(getChangedFormData(i,"kotaqx_poster_save_settings"))return"You have unsaved changes. Are you sure you want to close this tab/window?"},document.querySelectorAll("[data-required-id][data-required-compare]").forEach((e=>{const t=e.dataset.requiredCompare;let n=[];try{n=JSON.parse(e.dataset.requiredValue||"[]"),Array.isArray(n)||(n=[n])}catch{n=[e.dataset.requiredValue]}let a=[];try{a=JSON.parse(e.dataset.requiredId),Array.isArray(a)||(a=[a])}catch{a=[e.dataset.requiredId]}const o=()=>{let o=[];a.forEach((e=>{const n=`kotaqx_poster_settings[${e}${["in","not_in","min_checked","max_checked"].includes(t)?"[]":""}]`;document.querySelectorAll(`[name="${n}"]`).forEach((e=>{"checkbox"===e.type||"radio"===e.type?e.checked&&o.push(e.value):o.push(e.value)}))}));let s=!1;const r=o[0]??"",i=n[0]??"";switch(t){case"in":s=o.some((e=>n.includes(e)));break;case"not_in":s=o.every((e=>!n.includes(e)));break;case"=":s=o.includes(i);break;case"<>":s=!o.includes(i);break;case"isset":s=o.length===a.length&&o.every((e=>""!==e&&"none"!==e));break;case"empty":s=o.every((e=>""===e||"none"===e));break;case"min_checked":s=o.length>=parseInt(i||"1",10);break;case"max_checked":s=o.length<=parseInt(i||"1",10);break;case">":s=parseFloat(r)>parseFloat(i);break;case"<":s=parseFloat(r)<parseFloat(i);break;case">=":s=parseFloat(r)>=parseFloat(i);break;case"<=":s=parseFloat(r)<=parseFloat(i)}e.style.display=s?"":"none"};a.forEach((e=>{const t=`[name="kotaqx_poster_settings[${e}]"], [name="kotaqx_poster_settings[${e}][]"]`;document.querySelectorAll(t).forEach((e=>{e.addEventListener("change",o)}))})),o()}));const h=document.querySelectorAll(".platform-tab"),m=document.querySelectorAll(".platform-content");h.forEach((e=>{e.addEventListener("click",(()=>{const t=e.getAttribute("data-target");h.forEach((e=>e.classList.remove("is-active"))),e.classList.add("is-active"),m.forEach((e=>{e.id===t?e.style.display="block":e.style.display="none"}))}))})),document.querySelectorAll(".kotaqx-poster-content-preview").forEach((function(e){e.addEventListener("click",(async function(e){e.preventDefault();const t=this.getAttribute("data-platform"),a=this.getAttribute("data-nonce"),o=document.querySelector(`textarea[name="kotaqx_poster_settings[${t}_post_content]"]`),s=o?o.value:"",r=await async function(e){let t=null;const a=async(t="")=>{const a=await async function(e,t=""){const a=await n(ajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({action:"kotaqx_poster_get_post_list",search:t,nonce:e})});return await a.json()}(e,t);return Object.entries(a).map((([e,t])=>`<option value="${e}">${t}</option>`)).join("")};return await Swal.fire({title:"Select Post to Preview",html:'\n                <div class="field">\n                    <label class="label" for="post-search">Search Post</label>\n                    <div class="control has-icons-left">\n                        <input id="post-search" class="input" type="text" placeholder="Search post title...">\n                        <span class="icon is-left">\n                        <i class="fas fa-search"></i>\n                        </span>\n                    </div>\n                    </div>\n\n                    <div class="field">\n                    <label class="label" for="post-select">Select Post</label>\n                    <div class="control">\n                        <div class="select is-fullwidth">\n                        <select id="post-select">\n                            <option disabled selected>Loading post...</option>\n                        </select>\n                        </div>\n                    </div>\n                </div>\n            ',focusConfirm:!1,didOpen:async()=>{const e=document.getElementById("post-select"),t=document.getElementById("post-search");e.innerHTML=await a(),t.addEventListener("input",function(e,t){let n;return function(...a){clearTimeout(n),n=setTimeout((()=>e.apply(this,a)),t)}}((async function(){e.innerHTML=await a(this.value)}),400))},preConfirm:()=>document.getElementById("post-select").value,showCancelButton:!0}).then((e=>{t=e.value})),t}(a);if(!r)return;const i=new FormData;i.append("action","kotaqx_poster_preview_content"),i.append("platform",t),i.append("nonce",a),i.append("post_id",r),i.append("content",s),n(ajaxurl,{method:"POST",body:i}).then((e=>e.text())).then((e=>{Swal.fire({title:"Preview Content",html:e,width:"60%",showCloseButton:!0,focusConfirm:!1,confirmButtonText:"Close"})})).catch((e=>{console.error("Preview error:",e),Swal.fire("Error","Failed to load preview","error")}))}))})),initFormSnapshot(i);const f=document.getElementById("license-submit-btn");f&&f.addEventListener("click",(function(e){e.preventDefault();const t=document.getElementById("license_key").value,a=f.getAttribute("data-action"),o=f.getAttribute("data-nonce"),s=new FormData;s.append("action","kotaqx_poster_handle_license"),s.append("license_key",t),s.append("license_action",a),s.append("license_nonce",o);const r=()=>{n(ajaxurl,{method:"POST",credentials:"same-origin",body:s}).then((e=>e.json())).then((e=>{e.success?(Swal.fire({icon:"success",title:"Success",text:e.data.message||"License action completed."}),e.data?.reload&&location.reload()):Swal.fire({icon:"error",title:"Failed",text:e.data?.message||"License action failed."})})).catch((e=>{Swal.fire({icon:"error",title:"Oops...",text:"Something went wrong!"}),console.error("AJAX Error:",e)}))};"deactivate"===a?(s.delete("license_key"),Swal.fire({title:"Deactivate License?",text:"Are you sure you want to deactivate your license?",icon:"warning",showCancelButton:!0,confirmButtonText:"Yes, deactivate",cancelButtonText:"Cancel"}).then((e=>{e.isConfirmed&&r()}))):r()}));const g=document.querySelector(".activate-license-button");g&&g.addEventListener("click",(function(e){e.preventDefault();const t="license";s(t);const n=new URL(window.location.href);n.searchParams.set("tab",t),window.history.replaceState(null,"",n)}))}));
  • kotaqx-poster/trunk/kotaqx-poster.php

    r3380670 r3381631  
    55 * Plugin URI:        https://kotakdigital.com/downloads/kotaqx-poster/
    66 * Description:       Automatically recover missed schedule posts, and share published content to other platforms.
    7  * Version:           1.0.8
     7 * Version:           1.0.9
    88 * Author:            Kotak Digital
    99 * Author URI:        https://kotakdigital.com
     
    2020
    2121if (!defined('KOTAQX_POSTER_VERSION')) {
    22     define('KOTAQX_POSTER_VERSION', '1.0.8');
     22    define('KOTAQX_POSTER_VERSION', '1.0.9');
    2323}
    2424if (!defined('KOTAQX_POSTER_URL')) {
     
    7979        add_action('kotaqx_poster_schedule_cron', [$this, 'check_and_publish_missed_scheduled_posts']);
    8080        add_action('transition_post_status', [$this, 'handle_republish_post'], 10, 3);
    81         add_action('kotaqx_poster_do_republish_event', [$this, 'do_republish_event'], 10, 1);
     81        add_action('kotaqx_poster_do_republish_event', [$this, 'do_republish_event'], 10, 2);
    8282        add_action('kotaqx_poster_platform_republish_post', [$this, 'handle_republish_platform'], 10, 2);
    8383        add_action('admin_notices', [$this, 'maybe_show_token_expiry_notice']);
     
    275275    public function handle_republish_post($new_status, $old_status, $post) {
    276276        if ($new_status !== 'publish') {
    277             // Only handle when post is published
    278             return;
    279         }
    280 
    281         $settings  = Utils::load_settings();
    282         $repost_on_update = isset($settings['repost_on_update']) ? $settings['repost_on_update'] : false;
    283         $is_update = ($old_status === 'publish' && $new_status === 'publish');
    284 
    285         // Recheck for post status
    286         if ($is_update && !$repost_on_update) return;
    287 
    288         $ID = $post->ID;
     277            // Only process when entering publish status
     278            return;
     279        }
     280
     281        $settings = Utils::load_settings();
     282        $ID       = (int) $post->ID;
     283
     284        // We differentiate between first and republish using the "already published" meta.
     285        // This meta is set when the first publish is scheduled.
     286        $ever_meta_key  = '_kotaqx_poster_ever_published';
     287        $ever_published = (bool) get_post_meta($ID, $ever_meta_key, true);
     288
     289        // Transition classification:
     290        // - publish -> publish : update
     291        // - non-publish -> publish & never published : first
     292        // - non-publish -> publish & already published : republish
     293        if ($old_status === 'publish' && $new_status === 'publish') {
     294            $action = 'update';
     295        } else {
     296            $action = $ever_published ? 'republish' : 'first';
     297        }
     298
     299        // Initial gating according to policy
     300        $repost_on_update    = !empty($settings['repost_on_update']);
     301        $repost_on_republish = !empty($settings['repost_on_republish']);
     302
     303        if ($action === 'update' && !$repost_on_update) {
     304            return; // reupdate is disabled
     305        }
     306        if ($action === 'republish' && !$repost_on_republish) {
     307            return; // republish is disabled
     308        }
     309        // first: always continue
     310
     311        // Lock short scheduling to prevent double scheduling
    289312        $lock_key = 'kotaqx_poster_republish_lock_' . $ID;
    290 
    291313        if (get_transient($lock_key)) {
    292             // Skip if already locked
    293             return;
    294         }
    295 
    296         // Set lock 15s (to prevent double execution)
     314            return;
     315        }
    297316        set_transient($lock_key, true, 15);
    298317
    299         if (!wp_next_scheduled('kotaqx_poster_do_republish_event', [$ID, $new_status, $old_status])) {
    300             // Schedule the event to run
    301             $repost_delay = isset($settings['schedule_repost_interval']) ? $settings['schedule_repost_interval'] : '0'; // Default to 'immediately'
    302             $delay_seconds = 0;
    303 
    304             if ($repost_delay === 'custom') {
    305                 $val = isset($settings['schedule_repost_custom_delay_value']) ? intval($settings['schedule_repost_custom_delay_value']) : 1;
    306                 $unit = isset($settings['schedule_repost_custom_delay_unit']) ? $settings['schedule_repost_custom_delay_unit'] : 'minutes';
    307 
    308                 // Convert custom delay to seconds
    309                 switch ($unit) {
    310                     case 'minute':
    311                     case 'minutes':
    312                         $delay_seconds = $val * 60;
    313                         break;
    314                     case 'hour':
    315                     case 'hours':
    316                         $delay_seconds = $val * 3600;
    317                         break;
    318                     case 'day':
    319                     case 'days':
    320                         $delay_seconds = $val * 86400;
    321                         break;
    322                     default:
    323                         $delay_seconds = 0; // Fallback to 'immediately' if unit is invalid
    324                         break;
    325                 }
    326             } else {
    327                 $delay_seconds = max(0, intval($repost_delay)) * 60;
    328             }
    329 
    330             // Schedule with a delay 5s to ensure the post is fully saved
    331             wp_schedule_single_event(time() + $delay_seconds + 5, 'kotaqx_poster_do_republish_event', [$ID, $new_status, $old_status]);
    332         }
    333     }
     318        // Calculate delay
     319        $repost_delay = isset($settings['schedule_repost_interval']) ? $settings['schedule_repost_interval'] : '0'; // minute or 'custom'
     320        $delay_seconds = 0;
     321
     322        if ($repost_delay === 'custom') {
     323            $val  = isset($settings['schedule_repost_custom_delay_value']) ? max(0, intval($settings['schedule_repost_custom_delay_value'])) : 1;
     324            $unit = isset($settings['schedule_repost_custom_delay_unit']) ? $settings['schedule_repost_custom_delay_unit'] : 'minutes';
     325            switch ($unit) {
     326                case 'minute':
     327                case 'minutes':
     328                    $delay_seconds = $val * 60;
     329                    break;
     330                case 'hour':
     331                case 'hours':
     332                    $delay_seconds = $val * 3600;
     333                    break;
     334                case 'day':
     335                case 'days':
     336                    $delay_seconds = $val * 86400;
     337                    break;
     338                default:
     339                    $delay_seconds = 0;
     340                    break;
     341            }
     342        } else {
     343            $delay_seconds = max(0, intval($repost_delay)) * 60;
     344        }
     345
     346        // Schedule event (new arguments: [$ID, $action])
     347        $timestamp = time() + $delay_seconds + 5; // +5s for the post to be completely saved
     348        $existing  = wp_next_scheduled('kotaqx_poster_do_republish_event', [$ID, $action]);
     349
     350        if (!$existing) {
     351            $scheduled = wp_schedule_single_event($timestamp, 'kotaqx_poster_do_republish_event', [$ID, $action]);
     352        }
     353
     354        // Mark published if this is the first (set immediately so that subsequent determinations are consistent)
     355        if ($action === 'first' && !$ever_published) {
     356            update_post_meta($ID, $ever_meta_key, 1);
     357        }
     358    }
     359
    334360
    335361    /**
    336362     * Execute the republishing event for a given post ID.
    337363     *
    338      * @param int $ID The post ID.
    339      */
    340     public function do_republish_event($ID, $new_status = null, $old_status = null) {
    341         // Cooldown to prevent double execution
     364     * @param int         $ID
     365     * @param string|null $action
     366     * @param mixed       $unused_legacy (old args)
     367     */
     368    public function do_republish_event($ID, $action = null, $unused_legacy = null) {
     369        // Cooldown so it doesn't double run if crons are close together
    342370        $run_lock = 'kotaqx_poster_republish_run_' . $ID;
    343         if (get_transient($run_lock)) return;
    344         set_transient($run_lock, 1, 60); //60s cooldown
     371        if (get_transient($run_lock)) {
     372            return;
     373        }
     374        set_transient($run_lock, 1, 60);
    345375
    346376        $settings = Utils::load_settings();
    347         $post = get_post($ID);
    348         if (!$post) return;
    349 
    350         $repost_on_update = !empty($settings['repost_on_update']);
     377        $post     = get_post($ID);
     378        if (!$post) {
     379            return;
     380        }
     381
     382        // Action normalization (compatibility fallback)
     383        $valid_actions = ['first', 'republish', 'update'];
     384        if (!in_array($action, $valid_actions, true)) {
     385            // Fallback: detection from meta & current state (old event)
     386            if ($post->post_status !== 'publish') {
     387                // No longer publish when cron is running -> cancel
     388                return;
     389            }
     390            $ever_published = (bool) get_post_meta($ID, '_kotaqx_poster_ever_published', true);
     391            $action = $ever_published ? 'republish' : 'first';
     392
     393            // Mark as published if it turns out to be the first
     394            if ($action === 'first' && !$ever_published) {
     395                update_post_meta($ID, '_kotaqx_poster_ever_published', 1);
     396            }
     397        }
     398
     399        // Policy settings
     400        $repost_on_update    = !empty($settings['repost_on_update']);
    351401        $repost_on_republish = !empty($settings['repost_on_republish']);
    352402
    353         $is_publish_transition = ($new_status === 'publish');
    354         $was_published         = ($old_status === 'publish');
    355         $is_update             = ($is_publish_transition && $was_published); // publish -> publish
    356         $is_republish          = ($is_publish_transition && !$was_published); // non-publish -> publish
    357 
    358         // If there is no new status, use the post_date check as a fallback:
    359         if ($new_status === null) {
    360             $is_update    = ($post->post_modified !== $post->post_date);
    361             $is_republish = !$is_update && ($post->post_status === 'publish');
    362         }
    363 
    364         foreach ($this->platforms as $platform) {
    365             $enabled = $settings[$platform . '_enabled'] ?? false;
    366             $post_types = $settings[$platform . '_post_type'] ?? [];
    367 
    368             // Skip if the platform is not active or the post_type does not match
    369             if (!$enabled || !in_array($post->post_type, (array) $post_types)) {
     403        if ($action === 'update' && !$repost_on_update) {
     404            return;
     405        }
     406        if ($action === 'republish' && !$repost_on_republish) {
     407            return;
     408        }
     409        // 'first' always continue
     410
     411        // Loop platform
     412        $platforms_list = isset($this->platforms) ? $this->platforms : [];
     413
     414        foreach ($platforms_list as $platform) {
     415            $enabled    = $settings[$platform . '_enabled'] ?? false;
     416            $post_types = (array)($settings[$platform . '_post_type'] ?? []);
     417
     418            // Skip if platform is off or post type is not suitable
     419            if (!$enabled || !in_array($post->post_type, $post_types, true)) {
    370420                continue;
    371421            }
     
    375425            $post_sent     = ($raw_meta === '1' || $raw_meta === 1 || $raw_meta === true);
    376426
    377             // 1) If this is an UPDATE and the setting is off -> DO NOT send even if the meta doesn't exist yet
    378             if ($is_update && !$repost_on_update) {
    379                 Utils::log_event(false, $ID, "[{$platform}] Error 1.");
     427            // Gating per action
     428            if ($action === 'update' && !$repost_on_update) {
     429                Utils::log_event(false, $ID, "[{$platform}] Update blocked by setting.");
    380430                continue;
    381431            }
    382 
    383             // 2) If this is REPUBLISH and setting off -> DO NOT send even if meta does not exist
    384             if ($is_republish && !$repost_on_republish) {
    385                 Utils::log_event(false, $ID, "[{$platform}] Error 2.");
     432            if ($action === 'republish' && !$repost_on_republish) {
     433                Utils::log_event(false, $ID, "[{$platform}] Republish blocked by setting.");
    386434                continue;
    387435            }
    388436
    389             // 3) If reupdate is allowed and has already been sent, reset the flag to allow sending again.
    390             if ($is_update && $repost_on_update && $post_sent) {
    391                 update_post_meta($ID, $post_meta_key, false);
    392             }
    393 
    394             // 4) If republishing is allowed and has already been sent, reset the flag to allow sending again.
    395             if ($is_republish && $repost_on_republish && $post_sent) {
    396                 update_post_meta($ID, $post_meta_key, false);
    397             }
    398 
    399             // Reposting
     437            // Reset the send flag if the update/republish action is allowed & has already been sent
     438            if (($action === 'update' && $repost_on_update) || ($action === 'republish' && $repost_on_republish)) {
     439                if ($post_sent) {
     440                    update_post_meta($ID, $post_meta_key, false);
     441                }
     442            }
     443
     444            // First publish: continue (meta is usually not present yet). If present, submit anyway—hand it to the handler for additional validation.
    400445            Handler::repost_to($platform, $ID);
     446        }
     447
     448        // Make sure the "ever published" meta is installed (if the scenario is first)
     449        if ($action === 'first') {
     450            update_post_meta($ID, '_kotaqx_poster_ever_published', 1);
    401451        }
    402452    }
Note: See TracChangeset for help on using the changeset viewer.