Plugin Directory

Changeset 3310897


Ignore:
Timestamp:
06/13/2025 08:04:14 AM (10 months ago)
Author:
kovalchik8
Message:

1.1.0 Added support for WordPress menus export and import, minor fixes

Location:
magic-export-import
Files:
58 added
16 edited

Legend:

Unmodified
Added
Removed
  • magic-export-import/trunk/assets/magic-export-import.js

    r3220340 r3310897  
    283283                    response = JSON.parse(response)
    284284
    285                     this.$importProgress.html(response.progress_html)
     285                    if (response.progress_html) {
     286                        this.$importProgress.html(response.progress_html)
     287                    }
     288
    286289                    this.$importNotices.append(response.notices_html)
    287290
  • magic-export-import/trunk/assets/magic-export-import.min.css

    r3220340 r3310897  
    1 #magic-ex-im .magic-ex-im-wrapper{margin-top:15px}#magic-ex-im select,#magic-ex-im input[type=text]{max-width:100%;width:100%}#magic-ex-im p{transition:opacity .2s}#magic-ex-im p.submit{display:flex;align-items:center;-moz-column-gap:5px;column-gap:5px;margin-top:1em}#magic-ex-im label[for]{cursor:pointer}#magic-ex-im .form-field{transition:opacity .2s}#magic-ex-im .form-field p{line-height:1.3}#magic-ex-im .js-processing,#magic-ex-im .js-refreshing{pointer-events:none;opacity:.5}#magic-ex-im .js-processing p.submit:after{content:"";width:1.65em;aspect-ratio:1;background:url("../assets/loader.svg") center/contain no-repeat}#magic-ex-im #magic-ex-items-cnt-wrapper{background-color:#f0f0f1;padding:10px 0;position:sticky;top:32px;z-index:3;border-bottom:1px solid currentColor}@media(max-width: 782px){#magic-ex-im #magic-ex-items-cnt-wrapper{top:46px}}@media(max-width: 600px){#magic-ex-im #magic-ex-items-cnt-wrapper{top:0}}#magic-ex-im #magic-ex-items-cnt-wrapper h4{margin:0}#magic-ex-im #magic-ex-keys-filters{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:20px;margin-bottom:5px}#magic-ex-im #magic-ex-keys-by-group-wrapper{row-gap:5px;display:flex;flex-wrap:wrap}#magic-ex-im #magic-ex-keys-by-group{display:flex;flex-wrap:wrap;row-gap:5px;-moz-column-gap:15px;column-gap:15px}#magic-ex-im #magic-ex-advanced-wrapper{display:flex;align-items:flex-start;gap:10px}#magic-ex-im #magic-ex-advanced-wrapper:not(.js-active){display:none}#magic-ex-im #magic-ex-advanced-wrapper #magic-ex-apply-filters{flex-shrink:0;position:sticky;top:80px}#magic-ex-im #magic-ex-advanced-filters{flex-grow:1}#magic-ex-im #magic-ex-advanced-filters>*:not(.magic-ex-advanced-filter-type){padding-left:10px}#magic-ex-im .magic-ex-im-wrapper{display:flex;-moz-column-gap:50px;column-gap:50px}#magic-ex-im .magic-ex-im-wrapper>div{width:calc(50% - 25px)}@media(max-width: 1024px){#magic-ex-im .magic-ex-im-wrapper{flex-direction:column}#magic-ex-im .magic-ex-im-wrapper>div{width:100%}}#magic-ex-im .magic-ex-im-checkbox,#magic-ex-im .magic-ex-advanced-filter-type{display:flex;align-items:center}#magic-ex-im .magic-ex-im-checkbox input,#magic-ex-im .magic-ex-advanced-filter-type input{margin:0}#magic-ex-im .magic-ex-im-checkbox input:disabled+label,#magic-ex-im .magic-ex-advanced-filter-type input:disabled+label{opacity:.6;pointer-events:none}#magic-ex-im .magic-ex-im-checkbox label,#magic-ex-im .magic-ex-advanced-filter-type label{padding-left:.5em}#magic-ex-im .magic-ex-advanced-filter-type{margin:1.25em 0;padding-top:1.25em;border-top:1px solid #d3d3d3}#magic-ex-im .magic-ex-advanced-filter-type label{font-size:1.075em;font-weight:600}#magic-ex-im .magic-ex-checkboxes-wrapper{display:flex;flex-wrap:wrap;-moz-column-gap:20px;column-gap:20px;row-gap:15px}#magic-ex-advanced-wrapper .magic-ex-taxes-wrapper #magic-ex-im .magic-ex-checkboxes-wrapper{margin-left:15px}#magic-ex-im .flash{animation:flash 1s forwards}@keyframes flash{from,50%,to{opacity:1}25%,75%{opacity:0}}#magic-ex-partial-wrapper,#magic-ex-date-wrapper{display:flex;white-space:nowrap;align-items:center;-moz-column-gap:5px;column-gap:5px}#magic-ex-partial-wrapper input[type=number],#magic-ex-date-wrapper input[type=number]{width:70px}#magic-im-notices{margin-top:25px;max-height:500px;overflow-y:auto}#magic-im-notices li{margin-bottom:.65em}#magic-im-notices li:last-child{margin-bottom:0}#select2-magic-ex-keys-select-results,#select2-magic-ex-media-keys-select-results{padding:5px;-moz-column-gap:5px;column-gap:5px;row-gap:7px;display:flex;flex-wrap:wrap}#select2-magic-ex-keys-select-results li,#select2-magic-ex-media-keys-select-results li{margin:0;border-radius:3px}.select2-container{width:100% !important}.select2-selection--single{height:auto !important}.select2-selection__choice.mandatory,.select2-results__option.mandatory{opacity:.6;pointer-events:none;order:-1}.select2-selection__choice.mandatory .select2-selection__choice__remove,.select2-results__option.mandatory .select2-selection__choice__remove{display:none}.select2-results__option[aria-selected=true]:not(.select2-results__option--highlighted){background-color:rgba(221,221,221,.75)}.select2-selection__rendered .select2-search--inline,.select2-selection__rendered .select2-search__field{max-width:100%}.select2-selection__choice{margin:5px 5px 5px 0 !important;white-space:normal !important;border:none !important;min-height:25px;display:inline-flex;align-items:center}.select2-selection__choice code{margin-left:5px}.select2-selection__choice:has(code){padding-right:0 !important}.select2-selection--multiple .select2-selection__rendered{display:flex !important;flex-wrap:wrap}.select2-selection--multiple .select2-selection__choice{background-color:rgba(228,228,228,.75) !important}/*# sourceMappingURL=magic-export-import.min.css.map */
     1#magic-ex-im .magic-ex-im-wrapper{margin-top:15px}#magic-ex-im select,#magic-ex-im input[type=text]{max-width:100%;width:100%}#magic-ex-im p{transition:opacity .2s}#magic-ex-im p.submit{display:flex;align-items:center;-moz-column-gap:5px;column-gap:5px;margin-top:1em}#magic-ex-im label[for]{cursor:pointer}#magic-ex-im .form-field{transition:opacity .2s}#magic-ex-im .form-field p{line-height:1.3}#magic-ex-im .js-processing,#magic-ex-im .js-refreshing{pointer-events:none;opacity:.5}#magic-ex-im .js-processing p.submit:after{content:"";width:1.65em;aspect-ratio:1;background:url("../assets/loader.svg") center/contain no-repeat}#magic-ex-im #magic-ex-items-cnt-wrapper{background-color:#f0f0f1;padding:10px 0;position:sticky;top:32px;z-index:3;border-bottom:1px solid currentColor}@media(max-width: 782px){#magic-ex-im #magic-ex-items-cnt-wrapper{top:46px}}@media(max-width: 600px){#magic-ex-im #magic-ex-items-cnt-wrapper{top:0}}#magic-ex-im #magic-ex-items-cnt-wrapper h4{margin:0}#magic-ex-im #magic-ex-keys-filters{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:20px;margin-bottom:5px}#magic-ex-im #magic-ex-keys-by-group-wrapper{row-gap:5px;display:flex;flex-wrap:wrap}#magic-ex-im #magic-ex-keys-by-group{display:flex;flex-wrap:wrap;row-gap:5px;-moz-column-gap:15px;column-gap:15px}#magic-ex-im #magic-ex-advanced-wrapper{display:flex;align-items:flex-start;gap:10px}#magic-ex-im #magic-ex-advanced-wrapper:not(.js-active){display:none}#magic-ex-im #magic-ex-advanced-wrapper #magic-ex-apply-filters{flex-shrink:0;position:sticky;top:80px}#magic-ex-im #magic-ex-advanced-filters{flex-grow:1}#magic-ex-im #magic-ex-advanced-filters>*:not(.magic-ex-advanced-filter-type){padding-left:10px}#magic-ex-im .magic-ex-im-wrapper{display:flex;-moz-column-gap:50px;column-gap:50px}#magic-ex-im .magic-ex-im-wrapper>div{width:calc(50% - 25px)}@media(max-width: 1024px){#magic-ex-im .magic-ex-im-wrapper{flex-direction:column}#magic-ex-im .magic-ex-im-wrapper>div{width:100%}}#magic-ex-im .magic-ex-im-checkbox,#magic-ex-im .magic-ex-advanced-filter-type{display:flex;align-items:center}#magic-ex-im .magic-ex-im-checkbox input,#magic-ex-im .magic-ex-advanced-filter-type input{margin:0}#magic-ex-im .magic-ex-im-checkbox input:disabled+label,#magic-ex-im .magic-ex-advanced-filter-type input:disabled+label{opacity:.6;pointer-events:none}#magic-ex-im .magic-ex-im-checkbox label,#magic-ex-im .magic-ex-advanced-filter-type label{padding-left:.5em}#magic-ex-im .magic-ex-advanced-filter-type{margin:1.25em 0;padding-top:1.25em;border-top:1px solid #d3d3d3}#magic-ex-im .magic-ex-advanced-filter-type label{font-size:1.075em;font-weight:600}#magic-ex-im .magic-ex-checkboxes-wrapper{display:flex;flex-wrap:wrap;-moz-column-gap:20px;column-gap:20px;row-gap:15px}#magic-ex-advanced-wrapper .magic-ex-taxes-wrapper #magic-ex-im .magic-ex-checkboxes-wrapper{margin-left:15px}#magic-ex-im .flash{animation:flash 1s forwards}@keyframes flash{from,50%,to{opacity:1}25%,75%{opacity:0}}#magic-ex-partial-wrapper,#magic-ex-date-wrapper{display:flex;white-space:nowrap;align-items:center;-moz-column-gap:5px;column-gap:5px}#magic-ex-partial-wrapper input[type=number],#magic-ex-date-wrapper input[type=number]{width:70px}#magic-im-notices{margin-top:25px;max-height:500px;overflow-y:auto}#magic-im-notices li{margin-bottom:.65em}#magic-im-notices li:last-child{margin-bottom:0}#select2-magic-ex-keys-select-results,#select2-magic-ex-media-keys-select-results{padding:5px;-moz-column-gap:5px;column-gap:5px;row-gap:7px;display:flex;flex-wrap:wrap}#select2-magic-ex-keys-select-results li,#select2-magic-ex-media-keys-select-results li{margin:0;border-radius:3px}.select2-container{width:100% !important}.select2-selection--single{height:auto !important}.select2-selection__choice.mandatory,.select2-results__option.mandatory{opacity:.6;pointer-events:none;order:-1}.select2-selection__choice.mandatory .select2-selection__choice__remove,.select2-results__option.mandatory .select2-selection__choice__remove{display:none}.select2-selection__choice code:empty,.select2-results__option code:empty{display:none}.select2-results__option[aria-selected=true]:not(.select2-results__option--highlighted){background-color:rgba(221,221,221,.75)}.select2-selection__rendered .select2-search--inline,.select2-selection__rendered .select2-search__field{max-width:100%}.select2-selection__choice{margin:5px 5px 5px 0 !important;white-space:normal !important;border:none !important;min-height:25px;display:inline-flex;align-items:center}.select2-selection__choice code{margin-left:5px}.select2-selection__choice:has(code){padding-right:0 !important}.select2-selection--multiple .select2-selection__rendered{display:flex !important;flex-wrap:wrap}.select2-selection--multiple .select2-selection__choice{background-color:rgba(228,228,228,.75) !important}/*# sourceMappingURL=magic-export-import.min.css.map */
  • magic-export-import/trunk/assets/magic-export-import.min.css.map

    r3220340 r3310897  
    1 {"version":3,"sources":["magic-export-import.scss"],"names":[],"mappings":"AACI,kCACI,eAAA,CAEJ,kDAEI,cAAA,CACA,UAAA,CAEJ,eACI,sBAAA,CACA,sBACI,YAAA,CACA,kBAAA,CACA,mBAAA,CAAA,cAAA,CACA,cAAA,CAGR,wBACI,cAAA,CAEJ,yBACI,sBAAA,CACA,2BACI,eAAA,CAGR,wDAEI,mBAAA,CACA,UAAA,CAGA,2CACI,UAAA,CACA,YAAA,CACA,cAAA,CACA,+DAAA,CAGR,yCACI,wBAAA,CACA,cAAA,CACA,eAAA,CACA,QAAA,CACA,SAAA,CACA,oCAAA,CACA,yBAPJ,yCAQQ,QAAA,CAAA,CAEJ,yBAVJ,yCAWQ,KAAA,CAAA,CAEJ,4CACI,QAAA,CAGR,oCACI,YAAA,CACA,6BAAA,CACA,kBAAA,CACA,cAAA,CACA,QAAA,CACA,iBAAA,CAEJ,6CACI,WAAA,CACA,YAAA,CACA,cAAA,CAEJ,qCACI,YAAA,CACA,cAAA,CACA,WAAA,CACA,oBAAA,CAAA,eAAA,CAEJ,wCACI,YAAA,CACA,sBAAA,CACA,QAAA,CACA,wDACI,YAAA,CAEJ,gEACI,aAAA,CACA,eAAA,CACA,QAAA,CAGR,wCACI,WAAA,CACA,8EACI,iBAAA,CAKJ,kCACI,YAAA,CACA,oBAAA,CAAA,eAAA,CACA,sCACI,sBAAA,CAEJ,0BANJ,kCAOQ,qBAAA,CACA,sCACI,UAAA,CAAA,CAIZ,+EACI,YAAA,CACA,kBAAA,CACA,2FACI,QAAA,CACA,yHACI,UAAA,CACA,mBAAA,CAGR,2FACI,iBAAA,CAKR,4CAEI,eAAA,CACA,kBAAA,CACA,4BAAA,CACA,kDACI,iBAAA,CACA,eAAA,CAGR,0CACI,YAAA,CACA,cAAA,CACA,oBAAA,CAAA,eAAA,CACA,YAAA,CAEA,6FACI,gBAAA,CAKZ,oBACI,2BAAA,CAGJ,iBACI,YAGI,SAAA,CAGJ,QAEI,SAAA,CAAA,CAKZ,iDAEI,YAAA,CACA,kBAAA,CACA,kBAAA,CACA,mBAAA,CAAA,cAAA,CACA,uFACI,UAAA,CAIR,kBACI,eAAA,CACA,gBAAA,CACA,eAAA,CACA,qBACI,mBAAA,CACA,gCACI,eAAA,CAKZ,kFAEI,WAAA,CACA,mBAAA,CAAA,cAAA,CACA,WAAA,CACA,YAAA,CACA,cAAA,CACA,wFACI,QAAA,CACA,iBAAA,CAKJ,mBACI,qBAAA,CAEJ,2BACI,sBAAA,CAKA,wEACI,UAAA,CACA,mBAAA,CACA,QAAA,CACA,8IACI,YAAA,CAIZ,wFAGI,sCAAA,CAGA,yGAEI,cAAA,CAGR,2BACI,+BAAA,CACA,6BAAA,CACA,sBAAA,CACA,eAAA,CACA,mBAAA,CACA,kBAAA,CACA,gCACI,eAAA,CAEJ,qCACI,0BAAA,CAIJ,0DACI,uBAAA,CACA,cAAA,CAEJ,wDACI,iDAAA","file":"magic-export-import.min.css"}
     1{"version":3,"sources":["magic-export-import.scss"],"names":[],"mappings":"AACI,kCACI,eAAA,CAEJ,kDAEI,cAAA,CACA,UAAA,CAEJ,eACI,sBAAA,CACA,sBACI,YAAA,CACA,kBAAA,CACA,mBAAA,CAAA,cAAA,CACA,cAAA,CAGR,wBACI,cAAA,CAEJ,yBACI,sBAAA,CACA,2BACI,eAAA,CAGR,wDAEI,mBAAA,CACA,UAAA,CAGA,2CACI,UAAA,CACA,YAAA,CACA,cAAA,CACA,+DAAA,CAGR,yCACI,wBAAA,CACA,cAAA,CACA,eAAA,CACA,QAAA,CACA,SAAA,CACA,oCAAA,CACA,yBAPJ,yCAQQ,QAAA,CAAA,CAEJ,yBAVJ,yCAWQ,KAAA,CAAA,CAEJ,4CACI,QAAA,CAGR,oCACI,YAAA,CACA,6BAAA,CACA,kBAAA,CACA,cAAA,CACA,QAAA,CACA,iBAAA,CAEJ,6CACI,WAAA,CACA,YAAA,CACA,cAAA,CAEJ,qCACI,YAAA,CACA,cAAA,CACA,WAAA,CACA,oBAAA,CAAA,eAAA,CAEJ,wCACI,YAAA,CACA,sBAAA,CACA,QAAA,CACA,wDACI,YAAA,CAEJ,gEACI,aAAA,CACA,eAAA,CACA,QAAA,CAGR,wCACI,WAAA,CACA,8EACI,iBAAA,CAKJ,kCACI,YAAA,CACA,oBAAA,CAAA,eAAA,CACA,sCACI,sBAAA,CAEJ,0BANJ,kCAOQ,qBAAA,CACA,sCACI,UAAA,CAAA,CAIZ,+EACI,YAAA,CACA,kBAAA,CACA,2FACI,QAAA,CACA,yHACI,UAAA,CACA,mBAAA,CAGR,2FACI,iBAAA,CAKR,4CAEI,eAAA,CACA,kBAAA,CACA,4BAAA,CACA,kDACI,iBAAA,CACA,eAAA,CAGR,0CACI,YAAA,CACA,cAAA,CACA,oBAAA,CAAA,eAAA,CACA,YAAA,CAEA,6FACI,gBAAA,CAKZ,oBACI,2BAAA,CAGJ,iBACI,YAGI,SAAA,CAGJ,QAEI,SAAA,CAAA,CAKZ,iDAEI,YAAA,CACA,kBAAA,CACA,kBAAA,CACA,mBAAA,CAAA,cAAA,CACA,uFACI,UAAA,CAIR,kBACI,eAAA,CACA,gBAAA,CACA,eAAA,CACA,qBACI,mBAAA,CACA,gCACI,eAAA,CAKZ,kFAEI,WAAA,CACA,mBAAA,CAAA,cAAA,CACA,WAAA,CACA,YAAA,CACA,cAAA,CACA,wFACI,QAAA,CACA,iBAAA,CAKJ,mBACI,qBAAA,CAEJ,2BACI,sBAAA,CAKA,wEACI,UAAA,CACA,mBAAA,CACA,QAAA,CACA,8IACI,YAAA,CAGR,0EACI,YAAA,CAGR,wFAGI,sCAAA,CAGA,yGAEI,cAAA,CAGR,2BACI,+BAAA,CACA,6BAAA,CACA,sBAAA,CACA,eAAA,CACA,mBAAA,CACA,kBAAA,CACA,gCACI,eAAA,CAEJ,qCACI,0BAAA,CAIJ,0DACI,uBAAA,CACA,cAAA,CAEJ,wDACI,iDAAA","file":"magic-export-import.min.css"}
  • magic-export-import/trunk/assets/magic-export-import.min.js

    r3220340 r3310897  
    1 (function(e){var t={$magicItemSelect:e("#magic-ex-im-post-type-select"),$exportItemsSelect:e("#magic-ex-items-select"),exportKeysSelect:"#magic-ex-keys-select",exportMediaKeysSelect:"#magic-ex-media-keys-select",$selectGroupKeysCheckboxes:e(".magic-ex-group"),$selectAllKeysCheckbox:e("#magic-ex-all-keys-checkbox"),$exportForm:e("#magic-ex-form"),$exportItemsCnt:e("#magic-ex-items-cnt"),$exFiltersTogglerCheckbox:e("#magic-ex-advanced-checkbox"),$exFilters:e("#magic-ex-advanced-filters"),$exApplyFiltersBtn:e("#magic-ex-apply-filters"),exAppliedFiltersData:"",$importTestModeCheckbox:e("#magic-im-test-mode"),$importForm:e("#magic-im-form"),$importProgress:e("#magic-im-progress"),$importNotices:e("#magic-im-notices"),importXHR:null,select2IsUnselecting:!1,timer:0,init:function(){this.$exFilters.text().trim()||this.$exFiltersTogglerCheckbox.closest(".form-field").remove();let t=e("#magic-ex-generated-file");t.length&&setTimeout(()=>{let e=t.attr("href"),s=document.createElement("a");s.href=e,s.download="",document.body.appendChild(s),s.click(),document.body.removeChild(s)},500),this.$magicItemSelect.select2({escapeMarkup:e=>e,templateResult:e=>this.select2MagicItemTemplate(e),templateSelection:e=>this.select2MagicItemTemplate(e)}),this.$exportItemsSelect.select2({placeholder:"Select items to export",minimumInputLength:0,closeOnSelect:!1,width:"100%",escapeMarkup:e=>e,templateSelection:(e,t)=>e.text.split("<code>")[0],ajax:{type:"POST",url:WPLocalize.admin_ajax_url,delay:400,dataType:"json",data:function(e){return e.action=`get_individual_export_${WPLocalize.magic_type}`,e.magic_item=WPLocalize.magic_item,e}}}),e(".magic-ex-advanced-filter-type").next("div").find(":input").prop("disabled",!0),this.initExportKeysSelects(),this.events()},events:function(){e('[name="magic-ex-by-taxonomy"]').on("change",function(){e(".magic-ex-taxes-wrapper").toggle()}),this.$exFilters.on("input",":input",t=>{let s=e(t.target);if(s.is('[name^="magic-ex-by-"]')){let e=s.is(":checked"),t=s.closest(".magic-ex-advanced-filter-type").next("div").find(":input:not(.disabled)");t.prop("disabled",!e)}this.setApplyFiltersBtn()}),this.$exportItemsSelect.on("change",e=>{this.refreshExportKeys()}),this.$exApplyFiltersBtn.on("click",e=>{e.preventDefault(),this.refreshExportKeys()}),e('[href="#swift-details-wrapper"]').on("click",t=>{e("#swift-details-wrapper").toggle()}),this.$magicItemSelect.on("change",e=>{e.preventDefault(),this.$exportForm.add(this.$importForm).addClass("js-processing");let t=new URL(window.location);t.searchParams.set("magic_item",this.$magicItemSelect.val()),window.history.pushState({},null,t.toString()),window.location.reload()}),this.$exportForm.on("submit",()=>{this.$exportForm.addClass("js-processing")}),e("body").on("click","#magic-im-stop",e=>{e.preventDefault(),this.importXHR&&this.importXHR.abort()}),this.$importTestModeCheckbox.on("change",()=>{e("#magic-im-download-media").prop("disabled",this.$importTestModeCheckbox.is(":checked"))}),this.$importForm.on("submit",e=>{e.preventDefault(),this.$importProgress.html(""),this.$importNotices.html(""),this.toggleImportFormState(),this.sendImportAjax()}),e("body").on("select2:unselecting","select",t=>{let s=e(t.params.args.data.element);s.hasClass("mandatory")?t.preventDefault():this.select2IsUnselecting=!0}),e("body").on("select2:opening","select",e=>{if(this.select2IsUnselecting)return this.select2IsUnselecting=!1,!1}),e("body").on("change",this.exportKeysSelect,t=>{this.$selectAllKeysCheckbox.prop("checked",0==e(this.exportKeysSelect).find("option:not(:selected)").length),this.$selectGroupKeysCheckboxes.filter(":not(:disabled)").each((t,s)=>{let i=e(s),o=i.val();i.prop("checked",0==e(this.exportKeysSelect).find(`option[data-group="${o}"]:not(:selected)`).length)})}),this.$selectGroupKeysCheckboxes.on("change",t=>{let s=e(t.target),i=s.val();e(this.exportKeysSelect).find(`option[data-group=${i}]:not(.mandatory)`).prop("selected",s.is(":checked")),e(this.exportKeysSelect).trigger("change")}),this.$selectAllKeysCheckbox.on("change",t=>{let s=this.$selectAllKeysCheckbox.is(":checked");e(this.exportKeysSelect).find("option:not(.mandatory)").prop("selected",s),this.$selectGroupKeysCheckboxes.filter(":not(:disabled)").prop("checked",s),e(this.exportKeysSelect).trigger("change")}),this.$exFiltersTogglerCheckbox.on("change",()=>{e("#magic-ex-advanced-wrapper").toggleClass("js-active"),this.exAppliedFiltersData?this.refreshExportKeys():this.toggleExportFormState()})},sendImportAjax:function(t={}){let s=new FormData(this.$importForm[0]);s.append("action",`process_import_${WPLocalize.magic_type}_form`),s.append("progress",JSON.stringify(t)),this.importXHR=e.ajax({url:WPLocalize.admin_ajax_url,type:"POST",processData:!1,contentType:!1,data:s,success:e=>{if(e=JSON.parse(e),this.$importProgress.html(e.progress_html),this.$importNotices.append(e.notices_html),e.error)return alert(e.error),void this.toggleImportFormState();e.finish?this.toggleImportFormState():this.sendImportAjax(e)},error:(e,t,s)=>{if(this.toggleImportFormState(),"error"===t&&0===e.status){let e="File upload failed. Possible file change during upload.";alert(e)}else"abort"!==t&&alert(`${e.status} ${t} ${s}`)}})},initExportKeysSelects:function(){e(this.exportKeysSelect).select2(this.select2ExportKeysParams(e(this.exportKeysSelect))),e(this.exportMediaKeysSelect).select2(this.select2ExportKeysParams(e(this.exportMediaKeysSelect)))},refreshExportKeys:function(){let t=new FormData(this.$exportForm[0]),s=`refresh_export_${WPLocalize.magic_type}_keys`;t.delete(e(this.exportKeysSelect).attr("name")),t.delete(e(this.exportMediaKeysSelect).attr("name")),t.append("action",s),this.$exportForm.addClass("js-refreshing"),e.ajax({url:WPLocalize.admin_ajax_url,type:"POST",processData:!1,contentType:!1,data:t,success:t=>{t=JSON.parse(t);let s=t.export_keys_html,i=t.export_media_keys_html,o=t.export_items_cnt;this.$exportItemsCnt.text(o).addClass("flash").one("animationend",function(){e(this).removeClass("flash")}),e(this.exportKeysSelect).select2("destroy").replaceWith(s),this.$selectAllKeysCheckbox.prop("checked",!0),this.$selectGroupKeysCheckboxes.each((t,s)=>{let i=e(s),o=i.val(),r=0==e(this.exportKeysSelect).find(`option[data-group="${o}"]`).length;i.prop("checked",!r),i.prop("disabled",r)}),e(this.exportMediaKeysSelect).select2("destroy").replaceWith(i),this.setAppliedFiltersData(),this.initExportKeysSelects()},error:(e,t,s)=>{alert(`${e.status} ${t} ${s}`)},complete:()=>{this.$exportForm.removeClass("js-refreshing")}})},select2MagicItemTemplate:function(e){let t=this.$magicItemSelect.find(`option[value="${e.id}"]`),s=t.text(),i=t.attr("value");return e.text=`${s} <code>${i}</code>`,e.text},select2ExportKeysTemplate:function(t,s,i){let o=i.find(`option[value="${t.id}"]`),r=o.attr("data-group"),a=o.attr("data-key");return o.hasClass("mandatory")&&e(s).addClass("mandatory"),r&&a&&(t.text=`${a} <code>${r}</code>`),t.text},select2ExportKeysParams:function(e){let t={closeOnSelect:!1,width:"100%",escapeMarkup:e=>e,templateSelection:(t,s)=>this.select2ExportKeysTemplate(t,s,e),templateResult:(t,s)=>this.select2ExportKeysTemplate(t,s,e)};return t},toggleExportFormState:function(){let e=parseInt(this.$exportItemsCnt.text());this.$exportForm.find("p.submit input").prop("disabled",e<1||this.$exApplyFiltersBtn.is(":visible:not(:disabled)"))},toggleImportFormState:function(){this.$importForm.toggleClass("js-processing");let t=this.$importForm.hasClass("js-processing");t||e("#magic-im-stop").remove(),this.$importForm.find("p.submit input").prop("disabled",t)},setAppliedFiltersData:function(){this.exAppliedFiltersData=this.getFiltersData(),this.setApplyFiltersBtn()},setApplyFiltersBtn:function(){let t=this.$exApplyFiltersBtn.prop("disabled"),s=this.exAppliedFiltersData==this.getFiltersData();this.$exApplyFiltersBtn.prop("disabled",s),t&&!s&&this.$exApplyFiltersBtn.addClass("flash").one("animationend",function(){e(this).removeClass("flash")}),this.toggleExportFormState()},getFiltersData:function(){return this.$exFilters.find(':input:not([name^="magic-ex-by-"])').serialize()}};e(document).ready(function(){t.init()})})(jQuery);
     1(function(e){var t={$magicItemSelect:e("#magic-ex-im-post-type-select"),$exportItemsSelect:e("#magic-ex-items-select"),exportKeysSelect:"#magic-ex-keys-select",exportMediaKeysSelect:"#magic-ex-media-keys-select",$selectGroupKeysCheckboxes:e(".magic-ex-group"),$selectAllKeysCheckbox:e("#magic-ex-all-keys-checkbox"),$exportForm:e("#magic-ex-form"),$exportItemsCnt:e("#magic-ex-items-cnt"),$exFiltersTogglerCheckbox:e("#magic-ex-advanced-checkbox"),$exFilters:e("#magic-ex-advanced-filters"),$exApplyFiltersBtn:e("#magic-ex-apply-filters"),exAppliedFiltersData:"",$importTestModeCheckbox:e("#magic-im-test-mode"),$importForm:e("#magic-im-form"),$importProgress:e("#magic-im-progress"),$importNotices:e("#magic-im-notices"),importXHR:null,select2IsUnselecting:!1,timer:0,init:function(){this.$exFilters.text().trim()||this.$exFiltersTogglerCheckbox.closest(".form-field").remove();let t=e("#magic-ex-generated-file");t.length&&setTimeout(()=>{let e=t.attr("href"),s=document.createElement("a");s.href=e,s.download="",document.body.appendChild(s),s.click(),document.body.removeChild(s)},500),this.$magicItemSelect.select2({escapeMarkup:e=>e,templateResult:e=>this.select2MagicItemTemplate(e),templateSelection:e=>this.select2MagicItemTemplate(e)}),this.$exportItemsSelect.select2({placeholder:"Select items to export",minimumInputLength:0,closeOnSelect:!1,width:"100%",escapeMarkup:e=>e,templateSelection:(e,t)=>e.text.split("<code>")[0],ajax:{type:"POST",url:WPLocalize.admin_ajax_url,delay:400,dataType:"json",data:function(e){return e.action=`get_individual_export_${WPLocalize.magic_type}`,e.magic_item=WPLocalize.magic_item,e}}}),e(".magic-ex-advanced-filter-type").next("div").find(":input").prop("disabled",!0),this.initExportKeysSelects(),this.events()},events:function(){e('[name="magic-ex-by-taxonomy"]').on("change",function(){e(".magic-ex-taxes-wrapper").toggle()}),this.$exFilters.on("input",":input",t=>{let s=e(t.target);if(s.is('[name^="magic-ex-by-"]')){let e=s.is(":checked"),t=s.closest(".magic-ex-advanced-filter-type").next("div").find(":input:not(.disabled)");t.prop("disabled",!e)}this.setApplyFiltersBtn()}),this.$exportItemsSelect.on("change",e=>{this.refreshExportKeys()}),this.$exApplyFiltersBtn.on("click",e=>{e.preventDefault(),this.refreshExportKeys()}),e('[href="#swift-details-wrapper"]').on("click",t=>{e("#swift-details-wrapper").toggle()}),this.$magicItemSelect.on("change",e=>{e.preventDefault(),this.$exportForm.add(this.$importForm).addClass("js-processing");let t=new URL(window.location);t.searchParams.set("magic_item",this.$magicItemSelect.val()),window.history.pushState({},null,t.toString()),window.location.reload()}),this.$exportForm.on("submit",()=>{this.$exportForm.addClass("js-processing")}),e("body").on("click","#magic-im-stop",e=>{e.preventDefault(),this.importXHR&&this.importXHR.abort()}),this.$importTestModeCheckbox.on("change",()=>{e("#magic-im-download-media").prop("disabled",this.$importTestModeCheckbox.is(":checked"))}),this.$importForm.on("submit",e=>{e.preventDefault(),this.$importProgress.html(""),this.$importNotices.html(""),this.toggleImportFormState(),this.sendImportAjax()}),e("body").on("select2:unselecting","select",t=>{let s=e(t.params.args.data.element);s.hasClass("mandatory")?t.preventDefault():this.select2IsUnselecting=!0}),e("body").on("select2:opening","select",e=>{if(this.select2IsUnselecting)return this.select2IsUnselecting=!1,!1}),e("body").on("change",this.exportKeysSelect,t=>{this.$selectAllKeysCheckbox.prop("checked",0==e(this.exportKeysSelect).find("option:not(:selected)").length),this.$selectGroupKeysCheckboxes.filter(":not(:disabled)").each((t,s)=>{let i=e(s),o=i.val();i.prop("checked",0==e(this.exportKeysSelect).find(`option[data-group="${o}"]:not(:selected)`).length)})}),this.$selectGroupKeysCheckboxes.on("change",t=>{let s=e(t.target),i=s.val();e(this.exportKeysSelect).find(`option[data-group=${i}]:not(.mandatory)`).prop("selected",s.is(":checked")),e(this.exportKeysSelect).trigger("change")}),this.$selectAllKeysCheckbox.on("change",t=>{let s=this.$selectAllKeysCheckbox.is(":checked");e(this.exportKeysSelect).find("option:not(.mandatory)").prop("selected",s),this.$selectGroupKeysCheckboxes.filter(":not(:disabled)").prop("checked",s),e(this.exportKeysSelect).trigger("change")}),this.$exFiltersTogglerCheckbox.on("change",()=>{e("#magic-ex-advanced-wrapper").toggleClass("js-active"),this.exAppliedFiltersData?this.refreshExportKeys():this.toggleExportFormState()})},sendImportAjax:function(t={}){let s=new FormData(this.$importForm[0]);s.append("action",`process_import_${WPLocalize.magic_type}_form`),s.append("progress",JSON.stringify(t)),this.importXHR=e.ajax({url:WPLocalize.admin_ajax_url,type:"POST",processData:!1,contentType:!1,data:s,success:e=>{if(e=JSON.parse(e),e.progress_html&&this.$importProgress.html(e.progress_html),this.$importNotices.append(e.notices_html),e.error)return alert(e.error),void this.toggleImportFormState();e.finish?this.toggleImportFormState():this.sendImportAjax(e)},error:(e,t,s)=>{if(this.toggleImportFormState(),"error"===t&&0===e.status){let e="File upload failed. Possible file change during upload.";alert(e)}else"abort"!==t&&alert(`${e.status} ${t} ${s}`)}})},initExportKeysSelects:function(){e(this.exportKeysSelect).select2(this.select2ExportKeysParams(e(this.exportKeysSelect))),e(this.exportMediaKeysSelect).select2(this.select2ExportKeysParams(e(this.exportMediaKeysSelect)))},refreshExportKeys:function(){let t=new FormData(this.$exportForm[0]),s=`refresh_export_${WPLocalize.magic_type}_keys`;t.delete(e(this.exportKeysSelect).attr("name")),t.delete(e(this.exportMediaKeysSelect).attr("name")),t.append("action",s),this.$exportForm.addClass("js-refreshing"),e.ajax({url:WPLocalize.admin_ajax_url,type:"POST",processData:!1,contentType:!1,data:t,success:t=>{t=JSON.parse(t);let s=t.export_keys_html,i=t.export_media_keys_html,o=t.export_items_cnt;this.$exportItemsCnt.text(o).addClass("flash").one("animationend",function(){e(this).removeClass("flash")}),e(this.exportKeysSelect).select2("destroy").replaceWith(s),this.$selectAllKeysCheckbox.prop("checked",!0),this.$selectGroupKeysCheckboxes.each((t,s)=>{let i=e(s),o=i.val(),r=0==e(this.exportKeysSelect).find(`option[data-group="${o}"]`).length;i.prop("checked",!r),i.prop("disabled",r)}),e(this.exportMediaKeysSelect).select2("destroy").replaceWith(i),this.setAppliedFiltersData(),this.initExportKeysSelects()},error:(e,t,s)=>{alert(`${e.status} ${t} ${s}`)},complete:()=>{this.$exportForm.removeClass("js-refreshing")}})},select2MagicItemTemplate:function(e){let t=this.$magicItemSelect.find(`option[value="${e.id}"]`),s=t.text(),i=t.attr("value");return e.text=`${s} <code>${i}</code>`,e.text},select2ExportKeysTemplate:function(t,s,i){let o=i.find(`option[value="${t.id}"]`),r=o.attr("data-group"),a=o.attr("data-key");return o.hasClass("mandatory")&&e(s).addClass("mandatory"),r&&a&&(t.text=`${a} <code>${r}</code>`),t.text},select2ExportKeysParams:function(e){let t={closeOnSelect:!1,width:"100%",escapeMarkup:e=>e,templateSelection:(t,s)=>this.select2ExportKeysTemplate(t,s,e),templateResult:(t,s)=>this.select2ExportKeysTemplate(t,s,e)};return t},toggleExportFormState:function(){let e=parseInt(this.$exportItemsCnt.text());this.$exportForm.find("p.submit input").prop("disabled",e<1||this.$exApplyFiltersBtn.is(":visible:not(:disabled)"))},toggleImportFormState:function(){this.$importForm.toggleClass("js-processing");let t=this.$importForm.hasClass("js-processing");t||e("#magic-im-stop").remove(),this.$importForm.find("p.submit input").prop("disabled",t)},setAppliedFiltersData:function(){this.exAppliedFiltersData=this.getFiltersData(),this.setApplyFiltersBtn()},setApplyFiltersBtn:function(){let t=this.$exApplyFiltersBtn.prop("disabled"),s=this.exAppliedFiltersData==this.getFiltersData();this.$exApplyFiltersBtn.prop("disabled",s),t&&!s&&this.$exApplyFiltersBtn.addClass("flash").one("animationend",function(){e(this).removeClass("flash")}),this.toggleExportFormState()},getFiltersData:function(){return this.$exFilters.find(':input:not([name^="magic-ex-by-"])').serialize()}};e(document).ready(function(){t.init()})})(jQuery);
  • magic-export-import/trunk/assets/magic-export-import.scss

    r3220340 r3310897  
    219219            }
    220220        }
     221        code:empty {
     222            display: none;
     223        }
    221224    }
    222225    &-results__option[aria-selected='true']:not(
  • magic-export-import/trunk/class-magic-ex-im-setup.php

    r3307536 r3310897  
    22/**
    33 * Plugin Name: Magic Export & Import
    4  * Description: The ultimate tool to migrate any content including posts, terms, users, comments, WooCommerce shop orders and ACF Options pages.
    5  * Version: 1.0.4
     4 * Description: The ultimate tool to migrate any content including posts, terms, users, comments, WooCommerce shop orders, menus and ACF Options pages.
     5 * Version: 1.1.0
    66 * Requires at least: 6.2
    77 * Requires PHP: 7.4
     
    7171        include_once MAGIC_EX_IM_ABSPATH . 'includes/magic-types/class-magic-ex-im-type-shop-orders.php';
    7272        include_once MAGIC_EX_IM_ABSPATH . 'includes/magic-types/class-magic-ex-im-type-acf-options-pages.php';
     73        include_once MAGIC_EX_IM_ABSPATH . 'includes/magic-types/class-magic-ex-im-type-menus.php';
    7374
    7475        // Plugin adapters.
  • magic-export-import/trunk/includes/class-magic-ex-im-data.php

    r3220340 r3310897  
    7777
    7878    /**
     79     * Deferred data.
     80     *
     81     * @var array
     82     */
     83    private static $deferred_data = array();
     84
     85    /**
    7986     * Import notices.
    8087     *
     
    155162            self::$is_importing_item_new = $is_new_item;
    156163        }
     164
     165        self::set_import_deferred_data();
    157166    }
    158167
     
    249258        return array_filter( self::$import_notices );
    250259    }
     260
     261    /**
     262     * Adds deferred data.
     263     *
     264     * @param string $group Group name for the deferred data.
     265     * @param string $key Key for the deferred data.
     266     * @param mixed  $value Value for the deferred data.
     267     */
     268    public static function add_import_deferred_data( $group, $key, $value ) {
     269
     270        // Bail if nothing to defer.
     271        if ( ! $value ) {
     272            return;
     273        }
     274
     275        self::$deferred_data[ $group ]       ??= array();
     276        self::$deferred_data[ $group ][ $key ] = $value;
     277        self::set_import_deferred_data();
     278    }
     279
     280    /**
     281     * Sets deferred data.
     282     */
     283    public static function set_import_deferred_data() {
     284        $processing_item_id = self::get_processing_item_id();
     285
     286        if ( ! self::$deferred_data || ! $processing_item_id ) {
     287            return;
     288        }
     289
     290        $deferred_data                          = self::get_import_deferred_data();
     291        $deferred_data[ $processing_item_id ] ??= array();
     292
     293        $deferred_data[ $processing_item_id ] = array_merge(
     294            $deferred_data[ $processing_item_id ],
     295            self::$deferred_data
     296        );
     297
     298        if ( set_transient( 'magic_im_deferred_data', $deferred_data, 12 * HOUR_IN_SECONDS ) ) {
     299            self::$deferred_data = array(); // Reset collected deferred data after saving to transient.
     300        }
     301    }
     302
     303    /**
     304     * Gets deferred data.
     305     */
     306    public static function get_import_deferred_data() {
     307        $deferred_data = get_transient( 'magic_im_deferred_data' );
     308        return is_array( $deferred_data ) ? $deferred_data : array();
     309    }
     310
     311    /**
     312     * Resets deferred data.
     313     */
     314    public static function reset_import_deferred_data() {
     315        delete_transient( 'magic_im_deferred_data' );
     316    }
    251317}
  • magic-export-import/trunk/includes/class-magic-ex-im-type.php

    r3220340 r3310897  
    681681                    'old-domain'     => wp_parse_url( home_url() )['host'],
    682682                    'plugin-version' => $this->get_plugin_version(),
     683                    'is-ssl'         => is_ssl() ? 1 : 0,
     684                    'uploads-path'   => wp_parse_url( wp_upload_dir()['baseurl'] ?? '', PHP_URL_PATH ),
    683685                );
    684686
    685                 $params = apply_filters( 'magic_ex_file_params', $params );
    686 
     687                $params             = apply_filters( 'magic_ex_file_params', $params );
    687688                $item_export_data[] = $params;
    688689            } else {
     
    744745            $this->import_progress = array_merge(
    745746                array(
    746                     'first_entry'      => true,
    747                     'user_params'      => array(),
    748                     'file_params'      => array(),
    749                     'finish'           => false,
    750                     'total'            => 0,
    751                     'processed'        => 0,
    752                     'updated'          => 0,
    753                     'created'          => 0,
    754                     'skipped'          => 0,
    755                     'media_downloaded' => 0,
    756                     'error'            => '',
    757                     'progress_html'    => '',
    758                     'notices_html'     => '',
     747                    'first_entry'              => true,
     748                    'user_params'              => array(),
     749                    'file_params'              => array(),
     750                    'file_items_processed'     => false,
     751                    'deferred_items_processed' => false,
     752                    'finish'                   => false,
     753                    'total'                    => 0,
     754                    'processed'                => 0,
     755                    'updated'                  => 0,
     756                    'created'                  => 0,
     757                    'skipped'                  => 0,
     758                    'media_downloaded'         => 0,
     759                    'error'                    => '',
     760                    'progress_html'            => '',
     761                    'notices_html'             => '',
    759762                ),
    760763                $posted_data['progress'] ? json_decode( $posted_data['progress'], true ) : array()
     
    809812            $import_file_keys[0] = preg_replace( "/^$bom/", '', $import_file_keys[0] );
    810813
     814            // Set importing parameters.
     815            Magic_EX_IM_Data::set_user_params( $this->import_progress['user_params'], 'import' );
     816            Magic_EX_IM_Data::set_import_file_params( $this->import_progress['file_params'] );
     817
    811818            // First entry, validate file content and set import parameters.
    812819            if ( $this->import_progress['first_entry'] ) {
    813820
     821                Magic_EX_IM_Data::reset_import_deferred_data();
    814822                $this->import_progress['total'] = count( $import_file_content );
    815823
     
    879887                $this->import_progress['first_entry'] = false;
    880888
    881                 // Not a first entry, process import.
    882             } else {
    883 
    884                 Magic_EX_IM_Data::set_user_params( $this->import_progress['user_params'], 'import' );
    885                 Magic_EX_IM_Data::set_import_file_params( $this->import_progress['file_params'] );
     889                // Process file items.
     890            } elseif ( ! $this->import_progress['file_items_processed'] ) {
    886891
    887892                // Determine next slice to process.
     
    895900
    896901                // Determine if the last slice is being processed.
    897                 $finish_cnt                      = $this->import_progress['processed'] + count( $next_slice );
    898                 $this->import_progress['finish'] = count( $import_file_content ) <= $finish_cnt;
     902                $finish_cnt                                    = $this->import_progress['processed'] + count( $next_slice );
     903                $this->import_progress['file_items_processed'] = count( $import_file_content ) <= $finish_cnt;
    899904
    900905                // Set import items data.
     
    908913
    909914                $this->import_items( $import_items_data );
     915
     916                // Process deferred items data.
     917            } elseif ( ! $this->import_progress['deferred_items_processed'] ) {
     918                $deferred_items = Magic_EX_IM_Data::get_import_deferred_data();
     919
     920                foreach ( $deferred_items as $item_id => $item_data ) {
     921                    $this->import_item( $item_data, $item_id );
     922                }
     923
     924                $this->import_progress['deferred_items_processed'] = true;
    910925            }
    911926        } catch ( Exception $e ) {
     
    915930        }
    916931
    917         // Set import progress HTML.
    918         ob_start();
    919         load_template( MAGIC_EX_IM_ABSPATH . 'template-parts/import-progress.php', false, $this->import_progress );
    920         $this->import_progress['progress_html'] = ob_get_clean();
    921 
    922         $import_notices = array_unique( Magic_EX_IM_Data::get_import_notices() );
    923         $notices_html   = '';
    924 
    925         // Set import notices HTML.
    926         foreach ( $import_notices as $notice ) {
    927             $notices_html .= sprintf( '<li>%s</li>', $notice );
    928         }
    929 
    930         $this->import_progress['notices_html'] = $notices_html;
     932        if ( ! $this->import_progress['deferred_items_processed'] ) {
     933
     934            // Set import notices HTML.
     935            $notices_html   = '';
     936            $import_notices = array_unique( Magic_EX_IM_Data::get_import_notices() );
     937
     938            foreach ( $import_notices as $notice ) {
     939                $notices_html .= sprintf( '<li>%s</li>', $notice );
     940            }
     941            $this->import_progress['notices_html'] = $notices_html;
     942
     943            // Set import progress HTML.
     944            ob_start();
     945            load_template( MAGIC_EX_IM_ABSPATH . 'template-parts/import-progress.php', false, $this->import_progress );
     946            $this->import_progress['progress_html'] = ob_get_clean();
     947
     948        } else {
     949            $this->import_progress['notices_html']  = '';
     950            $this->import_progress['progress_html'] = '';
     951        }
     952
     953        $this->import_progress['finish'] = $this->import_progress['file_items_processed']
     954            && $this->import_progress['deferred_items_processed'];
    931955
    932956        echo wp_json_encode( $this->import_progress );
     
    10101034            }
    10111035
    1012             $item_data = apply_filters( 'magic_im_item_data_before_adjust', $item_data );
    1013 
    1014             // Adjust importing data before creating or updating the item.
    1015             foreach ( $item_data as $group => $data ) {
    1016                 foreach ( $data as $key => $key_data ) {
    1017                     $item_data[ $group ][ $key ] = $this->adjust_value( $key_data, $key, $group );
    1018                 }
    1019             }
    1020 
    1021             $existing_item_id = $this->get_existing_item( $item_data );
    1022 
    1023             // Item exists.
    1024             if ( $existing_item_id ) {
    1025 
    1026                 $is_new_item = false;
    1027 
    1028                 // Skip updating existing items in the test mode.
    1029                 if ( magic_ex_im_get_user_params( 'test-mode' ) ) {
    1030 
    1031                     ++$this->import_progress['processed'];
    1032 
    1033                     magic_ex_im_get_user_params( 'skip-existing-items' )
    1034                         ? ++$this->import_progress['skipped']
    1035                         : ++$this->import_progress['updated'];
    1036 
    1037                     $notice = sprintf( 'Already exists (id: %s).', $existing_item_id );
    1038 
    1039                     magic_im_add_notice(
    1040                         $notice,
    1041                         magic_ex_im_get_user_params( 'skip-existing-items' ) ? 'skipped' : 'updated'
    1042                     );
    1043 
    1044                     continue;
    1045                 }
    1046 
    1047                 // Skip item if user hasn't allowed to update existing items.
    1048                 if ( magic_ex_im_get_user_params( 'skip-existing-items' ) ) {
    1049 
    1050                     ++$this->import_progress['skipped'];
    1051                     ++$this->import_progress['processed'];
    1052 
    1053                     $notice = sprintf( 'Already exists (id: %d).', $existing_item_id );
    1054                     magic_im_add_notice( $notice, 'skipped' );
    1055                     continue;
    1056                 }
    1057 
    1058                 $item_id = $this->update_existing_item( $item_data, $existing_item_id );
    1059 
    1060                 // Item doesn't exist.
    1061             } else {
    1062 
    1063                 $is_new_item = true;
    1064 
    1065                 // Skip creating new items in the test mode.
    1066                 if ( magic_ex_im_get_user_params( 'test-mode' ) ) {
    1067 
    1068                     ++$this->import_progress['processed'];
    1069 
    1070                     magic_ex_im_get_user_params( 'skip-new-items' )
    1071                         ? ++$this->import_progress['skipped']
    1072                         : ++$this->import_progress['created'];
    1073 
    1074                     magic_im_add_notice(
    1075                         'Doesn\'t exist.',
    1076                         magic_ex_im_get_user_params( 'skip-new-items' ) ? 'skipped' : 'created'
    1077                     );
    1078 
    1079                     continue;
    1080                 }
    1081 
    1082                 // Skip item if user hasn't allowed to create new items.
    1083                 if ( magic_ex_im_get_user_params( 'skip-new-items' ) ) {
    1084 
    1085                     ++$this->import_progress['skipped'];
    1086                     ++$this->import_progress['processed'];
    1087 
    1088                     $notice = sprintf( 'Doesn\'t exist.' );
    1089                     magic_im_add_notice( $notice, 'skipped' );
    1090                     continue;
    1091                 }
    1092 
    1093                 $item_id = $this->create_new_item( $item_data );
    1094             }
    1095 
    1096             // Check for an error on updating or creating the item.
    1097             if ( ! $item_id || is_wp_error( $item_id ) ) {
    1098 
    1099                 $notice = sprintf(
    1100                     'Error to %s. %s',
    1101                     $is_new_item ? 'create' : 'update',
    1102                     is_wp_error( $item_id ) ? ( '<code>' . $item_id->get_error_message() . '</code>' ) : '',
     1036            $this->import_item( $item_data );
     1037        }
     1038    }
     1039
     1040    /**
     1041     * Imports an item based on the provided data.
     1042     *
     1043     * @param array $item_data Item data to import.
     1044     * @param int   $existing_item_id Existing item ID to update, if any.
     1045     */
     1046    private function import_item( $item_data, $existing_item_id = 0 ) {
     1047
     1048        $item_data = apply_filters( 'magic_im_item_data_before_adjust', $item_data );
     1049
     1050        // Adjust importing data before creating or updating the item.
     1051        foreach ( $item_data as $group => $data ) {
     1052            foreach ( $data as $key => $key_data ) {
     1053                $item_data[ $group ][ $key ] = $this->adjust_value( $key_data, $key, $group );
     1054            }
     1055        }
     1056
     1057        $existing_item_id = $existing_item_id ? $existing_item_id : $this->get_existing_item( $item_data );
     1058
     1059        // Item exists.
     1060        if ( $existing_item_id ) {
     1061            $is_new_item = false;
     1062
     1063            // Skip updating existing items in the test mode.
     1064            if ( magic_ex_im_get_user_params( 'test-mode' ) ) {
     1065
     1066                ++$this->import_progress['processed'];
     1067
     1068                magic_ex_im_get_user_params( 'skip-existing-items' )
     1069                    ? ++$this->import_progress['skipped']
     1070                    : ++$this->import_progress['updated'];
     1071
     1072                $notice = sprintf( 'Already exists (id: %s).', $existing_item_id );
     1073
     1074                magic_im_add_notice(
     1075                    $notice,
     1076                    magic_ex_im_get_user_params( 'skip-existing-items' ) ? 'skipped' : 'updated'
    11031077                );
    11041078
    1105                 magic_im_add_notice( $notice, 'skipped' );
     1079                return;
     1080            }
     1081
     1082            // Skip item if user don't allow to update existing items and not deferred items are processed.
     1083            if ( magic_ex_im_get_user_params( 'skip-existing-items' ) && ! $this->import_result['file_items_processed'] ) {
    11061084
    11071085                ++$this->import_progress['skipped'];
    11081086                ++$this->import_progress['processed'];
    1109                 continue;
    1110             }
    1111 
    1112             Magic_EX_IM_Data::set_processing_item( $item_id, $is_new_item );
    1113             do_action( 'magic_im_item_id', $item_data );
    1114 
    1115             // Adjust importing data after item ID was determined.
    1116             foreach ( $item_data as $group => $data ) {
    1117                 foreach ( $data as $key => $key_data ) {
    1118                     $item_data[ $group ][ $key ] = magic_im_maybe_adjust_value( $key_data, $key, $group, );
    1119                 }
    1120             }
    1121 
    1122             // Replace remaining old domain with the current in all strings.
    1123             $item_data = magic_im_replace_domain( $item_data );
    1124 
    1125             // Update importing item.
    1126 
    1127             $item_data = apply_filters( 'magic_im_item_data_before_update', $item_data );
    1128             $this->update_importing_item( $item_data, $item_id );
    1129 
    1130             // Count processed items.
    1131 
    1132             $is_new_item ? $this->import_progress['created']++ : $this->import_progress['updated']++;
     1087
     1088                $notice = sprintf( 'Already exists (id: %d).', $existing_item_id );
     1089                magic_im_add_notice( $notice, 'skipped' );
     1090                return;
     1091            }
     1092
     1093            $item_id = $this->update_existing_item( $item_data, $existing_item_id );
     1094
     1095            // Item doesn't exist.
     1096        } else {
     1097            $is_new_item = true;
     1098
     1099            // Skip creating new items in the test mode.
     1100            if ( magic_ex_im_get_user_params( 'test-mode' ) ) {
     1101
     1102                ++$this->import_progress['processed'];
     1103
     1104                magic_ex_im_get_user_params( 'skip-new-items' )
     1105                    ? ++$this->import_progress['skipped']
     1106                    : ++$this->import_progress['created'];
     1107
     1108                magic_im_add_notice(
     1109                    'Doesn\'t exist.',
     1110                    magic_ex_im_get_user_params( 'skip-new-items' ) ? 'skipped' : 'created'
     1111                );
     1112
     1113                return;
     1114            }
     1115
     1116            // Skip item if user don't allow to create new items.
     1117            if ( magic_ex_im_get_user_params( 'skip-new-items' ) ) {
     1118
     1119                ++$this->import_progress['skipped'];
     1120                ++$this->import_progress['processed'];
     1121
     1122                $notice = sprintf( 'Doesn\'t exist.' );
     1123                magic_im_add_notice( $notice, 'skipped' );
     1124                return;
     1125            }
     1126
     1127            $item_id = $this->create_new_item( $item_data );
     1128        }
     1129
     1130        // Check for an error on updating or creating the item.
     1131        if ( ! $item_id || is_wp_error( $item_id ) ) {
     1132
     1133            $notice = sprintf(
     1134                'Error to %s. %s',
     1135                $is_new_item ? 'create' : 'update',
     1136                is_wp_error( $item_id ) ? ( '<code>' . $item_id->get_error_message() . '</code>' ) : '',
     1137            );
     1138
     1139            magic_im_add_notice( $notice, 'skipped' );
     1140
     1141            ++$this->import_progress['skipped'];
    11331142            ++$this->import_progress['processed'];
    1134 
    1135             do_action( 'magic_im_item_processed', $item_data );
    1136         }
    1137 
     1143            return;
     1144        }
     1145
     1146        Magic_EX_IM_Data::set_processing_item( $item_id, $is_new_item );
     1147        do_action( 'magic_im_item_id', $item_data );
     1148
     1149        // Adjust importing data after item ID was determined.
     1150        foreach ( $item_data as $group => $data ) {
     1151            foreach ( $data as $key => $key_data ) {
     1152                $item_data[ $group ][ $key ] = magic_im_maybe_adjust_value( $key_data, $key, $group, );
     1153            }
     1154        }
     1155
     1156        // Replace remaining old domain with the current in all strings.
     1157        $item_data = magic_im_replace_domain( $item_data );
     1158
     1159        // Update importing item.
     1160        $item_data = apply_filters( 'magic_im_item_data_before_update', $item_data );
     1161        $this->update_importing_item( $item_data, $item_id );
     1162
     1163        // Count processed items.
     1164        $is_new_item ? $this->import_progress['created']++ : $this->import_progress['updated']++;
     1165        ++$this->import_progress['processed'];
     1166
     1167        do_action( 'magic_im_item_processed', $item_data );
    11381168        Magic_EX_IM_Data::reset_processing_item();
    11391169    }
  • magic-export-import/trunk/includes/magic-ex-im-functions.php

    r3307536 r3310897  
    1111
    1212/**
    13  * Gets processing magic type: 'posts', 'terms', 'users', 'comments', 'acf_options_pages', 'shop_orders'.
     13 * Gets processing magic type: 'posts', 'terms', 'users', 'comments', 'acf_options_pages', 'shop_orders', 'menus.
    1414 */
    1515function magic_ex_im_get_magic_type() {
     
    415415    // Check if URL refers to an existing media.
    416416    if ( $current_domain === $media_url_domain ) {
    417 
    418417        $media_file_id = attachment_url_to_postid( $media_url );
    419418
     
    456455    }
    457456
    458     // Media file not found, check whether it should be downloaded.
     457    // Media file not found, check whether it may be downloaded.
    459458    if ( $current_domain === $media_url_domain || ! magic_ex_im_get_user_params( 'download-media' ) || $prevent_download ) {
    460459        return false;
     
    530529        // Update existing attachment.
    531530        if ( $attachment_id ) {
    532 
    533531            update_attached_file( $attachment_id, $upload_path );
    534532            $media_file_id = $attachment_id;
     
    558556
    559557        require_once ABSPATH . 'wp-admin/includes/image.php';
    560 
    561558        $attachment_metadata = wp_generate_attachment_metadata( $media_file_id, $upload_path );
    562559
     
    784781
    785782    // Replace remaining media files URLs into local URLs.
    786     $new_content = preg_replace_callback(
    787         '/https?:\/\/[^\s"\'<>]+/i', // Matches URLs starting with http or https.
    788         function ( $matches ) {
     783
     784    $old_uploads_path = magic_im_get_file_params( 'uploads-path' );
     785    $old_uploads_path = $old_uploads_path ? $old_uploads_path : '/wp-content/uploads';
     786
     787    // Build a regex to match URLs starting with http/https or relative uploads paths.
     788    $old_uploads_path_escaped = preg_quote( $old_uploads_path, '/' );
     789    $new_content              = preg_replace_callback(
     790        '/(?:https?:\/\/[^\s"\'<>]+|' . $old_uploads_path_escaped . '\/[^\s"\'<>]+)/i',
     791        function ( $matches ) use ( $old_uploads_path ) {
    789792            $old_url = $matches[0];
     793
     794            // If it's a relative URL starting with the actual uploads path, prepend with old domain and protocol.
     795            // This is needed to ensure that the URL is absolute and can be processed correctly.
     796            if ( str_starts_with( $old_url, $old_uploads_path ) ) {
     797                $old_domain = magic_im_get_file_params( 'old-domain' );
     798                $protocol   = magic_im_get_file_params( 'is-ssl' ) ? 'https://' : 'http://';
     799                $old_url    = $protocol . $old_domain . $old_url;
     800            }
     801
    790802            return magic_im_media_url_to_local_url( $old_url );
    791803        },
     
    820832        }
    821833    } elseif ( is_string( $data ) ) {
    822 
    823834        $data = str_replace( $old_domain, $current_domain, $data, $replaced_cnt );
    824835
     
    876887 */
    877888function magic_im_term_id_by_slug( $term_slug, $term_tax ) {
    878 
    879889    $term_obj = get_term_by( 'slug', $term_slug, $term_tax );
    880890
     
    885895    return apply_filters( 'magic_im_item_data_taxonomy', $term_obj->term_id, $term_obj->taxonomy );
    886896}
     897
     898/**
     899 * Adds deferred data to process again on import finish to link newly created items.
     900 *
     901 * @param string $group Group name.
     902 * @param string $key Key name.
     903 * @param mixed  $value Value to store.
     904 */
     905function magic_im_add_deferred_data( $group, $key, $value ) {
     906    Magic_EX_IM_Data::add_import_deferred_data( $group, $key, $value );
     907}
  • magic-export-import/trunk/includes/magic-types/class-magic-ex-im-type-acf-options-pages.php

    r3220340 r3310897  
    4646    protected function get_importing_item_name( $item_data ) {
    4747
    48         $page_slug = $item_data[ self::KEY_GROUP ]['page_slug'] ?? '';
    49 
    50         return $page_slug;
    51     }
    52 
    53     /**
    54      * Gets all registered ACF option pages.
    55      *
    56      * @param string $acf_page_slug Particular ACF Options Page slug to get.
     48        $item_name  = '';
     49        $page_title = $item_data[ self::KEY_GROUP ]['page_title'] ?? '';
     50        $page_slug  = $item_data[ self::KEY_GROUP ]['page_slug'] ?? '';
     51
     52        if ( $page_title && $page_slug ) {
     53            $item_name = sprintf( '%s (%s)', $page_title, $page_slug );
     54        } else {
     55            $item_name = $page_slug ? $page_slug : $page_title;
     56        }
     57
     58        return $item_name;
     59    }
     60
     61    /**
     62     * Gets registered ACF option pages or specific ACF option page by slug.
     63     *
     64     * @param string $acf_page_slug Slug to get specific ACF Options Page.
    5765     * @return array
    5866     */
     
    6068
    6169        $acf_options_pages = function_exists( 'acf_get_options_pages' ) ? acf_get_options_pages() : array();
     70
     71        if ( ! is_array( $acf_options_pages ) ) {
     72            $acf_options_pages = array();
     73        }
    6274
    6375        return $acf_page_slug ? ( $acf_options_pages[ $acf_page_slug ] ?? array() ) : $acf_options_pages;
  • magic-export-import/trunk/includes/magic-types/class-magic-ex-im-type-posts.php

    r3307536 r3310897  
    9696    protected function get_importing_item_name( $item_data ) {
    9797
     98        $item_name  = '';
    9899        $post_name  = $item_data['post']['post_name'] ?? '';
    99100        $post_title = $item_data['post']['post_title'] ?? '';
    100101
    101         return $post_name ? $post_name : $post_title;
     102        if ( $post_title && $post_name ) {
     103            $item_name = sprintf( '%s (%s)', $post_title, $post_name );
     104        } else {
     105            $item_name = $post_name ? $post_name : $post_title;
     106        }
     107
     108        return $item_name;
    102109    }
    103110
     
    320327                    // Convert post parent name into ID.
    321328                    case 'post_parent':
    322                         $value = magic_im_post_id_by_name( $value, $this->magic_item );
     329                        $parent_post_id = magic_im_post_id_by_name( $value, $this->magic_item );
     330                        if ( ! $parent_post_id ) {
     331                            magic_im_add_deferred_data( $key_group, $key, $value );
     332                        }
     333                        $value = $parent_post_id;
    323334                        break;
    324335
  • magic-export-import/trunk/includes/magic-types/class-magic-ex-im-type-terms.php

    r3307536 r3310897  
    5252    protected function get_importing_item_name( $item_data ) {
    5353
     54        $item_name = '';
     55        $term_name = $item_data['term']['name'] ?? '';
    5456        $term_slug = $item_data['term']['slug'] ?? '';
    5557
    56         return $term_slug;
     58        if ( $term_name && $term_slug ) {
     59            $item_name = sprintf( '%s (%s)', $term_name, $term_slug );
     60        } else {
     61            $item_name = $term_slug ? $term_slug : $term_name;
     62        }
     63
     64        return $item_name;
    5765    }
    5866
     
    141149
    142150            // Convert term slug into ID.
    143             if ( 'term' === $key_group && 'parent' === $key && $value ) {
    144                 $value = magic_im_term_id_by_slug( $value, $this->magic_item );
     151            if ( 'term' === $key_group && 'parent' === $key ) {
     152                $parent_term_id = magic_im_term_id_by_slug( $value, $this->magic_item );
     153                if ( ! $parent_term_id ) {
     154                    magic_im_add_deferred_data( $key_group, $key, $value );
     155                }
     156                $value = $parent_term_id;
    145157            }
    146158        }
     
    199211        );
    200212
    201         // Exclude some term keys from the export.
     213        // Exclude specific term keys from the export.
    202214        $term_keys = array_diff( $term_keys, array( 'term_id', 'term_taxonomy_id', 'count' ) );
    203215
  • magic-export-import/trunk/includes/magic-types/class-magic-ex-im-type-users.php

    r3220340 r3310897  
    294294    protected function validate_importing_item_data( $item_data ) {
    295295
    296         $errors = array();
    297 
     296        $errors    = array();
    298297        $email_key = magic_ex_im_build_key( 'user_email', $this->get_key_group( 'user_email' ) );
    299298
  • magic-export-import/trunk/includes/plugin-adapters/class-magic-ex-im-adapter-acf.php

    r3307536 r3310897  
    1515class Magic_EX_IM_Adapter_ACF {
    1616
    17     const KEYS_GROUP = 'ACF';
     17    const KEYS_GROUP           = 'ACF';
     18    const KEYS_GROUP_MENU_ITEM = 'menu_item_ACF';
     19
     20    /**
     21     * Holds updated menu items.
     22     *
     23     * @var array
     24     */
     25    private $updated_menu_items = array();
    1826
    1927    /**
     
    4654        add_filter( 'magic_im_adjust_editor_value_gutenberg', array( $this, 'adjust_editor_value_gutenberg' ), 10, 2 );
    4755        add_filter( 'magic_im_maybe_adjust_value', array( $this, 'adjust_acf_value' ), 10, 3 );
    48         add_filter( 'magic_im_item_data_before_update', array( $this, 'magic_im_item_data_before_update' ) );
     56        add_filter( 'magic_im_item_processed', array( $this, 'magic_im_item_processed' ) );
    4957        remove_filter( 'content_save_pre', 'acf_parse_save_blocks', 5 );
     58        add_action( 'magic_im_menu_item_updated', array( $this, 'magic_im_menu_item_updated' ), 10, 2 );
     59    }
     60
     61    /**
     62     * Processes action hook 'magic_im_menu_item_updated'.
     63     *
     64     * @param int $new_menu_item_id New menu item ID.
     65     * @param int $old_menu_item_id Old menu item ID.
     66     *
     67     * @see Magic_EX_IM_Type_Menus::update_importing_item()
     68     */
     69    public function magic_im_menu_item_updated( $new_menu_item_id, $old_menu_item_id ) {
     70        // Store updated menu item ID.
     71        $this->updated_menu_items[ $old_menu_item_id ] = $new_menu_item_id;
    5072    }
    5173
     
    129151                    $acf_field_object = get_field_object( $acf_key );
    130152
    131                     $acf_value = $this->adjust_acf_value( $acf_value, $acf_name, self::KEYS_GROUP, $acf_field_object );
     153                    $adjusted_acf_value = $this->adjust_acf_value( $acf_value, $acf_name, self::KEYS_GROUP, $acf_field_object );
     154                    $acf_value          = is_array( $adjusted_acf_value ) ? array_filter( $adjusted_acf_value ) : $adjusted_acf_value;
    132155                }
    133156            }
     
    159182    public function magic_ex_keys( $export_keys ) {
    160183
    161         if ( ! isset( $export_keys['meta'] ) ) {
     184        if ( ! isset( $export_keys['meta'] ) && ! isset( $export_keys['menu_item_meta'] ) ) {
    162185            return $export_keys;
    163186        }
     
    165188        $meta_keys_to_exclude = array();
    166189
    167         // Exclude some meta keys for users.
    168 
     190        // Exclude specific meta keys for users.
    169191        if ( 'users' === magic_ex_im_get_magic_type() ) {
    170192            $meta_keys_to_exclude[] = 'acf_user_settings';
    171193        }
    172194
    173         foreach ( $export_keys['meta'] as $meta_key ) {
     195        foreach ( $export_keys['meta'] ?? array() as $meta_key ) {
    174196
    175197            if ( 'users' === magic_ex_im_get_magic_type()
    176                 && ( str_starts_with( $meta_key, 'manageedit-acf-' ) || str_ends_with( $meta_key, '_acf-field-group' ) ) ) {
     198                && ( str_starts_with( $meta_key, 'manageedit-acf-' )
     199                || str_ends_with( $meta_key, '_acf-field-group' ) ) ) {
    177200
    178201                $meta_keys_to_exclude[] = $meta_key;
     
    181204
    182205        // Exclude ACF related keys like '_meta_key', 'meta_key_subfield', 'meta_key_0_subfield' 'meta_key_group_subfield' etc.
    183         foreach ( $export_keys[ self::KEYS_GROUP ] ?? array() as $meta_key ) {
    184 
    185             $meta_keys_to_exclude[] = "_$meta_key";
    186 
    187             $regex = sprintf( '/^_?%s_.*/', $meta_key );
    188             array_push( $meta_keys_to_exclude, ...preg_grep( $regex, $export_keys['meta'] ) );
    189         }
    190 
    191         $export_keys['meta'] = array_diff( $export_keys['meta'], $meta_keys_to_exclude );
     206        foreach ( array( self::KEYS_GROUP, self::KEYS_GROUP_MENU_ITEM ) as $group ) {
     207            if ( isset( $export_keys[ $group ] ) && is_array( $export_keys[ $group ] ) ) {
     208
     209                foreach ( $export_keys[ $group ] as $meta_key ) {
     210                    $meta_keys_to_exclude[] = "_$meta_key";
     211                    $regex                  = sprintf( '/^_?%s_.*/', $meta_key );
     212                    array_push( $meta_keys_to_exclude, ...preg_grep( $regex, $export_keys['meta'] ) );
     213                }
     214            }
     215        }
     216
     217        if ( isset( $export_keys['meta'] ) ) {
     218            $export_keys['meta'] = array_diff( $export_keys['meta'], $meta_keys_to_exclude );
     219        }
     220
     221        if ( isset( $export_keys['menu_item_meta'] ) ) {
     222            $export_keys['menu_item_meta'] = array_diff( $export_keys['menu_item_meta'], $meta_keys_to_exclude );
     223        }
    192224
    193225        return $export_keys;
     
    204236    public function magic_ex_get_key_group( $group, $key, $all_export_keys ) {
    205237
    206         if ( 'meta' === $group && $this->get_acf_field_key_by_name( $key ) ) {
    207 
    208             // Check for existence of ACF meta with underscore prefix, where ACF field identifier should be stored.
     238        if ( ( 'meta' === $group || 'menu_item_meta' === $group ) && $this->get_acf_field_key_by_name( $key ) ) {
     239
     240            // Additionally check if ACF field identifier exists.
    209241            if ( in_array( "_$key", $all_export_keys, true ) ) {
    210                 return self::KEYS_GROUP;
     242
     243                switch ( $group ) {
     244                    case 'meta':
     245                        $group = self::KEYS_GROUP;
     246                        break;
     247
     248                    case 'menu_item_meta':
     249                        $group = self::KEYS_GROUP_MENU_ITEM;
     250                        break;
     251                }
    211252            }
    212253        }
     
    225266    public function magic_im_get_key_group( $group, $key, $predefined_group ) {
    226267
     268        // Additional check if predefined group matches.
    227269        if ( 'meta' === $group && $this->get_acf_field_key_by_name( $key ) ) {
    228270
    229             // Additional check if predefined group matches.
    230271            if ( self::KEYS_GROUP === $predefined_group ) {
    231                 return self::KEYS_GROUP;
     272                $group = self::KEYS_GROUP;
     273            }
     274        } elseif ( 'menu_item_meta' === $group && $this->get_acf_field_key_by_name( $key ) ) {
     275
     276            if ( self::KEYS_GROUP_MENU_ITEM === $predefined_group ) {
     277                $group = self::KEYS_GROUP_MENU_ITEM;
    232278            }
    233279        }
     
    246292    public function magic_ex_get_key_data( $key_data, $key, $group ) {
    247293
    248         if ( self::KEYS_GROUP !== $group ) {
    249             return $key_data;
    250         }
    251 
    252         $item_acf_id      = $this->get_item_acf_id( $key );
    253         $acf_field_object = get_field_object( $this->get_acf_field_key_by_name( $key ) );
    254 
    255         if ( isset( $acf_field_object['type'] ) && $item_acf_id ) {
    256 
    257             // Get unformatted ACF field data (as it's stored in the database).
    258             $key_data = get_field( $key, $item_acf_id, false );
    259 
    260             // Rebuild array where each key is ACF field name and value - its unformatted data from the database
    261             // in order to correctly update it with update_field() during import.
    262             if ( $key_data && is_array( $key_data )
    263                 && in_array( $acf_field_object['type'], array( 'group', 'repeater', 'flexible_content' ), true ) ) {
    264 
    265                 $rebuild_arr_fn = function ( $arr_with_needed_keys, $arr_with_needed_values ) use ( &$rebuild_arr_fn ) {
    266                     $values = array_values( $arr_with_needed_values );
    267                     $result = array();
    268                     $i      = 0;
    269 
    270                     foreach ( $arr_with_needed_keys as $key => $value ) {
    271 
    272                         $result[ $key ] = is_array( $value )
    273                             ? $rebuild_arr_fn( $value, $values[ $i++ ] )
    274                             : $values[ $i++ ];
    275                     }
    276 
    277                     return $result;
    278                 };
    279 
    280                 $key_data_formatted = get_field( $key, $item_acf_id, true );
    281                 $key_data           = $rebuild_arr_fn( $key_data_formatted, $key_data );
     294        if ( self::KEYS_GROUP === $group ) {
     295            return $this->get_key_data( $key );
     296
     297        } elseif ( self::KEYS_GROUP_MENU_ITEM === $group ) {
     298            $menu_items = wp_get_nav_menu_items( magic_ex_im_get_item_id() );
     299
     300            if ( $menu_items ) {
     301                foreach ( $menu_items as $menu_item ) {
     302
     303                    if ( metadata_exists( 'post', $menu_item->ID, $key ) ) {
     304                        $key_data                   = is_array( $key_data ) ? $key_data : array();
     305                        $key_data[ $menu_item->ID ] = $this->get_key_data( $key, $menu_item->ID );
     306                    }
     307                }
    282308            }
    283309        }
     
    601627
    602628    /**
    603      * Processes filter hook 'magic_im_item_data_before_update'.
     629     * Processes filter hook 'magic_im_item_processed'.
    604630     *
    605631     * @param array $item_data Importing item data grouped by type.
    606      * @return array
    607      */
    608     public function magic_im_item_data_before_update( $item_data ) {
     632     */
     633    public function magic_im_item_processed( $item_data ) {
    609634
    610635        // Update ACF related data.
    611636        foreach ( $item_data[ self::KEYS_GROUP ] ?? array() as $field_name => $value ) {
    612 
    613             $item_acf_id      = $this->get_item_acf_id( $field_name );
    614             $acf_field_object = get_field_object( $this->get_acf_field_key_by_name( $field_name ) );
    615 
    616             if ( isset( $acf_field_object['type'] ) && $item_acf_id ) {
    617                 update_field( $field_name, $value, $item_acf_id );
    618             }
    619         }
    620 
    621         return $item_data;
     637            $this->update_field( $field_name, $value );
     638        }
     639
     640        // Update menu item ACF related data.
     641        foreach ( $item_data[ self::KEYS_GROUP_MENU_ITEM ] ?? array() as $field_name => $arr ) {
     642            foreach ( (array) $arr as $old_menu_item_id => $value ) {
     643                if ( isset( $this->updated_menu_items[ $old_menu_item_id ] ) ) {
     644                    $new_menu_item_id = $this->updated_menu_items[ $old_menu_item_id ];
     645                    $this->update_field( $field_name, $value, $new_menu_item_id );
     646                }
     647            }
     648        }
    622649    }
    623650
     
    648675            case 'acf_options_pages':
    649676                $item_id = $this->get_acf_options_page_id( $item_id, $field_name );
     677                break;
     678
     679            case 'menus':
     680                $item_id = 'term_' . $item_id;
    650681                break;
    651682        }
     
    692723                }
    693724                break;
     725
     726            case 'menus':
     727                if ( $processing_item_id ) {
     728                    $filter = array(
     729                        'nav_menu_item' => '',
     730                        'nav_menu'      => $processing_item_id,
     731                    );
     732                }
     733                break;
    694734        }
    695735
     
    709749        return $acf_field_key;
    710750    }
     751
     752    /**
     753     * Gets ACF key data by its name.
     754     *
     755     * @param string          $key ACF field name.
     756     * @param string|int|null $item_acf_id ACF item ID.
     757     * @return mixed
     758     */
     759    private function get_key_data( $key, $item_acf_id = null ) {
     760
     761        $item_acf_id      = $item_acf_id ?? $this->get_item_acf_id( $key );
     762        $acf_field_object = get_field_object( $this->get_acf_field_key_by_name( $key ) );
     763
     764        if ( isset( $acf_field_object['type'] ) && $item_acf_id ) {
     765
     766            // Get unformatted ACF field data (as it's stored in the database).
     767            $key_data = get_field( $key, $item_acf_id, false );
     768
     769            // Rebuild array where each key is ACF field name and value - its unformatted data from the database
     770            // in order to correctly update it with update_field() during import.
     771            if ( $key_data && is_array( $key_data )
     772                && in_array( $acf_field_object['type'], array( 'group', 'repeater', 'flexible_content' ), true ) ) {
     773
     774                $rebuild_arr_fn = function ( $arr_with_needed_keys, $arr_with_needed_values ) use ( &$rebuild_arr_fn ) {
     775                    $values = array_values( $arr_with_needed_values );
     776                    $result = array();
     777                    $i      = 0;
     778
     779                    foreach ( $arr_with_needed_keys as $key => $value ) {
     780
     781                        $result[ $key ] = is_array( $value )
     782                            ? $rebuild_arr_fn( $value, $values[ $i++ ] )
     783                            : $values[ $i++ ];
     784                    }
     785
     786                    return $result;
     787                };
     788
     789                $key_data_formatted = get_field( $key, $item_acf_id, true );
     790                $key_data           = $rebuild_arr_fn( $key_data_formatted, $key_data );
     791            }
     792        }
     793
     794        return $key_data;
     795    }
     796
     797    /**
     798     * Updates ACF field value.
     799     *
     800     * @param string          $field_name ACF field name.
     801     * @param mixed           $value Value to update.
     802     * @param string|int|null $item_acf_id ACF item ID.
     803     */
     804    private function update_field( $field_name, $value, $item_acf_id = null ) {
     805
     806        $item_acf_id      = $item_acf_id ?? $this->get_item_acf_id( $field_name );
     807        $acf_field_object = get_field_object( $this->get_acf_field_key_by_name( $field_name ) );
     808
     809        if ( isset( $acf_field_object['type'] ) && $item_acf_id ) {
     810            update_field( $field_name, $value, $item_acf_id );
     811        }
     812    }
    711813}
    712814
  • magic-export-import/trunk/includes/plugin-adapters/class-magic-ex-im-adapter-polylang.php

    r3220340 r3310897  
    2525
    2626    /**
     27     * Polylang nav menu object.
     28     *
     29     * @var PLL_Admin_Nav_Menu
     30     */
     31    private $pll_nav_menu = null;
     32
     33    /**
    2734     * Constructor.
    2835     */
     
    6067        add_filter( 'magic_im_post_id_by_name_args', array( $this, 'magic_im_post_id_by_name_args' ) );
    6168        add_filter( 'magic_im_item_data_taxonomy', array( $this, 'translate_term' ), 10, 2 );
     69        add_filter( 'pre_update_option_theme_mods_' . get_stylesheet(), array( $this, 'pre_update_option_theme_mods' ) );
     70    }
     71
     72    /**
     73     * Gets Polylang nav menu object.
     74     *
     75     * @return PLL_Admin_Nav_Menu|null
     76     */
     77    private function get_pll_nav_menu() {
     78
     79        if ( ! isset( $this->pll_nav_menu ) && class_exists( 'PLL_Admin_Nav_Menu' ) ) {
     80            global $polylang;
     81            $this->pll_nav_menu = new PLL_Admin_Nav_Menu( $polylang );
     82        }
     83
     84        return $this->pll_nav_menu;
    6285    }
    6386
     
    93116
    94117        return '';
     118    }
     119
     120    /**
     121     * Processes action hook 'pre_update_option_theme_mods_' . get_stylesheet().
     122     *
     123     * @param array $mods Theme mods.
     124     *
     125     * @see PLL_Admin_Nav_Menu
     126     */
     127    public function pre_update_option_theme_mods( $mods ) {
     128
     129        // Manually update nav menu locations as Polylang does itself.
     130        if ( 'menus' === magic_ex_im_get_magic_type() && $this->get_pll_nav_menu() ) {
     131            $mods['nav_menu_locations'] = $this->get_pll_nav_menu()->update_nav_menu_locations( $mods['nav_menu_locations'] );
     132        }
     133
     134        return $mods;
    95135    }
    96136
     
    201241
    202242                if ( 'posts' === magic_ex_im_get_magic_type() ) {
    203 
    204243                    $key_data = pll_get_post_language( $item_id );
    205244
     
    347386        }
    348387
    349         if ( 'posts' === magic_ex_im_get_magic_type() && ! $item_lang ) {
     388        if ( ! $item_lang && 'posts' === magic_ex_im_get_magic_type() ) {
    350389            if ( ! $this->is_translated_item( $item_data['post']['post_type'] ) ) {
    351390
    352391                $this->set_lang( '' );
    353392                return $item_data;
     393            }
     394        }
     395
     396        if ( ! $item_lang && 'menus' === magic_ex_im_get_magic_type() ) {
     397
     398            if ( $this->get_pll_nav_menu() ) {
     399                $menu_location          = $item_data['menu']['menu_location'] ?? '';
     400                $menu_location_exploded = $this->get_pll_nav_menu()->explode_location( $menu_location );
     401                $item_lang              = $menu_location_exploded['lang'] ?? pll_default_language();
     402            } else {
     403                $item_lang = pll_default_language();
     404            }
     405
     406            if ( ! $this->is_installed_lang( $item_lang ) ) {
     407                $item_lang = pll_default_language();
    354408            }
    355409        }
  • magic-export-import/trunk/readme.txt

    r3307536 r3310897  
    33Donate link: https://send.monobank.ua/93SXgAPRmB
    44Tags: export, import, content migration, csv, custom fields
    5 Tested up to: 6.7
    6 Stable tag: 1.0.4
     5Tested up to: 6.8
     6Stable tag: 1.1.0
    77License: GPL v3 or later
    88License URI: https://www.gnu.org/licenses/gpl-3.0.txt
    99
    10 The ultimate tool to migrate any content including posts, terms, users, comments, WooCommerce shop orders and ACF Options pages.
     10The ultimate tool to migrate any content including posts, terms, users, comments, WooCommerce shop orders, menus and ACF Options pages.
    1111
    1212== Description ==
     
    2121- All types of comments, including product reviews
    2222- WooCommerce orders, with full support for High-Performance Order Storage (HPOS)
     23- Registered WordPress menus
    2324- Content of ACF Options pages
    2425
     
    7879== Changelog ==
    7980
     81= 1.1.0 =
     82* Added support for WordPress menus export and import
     83
    8084= 1.0.4 =
    8185* Added fields support for plugin ACF Extended : Advanced Link, Code Editor
Note: See TracChangeset for help on using the changeset viewer.