├── .githooks └── pre-commit ├── .github └── workflows │ └── php.yml ├── .gitignore ├── .make ├── check-compatibility.sh ├── check-composer.sh ├── check-duplicates.sh ├── check-npm.sh ├── deploy-cleanup.js ├── deploy-copy.js └── deploy-update-version.js ├── .scrutinizer.yml ├── Makefile ├── README.md ├── composer.json ├── phpcs.xml ├── phpunit.xml ├── src ├── Config │ └── Factory.php ├── DataConvert.php ├── DynamicContent │ ├── Field.php │ └── Strings.php ├── Hooks │ ├── DynamicElements.php │ ├── Frontend.php │ ├── GutenbergCleanup.php │ └── LandingPages.php ├── LanguageSwitcher │ ├── LanguageSwitcher.php │ ├── Widget.php │ └── WidgetAdaptor.php ├── class-wpml-elementor-adjust-global-widget-id-factory.php ├── class-wpml-elementor-adjust-global-widget-id.php ├── class-wpml-elementor-data-settings.php ├── class-wpml-elementor-db-factory.php ├── class-wpml-elementor-db.php ├── class-wpml-elementor-integration-factory.php ├── class-wpml-elementor-pb-handle-custom-fields-factory.php ├── class-wpml-elementor-register-strings.php ├── class-wpml-elementor-translatable-nodes.php ├── class-wpml-elementor-translate-ids-factory.php ├── class-wpml-elementor-translate-ids.php ├── class-wpml-elementor-update-translation.php ├── class-wpml-elementor-urls-factory.php ├── class-wpml-elementor-urls.php ├── class-wpml-elementor-woocommerce-hooks-factory.php ├── class-wpml-elementor-woocommerce-hooks.php ├── class-wpml-pb-fix-maintenance-query.php ├── media │ ├── class-wpml-elementor-media-hooks-factory.php │ ├── class-wpml-elementor-media-node-provider.php │ ├── class-wpml-elementor-media-nodes-iterator.php │ ├── class-wpml-elementor-update-media-factory.php │ └── modules │ │ ├── AllNodes.php │ │ ├── Hotspot.php │ │ ├── VideoPlaylist.php │ │ ├── abstract │ │ ├── class-wpml-elementor-media-node-with-image-property.php │ │ ├── class-wpml-elementor-media-node-with-images-property.php │ │ ├── class-wpml-elementor-media-node-with-slides.php │ │ └── class-wpml-elementor-media-node.php │ │ ├── class-wpml-elementor-media-node-call-to-action.php │ │ ├── class-wpml-elementor-media-node-image-box.php │ │ ├── class-wpml-elementor-media-node-image-carousel.php │ │ ├── class-wpml-elementor-media-node-image-gallery.php │ │ ├── class-wpml-elementor-media-node-image.php │ │ ├── class-wpml-elementor-media-node-media-carousel.php │ │ ├── class-wpml-elementor-media-node-slides.php │ │ ├── class-wpml-elementor-media-node-wp-widget-media-gallery.php │ │ └── class-wpml-elementor-media-node-wp-widget-media-image.php └── modules │ ├── MediaCarousel.php │ ├── ModuleWithItemsFromConfig.php │ ├── MultipleGallery.php │ ├── Reviews.php │ ├── class-wpml-elementor-accordion.php │ ├── class-wpml-elementor-form.php │ ├── class-wpml-elementor-icon-list.php │ ├── class-wpml-elementor-module-with-items.php │ ├── class-wpml-elementor-price-list.php │ ├── class-wpml-elementor-price-table.php │ ├── class-wpml-elementor-slides.php │ ├── class-wpml-elementor-tabs.php │ ├── class-wpml-elementor-testimonial-carousel.php │ └── class-wpml-elementor-toggle.php └── tests └── phpunit ├── bootstrap.php ├── stubs ├── IWPML_Backend_Action.php ├── IWPML_DIC_Action.php ├── IWPML_Frontend_Action.php ├── interface-iwpml-action.php └── stub-elementor-db.php ├── tests ├── DynamicContent │ └── TestStrings.php ├── Hooks │ ├── TestDynamicElements.php │ ├── TestFrontend.php │ ├── TestGutenbergCleanup.php │ └── TestLandingPages.php ├── LanguageSwitcher │ ├── TestLanguageSwitcher.php │ ├── TestWidget.php │ └── TestWidgetAdaptor.php ├── media │ ├── modules │ │ ├── TestHotspot.php │ │ ├── TestVideoPlaylist.php │ │ ├── test-AllNodes.php │ │ ├── test-wpml-elementor-media-node-call-to-action.php │ │ ├── test-wpml-elementor-media-node-image-box.php │ │ ├── test-wpml-elementor-media-node-image-carousel.php │ │ ├── test-wpml-elementor-media-node-image-gallery.php │ │ ├── test-wpml-elementor-media-node-image.php │ │ ├── test-wpml-elementor-media-node-media-carousel.php │ │ ├── test-wpml-elementor-media-node-slides.php │ │ ├── test-wpml-elementor-media-node-wp-widget-media-gallery.php │ │ └── test-wpml-elementor-media-node-wp-widget-media-image.php │ ├── test-wpml-elementor-media-hooks-factory.php │ ├── test-wpml-elementor-media-node-provider.php │ ├── test-wpml-elementor-media-nodes-iterator.php │ └── test-wpml-elementor-update-media-factory.php ├── modules │ ├── TestMediaCarousel.php │ ├── TestModuleWithItemsFromConfig.php │ ├── TestMultipleGallery.php │ ├── TestReviews.php │ ├── Test_WPML_Elementor_Module_With_Items.php │ ├── test-wpml-elementor-accordion.php │ ├── test-wpml-elementor-form.php │ ├── test-wpml-elementor-icon-list.php │ ├── test-wpml-elementor-price-list.php │ ├── test-wpml-elementor-price-table.php │ ├── test-wpml-elementor-slides.php │ ├── test-wpml-elementor-tabs.php │ ├── test-wpml-elementor-testimonial-carousel.php │ └── test-wpml-elementor-toggle.php ├── test-pb-fix-maintenance-query.php ├── test-wpml-elementor-adjust-global-widget-id.php ├── test-wpml-elementor-data-settings.php ├── test-wpml-elementor-db-factory.php ├── test-wpml-elementor-db.php ├── test-wpml-elementor-integration-factory.php ├── test-wpml-elementor-register-strings.php ├── test-wpml-elementor-translatable-nodes.php ├── test-wpml-elementor-translate-ids.php ├── test-wpml-elementor-update-translation.php ├── test-wpml-elementor-urls.php ├── test-wpml-elementor-woocommerce-hooks-factory.php ├── test-wpml-elementor-woocommerce-hooks.php └── test-wpml-pb-handle-custom-fields-factory.php └── util └── wpml-pb-test-case2.php /.githooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | BRANCH_NAME=$(git branch | grep '*' | sed 's/* //') 3 | 4 | if [ $BRANCH_NAME != '(no branch)' ] 5 | then 6 | make precommit 7 | fi 8 | -------------------------------------------------------------------------------- /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | name: OTGS CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | 11 | - uses: nanasess/setup-php@master 12 | with: 13 | php-version: '7.4' 14 | 15 | - name: Set github token for composer 16 | run: composer config -g github-oauth.github.com ${{ secrets.GITHUB_TOKEN }} 17 | 18 | - name: Validate composer.json and composer.lock 19 | run: composer validate 20 | 21 | - name: Install dependencies 22 | run: composer install --prefer-dist --no-progress --no-suggest 23 | 24 | - name: Run test suite 25 | run: ./vendor/bin/phpunit -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | composer.phar 4 | composer.lock 5 | /vendor/ 6 | -------------------------------------------------------------------------------- /.make/check-compatibility.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | STAGED_FILES_CMD=`git diff --cached --name-only --diff-filter=ACMR HEAD | grep \\\\.php` 4 | 5 | # Determine if a file list is passed 6 | if [[ "$#" -eq 1 ]] 7 | then 8 | oIFS=$IFS 9 | IFS=' 10 | ' 11 | STAGED_FILES="$1" 12 | IFS=${oIFS} 13 | fi 14 | STAGED_FILES=${STAGED_FILES:-$STAGED_FILES_CMD} 15 | 16 | echo "Checking PHP Lint..." 17 | for FILE in ${STAGED_FILES} 18 | do 19 | php -l -d display_errors=0 ./${FILE} 20 | if [[ $? != 0 ]] 21 | then 22 | echo "Fix the error before commit." 23 | exit 1 24 | else 25 | echo ":: PHP Lint: OK" 26 | fi 27 | 28 | if [[ ${FILE} != tests/* ]] 29 | then 30 | FILES="$FILES ./$FILE" 31 | fi 32 | 33 | FILES="$FILES ./$FILE" 34 | done 35 | 36 | if [[ "$FILES" != "" ]] 37 | then 38 | echo "Running compatibility checks..." 39 | ./vendor/bin/phpcs --standard=./phpcs.compatibility.xml --colors ${FILES} 40 | 41 | if [[ $? != 0 ]] 42 | then 43 | echo "Fix the error before commit!" 44 | exit 1 45 | else 46 | echo ":: Compatibility checks: OK" 47 | fi 48 | 49 | echo "Running Code Sniffer..." 50 | ./vendor/bin/phpcs --colors ${FILES} 51 | 52 | if [[ $? != 0 ]] 53 | then 54 | echo "Fix the error before commit!" 55 | echo "Run" 56 | echo " ./vendor/bin/phpcbf $FILES" 57 | echo "for automatic fix or fix it manually." 58 | exit 1 59 | else 60 | echo ":: Code Sniffer: OK!" 61 | fi 62 | fi 63 | 64 | exit $? 65 | -------------------------------------------------------------------------------- /.make/check-composer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | STAGED_FILES=`git diff --cached --name-only --diff-filter=ACMR HEAD | grep -E '^composer.(lock|json)'` 4 | 5 | for FILE in ${STAGED_FILES} 6 | do 7 | FILES="$FILES ./$FILE" 8 | done 9 | 10 | if [[ "$FILES" != "" ]] 11 | then 12 | echo "Validating composer.json" 13 | composer validate --no-check-all 14 | 15 | if [[ $? != 0 ]] 16 | then 17 | exit 1 18 | fi 19 | fi 20 | 21 | exit $? 22 | -------------------------------------------------------------------------------- /.make/check-duplicates.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | STAGED_FILES_CMD=`git diff --cached --name-only --diff-filter=ACMR HEAD | grep \\\\.php` 4 | 5 | # Determine if a file list is passed 6 | if [[ "$#" -eq 1 ]] 7 | then 8 | oIFS=$IFS 9 | IFS=' 10 | ' 11 | STAGED_FILES="$1" 12 | IFS=${oIFS} 13 | fi 14 | STAGED_FILES=${STAGED_FILES:-$STAGED_FILES_CMD} 15 | 16 | echo "Checking PHP Lint..." 17 | for FILE in ${STAGED_FILES} 18 | do 19 | if [[ ${FILE} != tests/* ]] 20 | then 21 | FILES="$FILES ./$FILE" 22 | fi 23 | done 24 | 25 | if [[ "$FILES" != "" ]] 26 | then 27 | echo "Running duplicates checks..." 28 | echo "vendor/bin/phpcpd --exclude tests --exclude vendor --${FILES}" 29 | vendor/bin/phpcpd --exclude tests --exclude vendor -- ${FILES} 30 | 31 | if [[ $? != 0 ]] 32 | then 33 | echo "Fix the error before commit!" 34 | exit 1 35 | else 36 | echo ":: Duplicates checks: OK" 37 | fi 38 | fi 39 | 40 | exit $? 41 | -------------------------------------------------------------------------------- /.make/check-npm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | STAGED_FILES=`git diff --cached --name-only --diff-filter=ACMR HEAD | grep -E '^src/(yarn.lock|package.json)'` 4 | 5 | for FILE in ${STAGED_FILES} 6 | do 7 | FILES="$FILES ./$FILE" 8 | done 9 | 10 | if [[ "$FILES" != "" ]] 11 | then 12 | echo "Validating package.json" 13 | npm doctor 14 | 15 | if [[ $? != 0 ]] 16 | then 17 | echo "Tip: if you get a failed 'npm config get registry' check, try \`npm config set registry https://registry.npmjs.org/\`" 18 | echo "Tip: if you get a failed 'Perms check on local node_modules' check, try \`sudo chown -R $(whoami) ./node_modules\`" 19 | echo "Tip: if you still get a failed 'Perms check on local node_modules' check, try \`rm -rf node_modules\` followed by \`npm install\`" 20 | exit 1 21 | fi 22 | fi 23 | 24 | exit $? 25 | -------------------------------------------------------------------------------- /.make/deploy-cleanup.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const path = require('path'); 4 | const del = require('del'); 5 | 6 | const argv = require('yargs') 7 | .usage('Usage: [options]') 8 | .option('t', { 9 | description: 'The target path to cleanup', 10 | alias: 'target', 11 | demandOption: true, 12 | string: true 13 | }) 14 | .option('d', { 15 | description: 'Debug', 16 | alias: 'debug', 17 | boolean: true, 18 | default: false 19 | }) 20 | .argv; 21 | 22 | const targetPath = path.normalize(argv.target); 23 | 24 | cleanupTarget(); 25 | 26 | function cleanupTarget() { 27 | if (!process.env.OTGS_CI_DEPLOY_DEL && argv.debug) { 28 | setTestPatterns(); 29 | } 30 | 31 | if (process.env.OTGS_CI_DEPLOY_DEL) { 32 | const currentDirectory = process.cwd(); 33 | process.chdir(targetPath); 34 | const del_patterns = JSON.parse(process.env.OTGS_CI_DEPLOY_DEL); 35 | 36 | return del(del_patterns, {dot: true,}) 37 | .then(paths => { 38 | process.chdir(currentDirectory); 39 | return console.info(`Deleted ${paths.length} files`); 40 | }) 41 | .catch(error => console.error(error)); 42 | } else { 43 | throw new Error('An environment variable named OTGS_CI_DEPLOY_DEL and containing a JSON array of Glob patterns is required.'); 44 | } 45 | } 46 | 47 | function setTestPatterns() { 48 | const testPatterns = [ 49 | "**/*/*.css.map", 50 | "**/*/*.js.map", 51 | "**/*/*.scss", 52 | "*.js", 53 | "*.json", 54 | "*.sh", 55 | "*.xml", 56 | "*.xml.dist", 57 | ".*", 58 | ".babelrc", 59 | ".browserslistrc", 60 | ".eslintrc", 61 | ".githooks", 62 | ".make", 63 | "build", 64 | "composer.*", 65 | "libraries/**/*/*.log", 66 | "libraries/**/*/*.md", 67 | "libraries/**/*/.*", 68 | "libraries/**/*/bin", 69 | "libraries/**/*/demo", 70 | "libraries/**/*/doc", 71 | "libraries/**/*/node_modules", 72 | "libraries/**/*/src", 73 | "libraries/**/*/test", 74 | "libraries/vkBeautify/**/*", 75 | "Makefile", 76 | "node_modules", 77 | "package.json", 78 | "postcss.config.json", 79 | "README.md", 80 | "res/scss", 81 | "src", 82 | "tests", 83 | "vendor/**/*/*.json", 84 | "vendor/**/*/*.md", 85 | "vendor/**/*/*.txt", 86 | "vendor/**/*/*.xml", 87 | "vendor/**/*/*.xml.dist", 88 | "vendor/**/*/.*", 89 | "vendor/**/*/composer.*", 90 | "vendor/**/*/test/**", 91 | "vendor/**/*/tests/**", 92 | "vendor/bin", 93 | "vendor/wimg", 94 | "vendor/xrstf", 95 | "webpack.config.js", 96 | "yarn-error.log", 97 | "yarn.lock", 98 | "!changelog.md", 99 | "!libraries/vkBeautify/vkbeautify.js", 100 | "!license.txt", 101 | "!readme.txt", 102 | "!vendor/**/*/lib/test*", 103 | "!vendor/**/*/README.md", 104 | "!vendor/**/*/src/test*", 105 | "!vendor/otgs/installer/*.xml", 106 | "!wpml-config.xml", 107 | "!wpml-dependencies.json" 108 | ]; 109 | 110 | process.env.OTGS_CI_DEPLOY_DEL = JSON.stringify(testPatterns); 111 | } 112 | -------------------------------------------------------------------------------- /.make/deploy-copy.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const fs = require('fs-extra'); 4 | const path = require('path'); 5 | const copy = require('recursive-copy'); 6 | 7 | const argv = require('yargs') 8 | .usage('Usage: [options]') 9 | .option('s', { 10 | description: 'The source path', 11 | alias: 'source', 12 | string: true, 13 | default: process.cwd(), 14 | }) 15 | .option('t', { 16 | description: 'The target path', 17 | alias: 'target', 18 | demandOption: true, 19 | string: true, 20 | }) 21 | .option('d', { 22 | description: 'Debug', 23 | alias: 'debug', 24 | boolean: true, 25 | default: false 26 | }) 27 | .demand('t') 28 | .argv; 29 | 30 | const sourcePath = validatePath(path.normalize(argv.source)); 31 | const targetPath = path.normalize(argv.target); 32 | 33 | emptyTargetDirectory().then(() => copySourceFiles()); 34 | 35 | function validatePath(pathToCheck) { 36 | if (pathToCheck && fs.existsSync(pathToCheck)) { 37 | return pathToCheck; 38 | } 39 | throw new Error(pathToCheck + ' does not exist!'); 40 | } 41 | 42 | 43 | function emptyTargetDirectory() { 44 | 45 | return fs.remove(targetPath) 46 | .then(() => console.log(targetPath + ' removed.')) 47 | .catch(err => console.error(err)); 48 | 49 | } 50 | 51 | function copySourceFiles() { 52 | return copy(sourcePath, targetPath, { 53 | dot: false, 54 | junk: false, 55 | results: true, 56 | }) 57 | // .on(copy.events.COPY_FILE_COMPLETE, function (copyOperation) { 58 | // process.stdout.write('.'); 59 | // // console.info('Copied to ' + copyOperation.dest); 60 | // }) 61 | .on(copy.events.ERROR, function (error, copyOperation) { 62 | console.error('Unable to copy ' + copyOperation.dest); 63 | }) 64 | .then(function (results) { 65 | console.info('\n' + results.length + ' file(s) copied'); 66 | }) 67 | .catch(function (error) { 68 | return console.error('\nCopy failed: ' + error); 69 | }); 70 | } 71 | -------------------------------------------------------------------------------- /.make/deploy-update-version.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const fs = require('fs-extra'); 4 | const path = require('path'); 5 | 6 | const argv = require('yargs') 7 | .usage('Usage: [options]') 8 | .option('t', { 9 | description: 'The target path to update', 10 | alias: 'target', 11 | default: process.cwd(), 12 | string: true 13 | }) 14 | .option('r', { 15 | description: 'The version', 16 | alias: 'ref', 17 | demandOption: true, 18 | string: true, 19 | }) 20 | .option('d', { 21 | description: 'Debug', 22 | alias: 'debug', 23 | boolean: true, 24 | default: false 25 | }) 26 | .argv; 27 | 28 | const targetPath = path.normalize(argv.target); 29 | 30 | updatePluginVersion(); 31 | 32 | function updatePluginVersion() { 33 | if (!process.env.OTGS_CI_REPLACEMENTS && argv.debug) { 34 | setTestPatterns(); 35 | } 36 | 37 | const tag = argv.ref.trim(); 38 | 39 | if (process.env.OTGS_CI_REPLACEMENTS) { 40 | const currentDirectory = process.cwd(); 41 | process.chdir(targetPath); 42 | 43 | const mainPluginFile = getMainPluginFile(); 44 | 45 | if (mainPluginFile) { 46 | const file = mainPluginFile.file; 47 | const content = mainPluginFile.content; 48 | let updatedContent = content; 49 | 50 | console.info('- Found "' + file + '": updating...'); 51 | 52 | const replacement_patterns = JSON.parse(process.env.OTGS_CI_REPLACEMENTS.replace(/(%)(\d)/g, '$$$2')); 53 | 54 | replacement_patterns 55 | .filter(regex_args => { 56 | const regExp = new RegExp(regex_args.searchPattern, 'g'); 57 | return regExp.test(updatedContent); 58 | }) 59 | .map((regex_args, index) => { 60 | 61 | const use = regex_args.extractSemVer ? regex_args.extractSemVer : false; 62 | const tagName = use ? extractSemVer(tag) : tag; 63 | const tagSlug = tagName.trim().replace(/\./g, '-'); 64 | 65 | const regExp = new RegExp(regex_args.searchPattern, 'g'); 66 | 67 | if (regExp.test(updatedContent)) { 68 | process.stdout.write((index + 1) + ') Will search for "' + regex_args.searchPattern); 69 | process.stdout.write(' using "' + tagName + '" as a tag and "' + tagSlug + '" as a tag slug'); 70 | process.stdout.write(' and replacing it with "' + regex_args.replacePattern + '"\n'); 71 | 72 | updatedContent = updatedContent.replace(regExp, regex_args.replacePattern) 73 | .replace(/{{tag-slug}}/g, tagSlug) 74 | .replace(/{{tag}}/g, tagName); 75 | } 76 | }); 77 | 78 | if (!argv.dryRun && updatedContent !== content) { 79 | fs.writeFileSync(file, updatedContent, {encoding: 'utf8'}); 80 | } 81 | 82 | process.chdir(currentDirectory); 83 | } 84 | } else { 85 | console.info('A constant named OTGS_CI_REPLACEMENTS hasn\'t been set: skipping.'); 86 | } 87 | } 88 | 89 | function extractSemVer(version) { 90 | const versionElements = version 91 | .trim() 92 | .replace(/-/g, '.') 93 | .replace(/_/g, '.') 94 | .replace(/\+/g, '.') 95 | .replace(/([^0-9.]+)/, '.$1.') 96 | .replace(/\.{2,}/g, '.') 97 | .split('.'); 98 | 99 | const nakedElements = ['0', '0', '0']; 100 | 101 | versionElements 102 | .filter(element => !isNaN(element)) 103 | .slice(0, 3) 104 | .map((element, index) => { 105 | nakedElements[index] = element; 106 | }); 107 | 108 | return nakedElements.join('.'); 109 | } 110 | 111 | function getMainPluginFile() { 112 | const files = fs.readdirSync(process.cwd()); 113 | 114 | const phpFiles = files 115 | .filter(file => path.extname(file).toLowerCase() === '.php') 116 | .filter(file => { 117 | 118 | const content = fs.readFileSync(file, 'utf8') 119 | .replace(/[\t\n\r]/g, '') 120 | .trim(); 121 | 122 | return content.indexOf(' 0 124 | && content.indexOf('Description: ') > 0; 125 | 126 | }); 127 | 128 | if (phpFiles) { 129 | const file = phpFiles[0]; 130 | return {file, content: fs.readFileSync(file, 'utf8')}; 131 | } 132 | return null; 133 | } 134 | 135 | function setTestPatterns() { 136 | const testPatterns = [ 137 | { 138 | "searchPattern": "(Version:\\s*)(\\d*.*)", 139 | "replacePattern": "%1{{tag}}" 140 | }, 141 | { 142 | "searchPattern": "(GRAVITYFORMS_MULTILINGUAL_VERSION\\',\\s*\\')(\\d*.*)(\\')", 143 | "replacePattern": "%1{{tag}}%3", 144 | "extractSemVer": true 145 | }, 146 | { 147 | "searchPattern": "(wpml.org\\/version\\/wpml-)([\\d\\-*]*)(\\/\">WPML )([\\d\\.*]*)( release notes)", 148 | "replacePattern": "%1{{tag-slug}}%3{{tag}}%5" 149 | }, 150 | { 151 | "searchPattern": "(WCML_VERSION\\',\\s*\\')(\\d*.*)(\\')", 152 | "replacePattern": "%1{{tag}}%3", 153 | "extractSemVer": true 154 | }, 155 | { 156 | "searchPattern": "(wpml.org\\/version\\/cms-nav-)([\\d\\-*]*)(\\/\">WPML CMS Nav )([\\d\\.*]*)( release notes)", 157 | "replacePattern": "%1{{tag-slug}}%3{{tag}}%5" 158 | }, 159 | { 160 | "searchPattern": "(wpml.org\\/version\\/gravityforms-multilingual-)([\\d\\-*]*)(\\/\">Gravity Forms Multilingual )([\\d\\.*]*)( release notes)", 161 | "replacePattern": "%1{{tag-slug}}%3{{tag}}%5" 162 | }, 163 | { 164 | "searchPattern": "(wpml.org\\/version\\/media-translation-)([\\d\\-*]*)(\\/\">WPML Media Translation )([\\d\\.*]*)( release notes)", 165 | "replacePattern": "%1{{tag-slug}}%3{{tag}}%5" 166 | }, 167 | { 168 | "searchPattern": "(wpml.org\\/version\\/sticky-links-)([\\d\\-*]*)(\\/\">WPML Sticky Links )([\\d\\.*]*)( release notes)", 169 | "replacePattern": "%1{{tag-slug}}%3{{tag}}%5" 170 | }, 171 | { 172 | "searchPattern": "(wpml.org\\/version\\/string-translation-)([\\d\\-*]*)(\\/\">WPML String Translation )([\\d\\.*]*)( release notes)", 173 | "replacePattern": "%1{{tag-slug}}%3{{tag}}%5" 174 | }, 175 | { 176 | "searchPattern": "(wpml.org\\/version\\/translation-management-)([\\d\\-*]*)(\\/\">WPML Translation Management )([\\d\\.*]*)( release notes)", 177 | "replacePattern": "%1{{tag-slug}}%3{{tag}}%5" 178 | }, 179 | { 180 | "searchPattern": "(ICL_SITEPRESS_VERSION\\',\\s*\\')(\\d*.*)(\\')", 181 | "replacePattern": "%1{{tag}}%3", 182 | "extractSemVer": true 183 | }, 184 | { 185 | "searchPattern": "(WPML_CMS_NAV_VERSION\\',\\s*\\')(\\d*.*)(\\')", 186 | "replacePattern": "%1{{tag}}%3", 187 | "extractSemVer": true 188 | }, 189 | { 190 | "searchPattern": "(WPML_MEDIA_VERSION\\',\\s*\\')(\\d*.*)(\\')", 191 | "replacePattern": "%1{{tag}}%3", 192 | "extractSemVer": true 193 | }, 194 | { 195 | "searchPattern": "(WPML_ST_VERSION\\',\\s*\\')(\\d*.*)(\\')", 196 | "replacePattern": "%1{{tag}}%3", 197 | "extractSemVer": true 198 | }, 199 | { 200 | "searchPattern": "(WPML_STICKY_LINKS_VERSION\\',\\s*\\')(\\d*.*)(\\')", 201 | "replacePattern": "%1{{tag}}%3", 202 | "extractSemVer": true 203 | }, 204 | { 205 | "searchPattern": "(WPML_TM_VERSION\\',\\s*\\')(\\d*.*)(\\')", 206 | "replacePattern": "%1{{tag}}%3", 207 | "extractSemVer": true 208 | } 209 | ]; 210 | 211 | process.env.OTGS_CI_REPLACEMENTS = JSON.stringify(testPatterns); 212 | } 213 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | --- 2 | checks: 3 | php: 4 | code_rating: true 5 | duplication: true 6 | filter: 7 | excluded_paths: 8 | - "tests/" 9 | - "vendor/" 10 | coding_style: 11 | php: 12 | indentation: 13 | general: 14 | use_tabs: true 15 | spaces: 16 | around_operators: 17 | concatenation: true 18 | negation: true 19 | 20 | build: 21 | cache: 22 | directories: 23 | - vendor/ 24 | nodes: 25 | phpcs: 26 | environment: 27 | php: 7.3 28 | tests: 29 | override: 30 | - on_node: 1 31 | idle_timeout: 4800 32 | command: "phpcs-run ./" 33 | 34 | php56: 35 | environment: 36 | php: 5.6 37 | php70: 38 | environment: 39 | php: 7.0 40 | php71: 41 | environment: 42 | php: 7.1 43 | php72: 44 | environment: 45 | php: 7.2 46 | 47 | php73-cover: 48 | environment: 49 | php: 7.3 50 | tests: 51 | override: 52 | - on_node: 2 53 | idle_timeout: 4800 54 | command: "./vendor/bin/phpunit --fail-on-warning --coverage-clover ./coverage.xml" 55 | coverage: 56 | file: ./coverage.xml 57 | format: php-clover 58 | tests: 59 | override: 60 | - on_node: 1 61 | idle_timeout: 4800 62 | command: "./vendor/bin/phpunit" 63 | 64 | build_failure_conditions: 65 | - 'project.metric_change("scrutinizer.test_coverage", < 0)' 66 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Tutorial: http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/ 2 | # Docs: https://www.gnu.org/software/make/ 3 | 4 | # Help 5 | .PHONY: help 6 | 7 | help: 8 | $(info Run `make setup` to configure the Git Hooks and install the dependencies`) 9 | $(info Run `make install` to install the dependencies) 10 | $(info Run `make install-prod` to install the dependencies in production mode) 11 | $(info - Run `make composer-install` to only install Composer dependencies) 12 | $(info - Run `make composer-install-prod` to only install Composer dependencies in production mode) 13 | $(info - Run `make npm-install` to only install Node dependencies) 14 | $(info - Run `make npm-install-prod` to only install Node dependencies in production mode) 15 | $(info Run `make tests` to run all tests) 16 | $(info - Run `make jest` to run only Jest tests) 17 | $(info - Run `make phpunit` to run only PhpUnit tests) 18 | $(info Run `make dev` to bundle WebPack modules in development mode) 19 | $(info Run `make prod` to bundle WebPack modules in production mode) 20 | 21 | # Setup 22 | .PHONY: setup githooks 23 | 24 | setup:: githooks 25 | setup:: install 26 | 27 | githooks: 28 | ifdef CI 29 | $(info Skipping Git Hooks in CI) 30 | else ifdef OS 31 | cp .githooks/* .git/hooks/ 32 | $(info Looks like you are on Windows... files copied.) 33 | 34 | else 35 | @find .git/hooks -type l -exec rm {} \; 36 | @find .githooks -type f -exec ln -sf ../../{} .git/hooks/ \; 37 | $(info Git Hooks installed) 38 | endif 39 | 40 | # Install 41 | .PHONY: install 42 | 43 | install: composer-install 44 | install: npm-install 45 | 46 | install-prod: composer-install-prod 47 | install-prod: npm-install-prod 48 | 49 | # Build 50 | .PHONY: dev prod 51 | 52 | dev prod: npm-install 53 | @npm run build:$@ 54 | $(info WebPack modules bundled) 55 | 56 | # Tests 57 | .PHONY: tests 58 | 59 | tests:: jest 60 | tests:: phpunit 61 | 62 | # Git Hooks 63 | .PHONY: precommit 64 | 65 | precommit:: validate-composer 66 | precommit:: validate-npm 67 | precommit:: dupes 68 | precommit:: compatibility 69 | 70 | # precommit 71 | .PHONY: dupes compatibility validate-composer validate-npm 72 | 73 | dupes: composer-install 74 | ./.make/check-duplicates.sh 75 | 76 | compatibility: composer-install 77 | ./.make/check-compatibility.sh 78 | 79 | validate-composer: composer-install 80 | ./.make/check-composer.sh 81 | 82 | validate-npm: npm-install 83 | ./.make/check-npm.sh 84 | 85 | 86 | # Dependency managers 87 | 88 | ## Composer 89 | .PHONY: composer-install 90 | 91 | composer.lock: composer-install 92 | @touch $@ 93 | 94 | vendor/autoload.php: composer-install 95 | @touch $@ 96 | 97 | composer-install: 98 | $(info Installing Composer dependencies) 99 | @composer install 100 | 101 | composer-install-prod: 102 | $(info Installing Composer dependencies) 103 | @composer --no-dev install 104 | 105 | ## NPM 106 | .PHONY: npm-install 107 | 108 | package.json: npm-install 109 | @touch $@ 110 | 111 | package-lock.json: npm-install 112 | @touch $@ 113 | 114 | npm-install: 115 | $(info Installing Node dependencies) 116 | @npm install 117 | 118 | npm-install-prod: 119 | $(info Installing Node dependencies) 120 | @npm --prod install 121 | 122 | # Tests 123 | .PHONY: jest phpunit 124 | 125 | jest: npm-install 126 | $(info Running Jest) 127 | @npm run test 128 | 129 | phpunit: composer-install 130 | $(info Running PhpUnit) 131 | @vendor/bin/phpunit --fail-on-warning 132 | # Use the following if the phpunit.xml is on a different location 133 | # @vendor/bin/phpunit --fail-on-warning --configuration tests/phpunit/phpunit.xml 134 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This package has been merged into the [WPML Page Builders](https://git.onthegosystems.com/glue-plugins/wpml/wpml-page-builders) addon. 2 | 3 | ---- 4 | 5 | # wpml-page-builders-elementor 6 | A library used by [WPML](https://wpml.org) for handling [Elementor](https://wpml.org/documentation/plugins-compatibility/elementor/). 7 | 8 | ![Code Quality](https://scrutinizer-ci.com/g/OnTheGoSystems/wpml-page-builders-elementor/badges/quality-score.png?b=master) 9 | 10 | ![Coverage](https://scrutinizer-ci.com/g/OnTheGoSystems/wpml-page-builders-elementor/badges/coverage.png?b=master) 11 | 12 | [![Build](https://scrutinizer-ci.com/g/OnTheGoSystems/wpml-page-builders-elementor/badges/build.png?b=master)](https://scrutinizer-ci.com/g/OnTheGoSystems/wpml-page-builders-elementor/build-status/master) 13 | 14 | ## Packagist 15 | 16 | [![Total Downloads](https://poser.pugx.org/wpml/page-builders-elementor/downloads)](https://packagist.org/packages/wpml/page-builders-elementor) 17 | 18 | [![License](https://poser.pugx.org/wpml/page-builders-elementor/license)](https://packagist.org/packages/wpml/page-builders-elementor) 19 | 20 | [![Latest Stable Version](https://poser.pugx.org/wpml/page-builders-elementor/v/stable)](https://packagist.org/packages/wpml/page-builders-elementor) 21 | 22 | [![Latest Unstable Version](https://poser.pugx.org/wpml/page-builders-elementor/v/unstable)](https://packagist.org/packages/wpml/page-builders-elementor) 23 | 24 | [![composer.lock](https://poser.pugx.org/wpml/page-builders-elementor/composerlock)](https://packagist.org/packages/wpml/page-builders-elementor) 25 | 26 | ## Monthly Downloads 27 | [![Monthly Downloads](https://poser.pugx.org/wpml/page-builders-elementor/d/monthly)](https://packagist.org/packages/wpml/page-builders-elementor) 28 | 29 | ## Daily Downloads 30 | [![Daily Downloads](https://poser.pugx.org/wpml/page-builders-elementor/d/daily)](https://packagist.org/packages/wpml/page-builders-elementor) 31 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wpml/page-builders-elementor", 3 | "description": "A library used by WPML to handle Elementor", 4 | "type": "library", 5 | "license": "GPL-3.0-or-later", 6 | "authors": [ 7 | { 8 | "name": "OnTheGoSystems", 9 | "email": "hello@wpml.org" 10 | } 11 | ], 12 | "repositories": { 13 | "elementor": { 14 | "type":"package", 15 | "package": { 16 | "name": "elementor/elementor", 17 | "version":"master", 18 | "source": { 19 | "url": "https://github.com/elementor/elementor.git", 20 | "type": "git", 21 | "reference":"master" 22 | } 23 | } 24 | }, 25 | "collect": { 26 | "type": "vcs", 27 | "url": "https://github.com/OnTheGoSystems/collect.git" 28 | } 29 | }, 30 | "autoload": { 31 | "classmap": [ 32 | "src/" 33 | ] 34 | }, 35 | "autoload-dev": { 36 | "classmap": [ 37 | "tests/phpunit/stubs/", 38 | "tests/phpunit/util/" 39 | ] 40 | }, 41 | "require": { 42 | "roave/security-advisories": "dev-master", 43 | "jakeasmith/http_build_url": "^1.0" 44 | }, 45 | "require-dev": { 46 | "phpunit/phpunit": "~5.7", 47 | "otgs/unit-tests-framework": "~1.2.0", 48 | "wpml/page-builders": "dev-master", 49 | "elementor/elementor": "dev-master", 50 | "wpml/collect": "dev-wpml-collect-rename", 51 | "wpml/fp": "^0.1.1", 52 | "wpml/wp": "^0.1.1", 53 | "composer/composer": "^1.10" 54 | }, 55 | "scripts": { 56 | "post-update-cmd": [ 57 | "sed -i'' 's/final public function add_group_control/public function add_group_control/g' ./vendor/elementor/elementor/includes/base/controls-stack.php" 58 | ] 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | WPML Coding Standards 4 | 5 | 6 | 7 | 8 | 9 | */vendor/* 10 | */tests/* 11 | *.js 12 | *.mo 13 | *.po 14 | *.twig 15 | *.css 16 | *.scss 17 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | ./tests/phpunit/tests/ 12 | 13 | 14 | 15 | 16 | ./src 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Config/Factory.php: -------------------------------------------------------------------------------- 1 | 'elementor-widgets', 9 | 'defaultConditionKey' => 'widgetType', 10 | 'pbKey' => 'elementor', 11 | 'translatableWidgetsHook' => 'wpml_elementor_widgets_to_translate', 12 | ]; 13 | 14 | /** 15 | * @inheritDoc 16 | */ 17 | protected function getPbData( $key ) { 18 | return self::DATA[ $key ]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/DataConvert.php: -------------------------------------------------------------------------------- 1 | tagValue = $tagValue; 45 | $this->tagKey = $tagKey; 46 | $this->nodeId = $nodeId; 47 | $this->itemId = $itemId; 48 | } 49 | 50 | /** 51 | * @see \WPML_Elementor_Translatable_Nodes::get_string_name() 52 | * @see \WPML_Elementor_Module_With_Items::get_string_name() 53 | * 54 | * @param WPML_PB_String $string 55 | * 56 | * @return bool 57 | */ 58 | public function isMatchingStaticString( WPML_PB_String $string ) { 59 | $pattern = '/^' . $this->tagKey . '-.*-' . $this->nodeId . '$/'; 60 | 61 | if ( $this->itemId ) { 62 | $pattern = '/^' . $this->tagKey . '-.*-' . $this->nodeId . '-' . $this->itemId . '$/'; 63 | } 64 | 65 | return (bool) preg_match( $pattern, $string->get_name() ); 66 | } 67 | } -------------------------------------------------------------------------------- /src/DynamicContent/Strings.php: -------------------------------------------------------------------------------- 1 | first( function( Field $field, $key ) use ( $string ) { 41 | return $field->isMatchingStaticString( $string ); 42 | } ); 43 | 44 | if ( $matchingField ) { 45 | return self::addBeforeAfterAndFallback( wpml_collect( [ $dynamicFields->pull( $dynamicFields->search( $matchingField ) ) ] ), $nodeId ); 46 | } 47 | 48 | return $string; 49 | }; 50 | 51 | return wpml_collect( $strings ) 52 | ->map( $updateFromDynamicFields ) 53 | ->merge( self::addBeforeAfterAndFallback( $dynamicFields, $nodeId ) ) 54 | ->flatten() 55 | ->toArray(); 56 | } 57 | 58 | /** 59 | * @param array $element 60 | * 61 | * @return Collection 62 | */ 63 | private static function getDynamicFields( array $element ) { 64 | if ( self::isModuleWithItems( $element ) ) { 65 | return self::getDynamicFieldsForModuleWithItems( $element ); 66 | } elseif ( isset( $element[ self::KEY_SETTINGS ][ self::KEY_DYNAMIC ] ) ) { 67 | return self::getFields( 68 | $element[ self::KEY_SETTINGS ][ self::KEY_DYNAMIC ], 69 | $element[ self::KEY_NODE_ID ] 70 | ); 71 | } 72 | 73 | return wpml_collect(); 74 | } 75 | 76 | /** 77 | * @param array $element 78 | * 79 | * @return Collection 80 | */ 81 | private static function getDynamicFieldsForModuleWithItems( array $element ) { 82 | $isDynamic = function( $item ) { return isset( $item[ self::KEY_DYNAMIC ] ); }; 83 | $getFields = function( array $item ) use ( $element ) { 84 | return self::getFields( 85 | $item[ self::KEY_DYNAMIC ], 86 | $element[ self::KEY_NODE_ID ], 87 | $item[ self::KEY_ITEM_ID ] 88 | ); 89 | }; 90 | 91 | return wpml_collect( reset( $element[ self::KEY_SETTINGS ] ) ) 92 | ->filter( $isDynamic ) 93 | ->map( $getFields ) 94 | ->flatten(); 95 | } 96 | 97 | /** 98 | * @param array $data 99 | * @param string $nodeId 100 | * @param string $itemId 101 | * 102 | * @return Collection 103 | */ 104 | private static function getFields( array $data, $nodeId, $itemId = '' ) { 105 | $buildField = function( $tagValue, $tagKey ) use ( $nodeId, $itemId ) { 106 | return new Field( $tagValue, $tagKey, $nodeId, $itemId ); 107 | }; 108 | 109 | return wpml_collect( $data )->map( $buildField ); 110 | } 111 | 112 | /** 113 | * @param array $element 114 | * 115 | * @return bool 116 | */ 117 | private static function isModuleWithItems( array $element ) { 118 | if ( isset( $element[ self::KEY_SETTINGS ] ) ) { 119 | $firstSettingElement = reset( $element[ self::KEY_SETTINGS ] ); 120 | return is_array( $firstSettingElement ) && 0 === key( $firstSettingElement ); 121 | } 122 | 123 | return false; 124 | } 125 | 126 | /** 127 | * @param Collection $dynamicFields 128 | * @param string $nodeId 129 | * 130 | * @return Collection 131 | */ 132 | private static function addBeforeAfterAndFallback( Collection $dynamicFields, $nodeId ) { 133 | $dynamicFieldToSettingStrings = function( Field $field ) use ( $nodeId ) { 134 | preg_match( self::SETTINGS_REGEX, $field->tagValue, $matches ); 135 | 136 | $isTranslatableSetting = function( $value, $settingField ) { 137 | return $value && in_array( $settingField, self::TRANSLATABLE_SETTINGS ); 138 | }; 139 | 140 | $buildStringFromSetting = function( $value, $settingField ) use ( $field ) { 141 | return new WPML_PB_String( 142 | $value, 143 | self::getStringName( $field->nodeId, $field->itemId, $field->tagKey, $settingField ), 144 | sprintf( __( 'Dynamic content string: %s', 'sitepress' ), $field->tagKey ), 145 | 'LINE' 146 | ); 147 | }; 148 | 149 | return wpml_collect( isset( $matches[1] ) ? self::decodeSettings( $matches[1] ) : [] ) 150 | ->filter( $isTranslatableSetting ) 151 | ->map( $buildStringFromSetting ); 152 | }; 153 | 154 | return $dynamicFields->map( $dynamicFieldToSettingStrings ); 155 | } 156 | 157 | /** 158 | * @param array $element 159 | * @param WPML_PB_String $string 160 | * 161 | * @return array 162 | */ 163 | public static function updateNode( array $element, WPML_PB_String $string ) { 164 | $stringNameParts = explode( self::DELIMITER, $string->get_name() ); 165 | 166 | if ( count( $stringNameParts ) !== 5 || self::NAME_PREFIX !== $stringNameParts[0] ) { 167 | return $element; 168 | } 169 | 170 | list( , , $itemId, $dynamicField, $settingField ) = $stringNameParts; 171 | 172 | if ( $itemId && self::isModuleWithItems( $element ) ) { 173 | $element = self::updateNodeWithItems( $element, $string, $stringNameParts ); 174 | } elseif ( isset( $element[ self::KEY_SETTINGS ][ self::KEY_DYNAMIC ][ $dynamicField ] ) ) { 175 | $element[ self::KEY_SETTINGS ][ self::KEY_DYNAMIC ][ $dynamicField ] = self::replaceSettingString( 176 | $element[ self::KEY_SETTINGS ][ self::KEY_DYNAMIC ][ $dynamicField ], 177 | $string, 178 | $settingField 179 | ); 180 | } 181 | 182 | return $element; 183 | } 184 | 185 | /** 186 | * @param string $encodedSettings 187 | * @param WPML_PB_String $string 188 | * @param string $settingField 189 | * 190 | * @return array 191 | */ 192 | private static function replaceSettingString( $encodedSettings, WPML_PB_String $string, $settingField ) { 193 | $replace = function( array $matches ) use ( $string, $settingField ) { 194 | $settings = self::decodeSettings( $matches[1] ); 195 | $settings[ $settingField ] = $string->get_value(); 196 | $replace = urlencode( json_encode( $settings ) ); 197 | 198 | return str_replace( $matches[1], $replace, $matches[0] ); 199 | }; 200 | 201 | return preg_replace_callback( self::SETTINGS_REGEX, $replace, $encodedSettings ); 202 | } 203 | 204 | /** 205 | * @param array $element 206 | * @param WPML_PB_String $string 207 | * @param array $stringNameParts 208 | * 209 | * @return array 210 | */ 211 | private static function updateNodeWithItems( array $element, WPML_PB_String $string, array $stringNameParts ) { 212 | list( , , $itemId, $dynamicField, $settingField ) = $stringNameParts; 213 | 214 | $items = wpml_collect( reset( $element[ self::KEY_SETTINGS ] ) ); 215 | $mainKey = key( $element[ self::KEY_SETTINGS ] ); 216 | 217 | $replaceStringInItem = function( array $item ) use ( $itemId, $string, $dynamicField, $settingField ) { 218 | if ( 219 | isset( $item[ self::KEY_DYNAMIC ][ $dynamicField ], $item[ self::KEY_ITEM_ID ] ) 220 | && $item[ self::KEY_ITEM_ID ] === $itemId 221 | ) { 222 | $item[ self::KEY_DYNAMIC ][ $dynamicField ] = self::replaceSettingString( $item[ self::KEY_DYNAMIC ][ $dynamicField ], $string, $settingField ); 223 | } 224 | 225 | return $item; 226 | }; 227 | 228 | $element[ self::KEY_SETTINGS ][ $mainKey ] = $items->map( $replaceStringInItem )->toArray(); 229 | 230 | return $element; 231 | } 232 | 233 | /** 234 | * @param string $settingsString 235 | * 236 | * @return array 237 | */ 238 | private static function decodeSettings( $settingsString ) { 239 | return json_decode( urldecode( $settingsString ), true ); 240 | } 241 | 242 | /** 243 | * @param string $nodeId 244 | * @param string $itemId 245 | * @param string $tagKey 246 | * @param string $settingField 247 | * 248 | * @return string 249 | */ 250 | public static function getStringName( $nodeId, $itemId, $tagKey, $settingField ) { 251 | return self::NAME_PREFIX . self::DELIMITER 252 | . $nodeId . self::DELIMITER 253 | . $itemId . self::DELIMITER 254 | . $tagKey . self::DELIMITER 255 | . $settingField; 256 | } 257 | } -------------------------------------------------------------------------------- /src/Hooks/DynamicElements.php: -------------------------------------------------------------------------------- 1 | isDynamicLink( $item ) ) { 19 | $item['settings']['__dynamic__']['link'] = $this->convertPopUpId( $item['settings']['__dynamic__']['link'] ); 20 | } 21 | 22 | $item['elements'] = $this->convert( $item['elements'] ); 23 | } 24 | 25 | return $data; 26 | } 27 | 28 | /** 29 | * @param array $data 30 | * 31 | * @return bool 32 | */ 33 | private function isDynamicLink( array $data ) { 34 | return isset( $data['elType'] ) 35 | && 'widget' === $data['elType'] 36 | && isset( $data['settings']['__dynamic__']['link'] ); 37 | } 38 | 39 | /** 40 | * @param string $tagString e.g. "[elementor-tag id="d3587f6" name="popup" settings="%7B%22popup%22%3A%228%22%7D"]" 41 | * 42 | * @return string 43 | */ 44 | private function convertPopUpId( $tagString ) { 45 | preg_match( '/name="(.*?(?="))"/', $tagString, $tagNameMatch ); 46 | 47 | if ( ! $tagNameMatch || $tagNameMatch[1] !== 'popup' ) { 48 | return $tagString; 49 | } 50 | 51 | return preg_replace_callback( '/settings="(.*?(?="]))/', function( array $matches ) { 52 | $settings = json_decode( urldecode( $matches[1] ), true ); 53 | 54 | if ( ! isset( $settings['popup'] ) ) { 55 | return $matches[0]; 56 | } 57 | 58 | $settings['popup'] = $this->convertId( $settings['popup'] ); 59 | $replace = urlencode( json_encode( $settings ) ); 60 | 61 | return str_replace( $matches[1], $replace, $matches[0] ); 62 | 63 | }, $tagString ); 64 | } 65 | 66 | /** 67 | * @param int $elementId 68 | * 69 | * @return int 70 | */ 71 | private function convertId( $elementId ) { 72 | return apply_filters( 'wpml_object_id', $elementId, get_post_type( $elementId ), true ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Hooks/Frontend.php: -------------------------------------------------------------------------------- 1 | bool 42 | $ifValueHasChanged = pipe( Relation::equals( $metaValue ), Logic::not() ); 43 | 44 | // $update :: int -> string -> bool 45 | $update = curryN( 2, function( $postId, $meta ) { 46 | // Do not use update_post_meta, we need update meta for revisions too. 47 | update_metadata( 'post', $postId, WPML_Elementor_Data_Settings::META_KEY_DATA, $meta ); 48 | self::deletePackage( self::getGutenbergPackage( $postId ) ); 49 | return true; 50 | } ); 51 | 52 | return Maybe::of( $metaValue ) 53 | ->map( [ DataConvert::class, 'unserialize' ] ) 54 | ->map( [ $this, 'removeBlockMetaInEditorWidget' ] ) 55 | ->map( [ DataConvert::class, 'serialize' ] ) 56 | ->filter( $ifValueHasChanged ) 57 | ->map( $update( $postId ) ) 58 | ->getOrElse( $check ); 59 | } 60 | 61 | return $check; 62 | } 63 | 64 | /** 65 | * @param array $data 66 | * 67 | * @return array 68 | */ 69 | public function removeBlockMetaInEditorWidget( array $data ) { 70 | foreach ( $data as &$element ) { 71 | if ( $element['elements'] ) { 72 | $element['elements'] = $this->removeBlockMetaInEditorWidget( $element['elements'] ); 73 | } elseif ( 'widget' === $element['elType'] && isset( $element['settings']['editor'] ) ) { 74 | $element['settings']['editor'] = preg_replace( '()', '', $element['settings']['editor'] ); 75 | } 76 | } 77 | 78 | return $data; 79 | } 80 | 81 | /** 82 | * @param int $postId 83 | */ 84 | public static function getGutenbergPackage( $postId ) { 85 | // $isGbPackage :: \WPML_Package -> bool 86 | $isGbPackage = Relation::propEq( 'kind_slug', 'gutenberg' ); 87 | 88 | return wpml_collect( apply_filters( 'wpml_st_get_post_string_packages', [], $postId ) ) 89 | ->filter( $isGbPackage ) 90 | ->first(); 91 | } 92 | 93 | /** 94 | * @param \WPML_Package|null $package 95 | */ 96 | public static function deletePackage( $package ) { 97 | $package && do_action( 'wpml_delete_package', $package->name, $package->kind ); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/Hooks/LandingPages.php: -------------------------------------------------------------------------------- 1 | sitepress = $sitepress; 16 | } 17 | 18 | public function add_hooks() { 19 | if ( get_option( 'permalink_structure' ) ) { 20 | add_filter( 'post_type_link', [ $this, 'adjustLink' ], PHP_INT_MAX, 2 ); 21 | } 22 | } 23 | 24 | /** 25 | * @see \Elementor\Modules\LandingPages\Module::remove_post_type_slug 26 | * 27 | * @param string $postUrl 28 | * @param \WP_Post $post 29 | * 30 | * @return string 31 | */ 32 | public function adjustLink( $postUrl, $post ) { 33 | if ( self::POST_TYPE !== $post->post_type || 'publish' !== $post->post_status) { 34 | return $postUrl; 35 | } 36 | 37 | $homeUrl = get_home_url(); 38 | $urlParts = wp_parse_url( $homeUrl ); 39 | $urlParts['path'] = trailingslashit( Obj::prop( 'path', $urlParts ) ) . $post->post_name . '/'; 40 | $newPostUrl = http_build_url( null, $urlParts ); 41 | $postLangCode = $this->sitepress->get_language_for_element( $post->ID, 'post_' . self::POST_TYPE ); 42 | 43 | return $this->sitepress->convert_url( $newPostUrl, $postLangCode ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/LanguageSwitcher/LanguageSwitcher.php: -------------------------------------------------------------------------------- 1 | widgets_manager->register_widget_type( new Widget() ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/LanguageSwitcher/Widget.php: -------------------------------------------------------------------------------- 1 | adaptor = $adaptor ?: new WidgetAdaptor(); 13 | $this->adaptor->setTarget( $this ); 14 | parent::__construct( $data, $args ); 15 | 16 | } 17 | 18 | /** @return string */ 19 | public function get_name() { 20 | return $this->adaptor->getName(); 21 | } 22 | 23 | /** @return string */ 24 | public function get_title() { 25 | return $this->adaptor->getTitle(); 26 | } 27 | 28 | /** @return string */ 29 | public function get_icon() { 30 | return $this->adaptor->getIcon(); 31 | } 32 | 33 | /** @return array */ 34 | public function get_categories() { 35 | return $this->adaptor->getCategories(); 36 | } 37 | 38 | /** 39 | * Register controls. 40 | * 41 | * Used to add new controls to any element type. For example, external 42 | * developers use this method to register controls in a widget. 43 | * 44 | * Should be inherited and register new controls using `add_control()`, 45 | * `add_responsive_control()` and `add_group_control()`, inside control 46 | * wrappers like `start_controls_section()`, `start_controls_tabs()` and 47 | * `start_controls_tab()`. 48 | */ 49 | protected function _register_controls() { 50 | $this->adaptor->registerControls(); 51 | } 52 | 53 | /** 54 | * Render element. 55 | * 56 | * Generates the final HTML on the frontend. 57 | */ 58 | protected function render() { 59 | $this->adaptor->render(); 60 | } 61 | } -------------------------------------------------------------------------------- /src/class-wpml-elementor-adjust-global-widget-id-factory.php: -------------------------------------------------------------------------------- 1 | create() ); 10 | 11 | return new WPML_Elementor_Adjust_Global_Widget_ID( 12 | $data_settings, 13 | new WPML_Translation_Element_Factory( $sitepress ), 14 | $sitepress 15 | ); 16 | } 17 | } -------------------------------------------------------------------------------- /src/class-wpml-elementor-adjust-global-widget-id.php: -------------------------------------------------------------------------------- 1 | elementor_settings = $elementor_settings; 23 | $this->translation_element_factory = $translation_element_factory; 24 | $this->sitepress = $sitepress; 25 | } 26 | 27 | public function add_hooks() { 28 | add_action( 'elementor/editor/before_enqueue_scripts', array( $this, 'adjust_ids' ) ); 29 | add_action( 'elementor/editor/after_enqueue_scripts', array( $this, 'restore_current_language' ) ); 30 | add_action( 'elementor/frontend/the_content', array( $this, 'duplicate_css_class_with_original_id' ) ); 31 | 32 | if ( is_admin() ) { 33 | add_filter( 34 | 'wpml_should_use_display_as_translated_snippet', 35 | array( $this, 'should_use_display_as_translated_snippet' ), 36 | PHP_INT_MAX, 37 | 2 38 | ); 39 | } 40 | } 41 | 42 | public function adjust_ids() { 43 | $this->current_language = $this->sitepress->get_current_language(); 44 | 45 | $post_id = absint( $_REQUEST['post'] ); // WPCS: sanitization ok. 46 | 47 | $post = $this->translation_element_factory->create_post( $post_id ); 48 | $language = $post->get_language_code(); 49 | $this->sitepress->switch_lang( $language ); 50 | 51 | $custom_field_data = get_post_meta( 52 | $post_id, 53 | $this->elementor_settings->get_meta_field() 54 | ); 55 | 56 | if ( ! $custom_field_data ) { 57 | return; 58 | } 59 | 60 | $custom_field_data = $this->elementor_settings->convert_data_to_array( $custom_field_data ); 61 | 62 | $custom_field_data_adjusted = $this->set_global_widget_id_for_language( $custom_field_data, $language ); 63 | 64 | if ( $custom_field_data_adjusted !== $custom_field_data ) { 65 | update_post_meta( 66 | $post_id, 67 | $this->elementor_settings->get_meta_field(), 68 | $this->elementor_settings->prepare_data_for_saving( $custom_field_data_adjusted ) 69 | ); 70 | 71 | // Update post date so Elementor doesn't use auto saved post 72 | $post_data = get_post( $post_id, ARRAY_A ); 73 | $post_data['post_date'] = current_time( 'mysql' ); 74 | $post_data['post_date_gmt'] = ''; 75 | 76 | wp_update_post( $post_data ); 77 | } 78 | 79 | } 80 | 81 | private function set_global_widget_id_for_language( $data_array, $language ) { 82 | foreach ( $data_array as &$data ) { 83 | if ( isset( $data['elType'] ) && 'widget' === $data['elType'] && 'global' === $data['widgetType'] ) { 84 | try { 85 | $widget_post = $this->translation_element_factory->create_post( $data['templateID'] ); 86 | if ( $widget_post->get_language_code() !== $language ) { 87 | $translation = $widget_post->get_translation( $language ); 88 | if ( $translation ) { 89 | $data['templateID'] = $translation->get_element_id(); 90 | } 91 | } 92 | } catch ( Exception $e ) { 93 | // Not much we can do if the elementor templateID is a non existing post 94 | } 95 | } 96 | $data['elements'] = $this->set_global_widget_id_for_language( $data['elements'], $language ); 97 | } 98 | 99 | return $data_array; 100 | } 101 | 102 | public function restore_current_language() { 103 | $this->sitepress->switch_lang( $this->current_language ); 104 | } 105 | 106 | /** 107 | * The snippet is a WHERE condition which is added to a DB query. 108 | * This will include the source element in the query results in case the element 109 | * does not exist in the current language. 110 | * 111 | * @see WPML_Query_Filter::display_as_translated_snippet 112 | * 113 | * @param bool $display_as_translated 114 | * @param array $post_types 115 | * 116 | * @return bool 117 | */ 118 | public function should_use_display_as_translated_snippet( $display_as_translated, $post_types ) { 119 | if ( isset( $_GET['action'] ) && 'elementor' === $_GET['action'] 120 | && in_array( 'elementor_library', array_keys( $post_types ), true ) ) { 121 | return true; 122 | } 123 | 124 | return $display_as_translated; 125 | } 126 | 127 | /** 128 | * @param string $content 129 | * 130 | * @return string 131 | */ 132 | public function duplicate_css_class_with_original_id( $content ) { 133 | $classPrefixes = wpml_collect( [ 134 | 'elementor-', 135 | 'elementor-global-', 136 | ] )->implode( '|' ); 137 | 138 | return preg_replace_callback( '/(class=".*(' . $classPrefixes . '))(\d+)/', array( $this, 'convert_id_to_original' ), $content ); 139 | } 140 | 141 | private function convert_id_to_original( array $matches ) { 142 | $class_prefix = $matches[2]; 143 | $id = (int)$matches[3]; 144 | $element = $this->translation_element_factory->create_post( $id ); 145 | $source = $element->get_source_element(); 146 | 147 | if ( $source ) { 148 | $new_class = $class_prefix . $source->get_id(); 149 | $search = $matches[0]; 150 | $replace = $search . ' ' . $new_class; 151 | $matches[0] = str_replace( $search, $replace, $matches[0] ); 152 | } 153 | 154 | return $matches[0]; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/class-wpml-elementor-data-settings.php: -------------------------------------------------------------------------------- 1 | elementor_db = $elementor_db; 17 | } 18 | 19 | public function add_hooks() { 20 | add_filter( 'wpml_custom_field_values_for_post_signature', array( $this, 'add_data_custom_field_to_md5' ), 10, 2 ); 21 | add_filter( 'wpml_pb_copy_meta_field', array( $this, 'mark_css_field_as_empty' ), 10, 4 ); 22 | 23 | if ( $this->elementor_db ) { 24 | add_action( 25 | 'wpml_page_builder_string_translated', 26 | array( $this, 'save_post_body_as_plain_text' ), 27 | 11, 28 | 5 29 | ); 30 | } 31 | } 32 | 33 | /** 34 | * @param array $value 35 | * @param int $translated_post_id 36 | * @param int $original_post_id 37 | * @param string $meta_key 38 | * 39 | * @return mixed 40 | */ 41 | public function mark_css_field_as_empty( $value, $translated_post_id, $original_post_id, $meta_key ) { 42 | if ( '_elementor_css' === $meta_key && is_array( $value ) ) { 43 | if ( ! isset( $value['status'] ) ) { 44 | $value = current( $value ); 45 | $value['status'] = ''; 46 | $value = array( $value ); 47 | } else { 48 | $value['status'] = ''; 49 | } 50 | } 51 | 52 | return $value; 53 | } 54 | 55 | public function save_post_body_as_plain_text( $type, $post_id, $original_post, $string_translations, $lang ) { 56 | if ( $this->is_handling_post( $post_id ) ) { 57 | $this->elementor_db->save_plain_text( $post_id ); 58 | } 59 | } 60 | 61 | /** 62 | * @return string 63 | */ 64 | public function get_meta_field() { 65 | return self::META_KEY_DATA; 66 | } 67 | 68 | /** 69 | * @return string 70 | */ 71 | public function get_node_id_field() { 72 | return 'id'; 73 | } 74 | 75 | /** 76 | * @return array 77 | */ 78 | public function get_fields_to_copy() { 79 | return array( 80 | '_elementor_version', 81 | self::META_KEY_MODE, 82 | '_elementor_css', 83 | '_elementor_template_type', 84 | '_elementor_template_widget_type', 85 | ); 86 | } 87 | 88 | /** 89 | * @param array|string $data 90 | * 91 | * @return array 92 | */ 93 | public function convert_data_to_array( $data ) { 94 | return DataConvert::unserialize( $data ); 95 | } 96 | 97 | /** 98 | * @param array $data 99 | * 100 | * @return string 101 | */ 102 | public function prepare_data_for_saving( array $data ) { 103 | return DataConvert::serialize( $data ); 104 | } 105 | 106 | /** 107 | * @return string 108 | */ 109 | public function get_pb_name(){ 110 | return 'Elementor'; 111 | } 112 | 113 | /** 114 | * @return array 115 | */ 116 | public function get_fields_to_save() { 117 | return array( '_elementor_data' ); 118 | } 119 | 120 | /** 121 | * @param array $custom_fields_values 122 | * @param int $post_id 123 | * 124 | * @return array 125 | */ 126 | public function add_data_custom_field_to_md5( array $custom_fields_values, $post_id ) { 127 | $custom_fields_values[] = get_post_meta( $post_id, $this->get_meta_field(), true ); 128 | return $custom_fields_values; 129 | } 130 | 131 | /** 132 | * @param int $postId 133 | * 134 | * @return bool 135 | */ 136 | public function is_handling_post( $postId ) { 137 | return (bool) get_post_meta( $postId, $this->get_meta_field(), true ) 138 | && self::is_edited_with_elementor( $postId ); 139 | } 140 | 141 | /** 142 | * @param int $postId 143 | * 144 | * @return bool 145 | */ 146 | public static function is_edited_with_elementor( $postId ) { 147 | return 'builder' === get_post_meta( $postId, self::META_KEY_MODE, true ); 148 | } 149 | } -------------------------------------------------------------------------------- /src/class-wpml-elementor-db-factory.php: -------------------------------------------------------------------------------- 1 | =' ) && class_exists( '\Elementor\DB' ) ) { 12 | // @codingStandardsIgnoreLine 13 | $elementor_db = new \Elementor\DB(); 14 | 15 | if ( method_exists( $elementor_db, 'save_plain_text' ) ) { 16 | $wpml_elementor_db = new WPML_Elementor_DB( $elementor_db ); 17 | } 18 | } 19 | 20 | return $wpml_elementor_db; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/class-wpml-elementor-db.php: -------------------------------------------------------------------------------- 1 | elementor_db = $elementor_db; 13 | } 14 | 15 | /** 16 | * @param int $post_id 17 | */ 18 | public function save_plain_text( $post_id ) { 19 | $this->elementor_db->save_plain_text( $post_id ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/class-wpml-elementor-integration-factory.php: -------------------------------------------------------------------------------- 1 | load( 17 | array( 18 | 'WPML_Elementor_Translate_IDs_Factory', 19 | 'WPML_Elementor_URLs_Factory', 20 | 'WPML_Elementor_Adjust_Global_Widget_ID_Factory', 21 | 'WPML_PB_Elementor_Handle_Custom_Fields_Factory', 22 | 'WPML_Elementor_Media_Hooks_Factory', 23 | 'WPML_Elementor_WooCommerce_Hooks_Factory', 24 | \WPML\PB\Elementor\LanguageSwitcher\LanguageSwitcher::class, 25 | \WPML\PB\Elementor\Hooks\DynamicElements::class, 26 | \WPML\PB\Elementor\Hooks\GutenbergCleanup::class, 27 | \WPML\PB\Elementor\Hooks\Frontend::class, 28 | \WPML\PB\Elementor\Config\Factory::class, 29 | \WPML\PB\Elementor\Hooks\LandingPages::class, 30 | ) 31 | ); 32 | 33 | $nodes = new WPML_Elementor_Translatable_Nodes(); 34 | $elementor_db_factory = new WPML_Elementor_DB_Factory(); 35 | $data_settings = new WPML_Elementor_Data_Settings( $elementor_db_factory->create() ); 36 | 37 | $string_registration_factory = new WPML_String_Registration_Factory( $data_settings->get_pb_name() ); 38 | $string_registration = $string_registration_factory->create(); 39 | 40 | $register_strings = new WPML_Elementor_Register_Strings( $nodes, $data_settings, $string_registration ); 41 | $update_translation = new WPML_Elementor_Update_Translation( $nodes, $data_settings ); 42 | 43 | return new WPML_Page_Builders_Integration( $register_strings, $update_translation, $data_settings ); 44 | } 45 | } -------------------------------------------------------------------------------- /src/class-wpml-elementor-pb-handle-custom-fields-factory.php: -------------------------------------------------------------------------------- 1 | create() ); 8 | 9 | return new WPML_PB_Handle_Custom_Fields( $data_settings ); 10 | } 11 | } -------------------------------------------------------------------------------- /src/class-wpml-elementor-register-strings.php: -------------------------------------------------------------------------------- 1 | register_strings_for_node( $data[ $this->data_settings->get_node_id_field() ], $data, $package ); 16 | } 17 | foreach ( $data[ 'elements' ] as $column ) { 18 | foreach ( $column[ 'elements' ] as $element ) { 19 | if ( 'widget' === $element['elType'] ) { 20 | $this->register_strings_for_node( $element[ $this->data_settings->get_node_id_field() ], $element, $package ); 21 | } else { 22 | $this->register_strings_for_modules( array( $element ), $package ); 23 | } 24 | } 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/class-wpml-elementor-translate-ids-factory.php: -------------------------------------------------------------------------------- 1 | debug_backtrace = $debug_backtrace; 20 | } 21 | 22 | public function add_hooks() { 23 | add_filter( 'elementor/theme/get_location_templates/template_id', array( $this, 'translate_theme_location_template_id' ) ); 24 | add_filter( 'elementor/theme/get_location_templates/condition_sub_id', array( $this, 'translate_location_condition_sub_id' ), 10, 2 ); 25 | add_filter( 'elementor/documents/get/post_id', array( 26 | $this, 27 | 'translate_template_id' 28 | ) ); 29 | add_filter( 'elementor/frontend/builder_content_data', array( $this, 'translate_global_widget_ids' ), 10, 2 ); 30 | add_filter( 'elementor/frontend/builder_content_data', array( $this, 'translate_product_ids' ), 10, 2 ); 31 | } 32 | 33 | public function translate_theme_location_template_id( $template_id ) { 34 | return $this->translate_id( $template_id ); 35 | } 36 | 37 | /** 38 | * @param int|string $sub_id 39 | * @param array $parsed_condition 40 | * 41 | * @return int|string 42 | */ 43 | public function translate_location_condition_sub_id( $sub_id, $parsed_condition ) { 44 | /** 45 | * `$sub_name` gives a context for the `$sub_id`, it can be either: 46 | * - `child_of` 47 | * - `in_{taxonomy}` 48 | * - `in_{taxonomy}_children` 49 | * - `{post_type}` 50 | * - `{taxonomy}` 51 | */ 52 | $sub_name = isset( $parsed_condition['sub_name'] ) ? $parsed_condition['sub_name'] : null; 53 | 54 | if ( (int) $sub_id > 0 && $sub_name ) { 55 | $element_type = $sub_name; 56 | 57 | if ( 'child_of' === $sub_name ) { 58 | $element_type = get_post_type( $sub_id ); 59 | } elseif ( 0 === strpos( $sub_name, 'in_' ) ) { 60 | $element_type = preg_replace( '/^in_|_children$/', '', $sub_name ); 61 | } 62 | 63 | $sub_id = $this->translate_id( $sub_id, $element_type ); 64 | } 65 | 66 | return $sub_id; 67 | } 68 | 69 | public function translate_template_id( $template_id ) { 70 | if ( $this->is_WP_widget_call() || $this->is_shortcode_call() || $this->is_template_widget_call() ) { 71 | $template_id = $this->translate_id( $template_id ); 72 | } 73 | 74 | return $template_id; 75 | } 76 | 77 | private function is_WP_widget_call() { 78 | return $this->debug_backtrace->is_class_function_in_call_stack( 79 | 'ElementorPro\Modules\Library\WP_Widgets\Elementor_Library', 80 | 'widget' ); 81 | } 82 | 83 | private function is_shortcode_call() { 84 | return $this->debug_backtrace->is_class_function_in_call_stack( 85 | 'ElementorPro\Modules\Library\Classes\Shortcode', 86 | 'shortcode' ); 87 | } 88 | 89 | private function is_template_widget_call() { 90 | return $this->debug_backtrace->is_class_function_in_call_stack( 91 | 'ElementorPro\Modules\Library\Widgets\Template', 92 | 'render' ); 93 | } 94 | 95 | public function translate_global_widget_ids( $data_array, $post_id ) { 96 | foreach ( $data_array as &$data ) { 97 | if ( isset( $data['elType'] ) && 'widget' === $data['elType'] ) { 98 | if ( 'global' === $data['widgetType'] ) { 99 | $data['templateID'] = $this->translate_id( $data['templateID'] ); 100 | } elseif ( 'template' === $data['widgetType'] ) { 101 | $data['settings']['template_id'] = $this->translate_id( $data['settings']['template_id'] ); 102 | } 103 | } 104 | $data['elements'] = $this->translate_global_widget_ids( $data['elements'], $post_id ); 105 | } 106 | 107 | return $data_array; 108 | } 109 | 110 | /** 111 | * @param array $data_array 112 | * @param int $post_id 113 | * 114 | * @return array 115 | */ 116 | public function translate_product_ids( $data_array, $post_id ) { 117 | foreach ( $data_array as &$data ) { 118 | if ( Obj::prop( 'elType', $data ) === 'widget' && Obj::prop( 'widgetType', $data ) === 'wc-add-to-cart' ) { 119 | $data['settings']['product_id'] = $this->translate_id( $data['settings']['product_id'] ); 120 | } 121 | 122 | $data['elements'] = $this->translate_product_ids( $data['elements'], $post_id ); 123 | } 124 | 125 | return $data_array; 126 | } 127 | 128 | /** 129 | * @param int $element_id 130 | * @param string $element_type 131 | * 132 | * @return int 133 | */ 134 | private function translate_id( $element_id, $element_type = null ) { 135 | if ( ! $element_type || $element_type === "any_child_of" ) { 136 | $element_type = get_post_type( $element_id ); 137 | } 138 | 139 | $translated_id = apply_filters( 'wpml_object_id', $element_id, $element_type, true ); 140 | 141 | if ( is_string( $element_id ) ) { 142 | $translated_id = (string) $translated_id; 143 | } 144 | 145 | return $translated_id; 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /src/class-wpml-elementor-update-translation.php: -------------------------------------------------------------------------------- 1 | update_strings_in_modules( $element['elements'] ); 10 | } else if ( 'widget' === $element['elType'] ) { 11 | $element = $this->update_strings_in_node( $element[ $this->data_settings->get_node_id_field() ], $element ); 12 | } 13 | } 14 | } 15 | 16 | /** 17 | * @param int $node_id 18 | * @param array $settings 19 | * 20 | * @return array 21 | */ 22 | protected function update_strings_in_node( $node_id, $settings ) { 23 | $strings = $this->translatable_nodes->get( $node_id, $settings ); 24 | foreach ( $strings as $string ) { 25 | $translation = $this->get_translation( $string ); 26 | 27 | if ( 'VISUAL' === $string->get_editor_type() ) { 28 | $translation->set_value( wpautop( $translation->get_value() ) ); 29 | } 30 | 31 | $settings = $this->translatable_nodes->update( $node_id, $settings, $translation ); 32 | } 33 | return $settings; 34 | } 35 | } -------------------------------------------------------------------------------- /src/class-wpml-elementor-urls-factory.php: -------------------------------------------------------------------------------- 1 | get_strategy(), 11 | $sitepress 12 | ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/class-wpml-elementor-urls.php: -------------------------------------------------------------------------------- 1 | element_factory = $element_factory; 20 | $this->language_converter = $language_converter; 21 | $this->current_language = $current_language; 22 | } 23 | 24 | public function add_hooks() { 25 | add_filter( 'elementor/document/urls/edit', array( $this, 'adjust_edit_with_elementor_url' ), 10, 2 ); 26 | add_filter( 'wpml_is_pagination_url_in_post', [ $this, 'is_pagination_url' ], 10, 3 ); 27 | add_filter( 'paginate_links', [ $this, 'fix_pagination_link_with_language_param' ], 10, 1 ); 28 | } 29 | 30 | public function adjust_edit_with_elementor_url( $url, $elementor_document ) { 31 | $post = $elementor_document->get_main_post(); 32 | 33 | $post_element = $this->element_factory->create_post( $post->ID ); 34 | $post_language = $post_element->get_language_code(); 35 | 36 | if ( ! $post_language ) { 37 | $post_language = $this->current_language->get_current_language(); 38 | } 39 | 40 | return $this->language_converter->convert_admin_url_string( $url, $post_language ); 41 | } 42 | 43 | /** 44 | * Check if the given URL is the pagination inside the post. 45 | * 46 | * @param bool $is_pagination_url_in_post 47 | * @param string $url URL to check 48 | * @param string $post_name Current post name 49 | * 50 | * @return bool 51 | */ 52 | public function is_pagination_url( $is_pagination_url_in_post, $url, $post_name ) { 53 | 54 | $post_name = preg_quote( $post_name, '/' ); 55 | 56 | return $is_pagination_url_in_post 57 | || ( 58 | WPML_Elementor_Data_Settings::is_edited_with_elementor( get_the_ID() ) 59 | && ( 60 | preg_match_all( "/{$post_name}\/([\d]*)\/$/", $url ) 61 | || preg_match_all( "/{$post_name}\/([\d]*)\/\?lang=([a-zA-Z_-]*)$/", $url ) 62 | ) 63 | ); 64 | } 65 | 66 | public function fix_pagination_link_with_language_param( $link ) { 67 | $post = get_post( get_the_ID() ); 68 | if ( 69 | $post 70 | && WPML_Elementor_Data_Settings::is_edited_with_elementor( $post->ID ) 71 | && preg_match_all( "/{$post->post_name}\/\?lang=([a-zA-Z_-]*)\/([\d]*)\/$/", $link ) 72 | ) { 73 | $link = $this->language_converter->convert_url_string( 74 | preg_replace( "/\?lang=([a-zA-Z_-]*)\//", '', $link ), 75 | $this->current_language->get_current_language() 76 | ); 77 | } 78 | 79 | return $link; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/class-wpml-elementor-woocommerce-hooks-factory.php: -------------------------------------------------------------------------------- 1 | query_vars ) && 'product' === $query->query_vars['post_type'] 17 | && isset( $_POST['action'] ) && 'elementor_ajax' === $_POST['action'] 18 | ) { 19 | $query->query_vars['suppress_filters'] = false; 20 | } 21 | 22 | return $query; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/class-wpml-pb-fix-maintenance-query.php: -------------------------------------------------------------------------------- 1 | ID ) && 15 | (int) \Elementor\Maintenance_Mode::get( 'template_id' ) === $GLOBALS['post']->ID 16 | ) { 17 | $GLOBALS['wp_the_query'] = $GLOBALS['wp_query']; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/media/class-wpml-elementor-media-hooks-factory.php: -------------------------------------------------------------------------------- 1 | media_translate = $media_translate; 13 | } 14 | 15 | /** 16 | * @param string $type 17 | * 18 | * @return WPML_Elementor_Media_Node 19 | */ 20 | public function get( $type ) { 21 | if ( ! array_key_exists( $type, $this->nodes ) ) { 22 | switch ( $type ) { 23 | case 'image': 24 | $node = new WPML_Elementor_Media_Node_Image( $this->media_translate ); 25 | break; 26 | 27 | case 'slides': 28 | $node = new WPML_Elementor_Media_Node_Slides( $this->media_translate ); 29 | break; 30 | 31 | case 'call-to-action': 32 | $node = new WPML_Elementor_Media_Node_Call_To_Action( $this->media_translate ); 33 | break; 34 | 35 | case 'media-carousel': 36 | $node = new WPML_Elementor_Media_Node_Media_Carousel( $this->media_translate ); 37 | break; 38 | 39 | case 'image-box': 40 | $node = new WPML_Elementor_Media_Node_Image_Box( $this->media_translate ); 41 | break; 42 | 43 | case 'image-gallery': 44 | $node = new WPML_Elementor_Media_Node_Image_Gallery( $this->media_translate ); 45 | break; 46 | 47 | case 'image-carousel': 48 | $node = new WPML_Elementor_Media_Node_Image_Carousel( $this->media_translate ); 49 | break; 50 | 51 | case 'wp-widget-media_image': 52 | $node = new WPML_Elementor_Media_Node_WP_Widget_Media_Image( $this->media_translate ); 53 | break; 54 | 55 | case 'wp-widget-media_gallery': 56 | $node = new WPML_Elementor_Media_Node_WP_Widget_Media_Gallery( $this->media_translate ); 57 | break; 58 | 59 | case 'all_nodes': 60 | $node = new \WPML\PB\Elementor\Media\Modules\AllNodes( $this->media_translate ); 61 | break; 62 | 63 | case 'video-playlist': 64 | $node = new \WPML\PB\Elementor\Media\Modules\VideoPlaylist( $this->media_translate ); 65 | break; 66 | 67 | case 'hotspot': 68 | $node = new \WPML\PB\Elementor\Media\Modules\Hotspot( $this->media_translate ); 69 | break; 70 | 71 | default: 72 | $node = null; 73 | } 74 | 75 | $this->nodes[ $type ] = $node; 76 | } 77 | 78 | return $this->nodes[ $type ]; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/media/class-wpml-elementor-media-nodes-iterator.php: -------------------------------------------------------------------------------- 1 | node_provider = $node_provider; 10 | } 11 | 12 | /** 13 | * @param array $data_array 14 | * @param string $lang 15 | * @param string $source_lang 16 | * 17 | * @return array 18 | */ 19 | public function translate( $data_array, $lang, $source_lang ) { 20 | foreach ( $data_array as &$node ) { 21 | if ( $this->is_parent_node( $node ) ) { 22 | $node['elements'] = $this->translate( $node['elements'], $lang, $source_lang ); 23 | } elseif ( $this->is_valid_media_node( $node ) ) { 24 | $node = $this->translate_node( $node, $lang, $source_lang ); 25 | } 26 | 27 | if ( isset( $node['settings'] ) ) { 28 | $node["settings"] = $this->node_provider->get( 'all_nodes' )->translate( $node['settings'], $lang, $source_lang ); 29 | } 30 | } 31 | 32 | return $data_array; 33 | } 34 | 35 | /** 36 | * @param array $node 37 | * 38 | * @return bool 39 | */ 40 | private function is_parent_node( $node ) { 41 | return isset( $node['elements'] ) && $node['elements']; 42 | } 43 | 44 | /** 45 | * @param array $node 46 | * 47 | * @return bool 48 | */ 49 | private function is_valid_media_node( $node ) { 50 | return isset( $node['elType'], $node['widgetType'], $node['settings'] ) 51 | && 'widget' === $node['elType']; 52 | } 53 | 54 | /** 55 | * @param stdClass $node_data 56 | * @param string $lang 57 | * @param string $source_lang 58 | * 59 | * @return stdClass 60 | */ 61 | private function translate_node( $node_data, $lang, $source_lang ) { 62 | $node = $this->node_provider->get( $node_data['widgetType'] ); 63 | 64 | if ( $node ) { 65 | $node_data['settings'] = $node->translate( $node_data['settings'], $lang, $source_lang ); 66 | } 67 | 68 | return $node_data; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/media/class-wpml-elementor-update-media-factory.php: -------------------------------------------------------------------------------- 1 | get_media_translate() ) 16 | ), 17 | new WPML_Page_Builders_Media_Usage( $this->get_media_translate(), new WPML_Media_Usage_Factory() ) 18 | ); 19 | } 20 | 21 | /** @return WPML_Page_Builders_Media_Translate */ 22 | private function get_media_translate() { 23 | global $sitepress; 24 | 25 | if ( ! $this->media_translate ) { 26 | $this->media_translate = new WPML_Page_Builders_Media_Translate( 27 | new WPML_Translation_Element_Factory( $sitepress ), 28 | new WPML_Media_Image_Translate( $sitepress, new WPML_Media_Attachment_By_URL_Factory() ) 29 | ); 30 | } 31 | 32 | return $this->media_translate; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/media/modules/AllNodes.php: -------------------------------------------------------------------------------- 1 | translate_image_property( $settings, $background , $target_lang, 26 | $source_lang ); 27 | } 28 | 29 | return $settings; 30 | } 31 | } -------------------------------------------------------------------------------- /src/media/modules/Hotspot.php: -------------------------------------------------------------------------------- 1 | map( Obj::over( $thumbnailLens, $convertImageArray ) ) 18 | ->toArray(), 19 | $settings 20 | ); 21 | } 22 | } -------------------------------------------------------------------------------- /src/media/modules/abstract/class-wpml-elementor-media-node-with-image-property.php: -------------------------------------------------------------------------------- 1 | translate_image_property( $settings, $this->get_property_name(), $target_lang, $source_lang ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/media/modules/abstract/class-wpml-elementor-media-node-with-images-property.php: -------------------------------------------------------------------------------- 1 | translate_images_property( $settings, $this->get_property_name(), $target_lang, $source_lang ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/media/modules/abstract/class-wpml-elementor-media-node-with-slides.php: -------------------------------------------------------------------------------- 1 | translate_image_property( $slide, $this->get_image_property_name(), $target_lang, $source_lang ); 22 | } 23 | 24 | return $settings; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/media/modules/abstract/class-wpml-elementor-media-node.php: -------------------------------------------------------------------------------- 1 | media_translate = $media_translate; 10 | } 11 | 12 | /** 13 | * @param array $settings 14 | * @param string $property 15 | * @param string $lang 16 | * @param string $source_lang 17 | * 18 | * @return mixed 19 | */ 20 | protected function translate_image_property( $settings, $property, $lang, $source_lang ) { 21 | if ( isset( $settings[ $property ] ) ) { 22 | $settings[ $property ] = $this->translate_image_array( $settings[ $property ], $lang, $source_lang ); 23 | } 24 | 25 | return $settings; 26 | } 27 | 28 | /** 29 | * @param array $settings 30 | * @param string $property 31 | * @param string $lang 32 | * @param string $source_lang 33 | * 34 | * @return mixed 35 | */ 36 | protected function translate_images_property( $settings, $property, $lang, $source_lang ) { 37 | if ( isset( $settings[ $property ] ) ) { 38 | 39 | foreach ( $settings[ $property ] as &$image ) { 40 | $image = $this->translate_image_array( $image, $lang, $source_lang ); 41 | } 42 | } 43 | 44 | return $settings; 45 | } 46 | 47 | /** 48 | * @param array $image 49 | * @param string $lang 50 | * @param string $source_lang 51 | * 52 | * @return mixed 53 | */ 54 | public function translate_image_array( $image, $lang, $source_lang ) { 55 | if ( isset( $image['id'] ) && $image['id'] ) { 56 | $image['id'] = $this->media_translate->translate_id( $image['id'], $lang ); 57 | } 58 | if ( isset( $image['url'] ) && $image['url'] ) { 59 | $image['url'] = $this->media_translate->translate_image_url( $image['url'], $lang, $source_lang ); 60 | } 61 | 62 | return $image; 63 | } 64 | 65 | abstract function translate( $settings, $target_lang, $source_lang ); 66 | } -------------------------------------------------------------------------------- /src/media/modules/class-wpml-elementor-media-node-call-to-action.php: -------------------------------------------------------------------------------- 1 | translate_image_property( $settings, 'image', $target_lang, $source_lang ); 14 | $settings = $this->translate_image_property( $settings, '_background_image', $target_lang, $source_lang ); 15 | $settings = $this->translate_image_property( $settings, '_background_hover_image', $target_lang, $source_lang ); 16 | 17 | if ( ! isset( $settings['caption'] ) && isset( $settings['image']['id'] ) ) { 18 | $image_data = wp_prepare_attachment_for_js( $settings['image']['id'] ); 19 | $settings['caption'] = $image_data['caption']; 20 | } 21 | 22 | return $settings; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/media/modules/class-wpml-elementor-media-node-media-carousel.php: -------------------------------------------------------------------------------- 1 | media_translate->translate_id( (int) $id, $target_lang ); 18 | } 19 | 20 | $settings['wp']['ids'] = implode( ',', $ids ); 21 | } 22 | 23 | return $settings; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/media/modules/class-wpml-elementor-media-node-wp-widget-media-image.php: -------------------------------------------------------------------------------- 1 | media_translate->translate_id( $settings['wp']['attachment_id'], $target_lang ); 15 | 16 | if ( $translated_id !== (int) $settings['wp']['attachment_id'] ) { 17 | $settings['wp']['attachment_id'] = $translated_id; 18 | 19 | $settings['wp']['url'] = $this->media_translate->translate_image_url( 20 | $settings['wp']['url'], 21 | $target_lang, 22 | $source_lang 23 | ); 24 | 25 | $image_data = wp_prepare_attachment_for_js( $translated_id ); 26 | $settings['wp']['caption'] = $image_data['caption']; 27 | $settings['wp']['alt'] = $image_data['alt']; 28 | $settings['wp']['image_title'] = $image_data['title']; 29 | } 30 | } 31 | 32 | return $settings; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/modules/MediaCarousel.php: -------------------------------------------------------------------------------- 1 | [ 'url' ] ]; 19 | } 20 | 21 | /** 22 | * @param string $field 23 | * 24 | * @return string 25 | */ 26 | protected function get_title( $field ) { 27 | switch ( $field ) { 28 | case 'url': 29 | return esc_html__( 'Media Carousel: link URL', 'sitepress' ); 30 | default: 31 | return ''; 32 | } 33 | } 34 | 35 | /** 36 | * @param string $field 37 | * 38 | * @return string 39 | */ 40 | protected function get_editor_type( $field ) { 41 | switch ( $field ) { 42 | case 'url': 43 | return 'LINK'; 44 | default: 45 | return ''; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/modules/ModuleWithItemsFromConfig.php: -------------------------------------------------------------------------------- 1 | itemsField = $itemsField; 24 | $this->init( $config ); 25 | } 26 | 27 | private function init( array $config ) { 28 | foreach ( $config as $key => $fieldConfig ) { 29 | $field = Obj::prop( 'field', $fieldConfig ); 30 | $keyOf = is_string( $key ) ? $key : null; 31 | 32 | if ( $keyOf ) { 33 | $this->fields[ $keyOf ] = [ $field ]; 34 | } else { 35 | $this->fields[] = $field; 36 | } 37 | 38 | $this->fieldDefinitions[ $field ] = $fieldConfig; 39 | } 40 | } 41 | 42 | private function getFieldData( $field, $key ) { 43 | return Obj::path( [ $field, $key ], $this->fieldDefinitions ); 44 | } 45 | 46 | /** 47 | * @inheritDoc 48 | */ 49 | public function get_title( $field ) { 50 | return $this->getFieldData( $field, 'type' ); 51 | } 52 | 53 | /** 54 | * @inheritDoc 55 | */ 56 | public function get_fields() { 57 | return $this->fields; 58 | } 59 | 60 | /** 61 | * @inheritDoc 62 | */ 63 | public function get_editor_type( $field ) { 64 | return $this->getFieldData( $field, 'editor_type' ); 65 | } 66 | 67 | /** 68 | * @inheritDoc 69 | */ 70 | public function get_items_field() { 71 | return $this->itemsField; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/modules/MultipleGallery.php: -------------------------------------------------------------------------------- 1 | [ 'field' => 'url' ] ]; 28 | } 29 | 30 | protected function get_editor_type( $field ) { 31 | if ( 'content' === $field ) { 32 | return 'LINE'; 33 | } 34 | if ( 'name' === $field ) { 35 | return 'LINE'; 36 | } 37 | if ( 'title' === $field ) { 38 | return 'LINE'; 39 | } 40 | if ( 'url' === $field ) { 41 | return 'LINK'; 42 | } 43 | } 44 | 45 | public function get_items_field() { 46 | return 'slides'; 47 | } 48 | } -------------------------------------------------------------------------------- /src/modules/class-wpml-elementor-accordion.php: -------------------------------------------------------------------------------- 1 | esc_html__( 'Form: Field label', 'sitepress' ), 40 | 'placeholder' => esc_html__( 'Form: Field placeholder', 'sitepress' ), 41 | 'field_html' => esc_html__( 'Form: Field HTML', 'sitepress' ), 42 | 'acceptance_text' => esc_html__( 'Form: Acceptance Text', 'sitepress' ), 43 | 'field_options' => esc_html__( 'Form: Checkbox Options', 'sitepress' ), 44 | 'step_next_label' => esc_html__( 'Form: Step Next Label', 'sitepress' ), 45 | 'step_previous_label' => esc_html__( 'Form: Step Previous Label', 'sitepress' ), 46 | 'previous_button' => esc_html__( 'Form: Previous Button', 'sitepress' ), 47 | 'next_button' => esc_html__( 'Form: Next Button', 'sitepress' ), 48 | ] )->get( $field, '' ); 49 | } 50 | 51 | /** 52 | * @param string $field 53 | * 54 | * @return string 55 | */ 56 | protected function get_editor_type( $field ) { 57 | return wpml_collect( [ 58 | 'field_label' => 'LINE', 59 | 'placeholder' => 'LINE', 60 | 'field_html' => 'VISUAL', 61 | 'acceptance_text' => 'LINE', 62 | 'field_options' => 'AREA', 63 | 'step_next_label' => 'LINE', 64 | 'step_previous_label' => 'LINE', 65 | 'previous_button' => 'LINE', 66 | 'next_button' => 'LINE', 67 | ] )->get( $field, '' ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/modules/class-wpml-elementor-icon-list.php: -------------------------------------------------------------------------------- 1 | array( 'url' ) ); 20 | } 21 | 22 | /** 23 | * @param string $field 24 | * 25 | * @return string 26 | */ 27 | protected function get_title( $field ) { 28 | switch( $field ) { 29 | case 'text': 30 | return esc_html__( 'Icon List: Text', 'sitepress' ); 31 | 32 | case 'url': 33 | return esc_html__( 'Icon List: Link URL', 'sitepress' ); 34 | 35 | default: 36 | return ''; 37 | } 38 | } 39 | 40 | /** 41 | * @param string $field 42 | * 43 | * @return string 44 | */ 45 | protected function get_editor_type( $field ) { 46 | switch( $field ) { 47 | case 'text': 48 | return 'LINE'; 49 | case 'url': 50 | return 'LINK'; 51 | 52 | default: 53 | return ''; 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/modules/class-wpml-elementor-module-with-items.php: -------------------------------------------------------------------------------- 1 | get_items( $element ) as $item ) { 39 | foreach( $this->get_fields() as $key => $field ) { 40 | if ( ! is_array( $field ) ) { 41 | 42 | if ( ! isset( $item[ $field ] ) ) { 43 | continue; 44 | } 45 | 46 | $strings[] = new WPML_PB_String( 47 | $item[ $field ], 48 | $this->get_string_name( $node_id, $item[ $field ], $field, $element['widgetType'], $item['_id'] ), 49 | $this->get_title( $field ), 50 | $this->get_editor_type( $field ) 51 | ); 52 | } else { 53 | foreach ( $field as $inner_field ) { 54 | 55 | if ( ! isset( $item[ $key ][ $inner_field ] ) ) { 56 | continue; 57 | } 58 | 59 | $strings[] = new WPML_PB_String( 60 | $item[ $key ][ $inner_field ], 61 | $this->get_string_name( $node_id, $item[ $key ][ $inner_field ], $inner_field, $element['widgetType'], $item['_id'] ), 62 | $this->get_title( $inner_field ), 63 | $this->get_editor_type( $inner_field ) 64 | ); 65 | } 66 | } 67 | } 68 | } 69 | return $strings; 70 | } 71 | 72 | /** 73 | * @param int|string $node_id 74 | * @param mixed $element 75 | * @param WPML_PB_String $string 76 | * 77 | * @return mixed 78 | */ 79 | public function update( $node_id, $element, WPML_PB_String $string ) { 80 | foreach ( $this->get_items( $element ) as $key => $item ) { 81 | foreach( $this->get_fields() as $field_key => $field ) { 82 | if ( ! is_array( $field ) ) { 83 | 84 | if ( ! isset( $item[ $field ] ) ) { 85 | continue; 86 | } 87 | 88 | if ( $this->get_string_name( $node_id, $item[ $field ], $field, $element['widgetType'], $item['_id'] ) === $string->get_name() ) { 89 | $item[ $field ] = $string->get_value(); 90 | $item['index'] = $key; 91 | return $item; 92 | } 93 | } else { 94 | foreach ( $field as $inner_field ) { 95 | if ( ! isset( $item[ $field_key ][ $inner_field ] ) ) { 96 | continue; 97 | } 98 | 99 | if ( $this->get_string_name( $node_id, $item[ $field_key ][ $inner_field ], $inner_field, $element['widgetType'], $item['_id'] ) === $string->get_name() ) { 100 | $item[ $field_key ][ $inner_field ] = $string->get_value(); 101 | $item['index'] = $key; 102 | return $item; 103 | } 104 | } 105 | } 106 | } 107 | } 108 | } 109 | 110 | /** 111 | * @param string $node_id 112 | * @param string $value 113 | * @param string $type 114 | * @param string $key 115 | * @param string $item_id 116 | * 117 | * @return string 118 | */ 119 | private function get_string_name( $node_id, $value, $type, $key = '', $item_id = '' ) { 120 | return $key . '-' . $type . '-' . $node_id . '-' . $item_id; 121 | } 122 | 123 | /** 124 | * @param $element 125 | * 126 | * @return mixed 127 | */ 128 | public function get_items( $element ) { 129 | return $element[ WPML_Elementor_Translatable_Nodes::SETTINGS_FIELD ][ $this->get_items_field() ]; 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /src/modules/class-wpml-elementor-price-list.php: -------------------------------------------------------------------------------- 1 | array( 'url' ) ); 20 | } 21 | 22 | /** 23 | * @param string $field 24 | * 25 | * @return string 26 | */ 27 | protected function get_title( $field ) { 28 | if ( 'title' === $field ) { 29 | return esc_html__( 'Price list: title', 'sitepress' ); 30 | } 31 | 32 | if ( 'item_description' === $field ) { 33 | return esc_html__( 'Pricing list: description', 'sitepress' ); 34 | } 35 | 36 | if ( 'url' === $field ) { 37 | return esc_html__( 'Pricing list: link', 'sitepress' ); 38 | } 39 | 40 | return ''; 41 | } 42 | 43 | /** 44 | * @param string $field 45 | * 46 | * @return string 47 | */ 48 | protected function get_editor_type( $field ) { 49 | if ( 'title' === $field ) { 50 | return 'LINE'; 51 | } 52 | 53 | if ( 'url' === $field ) { 54 | return 'LINK'; 55 | } 56 | 57 | if ( 'item_description' === $field ) { 58 | return 'AREA'; 59 | } 60 | 61 | return ''; 62 | } 63 | } -------------------------------------------------------------------------------- /src/modules/class-wpml-elementor-price-table.php: -------------------------------------------------------------------------------- 1 | array( 'url' ) ); 20 | } 21 | 22 | /** 23 | * @param string $field 24 | * 25 | * @return string 26 | */ 27 | protected function get_title( $field ) { 28 | switch( $field ) { 29 | case 'heading': 30 | return esc_html__( 'Slides: heading', 'sitepress' ); 31 | 32 | case 'description': 33 | return esc_html__( 'Slides: description', 'sitepress' ); 34 | 35 | case 'button_text': 36 | return esc_html__( 'Slides: button text', 'sitepress' ); 37 | 38 | case 'url': 39 | return esc_html__( 'Slides: link URL', 'sitepress' ); 40 | 41 | default: 42 | return ''; 43 | } 44 | } 45 | 46 | /** 47 | * @param string $field 48 | * 49 | * @return string 50 | */ 51 | protected function get_editor_type( $field ) { 52 | switch( $field ) { 53 | case 'heading': 54 | case 'button_text': 55 | return 'LINE'; 56 | 57 | case 'description': 58 | return 'VISUAL'; 59 | 60 | case 'url': 61 | return 'LINK'; 62 | 63 | default: 64 | return ''; 65 | } 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/modules/class-wpml-elementor-tabs.php: -------------------------------------------------------------------------------- 1 | getMessage(); 58 | exit( 1 ); 59 | } 60 | 61 | function autoload_tests_classes( $class ) { 62 | static $maps; 63 | 64 | if ( ! $maps ) { 65 | $dirs = [ 66 | __DIR__ . '/../../vendor/wpml/wp/tests/mocks', 67 | ]; 68 | 69 | $maps = []; 70 | foreach ( $dirs as $dir ) { 71 | $maps = array_merge( $maps, \Composer\Autoload\ClassMapGenerator::createMap( $dir ) ); 72 | } 73 | } 74 | 75 | if ( $maps && array_key_exists( $class, $maps ) ) { 76 | /** @noinspection PhpIncludeInspection */ 77 | require_once $maps[ $class ]; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /tests/phpunit/stubs/IWPML_Backend_Action.php: -------------------------------------------------------------------------------- 1 | add_hooks(); 19 | } 20 | 21 | /** 22 | * @test 23 | */ 24 | public function itShouldNotConvertWithNoConvertibleBlock() { 25 | $data = [ 26 | [ 27 | 'elType' => 'section', 28 | 'elements' => [], 29 | ], 30 | [ 31 | 'elType' => 'widget', 32 | 'elements' => [], 33 | ], 34 | [ 35 | 'elType' => 'widget', 36 | 'settings' => [ 37 | '__dynamic__' => [ 'not_a_link' ], 38 | ], 39 | 'elements' => [], 40 | ], 41 | [ 42 | 'elType' => 'widget', 43 | 'settings' => [ 44 | '__dynamic__' => [ 45 | 'link' => '[elementor-tag id="d3587f6"]', 46 | ], 47 | ], 48 | 'elements' => [], 49 | ], 50 | [ 51 | 'elType' => 'widget', 52 | 'settings' => [ 53 | '__dynamic__' => [ 54 | 'link' => '[elementor-tag id="d3587f6" settings="%7B%22popup%22%3A%228%22%7D"]', 55 | ], 56 | ], 57 | 'elements' => [], 58 | ], 59 | [ 60 | 'elType' => 'widget', 61 | 'settings' => [ 62 | '__dynamic__' => [ 63 | 'link' => '[elementor-tag id="d3587f6" name="not_a_popup" settings="%7B%22popup%22%3A%228%22%7D"]', 64 | ], 65 | ], 66 | 'elements' => [], 67 | ], 68 | [ 69 | 'elType' => 'widget', 70 | 'settings' => [ 71 | '__dynamic__' => [ 72 | 'link' => '[elementor-tag id="d3587f6" name="popup" settings="%7B%22not_a_popup%22%3A%228%22%7D"]', 73 | ], 74 | ], 75 | 'elements' => [], 76 | ], 77 | ]; 78 | 79 | $subject = new DynamicElements(); 80 | 81 | $filteredData = $subject->convert( $data ); 82 | 83 | $this->assertEquals( $data, $filteredData ); 84 | } 85 | 86 | /** 87 | * @test 88 | */ 89 | public function itShouldConvert() { 90 | $originalId = 7; 91 | $convertedId = 12; 92 | $postType = 'post_elementor_library'; 93 | 94 | \WP_Mock::userFunction( 'get_post_type', [ 95 | 'args' => [ $originalId ], 96 | 'return' => $postType, 97 | ] ); 98 | 99 | \WP_Mock::onFilter( 'wpml_object_id' ) 100 | ->with( $originalId, $postType, true ) 101 | ->reply( $convertedId ); 102 | 103 | $getData = function( $id ) { 104 | $encodedSettings = urlencode( json_encode( [ 'popup' => $id ] ) ); 105 | 106 | return [ 107 | [ 108 | 'elType' => 'section', 109 | 'elements' => [], 110 | ], 111 | [ 112 | 'elType' => 'section', 113 | 'elements' => [ 114 | [ 115 | 'elType' => 'section', 116 | 'elements' => [], 117 | ], 118 | [ 119 | 'elType' => 'widget', 120 | 'settings' => [ 121 | '__dynamic__' => [ 122 | 'link' => '[elementor-tag id="d3587f6" name="popup" settings="' . $encodedSettings . '"]', 123 | ], 124 | ], 125 | 'elements' => [], 126 | ], 127 | ], 128 | ], 129 | ]; 130 | }; 131 | 132 | $originalData = $getData( $originalId ); 133 | $expectedData = $getData( $convertedId ); 134 | 135 | $subject = new DynamicElements(); 136 | 137 | $this->assertEquals( $expectedData, $subject->convert( $originalData ) ); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /tests/phpunit/tests/Hooks/TestFrontend.php: -------------------------------------------------------------------------------- 1 | add_hooks(); 19 | } 20 | 21 | /** 22 | * @test 23 | */ 24 | public function itShouldAddLanguageFormField() { 25 | $subject = new Frontend(); 26 | \WP_Mock::expectAction( 'wpml_add_language_form_field' ); 27 | $subject->addLanguageFormField(); 28 | } 29 | } -------------------------------------------------------------------------------- /tests/phpunit/tests/Hooks/TestGutenbergCleanup.php: -------------------------------------------------------------------------------- 1 | setUpPostMock(); 23 | 24 | \WP_Mock::passthruFunction( 'wp_slash' ); 25 | \WP_Mock::userFunction( 'wp_json_encode', [ 26 | 'return' => function( $value ) { 27 | return json_encode( $value ); 28 | }, 29 | ] ); 30 | } 31 | 32 | /** 33 | * @test 34 | */ 35 | public function itShouldAddHooks() { 36 | $subject = new GutenbergCleanup(); 37 | 38 | \WP_Mock::expectFilterAdded( 39 | 'update_post_metadata', 40 | Fns::withoutRecursion( Fns::identity(), [ $subject, 'removeGutenbergFootprint' ] ), 41 | 10, 42 | 4 43 | ); 44 | 45 | $subject->add_hooks(); 46 | } 47 | 48 | /** 49 | * @test 50 | */ 51 | public function itShouldNOTRemoveGbFootprintIfNotElementorDataMeta() { 52 | $check = 'some boolean'; 53 | $postId = 123; 54 | $metaKey = 'some key'; 55 | $metaValue = 'some value'; 56 | 57 | $subject = new GutenbergCleanup(); 58 | 59 | $this->assertSame( 60 | $check, 61 | $subject->removeGutenbergFootprint( $check, $postId, $metaKey, $metaValue ) 62 | ); 63 | } 64 | 65 | /** 66 | * @test 67 | */ 68 | public function itShouldNOTRemoveGbFootprintIfPostNOTEditedWithElementor() { 69 | $check = 'some boolean'; 70 | $postId = 123; 71 | $metaKey = WPML_Elementor_Data_Settings::META_KEY_DATA; 72 | $metaValue = 'some value'; 73 | 74 | \WP_Mock::userFunction( 'update_metadata', [ 75 | 'times' => 0 76 | ]); 77 | 78 | $isEditedWithElementor = FunctionMocker::replace( WPML_Elementor_Data_Settings::class . '::is_edited_with_elementor', false ); 79 | 80 | $subject = new GutenbergCleanup(); 81 | 82 | $this->assertSame( 83 | $check, 84 | $subject->removeGutenbergFootprint( $check, $postId, $metaKey, $metaValue ) 85 | ); 86 | 87 | $isEditedWithElementor->wasCalledWithOnce( [ $postId ] ); 88 | } 89 | 90 | /** 91 | * @test 92 | */ 93 | public function itShouldNOTRemoveGbFootprintIfNOBlockIsFound() { 94 | $check = 'some boolean'; 95 | $postId = 123; 96 | $metaKey = WPML_Elementor_Data_Settings::META_KEY_DATA; 97 | $metaValue = self::getMetaValue( '

