Update elementor pro
This commit is contained in:
@@ -39,11 +39,9 @@ class Admin extends App {
|
||||
public function enqueue_styles() {
|
||||
$suffix = Utils::is_script_debug() ? '' : '.min';
|
||||
|
||||
$direction_suffix = is_rtl() ? '-rtl' : '';
|
||||
|
||||
wp_register_style(
|
||||
'elementor-pro-admin',
|
||||
ELEMENTOR_PRO_ASSETS_URL . 'css/admin' . $direction_suffix . $suffix . '.css',
|
||||
ELEMENTOR_PRO_URL . 'assets/css/admin' . $suffix . '.css',
|
||||
[],
|
||||
ELEMENTOR_PRO_VERSION
|
||||
);
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
namespace ElementorPro\Core\App;
|
||||
|
||||
use Elementor\Core\Base\App as BaseApp;
|
||||
use Elementor\Core\Utils\Assets_Config_Provider;
|
||||
use Elementor\Core\Utils\Collection;
|
||||
use Elementor\Utils;
|
||||
use ElementorPro\Plugin;
|
||||
use ElementorPro\Core\App\Modules\SiteEditor\Module as SiteEditor;
|
||||
use ElementorPro\Core\App\Modules\KitLibrary\Module as KitLibrary;
|
||||
use ElementorPro\Core\App\Modules\Onboarding\Module as Onboarding;
|
||||
use ElementorPro\Core\App\Modules\ImportExport\Module as ImportExport;
|
||||
use ElementorPro\Core\App\Modules\ImportExportCustomization\Module as ImportExportCustomization;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
@@ -45,10 +49,45 @@ class App extends BaseApp {
|
||||
return ELEMENTOR_PRO_URL;
|
||||
}
|
||||
|
||||
private function register_packages() {
|
||||
// Register Core's v2 packages for backward compatibility with Core 3.30
|
||||
// In 3.30, these exist but aren't loaded on the app page
|
||||
// In 3.31+, Core already registers them, so we skip
|
||||
$assets_config_provider = ( new Assets_Config_Provider() )
|
||||
->set_path_resolver( function ( $name ) {
|
||||
return ELEMENTOR_ASSETS_PATH . "js/packages/{$name}/{$name}.asset.php";
|
||||
} );
|
||||
|
||||
Collection::make( [ 'ui', 'icons' ] )
|
||||
->each( function( $package ) use ( $assets_config_provider ) {
|
||||
$suffix = Utils::is_script_debug() ? '' : '.min';
|
||||
$config = $assets_config_provider->load( $package )->get( $package );
|
||||
|
||||
if ( ! $config ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if Core already registered this (Core 3.31+)
|
||||
if ( wp_script_is( $config['handle'], 'registered' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_register_script(
|
||||
$config['handle'],
|
||||
ELEMENTOR_ASSETS_URL . "js/packages/{$package}/{$package}{$suffix}.js",
|
||||
$config['deps'],
|
||||
ELEMENTOR_VERSION,
|
||||
true
|
||||
);
|
||||
} );
|
||||
}
|
||||
|
||||
private function enqueue_assets() {
|
||||
$this->register_packages();
|
||||
|
||||
wp_enqueue_style(
|
||||
'elementor-pro-app',
|
||||
$this->get_css_assets_url( 'app', null, 'default', true ),
|
||||
$this->get_css_assets_url( 'app' ),
|
||||
[
|
||||
'elementor-app',
|
||||
'select2',
|
||||
@@ -63,6 +102,8 @@ class App extends BaseApp {
|
||||
'wp-i18n',
|
||||
'elementor-app-packages',
|
||||
'elementor-common',
|
||||
'elementor-v2-ui',
|
||||
'elementor-v2-icons',
|
||||
'select2',
|
||||
'react-dom',
|
||||
],
|
||||
@@ -88,6 +129,7 @@ class App extends BaseApp {
|
||||
$this->add_component( 'kit-library', new KitLibrary() );
|
||||
$this->add_component( 'onboarding', new Onboarding() );
|
||||
$this->add_component( 'import-export', new ImportExport() );
|
||||
$this->add_component( 'import-export-customization', new ImportExportCustomization() );
|
||||
|
||||
add_action( 'elementor/app/init', [ $this, 'init' ] );
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import Module from '../../modules/site-editor/assets/js/site-editor';
|
||||
import ImportExportCustomizationModule from '../../modules/import-export-customization/assets/js/module';
|
||||
|
||||
new Module();
|
||||
new ImportExportCustomizationModule();
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
import { useState } from 'react';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
CircularProgress,
|
||||
FormControlLabel,
|
||||
Checkbox,
|
||||
Grid,
|
||||
Button,
|
||||
Stack,
|
||||
} from '@elementor/ui';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import { htmlDecodeTextContent } from 'elementor-pro-app/utils';
|
||||
import { UpgradeTooltip } from './upgrade-tooltip';
|
||||
const DEFAULT_VISIBLE_ITEMS_COUNT = 16;
|
||||
|
||||
export function ListSettingSection( {
|
||||
items,
|
||||
title,
|
||||
loading,
|
||||
settings,
|
||||
onSettingChange,
|
||||
settingKey,
|
||||
disabled = false,
|
||||
tooltip = false,
|
||||
} ) {
|
||||
const [ showMore, setShowMore ] = useState( false );
|
||||
|
||||
return (
|
||||
<Box key={ settingKey } sx={ { mb: 3, border: 1, borderRadius: 1, borderColor: 'action.focus', p: 2.5 } }>
|
||||
<Stack spacing={ 2 } >
|
||||
<Typography variant="h6" >
|
||||
{ title }
|
||||
</Typography>
|
||||
<Grid container spacing={ 1 } alignItems="start" >
|
||||
{ loading
|
||||
? <Grid item xs={ 12 } sx={ { p: 1, alignItems: 'center', textAlign: 'center' } } >
|
||||
<CircularProgress size={ 30 } />
|
||||
</Grid>
|
||||
: <>
|
||||
<Grid key={ 'all' } item xs={ 12 } sx={ { py: 1, px: 0 } } >
|
||||
<UpgradeTooltip disabled={ disabled && settings.length === items.length } tooltip={ tooltip }>
|
||||
<Box sx={ {
|
||||
pointerEvents: 'auto',
|
||||
...( settings.length === items.length && disabled && { cursor: 'pointer' } ),
|
||||
} }>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
color="info"
|
||||
checked={ settings.length === items.length }
|
||||
indeterminate={ settings.length > 0 && ( settings.length !== items.length ) }
|
||||
onChange={ ( e, checked ) => {
|
||||
if ( checked ) {
|
||||
onSettingChange( items.map( ( { value } ) => value ), true );
|
||||
} else {
|
||||
onSettingChange( [], true );
|
||||
}
|
||||
} }
|
||||
sx={ { p: 0 } }
|
||||
disabled={ disabled }
|
||||
/>
|
||||
}
|
||||
sx={ {
|
||||
gap: 1,
|
||||
...( settings.length === items.length && disabled && { cursor: 'pointer' } ),
|
||||
} }
|
||||
slotProps={ {
|
||||
typography: {
|
||||
sx: {
|
||||
fontWeight: 500,
|
||||
...( settings.length === items.length && disabled && { cursor: 'pointer' } ),
|
||||
},
|
||||
},
|
||||
} }
|
||||
label={ `${ __( 'All', 'elementor-pro' ) } ${ title.toLowerCase() }` }
|
||||
/>
|
||||
</Box>
|
||||
</UpgradeTooltip>
|
||||
</Grid>
|
||||
{ ( showMore ? items : items.slice( 0, DEFAULT_VISIBLE_ITEMS_COUNT ) ).map( ( item ) => {
|
||||
return (
|
||||
<Grid key={ item.value } item xs={ 3 } sx={ { py: 1, px: 0 } } >
|
||||
<UpgradeTooltip disabled={ disabled && settings.includes( item.value ) } tooltip={ tooltip }>
|
||||
<Box sx={ {
|
||||
pointerEvents: 'auto',
|
||||
...( settings.includes( item.value ) && disabled && { cursor: 'pointer' } ),
|
||||
} }>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
color="info"
|
||||
checked={ settings.includes( item.value ) }
|
||||
onChange={ ( e, checked ) => {
|
||||
if ( checked ) {
|
||||
onSettingChange( [ ...settings, item.value ] );
|
||||
} else {
|
||||
onSettingChange( settings.filter( ( setting ) => setting !== item.value ) );
|
||||
}
|
||||
} }
|
||||
sx={ {
|
||||
p: 0,
|
||||
...( settings.includes( item.value ) && disabled && { cursor: 'pointer' } ),
|
||||
} }
|
||||
disabled={ disabled }
|
||||
/>
|
||||
}
|
||||
sx={ {
|
||||
maxWidth: '100%',
|
||||
gap: 1,
|
||||
...( settings.includes( item.value ) && disabled && { cursor: 'pointer' } ),
|
||||
} }
|
||||
slotProps={ {
|
||||
typography: {
|
||||
sx: {
|
||||
fontWeight: 400,
|
||||
maxWidth: '100%',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
...( settings.includes( item.value ) && disabled && { cursor: 'pointer' } ),
|
||||
},
|
||||
},
|
||||
} }
|
||||
label={ htmlDecodeTextContent( item.label ) }
|
||||
/>
|
||||
</Box>
|
||||
</UpgradeTooltip>
|
||||
</Grid>
|
||||
);
|
||||
} ) }
|
||||
</>
|
||||
}
|
||||
</Grid>
|
||||
</Stack>
|
||||
|
||||
{ items.length > DEFAULT_VISIBLE_ITEMS_COUNT && (
|
||||
<Button
|
||||
variant="text"
|
||||
color="info"
|
||||
onClick={ () => setShowMore( ! showMore ) }
|
||||
>
|
||||
{ showMore ? __( 'Show less', 'elementor' ) : __( 'Show more', 'elementor' ) }
|
||||
</Button>
|
||||
) }
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
ListSettingSection.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
children: PropTypes.node,
|
||||
loading: PropTypes.bool,
|
||||
disabled: PropTypes.bool,
|
||||
checked: PropTypes.bool,
|
||||
settingKey: PropTypes.string,
|
||||
onSettingChange: PropTypes.func.isRequired,
|
||||
tooltip: PropTypes.bool,
|
||||
items: PropTypes.arrayOf(
|
||||
PropTypes.shape( {
|
||||
label: PropTypes.string.isRequired,
|
||||
value: PropTypes.oneOfType( [ PropTypes.string, PropTypes.number ] ),
|
||||
} ),
|
||||
),
|
||||
settings: PropTypes.arrayOf( PropTypes.oneOfType( [ PropTypes.string, PropTypes.number ] ) ),
|
||||
};
|
||||
@@ -0,0 +1,87 @@
|
||||
import { Box, Typography, Switch, Stack } from '@elementor/ui';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import { UpgradeTooltip } from './upgrade-tooltip';
|
||||
|
||||
export const SettingSection = ( {
|
||||
checked = false,
|
||||
title,
|
||||
description,
|
||||
children,
|
||||
settingKey,
|
||||
onSettingChange,
|
||||
hasToggle = true,
|
||||
disabled = false,
|
||||
notExported = false,
|
||||
tooltip = false,
|
||||
} ) => {
|
||||
const getToggle = () => {
|
||||
if ( notExported ) {
|
||||
return (
|
||||
<Typography data-testid={ `${ settingKey }-description` } variant="body1" color="text.secondary">
|
||||
{ __( 'Not exported', 'elementor' ) }
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! hasToggle ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const switchElement = (
|
||||
<Switch
|
||||
data-testid={ `${ settingKey }-switch` }
|
||||
checked={ checked }
|
||||
onChange={ ( _, isChecked ) => onSettingChange && onSettingChange( settingKey, isChecked ) }
|
||||
color="info"
|
||||
size="medium"
|
||||
sx={ {
|
||||
alignSelf: 'center',
|
||||
...( disabled && tooltip && { cursor: 'pointer' } ),
|
||||
} }
|
||||
disabled={ disabled }
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<UpgradeTooltip disabled={ disabled } tooltip={ tooltip }>
|
||||
{ switchElement }
|
||||
</UpgradeTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box key={ settingKey } sx={ { mb: 3, border: 1, borderRadius: 1, borderColor: 'action.focus', p: 2.5 } }>
|
||||
<Box sx={ { display: 'flex', justifyContent: 'space-between', alignItems: 'center' } }>
|
||||
<Stack spacing={ 1 }>
|
||||
<Typography variant="h6">
|
||||
{ title }
|
||||
</Typography>
|
||||
{ description && (
|
||||
<Typography data-testid={ `${ settingKey }-description` } variant="body1" color="text.secondary">
|
||||
{ description }
|
||||
</Typography>
|
||||
) }
|
||||
</Stack>
|
||||
{ getToggle() }
|
||||
</Box>
|
||||
{ children && (
|
||||
<Box sx={ { mt: 1 } }>
|
||||
{ children }
|
||||
</Box>
|
||||
) }
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
SettingSection.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
description: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
hasToggle: PropTypes.bool,
|
||||
checked: PropTypes.bool,
|
||||
disabled: PropTypes.bool,
|
||||
settingKey: PropTypes.string,
|
||||
onSettingChange: PropTypes.func,
|
||||
notExported: PropTypes.bool,
|
||||
tooltip: PropTypes.bool,
|
||||
};
|
||||
@@ -0,0 +1,77 @@
|
||||
import { Box, Typography, Switch } from '@elementor/ui';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import { UpgradeTooltip } from './upgrade-tooltip';
|
||||
|
||||
export const SubSetting = ( {
|
||||
label,
|
||||
settingKey,
|
||||
onSettingChange,
|
||||
checked = false,
|
||||
disabled = false,
|
||||
notExported = false,
|
||||
tooltip = false,
|
||||
} ) => {
|
||||
const getToggle = () => {
|
||||
if ( notExported ) {
|
||||
return (
|
||||
<Typography
|
||||
data-testid={ `${ settingKey }-description` }
|
||||
variant="body1"
|
||||
color="text.secondary"
|
||||
sx={ {
|
||||
fontWeight: 400,
|
||||
alignSelf: 'center',
|
||||
} }
|
||||
>
|
||||
{ __( 'Not exported', 'elementor' ) }
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
||||
const switchElement = (
|
||||
<Switch
|
||||
data-testid={ `${ settingKey }-switch` }
|
||||
checked={ checked }
|
||||
disabled={ disabled }
|
||||
onChange={ ( _, isChecked ) => onSettingChange && onSettingChange( settingKey, isChecked ) }
|
||||
color="info"
|
||||
size="medium"
|
||||
sx={ {
|
||||
alignSelf: 'center',
|
||||
...( disabled && tooltip && { cursor: 'pointer' } ),
|
||||
} }
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<UpgradeTooltip disabled={ disabled } tooltip={ tooltip }>
|
||||
{ switchElement }
|
||||
</UpgradeTooltip>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={ {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
} }
|
||||
>
|
||||
<Typography data-testid={ `${ settingKey }-label` } variant="body1">
|
||||
{ label }
|
||||
</Typography>
|
||||
{ getToggle() }
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
SubSetting.propTypes = {
|
||||
checked: PropTypes.bool,
|
||||
disabled: PropTypes.bool,
|
||||
notExported: PropTypes.bool,
|
||||
label: PropTypes.string.isRequired,
|
||||
settingKey: PropTypes.string.isRequired,
|
||||
onSettingChange: PropTypes.func,
|
||||
tooltip: PropTypes.bool,
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './customization-list-setting-section';
|
||||
export * from './customization-setting-section';
|
||||
export * from './customization-sub-setting';
|
||||
@@ -0,0 +1,498 @@
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import { Stack, CircularProgress, Box, Radio, RadioGroup, FormControlLabel, FormControl, Typography, Alert, SvgIcon } from '@elementor/ui';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { KitCustomizationDialog } from './kit-customization-dialog';
|
||||
import { ListSettingSection } from './customization-list-setting-section';
|
||||
import { SettingSection } from './customization-setting-section';
|
||||
import { SubSetting } from './customization-sub-setting';
|
||||
import { UpgradeNoticeBanner } from './upgrade-notice-banner';
|
||||
import { useKitCustomizationPages } from '../hooks/use-kit-customization-pages';
|
||||
import { useKitCustomizationTaxonomies } from '../hooks/use-kit-customization-taxonomies';
|
||||
import { useKitCustomizationCustomPostTypes } from '../hooks/use-kit-customization-custom-post-types';
|
||||
import { isHighTier } from '../hooks/use-tier';
|
||||
import { UpgradeVersionBanner } from './upgrade-version-banner';
|
||||
import { transformValueForAnalytics } from '../utils/analytics-transformer';
|
||||
|
||||
const MEDIA_FORMAT_OPTIONS = {
|
||||
LINK: 'link',
|
||||
CLOUD: 'cloud',
|
||||
};
|
||||
|
||||
const MEDIA_FORMAT_CONFIG = [
|
||||
{
|
||||
value: MEDIA_FORMAT_OPTIONS.LINK,
|
||||
title: __( 'Link to media', 'elementor-pro' ),
|
||||
description: __( 'Stores only the URLs. The export stays light, but files load only while the original site is online.', 'elementor-pro' ),
|
||||
},
|
||||
{
|
||||
value: MEDIA_FORMAT_OPTIONS.CLOUD,
|
||||
title: __( 'Save media to the cloud', 'elementor-pro' ),
|
||||
description: __( 'All images and files are stored with the template. Keeps everything intact, but the file is larger.', 'elementor-pro' ),
|
||||
},
|
||||
];
|
||||
|
||||
const transformAnalyticsData = ( payload, pageOptions, taxonomyOptions, customPostTypes ) => {
|
||||
const optionsArray = [
|
||||
{ key: 'pages', options: pageOptions },
|
||||
{ key: 'taxonomies', options: taxonomyOptions },
|
||||
{ key: 'customPostTypes', options: customPostTypes },
|
||||
];
|
||||
|
||||
const transformed = {};
|
||||
|
||||
for ( const [ key, value ] of Object.entries( payload ) ) {
|
||||
transformed[ key ] = transformValueForAnalytics( key, value, optionsArray );
|
||||
}
|
||||
|
||||
return transformed;
|
||||
};
|
||||
|
||||
export function KitContentCustomizationDialog( {
|
||||
open,
|
||||
handleClose,
|
||||
handleSaveChanges,
|
||||
data,
|
||||
isImport,
|
||||
isOldExport,
|
||||
isOldElementorVersion,
|
||||
isCloudKitsEligible = false,
|
||||
showMediaFormatValidation = false,
|
||||
} ) {
|
||||
const initialState = data.includes.includes( 'content' );
|
||||
const { isLoading: isPagesLoading, pageOptions, isLoaded: isPagesLoaded } = useKitCustomizationPages( { open, data } );
|
||||
const { isLoading: isTaxonomiesLoading, taxonomyOptions, isLoaded: isTaxonomiesLoaded } = useKitCustomizationTaxonomies( { open, data } );
|
||||
const { customPostTypes } = useKitCustomizationCustomPostTypes( { data } );
|
||||
|
||||
const alertRef = useRef( null );
|
||||
const mediaFormatSectionRef = useRef( null );
|
||||
|
||||
const [ settings, setSettings ] = useState( () => {
|
||||
if ( data.customization.content ) {
|
||||
return data.customization.content;
|
||||
}
|
||||
|
||||
return {
|
||||
pages: [],
|
||||
menus: initialState,
|
||||
taxonomies: [],
|
||||
customPostTypes: [],
|
||||
mediaFormat: MEDIA_FORMAT_OPTIONS.LINK,
|
||||
};
|
||||
} );
|
||||
|
||||
useEffect( () => {
|
||||
if ( ! open || data.includes.includes( 'content' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
setSettings( {
|
||||
pages: [],
|
||||
menus: false,
|
||||
taxonomies: [],
|
||||
customPostTypes: [],
|
||||
mediaFormat: MEDIA_FORMAT_OPTIONS.LINK,
|
||||
} );
|
||||
}, [ open, data.includes ] );
|
||||
|
||||
useEffect( () => {
|
||||
if ( ! open || ! data.includes.includes( 'content' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
setSettings( ( prevSettings ) => ( {
|
||||
...prevSettings,
|
||||
pages: isPagesLoaded || isImport
|
||||
? ( data.customization.content?.pages || pageOptions.map( ( { value } ) => value ) )
|
||||
: prevSettings.pages,
|
||||
} ) );
|
||||
}, [
|
||||
open,
|
||||
data.includes,
|
||||
data.customization.content?.pages,
|
||||
isPagesLoaded,
|
||||
isImport,
|
||||
pageOptions,
|
||||
] );
|
||||
|
||||
useEffect( () => {
|
||||
if ( ! open || ! data.includes.includes( 'content' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
setSettings( ( prevSettings ) => ( {
|
||||
...prevSettings,
|
||||
taxonomies: isTaxonomiesLoaded || isImport
|
||||
? ( data.customization.content?.taxonomies || taxonomyOptions.map( ( { value } ) => value ) )
|
||||
: prevSettings.taxonomies,
|
||||
} ) );
|
||||
}, [
|
||||
open,
|
||||
data.includes,
|
||||
data.customization.content?.taxonomies,
|
||||
isTaxonomiesLoaded,
|
||||
isImport,
|
||||
taxonomyOptions,
|
||||
] );
|
||||
|
||||
useEffect( () => {
|
||||
if ( ! open || ! data.includes.includes( 'content' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
setSettings( ( prevSettings ) => ( {
|
||||
...prevSettings,
|
||||
customPostTypes: customPostTypes
|
||||
? ( data.customization.content?.customPostTypes || customPostTypes.map( ( { value } ) => value ) )
|
||||
: prevSettings.customPostTypes,
|
||||
} ) );
|
||||
}, [
|
||||
open,
|
||||
data.includes,
|
||||
data.customization.content?.customPostTypes,
|
||||
customPostTypes,
|
||||
] );
|
||||
|
||||
useEffect( () => {
|
||||
if ( ! open || ! data.includes.includes( 'content' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
setSettings( ( prevSettings ) => ( {
|
||||
...prevSettings,
|
||||
menus: isImport
|
||||
? ( data.customization.content?.menus || Object.keys( data?.uploadedData?.manifest[ 'wp-content' ]?.nav_menu_item || {} ).length > 0 )
|
||||
: ( data.customization.content?.menus ?? initialState ),
|
||||
} ) );
|
||||
}, [
|
||||
open,
|
||||
data.includes,
|
||||
data.customization.content?.menus,
|
||||
data.uploadedData?.manifest,
|
||||
isImport,
|
||||
] );
|
||||
|
||||
useEffect( () => {
|
||||
if ( ! open || ! data.includes.includes( 'content' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
setSettings( ( prevSettings ) => ( {
|
||||
...prevSettings,
|
||||
mediaFormat: data.customization.content?.mediaFormat || MEDIA_FORMAT_OPTIONS.LINK,
|
||||
} ) );
|
||||
}, [
|
||||
open,
|
||||
data.includes,
|
||||
data.customization.content?.mediaFormat,
|
||||
] );
|
||||
|
||||
useEffect( () => {
|
||||
if ( open ) {
|
||||
window.elementorModules?.appsEventTracking?.AppsEventTracking?.sendPageViewsWebsiteTemplates( elementorCommon.eventsManager.config.secondaryLocations.kitLibrary.kitExportCustomizationEdit );
|
||||
}
|
||||
}, [ open ] );
|
||||
|
||||
useEffect( () => {
|
||||
if ( showMediaFormatValidation ) {
|
||||
setTimeout( () => {
|
||||
const targetElement = alertRef.current || mediaFormatSectionRef.current;
|
||||
if ( targetElement ) {
|
||||
targetElement.scrollIntoView( { behavior: 'smooth', block: 'center' } );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}, [ showMediaFormatValidation ] );
|
||||
|
||||
const handleSettingsChange = ( settingKey, payload ) => {
|
||||
setSettings( ( prev ) => ( {
|
||||
...prev,
|
||||
[ settingKey ]: payload,
|
||||
} ) );
|
||||
};
|
||||
|
||||
const isTaxonomiesExported = () => {
|
||||
return isImport && taxonomyOptions?.length > 0;
|
||||
};
|
||||
|
||||
const isPagesExported = () => {
|
||||
const content = data?.uploadedData?.manifest?.content;
|
||||
const wpContent = data?.uploadedData?.manifest?.[ 'wp-content' ];
|
||||
|
||||
const isSomeContentExported = Object.keys( content?.page || {} )?.length;
|
||||
const isSomeWPContentExported = Object.keys( wpContent?.page || {} )?.length;
|
||||
|
||||
return Boolean( isSomeContentExported || isSomeWPContentExported );
|
||||
};
|
||||
const isMenusExported = () => {
|
||||
return Object.keys( data?.uploadedData?.manifest?.[ 'wp-content' ]?.nav_menu_item || {} ).length > 0 ||
|
||||
customPostTypes?.find( ( cpt ) => cpt.value.includes( 'nav_menu' ) );
|
||||
};
|
||||
|
||||
const isCustomPostTypesExported = () => {
|
||||
return isImport && customPostTypes?.length > 0;
|
||||
};
|
||||
|
||||
const renderPagesSection = () => {
|
||||
if ( isImport && isOldExport ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return isImport && ! isPagesExported() ? (
|
||||
<SettingSection
|
||||
title={ __( 'Site pages', 'elementor-pro' ) }
|
||||
settingKey="pages"
|
||||
notExported
|
||||
/>
|
||||
) : (
|
||||
<ListSettingSection
|
||||
settingKey="pages"
|
||||
title={ __( 'Site pages', 'elementor-pro' ) }
|
||||
onSettingChange={ ( selectedPages ) => {
|
||||
handleSettingsChange( 'pages', selectedPages );
|
||||
} }
|
||||
settings={ settings.pages }
|
||||
items={ pageOptions }
|
||||
loading={ isPagesLoading }
|
||||
disabled={ ! isHighTier() }
|
||||
tooltip={ ! isHighTier() }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const renderMenusSection = () => {
|
||||
if ( isImport && isOldExport ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingSection
|
||||
checked={ settings.menus }
|
||||
disabled={ ( isImport && ! isMenusExported() ) || ! isHighTier() }
|
||||
title={ __( 'Menus', 'elementor-pro' ) }
|
||||
settingKey="menus"
|
||||
tooltip={ ! isHighTier() }
|
||||
onSettingChange={ ( key, isChecked ) => {
|
||||
handleSettingsChange( key, isChecked );
|
||||
} }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const renderMediaFormatSection = () => {
|
||||
if ( isImport ) {
|
||||
return (
|
||||
<SettingSection
|
||||
title={ __( 'Media format', 'elementor-pro' ) }
|
||||
settingKey="mediaFormat"
|
||||
hasToggle={ false }
|
||||
>
|
||||
<Alert
|
||||
icon={
|
||||
<SvgIcon color="info" viewBox="0 0 24 24">
|
||||
<path d="M11.8623 14.7549C12.3665 14.8061 12.7598 15.2322 12.7598 15.75C12.7598 16.2678 12.3665 16.6939 11.8623 16.7451L11.7598 16.75H11.75C11.1977 16.75 10.75 16.3023 10.75 15.75C10.75 15.1977 11.1977 14.75 11.75 14.75H11.7598L11.8623 14.7549Z" fill="currentColor" />
|
||||
<path d="M11.75 7C12.1642 7 12.5 7.33579 12.5 7.75V12.75C12.5 13.1642 12.1642 13.5 11.75 13.5C11.3358 13.5 11 13.1642 11 12.75V7.75C11 7.33579 11.3358 7 11.75 7Z" fill="currentColor" />
|
||||
<path fillRule="evenodd" clipRule="evenodd" d="M11.75 2C17.1348 2 21.5 6.36522 21.5 11.75C21.5 17.1348 17.1348 21.5 11.75 21.5C6.36522 21.5 2 17.1348 2 11.75C2 6.36522 6.36522 2 11.75 2ZM11.75 3.5C7.19365 3.5 3.5 7.19365 3.5 11.75C3.5 16.3063 7.19365 20 11.75 20C16.3063 20 20 16.3063 20 11.75C20 7.19365 16.3063 3.5 11.75 3.5Z" fill="currentColor" />
|
||||
</SvgIcon>
|
||||
}
|
||||
sx={ {
|
||||
backgroundColor: 'transparent',
|
||||
p: 0,
|
||||
} }
|
||||
>
|
||||
<Typography variant="body2" color="text.primary">
|
||||
<strong>{ __( 'Note:', 'elementor-pro' ) }</strong> { __( 'The media will be uploaded automatically, just as it was saved during export', 'elementor-pro' ) }
|
||||
</Typography>
|
||||
</Alert>
|
||||
</SettingSection>
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! isImport && ! isCloudKitsEligible ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingSection
|
||||
ref={ mediaFormatSectionRef }
|
||||
description={ __( 'Select how do you want to save & export the media files.', 'elementor-pro' ) }
|
||||
title={ __( 'Media format', 'elementor-pro' ) }
|
||||
settingKey="mediaFormat"
|
||||
hasToggle={ false }
|
||||
disabled={ ! isHighTier() }
|
||||
tooltip={ ! isHighTier() }
|
||||
>
|
||||
<Box sx={ { pt: 2.5 } }>
|
||||
<FormControl component="fieldset" disabled={ ! isHighTier() } sx={ { width: '100%' } }>
|
||||
<RadioGroup
|
||||
value={ settings.mediaFormat }
|
||||
onChange={ ( event ) => {
|
||||
handleSettingsChange( 'mediaFormat', event.target.value );
|
||||
} }
|
||||
sx={ { width: '100%' } }
|
||||
>
|
||||
{ MEDIA_FORMAT_CONFIG.map( ( option, index ) => (
|
||||
<Box
|
||||
key={ option.value }
|
||||
sx={ {
|
||||
border: 1,
|
||||
borderColor: settings.mediaFormat === option.value ? 'info.light' : 'divider',
|
||||
borderRadius: 2,
|
||||
p: 1,
|
||||
mb: index < MEDIA_FORMAT_CONFIG.length - 1 ? 1.5 : 0,
|
||||
width: '100%',
|
||||
} }
|
||||
>
|
||||
<FormControlLabel
|
||||
value={ option.value }
|
||||
control={
|
||||
<Radio color="info" data-testid={ `media-format-${ option.value }` } />
|
||||
}
|
||||
label={
|
||||
<Box>
|
||||
<Typography variant="body2" sx={ { mb: 0.25 } }>
|
||||
{ option.title }
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{ option.description }
|
||||
</Typography>
|
||||
</Box>
|
||||
}
|
||||
sx={ { alignItems: 'flex-start', m: 0, width: '100%' } }
|
||||
/>
|
||||
</Box>
|
||||
) ) }
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
{ showMediaFormatValidation && (
|
||||
<Alert
|
||||
ref={ alertRef }
|
||||
icon={
|
||||
<SvgIcon color="error" viewBox="0 0 24 24">
|
||||
<path d="M11.8623 14.7549C12.3665 14.8061 12.7598 15.2322 12.7598 15.75C12.7598 16.2678 12.3665 16.6939 11.8623 16.7451L11.7598 16.75H11.75C11.1977 16.75 10.75 16.3023 10.75 15.75C10.75 15.1977 11.1977 14.75 11.75 14.75H11.7598L11.8623 14.7549Z" fill="currentColor" />
|
||||
<path d="M11.75 7C12.1642 7 12.5 7.33579 12.5 7.75V12.75C12.5 13.1642 12.1642 13.5 11.75 13.5C11.3358 13.5 11 13.1642 11 12.75V7.75C11 7.33579 11.3358 7 11.75 7Z" fill="currentColor" />
|
||||
<path fillRule="evenodd" clipRule="evenodd" d="M11.75 2C17.1348 2 21.5 6.36522 21.5 11.75C21.5 17.1348 17.1348 21.5 11.75 21.5C6.36522 21.5 2 17.1348 2 11.75C2 6.36522 6.36522 2 11.75 2ZM11.75 3.5C7.19365 3.5 3.5 7.19365 3.5 11.75C3.5 16.3063 7.19365 20 11.75 20C16.3063 20 20 16.3063 20 11.75C20 7.19365 16.3063 3.5 11.75 3.5Z" fill="currentColor" />
|
||||
</SvgIcon>
|
||||
}
|
||||
sx={ {
|
||||
mt: 2,
|
||||
ml: 1,
|
||||
backgroundColor: 'transparent',
|
||||
p: 0,
|
||||
} }
|
||||
>
|
||||
<Typography variant="body2" color="text.primary">
|
||||
<strong>{ __( 'Note:', 'elementor-pro' ) }</strong> { __( 'To export a ZIP, go to Edit Content, choose \'Link to Media\', then Export as ZIP.', 'elementor-pro' ) }<br></br>{ __( 'Or, save this template to the cloud instead.', 'elementor-pro' ) }
|
||||
</Typography>
|
||||
</Alert>
|
||||
) }
|
||||
</Box>
|
||||
</SettingSection>
|
||||
);
|
||||
};
|
||||
|
||||
const renderTaxonomiesSection = () => {
|
||||
if ( isImport && isOldExport ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingSection
|
||||
description={ __( 'Group your content by type, topic, or any structure you choose.', 'elementor-pro' ) }
|
||||
title={ __( 'Taxonomies', 'elementor-pro' ) }
|
||||
settingKey="taxonomies"
|
||||
notExported={ isImport && ! isTaxonomiesExported() }
|
||||
hasToggle={ false }
|
||||
>
|
||||
{ isTaxonomiesLoading
|
||||
? <Box sx={ { p: 1, alignItems: 'center', textAlign: 'center' } } >
|
||||
<CircularProgress size={ 30 } />
|
||||
</Box>
|
||||
: ( taxonomyOptions.map( ( taxonomy ) => {
|
||||
return (
|
||||
<SubSetting
|
||||
key={ taxonomy.value }
|
||||
label={ taxonomy.label }
|
||||
settingKey="taxonomies"
|
||||
checked={ settings.taxonomies.includes( taxonomy.value ) }
|
||||
disabled={ ! isHighTier() }
|
||||
tooltip={ ! isHighTier() }
|
||||
onSettingChange={ ( key, isChecked ) => {
|
||||
setSettings( ( prevState ) => {
|
||||
const selectedTaxonomies = isChecked
|
||||
? [ ...prevState.taxonomies, taxonomy.value ]
|
||||
: prevState.taxonomies.filter( ( value ) => value !== taxonomy.value );
|
||||
|
||||
return {
|
||||
...prevState,
|
||||
taxonomies: selectedTaxonomies,
|
||||
};
|
||||
} );
|
||||
} }
|
||||
/>
|
||||
);
|
||||
} )
|
||||
) }
|
||||
</SettingSection>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<KitCustomizationDialog
|
||||
open={ open }
|
||||
title={ __( 'Edit content', 'elementor-pro' ) }
|
||||
handleClose={ handleClose }
|
||||
handleSaveChanges={ () => {
|
||||
const hasEnabledCustomization = settings.pages.length > 0 || settings.menus || settings.customPostTypes.length > 0 || settings.taxonomies.length > 0 || settings.mediaFormat !== MEDIA_FORMAT_OPTIONS.LINK;
|
||||
const transformedAnalytics = transformAnalyticsData( settings, pageOptions, taxonomyOptions, customPostTypes );
|
||||
handleSaveChanges( 'content', settings, hasEnabledCustomization, transformedAnalytics );
|
||||
} }
|
||||
>
|
||||
<Stack sx={ { position: 'relative' } } gap={ 2 }>
|
||||
{ isOldElementorVersion && (
|
||||
<UpgradeVersionBanner />
|
||||
) }
|
||||
<Stack>
|
||||
{ renderPagesSection() }
|
||||
{
|
||||
isImport && ! isCustomPostTypesExported() ? (
|
||||
<SettingSection
|
||||
title={ __( 'Custom post types', 'elementor-pro' ) }
|
||||
settingKey="customPostTypes"
|
||||
notExported
|
||||
/>
|
||||
) : (
|
||||
<ListSettingSection
|
||||
settingKey="customPostTypes"
|
||||
title={ __( 'Custom post types', 'elementor-pro' ) }
|
||||
onSettingChange={ ( selectedCustomPostTypes ) => {
|
||||
handleSettingsChange( 'customPostTypes', selectedCustomPostTypes );
|
||||
} }
|
||||
settings={ settings.customPostTypes }
|
||||
items={ customPostTypes }
|
||||
disabled={ ( isImport && undefined === data?.uploadedData?.manifest[ 'custom-post-type-title' ] ) || ! isHighTier() }
|
||||
tooltip={ ! isHighTier() }
|
||||
/>
|
||||
)
|
||||
}
|
||||
{ renderMediaFormatSection() }
|
||||
{ renderMenusSection() }
|
||||
{ renderTaxonomiesSection() }
|
||||
</Stack>
|
||||
<UpgradeNoticeBanner />
|
||||
</Stack>
|
||||
</KitCustomizationDialog>
|
||||
);
|
||||
}
|
||||
|
||||
KitContentCustomizationDialog.propTypes = {
|
||||
open: PropTypes.bool.isRequired,
|
||||
isImport: PropTypes.bool,
|
||||
isOldExport: PropTypes.bool,
|
||||
isOldElementorVersion: PropTypes.bool,
|
||||
handleClose: PropTypes.func.isRequired,
|
||||
handleSaveChanges: PropTypes.func.isRequired,
|
||||
data: PropTypes.object.isRequired,
|
||||
isCloudKitsEligible: PropTypes.bool,
|
||||
showMediaFormatValidation: PropTypes.bool,
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
import {
|
||||
Dialog,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Button,
|
||||
} from '@elementor/ui';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import * as PropTypes from 'prop-types';
|
||||
|
||||
export function KitCustomizationDialog( {
|
||||
open,
|
||||
title,
|
||||
handleClose,
|
||||
handleSaveChanges,
|
||||
children,
|
||||
saveDisabled = false,
|
||||
} ) {
|
||||
return (
|
||||
<Dialog
|
||||
open={ open }
|
||||
onClose={ handleClose }
|
||||
maxWidth="md"
|
||||
fullWidth
|
||||
>
|
||||
<DialogHeader onClose={ handleClose }>
|
||||
<DialogTitle>
|
||||
{ title }
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<DialogContent dividers sx={ { pt: 3, px: 3, pb: 0 } }>
|
||||
{ children }
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
<Button
|
||||
onClick={ handleClose }
|
||||
color="secondary"
|
||||
>
|
||||
{ __( 'Cancel', 'elementor' ) }
|
||||
</Button>
|
||||
<Button
|
||||
disabled={ saveDisabled }
|
||||
onClick={ () => {
|
||||
handleSaveChanges();
|
||||
handleClose();
|
||||
} }
|
||||
variant="contained"
|
||||
color="primary"
|
||||
>
|
||||
{ __( 'Save changes', 'elementor' ) }
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
KitCustomizationDialog.propTypes = {
|
||||
open: PropTypes.bool.isRequired,
|
||||
handleClose: PropTypes.func.isRequired,
|
||||
handleSaveChanges: PropTypes.func.isRequired,
|
||||
children: PropTypes.node.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
saveDisabled: PropTypes.bool,
|
||||
};
|
||||
@@ -0,0 +1,247 @@
|
||||
import { Stack } from '@elementor/ui';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import { SettingSection } from './customization-setting-section';
|
||||
import { SubSetting } from './customization-sub-setting';
|
||||
import { KitCustomizationDialog } from './kit-customization-dialog';
|
||||
import { UpgradeNoticeBanner } from './upgrade-notice-banner';
|
||||
import { isHighTier } from '../hooks/use-tier';
|
||||
import { UpgradeVersionBanner } from './upgrade-version-banner';
|
||||
import { transformValueForAnalytics } from '../utils/analytics-transformer';
|
||||
|
||||
const transformAnalyticsData = ( payload ) => {
|
||||
const transformed = {};
|
||||
|
||||
for ( const [ key, value ] of Object.entries( payload ) ) {
|
||||
transformed[ key ] = transformValueForAnalytics( key, value, [] );
|
||||
}
|
||||
|
||||
return transformed;
|
||||
};
|
||||
|
||||
export function KitSettingsCustomizationDialog( {
|
||||
open,
|
||||
handleClose,
|
||||
handleSaveChanges,
|
||||
data,
|
||||
isImport,
|
||||
isOldExport,
|
||||
isOldElementorVersion,
|
||||
} ) {
|
||||
const getState = useCallback( ( initialState ) => {
|
||||
if ( ! data.includes.includes( 'settings' ) ) {
|
||||
return {
|
||||
theme: initialState,
|
||||
globalColors: initialState,
|
||||
globalFonts: initialState,
|
||||
themeStyleSettings: initialState,
|
||||
generalSettings: initialState,
|
||||
experiments: initialState,
|
||||
customFonts: initialState,
|
||||
customIcons: initialState,
|
||||
customCode: initialState,
|
||||
};
|
||||
}
|
||||
|
||||
if ( isImport ) {
|
||||
const manifestData = data?.uploadedData?.manifest?.[ 'site-settings' ];
|
||||
|
||||
let themeState = false;
|
||||
if ( isOldExport ) {
|
||||
themeState = ! initialState ? false : data?.uploadedData?.manifest?.theme;
|
||||
} else {
|
||||
themeState = manifestData?.theme ?? initialState;
|
||||
}
|
||||
|
||||
return {
|
||||
theme: themeState,
|
||||
globalColors: isOldExport ? true : manifestData?.globalColors ?? initialState,
|
||||
globalFonts: isOldExport ? true : manifestData?.globalFonts ?? initialState,
|
||||
themeStyleSettings: isOldExport ? true : manifestData?.themeStyleSettings ?? initialState,
|
||||
generalSettings: isOldExport ? true : manifestData?.generalSettings ?? initialState,
|
||||
experiments: isOldExport ? true : manifestData?.experiments ?? initialState,
|
||||
customFonts: isOldExport ? true : manifestData?.customFonts ?? initialState,
|
||||
customIcons: isOldExport ? true : manifestData?.customIcons ?? initialState,
|
||||
customCode: isOldExport ? true : manifestData?.customCode ?? initialState,
|
||||
};
|
||||
}
|
||||
|
||||
const customization = data?.customization?.settings;
|
||||
return {
|
||||
theme: customization?.theme ?? initialState,
|
||||
globalColors: customization?.globalColors ?? initialState,
|
||||
globalFonts: customization?.globalFonts ?? initialState,
|
||||
themeStyleSettings: customization?.themeStyleSettings ?? initialState,
|
||||
generalSettings: customization?.generalSettings ?? initialState,
|
||||
experiments: customization?.experiments ?? initialState,
|
||||
customFonts: customization?.customFonts ?? initialState,
|
||||
customIcons: customization?.customIcons ?? initialState,
|
||||
customCode: customization?.customCode ?? initialState,
|
||||
};
|
||||
}, [ data.includes, data?.uploadedData?.manifest, data?.customization?.settings, isImport, isOldExport ] );
|
||||
|
||||
const initialState = data.includes.includes( 'settings' );
|
||||
|
||||
const [ settings, setSettings ] = useState( () => {
|
||||
if ( data.customization.settings ) {
|
||||
return data.customization.settings;
|
||||
}
|
||||
|
||||
return getState( initialState );
|
||||
} );
|
||||
|
||||
useEffect( () => {
|
||||
if ( open ) {
|
||||
if ( data.customization.settings ) {
|
||||
setSettings( data.customization.settings );
|
||||
} else {
|
||||
const state = getState( initialState );
|
||||
setSettings( state );
|
||||
}
|
||||
}
|
||||
}, [ open, data.customization.settings, data?.uploadedData, initialState, getState ] );
|
||||
|
||||
useEffect( () => {
|
||||
if ( open ) {
|
||||
window.elementorModules?.appsEventTracking?.AppsEventTracking?.sendPageViewsWebsiteTemplates( elementorCommon.eventsManager.config.secondaryLocations.kitLibrary.kitExportCustomizationEdit );
|
||||
}
|
||||
}, [ open ] );
|
||||
|
||||
const handleToggleChange = ( settingKey ) => {
|
||||
setSettings( ( prev ) => ( {
|
||||
...prev,
|
||||
[ settingKey ]: ! prev[ settingKey ],
|
||||
} ) );
|
||||
};
|
||||
|
||||
return (
|
||||
<KitCustomizationDialog
|
||||
open={ open }
|
||||
title={ __( 'Edit settings & configurations', 'elementor' ) }
|
||||
handleClose={ handleClose }
|
||||
handleSaveChanges={ () => {
|
||||
const hasEnabledCustomization = settings.theme || settings.globalColors || settings.globalFonts || settings.themeStyleSettings || settings.generalSettings || settings.experiments || settings.customFonts || settings.customIcons || settings.customCode;
|
||||
const transformedAnalytics = transformAnalyticsData( settings );
|
||||
handleSaveChanges( 'settings', settings, hasEnabledCustomization, transformedAnalytics );
|
||||
} }
|
||||
>
|
||||
<Stack sx={ { position: 'relative' } } gap={ 2 }>
|
||||
{ isOldElementorVersion && (
|
||||
<UpgradeVersionBanner />
|
||||
) }
|
||||
<Stack>
|
||||
<SettingSection
|
||||
checked={ settings.theme }
|
||||
title={ __( 'Theme', 'elementor' ) }
|
||||
description={ __( 'Only public WordPress themes are supported', 'elementor' ) }
|
||||
settingKey="theme"
|
||||
onSettingChange={ handleToggleChange }
|
||||
notExported={ isImport && ! data?.uploadedData?.manifest.theme }
|
||||
/>
|
||||
|
||||
{ ! isOldExport && (
|
||||
<>
|
||||
<SettingSection
|
||||
title={ __( 'Site settings', 'elementor' ) }
|
||||
hasToggle={ false }
|
||||
>
|
||||
<Stack>
|
||||
<SubSetting
|
||||
label={ __( 'Global colors', 'elementor' ) }
|
||||
settingKey="globalColors"
|
||||
onSettingChange={ handleToggleChange }
|
||||
checked={ settings.globalColors }
|
||||
disabled={ ( isImport && ! data?.uploadedData?.manifest?.[ 'site-settings' ]?.globalColors ) || ! isHighTier() }
|
||||
tooltip={ ! isHighTier() }
|
||||
/>
|
||||
<SubSetting
|
||||
label={ __( 'Global fonts', 'elementor' ) }
|
||||
settingKey="globalFonts"
|
||||
onSettingChange={ handleToggleChange }
|
||||
checked={ settings.globalFonts }
|
||||
disabled={ ( isImport && ! data?.uploadedData?.manifest?.[ 'site-settings' ]?.globalFonts ) || ! isHighTier() }
|
||||
tooltip={ ! isHighTier() }
|
||||
/>
|
||||
<SubSetting
|
||||
label={ __( 'Theme style settings', 'elementor' ) }
|
||||
settingKey="themeStyleSettings"
|
||||
onSettingChange={ handleToggleChange }
|
||||
checked={ settings.themeStyleSettings }
|
||||
disabled={ ( isImport && ! data?.uploadedData?.manifest?.[ 'site-settings' ]?.themeStyleSettings ) || ! isHighTier() }
|
||||
tooltip={ ! isHighTier() }
|
||||
/>
|
||||
</Stack>
|
||||
</SettingSection>
|
||||
|
||||
<SettingSection
|
||||
checked={ settings.generalSettings }
|
||||
title={ __( 'Settings', 'elementor' ) }
|
||||
description={ __( 'Include site identity, background, layout, Lightbox, page transitions, and custom CSS', 'elementor' ) }
|
||||
settingKey="generalSettings"
|
||||
onSettingChange={ handleToggleChange }
|
||||
disabled={ ( isImport && ! data?.uploadedData?.manifest?.[ 'site-settings' ]?.generalSettings ) || ! isHighTier() }
|
||||
tooltip={ ! isHighTier() }
|
||||
/>
|
||||
|
||||
<SettingSection
|
||||
checked={ settings.experiments }
|
||||
title={ __( 'Experiments', 'elementor' ) }
|
||||
description={ __( 'This will apply all experiments that are still active during import', 'elementor' ) }
|
||||
settingKey="experiments"
|
||||
onSettingChange={ handleToggleChange }
|
||||
disabled={ ( isImport && ! data?.uploadedData?.manifest?.experiments ) || ! isHighTier() }
|
||||
tooltip={ ! isHighTier() }
|
||||
/>
|
||||
|
||||
<SettingSection
|
||||
title={ __( 'Custom files', 'elementor' ) }
|
||||
hasToggle={ false }
|
||||
>
|
||||
<Stack>
|
||||
<SubSetting
|
||||
label={ __( 'Custom fonts', 'elementor' ) }
|
||||
settingKey="customFonts"
|
||||
onSettingChange={ handleToggleChange }
|
||||
checked={ settings.customFonts }
|
||||
disabled={ ( isImport && ! data?.uploadedData?.manifest?.[ 'custom-fonts' ] ) || ! isHighTier() }
|
||||
tooltip={ ! isHighTier() }
|
||||
notExported={ isImport && ! data?.uploadedData?.manifest?.[ 'custom-fonts' ] }
|
||||
/>
|
||||
<SubSetting
|
||||
label={ __( 'Custom icons', 'elementor' ) }
|
||||
settingKey="customIcons"
|
||||
onSettingChange={ handleToggleChange }
|
||||
checked={ settings.customIcons }
|
||||
disabled={ ( isImport && ! data?.uploadedData?.manifest?.[ 'custom-icons' ] ) || ! isHighTier() }
|
||||
tooltip={ ! isHighTier() }
|
||||
notExported={ isImport && ! data?.uploadedData?.manifest?.[ 'custom-icons' ] }
|
||||
/>
|
||||
<SubSetting
|
||||
label={ __( 'Custom code', 'elementor' ) }
|
||||
settingKey="customCode"
|
||||
onSettingChange={ handleToggleChange }
|
||||
checked={ settings.customCode }
|
||||
disabled={ ( isImport && ! data?.uploadedData?.manifest?.[ 'custom-code' ] ) || ! isHighTier() }
|
||||
tooltip={ ! isHighTier() }
|
||||
/>
|
||||
</Stack>
|
||||
</SettingSection>
|
||||
</>
|
||||
) }
|
||||
</Stack>
|
||||
<UpgradeNoticeBanner />
|
||||
</Stack>
|
||||
</KitCustomizationDialog>
|
||||
);
|
||||
}
|
||||
|
||||
KitSettingsCustomizationDialog.propTypes = {
|
||||
open: PropTypes.bool.isRequired,
|
||||
isImport: PropTypes.bool,
|
||||
isOldExport: PropTypes.bool,
|
||||
isOldElementorVersion: PropTypes.bool,
|
||||
handleClose: PropTypes.func.isRequired,
|
||||
handleSaveChanges: PropTypes.func.isRequired,
|
||||
data: PropTypes.object.isRequired,
|
||||
};
|
||||
@@ -0,0 +1,196 @@
|
||||
import { Stack } from '@elementor/ui';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import { SettingSection } from './customization-setting-section';
|
||||
import { KitCustomizationDialog } from './kit-customization-dialog';
|
||||
import { UpgradeNoticeBanner } from './upgrade-notice-banner';
|
||||
import { isHighTier } from '../hooks/use-tier';
|
||||
import { ThemeBuilderCustomization } from './theme-builder-customization';
|
||||
import { UpgradeVersionBanner } from './upgrade-version-banner';
|
||||
import { transformValueForAnalytics } from '../utils/analytics-transformer';
|
||||
|
||||
const transformAnalyticsData = ( payload ) => {
|
||||
const transformed = {};
|
||||
|
||||
for ( const [ key, value ] of Object.entries( payload ) ) {
|
||||
transformed[ key ] = transformValueForAnalytics( key, value, [] );
|
||||
}
|
||||
|
||||
return transformed;
|
||||
};
|
||||
|
||||
export const hasTemplatesForExportGroup = ( exportGroup, manifest ) => {
|
||||
if ( ! manifest?.templates ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const exportGroups = elementorAppConfig?.[ 'import-export-customization' ]?.exportGroups || {};
|
||||
|
||||
return Object.values( manifest.templates ).some( ( template ) => {
|
||||
if ( ! template || typeof template !== 'object' || ! template.doc_type ) {
|
||||
return false;
|
||||
}
|
||||
const templateExportGroup = exportGroups[ template.doc_type ];
|
||||
return templateExportGroup === exportGroup;
|
||||
} );
|
||||
};
|
||||
|
||||
export function KitTemplatesCustomizationDialog( {
|
||||
open,
|
||||
handleClose,
|
||||
handleSaveChanges,
|
||||
data,
|
||||
isImport,
|
||||
isOldExport,
|
||||
isOldElementorVersion,
|
||||
} ) {
|
||||
const initialState = data.includes.includes( 'templates' );
|
||||
|
||||
const getState = useCallback( ( parentInitialState ) => {
|
||||
if ( ! data.includes.includes( 'templates' ) ) {
|
||||
return {
|
||||
siteTemplates: {
|
||||
enabled: parentInitialState,
|
||||
},
|
||||
themeBuilder: {
|
||||
enabled: parentInitialState,
|
||||
},
|
||||
globalWidgets: {
|
||||
enabled: parentInitialState,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if ( isImport ) {
|
||||
return {
|
||||
siteTemplates: {
|
||||
enabled: isImport && isOldExport ? true : hasTemplatesForExportGroup( 'site-templates', data?.uploadedData?.manifest ) ?? parentInitialState,
|
||||
},
|
||||
themeBuilder: {
|
||||
enabled: isImport && isOldExport ? true : hasTemplatesForExportGroup( 'theme-builder', data?.uploadedData?.manifest ) ?? parentInitialState,
|
||||
},
|
||||
globalWidgets: {
|
||||
enabled: isImport && isOldExport ? true : hasTemplatesForExportGroup( 'global-widget', data?.uploadedData?.manifest ) ?? parentInitialState,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
siteTemplates: {
|
||||
enabled: data?.customization?.templates?.siteTemplates?.enabled ?? parentInitialState,
|
||||
},
|
||||
themeBuilder: {
|
||||
enabled: data?.customization?.templates?.themeBuilder?.enabled ?? parentInitialState,
|
||||
},
|
||||
globalWidgets: {
|
||||
enabled: data?.customization?.templates?.globalWidgets?.enabled ?? parentInitialState,
|
||||
},
|
||||
};
|
||||
}, [ data.includes, data?.uploadedData?.manifest, data?.customization?.templates, isImport, isOldExport ] );
|
||||
|
||||
const [ templates, setTemplates ] = useState( {} );
|
||||
|
||||
useEffect( () => {
|
||||
if ( open ) {
|
||||
if ( data.customization.templates ) {
|
||||
setTemplates( data.customization.templates );
|
||||
} else {
|
||||
const state = getState( initialState );
|
||||
setTemplates( state );
|
||||
}
|
||||
}
|
||||
}, [ open, data.customization.templates, data?.uploadedData, initialState, getState ] );
|
||||
|
||||
useEffect( () => {
|
||||
if ( open ) {
|
||||
elementorModules?.appsEventTracking?.AppsEventTracking?.sendPageViewsWebsiteTemplates( elementorCommon.eventsManager.config.secondaryLocations.kitLibrary.kitExportCustomizationEdit );
|
||||
}
|
||||
}, [ open ] );
|
||||
|
||||
const handleToggleChange = ( settingKey, isChecked ) => {
|
||||
setTemplates( ( prev ) => ( {
|
||||
...prev,
|
||||
[ settingKey ]: {
|
||||
...prev[ settingKey ],
|
||||
enabled: isChecked,
|
||||
},
|
||||
} ) );
|
||||
};
|
||||
|
||||
return (
|
||||
<KitCustomizationDialog
|
||||
open={ open }
|
||||
title={ __( 'Edit templates', 'elementor' ) }
|
||||
handleClose={ handleClose }
|
||||
handleSaveChanges={ () => {
|
||||
const hasEnabledCustomization = templates.siteTemplates?.enabled || templates.themeBuilder?.enabled || templates.globalWidgets?.enabled;
|
||||
const transformedAnalytics = transformAnalyticsData( templates );
|
||||
handleSaveChanges( 'templates', templates, hasEnabledCustomization, transformedAnalytics );
|
||||
} }
|
||||
minHeight="auto"
|
||||
>
|
||||
<Stack sx={ { position: 'relative' } } gap={ 2 }>
|
||||
{ isOldElementorVersion && (
|
||||
<UpgradeVersionBanner />
|
||||
) }
|
||||
<Stack>
|
||||
{ ! isOldExport && (
|
||||
<SettingSection
|
||||
checked={ templates.siteTemplates?.enabled || false }
|
||||
title={ __( 'Site Templates', 'elementor' ) }
|
||||
settingKey="siteTemplates"
|
||||
onSettingChange={ handleToggleChange }
|
||||
disabled={ ! isHighTier() || ( isImport && ! hasTemplatesForExportGroup( 'site-templates', data?.uploadedData?.manifest ) ) }
|
||||
tooltip={ ! isHighTier() }
|
||||
/>
|
||||
) }
|
||||
|
||||
<ThemeBuilderCustomization
|
||||
state={ templates.themeBuilder }
|
||||
settingKey="themeBuilder"
|
||||
onStateChange={ ( key, newState, mergeMode = false ) => {
|
||||
setTemplates( ( prev ) => {
|
||||
if ( mergeMode ) {
|
||||
return {
|
||||
...prev,
|
||||
[ key ]: { ...prev[ key ], ...newState },
|
||||
};
|
||||
}
|
||||
return {
|
||||
...prev,
|
||||
[ key ]: newState,
|
||||
};
|
||||
} );
|
||||
} }
|
||||
data={ data }
|
||||
disabled={ ! isHighTier() || ( isImport && ! hasTemplatesForExportGroup( 'theme-builder', data?.uploadedData?.manifest ) ) }
|
||||
tooltip={ ! isHighTier() }
|
||||
/>
|
||||
|
||||
{ ! isOldExport && (
|
||||
<SettingSection
|
||||
checked={ templates.globalWidgets?.enabled || false }
|
||||
title="Global Widgets"
|
||||
settingKey="globalWidgets"
|
||||
onSettingChange={ handleToggleChange }
|
||||
disabled={ ! isHighTier() || ( isImport && ! hasTemplatesForExportGroup( 'global-widget', data?.uploadedData?.manifest ) ) }
|
||||
tooltip={ ! isHighTier() }
|
||||
/>
|
||||
) }
|
||||
</Stack>
|
||||
<UpgradeNoticeBanner />
|
||||
</Stack>
|
||||
</KitCustomizationDialog>
|
||||
);
|
||||
}
|
||||
|
||||
KitTemplatesCustomizationDialog.propTypes = {
|
||||
open: PropTypes.bool.isRequired,
|
||||
isImport: PropTypes.bool,
|
||||
isOldExport: PropTypes.bool,
|
||||
isOldElementorVersion: PropTypes.bool,
|
||||
handleClose: PropTypes.func.isRequired,
|
||||
handleSaveChanges: PropTypes.func.isRequired,
|
||||
data: PropTypes.object.isRequired,
|
||||
};
|
||||
@@ -0,0 +1,257 @@
|
||||
import { Stack, Box, Typography, Switch, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Radio, FormControlLabel, Link, SvgIcon, Alert, AlertTitle } from '@elementor/ui';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useState, useEffect } from 'react';
|
||||
import * as PropTypes from 'prop-types';
|
||||
import { UpgradeTooltip } from './upgrade-tooltip';
|
||||
|
||||
const ExternalLinkIcon = ( props ) => {
|
||||
return (
|
||||
<SvgIcon
|
||||
viewBox="0 0 18 18"
|
||||
sx={ {
|
||||
fontSize: 16,
|
||||
color: 'info.light',
|
||||
} }
|
||||
{ ...props }
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M11 1C11 0.585786 11.3358 0.25 11.75 0.25H16.75C17.1642 0.25 17.5 0.585786 17.5 1V6C17.5 6.41421 17.1642 6.75 16.75 6.75C16.3358 6.75 16 6.41421 16 6V2.81066L7.28033 11.5303C6.98744 11.8232 6.51256 11.8232 6.21967 11.5303C5.92678 11.2374 5.92678 10.7626 6.21967 10.4697L14.9393 1.75H11.75C11.3358 1.75 11 1.41421 11 1ZM0.805456 4.05546C1.32118 3.53973 2.02065 3.25 2.75 3.25H7.75C8.16421 3.25 8.5 3.58579 8.5 4C8.5 4.41421 8.16421 4.75 7.75 4.75H2.75C2.41848 4.75 2.10054 4.8817 1.86612 5.11612C1.6317 5.35054 1.5 5.66848 1.5 6V15C1.5 15.3315 1.6317 15.6495 1.86612 15.8839C2.10054 16.1183 2.41848 16.25 2.75 16.25H11.75C12.0815 16.25 12.3995 16.1183 12.6339 15.8839C12.8683 15.6495 13 15.3315 13 15V10C13 9.58579 13.3358 9.25 13.75 9.25C14.1642 9.25 14.5 9.58579 14.5 10V15C14.5 15.7293 14.2103 16.4288 13.6945 16.9445C13.1788 17.4603 12.4793 17.75 11.75 17.75H2.75C2.02065 17.75 1.32118 17.4603 0.805456 16.9445C0.289731 16.4288 0 15.7293 0 15V6C0 5.27065 0.289731 4.57118 0.805456 4.05546Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</SvgIcon>
|
||||
);
|
||||
};
|
||||
|
||||
export function ThemeBuilderCustomization( { state, settingKey, onStateChange, data, disabled, tooltip = false } ) {
|
||||
const isImport = data.hasOwnProperty( 'uploadedData' );
|
||||
|
||||
const [ conflicts, setConflicts ] = useState( [] );
|
||||
const [ loading, setLoading ] = useState( false );
|
||||
|
||||
useEffect( () => {
|
||||
if ( state?.enabled && isImport ) {
|
||||
loadConflicts();
|
||||
} else {
|
||||
setConflicts( [] );
|
||||
setLoading( false );
|
||||
}
|
||||
}, [ state?.enabled, isImport, data ] );
|
||||
|
||||
const loadConflicts = async () => {
|
||||
setLoading( true );
|
||||
try {
|
||||
const actualConflicts = data?.uploadedData?.conflicts ? Object.entries( data.uploadedData.conflicts ) : [];
|
||||
|
||||
const formattedConflicts = actualConflicts.map( ( [ importedTemplateId, conflictsList ] ) => {
|
||||
const importedTemplate = data?.uploadedData?.manifest?.templates?.[ importedTemplateId ];
|
||||
const firstConflict = conflictsList[ 0 ];
|
||||
|
||||
return {
|
||||
template_id: firstConflict.template_id,
|
||||
template_name: firstConflict.template_title,
|
||||
edit_url: firstConflict.edit_url,
|
||||
|
||||
imported_template_id: parseInt( importedTemplateId ),
|
||||
imported_template_name: importedTemplate?.title || 'Unknown Template',
|
||||
|
||||
location: importedTemplate?.location || '',
|
||||
location_label: getTemplateTypeLabel( importedTemplateId ),
|
||||
};
|
||||
} );
|
||||
|
||||
setConflicts( formattedConflicts );
|
||||
|
||||
if ( ! state?.overrideConditions || 0 === state.overrideConditions.length ) {
|
||||
const defaultOverrides = formattedConflicts.map( ( conflict ) => conflict.imported_template_id );
|
||||
|
||||
onStateChange( settingKey, {
|
||||
...state,
|
||||
overrideConditions: defaultOverrides,
|
||||
} );
|
||||
}
|
||||
} catch ( error ) {
|
||||
setConflicts( [] );
|
||||
} finally {
|
||||
setLoading( false );
|
||||
}
|
||||
};
|
||||
|
||||
const getTemplateTypeLabel = ( templateId ) => {
|
||||
const template = data?.uploadedData?.manifest?.templates?.[ templateId ];
|
||||
if ( ! template ) {
|
||||
return 'Unknown Template';
|
||||
}
|
||||
|
||||
const templateType = template.doc_type;
|
||||
const summaryTitle = elementorAppConfig?.[ 'import-export-customization' ]?.summaryTitles?.templates?.[ templateType ];
|
||||
|
||||
return summaryTitle?.single || templateType;
|
||||
};
|
||||
|
||||
const handleToggleEnabled = () => {
|
||||
const newState = {
|
||||
enabled: ! state?.enabled,
|
||||
};
|
||||
|
||||
if ( isImport ) {
|
||||
newState.overrideConditions = state?.enabled ? [] : ( state?.overrideConditions || [] );
|
||||
}
|
||||
|
||||
onStateChange( settingKey, newState );
|
||||
};
|
||||
|
||||
const handleConflictChoice = ( location, choice, importedTemplateId ) => {
|
||||
const currentOverrides = state?.overrideConditions || [];
|
||||
let newOverrides;
|
||||
|
||||
if ( 'imported' === choice ) {
|
||||
if ( ! currentOverrides.includes( importedTemplateId ) ) {
|
||||
newOverrides = [ ...currentOverrides, importedTemplateId ];
|
||||
} else {
|
||||
newOverrides = currentOverrides;
|
||||
}
|
||||
} else {
|
||||
newOverrides = currentOverrides.filter( ( templateId ) => templateId !== importedTemplateId );
|
||||
}
|
||||
|
||||
onStateChange( settingKey, {
|
||||
...state,
|
||||
overrideConditions: newOverrides,
|
||||
} );
|
||||
};
|
||||
|
||||
const getConflictChoice = ( importedTemplateId ) => {
|
||||
const overrides = state?.overrideConditions || [];
|
||||
const hasOverride = overrides.includes( importedTemplateId );
|
||||
return hasOverride ? 'imported' : 'current';
|
||||
};
|
||||
|
||||
const renderConflictTable = () => {
|
||||
if ( loading ) {
|
||||
return (
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{ __( 'Checking for conflicts...', 'elementor-pro' ) }
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack spacing={ 2 }>
|
||||
<Alert severity="warning">
|
||||
<AlertTitle key="title">{ __( 'Conflicted part', 'elementor-pro' ) }</AlertTitle>
|
||||
{ __( 'Some parts are in conflict. Choose which one you want to assign.', 'elementor-pro' ) }
|
||||
</Alert>
|
||||
|
||||
<TableContainer component={ Box } sx={ {
|
||||
maxWidth: '100%',
|
||||
border: 1,
|
||||
borderRadius: 1,
|
||||
borderColor: 'action.focus',
|
||||
} }>
|
||||
<Table size="small">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>{ __( 'Conflicted part', 'elementor-pro' ) }</TableCell>
|
||||
<TableCell>{ __( 'Current site part', 'elementor-pro' ) }</TableCell>
|
||||
<TableCell>{ __( 'Imported template part', 'elementor-pro' ) }</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{ conflicts.map( ( conflict, index ) => (
|
||||
<TableRow key={ index }>
|
||||
<TableCell>
|
||||
<Typography variant="body2" fontWeight="medium">
|
||||
{ getTemplateTypeLabel( conflict.imported_template_id ) }
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Radio
|
||||
checked={ 'current' === getConflictChoice( conflict.imported_template_id, conflict.location ) }
|
||||
onChange={ () => handleConflictChoice( conflict.location, 'current', conflict.imported_template_id ) }
|
||||
size="small"
|
||||
/>
|
||||
}
|
||||
label={ conflict.template_name }
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Radio
|
||||
checked={ 'imported' === getConflictChoice( conflict.imported_template_id, conflict.location ) }
|
||||
onChange={ () => handleConflictChoice( conflict.location, 'imported', conflict.imported_template_id ) }
|
||||
size="small"
|
||||
/>
|
||||
}
|
||||
label={ conflict.imported_template_name }
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) ) }
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={ { mb: 3, border: 1, borderRadius: 1, borderColor: 'action.focus', p: 2.5 } }>
|
||||
<Box sx={ { display: 'flex', justifyContent: 'space-between', alignItems: 'center' } }>
|
||||
<Stack spacing={ 1 }>
|
||||
<Typography variant="h6">
|
||||
{ __( 'Theme builder', 'elementor-pro' ) }
|
||||
</Typography>
|
||||
<Link
|
||||
href={ elementorAppConfig.base_url + '#/site-editor/templates' }
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
color="info.light"
|
||||
underline="hover"
|
||||
sx={ {
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: 0.5,
|
||||
} }
|
||||
>
|
||||
{ __( 'Check your themes builder', 'elementor-pro' ) }
|
||||
<ExternalLinkIcon />
|
||||
</Link>
|
||||
</Stack>
|
||||
<UpgradeTooltip disabled={ disabled } tooltip={ tooltip }>
|
||||
<Switch
|
||||
data-testid={ `${ settingKey }-switch` }
|
||||
checked={ state?.enabled || false }
|
||||
disabled={ disabled }
|
||||
onChange={ handleToggleEnabled }
|
||||
color="info"
|
||||
size="medium"
|
||||
sx={ {
|
||||
alignSelf: 'center',
|
||||
...( disabled && tooltip && { cursor: 'pointer' } ),
|
||||
} }
|
||||
/>
|
||||
</UpgradeTooltip>
|
||||
</Box>
|
||||
|
||||
{ state?.enabled && isImport && 0 < conflicts.length && (
|
||||
<Box sx={ { mt: 1 } }>
|
||||
{ renderConflictTable() }
|
||||
</Box>
|
||||
) }
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
ThemeBuilderCustomization.propTypes = {
|
||||
state: PropTypes.object.isRequired,
|
||||
settingKey: PropTypes.string.isRequired,
|
||||
onStateChange: PropTypes.func.isRequired,
|
||||
data: PropTypes.object.isRequired,
|
||||
disabled: PropTypes.bool,
|
||||
tooltip: PropTypes.bool,
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
import { Box, Button, Typography, Paper } from '@elementor/ui';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { isHighTier } from '../hooks/use-tier';
|
||||
|
||||
export function UpgradeNoticeBanner() {
|
||||
if ( isHighTier() ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper
|
||||
sx={ {
|
||||
position: 'sticky',
|
||||
bottom: 0,
|
||||
marginLeft: -3,
|
||||
marginRight: -3,
|
||||
zIndex: 1000,
|
||||
py: 2,
|
||||
px: 3,
|
||||
} }
|
||||
>
|
||||
<Paper
|
||||
elevation={ 0 }
|
||||
color="promotion"
|
||||
sx={ {
|
||||
borderRadius: 1,
|
||||
p: 2,
|
||||
} }
|
||||
>
|
||||
<Box sx={ { display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 2 } }>
|
||||
<Box sx={ { flex: 1, minWidth: 0 } }>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{ __( 'Take control of your workflow. The Expert plan lets you decide exactly what\'s included in your export/import kits, from themes to experiments so nothing gets left behind.', 'elementor' ) }
|
||||
</Typography>
|
||||
</Box>
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="promotion"
|
||||
onClick={ () => window.open( 'https://go.elementor.com/go-pro-import-export', '_blank' ) }
|
||||
startIcon={ <span className="eicon-upgrade-crown"></span> }
|
||||
sx={ { flexShrink: 0, whiteSpace: 'nowrap' } }
|
||||
>
|
||||
{ __( 'Check Expert plan', 'elementor' ) }
|
||||
</Button>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Tooltip, Box } from '@elementor/ui';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import * as PropTypes from 'prop-types';
|
||||
|
||||
export const UpgradeTooltip = ( { children, disabled = false, tooltip = false, ...props } ) => {
|
||||
if ( disabled && tooltip ) {
|
||||
return (
|
||||
<Tooltip
|
||||
title={ __( 'Upgrade your plan to choose which elements to adjust.', 'elementor' ) }
|
||||
placement="top"
|
||||
arrow
|
||||
componentsProps={ {
|
||||
tooltip: {
|
||||
sx: {
|
||||
maxWidth: 200,
|
||||
fontSize: '12px',
|
||||
fontWeight: 500,
|
||||
lineHeight: 1.4,
|
||||
textAlign: 'center',
|
||||
backgroundColor: 'background.paper',
|
||||
color: 'text.secondary',
|
||||
padding: 1.5,
|
||||
boxShadow: '0 4px 20px rgba(0, 0, 0, 0.15)',
|
||||
},
|
||||
},
|
||||
arrow: {
|
||||
sx: {
|
||||
fontSize: '1.2rem',
|
||||
color: 'background.paper',
|
||||
filter: 'drop-shadow(0 2px 8px rgba(0, 0, 0, 0.15))',
|
||||
'&::before': {
|
||||
backgroundColor: 'background.paper',
|
||||
},
|
||||
},
|
||||
},
|
||||
} }
|
||||
{ ...props }
|
||||
>
|
||||
<Box component="span">{ children }</Box>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
return children;
|
||||
};
|
||||
|
||||
UpgradeTooltip.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
disabled: PropTypes.bool,
|
||||
tooltip: PropTypes.bool,
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
import { Stack, SvgIcon, Typography, Paper, Button } from '@elementor/ui';
|
||||
|
||||
export function UpgradeVersionBanner() {
|
||||
return (
|
||||
<Paper
|
||||
color="info"
|
||||
elevation={ 0 }
|
||||
variant="elevation"
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
sx={ {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
gap: '5px',
|
||||
py: 1.5,
|
||||
px: 2.5,
|
||||
} }
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
sx={ {
|
||||
alignItems: 'center',
|
||||
gap: '5px',
|
||||
} }
|
||||
>
|
||||
<SvgIcon
|
||||
viewBox="0 0 22 22"
|
||||
sx={ {
|
||||
fontSize: 16,
|
||||
color: 'info.light',
|
||||
} }
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M4.58268 4.35352C4.5219 4.35352 4.46361 4.37766 4.42064 4.42064C4.37766 4.46361 4.35352 4.5219 4.35352 4.58268V6.64518H6.64518V4.35352H4.58268ZM4.58268 2.97852C4.15723 2.97852 3.7492 3.14753 3.44837 3.44837C3.14753 3.7492 2.97852 4.15723 2.97852 4.58268V17.416C2.97852 17.8415 3.14753 18.2495 3.44837 18.5503C3.74921 18.8512 4.15723 19.0202 4.58268 19.0202H17.416C17.8415 19.0202 18.2495 18.8512 18.5503 18.5503C18.8512 18.2495 19.0202 17.8415 19.0202 17.416V4.58268C19.0202 4.15723 18.8512 3.74921 18.5503 3.44837C18.2495 3.14753 17.8415 2.97852 17.416 2.97852H4.58268ZM8.02018 4.35352V6.64518H17.6452V4.58268C17.6452 4.5219 17.621 4.46361 17.5781 4.42064C17.5351 4.37766 17.4768 4.35352 17.416 4.35352H8.02018ZM17.6452 8.02018H4.35352V17.416C4.35352 17.4768 4.37766 17.5351 4.42064 17.5781C4.46361 17.621 4.5219 17.6452 4.58268 17.6452H17.416C17.4768 17.6452 17.5351 17.621 17.5781 17.5781C17.621 17.5351 17.6452 17.4768 17.6452 17.416V8.02018Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</SvgIcon>
|
||||
<Typography variant="body2">
|
||||
{ __( 'You’re using an older Elementor version. Update for full customization.', 'elementor' ) }
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={ () => window.open( elementorAppConfig[ 'import-export-customization' ]?.upgradeVersionUrl, '_blank' ) }
|
||||
color="info"
|
||||
>
|
||||
{ __( 'Update version', 'elementor' ) }
|
||||
</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
export function useCustomPostTypes( { include = [] } = {} ) {
|
||||
const [ customPostTypes, setCustomPostTypes ] = useState( [] );
|
||||
|
||||
useEffect( () => {
|
||||
const cpt = Object.assign( {}, elementorAppConfig[ 'import-export-customization' ]?.summaryTitles?.content?.customPostTypes || {} );
|
||||
|
||||
if ( include.length ) {
|
||||
Object.entries( elementorAppConfig[ 'import-export-customization' ]?.summaryTitles?.content || {} ).forEach( ( [ postType, post ] ) => {
|
||||
if ( include.includes( postType ) ) {
|
||||
cpt[ postType ] = post;
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
if ( Object.keys( cpt ).length ) {
|
||||
setCustomPostTypes( Object.entries( cpt ).map( ( [ postType, post ] ) => ( {
|
||||
value: postType,
|
||||
label: post.single,
|
||||
} ) ) );
|
||||
}
|
||||
}, [] );
|
||||
|
||||
return { customPostTypes };
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useCustomPostTypes } from './use-custom-post-types';
|
||||
|
||||
export function useKitCustomizationCustomPostTypes( { data } ) {
|
||||
const isImport = data?.hasOwnProperty( 'uploadedData' );
|
||||
|
||||
const { customPostTypes: builtInCustomPostTypes } = useCustomPostTypes( { include: [ 'post' ] } );
|
||||
|
||||
const customPostTypes = useMemo( () => {
|
||||
if ( ! isImport ) {
|
||||
return builtInCustomPostTypes;
|
||||
}
|
||||
|
||||
const customPostTypesTitles = Object.values( data?.uploadedData?.manifest?.[ 'custom-post-type-title' ] || {} ).map( ( postType ) => {
|
||||
return {
|
||||
value: postType.name,
|
||||
label: postType.label,
|
||||
};
|
||||
} );
|
||||
|
||||
if ( ! customPostTypesTitles.some( ( postType ) => 'post' === postType.value ) ) {
|
||||
customPostTypesTitles.push( {
|
||||
value: 'post',
|
||||
label: 'Post',
|
||||
} );
|
||||
}
|
||||
|
||||
const wpContent = data?.uploadedData?.manifest?.[ 'wp-content' ] || {};
|
||||
const content = data?.uploadedData?.manifest?.content || {};
|
||||
|
||||
return customPostTypesTitles.filter( ( postType ) => {
|
||||
const postTypeValue = postType.value;
|
||||
|
||||
const wpContentObject = wpContent[ postTypeValue ];
|
||||
const isInWpContent = wpContentObject && 'object' === typeof wpContentObject && Object.keys( wpContentObject ).length > 0;
|
||||
|
||||
const contentObject = content[ postTypeValue ];
|
||||
const isInElementorContent = contentObject && 'object' === typeof contentObject && Object.keys( contentObject ).length > 0;
|
||||
|
||||
return isInWpContent || isInElementorContent;
|
||||
} );
|
||||
}, [ isImport, data?.uploadedData, builtInCustomPostTypes ] );
|
||||
|
||||
return {
|
||||
customPostTypes,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { useMemo } from 'react';
|
||||
import { usePages } from './use-pages';
|
||||
|
||||
export function useKitCustomizationPages( { data, open } ) {
|
||||
const isImport = data?.hasOwnProperty( 'uploadedData' );
|
||||
|
||||
const { isLoading, pageOptions: loadedPagesOptions, isLoaded } = usePages( { skipLoading: isImport || ! open } );
|
||||
|
||||
const pageOptions = useMemo( () => {
|
||||
if ( ! isImport ) {
|
||||
return loadedPagesOptions;
|
||||
}
|
||||
|
||||
const elementorPages = Object.entries( data?.uploadedData?.manifest?.content?.page || {} ).map( ( [ id, page ] ) => {
|
||||
return { value: id, label: page.title };
|
||||
} );
|
||||
|
||||
const wpPages = Object.entries( data?.uploadedData?.manifest?.[ 'wp-content' ]?.page || {} ).map( ( [ id, page ] ) => {
|
||||
return { value: id, label: page.title };
|
||||
} );
|
||||
|
||||
return [ ...elementorPages, ...wpPages ];
|
||||
}, [ loadedPagesOptions, isImport, data?.uploadedData ] );
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
pageOptions,
|
||||
isLoaded,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useTaxonomies } from './use-taxonomies';
|
||||
|
||||
export function useKitCustomizationTaxonomies( { data, open } ) {
|
||||
const isImport = data?.hasOwnProperty( 'uploadedData' );
|
||||
|
||||
const { isLoading, taxonomyOptions: loadedTaxonomyOptions, isLoaded } = useTaxonomies( {
|
||||
skipLoading: isImport || ! open, exclude: [ 'nav_menu' ],
|
||||
} );
|
||||
|
||||
const taxonomyOptions = useMemo( () => {
|
||||
if ( ! isImport ) {
|
||||
return loadedTaxonomyOptions;
|
||||
}
|
||||
|
||||
const taxonomiesMap = {};
|
||||
|
||||
Object.values( data?.uploadedData?.manifest?.taxonomies || {} ).forEach( ( taxonomiesListForPostType ) => {
|
||||
taxonomiesListForPostType.forEach( ( taxonomy ) => {
|
||||
const taxonomyObj = 'string' === typeof taxonomy
|
||||
// BC For Old Export
|
||||
? { name: taxonomy, label: taxonomy.split( '_' ).join( ' ' ) }
|
||||
: taxonomy;
|
||||
|
||||
if ( ! taxonomiesMap[ taxonomyObj.name ] ) {
|
||||
taxonomiesMap[ taxonomyObj.name ] = { value: taxonomyObj.name, label: taxonomyObj.label };
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
||||
return Object.values( taxonomiesMap );
|
||||
}, [ data?.uploadedData, isImport, loadedTaxonomyOptions ] );
|
||||
|
||||
return {
|
||||
taxonomyOptions,
|
||||
isLoading,
|
||||
isLoaded,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
||||
|
||||
export function usePages( { skipLoading = false } = {} ) {
|
||||
const [ pages, setPages ] = useState( [] );
|
||||
const [ isLoading, setIsLoading ] = useState( false );
|
||||
const [ error, setError ] = useState( null );
|
||||
const [ hasMorePages, setHasMorePages ] = useState( true );
|
||||
const isLoaded = useRef( null );
|
||||
|
||||
const fetchAllPages = useCallback( async () => {
|
||||
if ( isLoaded.current ) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoading( true );
|
||||
setError( null );
|
||||
setPages( [] );
|
||||
setHasMorePages( true );
|
||||
|
||||
let currentPage = 1;
|
||||
let allPages = [];
|
||||
|
||||
while ( hasMorePages || 1 === currentPage ) {
|
||||
const baseUrl = new URL( elementorCommon.config.urls.rest, window.location.origin );
|
||||
|
||||
const isPlainPermalink = 'index.php' === baseUrl.pathname.replace( /\//g, '' );
|
||||
|
||||
baseUrl.pathname = isPlainPermalink ? baseUrl.pathname : `${ baseUrl.pathname }wp/v2/pages`;
|
||||
if ( isPlainPermalink ) {
|
||||
baseUrl.searchParams.set( 'rest_route', '/wp/v2/pages' );
|
||||
}
|
||||
baseUrl.searchParams.append( 'page', 1 );
|
||||
baseUrl.searchParams.append( 'per_page', 100 );
|
||||
baseUrl.searchParams.append( '_embed', '' );
|
||||
|
||||
const response = await fetch( baseUrl.toString(), {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-WP-Nonce': window.wpApiSettings?.nonce || '',
|
||||
},
|
||||
} );
|
||||
|
||||
if ( ! response.ok ) {
|
||||
throw new Error( `HTTP error! status: ${ response.status }` );
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
const totalPages = parseInt( response.headers.get( 'X-WP-TotalPages' ) || '1' );
|
||||
|
||||
allPages = [ ...allPages, ...data ];
|
||||
|
||||
if ( totalPages <= currentPage ) {
|
||||
setHasMorePages( false );
|
||||
break;
|
||||
}
|
||||
|
||||
currentPage++;
|
||||
}
|
||||
|
||||
setPages( allPages );
|
||||
isLoaded.current = true;
|
||||
} catch ( err ) {
|
||||
setError( err.message );
|
||||
} finally {
|
||||
setIsLoading( false );
|
||||
}
|
||||
}, [ hasMorePages ] );
|
||||
|
||||
const refreshPages = useCallback( () => {
|
||||
fetchAllPages();
|
||||
}, [ fetchAllPages ] );
|
||||
|
||||
const pageOptions = useMemo( () => {
|
||||
return pages.map( ( page ) => ( { value: page.id, label: page.title.rendered } ) );
|
||||
}, [ pages ] );
|
||||
|
||||
useEffect( () => {
|
||||
if ( ! skipLoading ) {
|
||||
fetchAllPages();
|
||||
}
|
||||
}, [ skipLoading ] );
|
||||
|
||||
return {
|
||||
pages,
|
||||
isLoading,
|
||||
error,
|
||||
refreshPages,
|
||||
pageOptions,
|
||||
isLoaded: isLoaded.current,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
|
||||
|
||||
const fetchTaxonomies = async () => {
|
||||
const requestUrl = `${ elementorCommon.config.urls.rest }wp/v2/taxonomies`;
|
||||
|
||||
const response = await fetch( requestUrl, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-WP-Nonce': window.wpApiSettings?.nonce || '',
|
||||
},
|
||||
} );
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if ( ! response.ok ) {
|
||||
const errorMessage = result?.data?.message || `HTTP error! with the following code: ${ result?.data?.code }`;
|
||||
throw new Error( errorMessage );
|
||||
}
|
||||
|
||||
return Object.values( result );
|
||||
};
|
||||
|
||||
export function useTaxonomies( { skipLoading = false, exclude = [] } = {} ) {
|
||||
const [ taxonomies, setTaxonomies ] = useState( [] );
|
||||
const [ isLoading, setIsLoading ] = useState( false );
|
||||
const [ error, setError ] = useState( null );
|
||||
const isLoaded = useRef( null );
|
||||
|
||||
const fetchAllTaxonomies = useCallback( async () => {
|
||||
if ( isLoaded.current ) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoading( true );
|
||||
setError( null );
|
||||
|
||||
const data = await fetchTaxonomies();
|
||||
|
||||
setTaxonomies(
|
||||
exclude.length
|
||||
? data.filter( ( taxonomy ) => ! exclude.includes( taxonomy.slug ) )
|
||||
: data,
|
||||
);
|
||||
isLoaded.current = true;
|
||||
} catch ( err ) {
|
||||
setError( err.message );
|
||||
} finally {
|
||||
setIsLoading( false );
|
||||
}
|
||||
}, [] );
|
||||
|
||||
const refreshTaxonomies = useCallback( () => {
|
||||
fetchAllTaxonomies();
|
||||
}, [ fetchAllTaxonomies ] );
|
||||
|
||||
const taxonomyOptions = useMemo( () => {
|
||||
return taxonomies.map( ( taxonomy ) => ( { value: taxonomy.slug, label: taxonomy.name } ) );
|
||||
}, [ taxonomies ] );
|
||||
|
||||
useEffect( () => {
|
||||
if ( ! skipLoading ) {
|
||||
fetchAllTaxonomies();
|
||||
}
|
||||
}, [ skipLoading ] );
|
||||
|
||||
return {
|
||||
taxonomies,
|
||||
isLoading,
|
||||
error,
|
||||
refreshTaxonomies,
|
||||
taxonomyOptions,
|
||||
isLoaded: isLoaded.current,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export const isHighTier = () => {
|
||||
try {
|
||||
return 'expert' === elementorCommon?.config?.library_connect?.plan_type ||
|
||||
'agency' === elementorCommon?.config?.library_connect?.plan_type;
|
||||
} catch ( error ) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
import { KitContentCustomizationDialog } from './components/kit-content-customization-dialog';
|
||||
import { KitTemplatesCustomizationDialog } from './components/kit-templates-customization-dialog';
|
||||
import { KitSettingsCustomizationDialog } from './components/kit-settings-customization-dialog';
|
||||
|
||||
export default class Module {
|
||||
constructor() {
|
||||
this.registerCustomizationDialogs();
|
||||
}
|
||||
|
||||
registerCustomizationDialogs() {
|
||||
if ( ! elementorCommon?.config?.experimentalFeatures?.[ 'import-export-customization' ] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const registry = window.elementorModules?.importExport?.customizationDialogsRegistry;
|
||||
if ( ! registry ) {
|
||||
return;
|
||||
}
|
||||
|
||||
registry.register( {
|
||||
key: 'content',
|
||||
title: 'Content Dialog',
|
||||
component: KitContentCustomizationDialog,
|
||||
} );
|
||||
|
||||
registry.register( {
|
||||
key: 'templates',
|
||||
title: 'Templates Dialog',
|
||||
component: KitTemplatesCustomizationDialog,
|
||||
} );
|
||||
|
||||
registry.register( {
|
||||
key: 'settings',
|
||||
title: 'Settings Dialog',
|
||||
component: KitSettingsCustomizationDialog,
|
||||
} );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
export const ANALYTICS_TRANSFORM_RULES = {
|
||||
STRING: ( value ) => value,
|
||||
BOOLEAN: ( value ) => value,
|
||||
EMPTY_ARRAY: () => 'None',
|
||||
FULL_ARRAY: () => 'All',
|
||||
PARTIAL_ARRAY: () => 'Partial',
|
||||
};
|
||||
|
||||
export const getTotalAvailableCount = ( key, optionsArray ) => {
|
||||
const optionsMap = optionsArray.reduce( ( map, { key: optionKey, options } ) => {
|
||||
map[ optionKey ] = options.length;
|
||||
return map;
|
||||
}, {} );
|
||||
|
||||
return optionsMap[ key ] || 0;
|
||||
};
|
||||
|
||||
export const transformValueForAnalytics = ( key, value, optionsArray ) => {
|
||||
if ( 'string' === typeof value || 'boolean' === typeof value ) {
|
||||
return ANALYTICS_TRANSFORM_RULES[ ( typeof value ).toUpperCase() ]( value );
|
||||
}
|
||||
|
||||
if ( 'object' === typeof value && value !== null && ! Array.isArray( value ) && 'enabled' in value ) {
|
||||
return value.enabled;
|
||||
}
|
||||
|
||||
if ( Array.isArray( value ) ) {
|
||||
if ( 0 === value.length ) {
|
||||
return ANALYTICS_TRANSFORM_RULES.EMPTY_ARRAY();
|
||||
}
|
||||
|
||||
const totalAvailable = getTotalAvailableCount( key, optionsArray );
|
||||
const isFullSelection = value.length === totalAvailable;
|
||||
|
||||
return isFullSelection
|
||||
? ANALYTICS_TRANSFORM_RULES.FULL_ARRAY()
|
||||
: ANALYTICS_TRANSFORM_RULES.PARTIAL_ARRAY();
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
namespace ElementorPro\Core\App\Modules\ImportExportCustomization;
|
||||
|
||||
use Elementor\Core\Base\Module as BaseModule;
|
||||
use ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Export;
|
||||
use ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Import;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Module extends BaseModule {
|
||||
|
||||
private $export_runners = [
|
||||
'site-settings' => Export\Site_Settings::class,
|
||||
'plugins' => Export\Plugins::class,
|
||||
'templates' => Export\Templates::class,
|
||||
'taxonomies' => Export\Taxonomies::class,
|
||||
];
|
||||
|
||||
private $import_runners = [
|
||||
'site-settings' => Import\Site_Settings::class,
|
||||
'plugins' => Import\Plugins::class,
|
||||
'templates' => Import\Templates::class,
|
||||
'taxonomies' => Import\Taxonomies::class,
|
||||
'elementor-content' => Import\Elementor_Content::class,
|
||||
];
|
||||
|
||||
public function get_name() {
|
||||
return 'import-export-customization';
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
$this->add_actions();
|
||||
}
|
||||
|
||||
private function add_actions() {
|
||||
add_filter( 'elementor/import-export-customization/export/site-settings/customization', [ $this, 'export_site_settings_customization' ], 10, 4 );
|
||||
add_filter( 'elementor/import-export-customization/import/site-settings/customization', [ $this, 'import_site_settings_customization' ], 10, 5 );
|
||||
|
||||
add_filter( 'elementor/import-export-customization/export/templates/customization', [ $this, 'export_templates_customization' ], 10, 4 );
|
||||
add_filter( 'elementor/import-export-customization/import/templates/customization', [ $this, 'import_templates_customization' ], 10, 5 );
|
||||
|
||||
add_filter( 'elementor/import-export-customization/export/taxonomies/customization', [ $this, 'export_taxonomies_customization' ], 10, 4 );
|
||||
add_filter( 'elementor/import-export-customization/import/taxonomies/customization', [ $this, 'import_taxonomies_customization' ], 10, 5 );
|
||||
|
||||
add_filter( 'elementor/import-export-customization/import/elementor-content/customization', [ $this, 'import_elementor_content_customization' ], 10, 5 );
|
||||
|
||||
add_filter( 'elementor/import-export-customization/elementor-content/post-types/customization', [ $this, 'elementor_content_post_types' ], 10, 2 );
|
||||
add_filter( 'elementor/import-export-customization/wp-content/post-types/customization', [ $this, 'wp_content_post_types' ], 10, 2 );
|
||||
add_filter( 'elementor/import-export-customization/export/elementor-content/query-args/customization', [ $this, 'export_elementor_content_filter_query_args' ], 10, 3 );
|
||||
add_filter( 'elementor/import-export-customization/export/wp-content/query-args/customization', [ $this, 'wp_content_filter_query_args' ], 10, 3 );
|
||||
add_filter( 'elementor/import-export-customization/import/wp-content/query-args/customization', [ $this, 'wp_content_filter_query_args' ], 10, 3 );
|
||||
}
|
||||
|
||||
public function export_site_settings_customization( $result, array $data, array $customization, $runner ) {
|
||||
return $this->delegate_to_export_runner( 'site-settings', $result, $data, $customization, $runner );
|
||||
}
|
||||
|
||||
public function import_site_settings_customization( $result, array $data, array $imported_data, array $customization, $runner ) {
|
||||
return $this->delegate_to_import_runner( 'site-settings', $result, $data, $imported_data, $customization, $runner );
|
||||
}
|
||||
|
||||
public function export_templates_customization( $result, array $data, array $customization, $runner ) {
|
||||
return $this->delegate_to_export_runner( 'templates', $result, $data, $customization, $runner );
|
||||
}
|
||||
|
||||
public function import_templates_customization( $result, array $data, array $imported_data, array $customization, $runner ) {
|
||||
return $this->delegate_to_import_runner( 'templates', $result, $data, $imported_data, $customization, $runner );
|
||||
}
|
||||
|
||||
public function export_taxonomies_customization( $result, array $data, $customization, $runner ) {
|
||||
return $this->delegate_to_export_runner( 'taxonomies', $result, $data, $customization, $runner );
|
||||
}
|
||||
|
||||
public function import_taxonomies_customization( $result, array $data, array $imported_data, $customization, $runner ) {
|
||||
return $this->delegate_to_import_runner( 'taxonomies', $result, $data, $imported_data, $customization, $runner );
|
||||
}
|
||||
|
||||
public function import_elementor_content_customization( $result, array $data, array $imported_data, $customization, $runner ) {
|
||||
return $this->delegate_to_import_runner( 'elementor-content', $result, $data, $imported_data, $customization, $runner );
|
||||
}
|
||||
|
||||
public function elementor_content_post_types( $post_types, $customization ) {
|
||||
$selected_pages = $customization['pages'] ?? null;
|
||||
|
||||
if ( null !== $selected_pages && empty( $selected_pages ) ) {
|
||||
return array_filter( $post_types, function ( $post_type ) {
|
||||
return 'page' !== $post_type;
|
||||
} );
|
||||
}
|
||||
|
||||
return $post_types;
|
||||
}
|
||||
|
||||
public function export_elementor_content_filter_query_args( $query_args, $post_type, $customization ) {
|
||||
$selected_pages = $customization['pages'] ?? null;
|
||||
|
||||
if ( 'page' === $post_type && ! empty( $selected_pages ) ) {
|
||||
$query_args['post__in'] = $selected_pages;
|
||||
}
|
||||
|
||||
return $query_args;
|
||||
}
|
||||
|
||||
public function wp_content_filter_query_args( $query_args, $post_type, $customization ) {
|
||||
if ( 'page' === $post_type ) {
|
||||
$query_args['include'] = $customization['pages'] ?? null;
|
||||
}
|
||||
|
||||
return $query_args;
|
||||
}
|
||||
|
||||
public function wp_content_post_types( $post_types, $data ) {
|
||||
$customization = $data['customization']['content'] ?? null;
|
||||
$selected_pages = $customization['pages'] ?? null;
|
||||
$include_menus = ! is_null( $customization['menus'] ) ? $customization['menus'] : true;
|
||||
|
||||
$exclude_post_types = [];
|
||||
|
||||
if ( ! $include_menus ) {
|
||||
$exclude_post_types[] = 'nav_menu_item';
|
||||
}
|
||||
|
||||
if ( null !== $selected_pages && empty( $selected_pages ) ) {
|
||||
$exclude_post_types[] = 'page';
|
||||
}
|
||||
|
||||
return array_diff( $post_types, $exclude_post_types );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate export to the appropriate runner.
|
||||
*
|
||||
* @param string $type Customization type
|
||||
* @param mixed $result Previous filter result
|
||||
* @param array $data Export data
|
||||
* @param array $customization Customization settings
|
||||
* @param object $runner Core runner instance
|
||||
* @return mixed
|
||||
*/
|
||||
private function delegate_to_export_runner( string $type, $result, array $data, array $customization, $runner ) {
|
||||
$runner_class = $this->export_runners[ $type ] ?? null;
|
||||
|
||||
if ( ! $runner_class ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return ( new $runner_class() )->handle( $result, $data, $customization, $runner );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate import to the appropriate runner.
|
||||
*
|
||||
* @param string $type Customization type
|
||||
* @param mixed $result Previous filter result
|
||||
* @param array $data Import data
|
||||
* @param array $imported_data Already imported data
|
||||
* @param array $customization Customization settings
|
||||
* @param object $runner Core runner instance
|
||||
* @return mixed
|
||||
*/
|
||||
private function delegate_to_import_runner( string $type, $result, array $data, array $imported_data, array $customization, $runner ) {
|
||||
$runner_class = $this->import_runners[ $type ] ?? null;
|
||||
|
||||
if ( ! $runner_class ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return ( new $runner_class() )->handle( $result, $data, $imported_data, $customization, $runner );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
namespace ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Base;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
abstract class Export_Runner_Base {
|
||||
/**
|
||||
* Handle the export customization for a specific type.
|
||||
*
|
||||
* @param mixed $result Previous filter result
|
||||
* @param array $data Export data
|
||||
* @param array $customization Customization settings
|
||||
* @param object $runner Core runner instance
|
||||
* @return array|mixed
|
||||
*/
|
||||
abstract public function handle( $result, array $data, array $customization, $runner );
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
namespace ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Base;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
abstract class Import_Runner_Base {
|
||||
/**
|
||||
* Handle the import customization for a specific type.
|
||||
*
|
||||
* @param mixed $result Previous filter result
|
||||
* @param array $data Import data
|
||||
* @param array $imported_data Already imported data
|
||||
* @param array $customization Customization settings
|
||||
* @param object $runner Core runner instance
|
||||
* @return array|mixed
|
||||
*/
|
||||
abstract public function handle( $result, array $data, array $imported_data, array $customization, $runner );
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
namespace ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Export;
|
||||
|
||||
use ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Base\Export_Runner_Base;
|
||||
use ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Traits\Site_Settings_Helpers;
|
||||
use ElementorPro\Plugin;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Site_Settings extends Export_Runner_Base {
|
||||
use Site_Settings_Helpers;
|
||||
|
||||
public function handle( $result, array $data, array $customization, $runner ) {
|
||||
if ( is_array( $result ) ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$kit = Plugin::elementor()->kits_manager->get_active_kit();
|
||||
$kit_data = $kit->get_export_data();
|
||||
|
||||
$allowed_settings = $this->get_allowed_settings();
|
||||
|
||||
foreach ( $customization as $key => $value ) {
|
||||
if ( ! in_array( $key, $allowed_settings, true ) ) {
|
||||
unset( $customization[ $key ] );
|
||||
}
|
||||
}
|
||||
|
||||
$manifest_data = [ 'site-settings' => $customization ];
|
||||
|
||||
if ( ! $customization['globalColors'] ) {
|
||||
$kit_data = $this->remove_setting_by_key( $kit_data, 'globalColors' );
|
||||
}
|
||||
|
||||
if ( ! $customization['globalFonts'] ) {
|
||||
$kit_data = $this->remove_setting_by_key( $kit_data, 'globalFonts' );
|
||||
}
|
||||
|
||||
if ( ! $customization['themeStyleSettings'] ) {
|
||||
$kit_data = $this->remove_setting_by_key( $kit_data, 'themeStyleSettings' );
|
||||
}
|
||||
|
||||
if ( ! $customization['generalSettings'] ) {
|
||||
$kit_data = $this->remove_setting_by_key( $kit_data, 'generalSettings' );
|
||||
}
|
||||
|
||||
if ( ! empty( $customization['theme'] ) ) {
|
||||
$theme_data = $runner->export_theme();
|
||||
if ( $theme_data ) {
|
||||
$kit_data['theme'] = $theme_data;
|
||||
$manifest_data['theme'] = $theme_data;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $customization['experiments'] ) ) {
|
||||
$experiments = $runner->export_experiments();
|
||||
if ( $experiments ) {
|
||||
$kit_data['experiments'] = $experiments;
|
||||
$manifest_data['experiments'] = array_keys( $experiments );
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'files' => [
|
||||
'path' => 'site-settings',
|
||||
'data' => $kit_data,
|
||||
],
|
||||
'manifest' => [
|
||||
$manifest_data,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
namespace ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Export;
|
||||
|
||||
use Elementor\App\Modules\ImportExportCustomization\Utils as ImportExportUtils;
|
||||
use ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Base\Export_Runner_Base;
|
||||
|
||||
class Taxonomies extends Export_Runner_Base {
|
||||
public function handle( $result, array $data, array $customization, $runner ) {
|
||||
if ( is_array( $result ) ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$include_menus = $customization['menus'] ?? true;
|
||||
$selected_custom_post_types = $data['selected_custom_post_types'] ?? null;
|
||||
$exclude_post_types = [];
|
||||
|
||||
if ( ! $include_menus ) {
|
||||
$exclude_post_types[] = 'nav_menu_item';
|
||||
}
|
||||
|
||||
if ( is_array( $selected_custom_post_types ) && ! in_array( 'post', $selected_custom_post_types, true ) ) {
|
||||
$exclude_post_types[] = 'post';
|
||||
}
|
||||
|
||||
$wp_builtin_post_types = ImportExportUtils::get_builtin_wp_post_types( $exclude_post_types );
|
||||
|
||||
$post_types = is_array( $selected_custom_post_types )
|
||||
? array_merge( $wp_builtin_post_types, $selected_custom_post_types )
|
||||
: $wp_builtin_post_types;
|
||||
|
||||
$export = $this->export_taxonomies( $post_types, $customization, $runner );
|
||||
|
||||
$manifest_data['taxonomies'] = $export['manifest'];
|
||||
|
||||
return [
|
||||
'files' => $export['files'],
|
||||
'manifest' => [
|
||||
$manifest_data,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function export_taxonomies( array $post_types, array $customization, $runner ) {
|
||||
$files = [];
|
||||
$manifest = [];
|
||||
|
||||
$taxonomies = get_taxonomies();
|
||||
|
||||
$selected_taxonomies = $customization['taxonomies'] ?? null;
|
||||
|
||||
foreach ( $taxonomies as $taxonomy ) {
|
||||
$taxonomy_obj = get_taxonomy( $taxonomy );
|
||||
$taxonomy_post_types = $taxonomy_obj->object_type;
|
||||
$intersected_post_types = array_intersect( $taxonomy_post_types, $post_types );
|
||||
|
||||
$should_export = null === $selected_taxonomies
|
||||
? ! empty( $intersected_post_types )
|
||||
: in_array( $taxonomy, $selected_taxonomies, true );
|
||||
|
||||
if ( ! $should_export ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = $runner->export_terms( $taxonomy );
|
||||
|
||||
if ( empty( $data ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $intersected_post_types as $post_type ) {
|
||||
$manifest[ $post_type ][] = [
|
||||
'name' => $taxonomy,
|
||||
'label' => $taxonomy_obj->label,
|
||||
];
|
||||
}
|
||||
|
||||
$files[] = [
|
||||
'path' => 'taxonomies/' . $taxonomy,
|
||||
'data' => $data,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'files' => $files,
|
||||
'manifest' => $manifest,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Export;
|
||||
|
||||
use ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Base\Export_Runner_Base;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Modules\Library\Documents\Library_Document;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Templates extends Export_Runner_Base {
|
||||
public function handle( $result, array $data, array $customization, $runner ) {
|
||||
if ( is_array( $result ) ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$template_types = [];
|
||||
|
||||
if ( isset( $customization['siteTemplates']['enabled'] ) && $customization['siteTemplates']['enabled'] ) {
|
||||
$template_types = array_keys( Plugin::$instance->documents->get_document_types( [
|
||||
'is_editable' => true,
|
||||
'show_in_library' => true,
|
||||
'export_group' => Library_Document::EXPORT_GROUP,
|
||||
] ) );
|
||||
}
|
||||
|
||||
return $runner->export_templates_by_types( $template_types, $data );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
namespace ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Import;
|
||||
|
||||
use ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Base\Import_Runner_Base;
|
||||
use ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Traits\Site_Settings_Helpers;
|
||||
use ElementorPro\Plugin;
|
||||
use Elementor\Core\Settings\Page\Manager as PageManager;
|
||||
use Elementor\App\Modules\ImportExportCustomization\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Elementor_Content extends Import_Runner_Base {
|
||||
public function handle( $result, array $data, array $imported_data, array $customization, $runner ) {
|
||||
if ( is_array( $result ) ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$selected_pages = $customization['pages'] ?? null;
|
||||
|
||||
if ( is_array( $selected_pages ) && ! in_array( $data['id'], $selected_pages ) ) {
|
||||
return [
|
||||
'status' => 'failed',
|
||||
'result' => __( 'Skipped', 'elementor-pro' ),
|
||||
];
|
||||
}
|
||||
|
||||
return $runner->read_and_import_post(
|
||||
$data['path'],
|
||||
$data['id'],
|
||||
$data['post_settings'],
|
||||
$data['post_type'],
|
||||
$data['imported_terms'],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
namespace ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Import;
|
||||
|
||||
use ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Base\Import_Runner_Base;
|
||||
use ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Traits\Site_Settings_Helpers;
|
||||
use ElementorPro\Plugin;
|
||||
use Elementor\Core\Settings\Page\Manager as PageManager;
|
||||
use Elementor\App\Modules\ImportExportCustomization\Utils;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Site_Settings extends Import_Runner_Base {
|
||||
use Site_Settings_Helpers;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $active_kit_id;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $previous_kit_id;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $imported_kit_id;
|
||||
|
||||
public function handle( $result, array $data, array $imported_data, array $customization, $runner ) {
|
||||
if ( is_array( $result ) ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$new_site_settings = $data['site_settings']['settings'];
|
||||
$title = $data['manifest']['title'] ?? 'Imported Kit';
|
||||
|
||||
$active_kit = Plugin::elementor()->kits_manager->get_active_kit();
|
||||
|
||||
$this->active_kit_id = (int) $active_kit->get_id();
|
||||
$this->previous_kit_id = (int) Plugin::elementor()->kits_manager->get_previous_id();
|
||||
|
||||
$result = [];
|
||||
|
||||
$old_settings = $active_kit->get_meta( PageManager::META_KEY );
|
||||
if ( ! $old_settings ) {
|
||||
$old_settings = [];
|
||||
}
|
||||
|
||||
$new_site_settings = $this->filter_settings_by_customization( $new_site_settings, $customization );
|
||||
|
||||
if ( ( $customization['globalColors'] ?? false ) && ! empty( $old_settings['custom_colors'] ) && ! empty( $new_site_settings['custom_colors'] ) ) {
|
||||
$new_site_settings['custom_colors'] = array_merge( $old_settings['custom_colors'], $new_site_settings['custom_colors'] );
|
||||
}
|
||||
|
||||
if ( ( $customization['globalFonts'] ?? false ) && ! empty( $old_settings['custom_typography'] ) && ! empty( $new_site_settings['custom_typography'] ) ) {
|
||||
$new_site_settings['custom_typography'] = array_merge( $old_settings['custom_typography'], $new_site_settings['custom_typography'] );
|
||||
}
|
||||
|
||||
if ( ( $customization['generalSettings'] ?? false ) && ! empty( $new_site_settings['space_between_widgets'] ) ) {
|
||||
$new_site_settings['space_between_widgets'] = Utils::update_space_between_widgets_values( $new_site_settings['space_between_widgets'] );
|
||||
}
|
||||
|
||||
$new_site_settings = array_replace_recursive( $old_settings, $new_site_settings );
|
||||
|
||||
$new_kit = Plugin::elementor()->kits_manager->create_new_kit( $title, $new_site_settings );
|
||||
$this->imported_kit_id = (int) $new_kit;
|
||||
|
||||
$result['site-settings'] = (bool) $new_kit;
|
||||
|
||||
if ( $customization['theme'] ?? false ) {
|
||||
$import_theme_result = $runner->import_theme( $data );
|
||||
if ( ! empty( $import_theme_result ) ) {
|
||||
$result['theme'] = $import_theme_result;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $customization['experiments'] ?? false ) {
|
||||
$runner->import_experiments( $data );
|
||||
$session_meta = $runner->get_import_session_metadata();
|
||||
$imported_experiments = $session_meta['imported_experiments'] ?? [];
|
||||
if ( ! empty( $imported_experiments ) ) {
|
||||
$result['experiments'] = $imported_experiments;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function filter_settings_by_customization( array $settings, array $customization ): array {
|
||||
$allowed_settings = $this->get_allowed_settings();
|
||||
|
||||
foreach ( $customization as $key => $value ) {
|
||||
if ( ! in_array( $key, $allowed_settings, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! $value ) {
|
||||
$settings = $this->remove_setting_by_key( $settings, $key );
|
||||
}
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Import;
|
||||
|
||||
use Elementor\App\Modules\ImportExportCustomization\Utils as ImportExportUtils;
|
||||
use ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Base\Import_Runner_Base;
|
||||
|
||||
class Taxonomies extends Import_Runner_Base {
|
||||
|
||||
private $import_session_id;
|
||||
|
||||
public function handle( $result, array $data, array $imported_data, array $customization, $runner ) {
|
||||
$path = $data['extracted_directory_path'] . 'taxonomies/';
|
||||
$this->import_session_id = $data['session_id'];
|
||||
$selected_taxonomies = $data['selected_taxonomies'] ?? [];
|
||||
|
||||
$wp_builtin_post_types = ImportExportUtils::get_builtin_wp_post_types();
|
||||
$selected_custom_post_types = isset( $data['selected_custom_post_types'] ) ? $data['selected_custom_post_types'] : [];
|
||||
$post_types = array_merge( $wp_builtin_post_types, $selected_custom_post_types );
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ( $post_types as $post_type ) {
|
||||
if ( empty( $data['manifest']['taxonomies'][ $post_type ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$taxonomies_to_import = [];
|
||||
|
||||
foreach ( $data['manifest']['taxonomies'][ $post_type ] as $taxonomy ) {
|
||||
if ( in_array( $taxonomy['name'], $selected_taxonomies, true ) ) {
|
||||
$taxonomies_to_import[] = $taxonomy;
|
||||
}
|
||||
}
|
||||
|
||||
$result['taxonomies'][ $post_type ] = $runner->import_taxonomies( $taxonomies_to_import, $path );
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
namespace ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Import;
|
||||
|
||||
use ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Base\Import_Runner_Base;
|
||||
use Elementor\Plugin;
|
||||
use Elementor\Modules\Library\Documents\Library_Document;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
class Templates extends Import_Runner_Base {
|
||||
public function handle( $result, array $data, array $imported_data, array $customization, $runner ) {
|
||||
if ( is_array( $result ) ) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result = [
|
||||
'templates' => [
|
||||
'succeed' => [],
|
||||
'failed' => [],
|
||||
'succeed_summary' => [],
|
||||
],
|
||||
];
|
||||
|
||||
if ( isset( $customization['siteTemplates']['enabled'] ) && $customization['siteTemplates']['enabled'] ) {
|
||||
$template_types = array_keys( Plugin::$instance->documents->get_document_types( [
|
||||
'is_editable' => true,
|
||||
'show_in_library' => true,
|
||||
'export_group' => Library_Document::EXPORT_GROUP,
|
||||
] ) );
|
||||
|
||||
$result = $runner->process_templates_import( $data, $template_types );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the templates import result to allow 3rd parties to add their own imported templates.
|
||||
*
|
||||
* @param array $result The import result structure with 'templates' key containing succeed/failed/succeed_summary.
|
||||
* @param array $data The full import data.
|
||||
* @param array|null $customization The customization settings for templates.
|
||||
* @param object $runner The runner instance.
|
||||
*/
|
||||
$result = apply_filters( 'elementor/import-export-customization/import/templates_result', $result, $data, $customization, $runner );
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
namespace ElementorPro\Core\App\Modules\ImportExportCustomization\Runners\Traits;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
trait Site_Settings_Helpers {
|
||||
/**
|
||||
* Get the allowed settings for site-settings runners.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_allowed_settings(): array {
|
||||
return [
|
||||
'theme',
|
||||
'globalColors',
|
||||
'globalFonts',
|
||||
'themeStyleSettings',
|
||||
'generalSettings',
|
||||
'experiments',
|
||||
'customCode',
|
||||
'customIcons',
|
||||
'customFonts',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove setting by key using the appropriate removal method.
|
||||
*
|
||||
* @param array $settings
|
||||
* @param string $setting_key
|
||||
* @return array
|
||||
*/
|
||||
protected function remove_setting_by_key( array $settings, string $setting_key ): array {
|
||||
switch ( $setting_key ) {
|
||||
case 'globalColors':
|
||||
$settings = $this->remove_global_colors( $settings );
|
||||
break;
|
||||
case 'globalFonts':
|
||||
$settings = $this->remove_global_fonts( $settings );
|
||||
break;
|
||||
case 'themeStyleSettings':
|
||||
$settings = $this->remove_theme_style( $settings );
|
||||
break;
|
||||
case 'generalSettings':
|
||||
$settings = $this->remove_other_settings( $settings );
|
||||
break;
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove global colors from settings.
|
||||
*
|
||||
* @param array $settings
|
||||
* @return array
|
||||
*/
|
||||
private function remove_global_colors( array $settings ): array {
|
||||
$color_keys = [ 'system_colors', 'custom_colors' ];
|
||||
foreach ( $color_keys as $key ) {
|
||||
if ( isset( $settings[ $key ] ) ) {
|
||||
unset( $settings[ $key ] );
|
||||
}
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove global fonts from settings.
|
||||
*
|
||||
* @param array $settings
|
||||
* @return array
|
||||
*/
|
||||
private function remove_global_fonts( array $settings ): array {
|
||||
$typography_keys = [ 'system_typography', 'custom_typography', 'default_generic_fonts' ];
|
||||
foreach ( $typography_keys as $key ) {
|
||||
if ( isset( $settings[ $key ] ) ) {
|
||||
unset( $settings[ $key ] );
|
||||
}
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove theme style settings.
|
||||
*
|
||||
* @param array $settings
|
||||
* @return array
|
||||
*/
|
||||
private function remove_theme_style( array $settings ): array {
|
||||
$theme_style_patterns = [
|
||||
'/^body_/',
|
||||
'/^h[1-6]_/',
|
||||
'/^button_/',
|
||||
'/^link_/',
|
||||
'/^form_field_/',
|
||||
];
|
||||
foreach ( $settings as $key => $value ) {
|
||||
foreach ( $theme_style_patterns as $pattern ) {
|
||||
if ( preg_match( $pattern, $key ) ) {
|
||||
unset( $settings[ $key ] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove other general settings.
|
||||
*
|
||||
* @param array $settings
|
||||
* @return array
|
||||
*/
|
||||
private function remove_other_settings( array $settings ): array {
|
||||
$settings_keys = [
|
||||
'template',
|
||||
'container_width',
|
||||
'container_padding',
|
||||
'space_between_widgets',
|
||||
'viewport_md',
|
||||
'viewport_lg',
|
||||
'page_title_selector',
|
||||
'activeItemIndex',
|
||||
'^settings-style-',
|
||||
];
|
||||
foreach ( $settings_keys as $key ) {
|
||||
if ( isset( $settings[ $key ] ) ) {
|
||||
unset( $settings[ $key ] );
|
||||
}
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ class Module extends BaseModule {
|
||||
|
||||
$app->set_settings( 'kit-library', array_merge( $prev_settings, [
|
||||
'is_pro' => true,
|
||||
'is_library_connected' => API::is_license_active(),
|
||||
'is_library_connected' => API::is_license_active() && $kit_library && $kit_library->is_connected(),
|
||||
'library_connect_url' => $activate->get_admin_url( 'authorize', [
|
||||
'utm_source' => 'kit-library',
|
||||
'utm_medium' => 'wp-dash',
|
||||
@@ -52,6 +52,7 @@ class Module extends BaseModule {
|
||||
] ),
|
||||
'access_level' => API::get_library_access_level( 'kit' ),
|
||||
'access_tier' => API::get_access_tier(),
|
||||
'plan_type' => API::get_plan_type(),
|
||||
] ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&:after {
|
||||
&::after {
|
||||
font-family: eicons;
|
||||
content: '\e8ad';
|
||||
font-size: $e-site-editor-input-wrapper-select-arrow-font-size;
|
||||
@@ -140,7 +140,7 @@
|
||||
&__input-wrapper--condition-type {
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
&::before {
|
||||
font-family: eicons;
|
||||
position: absolute;
|
||||
transform: translateY(-50%);
|
||||
@@ -162,13 +162,13 @@
|
||||
}
|
||||
|
||||
&[data-elementor-condition-type="include"] {
|
||||
&:before {
|
||||
&::before {
|
||||
content: '\e8cc';
|
||||
}
|
||||
}
|
||||
|
||||
&[data-elementor-condition-type="exclude"] {
|
||||
&:before {
|
||||
&::before {
|
||||
content: '\e8cd';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
margin: 0 auto;
|
||||
align-items: center;
|
||||
border-block-start: 1px solid var(--hr-color);
|
||||
padding-block-start: spacing(16);
|
||||
padding-block-end: spacing(16);
|
||||
padding-block: spacing(16);
|
||||
padding-inline: spacing(30);
|
||||
}
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ class Activate extends Common_App {
|
||||
License\Admin::set_license_key( $license_key );
|
||||
|
||||
License\API::set_license_data( $data );
|
||||
|
||||
$this->request( 'set_site_owner' );
|
||||
$this->add_notice( esc_html__( 'License has been activated successfully.', 'elementor-pro' ) );
|
||||
|
||||
$this->redirect_to_admin_page( License\Admin::get_url() );
|
||||
@@ -121,6 +121,7 @@ class Activate extends Common_App {
|
||||
'templates_access_level' => API::get_library_access_level( 'template' ),
|
||||
'kits_access_level' => API::get_library_access_level( 'kit' ),
|
||||
'access_tier' => API::get_access_tier(),
|
||||
'plan_type' => API::get_plan_type(),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<?php
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Elementor Dependency Injection Configuration.
|
||||
*
|
||||
* This file registers definitions for PHP-DI used by Elementor.
|
||||
*
|
||||
* @since 3.25.0
|
||||
*/
|
||||
|
||||
return [];
|
||||
@@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace ElementorPro\Core\Container;
|
||||
|
||||
use ElementorProDeps\DI\ContainerBuilder;
|
||||
use ElementorProDeps\DI\Container as DIContainer;
|
||||
use Exception;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Elementor Container.
|
||||
*
|
||||
* Elementor container handler class is responsible for the containerization
|
||||
* of manager classes and their dependencies.
|
||||
*
|
||||
* @since 3.25.0
|
||||
*/
|
||||
|
||||
class Container {
|
||||
|
||||
private static $instance;
|
||||
|
||||
private function __construct() {}
|
||||
|
||||
private function __clone() {}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function initialize(): void {
|
||||
$builder = new ContainerBuilder();
|
||||
|
||||
self::register_configuration( $builder );
|
||||
|
||||
self::$instance = $builder->build();
|
||||
}
|
||||
|
||||
private static function register_configuration( ContainerBuilder $builder ): void {
|
||||
$builder->addDefinitions( __DIR__ . '/config.php' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function get_instance(): DIContainer {
|
||||
if ( is_null( static::$instance ) ) {
|
||||
self::initialize();
|
||||
}
|
||||
|
||||
return static::$instance;
|
||||
}
|
||||
}
|
||||
@@ -14,9 +14,11 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
}
|
||||
|
||||
class Editor extends App {
|
||||
const EDITOR_V2_PACKAGES = [
|
||||
const EDITOR_V4_PACKAGES = [
|
||||
'editor-documents-extended',
|
||||
'editor-controls-extended',
|
||||
'editor-site-navigation-extended',
|
||||
'editor-editing-panel-extended',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -85,7 +87,7 @@ class Editor extends App {
|
||||
public function enqueue_editor_styles() {
|
||||
wp_enqueue_style(
|
||||
'elementor-pro',
|
||||
$this->get_css_assets_url( 'editor', null, 'default', true ),
|
||||
$this->get_css_assets_url( 'editor' ),
|
||||
[
|
||||
'elementor-editor',
|
||||
],
|
||||
@@ -118,7 +120,7 @@ class Editor extends App {
|
||||
return ELEMENTOR_PRO_ASSETS_PATH . "js/packages/{$name}/{$name}.asset.php";
|
||||
} );
|
||||
|
||||
$packages = apply_filters( 'elementor-pro/editor/v2/packages', self::EDITOR_V2_PACKAGES );
|
||||
$packages = apply_filters( 'elementor-pro/editor/v2/packages', self::EDITOR_V4_PACKAGES );
|
||||
|
||||
foreach ( $packages as $package ) {
|
||||
$assets_config->load( $package );
|
||||
|
||||
@@ -78,6 +78,7 @@ final class Modules_Manager {
|
||||
'floating-buttons',
|
||||
'search',
|
||||
'cloud-library',
|
||||
'variables',
|
||||
];
|
||||
|
||||
foreach ( $modules as $module_name ) {
|
||||
|
||||
@@ -20,7 +20,7 @@ class Preview extends App {
|
||||
public function enqueue_styles() {
|
||||
wp_enqueue_style(
|
||||
'pro-editor-preview',
|
||||
$this->get_css_assets_url( 'preview', null, 'default', true ),
|
||||
$this->get_css_assets_url( 'preview' ),
|
||||
[],
|
||||
ELEMENTOR_PRO_VERSION
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user