Changeset 3458296
- Timestamp:
- 02/10/2026 05:33:50 PM (7 weeks ago)
- Location:
- groundhogg
- Files:
-
- 30 added
- 26 edited
- 1 copied
-
tags/4.2.12 (copied) (copied from groundhogg/trunk)
-
tags/4.2.12/README.txt (modified) (2 diffs)
-
tags/4.2.12/admin/broadcasts/broadcasts-table.php (modified) (1 diff)
-
tags/4.2.12/admin/campaigns/campaigns-table.php (modified) (1 diff)
-
tags/4.2.12/admin/emails/emails-table.php (modified) (1 diff)
-
tags/4.2.12/admin/reports/views/broadcast-single.php (modified) (1 diff)
-
tags/4.2.12/assets/images/social-icons/black-boxed/bluesky.png (added)
-
tags/4.2.12/assets/images/social-icons/black-circle/bluesky.png (added)
-
tags/4.2.12/assets/images/social-icons/black-icons/bluesky.png (added)
-
tags/4.2.12/assets/images/social-icons/brand-boxed/bluesky.png (added)
-
tags/4.2.12/assets/images/social-icons/brand-circle/bluesky.png (added)
-
tags/4.2.12/assets/images/social-icons/brand-icons/bluesky.png (added)
-
tags/4.2.12/assets/images/social-icons/dark-grey-boxed/bluesky.png (added)
-
tags/4.2.12/assets/images/social-icons/dark-grey-circle/bluesky.png (added)
-
tags/4.2.12/assets/images/social-icons/dark-grey-icons/bluesky.png (added)
-
tags/4.2.12/assets/images/social-icons/grey-boxed/bluesky.png (added)
-
tags/4.2.12/assets/images/social-icons/grey-circle/bluesky.png (added)
-
tags/4.2.12/assets/images/social-icons/grey-icons/bluesky.png (added)
-
tags/4.2.12/assets/images/social-icons/white-boxed/bluesky.png (added)
-
tags/4.2.12/assets/images/social-icons/white-circle/bluesky.png (added)
-
tags/4.2.12/assets/images/social-icons/white-icons/bluesky.png (added)
-
tags/4.2.12/assets/js/admin/components.js (modified) (1 diff)
-
tags/4.2.12/assets/js/admin/components.min.js (modified) (1 diff)
-
tags/4.2.12/assets/js/admin/emails/email-block-editor.js (modified) (1 diff)
-
tags/4.2.12/assets/js/admin/emails/email-block-editor.min.js (modified) (1 diff)
-
tags/4.2.12/groundhogg.php (modified) (2 diffs)
-
tags/4.2.12/includes/classes/funnel.php (modified) (2 diffs)
-
tags/4.2.12/includes/form/form-v2.php (modified) (4 diffs)
-
tags/4.2.12/includes/functions.php (modified) (5 diffs)
-
trunk/README.txt (modified) (2 diffs)
-
trunk/admin/broadcasts/broadcasts-table.php (modified) (1 diff)
-
trunk/admin/campaigns/campaigns-table.php (modified) (1 diff)
-
trunk/admin/emails/emails-table.php (modified) (1 diff)
-
trunk/admin/reports/views/broadcast-single.php (modified) (1 diff)
-
trunk/assets/images/social-icons/black-boxed/bluesky.png (added)
-
trunk/assets/images/social-icons/black-circle/bluesky.png (added)
-
trunk/assets/images/social-icons/black-icons/bluesky.png (added)
-
trunk/assets/images/social-icons/brand-boxed/bluesky.png (added)
-
trunk/assets/images/social-icons/brand-circle/bluesky.png (added)
-
trunk/assets/images/social-icons/brand-icons/bluesky.png (added)
-
trunk/assets/images/social-icons/dark-grey-boxed/bluesky.png (added)
-
trunk/assets/images/social-icons/dark-grey-circle/bluesky.png (added)
-
trunk/assets/images/social-icons/dark-grey-icons/bluesky.png (added)
-
trunk/assets/images/social-icons/grey-boxed/bluesky.png (added)
-
trunk/assets/images/social-icons/grey-circle/bluesky.png (added)
-
trunk/assets/images/social-icons/grey-icons/bluesky.png (added)
-
trunk/assets/images/social-icons/white-boxed/bluesky.png (added)
-
trunk/assets/images/social-icons/white-circle/bluesky.png (added)
-
trunk/assets/images/social-icons/white-icons/bluesky.png (added)
-
trunk/assets/js/admin/components.js (modified) (1 diff)
-
trunk/assets/js/admin/components.min.js (modified) (1 diff)
-
trunk/assets/js/admin/emails/email-block-editor.js (modified) (1 diff)
-
trunk/assets/js/admin/emails/email-block-editor.min.js (modified) (1 diff)
-
trunk/groundhogg.php (modified) (2 diffs)
-
trunk/includes/classes/funnel.php (modified) (2 diffs)
-
trunk/includes/form/form-v2.php (modified) (4 diffs)
-
trunk/includes/functions.php (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
groundhogg/tags/4.2.12/README.txt
r3442828 r3458296 7 7 Tested up to: 6.9 8 8 Requires PHP: 7.1 9 Stable tag: 4.2.1 09 Stable tag: 4.2.12 10 10 License: GPLv3 11 11 License URI: https://www.gnu.org/licenses/gpl.md … … 378 378 379 379 == Changelog == 380 381 = 4.2.12 (2026-02-10) = 382 * ADDED Bluesky social icons for the email editor. 383 * ADDED Bulk delete bulk action for campaigns. 384 * FIXED Email preview link stopped working in the emails table. 385 * FIXED Notice generated by improper array access check when submitting a form. 380 386 381 387 = 4.2.11 (2026-01-19) = -
groundhogg/tags/4.2.12/admin/broadcasts/broadcasts-table.php
r3400645 r3458296 245 245 246 246 if ( $broadcast->is_email() ){ 247 $actions[] = html()->a( '# ', esc_html__( 'Preview', 'groundhogg' ), [ 'class' => 'gh-email-preview', 'data-id' => $broadcast->get_object_id() ]);247 $actions[] = html()->a( '#gh-email-preview/' . $broadcast->get_object_id(), esc_html__( 'Preview', 'groundhogg' ) ); 248 248 } 249 249 -
groundhogg/tags/4.2.12/admin/campaigns/campaigns-table.php
r3343709 r3458296 47 47 // Set parent defaults. 48 48 parent::__construct( array( 49 'singular' => ' tag', // Singular name of the listed records.50 'plural' => ' tags', // Plural name of the listed records.49 'singular' => 'campaign', // Singular name of the listed records. 50 'plural' => 'campaigns', // Plural name of the listed records. 51 51 'ajax' => false, // Does this table support ajax? 52 52 ) ); 53 } 54 55 /** 56 * @return array An associative array containing all the bulk actions. 57 */ 58 protected function get_bulk_actions() { 59 60 $actions = array( 61 'delete' => _x( 'Delete', 'List table bulk action', 'groundhogg' ), 62 ); 63 64 return apply_filters( 'groundhogg/admin/campaigns/table/bulk_actions', $actions ); 53 65 } 54 66 -
groundhogg/tags/4.2.12/admin/emails/emails-table.php
r3400645 r3458296 331 331 default: 332 332 $actions[] = [ 'class' => 'edit', 'display' => esc_html__( 'Edit' , 'groundhogg' ), 'url' => $item->admin_link() ]; 333 $actions[] = [ 'class' => 'gh-email-preview', 'display' => esc_html__( 'Preview' , 'groundhogg' ), 'url' => '# '];333 $actions[] = [ 'class' => 'gh-email-preview', 'display' => esc_html__( 'Preview' , 'groundhogg' ), 'url' => '#gh-email-preview/' . $item->get_id() ]; 334 334 $actions[] = [ 335 335 'class' => 'duplicate', -
groundhogg/tags/4.2.12/admin/reports/views/broadcast-single.php
r3400645 r3458296 17 17 <h1 class="report-title"><?php echo esc_html( $broadcast->get_title() ) ?></h1> 18 18 <?php if ( $broadcast->is_email() ): ?> 19 <a href="# " class="gh-button secondary gh-email-preview" data-id="<?php echo esc_attr( $broadcast->get_object_id() ); ?>"><?php esc_html_e( 'Preview', 'groundhogg' ); ?></a>19 <a href="#gh-email-preview/<?php echo esc_attr( $broadcast->get_object_id() ); ?>" class="gh-button secondary"><?php esc_html_e( 'Preview', 'groundhogg' ); ?></a> 20 20 <?php endif; ?> 21 21 </div> -
groundhogg/tags/4.2.12/assets/js/admin/components.js
r3422142 r3458296 1796 1796 EmailPreviewModal(parseInt(emailId), {}) 1797 1797 }) 1798 1799 window.addEventListener('hashchange', e => { 1800 let hash = window.location.hash.replace('#', '') 1801 if ( hash.startsWith('gh-email-preview/') ) { 1802 let emailId = hash.replace('gh-email-preview/', '') 1803 EmailPreviewModal(parseInt(emailId), {}) 1804 } 1805 }) 1798 1806 }) 1799 1807 -
groundhogg/tags/4.2.12/assets/js/admin/components.min.js
r3422142 r3458296 216 216 <div id="uploading-files"></div> 217 217 <div id="uploaded-files"></div> 218 `,onOpen:({close})=>{let file=null;let filesToUpload=[];let filesUploaded=[];let uploading=false;const pushFiles=()=>{renderUploadingFiles();file=filesToUpload.pop();if(!file){uploading=false;return}uploading=true;let fd=new FormData;fd.append(fileName,file,file.name);fd.append("gh_admin_ajax_nonce",Groundhogg.nonces._adminajax);fd.append("action",action);beforeUpload(fd);setTimeout(()=>{fetch(ajaxurl,{method:"POST",credentials:"same-origin",body:fd}).then(r=>{if(!r.ok){dialog({message:__("Something when wrong..."),type:"error"});return}return r.json()}).then(r=>{if(!r.success){dialog({message:r.data[0].message,type:"error"});pushFiles();return}onUpload(r,file);filesUploaded.unshift(file);renderUploadedFiles();pushFiles()})},2e3)};const renderUploadingFiles=()=>{$("#uploading-files").html(filesToUpload.map(f=>`<div class="file"><span class="hourglass">⌛</span> ${f.name}</div>`))};const renderUploadedFiles=()=>{$("#uploaded-files").html(filesUploaded.map(f=>`<div class="file">✅ ${f.name}</div>`))};const addFiles=files=>{filesToUpload.push(...files);if(!uploading){pushFiles()}};const $input=$("#upload-file-input");$input.on("change",e=>{addFiles(e.target.files)});$("#select-files").on("click",e=>{e.preventDefault();$input.click()});const $droppable=$(".droppable-handler");$droppable.on("dragover",e=>{e.preventDefault();$droppable.addClass("dragover")}).on("dragleave",e=>{$droppable.removeClass("dragover")}).on("drop",e=>{e.preventDefault();$droppable.removeClass("dragover");let{dataTransfer}=e.originalEvent;addFiles(dataTransfer.files)})}})};const EmailPreviewModal=async(emailId,{height=window.innerHeight*.85,width=900})=>{const{close}=loadingModal();let email;try{email=await EmailsStore.maybeFetchItem(emailId)}catch(err){close();throw err}const{from_avatar,from_email,from_name,subject,built:content}=email.context;close();return ModalFrame({frameAttributes:{className:"gh-modal-frame gh-email-preview-modal"}},({close})=>Div({style:{width:`${width}px`,height:`${height}px`}},EmailPreview({close:close,from_avatar:from_avatar,from_email:from_email,from_name:from_name,subject:subject,content:content})))};const EmailPreview=({close=false,from_avatar,from_email,from_name,subject,content})=>{return Div({className:"email-preview"},[Div({className:"from-preview display-flex gap-20 has-box-shadow"},[makeEl("img",{src:from_avatar,className:"from-avatar",height:40,width:40,style:{borderRadius:"50%"}}),Div({className:"subject-and-from"},[`<h2>${subject}</h2>`,`<span class="from-name">${from_name}</span> <span class="from-email"><${from_email}></span>`]),close!==false?Button({className:"gh-button secondary icon text",style:{marginLeft:"auto"},onClick:close},Dashicon("no-alt")):null]),Iframe({id:"desktop-preview-iframe"},content)])};$(()=>{$(document).on("click","a.gh-email-preview",e=>{e.preventDefault();let emailId=e.currentTarget.dataset.id??e.currentTarget.closest("tr").id;EmailPreviewModal(parseInt(emailId),{})}) });const ImagePicker=({multiple=false,title=__("Select a image to upload"),selectText=__("Use this image"),onChange=attachment=>{}})=>{let file_frame=wp.media({title:title,button:{text:selectText},multiple:multiple});file_frame.on("select",function(){let attachment=file_frame.state().get("selection").first().toJSON();onChange(attachment)});file_frame.open()};const ImageInput=({id,name="src",onChange,value=""})=>{const handleChange=(value,attachment=null)=>{onChange(value,attachment);morphdom(document.getElementById(id),ImageInput({id:id,name:name,onChange:onChange,value:value}))};return Div({id:id,className:"image-picker"},[value?Div({id:`${id}-preview`,className:"image-input-preview",style:{backgroundImage:`url(${value})`},onClick:e=>{e.preventDefault();ImagePicker({multiple:false,onChange:attachment=>handleChange(attachment.url,attachment)})}}):null,InputGroup([Input({type:"text",id:`${id}-src`,value:value,className:"control full-width",name:name,onChange:e=>{handleChange(e.target.value,null)}}),Button({id:`${id}-select`,className:"gh-button secondary icon",onClick:e=>{e.preventDefault();ImagePicker({multiple:false,onChange:attachment=>handleChange(attachment.url,attachment)})}},icons.image)])])};const FeedbackModal=({subject="",message="",onSubmit=r=>{}})=>{const State=Groundhogg.createState({subject:subject,message:message,submitting:false});ModalWithHeader({width:"400px",header:"Send Feedback"},({close,morph})=>Form({className:"display-flex column gap-5",onSubmit:e=>{e.preventDefault();State.set({submitting:true});morph();Groundhogg.api.ajax({action:"gh_plugin_feedback",subject:State.subject,message:State.message}).then(r=>{onSubmit(r);dialog({message:"Thanks for your feedback!"});close()});return false}},[Label({for:"feedback-subject"},["What feature are you submitting feedback for?"]),Input({id:"feedback-subject",value:State.subject,required:true,onInput:e=>State.set({subject:e.target.value})}),Div(),Label({for:"feedback-message"},["What is your feedback? Be as descriptive as possible."]),Textarea({id:"feedback-message",value:State.message,required:true,rows:4,onInput:e=>State.set({message:e.target.value})}),Button({className:"gh-button primary",type:"submit",disabled:State.submitting},"Send feedback"),Pg({},"Your email address will be collected to validate your feedback, but will not be used beyond that.")]))};$(document).on("click","a.feedback-modal",e=>{e.preventDefault();const{subject="",message=""}=e.currentTarget.dataset;FeedbackModal({subject:subject,message:message})});const ContactPhone=(icon,number,extension="")=>number?Span({className:"contact-phone"},[icon,An({href:`tel:${number}`},number),extension?Span({className:"ext"},` x${extension}`):null]):null;const ContactListItem=(item,{extra=item=>null,...props}={})=>{let allTags=jsonCopy(item.tags);let showTags=allTags.splice(0,10);const{ID}=item;const{full_name,gravatar,date_created,email}=item.data;const{primary_phone="",primary_phone_extension="",mobile_phone="",company_phone="",company_phone_extension=""}=item.meta;return Div({className:`contact-list-item`,id:`contact-list-item-${ID}`,dataId:ID,...props},[Div({className:"display-flex gap-10"},[Img({className:"avatar",src:gravatar,alt:"avatar"}),Div({className:"display-flex column"},[Div({},[makeEl("h4",{style:{margin:0}},full_name),Span({className:"subscribed"},` — ${sprintf(__("Subscribed %s"),`<abbr title="${formatDateTime(date_created)}">${sprintf(__("%s ago "),item.i18n.created)}</abbr>`)}`)]),Div({},[An({href:`mailto:${email}`},email),Span({},[" — ",Span({className:`gh-text ${item.is_marketable?"green":"red"}`},Groundhogg.filters.optin_status[item.data.optin_status])])])])]),Div({className:"show-on-hover"},[primary_phone||company_phone||mobile_phone?Div({className:"contact-phones"},[ContactPhone(icons.mobile,mobile_phone),ContactPhone(icons.phone,primary_phone,primary_phone_extension),ContactPhone(icons.phone,company_phone,company_phone_extension)]):null,Div({className:"gh-tags"},[...showTags.map(tag=>Span({className:"gh-tag"},tag.data.tag_name)),allTags.length?Span({},sprintf("and %d more...",allTags.length)):null]),maybeCall(extra,item)])])};const ContactList=(contacts=[],{noContacts=()=>null,itemProps={}}={})=>{if(!contacts.length){return maybeCall(noContacts)}return Div({className:"contact-list"},contacts.map(contact=>ContactListItem(contact,maybeCall(itemProps,contact))))};const QuickSearch=({itemProps={},queryOverrides={}}={})=>{const State=Groundhogg.createState({search:"",searched:false,results:[],loaded:false});const fetchResults=async()=>{let results=await ContactsStore.fetchItems({search:State.search,orderby:"date_created",order:"DESC",limit:5,...queryOverrides});State.set({results:results,searched:true,loaded:true})};return Div({id:"quick-search-wrap"},morph=>{if(!State.loaded){fetchResults().then(morph)}const updateResults=debounce(async()=>{await fetchResults();morph()},300);return Fragment([Form({action:adminPageURL("gh_contacts")},[Input({type:"hidden",name:"page",value:"gh_contacts"}),Input({id:"quick-search-input",placeholder:__("Search by name or email...","groundhogg"),type:"search",name:"s",value:State.search,onInput:e=>{State.set({search:e.target.value});updateResults()}})]),State.loaded?null:Skeleton({},["full","full","full"]),State.results.length?ContactList(State.results,{itemProps:item=>({className:"contact-list-item clickable",onClick:e=>{window.open(item.admin,"_self")},...maybeCall(itemProps,item)})}):null,State.results.length===0&&State.searched?Pg({style:{textAlign:"center"}},__("No contacts found for the current search","groundhogg")):null])})};const Panel=({id,name,collapsed=false,hidden=false,onCollapse=id=>{}},content)=>{if(hidden){return null}return Div({id:`${id}-panel`,className:`gh-panel ${collapsed?"closed":""}`},[Div({className:`gh-panel-header`},[H2({},name),Button({className:"toggle-indicator",onClick:e=>{onCollapse(id)}})]),collapsed?null:maybeCall(content)])};const Panels=overrides=>({...Groundhogg.createRegistry({}),storagePrefix:"gh-panels",collapse(id){if(!this.isCollapsed(id)){this.toggleCollapse(id)}},expand(id){if(this.isCollapsed(id)){this.toggleCollapse(id)}},hide(id){if(!this.isHidden(id)){this.toggleHidden(id)}},show(id){if(this.isHidden(id)){this.toggleHidden(id)}},togglePanel(id,suffix){let panels=this.getPanelIds(suffix);if(panels.includes(id)){panels.splice(panels.indexOf(id),1)}else{panels.push(id)}localStorage.setItem(`${this.storagePrefix}-${suffix}`,JSON.stringify(panels))},toggleHidden(id){this.togglePanel(id,"hidden")},toggleCollapse(id){this.togglePanel(id,"collapsed")},getPanelIds(suffix){return JSON.parse(localStorage.getItem(`${this.storagePrefix}-${suffix}`))||[]},getHiddenPanelIds(){return this.getPanelIds("hidden")},getCollapsedPanelIds(){return this.getPanelIds("collapsed")},isHidden(id){return this.getHiddenPanelIds().includes(id)},isCollapsed(id){return this.getCollapsedPanelIds().includes(id)},PanelControls(){return Div({},[...this.map((item,id)=>Div({className:"display-flex gap-10",style:{marginBottom:"10px"}},[Toggle({checked:!this.isHidden(id),id:`toggle-${id}`,onChange:e=>{this.toggleHidden(id)}}),Label({for:`toggle-${id}`},item.name)]))])},Panel(id){let{content,...panel}=this.get(id);return Panel({id:id,...panel,collapsed:this.isCollapsed(id),hidden:this.isHidden(id),onCollapse:id=>{this.toggleCollapse(id);morphdom(document.getElementById(`${id}-panel`),this.Panel(id))}},content)},Panels(){return Div({className:"display-flex column gap-20",id:this.storagePrefix},this.keys().map(id=>this.Panel(id)))},...overrides});const Relationships=({title="",id,store,child_type="",parent_type="",renderItem=item=>{},onAddItem=(r,j)=>{}})=>{const rel_type_key=child_type?"child_type":"parent_type";const rel_type=child_type||parent_type;const rel_id_key=child_type?"child_id":"parent_id";const State=Groundhogg.createState({loaded:false,items:[]});const fetchRelationships=()=>store.fetchRelationships(id,{[rel_type_key]:rel_type}).then(items=>State.set({items:items,loaded:true}));const deleteRelationship=itemId=>store.deleteRelationships(id,{[rel_type_key]:rel_type,[rel_id_key]:itemId}).then(()=>State.set({items:State.items.filter(item=>item.ID!==itemId)}));const createRelationship=item=>store.createRelationships(id,{[rel_type_key]:rel_type,[rel_id_key]:item.ID}).then(()=>State.set({items:[...State.items,item]}));return Div({id:`${rel_type_key}-${rel_type}-rel-of-${id}`,className:`display-flex column relationship-editor ${rel_type_key}-${rel_type}`},morph=>{const handleDeleteRelationship=itemId=>deleteRelationship(itemId).then(morph);if(!State.loaded){fetchRelationships().then(morph);return Skeleton({},["full","full","full"])}const AddRelButton=()=>Button({id:`add-${rel_type_key}-${rel_type}-rel-for-${id}`,className:"gh-button secondary text icon",onClick:e=>{let promise=new Promise((resolve,reject)=>onAddItem(resolve,reject,State));promise.then(item=>createRelationship(item).then(morph))}},[Dashicon("plus-alt2"),ToolTip(__("Add relationship","groundhogg"),"left")]);return Fragment([title?Div({className:"space-between"},[H4({},title),AddRelButton()]):null,...State.items.map(item=>renderItem({...item,onDelete:handleDeleteRelationship})),title?null:Div({className:"display-flex flex-end"},AddRelButton())])})};const OwnerPicker=({id="select-owners",selected=[],onChange=ids=>{},allow0=true,itemDisplay=user=>user.data.display_name,multiple=true,...overrides})=>ItemPicker({id:`select-users`,noneSelected:__("Select a user...","groundhogg"),selected:selected.map(user_id=>{if(user_id==0&&allow0){return{id:0,text:__("The contact owner","groundhogg")}}return{id:user_id,text:itemDisplay(getOwner(user_id))}}),multiple:multiple,style:{flexGrow:1},isValidSelection:id=>id===0||getOwner(id),fetchOptions:search=>{search=new RegExp(search,"i");let options=Groundhogg.filters.owners.map(u=>({id:u.ID,text:itemDisplay(u)}));if(allow0){options.push({id:0,text:__("The contact owner","groundhogg")})}options=options.filter(({text})=>text.match(search));return Promise.resolve(options)},onChange:items=>{if(multiple){onChange(items.map(({id})=>id));return}onChange(items)},...overrides});function getClosestRelativeAncestor(element){let parent=element.parentElement;while(parent){if(window.getComputedStyle(parent).position==="relative"){return parent}parent=parent.parentElement}return null}const Tour=(steps,{onFinish=()=>{},beforeDismiss=({dismiss})=>dismiss(),onDismiss=()=>{},fixed=false})=>{const State=Groundhogg.createState({current:0,step:null,target:null,relative:null});const currentStep=()=>steps[State.current];const removeSteps=()=>{document.querySelectorAll(".tour-prompt-container").forEach(el=>el.remove());document.querySelectorAll(".tour-prompt").forEach(el=>el.remove());document.querySelectorAll(".tour-highlighted").forEach(el=>el.classList.remove("tour-highlighted"))};const remove=()=>{removeSteps();document.removeEventListener("resize",rePositionStep);document.removeEventListener("scroll",rePositionStep)};const dismiss=async()=>{remove();onDismiss()};const next=()=>{const{onNext=()=>{}}=currentStep();onNext();if(State.current+1>=steps.length){remove();onFinish(true);return}State.current++;showStep()};const prev=()=>{const{onPrev=()=>{}}=currentStep();onPrev();if(State.current<=0){return}State.current--;showStep()};const showStep=()=>{removeSteps();positionStep()};function rePositionStep(){let{position}=currentStep();let{target,relative,step,windowEl}=State;const targetPos=target.getBoundingClientRect();const relativePos=relative.getBoundingClientRect();const stepPos=step.getBoundingClientRect();const gap=20;if(fixed){windowEl.style.height=`${targetPos.height+gap*2}px`;windowEl.style.width=`${targetPos.width+gap*2}px`;windowEl.style.borderWidth=`${Math.round(targetPos.y)-gap}px ${Math.round(window.innerWidth-targetPos.x)-gap}px ${Math.round(window.innerHeight-targetPos.y)-gap}px ${Math.round(targetPos.x)-gap}px`;switch(position){case"right":step.style.left=`${targetPos.x+targetPos.width+gap}px`;step.style.top=`${targetPos.y}px`;break;case"left":step.style.left=`${targetPos.x-stepPos.width-gap}px`;step.style.top=`${targetPos.y}px`;break;case"above":step.style.left=`${targetPos.x}px`;step.style.top=`${targetPos.y-stepPos.height-gap}px`;break;case"below":step.style.left=`${targetPos.x}px`;step.style.top=`${targetPos.y+targetPos.height+gap}px`;break;case"below-left":step.style.left=`${targetPos.x+targetPos.width-stepPos.width}px`;step.style.top=`${targetPos.y+targetPos.height+gap}px`;break}return}switch(position){case"right":step.style.left=`${targetPos.right-relativePos.left+gap}px`;step.style.top=`${targetPos.top-relativePos.top}px`;break;case"left":step.style.left=`${targetPos.left-relativePos.left-stepPos.width-gap}px`;step.style.top=`${targetPos.top-relativePos.top}px`;break;case"above":step.style.left=`${targetPos.left-relativePos.left}px`;step.style.top=`${targetPos.top-relativePos.top-stepPos.height-gap}px`;break;case"below":step.style.left=`${targetPos.left-relativePos.left}px`;step.style.top=`${targetPos.bottom-relativePos.top+gap}px`;break;case"below-left":step.style.left=`${targetPos.right-relativePos.left-stepPos.width}px`;step.style.top=`${targetPos.bottom-relativePos.top+gap}px`;break}}function positionStep(){let tourEl=TourStep();let windowEl=tourEl.querySelector(".tour-window");let stepEl=tourEl.querySelector(".tour-prompt");let{target,relative,onInit=()=>{},onBefore=()=>{}}=currentStep();target=document.querySelector(target);if(!target){next();return}if(fixed){relative=document.body}else if(relative){relative=target.closest(relative)}else{relative=getClosestRelativeAncestor(target)}stepEl.style.position=fixed?"fixed":"absolute";if(fixed){relative.append(tourEl)}else{target.classList.add("tour-highlighted");relative.append(stepEl)}target.scrollIntoView({behavior:"instant",block:"center",inline:"center"});onBefore({next:next,prev:prev,target:target,relative:relative,step:stepEl,windowEl:windowEl,currentStep:currentStep});State.set({step:stepEl,windowEl:windowEl,target:target,relative:relative});rePositionStep();onInit({next:next,prev:prev,target:target,relative:relative,step:stepEl,windowEl:windowEl,currentStep:currentStep});stepEl.querySelector("#tour-next").focus()}const TourStep=()=>MakeEl.Div({className:"tour-prompt-container"},[fixed?MakeEl.Div({className:"tour-window"},[MakeEl.Div({className:"tour-window-shadow",onClick:next})]):null,MakeEl.Div({className:`tour-prompt ${currentStep().position}`,style:{padding:"10px",width:"200px"}},[MakeEl.Button({className:"dismiss",onClick:e=>{beforeDismiss({dismiss:dismiss,State:State})}},MakeEl.Dashicon("no-alt")),MakeEl.Div({},currentStep().prompt),MakeEl.Div({className:"display-flex flex-end gap-5 space-above-10"},[State.current>0?MakeEl.Button({id:"tour-prev",className:"gh-button small secondary text prev-step",onClick:()=>prev()},"Prev"):null,currentStep().showNext===false?null:MakeEl.Button({id:"tour-next",className:`gh-button small ${State.current<steps.length-1?"secondary":"primary"} next-step`,onClick:()=>next()},State.current<steps.length-1?"Next":"Finish")])])]);document.addEventListener("resize",rePositionStep);document.addEventListener("scroll",rePositionStep);positionStep()};Groundhogg.components={QuickSearch:QuickSearch,addContactModal:addContactModal,internalForm:internalForm,betterTagPicker:betterTagPicker,quickAddForm:quickAddForm,selectContactModal:selectContactModal,quickEditContactModal:quickEditContactModal,makeInput:makeInput,emailModal:emailModal,EmailTemplateModal:EmailTemplateModal,fileUploader:fileUploader,EmailPreview:EmailPreview,EmailPreviewModal:EmailPreviewModal,ImageInput:ImageInput,ImagePicker:ImagePicker,FeedbackModal:FeedbackModal,ContactList:ContactList,ContactListItem:ContactListItem,Panel:Panel,Panels:Panels,Relationships:Relationships,OwnerPicker:OwnerPicker,Tour:Tour}})(jQuery);218 `,onOpen:({close})=>{let file=null;let filesToUpload=[];let filesUploaded=[];let uploading=false;const pushFiles=()=>{renderUploadingFiles();file=filesToUpload.pop();if(!file){uploading=false;return}uploading=true;let fd=new FormData;fd.append(fileName,file,file.name);fd.append("gh_admin_ajax_nonce",Groundhogg.nonces._adminajax);fd.append("action",action);beforeUpload(fd);setTimeout(()=>{fetch(ajaxurl,{method:"POST",credentials:"same-origin",body:fd}).then(r=>{if(!r.ok){dialog({message:__("Something when wrong..."),type:"error"});return}return r.json()}).then(r=>{if(!r.success){dialog({message:r.data[0].message,type:"error"});pushFiles();return}onUpload(r,file);filesUploaded.unshift(file);renderUploadedFiles();pushFiles()})},2e3)};const renderUploadingFiles=()=>{$("#uploading-files").html(filesToUpload.map(f=>`<div class="file"><span class="hourglass">⌛</span> ${f.name}</div>`))};const renderUploadedFiles=()=>{$("#uploaded-files").html(filesUploaded.map(f=>`<div class="file">✅ ${f.name}</div>`))};const addFiles=files=>{filesToUpload.push(...files);if(!uploading){pushFiles()}};const $input=$("#upload-file-input");$input.on("change",e=>{addFiles(e.target.files)});$("#select-files").on("click",e=>{e.preventDefault();$input.click()});const $droppable=$(".droppable-handler");$droppable.on("dragover",e=>{e.preventDefault();$droppable.addClass("dragover")}).on("dragleave",e=>{$droppable.removeClass("dragover")}).on("drop",e=>{e.preventDefault();$droppable.removeClass("dragover");let{dataTransfer}=e.originalEvent;addFiles(dataTransfer.files)})}})};const EmailPreviewModal=async(emailId,{height=window.innerHeight*.85,width=900})=>{const{close}=loadingModal();let email;try{email=await EmailsStore.maybeFetchItem(emailId)}catch(err){close();throw err}const{from_avatar,from_email,from_name,subject,built:content}=email.context;close();return ModalFrame({frameAttributes:{className:"gh-modal-frame gh-email-preview-modal"}},({close})=>Div({style:{width:`${width}px`,height:`${height}px`}},EmailPreview({close:close,from_avatar:from_avatar,from_email:from_email,from_name:from_name,subject:subject,content:content})))};const EmailPreview=({close=false,from_avatar,from_email,from_name,subject,content})=>{return Div({className:"email-preview"},[Div({className:"from-preview display-flex gap-20 has-box-shadow"},[makeEl("img",{src:from_avatar,className:"from-avatar",height:40,width:40,style:{borderRadius:"50%"}}),Div({className:"subject-and-from"},[`<h2>${subject}</h2>`,`<span class="from-name">${from_name}</span> <span class="from-email"><${from_email}></span>`]),close!==false?Button({className:"gh-button secondary icon text",style:{marginLeft:"auto"},onClick:close},Dashicon("no-alt")):null]),Iframe({id:"desktop-preview-iframe"},content)])};$(()=>{$(document).on("click","a.gh-email-preview",e=>{e.preventDefault();let emailId=e.currentTarget.dataset.id??e.currentTarget.closest("tr").id;EmailPreviewModal(parseInt(emailId),{})});window.addEventListener("hashchange",e=>{let hash=window.location.hash.replace("#","");if(hash.startsWith("gh-email-preview/")){let emailId=hash.replace("gh-email-preview/","");EmailPreviewModal(parseInt(emailId),{})}})});const ImagePicker=({multiple=false,title=__("Select a image to upload"),selectText=__("Use this image"),onChange=attachment=>{}})=>{let file_frame=wp.media({title:title,button:{text:selectText},multiple:multiple});file_frame.on("select",function(){let attachment=file_frame.state().get("selection").first().toJSON();onChange(attachment)});file_frame.open()};const ImageInput=({id,name="src",onChange,value=""})=>{const handleChange=(value,attachment=null)=>{onChange(value,attachment);morphdom(document.getElementById(id),ImageInput({id:id,name:name,onChange:onChange,value:value}))};return Div({id:id,className:"image-picker"},[value?Div({id:`${id}-preview`,className:"image-input-preview",style:{backgroundImage:`url(${value})`},onClick:e=>{e.preventDefault();ImagePicker({multiple:false,onChange:attachment=>handleChange(attachment.url,attachment)})}}):null,InputGroup([Input({type:"text",id:`${id}-src`,value:value,className:"control full-width",name:name,onChange:e=>{handleChange(e.target.value,null)}}),Button({id:`${id}-select`,className:"gh-button secondary icon",onClick:e=>{e.preventDefault();ImagePicker({multiple:false,onChange:attachment=>handleChange(attachment.url,attachment)})}},icons.image)])])};const FeedbackModal=({subject="",message="",onSubmit=r=>{}})=>{const State=Groundhogg.createState({subject:subject,message:message,submitting:false});ModalWithHeader({width:"400px",header:"Send Feedback"},({close,morph})=>Form({className:"display-flex column gap-5",onSubmit:e=>{e.preventDefault();State.set({submitting:true});morph();Groundhogg.api.ajax({action:"gh_plugin_feedback",subject:State.subject,message:State.message}).then(r=>{onSubmit(r);dialog({message:"Thanks for your feedback!"});close()});return false}},[Label({for:"feedback-subject"},["What feature are you submitting feedback for?"]),Input({id:"feedback-subject",value:State.subject,required:true,onInput:e=>State.set({subject:e.target.value})}),Div(),Label({for:"feedback-message"},["What is your feedback? Be as descriptive as possible."]),Textarea({id:"feedback-message",value:State.message,required:true,rows:4,onInput:e=>State.set({message:e.target.value})}),Button({className:"gh-button primary",type:"submit",disabled:State.submitting},"Send feedback"),Pg({},"Your email address will be collected to validate your feedback, but will not be used beyond that.")]))};$(document).on("click","a.feedback-modal",e=>{e.preventDefault();const{subject="",message=""}=e.currentTarget.dataset;FeedbackModal({subject:subject,message:message})});const ContactPhone=(icon,number,extension="")=>number?Span({className:"contact-phone"},[icon,An({href:`tel:${number}`},number),extension?Span({className:"ext"},` x${extension}`):null]):null;const ContactListItem=(item,{extra=item=>null,...props}={})=>{let allTags=jsonCopy(item.tags);let showTags=allTags.splice(0,10);const{ID}=item;const{full_name,gravatar,date_created,email}=item.data;const{primary_phone="",primary_phone_extension="",mobile_phone="",company_phone="",company_phone_extension=""}=item.meta;return Div({className:`contact-list-item`,id:`contact-list-item-${ID}`,dataId:ID,...props},[Div({className:"display-flex gap-10"},[Img({className:"avatar",src:gravatar,alt:"avatar"}),Div({className:"display-flex column"},[Div({},[makeEl("h4",{style:{margin:0}},full_name),Span({className:"subscribed"},` — ${sprintf(__("Subscribed %s"),`<abbr title="${formatDateTime(date_created)}">${sprintf(__("%s ago "),item.i18n.created)}</abbr>`)}`)]),Div({},[An({href:`mailto:${email}`},email),Span({},[" — ",Span({className:`gh-text ${item.is_marketable?"green":"red"}`},Groundhogg.filters.optin_status[item.data.optin_status])])])])]),Div({className:"show-on-hover"},[primary_phone||company_phone||mobile_phone?Div({className:"contact-phones"},[ContactPhone(icons.mobile,mobile_phone),ContactPhone(icons.phone,primary_phone,primary_phone_extension),ContactPhone(icons.phone,company_phone,company_phone_extension)]):null,Div({className:"gh-tags"},[...showTags.map(tag=>Span({className:"gh-tag"},tag.data.tag_name)),allTags.length?Span({},sprintf("and %d more...",allTags.length)):null]),maybeCall(extra,item)])])};const ContactList=(contacts=[],{noContacts=()=>null,itemProps={}}={})=>{if(!contacts.length){return maybeCall(noContacts)}return Div({className:"contact-list"},contacts.map(contact=>ContactListItem(contact,maybeCall(itemProps,contact))))};const QuickSearch=({itemProps={},queryOverrides={}}={})=>{const State=Groundhogg.createState({search:"",searched:false,results:[],loaded:false});const fetchResults=async()=>{let results=await ContactsStore.fetchItems({search:State.search,orderby:"date_created",order:"DESC",limit:5,...queryOverrides});State.set({results:results,searched:true,loaded:true})};return Div({id:"quick-search-wrap"},morph=>{if(!State.loaded){fetchResults().then(morph)}const updateResults=debounce(async()=>{await fetchResults();morph()},300);return Fragment([Form({action:adminPageURL("gh_contacts")},[Input({type:"hidden",name:"page",value:"gh_contacts"}),Input({id:"quick-search-input",placeholder:__("Search by name or email...","groundhogg"),type:"search",name:"s",value:State.search,onInput:e=>{State.set({search:e.target.value});updateResults()}})]),State.loaded?null:Skeleton({},["full","full","full"]),State.results.length?ContactList(State.results,{itemProps:item=>({className:"contact-list-item clickable",onClick:e=>{window.open(item.admin,"_self")},...maybeCall(itemProps,item)})}):null,State.results.length===0&&State.searched?Pg({style:{textAlign:"center"}},__("No contacts found for the current search","groundhogg")):null])})};const Panel=({id,name,collapsed=false,hidden=false,onCollapse=id=>{}},content)=>{if(hidden){return null}return Div({id:`${id}-panel`,className:`gh-panel ${collapsed?"closed":""}`},[Div({className:`gh-panel-header`},[H2({},name),Button({className:"toggle-indicator",onClick:e=>{onCollapse(id)}})]),collapsed?null:maybeCall(content)])};const Panels=overrides=>({...Groundhogg.createRegistry({}),storagePrefix:"gh-panels",collapse(id){if(!this.isCollapsed(id)){this.toggleCollapse(id)}},expand(id){if(this.isCollapsed(id)){this.toggleCollapse(id)}},hide(id){if(!this.isHidden(id)){this.toggleHidden(id)}},show(id){if(this.isHidden(id)){this.toggleHidden(id)}},togglePanel(id,suffix){let panels=this.getPanelIds(suffix);if(panels.includes(id)){panels.splice(panels.indexOf(id),1)}else{panels.push(id)}localStorage.setItem(`${this.storagePrefix}-${suffix}`,JSON.stringify(panels))},toggleHidden(id){this.togglePanel(id,"hidden")},toggleCollapse(id){this.togglePanel(id,"collapsed")},getPanelIds(suffix){return JSON.parse(localStorage.getItem(`${this.storagePrefix}-${suffix}`))||[]},getHiddenPanelIds(){return this.getPanelIds("hidden")},getCollapsedPanelIds(){return this.getPanelIds("collapsed")},isHidden(id){return this.getHiddenPanelIds().includes(id)},isCollapsed(id){return this.getCollapsedPanelIds().includes(id)},PanelControls(){return Div({},[...this.map((item,id)=>Div({className:"display-flex gap-10",style:{marginBottom:"10px"}},[Toggle({checked:!this.isHidden(id),id:`toggle-${id}`,onChange:e=>{this.toggleHidden(id)}}),Label({for:`toggle-${id}`},item.name)]))])},Panel(id){let{content,...panel}=this.get(id);return Panel({id:id,...panel,collapsed:this.isCollapsed(id),hidden:this.isHidden(id),onCollapse:id=>{this.toggleCollapse(id);morphdom(document.getElementById(`${id}-panel`),this.Panel(id))}},content)},Panels(){return Div({className:"display-flex column gap-20",id:this.storagePrefix},this.keys().map(id=>this.Panel(id)))},...overrides});const Relationships=({title="",id,store,child_type="",parent_type="",renderItem=item=>{},onAddItem=(r,j)=>{}})=>{const rel_type_key=child_type?"child_type":"parent_type";const rel_type=child_type||parent_type;const rel_id_key=child_type?"child_id":"parent_id";const State=Groundhogg.createState({loaded:false,items:[]});const fetchRelationships=()=>store.fetchRelationships(id,{[rel_type_key]:rel_type}).then(items=>State.set({items:items,loaded:true}));const deleteRelationship=itemId=>store.deleteRelationships(id,{[rel_type_key]:rel_type,[rel_id_key]:itemId}).then(()=>State.set({items:State.items.filter(item=>item.ID!==itemId)}));const createRelationship=item=>store.createRelationships(id,{[rel_type_key]:rel_type,[rel_id_key]:item.ID}).then(()=>State.set({items:[...State.items,item]}));return Div({id:`${rel_type_key}-${rel_type}-rel-of-${id}`,className:`display-flex column relationship-editor ${rel_type_key}-${rel_type}`},morph=>{const handleDeleteRelationship=itemId=>deleteRelationship(itemId).then(morph);if(!State.loaded){fetchRelationships().then(morph);return Skeleton({},["full","full","full"])}const AddRelButton=()=>Button({id:`add-${rel_type_key}-${rel_type}-rel-for-${id}`,className:"gh-button secondary text icon",onClick:e=>{let promise=new Promise((resolve,reject)=>onAddItem(resolve,reject,State));promise.then(item=>createRelationship(item).then(morph))}},[Dashicon("plus-alt2"),ToolTip(__("Add relationship","groundhogg"),"left")]);return Fragment([title?Div({className:"space-between"},[H4({},title),AddRelButton()]):null,...State.items.map(item=>renderItem({...item,onDelete:handleDeleteRelationship})),title?null:Div({className:"display-flex flex-end"},AddRelButton())])})};const OwnerPicker=({id="select-owners",selected=[],onChange=ids=>{},allow0=true,itemDisplay=user=>user.data.display_name,multiple=true,...overrides})=>ItemPicker({id:`select-users`,noneSelected:__("Select a user...","groundhogg"),selected:selected.map(user_id=>{if(user_id==0&&allow0){return{id:0,text:__("The contact owner","groundhogg")}}return{id:user_id,text:itemDisplay(getOwner(user_id))}}),multiple:multiple,style:{flexGrow:1},isValidSelection:id=>id===0||getOwner(id),fetchOptions:search=>{search=new RegExp(search,"i");let options=Groundhogg.filters.owners.map(u=>({id:u.ID,text:itemDisplay(u)}));if(allow0){options.push({id:0,text:__("The contact owner","groundhogg")})}options=options.filter(({text})=>text.match(search));return Promise.resolve(options)},onChange:items=>{if(multiple){onChange(items.map(({id})=>id));return}onChange(items)},...overrides});function getClosestRelativeAncestor(element){let parent=element.parentElement;while(parent){if(window.getComputedStyle(parent).position==="relative"){return parent}parent=parent.parentElement}return null}const Tour=(steps,{onFinish=()=>{},beforeDismiss=({dismiss})=>dismiss(),onDismiss=()=>{},fixed=false})=>{const State=Groundhogg.createState({current:0,step:null,target:null,relative:null});const currentStep=()=>steps[State.current];const removeSteps=()=>{document.querySelectorAll(".tour-prompt-container").forEach(el=>el.remove());document.querySelectorAll(".tour-prompt").forEach(el=>el.remove());document.querySelectorAll(".tour-highlighted").forEach(el=>el.classList.remove("tour-highlighted"))};const remove=()=>{removeSteps();document.removeEventListener("resize",rePositionStep);document.removeEventListener("scroll",rePositionStep)};const dismiss=async()=>{remove();onDismiss()};const next=()=>{const{onNext=()=>{}}=currentStep();onNext();if(State.current+1>=steps.length){remove();onFinish(true);return}State.current++;showStep()};const prev=()=>{const{onPrev=()=>{}}=currentStep();onPrev();if(State.current<=0){return}State.current--;showStep()};const showStep=()=>{removeSteps();positionStep()};function rePositionStep(){let{position}=currentStep();let{target,relative,step,windowEl}=State;const targetPos=target.getBoundingClientRect();const relativePos=relative.getBoundingClientRect();const stepPos=step.getBoundingClientRect();const gap=20;if(fixed){windowEl.style.height=`${targetPos.height+gap*2}px`;windowEl.style.width=`${targetPos.width+gap*2}px`;windowEl.style.borderWidth=`${Math.round(targetPos.y)-gap}px ${Math.round(window.innerWidth-targetPos.x)-gap}px ${Math.round(window.innerHeight-targetPos.y)-gap}px ${Math.round(targetPos.x)-gap}px`;switch(position){case"right":step.style.left=`${targetPos.x+targetPos.width+gap}px`;step.style.top=`${targetPos.y}px`;break;case"left":step.style.left=`${targetPos.x-stepPos.width-gap}px`;step.style.top=`${targetPos.y}px`;break;case"above":step.style.left=`${targetPos.x}px`;step.style.top=`${targetPos.y-stepPos.height-gap}px`;break;case"below":step.style.left=`${targetPos.x}px`;step.style.top=`${targetPos.y+targetPos.height+gap}px`;break;case"below-left":step.style.left=`${targetPos.x+targetPos.width-stepPos.width}px`;step.style.top=`${targetPos.y+targetPos.height+gap}px`;break}return}switch(position){case"right":step.style.left=`${targetPos.right-relativePos.left+gap}px`;step.style.top=`${targetPos.top-relativePos.top}px`;break;case"left":step.style.left=`${targetPos.left-relativePos.left-stepPos.width-gap}px`;step.style.top=`${targetPos.top-relativePos.top}px`;break;case"above":step.style.left=`${targetPos.left-relativePos.left}px`;step.style.top=`${targetPos.top-relativePos.top-stepPos.height-gap}px`;break;case"below":step.style.left=`${targetPos.left-relativePos.left}px`;step.style.top=`${targetPos.bottom-relativePos.top+gap}px`;break;case"below-left":step.style.left=`${targetPos.right-relativePos.left-stepPos.width}px`;step.style.top=`${targetPos.bottom-relativePos.top+gap}px`;break}}function positionStep(){let tourEl=TourStep();let windowEl=tourEl.querySelector(".tour-window");let stepEl=tourEl.querySelector(".tour-prompt");let{target,relative,onInit=()=>{},onBefore=()=>{}}=currentStep();target=document.querySelector(target);if(!target){next();return}if(fixed){relative=document.body}else if(relative){relative=target.closest(relative)}else{relative=getClosestRelativeAncestor(target)}stepEl.style.position=fixed?"fixed":"absolute";if(fixed){relative.append(tourEl)}else{target.classList.add("tour-highlighted");relative.append(stepEl)}target.scrollIntoView({behavior:"instant",block:"center",inline:"center"});onBefore({next:next,prev:prev,target:target,relative:relative,step:stepEl,windowEl:windowEl,currentStep:currentStep});State.set({step:stepEl,windowEl:windowEl,target:target,relative:relative});rePositionStep();onInit({next:next,prev:prev,target:target,relative:relative,step:stepEl,windowEl:windowEl,currentStep:currentStep});stepEl.querySelector("#tour-next").focus()}const TourStep=()=>MakeEl.Div({className:"tour-prompt-container"},[fixed?MakeEl.Div({className:"tour-window"},[MakeEl.Div({className:"tour-window-shadow",onClick:next})]):null,MakeEl.Div({className:`tour-prompt ${currentStep().position}`,style:{padding:"10px",width:"200px"}},[MakeEl.Button({className:"dismiss",onClick:e=>{beforeDismiss({dismiss:dismiss,State:State})}},MakeEl.Dashicon("no-alt")),MakeEl.Div({},currentStep().prompt),MakeEl.Div({className:"display-flex flex-end gap-5 space-above-10"},[State.current>0?MakeEl.Button({id:"tour-prev",className:"gh-button small secondary text prev-step",onClick:()=>prev()},"Prev"):null,currentStep().showNext===false?null:MakeEl.Button({id:"tour-next",className:`gh-button small ${State.current<steps.length-1?"secondary":"primary"} next-step`,onClick:()=>next()},State.current<steps.length-1?"Next":"Finish")])])]);document.addEventListener("resize",rePositionStep);document.addEventListener("scroll",rePositionStep);positionStep()};Groundhogg.components={QuickSearch:QuickSearch,addContactModal:addContactModal,internalForm:internalForm,betterTagPicker:betterTagPicker,quickAddForm:quickAddForm,selectContactModal:selectContactModal,quickEditContactModal:quickEditContactModal,makeInput:makeInput,emailModal:emailModal,EmailTemplateModal:EmailTemplateModal,fileUploader:fileUploader,EmailPreview:EmailPreview,EmailPreviewModal:EmailPreviewModal,ImageInput:ImageInput,ImagePicker:ImagePicker,FeedbackModal:FeedbackModal,ContactList:ContactList,ContactListItem:ContactListItem,Panel:Panel,Panels:Panels,Relationships:Relationships,OwnerPicker:OwnerPicker,Tour:Tour}})(jQuery); -
groundhogg/tags/4.2.12/assets/js/admin/emails/email-block-editor.js
r3395861 r3458296 9643 9643 discord : 'Discord', 9644 9644 rumble : 'Rumble', 9645 bluesky : 'Bluesky', 9645 9646 } 9646 9647 -
groundhogg/tags/4.2.12/assets/js/admin/emails/email-block-editor.min.js
r3395861 r3458296 195 195 `]),`<!-- /wp:query -->`]).innerHTML},defaults:{layout:"cards",featured:true,excerpt:false,thumbnail:true,thumbnail_size:"thumbnail",columns:2,gap:20,cardStyle:{},headingStyle:fontDefaults({fontSize:24}),excerptStyle:fontDefaults({fontSize:16}),queryId:"",post_type:"post",number:5,offset:0}});const ChildBlocks=({children})=>Div({className:`children sortable-blocks ${children.length?"":"empty"}`,onCreate:el=>{makeSortable(el)}},[...children.map(b=>EditBlockWrapper(b))]);const QueryLoopContent=({DynamicContent,...block})=>{let dynamicContent=Div({},DynamicContent());let childBlockPlace=dynamicContent.querySelector(".replace-with-child-blocks");if(!childBlockPlace){return dynamicContent.firstElementChild}childBlockPlace.replaceWith(ChildBlocks(block));return Fragment([dynamicContent.firstElementChild])};const PostTagReference=[{tag:"the_title",desc:"The post title"},{tag:"the_excerpt",desc:"The post excerpt"},{tag:"the_url",desc:"The link to the post"},{tag:"the_thumbnail",desc:"The thumbnail image"},{tag:"the_thumbnail_url",desc:"The thumbnail image URL"},{tag:"the_content",desc:"The post content"},{tag:"the_id",desc:"The post ID"},{tag:"the_date",desc:"The post publish date"},{tag:"the_author",desc:"The post author"},{tag:"read_more",desc:'A "Read more" link to the post'}];registerDynamicBlock("queryloop","Query Loop",{svg:`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> 196 196 <path fill="currentColor" d="M18 7a7.669 7.669 0 0 0-6 3.19A7.669 7.669 0 0 0 6 7c-3.687 0-5 2.583-5 5 0 3.687 2.583 5 5 5a7.669 7.669 0 0 0 6-3.19A7.669 7.669 0 0 0 18 17c2.417 0 5-1.313 5-5 0-2.417-1.313-5-5-5ZM6 15a2.689 2.689 0 0 1-3-3 2.689 2.689 0 0 1 3-3c2.579 0 4.225 2.065 4.837 3-.612.935-2.258 3-4.837 3Zm12 0c-2.579 0-4.225-2.065-4.837-3 .612-.935 2.258-3 4.837-3a2.689 2.689 0 0 1 3 3 2.689 2.689 0 0 1-3 3Z"/> 197 </svg>`,attributes:{children:el=>{let blockTable=el.querySelector("table:not(.email-columns)");return parseBlocksFromTable(blockTable)}},controls:({gap=10,columns=2,thumbnail_size="post-thumbnail",updateBlock,...query})=>{return Fragment([ControlGroup({id:"queryloop-layout",name:"Layout"},[Control({label:"Columns"},NumberControl({id:"columns",className:"control-input",value:columns,step:1,onInput:e=>updateBlock({columns:e.target.value})})),Control({label:"Gap"},NumberControl({id:"column-gap",className:"control-input",value:gap,step:5,unit:"px",onInput:e=>updateBlock({gap:e.target.value})})),Control({label:"Thumbnail Size"},Select({id:"thumbnail-size",style:{width:"115px"},selected:thumbnail_size,options:imageSizes.map(size=>({value:size,text:size})),onChange:e=>updateBlock({thumbnail_size:e.target.value})}))]),QueryControls({updateBlock:updateBlock,...query}),ControlGroup({id:"queryloop-reference",name:"Reference"},[Pg({},"Use the below merge tags to merge post data within the query loop."),...PostTagReference.map(tag=>Div({className:"display-flex space-between"},[makeEl("code",{className:"copy-text"},`#${tag.tag}#`),Span({className:"tag-desc"},tag.desc)]))])])},edit:QueryLoopContent,html:({DynamicContent,...block})=>{if(isGeneratingHTML()){return renderBlocksHTML(block.children)}return QueryLoopContent({DynamicContent:DynamicContent,...block})},plainText:({children=[]})=>renderBlocksPlainText(children),defaults:{children:[createBlock("text",{content:`<p>#the_thumbnail#</p>\n<h2>#the_title#</h2>\n<p>#the_excerpt#</p>\n<p>#read_more#</p>`})],columns:2,layout:"grid",queryId:"",post_type:"post",number:6,offset:0,thumbnail_size:"post-thumbnail"}});const socialIcons={facebook:"Facebook",instagram:"Instagram",linkedin:"LinkedIn",pinterest:"Pinterest",reddit:"Reddit",threads:"Threads",tiktok:"TikTok",tumblr:"Tumblr",twitch:"Twitch",twitter:"𝕏",vimeo:"Vimeo",whatsapp:"WhatsApp",wordpress:"WordPress",youtube:"YouTube",github:"GitHub",truthsocial:"Truth Social",odysee:"Odysee",discord:"Discord",rumble:"Rumble" };const socialIconThemes={"brand-boxed":"Brand Colors Square","brand-circle":"Brand Colors Circular","brand-icons":"Brand Colors Icons","black-boxed":"Black Boxed","black-circle":"Black Circular","black-icons":"Black Icons","dark-grey-boxed":"Gray Boxed","dark-grey-circle":"Gray Circle","dark-grey-icons":"Gray Icons","grey-boxed":"Gray Boxed","grey-circle":"Gray Circle","grey-icons":"Gray Icons","white-boxed":"White Boxed","white-circle":"White Circular","white-icons":"White Icons"};const SocialIcon=(icon,theme="brand-circle",size=20)=>makeEl("img",{src:`${Groundhogg.assets.images}/social-icons/${theme}/${icon||"facebook"}.png`,alt:socialIcons[icon],height:size,width:size,style:{verticalAlign:"bottom"}});const SocialIconTheme=(theme,selected,updateBlock)=>{let themeIcons=["facebook","instagram","twitter"];let{use="global",socials=[]}=getActiveBlock();if(use==="global"&&globalSocials.length>=3){themeIcons=globalSocials.map(([social])=>social).slice(0,3)}else if(use==="custom"&&socials.length>=3){themeIcons=socials.map(([social])=>social).slice(0,3)}return Button({id:`select-${theme}`,title:socialIconThemes[theme],className:`gh-button ${theme===selected?"primary":"secondary text"} social-icon-theme ${theme}`,onClick:e=>updateBlock({theme:theme,morphControls:true})},themeIcons.map(icon=>SocialIcon(icon,theme,20)))};const SocialLinksRepeater=({socials,theme,onChange})=>InputRepeater({id:"social-links",rows:socials,cells:[({setValue,value,id,...props})=>Button({className:"gh-button grey icon",id:id,onClick:e=>{theme=hasActiveBlock()?getActiveBlock().theme:"brand-boxed";MiniModal({selector:`#${id}`},({close})=>Div({className:`display-grid social-icon-picker ${theme}`},[...Object.keys(socialIcons).map(social=>Button({title:socialIcons[social],id:`${id}-${social}`,className:"gh-button secondary text dashicon span-3",onClick:e=>{setValue(social);close()}},SocialIcon(social,theme)))]))}},SocialIcon(value||"facebook",theme)),({setValue,...props},[icon])=>Input({type:"url",placeholder:`https://${icon}.com/your-profile/`,...props})],sortable:true,onChange:onChange});registerBlock("social","Socials",{attributes:{size:el=>parseInt(el.querySelector("img")?.width),gap:el=>parseInt(el.querySelector("td.gap")?.width),theme:el=>el.querySelector("img")?.src.split("/").at(-2),socials:el=>Array.from(el.querySelectorAll("a")).map(el=>{let png=el.firstElementChild.src.split("/").at(-1);return[png.substr(0,png.indexOf(".png")),el.href]})},svg:`197 </svg>`,attributes:{children:el=>{let blockTable=el.querySelector("table:not(.email-columns)");return parseBlocksFromTable(blockTable)}},controls:({gap=10,columns=2,thumbnail_size="post-thumbnail",updateBlock,...query})=>{return Fragment([ControlGroup({id:"queryloop-layout",name:"Layout"},[Control({label:"Columns"},NumberControl({id:"columns",className:"control-input",value:columns,step:1,onInput:e=>updateBlock({columns:e.target.value})})),Control({label:"Gap"},NumberControl({id:"column-gap",className:"control-input",value:gap,step:5,unit:"px",onInput:e=>updateBlock({gap:e.target.value})})),Control({label:"Thumbnail Size"},Select({id:"thumbnail-size",style:{width:"115px"},selected:thumbnail_size,options:imageSizes.map(size=>({value:size,text:size})),onChange:e=>updateBlock({thumbnail_size:e.target.value})}))]),QueryControls({updateBlock:updateBlock,...query}),ControlGroup({id:"queryloop-reference",name:"Reference"},[Pg({},"Use the below merge tags to merge post data within the query loop."),...PostTagReference.map(tag=>Div({className:"display-flex space-between"},[makeEl("code",{className:"copy-text"},`#${tag.tag}#`),Span({className:"tag-desc"},tag.desc)]))])])},edit:QueryLoopContent,html:({DynamicContent,...block})=>{if(isGeneratingHTML()){return renderBlocksHTML(block.children)}return QueryLoopContent({DynamicContent:DynamicContent,...block})},plainText:({children=[]})=>renderBlocksPlainText(children),defaults:{children:[createBlock("text",{content:`<p>#the_thumbnail#</p>\n<h2>#the_title#</h2>\n<p>#the_excerpt#</p>\n<p>#read_more#</p>`})],columns:2,layout:"grid",queryId:"",post_type:"post",number:6,offset:0,thumbnail_size:"post-thumbnail"}});const socialIcons={facebook:"Facebook",instagram:"Instagram",linkedin:"LinkedIn",pinterest:"Pinterest",reddit:"Reddit",threads:"Threads",tiktok:"TikTok",tumblr:"Tumblr",twitch:"Twitch",twitter:"𝕏",vimeo:"Vimeo",whatsapp:"WhatsApp",wordpress:"WordPress",youtube:"YouTube",github:"GitHub",truthsocial:"Truth Social",odysee:"Odysee",discord:"Discord",rumble:"Rumble",bluesky:"Bluesky"};const socialIconThemes={"brand-boxed":"Brand Colors Square","brand-circle":"Brand Colors Circular","brand-icons":"Brand Colors Icons","black-boxed":"Black Boxed","black-circle":"Black Circular","black-icons":"Black Icons","dark-grey-boxed":"Gray Boxed","dark-grey-circle":"Gray Circle","dark-grey-icons":"Gray Icons","grey-boxed":"Gray Boxed","grey-circle":"Gray Circle","grey-icons":"Gray Icons","white-boxed":"White Boxed","white-circle":"White Circular","white-icons":"White Icons"};const SocialIcon=(icon,theme="brand-circle",size=20)=>makeEl("img",{src:`${Groundhogg.assets.images}/social-icons/${theme}/${icon||"facebook"}.png`,alt:socialIcons[icon],height:size,width:size,style:{verticalAlign:"bottom"}});const SocialIconTheme=(theme,selected,updateBlock)=>{let themeIcons=["facebook","instagram","twitter"];let{use="global",socials=[]}=getActiveBlock();if(use==="global"&&globalSocials.length>=3){themeIcons=globalSocials.map(([social])=>social).slice(0,3)}else if(use==="custom"&&socials.length>=3){themeIcons=socials.map(([social])=>social).slice(0,3)}return Button({id:`select-${theme}`,title:socialIconThemes[theme],className:`gh-button ${theme===selected?"primary":"secondary text"} social-icon-theme ${theme}`,onClick:e=>updateBlock({theme:theme,morphControls:true})},themeIcons.map(icon=>SocialIcon(icon,theme,20)))};const SocialLinksRepeater=({socials,theme,onChange})=>InputRepeater({id:"social-links",rows:socials,cells:[({setValue,value,id,...props})=>Button({className:"gh-button grey icon",id:id,onClick:e=>{theme=hasActiveBlock()?getActiveBlock().theme:"brand-boxed";MiniModal({selector:`#${id}`},({close})=>Div({className:`display-grid social-icon-picker ${theme}`},[...Object.keys(socialIcons).map(social=>Button({title:socialIcons[social],id:`${id}-${social}`,className:"gh-button secondary text dashicon span-3",onClick:e=>{setValue(social);close()}},SocialIcon(social,theme)))]))}},SocialIcon(value||"facebook",theme)),({setValue,...props},[icon])=>Input({type:"url",placeholder:`https://${icon}.com/your-profile/`,...props})],sortable:true,onChange:onChange});registerBlock("social","Socials",{attributes:{size:el=>parseInt(el.querySelector("img")?.width),gap:el=>parseInt(el.querySelector("td.gap")?.width),theme:el=>el.querySelector("img")?.src.split("/").at(-2),socials:el=>Array.from(el.querySelectorAll("a")).map(el=>{let png=el.firstElementChild.src.split("/").at(-1);return[png.substr(0,png.indexOf(".png")),el.href]})},svg:` 198 198 <svg viewBox="-33 0 512 512.001" xmlns="http://www.w3.org/2000/svg"> 199 199 <path fill="currentColor" -
groundhogg/tags/4.2.12/groundhogg.php
r3442828 r3458296 4 4 * Plugin URI: https://www.groundhogg.io/?utm_source=wp-plugins&utm_campaign=plugin-uri&utm_medium=wp-dash 5 5 * Description: CRM and marketing automation for WordPress 6 * Version: 4.2.1 16 * Version: 4.2.12 7 7 * Author: Groundhogg Inc. 8 8 * Author URI: https://www.groundhogg.io/?utm_source=wp-plugins&utm_campaign=author-uri&utm_medium=wp-dash … … 25 25 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly 26 26 27 define( 'GROUNDHOGG_VERSION', '4.2.1 1' );28 define( 'GROUNDHOGG_PREVIOUS_STABLE_VERSION', '4.2.1 0' );27 define( 'GROUNDHOGG_VERSION', '4.2.12' ); 28 define( 'GROUNDHOGG_PREVIOUS_STABLE_VERSION', '4.2.11' ); 29 29 30 30 define( 'GROUNDHOGG__FILE__', __FILE__ ); -
groundhogg/tags/4.2.12/includes/classes/funnel.php
r3343709 r3458296 642 642 } 643 643 644 $last_changed = db()->steps->cache_get_last_changed();645 $cache_key = "$this->ID:steps:$last_changed:" . md5serialize( $query );646 $steps = wp_cache_get( $cache_key, db()->steps->get_cache_group(), false, $found );647 648 // make sure all items in array are also steps649 if ( $found && is_array( $steps ) && array_all( $steps, function ( $step ) {650 return is_a( $step, Step::class );651 } ) ) {652 653 return $steps;654 }644 // $last_changed = db()->steps->cache_get_last_changed(); 645 // $cache_key = "$this->ID:steps:$last_changed:" . md5serialize( $query ); 646 // $steps = wp_cache_get( $cache_key, db()->steps->get_cache_group(), false, $found ); 647 // 648 // // make sure all items in array are also steps 649 // if ( $found && is_array( $steps ) && array_all( $steps, function ( $step ) { 650 // return is_a( $step, Step::class ); 651 // } ) ) { 652 // 653 // return $steps; 654 // } 655 655 656 656 $steps = $this->get_steps_db()->query( $query ); … … 674 674 } 675 675 676 wp_cache_set( $cache_key, $steps, db()->steps->get_cache_group(), MINUTE_IN_SECONDS );676 // wp_cache_set( $cache_key, $steps, db()->steps->get_cache_group(), MINUTE_IN_SECONDS ); 677 677 678 678 return $steps; -
groundhogg/tags/4.2.12/includes/form/form-v2.php
r3400645 r3458296 1903 1903 protected $hidden_fields = []; 1904 1904 1905 public function is_turnstile_enabled() { 1906 $turnstile = get_array_var( $this->get_cleaned_json_config(), 'turnstile', [] ); 1907 return isset_not_empty( $turnstile, 'enabled' ) && is_recaptcha_enabled(); 1908 } 1909 1910 public function is_recaptcha_enabled() { 1911 $recaptcha = get_array_var( $this->get_cleaned_json_config(), 'recaptcha', [] ); 1912 return isset_not_empty( $recaptcha, 'enabled' ) && is_recaptcha_enabled(); 1913 } 1914 1915 /** 1916 * Get a cleaned assoc array of the form config 1917 * 1918 * @return array 1919 */ 1920 private function get_cleaned_json_config() { 1921 $config = $this->get_meta( 'form' ); 1922 // encode and decode to fix potential mutation errors when importing/exporting 1923 return json_decode( wp_json_encode( $config ), true ); 1924 } 1925 1905 1926 /** 1906 1927 * Get the HTML For the fields … … 1909 1930 */ 1910 1931 function get_field_html() { 1911 1912 $config = $this->get_meta( 'form' ); 1913 1914 $config = json_decode( wp_json_encode( $config ), true ); 1915 1932 $config = $this->get_cleaned_json_config(); 1916 1933 $fields = get_array_var( $config, 'fields', [] ); 1917 1934 … … 2203 2220 */ 2204 2221 public function get_fields() { 2205 $config = json_decode( wp_json_encode( $this->get_meta( 'form' ) ), true);2222 $config = $this->get_cleaned_json_config(); 2206 2223 2207 2224 if ( ! is_array( $config ) || ! isset( $config['fields'] ) ) { … … 2229 2246 2230 2247 // Ensure array and not stdClass 2231 $config = json_decode( wp_json_encode( $this->get_meta( 'form' ) ), true);2248 $config = $this->get_cleaned_json_config(); 2232 2249 $fields = $config['fields']; 2233 $recaptcha = $config['recaptcha']; 2234 $turnstile = $config['turnstile'];2235 2236 if ( $recaptcha['enabled'] ) {2237 $fields[] = $recaptcha;2238 } 2239 2240 if ( $turnstile['enabled'] ){2241 $fields[] = $ turnstile;2250 2251 // add the recaptcha to fields array for validation 2252 if ( isset( $config[ 'recaptcha' ] ) && $config[ 'recaptcha' ]['enabled'] ){ 2253 $fields[] = $config['recaptcha']; 2254 } 2255 2256 // add the turnstile to fields array for validation 2257 if ( isset( $config[ 'turnstile' ] ) && $config[ 'turnstile' ]['enabled'] ){ 2258 $fields[] = $config['turnstile']; 2242 2259 } 2243 2260 -
groundhogg/tags/4.2.12/includes/functions.php
r3442828 r3458296 2590 2590 * @return false|Contact 2591 2591 */ 2592 function update_contact_with_map( $contact, array $fields, array $map = [],$submission = [] ) {2592 function update_contact_with_map( mixed $contact, array $fields, array $map = [], array $submission = [] ) { 2593 2593 return generate_contact_with_map( $fields, $map, $submission, $contact ); 2594 2594 } … … 2599 2599 * @throws \Exception 2600 2600 * 2601 * @param $fields arraythe raw data from the source2602 * @param $map arraymap of field_ids to contact keys2601 * @param array $fields the raw data from the source 2602 * @param array $map map of field_ids to contact keys 2603 2603 * @param array $submission settings for the submission 2604 2604 * @param null|Contact $contact an existing contact record to modify … … 2606 2606 * @return Contact|false 2607 2607 */ 2608 function generate_contact_with_map( $fields, $map = [], $submission = [],$contact = null ) {2608 function generate_contact_with_map( array $fields, array $map = [], array $submission = [], mixed $contact = null ) { 2609 2609 2610 2610 if ( empty( $map ) ) { … … 3862 3862 * 3863 3863 * @param string $call_name the name you wish to call 3864 * @param string $id_or_email id or email of the contact3864 * @param string|int|Contact $id_or_email id or email of the contact 3865 3865 * @param bool $by_user_id whether the ID is the ID of a WP user 3866 3866 */ … … 8273 8273 */ 8274 8274 function verify_admin_ajax_nonce() { 8275 return wp_verify_nonce( get_request_var( 'gh_admin_ajax_nonce' ), 'admin_ajax');8275 return check_ajax_referer( 'admin_ajax', 'gh_admin_ajax_nonce', false ); 8276 8276 } 8277 8277 -
groundhogg/trunk/README.txt
r3442828 r3458296 7 7 Tested up to: 6.9 8 8 Requires PHP: 7.1 9 Stable tag: 4.2.1 09 Stable tag: 4.2.12 10 10 License: GPLv3 11 11 License URI: https://www.gnu.org/licenses/gpl.md … … 378 378 379 379 == Changelog == 380 381 = 4.2.12 (2026-02-10) = 382 * ADDED Bluesky social icons for the email editor. 383 * ADDED Bulk delete bulk action for campaigns. 384 * FIXED Email preview link stopped working in the emails table. 385 * FIXED Notice generated by improper array access check when submitting a form. 380 386 381 387 = 4.2.11 (2026-01-19) = -
groundhogg/trunk/admin/broadcasts/broadcasts-table.php
r3400645 r3458296 245 245 246 246 if ( $broadcast->is_email() ){ 247 $actions[] = html()->a( '# ', esc_html__( 'Preview', 'groundhogg' ), [ 'class' => 'gh-email-preview', 'data-id' => $broadcast->get_object_id() ]);247 $actions[] = html()->a( '#gh-email-preview/' . $broadcast->get_object_id(), esc_html__( 'Preview', 'groundhogg' ) ); 248 248 } 249 249 -
groundhogg/trunk/admin/campaigns/campaigns-table.php
r3343709 r3458296 47 47 // Set parent defaults. 48 48 parent::__construct( array( 49 'singular' => ' tag', // Singular name of the listed records.50 'plural' => ' tags', // Plural name of the listed records.49 'singular' => 'campaign', // Singular name of the listed records. 50 'plural' => 'campaigns', // Plural name of the listed records. 51 51 'ajax' => false, // Does this table support ajax? 52 52 ) ); 53 } 54 55 /** 56 * @return array An associative array containing all the bulk actions. 57 */ 58 protected function get_bulk_actions() { 59 60 $actions = array( 61 'delete' => _x( 'Delete', 'List table bulk action', 'groundhogg' ), 62 ); 63 64 return apply_filters( 'groundhogg/admin/campaigns/table/bulk_actions', $actions ); 53 65 } 54 66 -
groundhogg/trunk/admin/emails/emails-table.php
r3400645 r3458296 331 331 default: 332 332 $actions[] = [ 'class' => 'edit', 'display' => esc_html__( 'Edit' , 'groundhogg' ), 'url' => $item->admin_link() ]; 333 $actions[] = [ 'class' => 'gh-email-preview', 'display' => esc_html__( 'Preview' , 'groundhogg' ), 'url' => '# '];333 $actions[] = [ 'class' => 'gh-email-preview', 'display' => esc_html__( 'Preview' , 'groundhogg' ), 'url' => '#gh-email-preview/' . $item->get_id() ]; 334 334 $actions[] = [ 335 335 'class' => 'duplicate', -
groundhogg/trunk/admin/reports/views/broadcast-single.php
r3400645 r3458296 17 17 <h1 class="report-title"><?php echo esc_html( $broadcast->get_title() ) ?></h1> 18 18 <?php if ( $broadcast->is_email() ): ?> 19 <a href="# " class="gh-button secondary gh-email-preview" data-id="<?php echo esc_attr( $broadcast->get_object_id() ); ?>"><?php esc_html_e( 'Preview', 'groundhogg' ); ?></a>19 <a href="#gh-email-preview/<?php echo esc_attr( $broadcast->get_object_id() ); ?>" class="gh-button secondary"><?php esc_html_e( 'Preview', 'groundhogg' ); ?></a> 20 20 <?php endif; ?> 21 21 </div> -
groundhogg/trunk/assets/js/admin/components.js
r3422142 r3458296 1796 1796 EmailPreviewModal(parseInt(emailId), {}) 1797 1797 }) 1798 1799 window.addEventListener('hashchange', e => { 1800 let hash = window.location.hash.replace('#', '') 1801 if ( hash.startsWith('gh-email-preview/') ) { 1802 let emailId = hash.replace('gh-email-preview/', '') 1803 EmailPreviewModal(parseInt(emailId), {}) 1804 } 1805 }) 1798 1806 }) 1799 1807 -
groundhogg/trunk/assets/js/admin/components.min.js
r3422142 r3458296 216 216 <div id="uploading-files"></div> 217 217 <div id="uploaded-files"></div> 218 `,onOpen:({close})=>{let file=null;let filesToUpload=[];let filesUploaded=[];let uploading=false;const pushFiles=()=>{renderUploadingFiles();file=filesToUpload.pop();if(!file){uploading=false;return}uploading=true;let fd=new FormData;fd.append(fileName,file,file.name);fd.append("gh_admin_ajax_nonce",Groundhogg.nonces._adminajax);fd.append("action",action);beforeUpload(fd);setTimeout(()=>{fetch(ajaxurl,{method:"POST",credentials:"same-origin",body:fd}).then(r=>{if(!r.ok){dialog({message:__("Something when wrong..."),type:"error"});return}return r.json()}).then(r=>{if(!r.success){dialog({message:r.data[0].message,type:"error"});pushFiles();return}onUpload(r,file);filesUploaded.unshift(file);renderUploadedFiles();pushFiles()})},2e3)};const renderUploadingFiles=()=>{$("#uploading-files").html(filesToUpload.map(f=>`<div class="file"><span class="hourglass">⌛</span> ${f.name}</div>`))};const renderUploadedFiles=()=>{$("#uploaded-files").html(filesUploaded.map(f=>`<div class="file">✅ ${f.name}</div>`))};const addFiles=files=>{filesToUpload.push(...files);if(!uploading){pushFiles()}};const $input=$("#upload-file-input");$input.on("change",e=>{addFiles(e.target.files)});$("#select-files").on("click",e=>{e.preventDefault();$input.click()});const $droppable=$(".droppable-handler");$droppable.on("dragover",e=>{e.preventDefault();$droppable.addClass("dragover")}).on("dragleave",e=>{$droppable.removeClass("dragover")}).on("drop",e=>{e.preventDefault();$droppable.removeClass("dragover");let{dataTransfer}=e.originalEvent;addFiles(dataTransfer.files)})}})};const EmailPreviewModal=async(emailId,{height=window.innerHeight*.85,width=900})=>{const{close}=loadingModal();let email;try{email=await EmailsStore.maybeFetchItem(emailId)}catch(err){close();throw err}const{from_avatar,from_email,from_name,subject,built:content}=email.context;close();return ModalFrame({frameAttributes:{className:"gh-modal-frame gh-email-preview-modal"}},({close})=>Div({style:{width:`${width}px`,height:`${height}px`}},EmailPreview({close:close,from_avatar:from_avatar,from_email:from_email,from_name:from_name,subject:subject,content:content})))};const EmailPreview=({close=false,from_avatar,from_email,from_name,subject,content})=>{return Div({className:"email-preview"},[Div({className:"from-preview display-flex gap-20 has-box-shadow"},[makeEl("img",{src:from_avatar,className:"from-avatar",height:40,width:40,style:{borderRadius:"50%"}}),Div({className:"subject-and-from"},[`<h2>${subject}</h2>`,`<span class="from-name">${from_name}</span> <span class="from-email"><${from_email}></span>`]),close!==false?Button({className:"gh-button secondary icon text",style:{marginLeft:"auto"},onClick:close},Dashicon("no-alt")):null]),Iframe({id:"desktop-preview-iframe"},content)])};$(()=>{$(document).on("click","a.gh-email-preview",e=>{e.preventDefault();let emailId=e.currentTarget.dataset.id??e.currentTarget.closest("tr").id;EmailPreviewModal(parseInt(emailId),{})}) });const ImagePicker=({multiple=false,title=__("Select a image to upload"),selectText=__("Use this image"),onChange=attachment=>{}})=>{let file_frame=wp.media({title:title,button:{text:selectText},multiple:multiple});file_frame.on("select",function(){let attachment=file_frame.state().get("selection").first().toJSON();onChange(attachment)});file_frame.open()};const ImageInput=({id,name="src",onChange,value=""})=>{const handleChange=(value,attachment=null)=>{onChange(value,attachment);morphdom(document.getElementById(id),ImageInput({id:id,name:name,onChange:onChange,value:value}))};return Div({id:id,className:"image-picker"},[value?Div({id:`${id}-preview`,className:"image-input-preview",style:{backgroundImage:`url(${value})`},onClick:e=>{e.preventDefault();ImagePicker({multiple:false,onChange:attachment=>handleChange(attachment.url,attachment)})}}):null,InputGroup([Input({type:"text",id:`${id}-src`,value:value,className:"control full-width",name:name,onChange:e=>{handleChange(e.target.value,null)}}),Button({id:`${id}-select`,className:"gh-button secondary icon",onClick:e=>{e.preventDefault();ImagePicker({multiple:false,onChange:attachment=>handleChange(attachment.url,attachment)})}},icons.image)])])};const FeedbackModal=({subject="",message="",onSubmit=r=>{}})=>{const State=Groundhogg.createState({subject:subject,message:message,submitting:false});ModalWithHeader({width:"400px",header:"Send Feedback"},({close,morph})=>Form({className:"display-flex column gap-5",onSubmit:e=>{e.preventDefault();State.set({submitting:true});morph();Groundhogg.api.ajax({action:"gh_plugin_feedback",subject:State.subject,message:State.message}).then(r=>{onSubmit(r);dialog({message:"Thanks for your feedback!"});close()});return false}},[Label({for:"feedback-subject"},["What feature are you submitting feedback for?"]),Input({id:"feedback-subject",value:State.subject,required:true,onInput:e=>State.set({subject:e.target.value})}),Div(),Label({for:"feedback-message"},["What is your feedback? Be as descriptive as possible."]),Textarea({id:"feedback-message",value:State.message,required:true,rows:4,onInput:e=>State.set({message:e.target.value})}),Button({className:"gh-button primary",type:"submit",disabled:State.submitting},"Send feedback"),Pg({},"Your email address will be collected to validate your feedback, but will not be used beyond that.")]))};$(document).on("click","a.feedback-modal",e=>{e.preventDefault();const{subject="",message=""}=e.currentTarget.dataset;FeedbackModal({subject:subject,message:message})});const ContactPhone=(icon,number,extension="")=>number?Span({className:"contact-phone"},[icon,An({href:`tel:${number}`},number),extension?Span({className:"ext"},` x${extension}`):null]):null;const ContactListItem=(item,{extra=item=>null,...props}={})=>{let allTags=jsonCopy(item.tags);let showTags=allTags.splice(0,10);const{ID}=item;const{full_name,gravatar,date_created,email}=item.data;const{primary_phone="",primary_phone_extension="",mobile_phone="",company_phone="",company_phone_extension=""}=item.meta;return Div({className:`contact-list-item`,id:`contact-list-item-${ID}`,dataId:ID,...props},[Div({className:"display-flex gap-10"},[Img({className:"avatar",src:gravatar,alt:"avatar"}),Div({className:"display-flex column"},[Div({},[makeEl("h4",{style:{margin:0}},full_name),Span({className:"subscribed"},` — ${sprintf(__("Subscribed %s"),`<abbr title="${formatDateTime(date_created)}">${sprintf(__("%s ago "),item.i18n.created)}</abbr>`)}`)]),Div({},[An({href:`mailto:${email}`},email),Span({},[" — ",Span({className:`gh-text ${item.is_marketable?"green":"red"}`},Groundhogg.filters.optin_status[item.data.optin_status])])])])]),Div({className:"show-on-hover"},[primary_phone||company_phone||mobile_phone?Div({className:"contact-phones"},[ContactPhone(icons.mobile,mobile_phone),ContactPhone(icons.phone,primary_phone,primary_phone_extension),ContactPhone(icons.phone,company_phone,company_phone_extension)]):null,Div({className:"gh-tags"},[...showTags.map(tag=>Span({className:"gh-tag"},tag.data.tag_name)),allTags.length?Span({},sprintf("and %d more...",allTags.length)):null]),maybeCall(extra,item)])])};const ContactList=(contacts=[],{noContacts=()=>null,itemProps={}}={})=>{if(!contacts.length){return maybeCall(noContacts)}return Div({className:"contact-list"},contacts.map(contact=>ContactListItem(contact,maybeCall(itemProps,contact))))};const QuickSearch=({itemProps={},queryOverrides={}}={})=>{const State=Groundhogg.createState({search:"",searched:false,results:[],loaded:false});const fetchResults=async()=>{let results=await ContactsStore.fetchItems({search:State.search,orderby:"date_created",order:"DESC",limit:5,...queryOverrides});State.set({results:results,searched:true,loaded:true})};return Div({id:"quick-search-wrap"},morph=>{if(!State.loaded){fetchResults().then(morph)}const updateResults=debounce(async()=>{await fetchResults();morph()},300);return Fragment([Form({action:adminPageURL("gh_contacts")},[Input({type:"hidden",name:"page",value:"gh_contacts"}),Input({id:"quick-search-input",placeholder:__("Search by name or email...","groundhogg"),type:"search",name:"s",value:State.search,onInput:e=>{State.set({search:e.target.value});updateResults()}})]),State.loaded?null:Skeleton({},["full","full","full"]),State.results.length?ContactList(State.results,{itemProps:item=>({className:"contact-list-item clickable",onClick:e=>{window.open(item.admin,"_self")},...maybeCall(itemProps,item)})}):null,State.results.length===0&&State.searched?Pg({style:{textAlign:"center"}},__("No contacts found for the current search","groundhogg")):null])})};const Panel=({id,name,collapsed=false,hidden=false,onCollapse=id=>{}},content)=>{if(hidden){return null}return Div({id:`${id}-panel`,className:`gh-panel ${collapsed?"closed":""}`},[Div({className:`gh-panel-header`},[H2({},name),Button({className:"toggle-indicator",onClick:e=>{onCollapse(id)}})]),collapsed?null:maybeCall(content)])};const Panels=overrides=>({...Groundhogg.createRegistry({}),storagePrefix:"gh-panels",collapse(id){if(!this.isCollapsed(id)){this.toggleCollapse(id)}},expand(id){if(this.isCollapsed(id)){this.toggleCollapse(id)}},hide(id){if(!this.isHidden(id)){this.toggleHidden(id)}},show(id){if(this.isHidden(id)){this.toggleHidden(id)}},togglePanel(id,suffix){let panels=this.getPanelIds(suffix);if(panels.includes(id)){panels.splice(panels.indexOf(id),1)}else{panels.push(id)}localStorage.setItem(`${this.storagePrefix}-${suffix}`,JSON.stringify(panels))},toggleHidden(id){this.togglePanel(id,"hidden")},toggleCollapse(id){this.togglePanel(id,"collapsed")},getPanelIds(suffix){return JSON.parse(localStorage.getItem(`${this.storagePrefix}-${suffix}`))||[]},getHiddenPanelIds(){return this.getPanelIds("hidden")},getCollapsedPanelIds(){return this.getPanelIds("collapsed")},isHidden(id){return this.getHiddenPanelIds().includes(id)},isCollapsed(id){return this.getCollapsedPanelIds().includes(id)},PanelControls(){return Div({},[...this.map((item,id)=>Div({className:"display-flex gap-10",style:{marginBottom:"10px"}},[Toggle({checked:!this.isHidden(id),id:`toggle-${id}`,onChange:e=>{this.toggleHidden(id)}}),Label({for:`toggle-${id}`},item.name)]))])},Panel(id){let{content,...panel}=this.get(id);return Panel({id:id,...panel,collapsed:this.isCollapsed(id),hidden:this.isHidden(id),onCollapse:id=>{this.toggleCollapse(id);morphdom(document.getElementById(`${id}-panel`),this.Panel(id))}},content)},Panels(){return Div({className:"display-flex column gap-20",id:this.storagePrefix},this.keys().map(id=>this.Panel(id)))},...overrides});const Relationships=({title="",id,store,child_type="",parent_type="",renderItem=item=>{},onAddItem=(r,j)=>{}})=>{const rel_type_key=child_type?"child_type":"parent_type";const rel_type=child_type||parent_type;const rel_id_key=child_type?"child_id":"parent_id";const State=Groundhogg.createState({loaded:false,items:[]});const fetchRelationships=()=>store.fetchRelationships(id,{[rel_type_key]:rel_type}).then(items=>State.set({items:items,loaded:true}));const deleteRelationship=itemId=>store.deleteRelationships(id,{[rel_type_key]:rel_type,[rel_id_key]:itemId}).then(()=>State.set({items:State.items.filter(item=>item.ID!==itemId)}));const createRelationship=item=>store.createRelationships(id,{[rel_type_key]:rel_type,[rel_id_key]:item.ID}).then(()=>State.set({items:[...State.items,item]}));return Div({id:`${rel_type_key}-${rel_type}-rel-of-${id}`,className:`display-flex column relationship-editor ${rel_type_key}-${rel_type}`},morph=>{const handleDeleteRelationship=itemId=>deleteRelationship(itemId).then(morph);if(!State.loaded){fetchRelationships().then(morph);return Skeleton({},["full","full","full"])}const AddRelButton=()=>Button({id:`add-${rel_type_key}-${rel_type}-rel-for-${id}`,className:"gh-button secondary text icon",onClick:e=>{let promise=new Promise((resolve,reject)=>onAddItem(resolve,reject,State));promise.then(item=>createRelationship(item).then(morph))}},[Dashicon("plus-alt2"),ToolTip(__("Add relationship","groundhogg"),"left")]);return Fragment([title?Div({className:"space-between"},[H4({},title),AddRelButton()]):null,...State.items.map(item=>renderItem({...item,onDelete:handleDeleteRelationship})),title?null:Div({className:"display-flex flex-end"},AddRelButton())])})};const OwnerPicker=({id="select-owners",selected=[],onChange=ids=>{},allow0=true,itemDisplay=user=>user.data.display_name,multiple=true,...overrides})=>ItemPicker({id:`select-users`,noneSelected:__("Select a user...","groundhogg"),selected:selected.map(user_id=>{if(user_id==0&&allow0){return{id:0,text:__("The contact owner","groundhogg")}}return{id:user_id,text:itemDisplay(getOwner(user_id))}}),multiple:multiple,style:{flexGrow:1},isValidSelection:id=>id===0||getOwner(id),fetchOptions:search=>{search=new RegExp(search,"i");let options=Groundhogg.filters.owners.map(u=>({id:u.ID,text:itemDisplay(u)}));if(allow0){options.push({id:0,text:__("The contact owner","groundhogg")})}options=options.filter(({text})=>text.match(search));return Promise.resolve(options)},onChange:items=>{if(multiple){onChange(items.map(({id})=>id));return}onChange(items)},...overrides});function getClosestRelativeAncestor(element){let parent=element.parentElement;while(parent){if(window.getComputedStyle(parent).position==="relative"){return parent}parent=parent.parentElement}return null}const Tour=(steps,{onFinish=()=>{},beforeDismiss=({dismiss})=>dismiss(),onDismiss=()=>{},fixed=false})=>{const State=Groundhogg.createState({current:0,step:null,target:null,relative:null});const currentStep=()=>steps[State.current];const removeSteps=()=>{document.querySelectorAll(".tour-prompt-container").forEach(el=>el.remove());document.querySelectorAll(".tour-prompt").forEach(el=>el.remove());document.querySelectorAll(".tour-highlighted").forEach(el=>el.classList.remove("tour-highlighted"))};const remove=()=>{removeSteps();document.removeEventListener("resize",rePositionStep);document.removeEventListener("scroll",rePositionStep)};const dismiss=async()=>{remove();onDismiss()};const next=()=>{const{onNext=()=>{}}=currentStep();onNext();if(State.current+1>=steps.length){remove();onFinish(true);return}State.current++;showStep()};const prev=()=>{const{onPrev=()=>{}}=currentStep();onPrev();if(State.current<=0){return}State.current--;showStep()};const showStep=()=>{removeSteps();positionStep()};function rePositionStep(){let{position}=currentStep();let{target,relative,step,windowEl}=State;const targetPos=target.getBoundingClientRect();const relativePos=relative.getBoundingClientRect();const stepPos=step.getBoundingClientRect();const gap=20;if(fixed){windowEl.style.height=`${targetPos.height+gap*2}px`;windowEl.style.width=`${targetPos.width+gap*2}px`;windowEl.style.borderWidth=`${Math.round(targetPos.y)-gap}px ${Math.round(window.innerWidth-targetPos.x)-gap}px ${Math.round(window.innerHeight-targetPos.y)-gap}px ${Math.round(targetPos.x)-gap}px`;switch(position){case"right":step.style.left=`${targetPos.x+targetPos.width+gap}px`;step.style.top=`${targetPos.y}px`;break;case"left":step.style.left=`${targetPos.x-stepPos.width-gap}px`;step.style.top=`${targetPos.y}px`;break;case"above":step.style.left=`${targetPos.x}px`;step.style.top=`${targetPos.y-stepPos.height-gap}px`;break;case"below":step.style.left=`${targetPos.x}px`;step.style.top=`${targetPos.y+targetPos.height+gap}px`;break;case"below-left":step.style.left=`${targetPos.x+targetPos.width-stepPos.width}px`;step.style.top=`${targetPos.y+targetPos.height+gap}px`;break}return}switch(position){case"right":step.style.left=`${targetPos.right-relativePos.left+gap}px`;step.style.top=`${targetPos.top-relativePos.top}px`;break;case"left":step.style.left=`${targetPos.left-relativePos.left-stepPos.width-gap}px`;step.style.top=`${targetPos.top-relativePos.top}px`;break;case"above":step.style.left=`${targetPos.left-relativePos.left}px`;step.style.top=`${targetPos.top-relativePos.top-stepPos.height-gap}px`;break;case"below":step.style.left=`${targetPos.left-relativePos.left}px`;step.style.top=`${targetPos.bottom-relativePos.top+gap}px`;break;case"below-left":step.style.left=`${targetPos.right-relativePos.left-stepPos.width}px`;step.style.top=`${targetPos.bottom-relativePos.top+gap}px`;break}}function positionStep(){let tourEl=TourStep();let windowEl=tourEl.querySelector(".tour-window");let stepEl=tourEl.querySelector(".tour-prompt");let{target,relative,onInit=()=>{},onBefore=()=>{}}=currentStep();target=document.querySelector(target);if(!target){next();return}if(fixed){relative=document.body}else if(relative){relative=target.closest(relative)}else{relative=getClosestRelativeAncestor(target)}stepEl.style.position=fixed?"fixed":"absolute";if(fixed){relative.append(tourEl)}else{target.classList.add("tour-highlighted");relative.append(stepEl)}target.scrollIntoView({behavior:"instant",block:"center",inline:"center"});onBefore({next:next,prev:prev,target:target,relative:relative,step:stepEl,windowEl:windowEl,currentStep:currentStep});State.set({step:stepEl,windowEl:windowEl,target:target,relative:relative});rePositionStep();onInit({next:next,prev:prev,target:target,relative:relative,step:stepEl,windowEl:windowEl,currentStep:currentStep});stepEl.querySelector("#tour-next").focus()}const TourStep=()=>MakeEl.Div({className:"tour-prompt-container"},[fixed?MakeEl.Div({className:"tour-window"},[MakeEl.Div({className:"tour-window-shadow",onClick:next})]):null,MakeEl.Div({className:`tour-prompt ${currentStep().position}`,style:{padding:"10px",width:"200px"}},[MakeEl.Button({className:"dismiss",onClick:e=>{beforeDismiss({dismiss:dismiss,State:State})}},MakeEl.Dashicon("no-alt")),MakeEl.Div({},currentStep().prompt),MakeEl.Div({className:"display-flex flex-end gap-5 space-above-10"},[State.current>0?MakeEl.Button({id:"tour-prev",className:"gh-button small secondary text prev-step",onClick:()=>prev()},"Prev"):null,currentStep().showNext===false?null:MakeEl.Button({id:"tour-next",className:`gh-button small ${State.current<steps.length-1?"secondary":"primary"} next-step`,onClick:()=>next()},State.current<steps.length-1?"Next":"Finish")])])]);document.addEventListener("resize",rePositionStep);document.addEventListener("scroll",rePositionStep);positionStep()};Groundhogg.components={QuickSearch:QuickSearch,addContactModal:addContactModal,internalForm:internalForm,betterTagPicker:betterTagPicker,quickAddForm:quickAddForm,selectContactModal:selectContactModal,quickEditContactModal:quickEditContactModal,makeInput:makeInput,emailModal:emailModal,EmailTemplateModal:EmailTemplateModal,fileUploader:fileUploader,EmailPreview:EmailPreview,EmailPreviewModal:EmailPreviewModal,ImageInput:ImageInput,ImagePicker:ImagePicker,FeedbackModal:FeedbackModal,ContactList:ContactList,ContactListItem:ContactListItem,Panel:Panel,Panels:Panels,Relationships:Relationships,OwnerPicker:OwnerPicker,Tour:Tour}})(jQuery);218 `,onOpen:({close})=>{let file=null;let filesToUpload=[];let filesUploaded=[];let uploading=false;const pushFiles=()=>{renderUploadingFiles();file=filesToUpload.pop();if(!file){uploading=false;return}uploading=true;let fd=new FormData;fd.append(fileName,file,file.name);fd.append("gh_admin_ajax_nonce",Groundhogg.nonces._adminajax);fd.append("action",action);beforeUpload(fd);setTimeout(()=>{fetch(ajaxurl,{method:"POST",credentials:"same-origin",body:fd}).then(r=>{if(!r.ok){dialog({message:__("Something when wrong..."),type:"error"});return}return r.json()}).then(r=>{if(!r.success){dialog({message:r.data[0].message,type:"error"});pushFiles();return}onUpload(r,file);filesUploaded.unshift(file);renderUploadedFiles();pushFiles()})},2e3)};const renderUploadingFiles=()=>{$("#uploading-files").html(filesToUpload.map(f=>`<div class="file"><span class="hourglass">⌛</span> ${f.name}</div>`))};const renderUploadedFiles=()=>{$("#uploaded-files").html(filesUploaded.map(f=>`<div class="file">✅ ${f.name}</div>`))};const addFiles=files=>{filesToUpload.push(...files);if(!uploading){pushFiles()}};const $input=$("#upload-file-input");$input.on("change",e=>{addFiles(e.target.files)});$("#select-files").on("click",e=>{e.preventDefault();$input.click()});const $droppable=$(".droppable-handler");$droppable.on("dragover",e=>{e.preventDefault();$droppable.addClass("dragover")}).on("dragleave",e=>{$droppable.removeClass("dragover")}).on("drop",e=>{e.preventDefault();$droppable.removeClass("dragover");let{dataTransfer}=e.originalEvent;addFiles(dataTransfer.files)})}})};const EmailPreviewModal=async(emailId,{height=window.innerHeight*.85,width=900})=>{const{close}=loadingModal();let email;try{email=await EmailsStore.maybeFetchItem(emailId)}catch(err){close();throw err}const{from_avatar,from_email,from_name,subject,built:content}=email.context;close();return ModalFrame({frameAttributes:{className:"gh-modal-frame gh-email-preview-modal"}},({close})=>Div({style:{width:`${width}px`,height:`${height}px`}},EmailPreview({close:close,from_avatar:from_avatar,from_email:from_email,from_name:from_name,subject:subject,content:content})))};const EmailPreview=({close=false,from_avatar,from_email,from_name,subject,content})=>{return Div({className:"email-preview"},[Div({className:"from-preview display-flex gap-20 has-box-shadow"},[makeEl("img",{src:from_avatar,className:"from-avatar",height:40,width:40,style:{borderRadius:"50%"}}),Div({className:"subject-and-from"},[`<h2>${subject}</h2>`,`<span class="from-name">${from_name}</span> <span class="from-email"><${from_email}></span>`]),close!==false?Button({className:"gh-button secondary icon text",style:{marginLeft:"auto"},onClick:close},Dashicon("no-alt")):null]),Iframe({id:"desktop-preview-iframe"},content)])};$(()=>{$(document).on("click","a.gh-email-preview",e=>{e.preventDefault();let emailId=e.currentTarget.dataset.id??e.currentTarget.closest("tr").id;EmailPreviewModal(parseInt(emailId),{})});window.addEventListener("hashchange",e=>{let hash=window.location.hash.replace("#","");if(hash.startsWith("gh-email-preview/")){let emailId=hash.replace("gh-email-preview/","");EmailPreviewModal(parseInt(emailId),{})}})});const ImagePicker=({multiple=false,title=__("Select a image to upload"),selectText=__("Use this image"),onChange=attachment=>{}})=>{let file_frame=wp.media({title:title,button:{text:selectText},multiple:multiple});file_frame.on("select",function(){let attachment=file_frame.state().get("selection").first().toJSON();onChange(attachment)});file_frame.open()};const ImageInput=({id,name="src",onChange,value=""})=>{const handleChange=(value,attachment=null)=>{onChange(value,attachment);morphdom(document.getElementById(id),ImageInput({id:id,name:name,onChange:onChange,value:value}))};return Div({id:id,className:"image-picker"},[value?Div({id:`${id}-preview`,className:"image-input-preview",style:{backgroundImage:`url(${value})`},onClick:e=>{e.preventDefault();ImagePicker({multiple:false,onChange:attachment=>handleChange(attachment.url,attachment)})}}):null,InputGroup([Input({type:"text",id:`${id}-src`,value:value,className:"control full-width",name:name,onChange:e=>{handleChange(e.target.value,null)}}),Button({id:`${id}-select`,className:"gh-button secondary icon",onClick:e=>{e.preventDefault();ImagePicker({multiple:false,onChange:attachment=>handleChange(attachment.url,attachment)})}},icons.image)])])};const FeedbackModal=({subject="",message="",onSubmit=r=>{}})=>{const State=Groundhogg.createState({subject:subject,message:message,submitting:false});ModalWithHeader({width:"400px",header:"Send Feedback"},({close,morph})=>Form({className:"display-flex column gap-5",onSubmit:e=>{e.preventDefault();State.set({submitting:true});morph();Groundhogg.api.ajax({action:"gh_plugin_feedback",subject:State.subject,message:State.message}).then(r=>{onSubmit(r);dialog({message:"Thanks for your feedback!"});close()});return false}},[Label({for:"feedback-subject"},["What feature are you submitting feedback for?"]),Input({id:"feedback-subject",value:State.subject,required:true,onInput:e=>State.set({subject:e.target.value})}),Div(),Label({for:"feedback-message"},["What is your feedback? Be as descriptive as possible."]),Textarea({id:"feedback-message",value:State.message,required:true,rows:4,onInput:e=>State.set({message:e.target.value})}),Button({className:"gh-button primary",type:"submit",disabled:State.submitting},"Send feedback"),Pg({},"Your email address will be collected to validate your feedback, but will not be used beyond that.")]))};$(document).on("click","a.feedback-modal",e=>{e.preventDefault();const{subject="",message=""}=e.currentTarget.dataset;FeedbackModal({subject:subject,message:message})});const ContactPhone=(icon,number,extension="")=>number?Span({className:"contact-phone"},[icon,An({href:`tel:${number}`},number),extension?Span({className:"ext"},` x${extension}`):null]):null;const ContactListItem=(item,{extra=item=>null,...props}={})=>{let allTags=jsonCopy(item.tags);let showTags=allTags.splice(0,10);const{ID}=item;const{full_name,gravatar,date_created,email}=item.data;const{primary_phone="",primary_phone_extension="",mobile_phone="",company_phone="",company_phone_extension=""}=item.meta;return Div({className:`contact-list-item`,id:`contact-list-item-${ID}`,dataId:ID,...props},[Div({className:"display-flex gap-10"},[Img({className:"avatar",src:gravatar,alt:"avatar"}),Div({className:"display-flex column"},[Div({},[makeEl("h4",{style:{margin:0}},full_name),Span({className:"subscribed"},` — ${sprintf(__("Subscribed %s"),`<abbr title="${formatDateTime(date_created)}">${sprintf(__("%s ago "),item.i18n.created)}</abbr>`)}`)]),Div({},[An({href:`mailto:${email}`},email),Span({},[" — ",Span({className:`gh-text ${item.is_marketable?"green":"red"}`},Groundhogg.filters.optin_status[item.data.optin_status])])])])]),Div({className:"show-on-hover"},[primary_phone||company_phone||mobile_phone?Div({className:"contact-phones"},[ContactPhone(icons.mobile,mobile_phone),ContactPhone(icons.phone,primary_phone,primary_phone_extension),ContactPhone(icons.phone,company_phone,company_phone_extension)]):null,Div({className:"gh-tags"},[...showTags.map(tag=>Span({className:"gh-tag"},tag.data.tag_name)),allTags.length?Span({},sprintf("and %d more...",allTags.length)):null]),maybeCall(extra,item)])])};const ContactList=(contacts=[],{noContacts=()=>null,itemProps={}}={})=>{if(!contacts.length){return maybeCall(noContacts)}return Div({className:"contact-list"},contacts.map(contact=>ContactListItem(contact,maybeCall(itemProps,contact))))};const QuickSearch=({itemProps={},queryOverrides={}}={})=>{const State=Groundhogg.createState({search:"",searched:false,results:[],loaded:false});const fetchResults=async()=>{let results=await ContactsStore.fetchItems({search:State.search,orderby:"date_created",order:"DESC",limit:5,...queryOverrides});State.set({results:results,searched:true,loaded:true})};return Div({id:"quick-search-wrap"},morph=>{if(!State.loaded){fetchResults().then(morph)}const updateResults=debounce(async()=>{await fetchResults();morph()},300);return Fragment([Form({action:adminPageURL("gh_contacts")},[Input({type:"hidden",name:"page",value:"gh_contacts"}),Input({id:"quick-search-input",placeholder:__("Search by name or email...","groundhogg"),type:"search",name:"s",value:State.search,onInput:e=>{State.set({search:e.target.value});updateResults()}})]),State.loaded?null:Skeleton({},["full","full","full"]),State.results.length?ContactList(State.results,{itemProps:item=>({className:"contact-list-item clickable",onClick:e=>{window.open(item.admin,"_self")},...maybeCall(itemProps,item)})}):null,State.results.length===0&&State.searched?Pg({style:{textAlign:"center"}},__("No contacts found for the current search","groundhogg")):null])})};const Panel=({id,name,collapsed=false,hidden=false,onCollapse=id=>{}},content)=>{if(hidden){return null}return Div({id:`${id}-panel`,className:`gh-panel ${collapsed?"closed":""}`},[Div({className:`gh-panel-header`},[H2({},name),Button({className:"toggle-indicator",onClick:e=>{onCollapse(id)}})]),collapsed?null:maybeCall(content)])};const Panels=overrides=>({...Groundhogg.createRegistry({}),storagePrefix:"gh-panels",collapse(id){if(!this.isCollapsed(id)){this.toggleCollapse(id)}},expand(id){if(this.isCollapsed(id)){this.toggleCollapse(id)}},hide(id){if(!this.isHidden(id)){this.toggleHidden(id)}},show(id){if(this.isHidden(id)){this.toggleHidden(id)}},togglePanel(id,suffix){let panels=this.getPanelIds(suffix);if(panels.includes(id)){panels.splice(panels.indexOf(id),1)}else{panels.push(id)}localStorage.setItem(`${this.storagePrefix}-${suffix}`,JSON.stringify(panels))},toggleHidden(id){this.togglePanel(id,"hidden")},toggleCollapse(id){this.togglePanel(id,"collapsed")},getPanelIds(suffix){return JSON.parse(localStorage.getItem(`${this.storagePrefix}-${suffix}`))||[]},getHiddenPanelIds(){return this.getPanelIds("hidden")},getCollapsedPanelIds(){return this.getPanelIds("collapsed")},isHidden(id){return this.getHiddenPanelIds().includes(id)},isCollapsed(id){return this.getCollapsedPanelIds().includes(id)},PanelControls(){return Div({},[...this.map((item,id)=>Div({className:"display-flex gap-10",style:{marginBottom:"10px"}},[Toggle({checked:!this.isHidden(id),id:`toggle-${id}`,onChange:e=>{this.toggleHidden(id)}}),Label({for:`toggle-${id}`},item.name)]))])},Panel(id){let{content,...panel}=this.get(id);return Panel({id:id,...panel,collapsed:this.isCollapsed(id),hidden:this.isHidden(id),onCollapse:id=>{this.toggleCollapse(id);morphdom(document.getElementById(`${id}-panel`),this.Panel(id))}},content)},Panels(){return Div({className:"display-flex column gap-20",id:this.storagePrefix},this.keys().map(id=>this.Panel(id)))},...overrides});const Relationships=({title="",id,store,child_type="",parent_type="",renderItem=item=>{},onAddItem=(r,j)=>{}})=>{const rel_type_key=child_type?"child_type":"parent_type";const rel_type=child_type||parent_type;const rel_id_key=child_type?"child_id":"parent_id";const State=Groundhogg.createState({loaded:false,items:[]});const fetchRelationships=()=>store.fetchRelationships(id,{[rel_type_key]:rel_type}).then(items=>State.set({items:items,loaded:true}));const deleteRelationship=itemId=>store.deleteRelationships(id,{[rel_type_key]:rel_type,[rel_id_key]:itemId}).then(()=>State.set({items:State.items.filter(item=>item.ID!==itemId)}));const createRelationship=item=>store.createRelationships(id,{[rel_type_key]:rel_type,[rel_id_key]:item.ID}).then(()=>State.set({items:[...State.items,item]}));return Div({id:`${rel_type_key}-${rel_type}-rel-of-${id}`,className:`display-flex column relationship-editor ${rel_type_key}-${rel_type}`},morph=>{const handleDeleteRelationship=itemId=>deleteRelationship(itemId).then(morph);if(!State.loaded){fetchRelationships().then(morph);return Skeleton({},["full","full","full"])}const AddRelButton=()=>Button({id:`add-${rel_type_key}-${rel_type}-rel-for-${id}`,className:"gh-button secondary text icon",onClick:e=>{let promise=new Promise((resolve,reject)=>onAddItem(resolve,reject,State));promise.then(item=>createRelationship(item).then(morph))}},[Dashicon("plus-alt2"),ToolTip(__("Add relationship","groundhogg"),"left")]);return Fragment([title?Div({className:"space-between"},[H4({},title),AddRelButton()]):null,...State.items.map(item=>renderItem({...item,onDelete:handleDeleteRelationship})),title?null:Div({className:"display-flex flex-end"},AddRelButton())])})};const OwnerPicker=({id="select-owners",selected=[],onChange=ids=>{},allow0=true,itemDisplay=user=>user.data.display_name,multiple=true,...overrides})=>ItemPicker({id:`select-users`,noneSelected:__("Select a user...","groundhogg"),selected:selected.map(user_id=>{if(user_id==0&&allow0){return{id:0,text:__("The contact owner","groundhogg")}}return{id:user_id,text:itemDisplay(getOwner(user_id))}}),multiple:multiple,style:{flexGrow:1},isValidSelection:id=>id===0||getOwner(id),fetchOptions:search=>{search=new RegExp(search,"i");let options=Groundhogg.filters.owners.map(u=>({id:u.ID,text:itemDisplay(u)}));if(allow0){options.push({id:0,text:__("The contact owner","groundhogg")})}options=options.filter(({text})=>text.match(search));return Promise.resolve(options)},onChange:items=>{if(multiple){onChange(items.map(({id})=>id));return}onChange(items)},...overrides});function getClosestRelativeAncestor(element){let parent=element.parentElement;while(parent){if(window.getComputedStyle(parent).position==="relative"){return parent}parent=parent.parentElement}return null}const Tour=(steps,{onFinish=()=>{},beforeDismiss=({dismiss})=>dismiss(),onDismiss=()=>{},fixed=false})=>{const State=Groundhogg.createState({current:0,step:null,target:null,relative:null});const currentStep=()=>steps[State.current];const removeSteps=()=>{document.querySelectorAll(".tour-prompt-container").forEach(el=>el.remove());document.querySelectorAll(".tour-prompt").forEach(el=>el.remove());document.querySelectorAll(".tour-highlighted").forEach(el=>el.classList.remove("tour-highlighted"))};const remove=()=>{removeSteps();document.removeEventListener("resize",rePositionStep);document.removeEventListener("scroll",rePositionStep)};const dismiss=async()=>{remove();onDismiss()};const next=()=>{const{onNext=()=>{}}=currentStep();onNext();if(State.current+1>=steps.length){remove();onFinish(true);return}State.current++;showStep()};const prev=()=>{const{onPrev=()=>{}}=currentStep();onPrev();if(State.current<=0){return}State.current--;showStep()};const showStep=()=>{removeSteps();positionStep()};function rePositionStep(){let{position}=currentStep();let{target,relative,step,windowEl}=State;const targetPos=target.getBoundingClientRect();const relativePos=relative.getBoundingClientRect();const stepPos=step.getBoundingClientRect();const gap=20;if(fixed){windowEl.style.height=`${targetPos.height+gap*2}px`;windowEl.style.width=`${targetPos.width+gap*2}px`;windowEl.style.borderWidth=`${Math.round(targetPos.y)-gap}px ${Math.round(window.innerWidth-targetPos.x)-gap}px ${Math.round(window.innerHeight-targetPos.y)-gap}px ${Math.round(targetPos.x)-gap}px`;switch(position){case"right":step.style.left=`${targetPos.x+targetPos.width+gap}px`;step.style.top=`${targetPos.y}px`;break;case"left":step.style.left=`${targetPos.x-stepPos.width-gap}px`;step.style.top=`${targetPos.y}px`;break;case"above":step.style.left=`${targetPos.x}px`;step.style.top=`${targetPos.y-stepPos.height-gap}px`;break;case"below":step.style.left=`${targetPos.x}px`;step.style.top=`${targetPos.y+targetPos.height+gap}px`;break;case"below-left":step.style.left=`${targetPos.x+targetPos.width-stepPos.width}px`;step.style.top=`${targetPos.y+targetPos.height+gap}px`;break}return}switch(position){case"right":step.style.left=`${targetPos.right-relativePos.left+gap}px`;step.style.top=`${targetPos.top-relativePos.top}px`;break;case"left":step.style.left=`${targetPos.left-relativePos.left-stepPos.width-gap}px`;step.style.top=`${targetPos.top-relativePos.top}px`;break;case"above":step.style.left=`${targetPos.left-relativePos.left}px`;step.style.top=`${targetPos.top-relativePos.top-stepPos.height-gap}px`;break;case"below":step.style.left=`${targetPos.left-relativePos.left}px`;step.style.top=`${targetPos.bottom-relativePos.top+gap}px`;break;case"below-left":step.style.left=`${targetPos.right-relativePos.left-stepPos.width}px`;step.style.top=`${targetPos.bottom-relativePos.top+gap}px`;break}}function positionStep(){let tourEl=TourStep();let windowEl=tourEl.querySelector(".tour-window");let stepEl=tourEl.querySelector(".tour-prompt");let{target,relative,onInit=()=>{},onBefore=()=>{}}=currentStep();target=document.querySelector(target);if(!target){next();return}if(fixed){relative=document.body}else if(relative){relative=target.closest(relative)}else{relative=getClosestRelativeAncestor(target)}stepEl.style.position=fixed?"fixed":"absolute";if(fixed){relative.append(tourEl)}else{target.classList.add("tour-highlighted");relative.append(stepEl)}target.scrollIntoView({behavior:"instant",block:"center",inline:"center"});onBefore({next:next,prev:prev,target:target,relative:relative,step:stepEl,windowEl:windowEl,currentStep:currentStep});State.set({step:stepEl,windowEl:windowEl,target:target,relative:relative});rePositionStep();onInit({next:next,prev:prev,target:target,relative:relative,step:stepEl,windowEl:windowEl,currentStep:currentStep});stepEl.querySelector("#tour-next").focus()}const TourStep=()=>MakeEl.Div({className:"tour-prompt-container"},[fixed?MakeEl.Div({className:"tour-window"},[MakeEl.Div({className:"tour-window-shadow",onClick:next})]):null,MakeEl.Div({className:`tour-prompt ${currentStep().position}`,style:{padding:"10px",width:"200px"}},[MakeEl.Button({className:"dismiss",onClick:e=>{beforeDismiss({dismiss:dismiss,State:State})}},MakeEl.Dashicon("no-alt")),MakeEl.Div({},currentStep().prompt),MakeEl.Div({className:"display-flex flex-end gap-5 space-above-10"},[State.current>0?MakeEl.Button({id:"tour-prev",className:"gh-button small secondary text prev-step",onClick:()=>prev()},"Prev"):null,currentStep().showNext===false?null:MakeEl.Button({id:"tour-next",className:`gh-button small ${State.current<steps.length-1?"secondary":"primary"} next-step`,onClick:()=>next()},State.current<steps.length-1?"Next":"Finish")])])]);document.addEventListener("resize",rePositionStep);document.addEventListener("scroll",rePositionStep);positionStep()};Groundhogg.components={QuickSearch:QuickSearch,addContactModal:addContactModal,internalForm:internalForm,betterTagPicker:betterTagPicker,quickAddForm:quickAddForm,selectContactModal:selectContactModal,quickEditContactModal:quickEditContactModal,makeInput:makeInput,emailModal:emailModal,EmailTemplateModal:EmailTemplateModal,fileUploader:fileUploader,EmailPreview:EmailPreview,EmailPreviewModal:EmailPreviewModal,ImageInput:ImageInput,ImagePicker:ImagePicker,FeedbackModal:FeedbackModal,ContactList:ContactList,ContactListItem:ContactListItem,Panel:Panel,Panels:Panels,Relationships:Relationships,OwnerPicker:OwnerPicker,Tour:Tour}})(jQuery); -
groundhogg/trunk/assets/js/admin/emails/email-block-editor.js
r3395861 r3458296 9643 9643 discord : 'Discord', 9644 9644 rumble : 'Rumble', 9645 bluesky : 'Bluesky', 9645 9646 } 9646 9647 -
groundhogg/trunk/assets/js/admin/emails/email-block-editor.min.js
r3395861 r3458296 195 195 `]),`<!-- /wp:query -->`]).innerHTML},defaults:{layout:"cards",featured:true,excerpt:false,thumbnail:true,thumbnail_size:"thumbnail",columns:2,gap:20,cardStyle:{},headingStyle:fontDefaults({fontSize:24}),excerptStyle:fontDefaults({fontSize:16}),queryId:"",post_type:"post",number:5,offset:0}});const ChildBlocks=({children})=>Div({className:`children sortable-blocks ${children.length?"":"empty"}`,onCreate:el=>{makeSortable(el)}},[...children.map(b=>EditBlockWrapper(b))]);const QueryLoopContent=({DynamicContent,...block})=>{let dynamicContent=Div({},DynamicContent());let childBlockPlace=dynamicContent.querySelector(".replace-with-child-blocks");if(!childBlockPlace){return dynamicContent.firstElementChild}childBlockPlace.replaceWith(ChildBlocks(block));return Fragment([dynamicContent.firstElementChild])};const PostTagReference=[{tag:"the_title",desc:"The post title"},{tag:"the_excerpt",desc:"The post excerpt"},{tag:"the_url",desc:"The link to the post"},{tag:"the_thumbnail",desc:"The thumbnail image"},{tag:"the_thumbnail_url",desc:"The thumbnail image URL"},{tag:"the_content",desc:"The post content"},{tag:"the_id",desc:"The post ID"},{tag:"the_date",desc:"The post publish date"},{tag:"the_author",desc:"The post author"},{tag:"read_more",desc:'A "Read more" link to the post'}];registerDynamicBlock("queryloop","Query Loop",{svg:`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> 196 196 <path fill="currentColor" d="M18 7a7.669 7.669 0 0 0-6 3.19A7.669 7.669 0 0 0 6 7c-3.687 0-5 2.583-5 5 0 3.687 2.583 5 5 5a7.669 7.669 0 0 0 6-3.19A7.669 7.669 0 0 0 18 17c2.417 0 5-1.313 5-5 0-2.417-1.313-5-5-5ZM6 15a2.689 2.689 0 0 1-3-3 2.689 2.689 0 0 1 3-3c2.579 0 4.225 2.065 4.837 3-.612.935-2.258 3-4.837 3Zm12 0c-2.579 0-4.225-2.065-4.837-3 .612-.935 2.258-3 4.837-3a2.689 2.689 0 0 1 3 3 2.689 2.689 0 0 1-3 3Z"/> 197 </svg>`,attributes:{children:el=>{let blockTable=el.querySelector("table:not(.email-columns)");return parseBlocksFromTable(blockTable)}},controls:({gap=10,columns=2,thumbnail_size="post-thumbnail",updateBlock,...query})=>{return Fragment([ControlGroup({id:"queryloop-layout",name:"Layout"},[Control({label:"Columns"},NumberControl({id:"columns",className:"control-input",value:columns,step:1,onInput:e=>updateBlock({columns:e.target.value})})),Control({label:"Gap"},NumberControl({id:"column-gap",className:"control-input",value:gap,step:5,unit:"px",onInput:e=>updateBlock({gap:e.target.value})})),Control({label:"Thumbnail Size"},Select({id:"thumbnail-size",style:{width:"115px"},selected:thumbnail_size,options:imageSizes.map(size=>({value:size,text:size})),onChange:e=>updateBlock({thumbnail_size:e.target.value})}))]),QueryControls({updateBlock:updateBlock,...query}),ControlGroup({id:"queryloop-reference",name:"Reference"},[Pg({},"Use the below merge tags to merge post data within the query loop."),...PostTagReference.map(tag=>Div({className:"display-flex space-between"},[makeEl("code",{className:"copy-text"},`#${tag.tag}#`),Span({className:"tag-desc"},tag.desc)]))])])},edit:QueryLoopContent,html:({DynamicContent,...block})=>{if(isGeneratingHTML()){return renderBlocksHTML(block.children)}return QueryLoopContent({DynamicContent:DynamicContent,...block})},plainText:({children=[]})=>renderBlocksPlainText(children),defaults:{children:[createBlock("text",{content:`<p>#the_thumbnail#</p>\n<h2>#the_title#</h2>\n<p>#the_excerpt#</p>\n<p>#read_more#</p>`})],columns:2,layout:"grid",queryId:"",post_type:"post",number:6,offset:0,thumbnail_size:"post-thumbnail"}});const socialIcons={facebook:"Facebook",instagram:"Instagram",linkedin:"LinkedIn",pinterest:"Pinterest",reddit:"Reddit",threads:"Threads",tiktok:"TikTok",tumblr:"Tumblr",twitch:"Twitch",twitter:"𝕏",vimeo:"Vimeo",whatsapp:"WhatsApp",wordpress:"WordPress",youtube:"YouTube",github:"GitHub",truthsocial:"Truth Social",odysee:"Odysee",discord:"Discord",rumble:"Rumble" };const socialIconThemes={"brand-boxed":"Brand Colors Square","brand-circle":"Brand Colors Circular","brand-icons":"Brand Colors Icons","black-boxed":"Black Boxed","black-circle":"Black Circular","black-icons":"Black Icons","dark-grey-boxed":"Gray Boxed","dark-grey-circle":"Gray Circle","dark-grey-icons":"Gray Icons","grey-boxed":"Gray Boxed","grey-circle":"Gray Circle","grey-icons":"Gray Icons","white-boxed":"White Boxed","white-circle":"White Circular","white-icons":"White Icons"};const SocialIcon=(icon,theme="brand-circle",size=20)=>makeEl("img",{src:`${Groundhogg.assets.images}/social-icons/${theme}/${icon||"facebook"}.png`,alt:socialIcons[icon],height:size,width:size,style:{verticalAlign:"bottom"}});const SocialIconTheme=(theme,selected,updateBlock)=>{let themeIcons=["facebook","instagram","twitter"];let{use="global",socials=[]}=getActiveBlock();if(use==="global"&&globalSocials.length>=3){themeIcons=globalSocials.map(([social])=>social).slice(0,3)}else if(use==="custom"&&socials.length>=3){themeIcons=socials.map(([social])=>social).slice(0,3)}return Button({id:`select-${theme}`,title:socialIconThemes[theme],className:`gh-button ${theme===selected?"primary":"secondary text"} social-icon-theme ${theme}`,onClick:e=>updateBlock({theme:theme,morphControls:true})},themeIcons.map(icon=>SocialIcon(icon,theme,20)))};const SocialLinksRepeater=({socials,theme,onChange})=>InputRepeater({id:"social-links",rows:socials,cells:[({setValue,value,id,...props})=>Button({className:"gh-button grey icon",id:id,onClick:e=>{theme=hasActiveBlock()?getActiveBlock().theme:"brand-boxed";MiniModal({selector:`#${id}`},({close})=>Div({className:`display-grid social-icon-picker ${theme}`},[...Object.keys(socialIcons).map(social=>Button({title:socialIcons[social],id:`${id}-${social}`,className:"gh-button secondary text dashicon span-3",onClick:e=>{setValue(social);close()}},SocialIcon(social,theme)))]))}},SocialIcon(value||"facebook",theme)),({setValue,...props},[icon])=>Input({type:"url",placeholder:`https://${icon}.com/your-profile/`,...props})],sortable:true,onChange:onChange});registerBlock("social","Socials",{attributes:{size:el=>parseInt(el.querySelector("img")?.width),gap:el=>parseInt(el.querySelector("td.gap")?.width),theme:el=>el.querySelector("img")?.src.split("/").at(-2),socials:el=>Array.from(el.querySelectorAll("a")).map(el=>{let png=el.firstElementChild.src.split("/").at(-1);return[png.substr(0,png.indexOf(".png")),el.href]})},svg:`197 </svg>`,attributes:{children:el=>{let blockTable=el.querySelector("table:not(.email-columns)");return parseBlocksFromTable(blockTable)}},controls:({gap=10,columns=2,thumbnail_size="post-thumbnail",updateBlock,...query})=>{return Fragment([ControlGroup({id:"queryloop-layout",name:"Layout"},[Control({label:"Columns"},NumberControl({id:"columns",className:"control-input",value:columns,step:1,onInput:e=>updateBlock({columns:e.target.value})})),Control({label:"Gap"},NumberControl({id:"column-gap",className:"control-input",value:gap,step:5,unit:"px",onInput:e=>updateBlock({gap:e.target.value})})),Control({label:"Thumbnail Size"},Select({id:"thumbnail-size",style:{width:"115px"},selected:thumbnail_size,options:imageSizes.map(size=>({value:size,text:size})),onChange:e=>updateBlock({thumbnail_size:e.target.value})}))]),QueryControls({updateBlock:updateBlock,...query}),ControlGroup({id:"queryloop-reference",name:"Reference"},[Pg({},"Use the below merge tags to merge post data within the query loop."),...PostTagReference.map(tag=>Div({className:"display-flex space-between"},[makeEl("code",{className:"copy-text"},`#${tag.tag}#`),Span({className:"tag-desc"},tag.desc)]))])])},edit:QueryLoopContent,html:({DynamicContent,...block})=>{if(isGeneratingHTML()){return renderBlocksHTML(block.children)}return QueryLoopContent({DynamicContent:DynamicContent,...block})},plainText:({children=[]})=>renderBlocksPlainText(children),defaults:{children:[createBlock("text",{content:`<p>#the_thumbnail#</p>\n<h2>#the_title#</h2>\n<p>#the_excerpt#</p>\n<p>#read_more#</p>`})],columns:2,layout:"grid",queryId:"",post_type:"post",number:6,offset:0,thumbnail_size:"post-thumbnail"}});const socialIcons={facebook:"Facebook",instagram:"Instagram",linkedin:"LinkedIn",pinterest:"Pinterest",reddit:"Reddit",threads:"Threads",tiktok:"TikTok",tumblr:"Tumblr",twitch:"Twitch",twitter:"𝕏",vimeo:"Vimeo",whatsapp:"WhatsApp",wordpress:"WordPress",youtube:"YouTube",github:"GitHub",truthsocial:"Truth Social",odysee:"Odysee",discord:"Discord",rumble:"Rumble",bluesky:"Bluesky"};const socialIconThemes={"brand-boxed":"Brand Colors Square","brand-circle":"Brand Colors Circular","brand-icons":"Brand Colors Icons","black-boxed":"Black Boxed","black-circle":"Black Circular","black-icons":"Black Icons","dark-grey-boxed":"Gray Boxed","dark-grey-circle":"Gray Circle","dark-grey-icons":"Gray Icons","grey-boxed":"Gray Boxed","grey-circle":"Gray Circle","grey-icons":"Gray Icons","white-boxed":"White Boxed","white-circle":"White Circular","white-icons":"White Icons"};const SocialIcon=(icon,theme="brand-circle",size=20)=>makeEl("img",{src:`${Groundhogg.assets.images}/social-icons/${theme}/${icon||"facebook"}.png`,alt:socialIcons[icon],height:size,width:size,style:{verticalAlign:"bottom"}});const SocialIconTheme=(theme,selected,updateBlock)=>{let themeIcons=["facebook","instagram","twitter"];let{use="global",socials=[]}=getActiveBlock();if(use==="global"&&globalSocials.length>=3){themeIcons=globalSocials.map(([social])=>social).slice(0,3)}else if(use==="custom"&&socials.length>=3){themeIcons=socials.map(([social])=>social).slice(0,3)}return Button({id:`select-${theme}`,title:socialIconThemes[theme],className:`gh-button ${theme===selected?"primary":"secondary text"} social-icon-theme ${theme}`,onClick:e=>updateBlock({theme:theme,morphControls:true})},themeIcons.map(icon=>SocialIcon(icon,theme,20)))};const SocialLinksRepeater=({socials,theme,onChange})=>InputRepeater({id:"social-links",rows:socials,cells:[({setValue,value,id,...props})=>Button({className:"gh-button grey icon",id:id,onClick:e=>{theme=hasActiveBlock()?getActiveBlock().theme:"brand-boxed";MiniModal({selector:`#${id}`},({close})=>Div({className:`display-grid social-icon-picker ${theme}`},[...Object.keys(socialIcons).map(social=>Button({title:socialIcons[social],id:`${id}-${social}`,className:"gh-button secondary text dashicon span-3",onClick:e=>{setValue(social);close()}},SocialIcon(social,theme)))]))}},SocialIcon(value||"facebook",theme)),({setValue,...props},[icon])=>Input({type:"url",placeholder:`https://${icon}.com/your-profile/`,...props})],sortable:true,onChange:onChange});registerBlock("social","Socials",{attributes:{size:el=>parseInt(el.querySelector("img")?.width),gap:el=>parseInt(el.querySelector("td.gap")?.width),theme:el=>el.querySelector("img")?.src.split("/").at(-2),socials:el=>Array.from(el.querySelectorAll("a")).map(el=>{let png=el.firstElementChild.src.split("/").at(-1);return[png.substr(0,png.indexOf(".png")),el.href]})},svg:` 198 198 <svg viewBox="-33 0 512 512.001" xmlns="http://www.w3.org/2000/svg"> 199 199 <path fill="currentColor" -
groundhogg/trunk/groundhogg.php
r3442828 r3458296 4 4 * Plugin URI: https://www.groundhogg.io/?utm_source=wp-plugins&utm_campaign=plugin-uri&utm_medium=wp-dash 5 5 * Description: CRM and marketing automation for WordPress 6 * Version: 4.2.1 16 * Version: 4.2.12 7 7 * Author: Groundhogg Inc. 8 8 * Author URI: https://www.groundhogg.io/?utm_source=wp-plugins&utm_campaign=author-uri&utm_medium=wp-dash … … 25 25 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly 26 26 27 define( 'GROUNDHOGG_VERSION', '4.2.1 1' );28 define( 'GROUNDHOGG_PREVIOUS_STABLE_VERSION', '4.2.1 0' );27 define( 'GROUNDHOGG_VERSION', '4.2.12' ); 28 define( 'GROUNDHOGG_PREVIOUS_STABLE_VERSION', '4.2.11' ); 29 29 30 30 define( 'GROUNDHOGG__FILE__', __FILE__ ); -
groundhogg/trunk/includes/classes/funnel.php
r3343709 r3458296 642 642 } 643 643 644 $last_changed = db()->steps->cache_get_last_changed();645 $cache_key = "$this->ID:steps:$last_changed:" . md5serialize( $query );646 $steps = wp_cache_get( $cache_key, db()->steps->get_cache_group(), false, $found );647 648 // make sure all items in array are also steps649 if ( $found && is_array( $steps ) && array_all( $steps, function ( $step ) {650 return is_a( $step, Step::class );651 } ) ) {652 653 return $steps;654 }644 // $last_changed = db()->steps->cache_get_last_changed(); 645 // $cache_key = "$this->ID:steps:$last_changed:" . md5serialize( $query ); 646 // $steps = wp_cache_get( $cache_key, db()->steps->get_cache_group(), false, $found ); 647 // 648 // // make sure all items in array are also steps 649 // if ( $found && is_array( $steps ) && array_all( $steps, function ( $step ) { 650 // return is_a( $step, Step::class ); 651 // } ) ) { 652 // 653 // return $steps; 654 // } 655 655 656 656 $steps = $this->get_steps_db()->query( $query ); … … 674 674 } 675 675 676 wp_cache_set( $cache_key, $steps, db()->steps->get_cache_group(), MINUTE_IN_SECONDS );676 // wp_cache_set( $cache_key, $steps, db()->steps->get_cache_group(), MINUTE_IN_SECONDS ); 677 677 678 678 return $steps; -
groundhogg/trunk/includes/form/form-v2.php
r3400645 r3458296 1903 1903 protected $hidden_fields = []; 1904 1904 1905 public function is_turnstile_enabled() { 1906 $turnstile = get_array_var( $this->get_cleaned_json_config(), 'turnstile', [] ); 1907 return isset_not_empty( $turnstile, 'enabled' ) && is_recaptcha_enabled(); 1908 } 1909 1910 public function is_recaptcha_enabled() { 1911 $recaptcha = get_array_var( $this->get_cleaned_json_config(), 'recaptcha', [] ); 1912 return isset_not_empty( $recaptcha, 'enabled' ) && is_recaptcha_enabled(); 1913 } 1914 1915 /** 1916 * Get a cleaned assoc array of the form config 1917 * 1918 * @return array 1919 */ 1920 private function get_cleaned_json_config() { 1921 $config = $this->get_meta( 'form' ); 1922 // encode and decode to fix potential mutation errors when importing/exporting 1923 return json_decode( wp_json_encode( $config ), true ); 1924 } 1925 1905 1926 /** 1906 1927 * Get the HTML For the fields … … 1909 1930 */ 1910 1931 function get_field_html() { 1911 1912 $config = $this->get_meta( 'form' ); 1913 1914 $config = json_decode( wp_json_encode( $config ), true ); 1915 1932 $config = $this->get_cleaned_json_config(); 1916 1933 $fields = get_array_var( $config, 'fields', [] ); 1917 1934 … … 2203 2220 */ 2204 2221 public function get_fields() { 2205 $config = json_decode( wp_json_encode( $this->get_meta( 'form' ) ), true);2222 $config = $this->get_cleaned_json_config(); 2206 2223 2207 2224 if ( ! is_array( $config ) || ! isset( $config['fields'] ) ) { … … 2229 2246 2230 2247 // Ensure array and not stdClass 2231 $config = json_decode( wp_json_encode( $this->get_meta( 'form' ) ), true);2248 $config = $this->get_cleaned_json_config(); 2232 2249 $fields = $config['fields']; 2233 $recaptcha = $config['recaptcha']; 2234 $turnstile = $config['turnstile'];2235 2236 if ( $recaptcha['enabled'] ) {2237 $fields[] = $recaptcha;2238 } 2239 2240 if ( $turnstile['enabled'] ){2241 $fields[] = $ turnstile;2250 2251 // add the recaptcha to fields array for validation 2252 if ( isset( $config[ 'recaptcha' ] ) && $config[ 'recaptcha' ]['enabled'] ){ 2253 $fields[] = $config['recaptcha']; 2254 } 2255 2256 // add the turnstile to fields array for validation 2257 if ( isset( $config[ 'turnstile' ] ) && $config[ 'turnstile' ]['enabled'] ){ 2258 $fields[] = $config['turnstile']; 2242 2259 } 2243 2260 -
groundhogg/trunk/includes/functions.php
r3442828 r3458296 2590 2590 * @return false|Contact 2591 2591 */ 2592 function update_contact_with_map( $contact, array $fields, array $map = [],$submission = [] ) {2592 function update_contact_with_map( mixed $contact, array $fields, array $map = [], array $submission = [] ) { 2593 2593 return generate_contact_with_map( $fields, $map, $submission, $contact ); 2594 2594 } … … 2599 2599 * @throws \Exception 2600 2600 * 2601 * @param $fields arraythe raw data from the source2602 * @param $map arraymap of field_ids to contact keys2601 * @param array $fields the raw data from the source 2602 * @param array $map map of field_ids to contact keys 2603 2603 * @param array $submission settings for the submission 2604 2604 * @param null|Contact $contact an existing contact record to modify … … 2606 2606 * @return Contact|false 2607 2607 */ 2608 function generate_contact_with_map( $fields, $map = [], $submission = [],$contact = null ) {2608 function generate_contact_with_map( array $fields, array $map = [], array $submission = [], mixed $contact = null ) { 2609 2609 2610 2610 if ( empty( $map ) ) { … … 3862 3862 * 3863 3863 * @param string $call_name the name you wish to call 3864 * @param string $id_or_email id or email of the contact3864 * @param string|int|Contact $id_or_email id or email of the contact 3865 3865 * @param bool $by_user_id whether the ID is the ID of a WP user 3866 3866 */ … … 8273 8273 */ 8274 8274 function verify_admin_ajax_nonce() { 8275 return wp_verify_nonce( get_request_var( 'gh_admin_ajax_nonce' ), 'admin_ajax');8275 return check_ajax_referer( 'admin_ajax', 'gh_admin_ajax_nonce', false ); 8276 8276 } 8277 8277
Note: See TracChangeset
for help on using the changeset viewer.