<\/p>\n

Something sure!<\/p>\n

<\/p>' ); 98 | 99 | \WP_Mock::userFunction( 'update_metadata', [ 100 | 'times' => 0 101 | ]); 102 | 103 | $isEditedWithElementor = FunctionMocker::replace( WPML_Elementor_Data_Settings::class . '::is_edited_with_elementor', true ); 104 | 105 | \WP_Mock::userFunction( 'update_post_metadata' )->never(); 106 | 107 | $subject = new GutenbergCleanup(); 108 | 109 | $this->assertSame( 110 | $check, 111 | $subject->removeGutenbergFootprint( $check, $postId, $metaKey, $metaValue ) 112 | ); 113 | 114 | $isEditedWithElementor->wasCalledWithOnce( [ $postId ] ); 115 | } 116 | 117 | /** 118 | * @test 119 | */ 120 | public function itShouldRemoveGbFootprintIfOneBlockIsFound() { 121 | $check = 'some boolean'; 122 | $postId = 123; 123 | $metaKey = WPML_Elementor_Data_Settings::META_KEY_DATA; 124 | $metaValue = self::getMetaValue( '

<\/p>\n

Something sure!<\/p>\n

<\/p>' ); 125 | $newMetaValue = self::getMetaValue( '

<\/p>\n

Something sure!<\/p>\n

<\/p>' ); 126 | 127 | \WP_Mock::userFunction( 'update_metadata', [ 128 | 'args' => [ 'post', $postId, WPML_Elementor_Data_Settings::META_KEY_DATA, $newMetaValue ], 129 | 'times' => 1 130 | ]); 131 | 132 | $elementorPackage = $this->getPackage( 'elementor', $postId ); 133 | $gutenbergPackage = $this->getPackage( 'gutenberg', $postId ); 134 | $unknownPackage = $this->getPackage( 'unknown', $postId ); 135 | 136 | $isEditedWithElementor = FunctionMocker::replace( WPML_Elementor_Data_Settings::class . '::is_edited_with_elementor', true ); 137 | 138 | \WP_Mock::onFilter( 'wpml_st_get_post_string_packages' ) 139 | ->with( [], $postId ) 140 | ->reply( [ $elementorPackage, $gutenbergPackage, $unknownPackage ] ); 141 | 142 | $deleteAction = FunctionMocker::replace( 'do_action' ); 143 | 144 | $subject = new GutenbergCleanup(); 145 | 146 | $this->assertTrue( 147 | $subject->removeGutenbergFootprint( $check, $postId, $metaKey, $metaValue ) 148 | ); 149 | 150 | $isEditedWithElementor->wasCalledWithOnce( [ $postId ] ); 151 | $deleteAction->wasCalledWithOnce( [ 'wpml_delete_package', $gutenbergPackage->name, $gutenbergPackage->kind ] ); 152 | } 153 | 154 | private static function getMetaValue( $editorContent ) { 155 | return '[{"id":"dc45eec","elType":"section","settings":[],"elements":[{"id":"761adbdf","elType":"column","settings":{"_column_size":100},"elements":[{"id":"88004c2","elType":"widget","settings":{"title":"Add Your Heading Text Here"},"elements":[],"widgetType":"heading"},{"id":"888e41","elType":"widget","settings":{"editor":"' . $editorContent . '","__globals__":{"text_color":"globals\/colors?id=accent"}},"elements":[],"widgetType":"text-editor"}],"isInner":false}],"isInner":false}]'; 156 | } 157 | 158 | private function getPackage( $kind, $name ) { 159 | $package = $this->getMockBuilder( '\WPML_Package' )->getMock(); 160 | $package->kind_slug = strtolower( $kind ); 161 | $package->kind = strtoupper( $kind ); 162 | $package->name = $name; 163 | 164 | return $package; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /tests/phpunit/tests/Hooks/TestLandingPages.php: -------------------------------------------------------------------------------- 1 | with( 'permalink_structure' )->andReturn( '' ); 16 | 17 | $subject = $this->getSubject(); 18 | \WP_Mock::expectFilterNotAdded( 'post_type_link', [ $subject, 'adjustLink' ], PHP_INT_MAX, 2 ); 19 | $subject->add_hooks(); 20 | } 21 | 22 | /** 23 | * @test 24 | */ 25 | public function itShouldAddHooksWithCustomPermalinks() { 26 | \WP_Mock::userFunction( 'get_option' )->with( 'permalink_structure' )->andReturn( 'some custom permalinks' ); 27 | 28 | $subject = $this->getSubject(); 29 | \WP_Mock::expectFilterAdded( 'post_type_link', [ $subject, 'adjustLink' ], PHP_INT_MAX, 2 ); 30 | $subject->add_hooks(); 31 | } 32 | 33 | /** 34 | * @test 35 | * @dataProvider dpShouldNotFilterLinks 36 | * 37 | * @param \WP_Post $post 38 | */ 39 | public function itShouldNotFilterLinks( $post ) { 40 | $postUrl = 'https://example.com/city/london/'; 41 | 42 | $this->assertEquals( 43 | $postUrl, 44 | $this->getSubject()->adjustLink( $postUrl, $post ) 45 | ); 46 | } 47 | 48 | public function dpShouldNotFilterLinks() { 49 | return [ 50 | 'not a landing page' => [ $this->getPost( 123, 'city', 'publish' ) ], 51 | 'not published' => [ $this->getPost( 123, LandingPages::POST_TYPE, 'draft' ) ], 52 | ]; 53 | } 54 | 55 | /** 56 | * @test 57 | */ 58 | public function itShouldFilterLinks() { 59 | $homeUrl = 'https://example.com'; 60 | $postUrl = $homeUrl . '/my-landing-page/'; 61 | $post = $this->getPost( 123, LandingPages::POST_TYPE, 'publish','translated-landing-page' ); 62 | $postLang = 'fr'; 63 | $translatedUrl = $homeUrl . '/' . $postLang . '/' . $post->post_name; 64 | 65 | \WP_Mock::userFunction( 'get_home_url' )->andReturn( $homeUrl ); 66 | \WP_Mock::userFunction( 'wp_parse_url', [ 67 | 'return' => function( $url ) { return parse_url( $url ); }, 68 | ] ); 69 | \WP_Mock::userFunction( 'trailingslashit', [ 70 | 'return' => function( $value ) { return $value . '/'; }, 71 | ] ); 72 | 73 | $sitepress = $this->getSitepress(); 74 | $sitepress->method( 'get_language_for_element' ) 75 | ->with( $post->ID, 'post_' . LandingPages::POST_TYPE ) 76 | ->willReturn( $postLang ); 77 | $sitepress->method( 'convert_url' ) 78 | ->with( $homeUrl . '/' . $post->post_name . '/' ) 79 | ->willReturn( $translatedUrl ); 80 | 81 | $subject = $this->getSubject( $sitepress ); 82 | 83 | $this->assertEquals( 84 | $translatedUrl, 85 | $subject->adjustLink( $postUrl, $post ) 86 | ); 87 | } 88 | 89 | private function getSubject( $sitepress = null ) { 90 | $sitepress = $sitepress ?: $this->getSitepress(); 91 | return new LandingPages( $sitepress ); 92 | } 93 | 94 | private function getSitepress() { 95 | return $this->getMockBuilder( \SitePress::class ) 96 | ->setMethods( [ 97 | 'get_language_for_element', 98 | 'convert_url', 99 | ] ) 100 | ->getMock(); 101 | } 102 | 103 | private function getPost( $id, $type, $status = 'publish', $name = 'something' ) { 104 | $post = $this->getMockBuilder( \WP_Post::class )->getMock(); 105 | $post->ID = $id; 106 | $post->post_type = $type; 107 | $post->post_status = $status; 108 | $post->post_name = $name; 109 | 110 | return $post; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /tests/phpunit/tests/LanguageSwitcher/TestLanguageSwitcher.php: -------------------------------------------------------------------------------- 1 | add_hooks(); 18 | } 19 | 20 | /** 21 | * @test 22 | */ 23 | public function itShouldregisterWidgets() { 24 | $subject = new LanguageSwitcher(); 25 | 26 | $widgetManager = $this->getMockBuilder( 'Elementor\Widgets_Manager' ) 27 | ->setMethods( [ 'register_widget_type' ] ) 28 | ->disableOriginalConstructor() 29 | ->getMock(); 30 | $widgetManager->expects( $this->once() )->method( 'register_widget_type' )->with( new Widget() ); 31 | 32 | $elementorPlugin = \Mockery::mock( 'alias:Elementor\Plugin' ); 33 | $elementorPlugin->shouldReceive( 'instance' )->andReturnSelf(); 34 | $elementorPlugin->widgets_manager = $widgetManager; 35 | 36 | $subject->registerWidgets(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/phpunit/tests/LanguageSwitcher/TestWidget.php: -------------------------------------------------------------------------------- 1 | getAdaptor(); 24 | $adaptor->method( 'getName' )->willReturn( 'my name' ); 25 | 26 | $subject = $this->getSubject( $adaptor ); 27 | 28 | $this->assertEquals( 'my name', $subject->get_name() ); 29 | } 30 | 31 | /** 32 | * @test 33 | */ 34 | public function itShouldGetTitle() { 35 | $adaptor = $this->getAdaptor(); 36 | $adaptor->method( 'getTitle' )->willReturn( 'my title' ); 37 | 38 | $subject = $this->getSubject( $adaptor ); 39 | 40 | $this->assertEquals( 'my title', $subject->get_title() ); 41 | } 42 | 43 | /** 44 | * @test 45 | */ 46 | public function itShouldGetIcon() { 47 | $adaptor = $this->getAdaptor(); 48 | $adaptor->method( 'getIcon' )->willReturn( 'my icon' ); 49 | 50 | $subject = $this->getSubject( $adaptor ); 51 | 52 | $this->assertEquals( 'my icon', $subject->get_icon() ); 53 | } 54 | 55 | /** 56 | * @test 57 | */ 58 | public function itShouldGetCategories() { 59 | $categories = [ 'my category' ]; 60 | 61 | $adaptor = $this->getAdaptor(); 62 | $adaptor->method( 'getCategories' )->willReturn( $categories ); 63 | 64 | $subject = $this->getSubject( $adaptor ); 65 | 66 | $this->assertEquals( $categories, $subject->get_categories() ); 67 | } 68 | 69 | private function getSubject( $adaptor ) { 70 | return new Widget( [], null, $adaptor ); 71 | } 72 | 73 | private function getAdaptor() { 74 | return $this->getMockBuilder( WidgetAdaptor::class ) 75 | ->setMethods( 76 | [ 77 | 'getName', 78 | 'getTitle', 79 | 'getIcon', 80 | 'getCategories', 81 | ] 82 | )->disableOriginalConstructor()->getMock(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /tests/phpunit/tests/media/modules/TestHotspot.php: -------------------------------------------------------------------------------- 1 | 'bar', 24 | 'image' => [ 25 | 'url' => $url, 26 | 'id' => $id, 27 | ], 28 | ]; 29 | }; 30 | 31 | $mediaTranslate = $this->getMockBuilder( \WPML_Page_Builders_Media_Translate::class ) 32 | ->setMethods( [ 'translate_id', 'translate_image_url' ] ) 33 | ->disableOriginalConstructor()->getMock(); 34 | 35 | $mediaTranslate->method( 'translate_id' ) 36 | ->with( $id, $targetLang ) 37 | ->willReturn( $idConverted ); 38 | $mediaTranslate->method( 'translate_image_url' ) 39 | ->with( $url, $targetLang, $sourceLang ) 40 | ->willReturn( $urlConverted ); 41 | 42 | $subject = new Hotspot( $mediaTranslate ); 43 | 44 | $this->assertEquals( 45 | $getSettings( $urlConverted, $idConverted ), 46 | $subject->translate( $getSettings( $url, $id ), $targetLang, $sourceLang ) 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tests/phpunit/tests/media/modules/TestVideoPlaylist.php: -------------------------------------------------------------------------------- 1 | 'bar', 24 | 'tabs' => [ 25 | [ 26 | 'thumbnail' => [ 27 | 'url' => $url, 28 | 'id' => $id, 29 | ], 30 | ], 31 | [ 32 | 'thumbnail' => [ 33 | 'id' => $id, 34 | ], 35 | ], 36 | [ 37 | 'thumbnail' => [], 38 | ], 39 | ], 40 | ]; 41 | }; 42 | 43 | $mediaTranslate = $this->getMockBuilder( \WPML_Page_Builders_Media_Translate::class ) 44 | ->setMethods( [ 'translate_id', 'translate_image_url' ] ) 45 | ->disableOriginalConstructor()->getMock(); 46 | 47 | $mediaTranslate->method( 'translate_id' ) 48 | ->with( $id, $targetLang ) 49 | ->willReturn( $idConverted ); 50 | $mediaTranslate->method( 'translate_image_url' ) 51 | ->with( $url, $targetLang, $sourceLang ) 52 | ->willReturn( $urlConverted ); 53 | 54 | $subject = new VideoPlaylist( $mediaTranslate ); 55 | 56 | $this->assertEquals( 57 | $getSettings( $urlConverted, $idConverted ), 58 | $subject->translate( $getSettings( $url, $id ), $targetLang, $sourceLang ) 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tests/phpunit/tests/media/modules/test-AllNodes.php: -------------------------------------------------------------------------------- 1 | 20, 18 | 'background_image' => [ 19 | 'url' => 'background_image_1', 20 | 'id' => 1, 21 | ], 22 | 'background_size' => 'contain', 23 | 'background_hover_image' => [ 24 | 'url' => 'background_image_2', 25 | 'id' => 2, 26 | ], 27 | ]; 28 | 29 | $source = 'en'; 30 | $target = 'fr'; 31 | 32 | $expected = [ 33 | 'structure' => 20, 34 | 'background_image' => [ 35 | 'url' => 'translated_background_image_1', 36 | 'id' => 101, 37 | ], 38 | 'background_size' => 'contain', 39 | 'background_hover_image' => [ 40 | 'url' => 'translated_background_image_2', 41 | 'id' => 102, 42 | ], 43 | ]; 44 | 45 | $mediaTranslate = $this->createMock( '\WPML_Page_Builders_Media_Translate' ); 46 | $mediaTranslate->method( 'translate_id' )->willReturnCallback( Math::add( 100 ) ); 47 | $mediaTranslate->method( 'translate_image_url' )->willReturnMap( [ 48 | [ 'background_image_1', $target, $source, 'translated_background_image_1' ], 49 | [ 'background_image_2', $target, $source, 'translated_background_image_2' ], 50 | ] ); 51 | 52 | $subject = new AllNodes( $mediaTranslate ); 53 | 54 | $this->assertEquals( $expected, $subject->translate( $settings, $target, $source ) ); 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /tests/phpunit/tests/media/modules/test-wpml-elementor-media-node-call-to-action.php: -------------------------------------------------------------------------------- 1 | array( 'id' => $original_id, 'url' => $original_url ), 21 | ); 22 | 23 | $expected_settings = array( 24 | 'bg_image' => array( 'id' => $translated_id, 'url' => $translated_url ), 25 | ); 26 | 27 | $media_translate = $this->get_media_translate(); 28 | $media_translate->method( 'translate_id' )->with( $original_id, $target_lang )->willReturn( $translated_id ); 29 | $media_translate->method( 'translate_image_url' ) 30 | ->with( $original_url, $target_lang, $source_lang )->willReturn( $translated_url ); 31 | 32 | $subject = $this->get_subject( $media_translate ); 33 | 34 | $this->assertEquals( $expected_settings, $subject->translate( $settings, $target_lang, $source_lang ) ); 35 | } 36 | 37 | /** 38 | * @test 39 | */ 40 | public function it_should_not_translate_if_property_is_missing() { 41 | $settings = array( 'something' ); 42 | 43 | $media_translate = $this->get_media_translate(); 44 | 45 | $subject = $this->get_subject( $media_translate ); 46 | 47 | $this->assertEquals( $settings, $subject->translate( $settings, 'fr', 'en' ) ); 48 | } 49 | 50 | private function get_subject( $media_translate ) { 51 | return new WPML_Elementor_Media_Node_Call_To_Action( $media_translate ); 52 | } 53 | 54 | private function get_media_translate() { 55 | return $this->getMockBuilder( 'WPML_Page_Builders_Media_Translate' ) 56 | ->setMethods( array( 'translate_id', 'translate_image_url' ) ) 57 | ->disableOriginalConstructor()->getMock(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/phpunit/tests/media/modules/test-wpml-elementor-media-node-image-box.php: -------------------------------------------------------------------------------- 1 | array( 'id' => $original_id, 'url' => $original_url ), 21 | ); 22 | 23 | $expected_settings = array( 24 | 'image' => array( 'id' => $translated_id, 'url' => $translated_url ), 25 | ); 26 | 27 | $media_translate = $this->get_media_translate(); 28 | $media_translate->method( 'translate_id' )->with( $original_id, $target_lang )->willReturn( $translated_id ); 29 | $media_translate->method( 'translate_image_url' ) 30 | ->with( $original_url, $target_lang, $source_lang )->willReturn( $translated_url ); 31 | 32 | $subject = $this->get_subject( $media_translate ); 33 | 34 | $this->assertEquals( $expected_settings, $subject->translate( $settings, $target_lang, $source_lang ) ); 35 | } 36 | 37 | /** 38 | * @test 39 | */ 40 | public function it_should_not_translate_if_property_is_missing() { 41 | $settings = array( 'something' ); 42 | 43 | $media_translate = $this->get_media_translate(); 44 | 45 | $subject = $this->get_subject( $media_translate ); 46 | 47 | $this->assertEquals( $settings, $subject->translate( $settings, 'fr', 'en' ) ); 48 | } 49 | 50 | private function get_subject( $media_translate ) { 51 | return new WPML_Elementor_Media_Node_Image_Box( $media_translate ); 52 | } 53 | 54 | private function get_media_translate() { 55 | return $this->getMockBuilder( 'WPML_Page_Builders_Media_Translate' ) 56 | ->setMethods( array( 'translate_id', 'translate_image_url' ) ) 57 | ->disableOriginalConstructor()->getMock(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/phpunit/tests/media/modules/test-wpml-elementor-media-node-image-carousel.php: -------------------------------------------------------------------------------- 1 | array( 21 | array( 'id' => $original_id, 'url' => $original_url ), 22 | ), 23 | ); 24 | 25 | $expected_settings = array( 26 | 'carousel' => array( 27 | array( 'id' => $translated_id, 'url' => $translated_url ), 28 | ), 29 | ); 30 | 31 | $media_translate = $this->get_media_translate(); 32 | $media_translate->method( 'translate_id' )->with( $original_id, $target_lang )->willReturn( $translated_id ); 33 | $media_translate->method( 'translate_image_url' ) 34 | ->with( $original_url, $target_lang, $source_lang )->willReturn( $translated_url ); 35 | 36 | $subject = $this->get_subject( $media_translate ); 37 | 38 | $this->assertEquals( $expected_settings, $subject->translate( $settings, $target_lang, $source_lang ) ); 39 | } 40 | 41 | /** 42 | * @test 43 | */ 44 | public function it_should_not_translate_if_property_is_missing() { 45 | $settings = array( 'something' ); 46 | 47 | $media_translate = $this->get_media_translate(); 48 | 49 | $subject = $this->get_subject( $media_translate ); 50 | 51 | $this->assertEquals( $settings, $subject->translate( $settings, 'fr', 'en' ) ); 52 | } 53 | 54 | private function get_subject( $media_translate ) { 55 | return new WPML_Elementor_Media_Node_Image_Carousel( $media_translate ); 56 | } 57 | 58 | private function get_media_translate() { 59 | return $this->getMockBuilder( 'WPML_Page_Builders_Media_Translate' ) 60 | ->setMethods( array( 'translate_id', 'translate_image_url' ) ) 61 | ->disableOriginalConstructor()->getMock(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tests/phpunit/tests/media/modules/test-wpml-elementor-media-node-image-gallery.php: -------------------------------------------------------------------------------- 1 | array( 21 | array( 'id' => $original_id, 'url' => $original_url ), 22 | ), 23 | ); 24 | 25 | $expected_settings = array( 26 | 'wp_gallery' => array( 27 | array( 'id' => $translated_id, 'url' => $translated_url ), 28 | ), 29 | ); 30 | 31 | $media_translate = $this->get_media_translate(); 32 | $media_translate->method( 'translate_id' )->with( $original_id, $target_lang )->willReturn( $translated_id ); 33 | $media_translate->method( 'translate_image_url' ) 34 | ->with( $original_url, $target_lang, $source_lang )->willReturn( $translated_url ); 35 | 36 | $subject = $this->get_subject( $media_translate ); 37 | 38 | $this->assertEquals( $expected_settings, $subject->translate( $settings, $target_lang, $source_lang ) ); 39 | } 40 | 41 | /** 42 | * @test 43 | */ 44 | public function it_should_not_translate_if_property_is_missing() { 45 | $settings = array( 'something' ); 46 | 47 | $media_translate = $this->get_media_translate(); 48 | 49 | $subject = $this->get_subject( $media_translate ); 50 | 51 | $this->assertEquals( $settings, $subject->translate( $settings, 'fr', 'en' ) ); 52 | } 53 | 54 | private function get_subject( $media_translate ) { 55 | return new WPML_Elementor_Media_Node_Image_Gallery( $media_translate ); 56 | } 57 | 58 | private function get_media_translate() { 59 | return $this->getMockBuilder( 'WPML_Page_Builders_Media_Translate' ) 60 | ->setMethods( array( 'translate_id', 'translate_image_url' ) ) 61 | ->disableOriginalConstructor()->getMock(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tests/phpunit/tests/media/modules/test-wpml-elementor-media-node-image.php: -------------------------------------------------------------------------------- 1 | array( 'id' => $original_id, 'url' => $original_url ), 21 | '_background_image' => array( 'id' => $original_id, 'url' => $original_url ), 22 | '_background_hover_image' => array( 'id' => $original_id, 'url' => $original_url ), 23 | ); 24 | 25 | $expected_settings = array( 26 | 'image' => array( 'id' => $translated_id, 'url' => $translated_url ), 27 | '_background_image' => array( 'id' => $translated_id, 'url' => $translated_url ), 28 | '_background_hover_image' => array( 'id' => $translated_id, 'url' => $translated_url ), 29 | 'caption' => $target_lang . 'The caption', 30 | ); 31 | 32 | \WP_Mock::userFunction( 'wp_prepare_attachment_for_js', array( 33 | 'args' => array( $translated_id ), 34 | 'return' => array( 35 | 'caption' => $target_lang . 'The caption', 36 | ), 37 | )); 38 | 39 | $media_translate = $this->get_media_translate(); 40 | $media_translate->method( 'translate_id' )->with( $original_id, $target_lang )->willReturn( $translated_id ); 41 | $media_translate->method( 'translate_image_url' ) 42 | ->with( $original_url, $target_lang, $source_lang )->willReturn( $translated_url ); 43 | 44 | $subject = $this->get_subject( $media_translate ); 45 | 46 | $this->assertEquals( $expected_settings, $subject->translate( $settings, $target_lang, $source_lang ) ); 47 | } 48 | 49 | /** 50 | * @test 51 | */ 52 | public function it_should_not_translate_if_properties_missing() { 53 | $settings = array( 54 | 'caption' => '', 55 | ); 56 | 57 | \WP_Mock::userFunction( 'wp_prepare_attachment_for_js' )->andReturn( $settings ); 58 | 59 | $media_translate = $this->get_media_translate(); 60 | 61 | $subject = $this->get_subject( $media_translate ); 62 | 63 | $this->assertEquals( $settings, $subject->translate( $settings, 'fr', 'en' ) ); 64 | } 65 | 66 | private function get_subject( $media_translate ) { 67 | return new WPML_Elementor_Media_Node_Image( $media_translate ); 68 | } 69 | 70 | private function get_media_translate() { 71 | return $this->getMockBuilder( 'WPML_Page_Builders_Media_Translate' ) 72 | ->setMethods( array( 'translate_id', 'translate_image_url' ) ) 73 | ->disableOriginalConstructor()->getMock(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /tests/phpunit/tests/media/modules/test-wpml-elementor-media-node-media-carousel.php: -------------------------------------------------------------------------------- 1 | array( 21 | array( 22 | 'image' => array( 'id' => $original_id, 'url' => $original_url ), 23 | ), 24 | ), 25 | ); 26 | 27 | $expected_settings = array( 28 | 'slides' => array( 29 | array( 30 | 'image' => array( 'id' => $translated_id, 'url' => $translated_url ), 31 | ), 32 | ), 33 | ); 34 | 35 | $media_translate = $this->get_media_translate(); 36 | $media_translate->method( 'translate_id' )->with( $original_id, $target_lang )->willReturn( $translated_id ); 37 | $media_translate->method( 'translate_image_url' ) 38 | ->with( $original_url, $target_lang, $source_lang )->willReturn( $translated_url ); 39 | 40 | $subject = $this->get_subject( $media_translate ); 41 | 42 | $this->assertEquals( $expected_settings, $subject->translate( $settings, $target_lang, $source_lang ) ); 43 | } 44 | 45 | /** 46 | * @test 47 | * @dataProvider dp_should_not_translate 48 | * 49 | * @param array $settings 50 | */ 51 | public function it_should_not_translate_if_slide_is_incomplete( $settings ) { 52 | $media_translate = $this->get_media_translate(); 53 | 54 | $subject = $this->get_subject( $media_translate ); 55 | 56 | $this->assertEquals( $settings, $subject->translate( $settings, 'fr', 'en' ) ); 57 | } 58 | 59 | public function dp_should_not_translate() { 60 | return array( 61 | array( array() ), 62 | array( array( 'slides' => 'no an array' ) ), 63 | array( array( 'slides' => array( 'image' => array() ) ) ), 64 | ); 65 | } 66 | 67 | private function get_subject( $media_translate ) { 68 | return new WPML_Elementor_Media_Node_Media_Carousel( $media_translate ); 69 | } 70 | 71 | private function get_media_translate() { 72 | return $this->getMockBuilder( 'WPML_Page_Builders_Media_Translate' ) 73 | ->setMethods( array( 'translate_id', 'translate_image_url' ) ) 74 | ->disableOriginalConstructor()->getMock(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /tests/phpunit/tests/media/modules/test-wpml-elementor-media-node-slides.php: -------------------------------------------------------------------------------- 1 | array( 21 | array( 22 | 'background_image' => array( 'id' => $original_id, 'url' => $original_url ), 23 | ), 24 | ), 25 | ); 26 | 27 | $expected_settings = array( 28 | 'slides' => array( 29 | array( 30 | 'background_image' => array( 'id' => $translated_id, 'url' => $translated_url ), 31 | ), 32 | ), 33 | ); 34 | 35 | $media_translate = $this->get_media_translate(); 36 | $media_translate->method( 'translate_id' )->with( $original_id, $target_lang )->willReturn( $translated_id ); 37 | $media_translate->method( 'translate_image_url' ) 38 | ->with( $original_url, $target_lang, $source_lang )->willReturn( $translated_url ); 39 | 40 | $subject = $this->get_subject( $media_translate ); 41 | 42 | $this->assertEquals( $expected_settings, $subject->translate( $settings, $target_lang, $source_lang ) ); 43 | } 44 | 45 | /** 46 | * @test 47 | * @dataProvider dp_should_not_translate 48 | * 49 | * @param array $settings 50 | */ 51 | public function it_should_not_translate_if_slide_is_incomplete( $settings ) { 52 | $media_translate = $this->get_media_translate(); 53 | 54 | $subject = $this->get_subject( $media_translate ); 55 | 56 | $this->assertEquals( $settings, $subject->translate( $settings, 'fr', 'en' ) ); 57 | } 58 | 59 | public function dp_should_not_translate() { 60 | return array( 61 | array( array() ), 62 | array( array( 'slides' => 'no an array' ) ), 63 | array( array( 'slides' => array( 'background_image' => array() ) ) ), 64 | ); 65 | } 66 | 67 | private function get_subject( $media_translate ) { 68 | return new WPML_Elementor_Media_Node_Slides( $media_translate ); 69 | } 70 | 71 | private function get_media_translate() { 72 | return $this->getMockBuilder( 'WPML_Page_Builders_Media_Translate' ) 73 | ->setMethods( array( 'translate_id', 'translate_image_url' ) ) 74 | ->disableOriginalConstructor()->getMock(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /tests/phpunit/tests/media/modules/test-wpml-elementor-media-node-wp-widget-media-gallery.php: -------------------------------------------------------------------------------- 1 | array( 21 | 'ids' => "$original_id_1,$original_id_2", 22 | ), 23 | ); 24 | 25 | $expected_settings = array( 26 | 'wp' => array( 27 | 'ids' => "$translated_id_1,$translated_id_2", 28 | ), 29 | ); 30 | 31 | $media_translate = $this->get_media_translate(); 32 | $media_translate->method( 'translate_id' )->willReturnMap( 33 | array( 34 | array( $original_id_1, $target_lang, $translated_id_1 ), 35 | array( $original_id_2, $target_lang, $translated_id_2 ), 36 | ) 37 | ); 38 | 39 | $subject = $this->get_subject( $media_translate ); 40 | 41 | $this->assertEquals( $expected_settings, $subject->translate( $settings, $target_lang, $source_lang ) ); 42 | } 43 | 44 | /** 45 | * @test 46 | * @dataProvider dp_should_not_translate 47 | * 48 | * @param array $settings 49 | */ 50 | public function it_should_not_translate_if_slide_is_incomplete( $settings ) { 51 | $media_translate = $this->get_media_translate(); 52 | 53 | $subject = $this->get_subject( $media_translate ); 54 | 55 | $this->assertEquals( $settings, $subject->translate( $settings, 'fr', 'en' ) ); 56 | } 57 | 58 | public function dp_should_not_translate() { 59 | return array( 60 | array( array() ), 61 | array( array( 'wp' => 'no an array' ) ), 62 | array( array( 'wp' => array( 'ids' => '' ) ) ), 63 | ); 64 | } 65 | 66 | private function get_subject( $media_translate ) { 67 | return new WPML_Elementor_Media_Node_WP_Widget_Media_Gallery( $media_translate ); 68 | } 69 | 70 | private function get_media_translate() { 71 | return $this->getMockBuilder( 'WPML_Page_Builders_Media_Translate' ) 72 | ->setMethods( array( 'translate_id', 'translate_image_url' ) ) 73 | ->disableOriginalConstructor()->getMock(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /tests/phpunit/tests/media/modules/test-wpml-elementor-media-node-wp-widget-media-image.php: -------------------------------------------------------------------------------- 1 | array( 21 | 'attachment_id' => $original_id, 22 | 'url' => $original_url, 23 | 'caption' => 'the caption', 24 | 'alt' => 'the alt', 25 | 'image_title' => 'the image title', 26 | ), 27 | ); 28 | 29 | $expected_settings = array( 30 | 'wp' => array( 31 | 'attachment_id' => $translated_id, 32 | 'url' => $translated_url, 33 | 'caption' => $target_lang . 'the caption', 34 | 'alt' => $target_lang . 'the alt', 35 | 'image_title' => $target_lang . 'the image title', 36 | ), 37 | ); 38 | 39 | \WP_Mock::userFunction( 'wp_prepare_attachment_for_js', array( 40 | 'args' => array( $translated_id ), 41 | 'return' => array( 42 | 'caption' => $target_lang . 'the caption', 43 | 'alt' => $target_lang . 'the alt', 44 | 'title' => $target_lang . 'the image title', 45 | ), 46 | )); 47 | 48 | $media_translate = $this->get_media_translate(); 49 | $media_translate->method( 'translate_id' )->with( $original_id, $target_lang )->willReturn( $translated_id ); 50 | $media_translate->method( 'translate_image_url' ) 51 | ->with( $original_url, $target_lang, $source_lang )->willReturn( $translated_url ); 52 | 53 | $subject = $this->get_subject( $media_translate ); 54 | 55 | $this->assertEquals( $expected_settings, $subject->translate( $settings, $target_lang, $source_lang ) ); 56 | } 57 | 58 | /** 59 | * @test 60 | * @dataProvider dp_should_not_translate 61 | * 62 | * @param array $settings 63 | */ 64 | public function it_should_not_translate_if_slide_is_incomplete( $settings ) { 65 | $media_translate = $this->get_media_translate(); 66 | 67 | $subject = $this->get_subject( $media_translate ); 68 | 69 | $this->assertEquals( $settings, $subject->translate( $settings, 'fr', 'en' ) ); 70 | } 71 | 72 | public function dp_should_not_translate() { 73 | return array( 74 | array( array() ), 75 | array( array( 'wp' => 'no an array' ) ), 76 | ); 77 | } 78 | 79 | private function get_subject( $media_translate ) { 80 | return new WPML_Elementor_Media_Node_WP_Widget_Media_Image( $media_translate ); 81 | } 82 | 83 | private function get_media_translate() { 84 | return $this->getMockBuilder( 'WPML_Page_Builders_Media_Translate' ) 85 | ->setMethods( array( 'translate_id', 'translate_image_url' ) ) 86 | ->disableOriginalConstructor()->getMock(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /tests/phpunit/tests/media/test-wpml-elementor-media-hooks-factory.php: -------------------------------------------------------------------------------- 1 | getMockBuilder( 'SitePress' )->getMock(); 13 | \Mockery::mock( 'alias:WPML_Page_Builders_Media_Hooks' ); 14 | $subject = new WPML_Elementor_Media_Hooks_Factory(); 15 | $this->assertInstanceOf( 'WPML_Page_Builders_Media_Hooks', $subject->create() ); 16 | } 17 | 18 | /** 19 | * @test 20 | */ 21 | public function it_should_implements_backend_and_fronted_action() { 22 | $subject = new WPML_Elementor_Media_Hooks_Factory(); 23 | $this->assertInstanceOf( 'IWPML_Backend_Action_Loader', $subject ); 24 | $this->assertInstanceOf( 'IWPML_Frontend_Action_Loader', $subject ); 25 | } 26 | } 27 | 28 | if ( ! interface_exists( 'IWPML_PB_Media_Update_Factory' ) ) { 29 | interface IWPML_PB_Media_Update_Factory {} 30 | } 31 | -------------------------------------------------------------------------------- /tests/phpunit/tests/media/test-wpml-elementor-media-node-provider.php: -------------------------------------------------------------------------------- 1 | getMockBuilder( 'SitePress' )->disableOriginalConstructor()->getMock(); 17 | $this->mock_external_classes(); 18 | 19 | $media_translate = $this->getMockBuilder( 'WPML_Page_Builders_Media_Translate' ) 20 | ->disableOriginalConstructor()->getMock(); 21 | 22 | $subject = new WPML_Elementor_Media_Node_Provider( $media_translate ); 23 | 24 | $this->assertInstanceOf( $class_name, $subject->get( $type ) ); 25 | $this->assertSame( $subject->get( $type ), $subject->get( $type ) ); 26 | } 27 | 28 | public function dp_node_types() { 29 | return array( 30 | 'image' => array( 'image', 'WPML_Elementor_Media_Node_Image' ), 31 | 'slides' => array( 'slides', 'WPML_Elementor_Media_Node_Slides' ), 32 | 'call-to-action' => array( 'call-to-action', 'WPML_Elementor_Media_Node_Call_To_Action' ), 33 | 'media-carousel' => array( 'media-carousel', 'WPML_Elementor_Media_Node_Media_Carousel' ), 34 | 'image-box' => array( 'image-box', 'WPML_Elementor_Media_Node_Image_Box' ), 35 | 'image-gallery' => array( 'image-gallery', 'WPML_Elementor_Media_Node_Image_Gallery' ), 36 | 'image-carousel' => array( 'image-carousel', 'WPML_Elementor_Media_Node_Image_Carousel' ), 37 | 'wp-widget-media_image' => array( 'wp-widget-media_image', 'WPML_Elementor_Media_Node_WP_Widget_Media_Image' ), 38 | 'wp-widget-media_gallery' => array( 'wp-widget-media_gallery', 'WPML_Elementor_Media_Node_WP_Widget_Media_Gallery' ), 39 | 'background-images' => array( 'all_nodes', \WPML\PB\Elementor\Media\Modules\AllNodes::class ), 40 | 'video-playlist' => array( 'video-playlist', \WPML\PB\Elementor\Media\Modules\VideoPlaylist::class ), 41 | 'hotspot' => array( 'hotspot', \WPML\PB\Elementor\Media\Modules\Hotspot::class ), 42 | ); 43 | } 44 | 45 | private function mock_external_classes() { 46 | $this->getMockBuilder( 'WPML_Translation_Element_Factory' )->getMock(); 47 | $this->getMockBuilder( 'WPML_Media_Image_Translate' )->getMock(); 48 | $this->getMockBuilder( 'WPML_Media_Attachment_By_URL_Factory' )->getMock(); 49 | } 50 | } -------------------------------------------------------------------------------- /tests/phpunit/tests/media/test-wpml-elementor-media-nodes-iterator.php: -------------------------------------------------------------------------------- 1 | 'column', 21 | 'elements' => array( 22 | array( 23 | 'elType' => 'widget', 24 | 'widgetType' => 'image', 25 | 'settings' => $image_data, 26 | ), 27 | ), 28 | ), 29 | array( 30 | 'elType' => 'widget', 31 | 'widgetType' => 'slides', 32 | 'settings' => $slides_data, 33 | 'elements' => array(), 34 | ), 35 | array( 36 | 'elType' => 'section', 37 | 'widgetType' => 'section', 38 | 'settings' => [ 'background image data' ], 39 | 'elements' => array(), 40 | ), 41 | // Not supported module 42 | array( 43 | 'elType' => 'widget', 44 | 'widgetType' => 'not-supported', 45 | 'settings' => array(), 46 | 'elements' => array(), 47 | ), 48 | // Not translated modules 49 | array(), 50 | array( 'elType' => 'not a widget' ), 51 | array( 'elType' => 'widget' ), 52 | array( 'elType' => 'widget', 'settings' => array() ), 53 | array( 'elType' => 'widget', 'widgetType' => 'image' ), 54 | ); 55 | 56 | $translated_image_data = array( 'translated image data' ); 57 | $translated_slides_data = array( 'translated slides data' ); 58 | 59 | $expected_data = $data; 60 | $expected_data[0]['elements'][0]['settings'] = $translated_image_data; 61 | $expected_data[1]['settings'] = $translated_slides_data; 62 | $expected_data[2]['settings'] = ['translated background image data']; 63 | 64 | 65 | $node_image = $this->get_node(); 66 | $node_image->method( 'translate' )->with( $image_data, $lang, $source_lang ) 67 | ->willReturn( $translated_image_data ); 68 | 69 | $node_slides = $this->get_node(); 70 | $node_slides->method( 'translate' )->with( $slides_data, $lang, $source_lang ) 71 | ->willReturn( $translated_slides_data ); 72 | 73 | $all_nodes = $this->createMock( \WPML\PB\Elementor\Media\Modules\AllNodes::class ); 74 | $all_nodes->method( 'translate' )->willReturnCallback( function ( $settings ) { 75 | return $settings === ['background image data'] ? ['translated background image data'] : $settings; 76 | } ); 77 | 78 | $node_provider = $this->get_node_provider(); 79 | $node_provider->method( 'get' )->willReturnMap( 80 | [ 81 | [ 'image', $node_image ], 82 | [ 'slides', $node_slides ], 83 | [ 'not-supported', null ], 84 | [ 'all_nodes', $all_nodes ], 85 | ] 86 | ); 87 | 88 | $subject = $this->get_subject( $node_provider ); 89 | 90 | $this->assertEquals( $expected_data, $subject->translate( $data, $lang, $source_lang ) ); 91 | } 92 | 93 | private function get_subject( $node_provider ) { 94 | return new WPML_Elementor_Media_Nodes_Iterator( $node_provider ); 95 | } 96 | 97 | private function get_node_provider() { 98 | return $this->getMockBuilder( 'WPML_Elementor_Media_Node_Provider' ) 99 | ->setMethods( array( 'get' ) )->disableOriginalConstructor()->getMock(); 100 | } 101 | 102 | private function get_node() { 103 | return $this->getMockBuilder( 'WPML_Elementor_Media_Node' ) 104 | ->setMethods( array( 'translate' ) )->disableOriginalConstructor()->getMock(); 105 | } 106 | } 107 | 108 | if ( ! interface_exists( 'IWPML_PB_Media_Nodes_Iterator' ) ) { 109 | interface IWPML_PB_Media_Nodes_Iterator {} 110 | } -------------------------------------------------------------------------------- /tests/phpunit/tests/media/test-wpml-elementor-update-media-factory.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf( 'IWPML_PB_Media_Update_Factory', $subject ); 20 | } 21 | 22 | /** 23 | * @test 24 | */ 25 | public function it_should_return_an_instance_of_page_builders_media_update() { 26 | $subject = new WPML_Elementor_Update_Media_Factory(); 27 | $this->assertInstanceOf( 'WPML_Page_Builders_Update_Media', $subject->create() ); 28 | } 29 | } 30 | 31 | if ( ! interface_exists( 'IWPML_PB_Media_Update_Factory' ) ) { 32 | interface IWPML_PB_Media_Update_Factory {} 33 | } 34 | -------------------------------------------------------------------------------- /tests/phpunit/tests/modules/TestMediaCarousel.php: -------------------------------------------------------------------------------- 1 | assertEquals( [ 'image_link_to' => [ 'url' ] ], $subject->get_fields() ); 17 | } 18 | 19 | /** 20 | * @test 21 | */ 22 | public function it_get_items_field() { 23 | $subject = new MediaCarousel(); 24 | $this->assertEquals( 'slides', $subject->get_items_field() ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/phpunit/tests/modules/TestModuleWithItemsFromConfig.php: -------------------------------------------------------------------------------- 1 | 'title', 19 | 'type' => 'The slide title', 20 | 'editor_type' => 'LINE', 21 | ], 22 | 'link' => [ 23 | 'field' => 'url', 24 | 'type' => 'The slide url', 25 | 'editor_type' => 'LINK', 26 | ], 27 | ]; 28 | 29 | 30 | $subject = new ModuleWithItemsFromConfig( $itemsField, $config ); 31 | 32 | $this->assertEquals( $itemsField, $subject->get_items_field() ); 33 | 34 | $this->assertEquals( 35 | [ 'title', 'link' => [ 'url' ] ], 36 | $subject->get_fields() 37 | ); 38 | 39 | // Title field 40 | $this->assertEquals( 'The slide title', $subject->get_title( 'title' ) ); 41 | $this->assertEquals( 'LINE', $subject->get_editor_type( 'title' ) ); 42 | 43 | // Link field 44 | $this->assertEquals( 'The slide url', $subject->get_title( 'url' ) ); 45 | $this->assertEquals( 'LINK', $subject->get_editor_type( 'url' ) ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/phpunit/tests/modules/TestMultipleGallery.php: -------------------------------------------------------------------------------- 1 | assertEquals( [ 'gallery_title' ], $subject->get_fields() ); 17 | } 18 | 19 | /** 20 | * @test 21 | */ 22 | public function it_get_items_field() { 23 | $subject = new MulitpleGallery(); 24 | $this->assertEquals( 'galleries', $subject->get_items_field() ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/phpunit/tests/modules/TestReviews.php: -------------------------------------------------------------------------------- 1 | assertEquals( [ 'content', 'name', 'title', 'link' => [ 'field' => 'url' ] ], $subject->get_fields() ); 17 | } 18 | 19 | /** 20 | * @test 21 | */ 22 | public function it_get_items_field() { 23 | $subject = new Reviews(); 24 | $this->assertEquals( 'slides', $subject->get_items_field() ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/phpunit/tests/modules/Test_WPML_Elementor_Module_With_Items.php: -------------------------------------------------------------------------------- 1 | 'id_1', 21 | 'field_A' => self::get_string_value( 'field_A', 'id_1' ), 22 | 'field_B' => [ 'inner_field_B' => self::get_string_value( 'inner_field_B', 'id_1' ) ], 23 | ], 24 | [ 25 | '_id' => 'id_2', 26 | 'field_A' => self::get_string_value( 'field_A', 'id_2' ), 27 | 'not_translated' => 'not translated field', 28 | ], 29 | ]; 30 | 31 | $element = self::get_element( $fields ); 32 | 33 | $expected_strings = array_merge( 34 | $strings, 35 | [ 36 | $this->get_string( 'field_A', 'id_1' ), 37 | $this->get_string( 'inner_field_B', 'id_1' ), 38 | $this->get_string( 'field_A', 'id_2' ), 39 | ] 40 | ); 41 | 42 | $subject = new WPML_Elementor_Module_With_Items_For_Test(); 43 | 44 | $this->assertEquals( $expected_strings, $subject->get( self::NODE_ID, $element, $strings ) ); 45 | } 46 | 47 | /** 48 | * @test 49 | */ 50 | public function it_updates_a_field() { 51 | $translated_value = 'the translated value'; 52 | 53 | $string = $this->get_string( 'field_A', 'id_2' ); 54 | $string->set_value( $translated_value ); 55 | 56 | $field_to_translate = [ 57 | '_id' => 'id_2', 58 | 'field_A' => 'something to translate', 59 | 'not_translated' => 'not translated field', 60 | ]; 61 | 62 | $expected_translated_field = $field_to_translate; 63 | $expected_translated_field['field_A'] = $translated_value; 64 | // We are modifying the field, and this 'index' will be used outside the class... 65 | $expected_translated_field['index'] = 1; 66 | 67 | $fields = [ 68 | [ 69 | '_id' => 'id_1', 70 | 'field_A' => 'some value for field_A - id_1', 71 | 'field_B' => [ 'inner_field_B' => 'some value for inner_field_B - id_1' ], 72 | ], 73 | $field_to_translate, 74 | [ 75 | '_id' => 'id_3', 76 | 'field_A' => 'some value for field_A - id_3', 77 | 'field_B' => [ 'inner_field_B' => 'some value for inner_field_B - id_3' ], 78 | ], 79 | ]; 80 | 81 | $element = self::get_element( $fields ); 82 | 83 | $subject = new WPML_Elementor_Module_With_Items_For_Test(); 84 | 85 | $this->assertEquals( 86 | $expected_translated_field, 87 | $subject->update( self::NODE_ID, $element, $string ) 88 | ); 89 | } 90 | 91 | /** 92 | * @test 93 | */ 94 | public function it_updates_a_inner_field() { 95 | $translated_value = 'the translated value'; 96 | 97 | $string = $this->get_string( 'inner_field_B', 'id_2' ); 98 | $string->set_value( $translated_value ); 99 | 100 | $field_to_translate = [ 101 | '_id' => 'id_2', 102 | 'field_A' => 'something', 103 | 'field_B' => [ 104 | 'foo' => 'bar', 105 | 'inner_field_B' => 'TO BE TRANSLATED', 106 | 'hello' => 'there', 107 | ], 108 | ]; 109 | 110 | $expected_translated_field = $field_to_translate; 111 | $expected_translated_field['field_B']['inner_field_B'] = $translated_value; 112 | // We are modifying the field, and this 'index' will be used outside the class... 113 | $expected_translated_field['index'] = 1; 114 | 115 | $fields = [ 116 | [ 117 | '_id' => 'id_1', 118 | 'field_A' => 'some value for field_A - id_1', 119 | 'field_B' => [ 'inner_field_B' => 'some value for inner_field_B - id_1' ], 120 | ], 121 | $field_to_translate, 122 | [ 123 | '_id' => 'id_3', 124 | 'field_A' => 'some value for field_A - id_3', 125 | 'field_B' => [ 'inner_field_B' => 'some value for inner_field_B - id_3' ], 126 | ], 127 | ]; 128 | 129 | $element = self::get_element( $fields ); 130 | 131 | $subject = new WPML_Elementor_Module_With_Items_For_Test(); 132 | 133 | $this->assertEquals( 134 | $expected_translated_field, 135 | $subject->update( self::NODE_ID, $element, $string ) 136 | ); 137 | } 138 | 139 | public static function get_string_value( $name, $item_id ) { 140 | return "The value for $name - $item_id"; 141 | } 142 | 143 | public static function get_field_prop( $field, $prop ) { 144 | return "The $prop for $field"; 145 | } 146 | 147 | private static function get_string( $field, $item_id ) { 148 | return new WPML_PB_String( 149 | self::get_string_value( $field, $item_id ), 150 | WPML_Elementor_Module_With_Items_For_Test::WIDGET_TYPE . '-' . $field . '-' . Test_WPML_Elementor_Module_With_Items::NODE_ID . '-' . $item_id, 151 | Test_WPML_Elementor_Module_With_Items::get_field_prop( $field, 'title' ), 152 | Test_WPML_Elementor_Module_With_Items::get_field_prop( $field, 'type' ) 153 | ); 154 | } 155 | 156 | private static function get_element( array $fields ) { 157 | return [ 158 | WPML_Elementor_Translatable_Nodes::TYPE => WPML_Elementor_Module_With_Items_For_Test::WIDGET_TYPE, 159 | WPML_Elementor_Translatable_Nodes::SETTINGS_FIELD => [ 160 | 'test_field' => $fields, 161 | ], 162 | ]; 163 | } 164 | } 165 | 166 | class WPML_Elementor_Module_With_Items_For_Test extends WPML_Elementor_Module_With_Items { 167 | 168 | const WIDGET_TYPE = 'nice-widget'; 169 | 170 | public function get_fields() { 171 | return [ 172 | 'first_missing_field_in_widget', 173 | 'field_A', 174 | 'field_B' => [ 175 | 'first_missing_inner_field', 176 | 'inner_field_B', 177 | 'second_missing_inner_field', 178 | ], 179 | 'second_missing_field_in_widget', 180 | ]; 181 | } 182 | 183 | protected function get_title( $field ) { 184 | return Test_WPML_Elementor_Module_With_Items::get_field_prop( $field, 'title' ); 185 | } 186 | 187 | protected function get_editor_type( $field ) { 188 | return Test_WPML_Elementor_Module_With_Items::get_field_prop( $field, 'type' ); 189 | } 190 | 191 | public function get_items_field() { 192 | return 'test_field'; 193 | } 194 | } -------------------------------------------------------------------------------- /tests/phpunit/tests/modules/test-wpml-elementor-accordion.php: -------------------------------------------------------------------------------- 1 | assertEquals( $expected, $subject->get_fields() ); 19 | } 20 | 21 | /** 22 | * @test 23 | */ 24 | public function it_get_items_field() { 25 | $subject = new WPML_Elementor_Accordion(); 26 | $this->assertEquals( 'tabs', $subject->get_items_field() ); 27 | } 28 | } -------------------------------------------------------------------------------- /tests/phpunit/tests/modules/test-wpml-elementor-form.php: -------------------------------------------------------------------------------- 1 | assertEquals( $expected, $subject->get_fields() ); 29 | } 30 | 31 | /** 32 | * @test 33 | */ 34 | public function it_get_items_field() { 35 | $subject = new WPML_Elementor_Form(); 36 | $this->assertEquals( 'form_fields', $subject->get_items_field() ); 37 | } 38 | } -------------------------------------------------------------------------------- /tests/phpunit/tests/modules/test-wpml-elementor-icon-list.php: -------------------------------------------------------------------------------- 1 | array( 'url' ) ); 17 | $subject = new WPML_Elementor_Icon_List(); 18 | $this->assertEquals( $expected, $subject->get_fields() ); 19 | } 20 | 21 | /** 22 | * @test 23 | */ 24 | public function it_get_items_field() { 25 | $subject = new WPML_Elementor_Icon_List(); 26 | $this->assertEquals( 'icon_list', $subject->get_items_field() ); 27 | } 28 | 29 | /** 30 | * @test 31 | * @group wpmlcore-5929 32 | */ 33 | public function it_should_get() { 34 | $node_id ='f94a4f8'; 35 | $element = array( 36 | 'id' => $node_id, 37 | 'elType' => 'widget', 38 | 'settings' => array( 39 | 'icon_list' => array( 40 | array( 41 | 'text' => 'List Item #1', 42 | 'icon' => 'fa fa-check', 43 | '_id' => '38f8b31', 44 | 'link' => array( 45 | 'url' => 'http://wpml.local/sample-page/', 46 | 'is_external' => '', 47 | 'nofollow' => '', 48 | ), 49 | ), 50 | ), 51 | ), 52 | 'elements' => array(), 53 | 'widgetType' => 'icon-list', 54 | ); 55 | 56 | $strings = array(); 57 | 58 | $expected_text_string = new WPML_PB_String( 59 | 'List Item #1', 60 | 'icon-list-text-f94a4f8-38f8b31', 61 | 'Icon List: Text', 62 | 'LINE' 63 | ); 64 | 65 | $expected_link_string = new WPML_PB_String( 66 | 'http://wpml.local/sample-page/', 67 | 'icon-list-url-f94a4f8-38f8b31', 68 | 'Icon List: Link URL', 69 | 'LINK' 70 | ); 71 | 72 | $subject = new WPML_Elementor_Icon_List(); 73 | 74 | $actual_strings = $subject->get( $node_id, $element, $strings ); 75 | 76 | $this->assertEquals( $expected_text_string, $actual_strings[0] ); 77 | $this->assertEquals( $expected_link_string, $actual_strings[1] ); 78 | 79 | } 80 | } -------------------------------------------------------------------------------- /tests/phpunit/tests/modules/test-wpml-elementor-price-list.php: -------------------------------------------------------------------------------- 1 | array( 'url' ) ); 17 | $subject = new WPML_Elementor_Price_List(); 18 | $this->assertEquals( $expected, $subject->get_fields() ); 19 | } 20 | 21 | /** 22 | * @test 23 | */ 24 | public function it_get_items_field() { 25 | $subject = new WPML_Elementor_Price_List(); 26 | $this->assertEquals( 'price_list', $subject->get_items_field() ); 27 | } 28 | } -------------------------------------------------------------------------------- /tests/phpunit/tests/modules/test-wpml-elementor-price-table.php: -------------------------------------------------------------------------------- 1 | assertEquals( $expected, $subject->get_fields() ); 19 | } 20 | 21 | /** 22 | * @test 23 | */ 24 | public function it_get_items_field() { 25 | $subject = new WPML_Elementor_Price_Table(); 26 | $this->assertEquals( 'features_list', $subject->get_items_field() ); 27 | } 28 | } -------------------------------------------------------------------------------- /tests/phpunit/tests/modules/test-wpml-elementor-slides.php: -------------------------------------------------------------------------------- 1 | array( 'url' ) ); 17 | $subject = new WPML_Elementor_Slides(); 18 | $this->assertEquals( $expected, $subject->get_fields() ); 19 | } 20 | 21 | /** 22 | * @test 23 | */ 24 | public function it_get_items_field() { 25 | $subject = new WPML_Elementor_Slides(); 26 | $this->assertEquals( 'slides', $subject->get_items_field() ); 27 | } 28 | } -------------------------------------------------------------------------------- /tests/phpunit/tests/modules/test-wpml-elementor-tabs.php: -------------------------------------------------------------------------------- 1 | assertEquals( $expected, $subject->get_fields() ); 19 | } 20 | 21 | /** 22 | * @test 23 | */ 24 | public function it_get_items_field() { 25 | $subject = new WPML_Elementor_Tabs(); 26 | $this->assertEquals( 'tabs', $subject->get_items_field() ); 27 | } 28 | } -------------------------------------------------------------------------------- /tests/phpunit/tests/modules/test-wpml-elementor-testimonial-carousel.php: -------------------------------------------------------------------------------- 1 | assertEquals( $expected, $subject->get_fields() ); 19 | } 20 | 21 | /** 22 | * @test 23 | */ 24 | public function it_get_items_field() { 25 | $subject = new WPML_Elementor_Testimonial_Carousel(); 26 | $this->assertEquals( 'slides', $subject->get_items_field() ); 27 | } 28 | } -------------------------------------------------------------------------------- /tests/phpunit/tests/modules/test-wpml-elementor-toggle.php: -------------------------------------------------------------------------------- 1 | assertEquals( $expected, $subject->get_fields() ); 19 | } 20 | 21 | /** 22 | * @test 23 | */ 24 | public function it_get_items_field() { 25 | $subject = new WPML_Elementor_Toggle(); 26 | $this->assertEquals( 'tabs', $subject->get_items_field() ); 27 | } 28 | } -------------------------------------------------------------------------------- /tests/phpunit/tests/test-pb-fix-maintenance-query.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf( 'IWPML_Frontend_Action', $subject ); 17 | } 18 | 19 | /** 20 | * @test 21 | */ 22 | public function it_adds_hooks() { 23 | $subject = new WPML_PB_Fix_Maintenance_Query(); 24 | \WP_Mock::expectActionAdded( 'template_redirect', array( $subject, 'fix_global_query' ), 12 ); 25 | $subject->add_hooks(); 26 | } 27 | 28 | /** 29 | * @test 30 | * @group wpmlcore-6218 31 | */ 32 | public function it_does_not_fix_global_query_if_post_is_not_set() { 33 | $original_post = new stdClass(); 34 | $template_post = new stdClass(); 35 | $template_id = 3; 36 | 37 | $post_backup = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null; 38 | $the_query_backup = isset( $GLOBALS['wp_the_query'] ) ? $GLOBALS['wp_the_query'] : null; 39 | $query_backup = isset( $GLOBALS['wp_query'] ) ? $GLOBALS['wp_query'] : null; 40 | 41 | $GLOBALS['post'] = null; 42 | $GLOBALS['wp_the_query'] = $original_post; 43 | $GLOBALS['wp_query'] = $template_post; 44 | 45 | $subject = new WPML_PB_Fix_Maintenance_Query(); 46 | 47 | \mockery::mock( 'overload:' . \Elementor\Maintenance_Mode::class ) 48 | ->shouldReceive( 'get' ) 49 | ->with( 'template_id' ) 50 | ->andReturn( $template_id ); 51 | 52 | $subject->fix_global_query(); 53 | $this->assertEquals( $original_post, $GLOBALS['wp_the_query'] ); 54 | $this->assertEquals( $template_post, $GLOBALS['wp_query'] ); 55 | 56 | $GLOBALS['post'] = $post_backup; 57 | $GLOBALS['wp_the_query'] = $the_query_backup; 58 | $GLOBALS['post'] = $post_backup; 59 | $GLOBALS['wp_query'] = $query_backup; 60 | } 61 | 62 | /** 63 | * @test 64 | */ 65 | public function it_fixes_global_query() { 66 | $original_post = new stdClass(); 67 | $template_post = new stdClass(); 68 | $template_id = 3; 69 | 70 | $post_backup = isset( $GLOBALS['post'] ) ? $GLOBALS['post'] : null; 71 | $the_query_backup = isset( $GLOBALS['wp_the_query'] ) ? $GLOBALS['wp_the_query'] : null; 72 | $query_backup = isset( $GLOBALS['wp_query'] ) ? $GLOBALS['wp_query'] : null; 73 | 74 | $GLOBALS['post'] = $template_post; 75 | $GLOBALS['post']->ID = $template_id; 76 | $GLOBALS['wp_the_query'] = $original_post; 77 | $GLOBALS['wp_query'] = $template_post; 78 | 79 | $subject = new WPML_PB_Fix_Maintenance_Query(); 80 | 81 | \mockery::mock( 'overload:' . \Elementor\Maintenance_Mode::class ) 82 | ->shouldReceive( 'get' ) 83 | ->with( 'template_id' ) 84 | ->andReturn( $template_id ); 85 | 86 | $subject->fix_global_query(); 87 | $this->assertEquals( $GLOBALS['wp_query'], $GLOBALS['wp_the_query'] ); 88 | 89 | $GLOBALS['post'] = $post_backup; 90 | $GLOBALS['wp_the_query'] = $the_query_backup; 91 | $GLOBALS['post'] = $post_backup; 92 | $GLOBALS['wp_query'] = $query_backup; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /tests/phpunit/tests/test-wpml-elementor-db-factory.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf( 'WPML_Elementor_DB', $subject->create() ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/phpunit/tests/test-wpml-elementor-db.php: -------------------------------------------------------------------------------- 1 | getMockBuilder( '\Elementor\DB' ) 17 | ->setMethods( array( 'save_plain_text' ) ) 18 | ->disableOriginalConstructor() 19 | ->getMock(); 20 | 21 | $post_id = mt_rand( 1, 10 ); 22 | 23 | $elementor_db->expects( $this->once() ) 24 | ->method( 'save_plain_text' ) 25 | ->with( $post_id ); 26 | 27 | $subject = new WPML_Elementor_DB( $elementor_db ); 28 | $subject->save_plain_text( $post_id ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/phpunit/tests/test-wpml-elementor-integration-factory.php: -------------------------------------------------------------------------------- 1 | shouldReceive( 'load' ) 22 | ->once() 23 | ->with( array( 24 | 'WPML_Elementor_Translate_IDs_Factory', 25 | 'WPML_Elementor_URLs_Factory', 26 | 'WPML_Elementor_Adjust_Global_Widget_ID_Factory', 27 | 'WPML_PB_Elementor_Handle_Custom_Fields_Factory', 28 | 'WPML_Elementor_Media_Hooks_Factory', 29 | 'WPML_Elementor_WooCommerce_Hooks_Factory', 30 | \WPML\PB\Elementor\LanguageSwitcher\LanguageSwitcher::class, 31 | \WPML\PB\Elementor\Hooks\DynamicElements::class, 32 | \WPML\PB\Elementor\Hooks\GutenbergCleanup::class, 33 | \WPML\PB\Elementor\Hooks\Frontend::class, 34 | \WPML\PB\Elementor\Config\Factory::class, 35 | \WPML\PB\Elementor\Hooks\LandingPages::class, 36 | ) ); 37 | 38 | $string_registration = \Mockery::mock( 'overload:WPML_PB_String_Registration' ); 39 | 40 | $string_registration_factory = \Mockery::mock( 'overload:WPML_String_Registration_Factory' ); 41 | $string_registration_factory->shouldReceive( 'create' ) 42 | ->andReturn( $string_registration ); 43 | 44 | $this->assertInstanceOf( 'WPML_Page_Builders_Integration', $elementor_factory->create() ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/phpunit/tests/test-wpml-elementor-register-strings.php: -------------------------------------------------------------------------------- 1 | get_post_and_package( 'Elementor' ); 16 | $node_id = mt_rand(); 17 | $settings = array( 18 | 'id' => $node_id, 19 | 'elType' => 'widget', 20 | 'settings' => array( 21 | 'editor' => '', 22 | ), 23 | 'widgetType' => 'text-editor', 24 | ); 25 | $string = new WPML_PB_String( rand_str(), rand_str(), rand_str(), rand_str() ); 26 | $strings = array( $string ); 27 | 28 | $elementor_data = array( 29 | array( 30 | 'id' => mt_rand(), 31 | 'elements' => array( 32 | array( 33 | 'id' => mt_rand(), 34 | 'elements' => array( 35 | array( 36 | 'id' => $node_id, 37 | 'elType' => 'widget', 38 | 'settings' => array( 39 | 'editor' => '', 40 | ), 41 | 'widgetType' => 'text-editor', 42 | ), 43 | ), 44 | ), 45 | ), 46 | ), 47 | ); 48 | 49 | \WP_Mock::wpFunction( 'get_post_meta', array( 50 | 'times' => 1, 51 | 'args' => array( $post->ID, '_elementor_data', false ), 52 | 'return' => array( json_encode( $elementor_data ) ), 53 | ) ); 54 | 55 | WP_Mock::expectAction( 'wpml_start_string_package_registration', $package ); 56 | WP_Mock::expectAction( 'wpml_delete_unused_package_strings', $package ); 57 | 58 | $translatable_nodes = $this->getMockBuilder( 'WPML_Elementor_Translatable_Nodes' ) 59 | ->setMethods( array( 'get', 'initialize_nodes_to_translate' ) ) 60 | ->disableOriginalConstructor() 61 | ->getMock(); 62 | $translatable_nodes->expects( $this->once() ) 63 | ->method( 'get' ) 64 | ->with( $node_id, $settings ) 65 | ->willReturn( $strings ); 66 | 67 | $data_settings = $this->getMockBuilder( 'WPML_Elementor_Data_Settings' ) 68 | ->disableOriginalConstructor() 69 | ->getMock(); 70 | 71 | $data_settings->method( 'get_meta_field' ) 72 | ->willReturn( '_elementor_data' ); 73 | 74 | $data_settings->method( 'get_node_id_field' ) 75 | ->willReturn( 'id' ); 76 | 77 | $data_settings->method( 'convert_data_to_array' ) 78 | ->with( array( json_encode( $elementor_data ) ) ) 79 | ->willReturn( json_decode( json_encode( $elementor_data ), true ) ); 80 | 81 | $data_settings->method( 'is_handling_post' )->with( $post->ID )->willReturn( true ); 82 | 83 | $string_registration = $this->getMockBuilder( 'WPML_PB_String_Registration' ) 84 | ->setMethods( array( 'register_string' ) ) 85 | ->disableOriginalConstructor() 86 | ->getMock(); 87 | 88 | $string_registration->expects( $this->once() ) 89 | ->method( 'register_string' ) 90 | ->with( 91 | $package['post_id'], 92 | $string->get_value(), 93 | $string->get_editor_type(), 94 | $string->get_title(), 95 | $string->get_name() ); 96 | 97 | $subject = new WPML_Elementor_Register_Strings( $translatable_nodes, $data_settings, $string_registration ); 98 | $subject->register_strings( $post, $package ); 99 | } 100 | } -------------------------------------------------------------------------------- /tests/phpunit/tests/test-wpml-elementor-urls.php: -------------------------------------------------------------------------------- 1 | expectFilterAdded( 'elementor/document/urls/edit', array( 17 | $subject, 18 | 'adjust_edit_with_elementor_url' 19 | ), 10, 2 ); 20 | 21 | $this->expectFilterAdded( 'wpml_is_pagination_url_in_post', [ 22 | $subject, 23 | 'is_pagination_url' 24 | ], 10, 3 ); 25 | 26 | $this->expectFilterAdded( 'paginate_links', [ 27 | $subject, 28 | 'fix_pagination_link_with_language_param' 29 | ], 10, 1 ); 30 | 31 | $subject->add_hooks(); 32 | 33 | } 34 | 35 | /** 36 | * @test 37 | */ 38 | public function it_adjusts_url_for_domain() { 39 | 40 | $original_url = 'http://my-site.com/wp-admin/post.php?post=6&action=elementor'; 41 | $translated_url = 'http://fr.my-site.com/wp-admin/post.php?post=6&action=elementor'; 42 | 43 | $post_language = 'fr'; 44 | 45 | $post = (object) array( 'ID' => 123 ); 46 | 47 | $post_element = \Mockery::mock( 'WPML_Post_Element' ); 48 | $post_element->shouldReceive( 'get_language_code' )->andReturn( $post_language ); 49 | 50 | $element_factory = \Mockery::mock( 'WPML_Translation_Element_Factory' ); 51 | $element_factory->shouldReceive( 'create_post' ) 52 | ->with( $post->ID ) 53 | ->andReturn( $post_element ); 54 | 55 | $language_converter = \Mockery::mock( 'IWPML_URL_Converter_Strategy' ); 56 | $language_converter->shouldReceive( 'convert_admin_url_string' ) 57 | ->with( $original_url, $post_language ) 58 | ->andReturn( $translated_url ); 59 | 60 | $current_language = \Mockery::mock( 'IWPML_Current_Language' ); 61 | 62 | $subject = new WPML_Elementor_URLs( 63 | $element_factory, 64 | $language_converter, 65 | $current_language 66 | ); 67 | 68 | $elementor_document = \Mockery::mock( 'Elementor_Document' ); // Note: this is not the real class name 69 | $elementor_document->shouldReceive( 'get_main_post' )->andReturn( $post ); 70 | 71 | $this->assertEquals( $translated_url, $subject->adjust_edit_with_elementor_url( $original_url, $elementor_document ) ); 72 | } 73 | 74 | /** 75 | * @test 76 | */ 77 | public function it_adjusts_url_for_domain_using_current_language_if_element_has_no_language() { 78 | $original_url = 'http://my-site.com/wp-admin/post.php?post=6&action=elementor'; 79 | $translated_url = 'http://fr.my-site.com/wp-admin/post.php?post=6&action=elementor'; 80 | 81 | $site_language = 'fr'; 82 | 83 | $post = (object) array( 'ID' => 123 ); 84 | 85 | $post_element = \Mockery::mock( 'WPML_Post_Element' ); 86 | $post_element->shouldReceive( 'get_language_code' )->andReturn( '' ); 87 | 88 | $element_factory = \Mockery::mock( 'WPML_Translation_Element_Factory' ); 89 | $element_factory->shouldReceive( 'create_post' ) 90 | ->with( $post->ID ) 91 | ->andReturn( $post_element ); 92 | 93 | $language_converter = \Mockery::mock( 'IWPML_URL_Converter_Strategy' ); 94 | $language_converter->shouldReceive( 'convert_admin_url_string' ) 95 | ->with( $original_url, $site_language ) 96 | ->andReturn( $translated_url ); 97 | 98 | $current_language = \Mockery::mock( 'IWPML_Current_Language' ); 99 | $current_language->shouldReceive( 'get_current_language' )->andReturn( $site_language ); 100 | 101 | $subject = $this->get_subject( 102 | $element_factory, 103 | $language_converter, 104 | $current_language 105 | ); 106 | 107 | $elementor_document = \Mockery::mock( 'Elementor_Document' ); // Note: this is not the real class name 108 | $elementor_document->shouldReceive( 'get_main_post' )->andReturn( $post ); 109 | 110 | $this->assertEquals( $translated_url, $subject->adjust_edit_with_elementor_url( $original_url, $elementor_document ) ); 111 | } 112 | 113 | public function pagination_provider(){ 114 | return [ 115 | [ 'elementor-post', 'index.php/elementor-post/2/' ], 116 | [ 'elementor/post', 'index.php/elementor/post/2/' ] 117 | ]; 118 | } 119 | 120 | /** 121 | * @test 122 | * 123 | * @dataProvider pagination_provider 124 | * 125 | * @param string $post_name 126 | * @param string $path 127 | */ 128 | public function it_filters_pagination_in_post( $post_name, $path ) { 129 | 130 | $element_factory = \Mockery::mock( 'WPML_Translation_Element_Factory' ); 131 | $language_converter = \Mockery::mock( 'IWPML_URL_Converter_Strategy' ); 132 | $current_language = \Mockery::mock( 'IWPML_Current_Language' ); 133 | 134 | WP_Mock::userFunction( 'get_post_meta', [ 'return' => 'builder' ] ); 135 | WP_Mock::userFunction( 'get_the_ID', [ 'return' => 1 ] ); 136 | 137 | $subject = $this->get_subject( 138 | $element_factory, 139 | $language_converter, 140 | $current_language 141 | ); 142 | 143 | $this->assertSame( 144 | true, 145 | $subject->is_pagination_url( false, $path, $post_name ) 146 | ); 147 | } 148 | 149 | /** 150 | * @test 151 | */ 152 | public function it_fixes_pagination_link_with_langauge_param() { 153 | $post_name = 'elementor-post'; 154 | $lang = 'de'; 155 | $link_without_lang = "http://example.com/$post_name/2/"; 156 | $link = "http://example.com/$post_name/?lang=$lang/2/"; 157 | $proper_link = "http://example.com/$post_name/2/?lang=$lang"; 158 | 159 | $element_factory = \Mockery::mock( 'WPML_Translation_Element_Factory' ); 160 | $language_converter = \Mockery::mock( 'IWPML_URL_Converter_Strategy' ); 161 | $current_language = \Mockery::mock( 'IWPML_Current_Language' ); 162 | 163 | $current_language->shouldReceive( 'get_current_language' )->andReturn( $lang ); 164 | 165 | $language_converter->shouldReceive( 'convert_url_string' ) 166 | ->with( $link_without_lang, $lang ) 167 | ->andReturn( $proper_link ); 168 | 169 | WP_Mock::userFunction( 'get_post_meta', [ 'return' => 'builder' ] ); 170 | WP_Mock::userFunction( 'get_the_ID', [ 'return' => 1 ] ); 171 | WP_Mock::userFunction( 'get_post', [ 172 | 'return' => (object) [ 173 | 'ID' => 1, 174 | 'post_name' => $post_name 175 | ] 176 | ] ); 177 | 178 | $subject = $this->get_subject( 179 | $element_factory, 180 | $language_converter, 181 | $current_language 182 | ); 183 | 184 | $this->assertSame( 185 | $proper_link, 186 | $subject->fix_pagination_link_with_language_param( $link ) 187 | ); 188 | } 189 | 190 | private function get_subject( $element_factory, $language_converter, $current_language ) { 191 | return new WPML_Elementor_URLs( 192 | $element_factory, 193 | $language_converter, 194 | $current_language 195 | ); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /tests/phpunit/tests/test-wpml-elementor-woocommerce-hooks-factory.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf( WPML_Elementor_WooCommerce_Hooks::class, $subject->create() ); 16 | } 17 | } -------------------------------------------------------------------------------- /tests/phpunit/tests/test-wpml-elementor-woocommerce-hooks.php: -------------------------------------------------------------------------------- 1 | add_hooks(); 23 | } 24 | 25 | /** 26 | * @test 27 | */ 28 | public function it_should_not_change_if_post_type_is_NOT_in_query_vars() { 29 | $subject = new WPML_Elementor_WooCommerce_Hooks(); 30 | 31 | $query = $this->get_wp_query(); 32 | 33 | $_POST['action'] = 'elementor_ajax'; 34 | 35 | $query->query_vars[ 'suppress_filters' ] = true; 36 | 37 | $filtered_query = $subject->do_not_suppress_filters_on_product_widget( $query ); 38 | 39 | $this->assertTrue( $filtered_query->query_vars['suppress_filters'] ); 40 | } 41 | 42 | /** 43 | * @test 44 | */ 45 | public function it_should_not_change_if_post_type_is_not_product() { 46 | $subject = new WPML_Elementor_WooCommerce_Hooks(); 47 | 48 | $query = $this->get_wp_query(); 49 | 50 | $_POST['action'] = 'elementor_ajax'; 51 | 52 | $query->query_vars[ 'suppress_filters' ] = true; 53 | $query->query_vars[ 'post_type' ] = 'something'; 54 | 55 | $filtered_query = $subject->do_not_suppress_filters_on_product_widget( $query ); 56 | 57 | $this->assertTrue( $filtered_query->query_vars['suppress_filters'] ); 58 | } 59 | 60 | /** 61 | * @test 62 | */ 63 | public function it_should_not_change_if_action_global_is_not_set() { 64 | $subject = new WPML_Elementor_WooCommerce_Hooks(); 65 | 66 | $query = $this->get_wp_query(); 67 | 68 | $query->query_vars[ 'suppress_filters' ] = true; 69 | $query->query_vars[ 'post_type' ] = 'product'; 70 | 71 | $filtered_query = $subject->do_not_suppress_filters_on_product_widget( $query ); 72 | 73 | $this->assertTrue( $filtered_query->query_vars['suppress_filters'] ); 74 | } 75 | 76 | /** 77 | * @test 78 | */ 79 | public function it_should_not_change_if_action_global_is_different_than_the_one_for_adding_widgets() { 80 | $subject = new WPML_Elementor_WooCommerce_Hooks(); 81 | 82 | $query = $this->get_wp_query(); 83 | 84 | $query->query_vars[ 'suppress_filters' ] = true; 85 | $query->query_vars[ 'post_type' ] = 'product'; 86 | 87 | $_POST['action'] = 'some-other-action'; 88 | 89 | $filtered_query = $subject->do_not_suppress_filters_on_product_widget( $query ); 90 | 91 | $this->assertTrue( $filtered_query->query_vars['suppress_filters'] ); 92 | } 93 | 94 | /** 95 | * @test 96 | */ 97 | public function it_should_set_suppress_filters_to_false() { 98 | $subject = new WPML_Elementor_WooCommerce_Hooks(); 99 | 100 | $query = $this->get_wp_query(); 101 | 102 | $query->query_vars[ 'suppress_filters' ] = true; 103 | $query->query_vars[ 'post_type' ] = 'product'; 104 | 105 | $_POST['action'] = 'elementor_ajax'; 106 | 107 | $filtered_query = $subject->do_not_suppress_filters_on_product_widget( $query ); 108 | 109 | $this->assertFalse( $filtered_query->query_vars['suppress_filters'] ); 110 | } 111 | 112 | /** 113 | * @return \WP_Query|\PHPUnit_Framework_MockObject_MockObject 114 | */ 115 | private function get_wp_query() { 116 | return $this->getMockBuilder( 'WP_Query' ) 117 | ->disableOriginalConstructor() 118 | ->getMock(); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /tests/phpunit/tests/test-wpml-pb-handle-custom-fields-factory.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf( 'IWPML_Backend_Action_Loader', $subject ); 16 | $this->assertInstanceOf( 'IWPML_AJAX_Action_Loader', $subject ); 17 | $this->assertInstanceOf( 'IWPML_Frontend_Action_Loader', $subject ); 18 | } 19 | 20 | /** 21 | * @test 22 | */ 23 | public function it_returns_instance() { 24 | \Mockery::mock( 'overload:WPML_PB_Handle_Custom_Fields' ); 25 | 26 | $subject = new WPML_PB_Elementor_Handle_Custom_Fields_Factory(); 27 | $this->assertInstanceOf( 'WPML_PB_Handle_Custom_Fields', $subject->create() ); 28 | } 29 | } 30 | 31 | if ( ! interface_exists( 'IWPML_Backend_Action_Loader' ) ) { 32 | interface IWPML_Backend_Action_Loader{} 33 | } 34 | 35 | if ( ! interface_exists( 'IWPML_AJAX_Action_Loader' ) ) { 36 | interface IWPML_AJAX_Action_Loader{} 37 | } 38 | 39 | if ( ! interface_exists( 'IWPML_Frontend_Action_Loader' ) ) { 40 | interface IWPML_Frontend_Action_Loader{} 41 | } -------------------------------------------------------------------------------- /tests/phpunit/util/wpml-pb-test-case2.php: -------------------------------------------------------------------------------- 1 | add_shortcodes( 26 | array( 27 | array( 28 | 'tag' => array( 29 | 'value' => 'vc_column_text', 30 | 'encoding' => $encoding, 31 | ), 32 | 'attributes' => array( 33 | array( 'value' => 'text', 'encoding' => $encoding ), 34 | array( 'value' => 'heading', 'encoding' => $encoding ), 35 | array( 'value' => 'title', 'encoding' => $encoding ), 36 | ), 37 | ), 38 | array( 39 | 'tag' => array( 40 | 'value' => 'vc_text_separator', 41 | 'encoding' => $encoding, 42 | ), 43 | 'attributes' => array( 44 | array( 'value' => 'text', 'encoding' => $encoding ), 45 | array( 'value' => 'heading', 'encoding' => $encoding ), 46 | array( 'value' => 'title', 'encoding' => $encoding ), 47 | ), 48 | ), 49 | array( 50 | 'tag' => array( 51 | 'value' => 'vc_message', 52 | 'encoding' => $encoding, 53 | ), 54 | ), 55 | ) 56 | ); 57 | 58 | $strategy->set_factory( $factory ); 59 | return $strategy; 60 | } 61 | 62 | protected function get_api_hooks_strategy( WPML_PB_Factory $factory ) { 63 | $strategy = new WPML_PB_API_Hooks_Strategy( 'Layout' ); 64 | $strategy->set_factory( $factory ); 65 | return $strategy; 66 | } 67 | 68 | protected function get_post_and_package( $name = '' ) { 69 | if ( ! $name ) { 70 | $name = rand_str(); 71 | } 72 | $post_id = rand(); 73 | 74 | $post = $this->get_post_stub(); 75 | $post->ID = $post_id; 76 | 77 | $package = array( 78 | 'kind' => $name, 79 | 'name' => $post_id, 80 | 'title' => 'Page Builder Page ' . $post_id, 81 | 'post_id' => $post_id, 82 | ); 83 | 84 | return array( $name, $post, $package ); 85 | } 86 | 87 | private function get_post_stub() { 88 | return $this->getMockBuilder( 'WP_Post' ) 89 | ->disableOriginalConstructor() 90 | ->getMock(); 91 | } 92 | 93 | /** 94 | * @return array 95 | */ 96 | protected function get_dummy_ls_languages() { 97 | return array( 98 | 'en' => array( 99 | 'code' => 'en', 100 | 'id' => '1', 101 | 'native_name' => 'English', 102 | 'major' => '1', 103 | 'active' => '1', 104 | 'default_locale' => 'en_US', 105 | 'encode_url' => '0', 106 | 'tag' => 'en', 107 | 'translated_name' => 'English', 108 | 'display_name' => 'English', 109 | 'url' => 'http://example.org', 110 | 'country_flag_url' => 'http://example.org/wp-content/plugins/sitepress-multilingual-cms/res/flags/en.png', 111 | 'language_code' => 'en', 112 | ), 113 | 'fr' => array( 114 | 'code' => 'fr', 115 | 'id' => '4', 116 | 'native_name' => 'Français', 117 | 'major' => '1', 118 | 'active' => 0, 119 | 'default_locale' => 'fr_FR', 120 | 'encode_url' => '0', 121 | 'tag' => 'fr', 122 | 'translated_name' => 'French', 123 | 'display_name' => 'French', 124 | 'url' => 'http://example.org?lang=fr', 125 | 'country_flag_url' => 'http://example.org/wp-content/plugins/sitepress-multilingual-cms/res/flags/fr.png', 126 | 'language_code' => 'fr', 127 | ), 128 | 'de' => array( 129 | 'code' => 'de', 130 | 'id' => '3', 131 | 'native_name' => 'Deutsch', 132 | 'major' => '1', 133 | 'active' => 0, 134 | 'default_locale' => 'de_DE', 135 | 'encode_url' => '0', 136 | 'tag' => 'de', 137 | 'translated_name' => 'German', 138 | 'display_name' => 'German', 139 | 'url' => 'http://example.org?lang=de', 140 | 'country_flag_url' => 'http://example.org/wp-content/plugins/sitepress-multilingual-cms/res/flags/de.png', 141 | 'language_code' => 'de', 142 | ), 143 | 'it' => array( 144 | 'code' => 'it', 145 | 'id' => '27', 146 | 'native_name' => 'Italiano', 147 | 'major' => '1', 148 | 'active' => 0, 149 | 'default_locale' => 'it_IT', 150 | 'encode_url' => '0', 151 | 'tag' => 'it', 152 | 'translated_name' => 'Italian', 153 | 'display_name' => 'Italian', 154 | 'url' => 'http://example.org?lang=it', 155 | 'country_flag_url' => 'http://example.org/wp-content/plugins/sitepress-multilingual-cms/res/flags/it.png', 156 | 'language_code' => 'it', 157 | ), 158 | 'ru' => array( 159 | 'code' => 'ru', 160 | 'id' => '46', 161 | 'native_name' => 'Русский', 162 | 'major' => '1', 163 | 'active' => 0, 164 | 'default_locale' => 'ru_RU', 165 | 'encode_url' => '0', 166 | 'tag' => 'ru', 167 | 'translated_name' => 'Russian', 168 | 'display_name' => 'Russian', 169 | 'url' => 'http://example.org?lang=ru', 170 | 'country_flag_url' => 'http://example.org/wp-content/plugins/sitepress-multilingual-cms/res/flags/ru.png', 171 | 'language_code' => 'ru', 172 | ), 173 | 'es' => array( 174 | 'code' => 'es', 175 | 'id' => '2', 176 | 'native_name' => 'Español', 177 | 'major' => '1', 178 | 'active' => 0, 179 | 'default_locale' => 'es_ES', 180 | 'encode_url' => '0', 181 | 'tag' => 'es', 182 | 'translated_name' => 'Spanish', 183 | 'display_name' => 'Spanish', 184 | 'url' => 'http://example.org?lang=es', 185 | 'country_flag_url' => 'http://example.org/wp-content/plugins/sitepress-multilingual-cms/res/flags/es.png', 186 | 'language_code' => 'es', 187 | ), 188 | ); 189 | } 190 | } 191 | 192 | --------------------------------------------------------------------------------