WIP on fix-config
This commit is contained in:
BIN
is_themecore/is_themecore/.DS_Store
vendored
Normal file
BIN
is_themecore/is_themecore/.DS_Store
vendored
Normal file
Binary file not shown.
9
is_themecore/is_themecore/.php-cs-fixer.dist.php
Normal file
9
is_themecore/is_themecore/.php-cs-fixer.dist.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
$config = new PrestaShop\CodingStandards\CsFixer\Config();
|
||||
|
||||
/** @var \Symfony\Component\Finder\Finder $finder */
|
||||
$finder = $config->setUsingCache(true)->getFinder();
|
||||
$finder->in(__DIR__)->exclude('vendor');
|
||||
|
||||
return $config;
|
||||
21
is_themecore/is_themecore/Makefile
Normal file
21
is_themecore/is_themecore/Makefile
Normal file
@@ -0,0 +1,21 @@
|
||||
build-module-zip: build-composer build-assets build-zip
|
||||
|
||||
build-zip:
|
||||
rm -rf is_themecore.zip
|
||||
cp -Ra $(PWD) /tmp/is_themecore
|
||||
rm -rf /tmp/is_themecore/config_*.xml
|
||||
rm -rf /tmp/is_themecore/_theme_dev/node_modules
|
||||
rm -rf /tmp/is_themecore/_module_dev/node_modules
|
||||
rm -rf /tmp/is_themecore/.github
|
||||
rm -rf /tmp/is_themecore/.gitignore
|
||||
rm -rf /tmp/is_themecore/.php-cs-fixer.cache
|
||||
rm -rf /tmp/is_themecore/.git
|
||||
mv -v /tmp/is_themecore $(PWD)/is_themecore
|
||||
zip -r is_themecore.zip is_themecore
|
||||
rm -rf $(PWD)/is_themecore
|
||||
|
||||
build-composer:
|
||||
composer install --no-dev -o
|
||||
|
||||
build-assets:
|
||||
cd _module_dev && . ${HOME}/.nvm/nvm.sh && nvm install && npm install && npm run build
|
||||
96
is_themecore/is_themecore/README.md
Normal file
96
is_themecore/is_themecore/README.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Theme core module
|
||||
Prestashop module created for [starter theme](https://github.com/Oksydan/modern-prestashop-starter-theme)
|
||||
|
||||
#### How to use assets.yml file
|
||||
|
||||
`assets.yml` file have to be placed inside `themes/THEME_NAME/config/` to work.
|
||||
Example of `assets.yml` file:
|
||||
|
||||
```yml
|
||||
css:
|
||||
product:
|
||||
fileName: product.css
|
||||
priority: 200
|
||||
include:
|
||||
- product
|
||||
checkout:
|
||||
fileName: checkout.css
|
||||
priority: 200
|
||||
include:
|
||||
- cart
|
||||
- order
|
||||
- orderconfirmation
|
||||
blog:
|
||||
fileName: blog.css
|
||||
priority: 200
|
||||
include:
|
||||
- module-blog-*
|
||||
example_remote_bootstrap:
|
||||
fileName: //cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css
|
||||
server: remote # required to set server: remote for remote file
|
||||
priority: 200
|
||||
|
||||
js:
|
||||
product:
|
||||
fileName: product.js
|
||||
priority: 200
|
||||
include:
|
||||
- product
|
||||
checkout:
|
||||
fileName: checkout.js
|
||||
priority: 200
|
||||
include:
|
||||
- cart
|
||||
- order
|
||||
- orderconfirmation
|
||||
blog:
|
||||
fileName: blog.js
|
||||
priority: 200
|
||||
include:
|
||||
- module-blog-*
|
||||
example_remote_bootstrap:
|
||||
fileName: //cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js
|
||||
server: remote # required to set server: remote for remote file
|
||||
priority: 200
|
||||
```
|
||||
|
||||
You are able to use windcard `*` with include page name.
|
||||
|
||||
|
||||
#### Structured data modification
|
||||
|
||||
You are able to modify structured data with hooks. List of hooks:
|
||||
- `actionStructuredDataBreadcrumb`
|
||||
- `actionStructuredDataProduct`
|
||||
- `actionStructuredDataShop`
|
||||
- `actionStructuredDataWebsite`
|
||||
|
||||
Every hook $param is an array with two keys:
|
||||
- `$data` - reference of structured data array
|
||||
- `$rawData` - raw structured data array (provided by data provider)
|
||||
|
||||
#### Partytown
|
||||
|
||||
You are able to use [partytown](https://partytown.builder.io/) with this module. You have to enable it first in module configuration.
|
||||
Example of usage for GTAG:
|
||||
|
||||
```html
|
||||
<script>
|
||||
window.partytown.forward.push('datalayer.push');
|
||||
window.partytown.forward.push('gtag');
|
||||
</script>
|
||||
<script type="text/partytown" src="https://www.googletagmanager.com/gtag/js?id=YOUR_GTAG_CODE"></script>
|
||||
<script type="text/partytown">
|
||||
dataLayer = window.dataLayer || [];
|
||||
window.gtag = function () {
|
||||
dataLayer.push(arguments);
|
||||
};
|
||||
|
||||
window.gtag('js', new Date());
|
||||
|
||||
window.gtag('config', 'YOUR_GTAG_CODE');
|
||||
</script>
|
||||
```
|
||||
|
||||
##### Beware that partytown is still in beta, and it may not work as expected. Make sure to test it before using in production.
|
||||
|
||||
1
is_themecore/is_themecore/_module_dev/.nvmrc
Normal file
1
is_themecore/is_themecore/_module_dev/.nvmrc
Normal file
@@ -0,0 +1 @@
|
||||
v18.12.1
|
||||
3049
is_themecore/is_themecore/_module_dev/package-lock.json
generated
Normal file
3049
is_themecore/is_themecore/_module_dev/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
is_themecore/is_themecore/_module_dev/package.json
Normal file
20
is_themecore/is_themecore/_module_dev/package.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "is_themecore_module",
|
||||
"label": "Theme core module",
|
||||
"version": "4.0.0",
|
||||
"description": "Theme core module",
|
||||
"author": "Igor Stępień",
|
||||
"scripts": {
|
||||
"build": "webpack --progress --mode=production --env stats=errors-only",
|
||||
"js-lint": "eslint -c .eslintrc.js --ext .js,.vue ./src",
|
||||
"js-lint-fix": "eslint -c .eslintrc.js --ext .js,.vue ./src --fix"
|
||||
},
|
||||
"devDependencies": {
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"webpack": "^5.81.0",
|
||||
"webpack-cli": "^5.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@builder.io/partytown": "^0.8.0"
|
||||
}
|
||||
}
|
||||
10
is_themecore/is_themecore/_module_dev/src/index.js
Normal file
10
is_themecore/is_themecore/_module_dev/src/index.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import { partytownSnippet } from '@builder.io/partytown/integration';
|
||||
|
||||
const snippetContent = partytownSnippet();
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const script = document.createElement("script");
|
||||
|
||||
script.textContent=snippetContent;
|
||||
document.body.append(script)
|
||||
});
|
||||
26
is_themecore/is_themecore/_module_dev/webpack.config.js
Normal file
26
is_themecore/is_themecore/_module_dev/webpack.config.js
Normal file
@@ -0,0 +1,26 @@
|
||||
// webpack.config.js
|
||||
const path = require('path');
|
||||
const CopyPlugin = require('copy-webpack-plugin');
|
||||
const partytown = require('@builder.io/partytown/utils');
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
partytown: './src/index.js',
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, '../public'),
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.js', '.mjs'],
|
||||
},
|
||||
plugins: [
|
||||
new CopyPlugin({
|
||||
patterns: [
|
||||
{
|
||||
from: partytown.libDirPath(),
|
||||
to: path.join(__dirname, '../public', '~partytown'),
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
};
|
||||
4
is_themecore/is_themecore/_theme_dev/.eslintignore
Normal file
4
is_themecore/is_themecore/_theme_dev/.eslintignore
Normal file
@@ -0,0 +1,4 @@
|
||||
/node_modules/
|
||||
/webpack/
|
||||
postcss.config.js
|
||||
webpack.config.js
|
||||
27
is_themecore/is_themecore/_theme_dev/.eslintrc.js
Normal file
27
is_themecore/is_themecore/_theme_dev/.eslintrc.js
Normal file
@@ -0,0 +1,27 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
browser: true,
|
||||
node: false,
|
||||
es6: true,
|
||||
jquery: true,
|
||||
},
|
||||
globals: {
|
||||
google: true,
|
||||
document: true,
|
||||
navigator: false,
|
||||
window: true,
|
||||
prestashop: true,
|
||||
},
|
||||
extends: ['airbnb-base'],
|
||||
rules: {
|
||||
'max-len': ['error', {code: 140}],
|
||||
'no-underscore-dangle': 'off',
|
||||
'no-restricted-syntax': 'off',
|
||||
'no-param-reassign': 'off',
|
||||
'import/no-unresolved': 'off',
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 2022
|
||||
},
|
||||
}
|
||||
1
is_themecore/is_themecore/_theme_dev/.nvmrc
Normal file
1
is_themecore/is_themecore/_theme_dev/.nvmrc
Normal file
@@ -0,0 +1 @@
|
||||
v18.12.1
|
||||
6
is_themecore/is_themecore/_theme_dev/.stylelintrc.json
Normal file
6
is_themecore/is_themecore/_theme_dev/.stylelintrc.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "stylelint-config-recommended-scss",
|
||||
"rules": {
|
||||
"scss/at-extend-no-missing-placeholder": null
|
||||
}
|
||||
}
|
||||
11
is_themecore/is_themecore/_theme_dev/index.php
Normal file
11
is_themecore/is_themecore/_theme_dev/index.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: ../');
|
||||
exit;
|
||||
21
is_themecore/is_themecore/_theme_dev/package.json
Normal file
21
is_themecore/is_themecore/_theme_dev/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "is_themecore",
|
||||
"label": "Theme core module",
|
||||
"version": "4.0.0",
|
||||
"description": "Theme core module",
|
||||
"author": "Igor Stępień",
|
||||
"scripts": {
|
||||
"scss-lint": "stylelint \"**/*.scss\" --fix",
|
||||
"scss-lint-fix": "stylelint \"**/*.scss\" --fix",
|
||||
"js-lint": "eslint -c .eslintrc.js --ext .js,.vue ./src",
|
||||
"js-lint-fix": "eslint -c .eslintrc.js --ext .js,.vue ./src --fix"
|
||||
},
|
||||
"devDependencies": {
|
||||
"stylelint": "^14.11.0",
|
||||
"stylelint-config-recommended-scss": "^7.0.0",
|
||||
"eslint": "^8.23.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"postcss": "^8.3.6"
|
||||
}
|
||||
}
|
||||
11
is_themecore/is_themecore/_theme_dev/src/index.php
Normal file
11
is_themecore/is_themecore/_theme_dev/src/index.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: ../');
|
||||
exit;
|
||||
11
is_themecore/is_themecore/_theme_dev/src/js/index.php
Normal file
11
is_themecore/is_themecore/_theme_dev/src/js/index.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: ../');
|
||||
exit;
|
||||
58
is_themecore/is_themecore/_theme_dev/src/js/listing/index.js
Normal file
58
is_themecore/is_themecore/_theme_dev/src/js/listing/index.js
Normal file
@@ -0,0 +1,58 @@
|
||||
function initListDisplay() {
|
||||
const handleClickEvent = (e) => {
|
||||
const target = e.target.closest('[data-toggle-listing]');
|
||||
|
||||
if (target && target.dataset.toggleListing !== undefined) {
|
||||
e.preventDefault();
|
||||
|
||||
if (target.classList.contains('active')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const display = target.dataset.displayType;
|
||||
const allButtons = document.querySelectorAll('[data-toggle-listing]');
|
||||
|
||||
allButtons.forEach((button) => {
|
||||
button.classList.remove('active');
|
||||
});
|
||||
|
||||
target.classList.add('active');
|
||||
|
||||
let requestData = {
|
||||
displayType: display,
|
||||
ajax: 1,
|
||||
};
|
||||
|
||||
requestData = Object.keys(requestData).map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(requestData[key])}`).join('&');
|
||||
|
||||
fetch(window.listDisplayAjaxUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: requestData,
|
||||
})
|
||||
.then((resp) => resp.text())
|
||||
.then((resp) => {
|
||||
try {
|
||||
const response = JSON.parse(resp);
|
||||
|
||||
if (response.success) {
|
||||
prestashop.emit('updateFacets', window.location.href);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error); // eslint-disable-line no-console
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error); // eslint-disable-line no-console
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('click', handleClickEvent);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initListDisplay();
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: ../');
|
||||
exit;
|
||||
2096
is_themecore/is_themecore/_theme_dev/yarn.lock
Normal file
2096
is_themecore/is_themecore/_theme_dev/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
33
is_themecore/is_themecore/composer.json
Normal file
33
is_themecore/is_themecore/composer.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "oksydan/is_themecore",
|
||||
"description": "Theme core module",
|
||||
"type": "prestashop-module",
|
||||
"license": "AFL-3.0",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Igor Stępień",
|
||||
"email": "igor@istpien.dev",
|
||||
"homepage": "https://istpien.dev",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.1.0",
|
||||
"rosell-dk/webp-convert": "^2.9"
|
||||
},
|
||||
"require-dev": {
|
||||
"prestashop/php-dev-tools": "^4.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Oksydan\\Module\\IsThemeCore\\": "src/"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"prepend-autoloader": false,
|
||||
"allow-plugins": {
|
||||
"phpro/grumphp-shim": false,
|
||||
"phpstan/extension-installer": false
|
||||
}
|
||||
}
|
||||
}
|
||||
2788
is_themecore/is_themecore/composer.lock
generated
Normal file
2788
is_themecore/is_themecore/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
75
is_themecore/is_themecore/config/admin/services.yml
Normal file
75
is_themecore/is_themecore/config/admin/services.yml
Normal file
@@ -0,0 +1,75 @@
|
||||
imports:
|
||||
- { resource: ../common.yml }
|
||||
services:
|
||||
_defaults:
|
||||
public: true
|
||||
|
||||
oksydan.module.is_themecore.form.settings.general_type:
|
||||
class: 'Oksydan\Module\IsThemeCore\Form\Settings\GeneralType'
|
||||
parent: "form.type.translatable.aware"
|
||||
public: true
|
||||
arguments:
|
||||
$displayListChoices: '@=service("oksydan.module.is_themecore.form.choice_provider.list_display_choice_provider").getChoices()'
|
||||
tags:
|
||||
- { name: form.type }
|
||||
|
||||
oksydan.module.is_themecore.form.settings.general_form_data_provider:
|
||||
class: 'Oksydan\Module\IsThemeCore\Form\Settings\GeneralFormDataProvider'
|
||||
arguments:
|
||||
- "@oksydan.module.is_themecore.form.settings.general_configuration"
|
||||
|
||||
oksydan.module.is_themecore.form.settings.general_form_data_handler:
|
||||
class: 'PrestaShop\PrestaShop\Core\Form\Handler'
|
||||
arguments:
|
||||
- "@form.factory"
|
||||
- "@prestashop.core.hook.dispatcher"
|
||||
- "@oksydan.module.is_themecore.form.settings.general_form_data_provider"
|
||||
- 'Oksydan\Module\IsThemeCore\Form\Settings\GeneralType'
|
||||
- "General"
|
||||
|
||||
oksydan.module.is_themecore.form.settings.webp_type:
|
||||
class: 'Oksydan\Module\IsThemeCore\Form\Settings\WebpType'
|
||||
parent: "form.type.translatable.aware"
|
||||
public: true
|
||||
arguments:
|
||||
$convertersList: '@=service("oksydan.module.is_themecore.form.choice_provider.webp_library_choice_provider").getChoices()'
|
||||
$convertersListFull: '@=service("oksydan.module.is_themecore.form.choice_provider.webp_library_choice_provider").getChoicesFull()'
|
||||
$router: "@router"
|
||||
tags:
|
||||
- { name: form.type }
|
||||
|
||||
oksydan.module.is_themecore.form.settings.webp_form_data_provider:
|
||||
class: 'Oksydan\Module\IsThemeCore\Form\Settings\WebpFormDataProvider'
|
||||
arguments:
|
||||
- "@oksydan.module.is_themecore.form.settings.webp_configuration"
|
||||
|
||||
oksydan.module.is_themecore.form.settings.webp_form_data_handler:
|
||||
class: 'PrestaShop\PrestaShop\Core\Form\Handler'
|
||||
arguments:
|
||||
- "@form.factory"
|
||||
- "@prestashop.core.hook.dispatcher"
|
||||
- "@oksydan.module.is_themecore.form.settings.webp_form_data_provider"
|
||||
- 'Oksydan\Module\IsThemeCore\Form\Settings\WebpType'
|
||||
- "Webp"
|
||||
|
||||
oksydan.module.is_themecore.form.choice_provider.webp_library_choice_provider:
|
||||
class: Oksydan\Module\IsThemeCore\Form\ChoiceProvider\WebpLibraryChoiceProvider
|
||||
arguments:
|
||||
- "@oksydan.module.is_themecore.core.webp.webp_convert_libraries"
|
||||
|
||||
oksydan.module.is_themecore.form.choice_provider.list_display_choice_provider:
|
||||
class: Oksydan\Module\IsThemeCore\Form\ChoiceProvider\ListDisplayChoiceProvider
|
||||
arguments:
|
||||
- "@oksydan.module.is_themecore.core.listing_display.theme_list_display"
|
||||
|
||||
oksydan.module.is_themecore.form.settings.webp_configuration:
|
||||
class: Oksydan\Module\IsThemeCore\Form\Settings\WebpConfiguration
|
||||
arguments:
|
||||
- "@prestashop.adapter.legacy.configuration"
|
||||
|
||||
oksydan.module.is_themecore.form.settings.general_configuration:
|
||||
class: Oksydan\Module\IsThemeCore\Form\Settings\GeneralConfiguration
|
||||
arguments:
|
||||
- "@prestashop.adapter.legacy.configuration"
|
||||
- "@prestashop.adapter.shop.context"
|
||||
- "@prestashop.adapter.multistore_feature"
|
||||
39
is_themecore/is_themecore/config/common.yml
Normal file
39
is_themecore/is_themecore/config/common.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
services:
|
||||
_defaults:
|
||||
public: true
|
||||
|
||||
oksydan.module.is_themecore.module:
|
||||
class: Is_themecore
|
||||
factory: [ 'Module', 'getInstanceByName' ]
|
||||
public: false
|
||||
arguments:
|
||||
- 'is_themecore'
|
||||
|
||||
Is_themecore: '@oksydan.module.is_themecore.module'
|
||||
|
||||
oksydan.module.is_themecore.core.webp.webp_convert_libraries:
|
||||
class: Oksydan\Module\IsThemeCore\Core\Webp\WebpConvertLibraries
|
||||
|
||||
oksydan.module.is_themecore.core.webp.webp_files_eraser:
|
||||
class: Oksydan\Module\IsThemeCore\Core\Webp\WebpFilesEraser
|
||||
|
||||
oksydan.module.is_themecore.core.listing_display.theme_list_display:
|
||||
class: Oksydan\Module\IsThemeCore\Core\ListingDisplay\ThemeListDisplay
|
||||
|
||||
oksydan.module.is_themecore.core.htaccess.htaccess_generator:
|
||||
class: Oksydan\Module\IsThemeCore\Core\Htaccess\HtaccessGenerator
|
||||
arguments:
|
||||
- "@Is_themecore"
|
||||
|
||||
oksydan.module.is_themecore.core.webp.related_image_file_finder:
|
||||
class: Oksydan\Module\IsThemeCore\Core\Webp\RelatedImageFileFinder
|
||||
|
||||
oksydan.module.is_themecore.core.webp.webp_generator:
|
||||
class: Oksydan\Module\IsThemeCore\Core\Webp\WebpGenerator
|
||||
arguments:
|
||||
- "@oksydan.module.is_themecore.core.webp.related_image_file_finder"
|
||||
|
||||
Oksydan\Module\IsThemeCore\Core\Partytown\FilesInstallation:
|
||||
class: Oksydan\Module\IsThemeCore\Core\Partytown\FilesInstallation
|
||||
arguments:
|
||||
- "@Is_themecore"
|
||||
81
is_themecore/is_themecore/config/front/services.yml
Normal file
81
is_themecore/is_themecore/config/front/services.yml
Normal file
@@ -0,0 +1,81 @@
|
||||
imports:
|
||||
- { resource: ../common.yml }
|
||||
services:
|
||||
_defaults:
|
||||
public: true
|
||||
|
||||
Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataBreadcrumbPresenter:
|
||||
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataBreadcrumbPresenter'
|
||||
|
||||
Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataBreadcrumbProvider:
|
||||
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataBreadcrumbProvider'
|
||||
arguments:
|
||||
- "@=service('prestashop.adapter.legacy.context').getContext()"
|
||||
|
||||
Oksydan\Module\IsThemeCore\Core\StructuredData\BreadcrumbStructuredData:
|
||||
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\BreadcrumbStructuredData'
|
||||
public: true
|
||||
arguments:
|
||||
- '@Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataBreadcrumbProvider'
|
||||
- '@Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataBreadcrumbPresenter'
|
||||
|
||||
Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataWebsitePresenter:
|
||||
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataWebsitePresenter'
|
||||
arguments:
|
||||
- "@=service('prestashop.adapter.legacy.context').getContext()"
|
||||
|
||||
Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataWebsiteProvider:
|
||||
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataWebsiteProvider'
|
||||
arguments:
|
||||
- "@=service('prestashop.adapter.legacy.context').getContext()"
|
||||
|
||||
Oksydan\Module\IsThemeCore\Core\StructuredData\WebsiteStructuredData:
|
||||
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\WebsiteStructuredData'
|
||||
public: true
|
||||
arguments:
|
||||
- '@Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataWebsiteProvider'
|
||||
- '@Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataWebsitePresenter'
|
||||
|
||||
Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataShopPresenter:
|
||||
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataShopPresenter'
|
||||
arguments:
|
||||
- "@=service('prestashop.adapter.legacy.context').getContext()"
|
||||
|
||||
Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataShopProvider:
|
||||
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataShopProvider'
|
||||
arguments:
|
||||
- "@=service('prestashop.adapter.legacy.context').getContext()"
|
||||
|
||||
Oksydan\Module\IsThemeCore\Core\StructuredData\ShopStructuredData:
|
||||
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\ShopStructuredData'
|
||||
public: true
|
||||
arguments:
|
||||
- '@Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataShopProvider'
|
||||
- '@Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataShopPresenter'
|
||||
|
||||
Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataProductPresenter:
|
||||
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataProductPresenter'
|
||||
arguments:
|
||||
- "@=service('prestashop.adapter.legacy.context').getContext()"
|
||||
|
||||
Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataProductProvider:
|
||||
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataProductProvider'
|
||||
arguments:
|
||||
- "@=service('prestashop.adapter.legacy.context').getContext()"
|
||||
|
||||
Oksydan\Module\IsThemeCore\Core\StructuredData\ProductStructuredData:
|
||||
class: 'Oksydan\Module\IsThemeCore\Core\StructuredData\ProductStructuredData'
|
||||
public: true
|
||||
arguments:
|
||||
- '@Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataProductProvider'
|
||||
- '@Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataProductPresenter'
|
||||
|
||||
Oksydan\Module\IsThemeCore\Core\Partytown\PartytownScript:
|
||||
class: Oksydan\Module\IsThemeCore\Core\Partytown\PartytownScript
|
||||
arguments:
|
||||
- "@Is_themecore"
|
||||
|
||||
Oksydan\Module\IsThemeCore\Core\Partytown\PartytownScriptUriResolver:
|
||||
class: Oksydan\Module\IsThemeCore\Core\Partytown\PartytownScriptUriResolver
|
||||
arguments:
|
||||
- "@=service('prestashop.adapter.legacy.context').getContext()"
|
||||
11
is_themecore/is_themecore/config/index.php
Normal file
11
is_themecore/is_themecore/config/index.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: ../');
|
||||
exit;
|
||||
30
is_themecore/is_themecore/config/routes.yml
Normal file
30
is_themecore/is_themecore/config/routes.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
is_themecore_module_settings:
|
||||
path: /is_themecore/settings
|
||||
methods: [GET]
|
||||
defaults:
|
||||
_controller: 'Oksydan\Module\IsThemeCore\Controller\Admin\SettingsController::indexAction'
|
||||
_legacy_controller: themecoreSettings
|
||||
|
||||
is_themecore_module_settings_general_save:
|
||||
path: /is_themecore/settings/general
|
||||
methods: [POST, PATCH]
|
||||
defaults:
|
||||
_controller: 'Oksydan\Module\IsThemeCore\Controller\Admin\SettingsController::processGeneralFormAction'
|
||||
_legacy_controller: themecoreSettings
|
||||
_legacy_link: themecoreSettings:update
|
||||
|
||||
is_themecore_module_settings_webp_save:
|
||||
path: /is_themecore/settings/webp
|
||||
methods: [POST, PATCH]
|
||||
defaults:
|
||||
_controller: 'Oksydan\Module\IsThemeCore\Controller\Admin\SettingsController::processWebpFormAction'
|
||||
_legacy_controller: themecoreSettings
|
||||
_legacy_link: themecoreSettings:update
|
||||
|
||||
is_themecore_module_settings_webp_erase_all:
|
||||
path: /is_themecore/settings/webp
|
||||
methods: [GET]
|
||||
defaults:
|
||||
_controller: 'Oksydan\Module\IsThemeCore\Controller\Admin\SettingsController::processWebpEraseImages'
|
||||
_legacy_controller: themecoreSettings
|
||||
_legacy_link: themecoreSettings:update
|
||||
11
is_themecore/is_themecore/config_nl.xml
Normal file
11
is_themecore/is_themecore/config_nl.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<module>
|
||||
<name>is_themecore</name>
|
||||
<displayName><![CDATA[Theme core module]]></displayName>
|
||||
<version><![CDATA[4.1.3]]></version>
|
||||
<description><![CDATA[Required for theme to work.]]></description>
|
||||
<author><![CDATA[Igor Stępień]]></author>
|
||||
<tab><![CDATA[others]]></tab>
|
||||
<is_configurable>1</is_configurable>
|
||||
<need_instance>1</need_instance>
|
||||
</module>
|
||||
35
is_themecore/is_themecore/controllers/front/ajaxTheme.php
Normal file
35
is_themecore/is_themecore/controllers/front/ajaxTheme.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Oksydan\Module\IsThemeCore\Core\ListingDisplay\ThemeListDisplay;
|
||||
|
||||
class Is_themecoreAjaxThemeModuleFrontController extends ModuleFrontController
|
||||
{
|
||||
public $displayType;
|
||||
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
|
||||
$this->displayType = Tools::getValue('displayType');
|
||||
}
|
||||
|
||||
public function initContent()
|
||||
{
|
||||
parent::initContent();
|
||||
|
||||
$themeDisplay = new ThemeListDisplay();
|
||||
|
||||
if ($this->displayType) {
|
||||
$themeDisplay->setDisplay($this->displayType);
|
||||
$this->ajaxRender(json_encode([
|
||||
'hasError' => false,
|
||||
'success' => true,
|
||||
]));
|
||||
} else {
|
||||
$this->ajaxRender(json_encode([
|
||||
'hasError' => true,
|
||||
'success' => false,
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
11
is_themecore/is_themecore/controllers/front/index.php
Normal file
11
is_themecore/is_themecore/controllers/front/index.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: ../');
|
||||
exit;
|
||||
11
is_themecore/is_themecore/controllers/index.php
Normal file
11
is_themecore/is_themecore/controllers/index.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: ../');
|
||||
exit;
|
||||
11
is_themecore/is_themecore/index.php
Normal file
11
is_themecore/is_themecore/index.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: ../');
|
||||
exit;
|
||||
212
is_themecore/is_themecore/is_themecore.php
Normal file
212
is_themecore/is_themecore/is_themecore.php
Normal file
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
if (file_exists(dirname(__FILE__) . '/vendor/autoload.php')) {
|
||||
require_once dirname(__FILE__) . '/vendor/autoload.php';
|
||||
}
|
||||
|
||||
use Oksydan\Module\IsThemeCore\Core\Partytown\FilesInstallation;
|
||||
use Oksydan\Module\IsThemeCore\Form\Settings\GeneralConfiguration;
|
||||
use Oksydan\Module\IsThemeCore\Form\Settings\WebpConfiguration;
|
||||
use Oksydan\Module\IsThemeCore\HookDispatcher;
|
||||
use PrestaShop\PrestaShop\Adapter\Configuration;
|
||||
use PrestaShop\PrestaShop\Adapter\SymfonyContainer;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
class Is_themecore extends Module
|
||||
{
|
||||
protected $hookDispatcher;
|
||||
|
||||
/**
|
||||
* @var array<string, string> Configuration values
|
||||
*/
|
||||
public const CONFIGURATION_VALUES = [
|
||||
GeneralConfiguration::THEMECORE_DISPLAY_LIST => 'grid',
|
||||
GeneralConfiguration::THEMECORE_EARLY_HINTS => false,
|
||||
GeneralConfiguration::THEMECORE_PRELOAD_CSS => false,
|
||||
GeneralConfiguration::THEMECORE_LOAD_PARTY_TOWN => false,
|
||||
GeneralConfiguration::THEMECORE_DEBUG_PARTY_TOWN => false,
|
||||
WebpConfiguration::THEMECORE_WEBP_ENABLED => false,
|
||||
WebpConfiguration::THEMECORE_WEBP_QUALITY => 90,
|
||||
WebpConfiguration::THEMECORE_WEBP_CONVERTER => null,
|
||||
WebpConfiguration::THEMECORE_WEBP_SHARPYUV => false,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string[] Hooks to register
|
||||
*/
|
||||
public const HOOKS = [
|
||||
'actionOutputHTMLBefore',
|
||||
'actionDispatcherBefore',
|
||||
'actionFrontControllerSetMedia',
|
||||
'displayListingStructuredData',
|
||||
'displayHeader',
|
||||
'actionProductSearchAfter',
|
||||
'actionHtaccessCreate',
|
||||
'objectShopUrlAddAfter',
|
||||
'objectShopUrlUpdateAfter',
|
||||
'objectShopUrlDeleteAfter',
|
||||
'actionFrontControllerInitBefore',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var Configuration<string, mixed> Configuration
|
||||
*/
|
||||
private $configuration;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->name = 'is_themecore';
|
||||
$this->tab = 'others';
|
||||
$this->version = '4.1.3';
|
||||
$this->author = 'Igor Stępień';
|
||||
$this->ps_versions_compliancy = ['min' => '8.0.0', 'max' => _PS_VERSION_];
|
||||
|
||||
$this->bootstrap = true;
|
||||
|
||||
parent::__construct();
|
||||
|
||||
$this->displayName = $this->trans(
|
||||
'Theme core module',
|
||||
[],
|
||||
'Modules.isthemecore.Admin'
|
||||
);
|
||||
$this->description = $this->trans(
|
||||
'Required for theme to work.',
|
||||
[],
|
||||
'Modules.isthemecore.Admin'
|
||||
);
|
||||
|
||||
$this->tabs = [
|
||||
[
|
||||
'name' => 'Theme core module settings',
|
||||
'class_name' => 'themecoreSettings',
|
||||
'route_name' => 'is_themecore_module_settings',
|
||||
'parent_class_name' => 'CONFIGURE',
|
||||
'visible' => false,
|
||||
'wording' => 'Theme core module settings',
|
||||
'wording_domain' => 'Modules.isthemecore.Admin',
|
||||
],
|
||||
];
|
||||
|
||||
$this->configuration = new Configuration();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isUsingNewTranslationSystem(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function install(): bool
|
||||
{
|
||||
return parent::install()
|
||||
&& $this->installConfiguration()
|
||||
&& $this->installPartytown()
|
||||
&& $this->registerHook(self::HOOKS)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install configuration values
|
||||
*/
|
||||
private function installConfiguration(): bool
|
||||
{
|
||||
try {
|
||||
foreach (self::CONFIGURATION_VALUES as $key => $default_value) {
|
||||
$this->configuration->set($key, $default_value);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install Partytown
|
||||
*/
|
||||
private function installPartytown(): bool
|
||||
{
|
||||
$installer = new FilesInstallation($this); // SERVICES NOT AVAILABLE DURING INSTALLATION
|
||||
|
||||
try {
|
||||
$installer->installFiles();
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function uninstall(): bool
|
||||
{
|
||||
return parent::uninstall()
|
||||
&& $this->uninstallConfiguration()
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall configuration values
|
||||
*/
|
||||
private function uninstallConfiguration(): bool
|
||||
{
|
||||
try {
|
||||
foreach (array_keys(self::CONFIGURATION_VALUES) as $key) {
|
||||
$this->configuration->remove($key);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get module configuration page content
|
||||
*/
|
||||
public function getContent(): void
|
||||
{
|
||||
$container = SymfonyContainer::getInstance();
|
||||
|
||||
if ($container != null) {
|
||||
/** @var UrlGeneratorInterface */
|
||||
$router = $container->get('router');
|
||||
|
||||
header('Location: https://houtendiershop.com'. $router->generate('is_themecore_module_settings'));
|
||||
exit;
|
||||
|
||||
/*Tools::redirectAdmin($router->generate('is_themecore_module_settings'));
|
||||
die();*/
|
||||
}
|
||||
}
|
||||
|
||||
public function __call(string $methodName, array $arguments)
|
||||
{
|
||||
return $this
|
||||
->getHookDispatcher()
|
||||
->dispatch($methodName, $arguments[0] ?? []);
|
||||
}
|
||||
|
||||
protected function getHookDispatcher(): HookDispatcher
|
||||
{
|
||||
if (!isset($this->hookDispatcher)) {
|
||||
if (!class_exists(HookDispatcher::class)) {
|
||||
require_once dirname(__FILE__) . '/vendor/autoload.php';
|
||||
}
|
||||
|
||||
$this->hookDispatcher = new HookDispatcher($this);
|
||||
}
|
||||
|
||||
return $this->hookDispatcher;
|
||||
}
|
||||
}
|
||||
BIN
is_themecore/is_themecore/logo.png
Normal file
BIN
is_themecore/is_themecore/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
1
is_themecore/is_themecore/public/partytown.js
Normal file
1
is_themecore/is_themecore/public/partytown.js
Normal file
@@ -0,0 +1 @@
|
||||
(()=>{"use strict";const t=((t,e)=>{const{forward:n=[],...i}={},o=JSON.stringify(i,((t,e)=>("function"==typeof e&&(e=String(e)).startsWith(t+"(")&&(e="function "+e),e)));return["!(function(w,p,f,c){",Object.keys(i).length>0?`c=w[p]=Object.assign(w[p]||{},${o});`:"c=w[p]=w[p]||{};","c[f]=(c[f]||[])",n.length>0?`.concat(${JSON.stringify(n)})`:"","})(window,'partytown','forward');",'/* Partytown 0.8.0 - MIT builder.io */\n!function(t,e,n,i,r,o,a,d,s,c,l,p){function u(){p||(p=1,"/"==(a=(o.lib||"/~partytown/")+(o.debug?"debug/":""))[0]&&(s=e.querySelectorAll(\'script[type="text/partytown"]\'),i!=t?i.dispatchEvent(new CustomEvent("pt1",{detail:t})):(d=setTimeout(f,1e4),e.addEventListener("pt0",w),r?h(1):n.serviceWorker?n.serviceWorker.register(a+(o.swPath||"partytown-sw.js"),{scope:a}).then((function(t){t.active?h():t.installing&&t.installing.addEventListener("statechange",(function(t){"activated"==t.target.state&&h()}))}),console.error):f())))}function h(t){c=e.createElement(t?"script":"iframe"),t||(c.setAttribute("style","display:block;width:0;height:0;border:0;visibility:hidden"),c.setAttribute("aria-hidden",!0)),c.src=a+"partytown-"+(t?"atomics.js?v=0.8.0":"sandbox-sw.html?"+Date.now()),e.querySelector(o.sandboxParent||"body").appendChild(c)}function f(n,r){for(w(),i==t&&(o.forward||[]).map((function(e){delete t[e.split(".")[0]]})),n=0;n<s.length;n++)(r=e.createElement("script")).innerHTML=s[n].innerHTML,e.head.appendChild(r);c&&c.parentNode.removeChild(c)}function w(){clearTimeout(d)}o=t.partytown||{},i==t&&(o.forward||[]).map((function(e){l=t,e.split(".").map((function(e,n,i){l=l[i[n]]=n+1<i.length?"push"==i[n+1]?[]:l[i[n]]||{}:function(){(t._ptf=t._ptf||[]).push(i,arguments)}}))})),"complete"==e.readyState?u():(t.addEventListener("DOMContentLoaded",u),t.addEventListener("load",u))}(window,document,navigator,top,window.crossOriginIsolated);'].join("")})();document.addEventListener("DOMContentLoaded",(()=>{const e=document.createElement("script");e.textContent=t,document.body.append(e)}))})();
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
const resolves=new Map,swMessageError=(e,s)=>({$msgId$:e.$msgId$,$error$:s}),httpRequestFromWebWorker=e=>new Promise((async s=>{const t=await e.clone().json(),o=await(e=>new Promise((async s=>{const t=[...await self.clients.matchAll()].sort(((e,s)=>e.url>s.url?-1:e.url<s.url?1:0))[0];if(t){const o=[s,setTimeout((()=>{resolves.delete(e.$msgId$),s(swMessageError(e,"Timeout"))}),12e4)];resolves.set(e.$msgId$,o),t.postMessage(e)}else s(swMessageError(e,"NoParty"))})))(t);s(response(JSON.stringify(o),"application/json"))})),response=(e,s)=>new Response(e,{headers:{"content-type":s||"text/html","Cache-Control":"no-store"}});self.oninstall=()=>self.skipWaiting(),self.onactivate=()=>self.clients.claim(),self.onmessage=e=>{const s=e.data,t=resolves.get(s.$msgId$);t&&(resolves.delete(s.$msgId$),clearTimeout(t[1]),t[0](s))},self.onfetch=e=>{const s=e.request,t=new URL(s.url).pathname;t.endsWith("sw.html")?e.respondWith(response('<!DOCTYPE html><html><head><meta charset="utf-8"><script src="./partytown-sandbox-sw.js?v=0.8.0"><\/script></head></html>')):t.endsWith("proxytown")&&e.respondWith(httpRequestFromWebWorker(s))};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
!function(t,e,n,i,r,o,a,s,d,c,l,p){function u(){p||(p=1,"/"==(a=(o.lib||"/~partytown/")+(!1!==o.debug?"debug/":""))[0]?(d=e.querySelectorAll('script[type="text/partytown"]'),i!=t?i.dispatchEvent(new CustomEvent("pt1",{detail:t})):(s=setTimeout(f,1e4),e.addEventListener("pt0",h),r?w(1):n.serviceWorker?n.serviceWorker.register(a+(o.swPath||"partytown-sw.js"),{scope:a}).then((function(t){t.active?w():t.installing?t.installing.addEventListener("statechange",(function(t){"activated"==t.target.state&&w()})):console.warn(t)}),console.error):f())):console.warn('Partytown config.lib url must start with "/"'))}function w(t){c=e.createElement(t?"script":"iframe"),t||(c.setAttribute("style","display:block;width:0;height:0;border:0;visibility:hidden"),c.setAttribute("aria-hidden",!0)),c.src=a+"partytown-"+(t?"atomics.js?v=0.8.0":"sandbox-sw.html?"+Date.now()),e.querySelector(o.sandboxParent||"body").appendChild(c)}function f(n,r){for(console.warn("Partytown script fallback"),h(),i==t&&(o.forward||[]).map((function(e){delete t[e.split(".")[0]]})),n=0;n<d.length;n++)(r=e.createElement("script")).innerHTML=d[n].innerHTML,e.head.appendChild(r);c&&c.parentNode.removeChild(c)}function h(){clearTimeout(s)}o=t.partytown||{},i==t&&(o.forward||[]).map((function(e){l=t,e.split(".").map((function(e,n,i){l=l[i[n]]=n+1<i.length?"push"==i[n+1]?[]:l[i[n]]||{}:function(){(t._ptf=t._ptf||[]).push(i,arguments)}}))})),"complete"==e.readyState?u():(t.addEventListener("DOMContentLoaded",u),t.addEventListener("load",u))}(window,document,navigator,top,window.crossOriginIsolated);
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
is_themecore/is_themecore/public/~partytown/partytown.js
Normal file
1
is_themecore/is_themecore/public/~partytown/partytown.js
Normal file
@@ -0,0 +1 @@
|
||||
!function(t,e,n,i,r,o,a,d,s,c,l,p){function u(){p||(p=1,"/"==(a=(o.lib||"/~partytown/")+(o.debug?"debug/":""))[0]&&(s=e.querySelectorAll('script[type="text/partytown"]'),i!=t?i.dispatchEvent(new CustomEvent("pt1",{detail:t})):(d=setTimeout(f,1e4),e.addEventListener("pt0",w),r?h(1):n.serviceWorker?n.serviceWorker.register(a+(o.swPath||"partytown-sw.js"),{scope:a}).then((function(t){t.active?h():t.installing&&t.installing.addEventListener("statechange",(function(t){"activated"==t.target.state&&h()}))}),console.error):f())))}function h(t){c=e.createElement(t?"script":"iframe"),t||(c.setAttribute("style","display:block;width:0;height:0;border:0;visibility:hidden"),c.setAttribute("aria-hidden",!0)),c.src=a+"partytown-"+(t?"atomics.js?v=0.8.0":"sandbox-sw.html?"+Date.now()),e.querySelector(o.sandboxParent||"body").appendChild(c)}function f(n,r){for(w(),i==t&&(o.forward||[]).map((function(e){delete t[e.split(".")[0]]})),n=0;n<s.length;n++)(r=e.createElement("script")).innerHTML=s[n].innerHTML,e.head.appendChild(r);c&&c.parentNode.removeChild(c)}function w(){clearTimeout(d)}o=t.partytown||{},i==t&&(o.forward||[]).map((function(e){l=t,e.split(".").map((function(e,n,i){l=l[i[n]]=n+1<i.length?"push"==i[n+1]?[]:l[i[n]]||{}:function(){(t._ptf=t._ptf||[]).push(i,arguments)}}))})),"complete"==e.readyState?u():(t.addEventListener("DOMContentLoaded",u),t.addEventListener("load",u))}(window,document,navigator,top,window.crossOriginIsolated);
|
||||
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Controller\Admin;
|
||||
|
||||
use PrestaShop\PrestaShop\Core\Form\FormHandlerInterface;
|
||||
use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController;
|
||||
use PrestaShopBundle\Security\Annotation\AdminSecurity;
|
||||
use PrestaShopBundle\Security\Annotation\DemoRestricted;
|
||||
use PrestaShopBundle\Security\Annotation\ModuleActivated;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Class SettingsController
|
||||
*
|
||||
* @ModuleActivated(moduleName="is_themecore", redirectRoute="admin_module_manage")
|
||||
*/
|
||||
class SettingsController extends FrameworkBundleAdminController
|
||||
{
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function indexAction(Request $request): Response
|
||||
{
|
||||
$generalFormDataHandler = $this->getGeneralFormHandler();
|
||||
$webpFormDataHandler = $this->getWebpFormHandler();
|
||||
|
||||
/** @var FormInterface<string, mixed> $generalForm */
|
||||
$generalForm = $generalFormDataHandler->getForm();
|
||||
$webpForm = $webpFormDataHandler->getForm();
|
||||
|
||||
return $this->render('@Modules/is_themecore/views/templates/back/components/layouts/settings.html.twig', [
|
||||
'general_form' => $generalForm->createView(),
|
||||
'webp_form' => $webpForm->createView(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return RedirectResponse
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function processGeneralFormAction(Request $request)
|
||||
{
|
||||
return $this->processForm(
|
||||
$request,
|
||||
$this->getGeneralFormHandler(),
|
||||
'General'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return RedirectResponse
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function processWebpFormAction(Request $request)
|
||||
{
|
||||
return $this->processForm(
|
||||
$request,
|
||||
$this->getWebpFormHandler(),
|
||||
'Webp'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return RedirectResponse
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function processWebpEraseImages(Request $request)
|
||||
{
|
||||
$time_start = microtime(true);
|
||||
$eraser = $this->get('oksydan.module.is_themecore.core.webp.webp_files_eraser');
|
||||
|
||||
switch ($request->get('type')) {
|
||||
case 'all':
|
||||
$eraser->setQuery(\_PS_ROOT_DIR_);
|
||||
break;
|
||||
case 'product':
|
||||
$eraser->setQuery(\_PS_IMG_DIR_ . 'p/');
|
||||
break;
|
||||
case 'module':
|
||||
$eraser->setQuery(\_PS_MODULE_DIR_);
|
||||
break;
|
||||
case 'cms':
|
||||
$eraser->setQuery(\_PS_IMG_DIR_ . 'cms/');
|
||||
break;
|
||||
case 'themes':
|
||||
$eraser->setQuery(\_PS_ROOT_DIR_ . '/themes/');
|
||||
break;
|
||||
default:
|
||||
$eraser->setQuery(\_PS_ROOT_DIR_);
|
||||
break;
|
||||
}
|
||||
|
||||
$eraser->eraseFiles();
|
||||
|
||||
$time_end = microtime(true);
|
||||
$execution_time = round($time_end - $time_start, 2);
|
||||
|
||||
$this->addFlash('success', $this->trans('%1$s - webp images has been erased successfully in %2$ss', 'Modules.isthemecore.Admin', [$eraser->getFilesCount(), $execution_time]));
|
||||
|
||||
return $this->redirectToRoute('is_themecore_module_settings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Process form.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param FormHandlerInterface $formHandler
|
||||
* @param string $hookName
|
||||
*
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
private function processForm(Request $request, FormHandlerInterface $formHandler)
|
||||
{
|
||||
$form = $formHandler->getForm();
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted()) {
|
||||
if ($form->isValid()) {
|
||||
$data = $form->getData();
|
||||
$saveErrors = $formHandler->save($data);
|
||||
|
||||
if (!empty($data['webp_enabled'])) {
|
||||
$generator = $this->get('oksydan.module.is_themecore.core.htaccess.htaccess_generator');
|
||||
|
||||
$generator->generate((bool) $data['webp_enabled']);
|
||||
$generator->writeFile();
|
||||
}
|
||||
|
||||
if (0 === count($saveErrors)) {
|
||||
$this->addFlash('success', $this->trans('Successful update.', 'Admin.Notifications.Success'));
|
||||
} else {
|
||||
$this->flashErrors($saveErrors);
|
||||
}
|
||||
}
|
||||
|
||||
$formErrors = [];
|
||||
foreach ($form->getErrors(true) as $error) {
|
||||
$formErrors[] = $error->getMessage();
|
||||
}
|
||||
$this->flashErrors($formErrors);
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('is_themecore_module_settings');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FormHandlerInterface
|
||||
*/
|
||||
private function getGeneralFormHandler()
|
||||
{
|
||||
/** @var FormHandlerInterface */
|
||||
$formDataHandler = $this->get('oksydan.module.is_themecore.form.settings.general_form_data_handler');
|
||||
|
||||
return $formDataHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FormHandlerInterface
|
||||
*/
|
||||
private function getWebpFormHandler()
|
||||
{
|
||||
/** @var FormHandlerInterface */
|
||||
$formDataHandler = $this->get('oksydan.module.is_themecore.form.settings.webp_form_data_handler');
|
||||
|
||||
return $formDataHandler;
|
||||
}
|
||||
}
|
||||
11
is_themecore/is_themecore/src/Controller/Admin/index.php
Normal file
11
is_themecore/is_themecore/src/Controller/Admin/index.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: ../');
|
||||
exit;
|
||||
11
is_themecore/is_themecore/src/Controller/Front/index.php
Normal file
11
is_themecore/is_themecore/src/Controller/Front/index.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: ../');
|
||||
exit;
|
||||
11
is_themecore/is_themecore/src/Controller/index.php
Normal file
11
is_themecore/is_themecore/src/Controller/index.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: ../');
|
||||
exit;
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\Breadcrumbs;
|
||||
|
||||
class ThemeBreadcrumbs
|
||||
{
|
||||
protected $breadcrumbs = [];
|
||||
public $pageName;
|
||||
public $translator;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->context = \Context::getContext();
|
||||
$this->pageName = $this->context->controller->getPageName();
|
||||
$this->getAvailableBreadcrumbs();
|
||||
}
|
||||
|
||||
protected function getAvailableBreadcrumbs()
|
||||
{
|
||||
$this->breadcrumbs = $this->getCommonBreadcrumbs();
|
||||
}
|
||||
|
||||
public function getBreadcrumb()
|
||||
{
|
||||
$breadcrumb = [];
|
||||
$breadcrumb['links'] = $this->getBreadcrumbByPageName();
|
||||
$breadcrumb['count'] = count($breadcrumb['links']);
|
||||
|
||||
return $breadcrumb;
|
||||
}
|
||||
|
||||
public function getBreadcrumbByPageName()
|
||||
{
|
||||
$breadcrumb = [];
|
||||
|
||||
if (isset($this->breadcrumbs[$this->pageName])) {
|
||||
$breadcrumb = $this->breadcrumbs[$this->pageName];
|
||||
}
|
||||
|
||||
return $breadcrumb;
|
||||
}
|
||||
|
||||
protected function getCommonBreadcrumbs()
|
||||
{
|
||||
$pages = [
|
||||
[
|
||||
'controller' => 'cart',
|
||||
'name' => $this->context->getTranslator()->trans('Shopping Cart', [], 'Shop.Theme.Checkout'),
|
||||
],
|
||||
[
|
||||
'controller' => 'pagenotfound',
|
||||
'name' => $this->context->getTranslator()->trans('404', [], 'Shop.Theme.Global'),
|
||||
],
|
||||
[
|
||||
'controller' => 'stores',
|
||||
'name' => $this->context->getTranslator()->trans('Our stores', [], 'Shop.Theme.Global'),
|
||||
],
|
||||
[
|
||||
'controller' => 'sitemap',
|
||||
'name' => $this->context->getTranslator()->trans('Sitemap', [], 'Shop.Theme.Global'),
|
||||
],
|
||||
];
|
||||
|
||||
$breadcrumbs = [];
|
||||
foreach ($pages as $page) {
|
||||
$breadcrumbs[$page['controller']] = [
|
||||
[
|
||||
'url' => $this->context->link->getPageLink('index'),
|
||||
'title' => $this->context->getTranslator()->trans('Home', [], 'Shop.Theme.Global'),
|
||||
],
|
||||
[
|
||||
'url' => $this->context->link->getPageLink($page['controller']),
|
||||
'title' => $page['name'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return $breadcrumbs;
|
||||
}
|
||||
}
|
||||
11
is_themecore/is_themecore/src/Core/Breadcrumbs/index.php
Normal file
11
is_themecore/is_themecore/src/Core/Breadcrumbs/index.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: ../');
|
||||
exit;
|
||||
@@ -0,0 +1,233 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\Htaccess;
|
||||
|
||||
class HtaccessGenerator
|
||||
{
|
||||
private $module;
|
||||
private $domains = [];
|
||||
private $medias = [];
|
||||
|
||||
protected $moduleWebpGeneratorFile;
|
||||
protected $mediaDomains = null;
|
||||
protected $tempContent = '';
|
||||
protected $contentBefore = '';
|
||||
protected $contentAfter = '';
|
||||
|
||||
protected $wrapperBlockComments = [
|
||||
'COMMENT_START' => '~~start-is_themecore~~',
|
||||
'COMMENT_END' => '~~end-is_themecore~~',
|
||||
];
|
||||
|
||||
public function __construct(\Is_themecore $module)
|
||||
{
|
||||
$this->domains = \Tools::getDomains();
|
||||
$this->module = $module;
|
||||
$this->moduleWebpGeneratorFile = "%{ENV:REWRITEBASE}modules/{$this->module->name}/webp.php";
|
||||
}
|
||||
|
||||
public function generate($addRewrite = true): void
|
||||
{
|
||||
$htaccessFile = $this->getHtaccessFilePath();
|
||||
|
||||
if (file_exists($htaccessFile)) {
|
||||
$content = \Tools::file_get_contents($htaccessFile);
|
||||
|
||||
if (preg_match('#^(.*)\# ' . $this->wrapperBlockComments['COMMENT_START'] . '.*\# ' . $this->wrapperBlockComments['COMMENT_END'] . '[^\n]*(.*)$#s', $content, $match)) {
|
||||
$this->contentBefore = $match[1];
|
||||
$this->contentAfter = $match[2];
|
||||
} else {
|
||||
$this->contentAfter = $content;
|
||||
}
|
||||
}
|
||||
|
||||
if ($addRewrite) {
|
||||
$this->generateHtaccessHeader();
|
||||
|
||||
$this->write('<IfModule mod_rewrite.c>');
|
||||
$this->write('RewriteEngine On');
|
||||
$this->writeNl();
|
||||
$this->generateImagesRewrites();
|
||||
$this->write('</IfModule>');
|
||||
|
||||
$this->write("# {$this->wrapperBlockComments['COMMENT_END']} Do not remove this comment");
|
||||
}
|
||||
|
||||
$this->write($this->contentAfter);
|
||||
}
|
||||
|
||||
public function writeFile(): bool
|
||||
{
|
||||
$htaccessFile = $this->getHtaccessFilePath();
|
||||
|
||||
if (!$writeToFile = @fopen($htaccessFile, 'wb')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!fwrite($writeToFile, $this->tempContent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose($writeToFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function getMediaDomains(): string
|
||||
{
|
||||
if ($this->mediaDomains === null) {
|
||||
if (\Configuration::getMultiShopValues('PS_MEDIA_SERVER_1')
|
||||
&& \Configuration::getMultiShopValues('PS_MEDIA_SERVER_2')
|
||||
&& \Configuration::getMultiShopValues('PS_MEDIA_SERVER_3')
|
||||
) {
|
||||
$this->medias = [
|
||||
\Configuration::getMultiShopValues('PS_MEDIA_SERVER_1'),
|
||||
\Configuration::getMultiShopValues('PS_MEDIA_SERVER_2'),
|
||||
\Configuration::getMultiShopValues('PS_MEDIA_SERVER_3'),
|
||||
];
|
||||
}
|
||||
|
||||
$this->mediaDomains = '';
|
||||
|
||||
foreach ($this->medias as $media) {
|
||||
foreach ($media as $mediaUrl) {
|
||||
if ($mediaUrl) {
|
||||
$this->mediaDomains .= 'RewriteCond %{HTTP_HOST} ^' . $mediaUrl . '$ [OR]' . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->mediaDomains;
|
||||
}
|
||||
|
||||
protected function getDomainRewriteCond($domain): string
|
||||
{
|
||||
return "RewriteCond %{HTTP_HOST} ^$domain$";
|
||||
}
|
||||
|
||||
protected function generateImagesRewrites(): void
|
||||
{
|
||||
foreach ($this->domains as $domain => $uri) {
|
||||
$this->generateProductImagesRewrite($domain);
|
||||
$this->generateCategoryImagesRewrite($domain);
|
||||
$this->generateOtherImagesRewrite($domain);
|
||||
}
|
||||
}
|
||||
|
||||
protected function writeMediaDomainsCondition()
|
||||
{
|
||||
$mediaDomains = $this->getMediaDomains();
|
||||
|
||||
if ($mediaDomains) {
|
||||
$this->write($mediaDomains, false);
|
||||
}
|
||||
}
|
||||
|
||||
protected function generateProductImagesRewrite($domain): void
|
||||
{
|
||||
$domainRewriteCond = $this->getDomainRewriteCond($domain);
|
||||
|
||||
for ($i = 1; $i <= 7; ++$i) {
|
||||
$imgPath = $imgName = '';
|
||||
for ($j = 1; $j <= $i; ++$j) {
|
||||
$imgPath .= '$' . $j . '/';
|
||||
$imgName .= '$' . $j;
|
||||
}
|
||||
$imgName .= '$' . $j;
|
||||
|
||||
// WEBP FILE EXISTS
|
||||
$this->writeMediaDomainsCondition();
|
||||
$this->write($domainRewriteCond);
|
||||
$this->write('RewriteCond %{DOCUMENT_ROOT}/img/p/' . $imgPath . $imgName . '$' . ($j + 1) . '.webp -f');
|
||||
$this->write('RewriteRule ^' . str_repeat('([0-9])', $i) . '(\-[_a-zA-Z0-9-]*)?(-[0-9]+)?/.+\.webp$ %{ENV:REWRITEBASE}img/p/' . $imgPath . $imgName . '$' . ($j + 1) . '.webp [L]');
|
||||
$this->writeNl();
|
||||
|
||||
// WEBP FILE NOT EXISTS
|
||||
$this->writeMediaDomainsCondition();
|
||||
$this->write($domainRewriteCond);
|
||||
$this->write('RewriteCond %{DOCUMENT_ROOT}/img/p/' . $imgPath . $imgName . '$' . ($j + 1) . '.webp !-f');
|
||||
$this->write('RewriteRule ^' . str_repeat('([0-9])', $i) . '(\-[_a-zA-Z0-9-]*)?(-[0-9]+)?/.+\.webp$ ' . $this->moduleWebpGeneratorFile . '?source=%{DOCUMENT_ROOT}/img/p/' . $imgPath . $imgName . '$' . ($j + 1) . '.webp [NC,L]');
|
||||
$this->writeNl();
|
||||
}
|
||||
}
|
||||
|
||||
protected function generateCategoryImagesRewrite($domain): void
|
||||
{
|
||||
$domainRewriteCond = $this->getDomainRewriteCond($domain);
|
||||
|
||||
// WEBP FILE EXISTS
|
||||
$this->writeMediaDomainsCondition();
|
||||
$this->write($domainRewriteCond);
|
||||
$this->write('RewriteCond %{DOCUMENT_ROOT}/img/c/$1$2.webp -f');
|
||||
$this->write('RewriteRule ^c/([0-9]+)(\-[\.*_a-zA-Z0-9-]*)(-[0-9]+)?/.+\.webp$ %{ENV:REWRITEBASE}img/c/$1$2.webp [L]');
|
||||
$this->writeNl();
|
||||
|
||||
$this->writeMediaDomainsCondition();
|
||||
$this->write($domainRewriteCond);
|
||||
$this->write('RewriteCond %{DOCUMENT_ROOT}/img/c/$1$2$3.webp -f');
|
||||
$this->write('RewriteRule ^c/([0-9]+)(\-[\.*_a-zA-Z0-9-]*)(-[0-9]+)?/.+\.webp$ %{ENV:REWRITEBASE}img/c/$1$2$3.webp [L]');
|
||||
$this->writeNl();
|
||||
|
||||
// WEBP FILE NOT EXISTS
|
||||
$this->writeMediaDomainsCondition();
|
||||
$this->write($domainRewriteCond);
|
||||
$this->write('RewriteCond %{DOCUMENT_ROOT}/img/c/$1$2.webp !-f');
|
||||
$this->write('RewriteRule ^c/([0-9]+)(\-[\.*_a-zA-Z0-9-]*)(-[0-9]+)?/.+\.webp$ ' . $this->moduleWebpGeneratorFile . '?source=%{DOCUMENT_ROOT}/img/c/$1$2.webp [NC,L]');
|
||||
$this->writeNl();
|
||||
|
||||
$this->writeMediaDomainsCondition();
|
||||
$this->write($domainRewriteCond);
|
||||
$this->write('RewriteCond %{DOCUMENT_ROOT}/img/c/$1$2$3.webp !-f');
|
||||
$this->write('RewriteRule ^c/([0-9]+)(\-[\.*_a-zA-Z0-9-]*)(-[0-9]+)?/.+\.webp$ ' . $this->moduleWebpGeneratorFile . '?source=%{DOCUMENT_ROOT}/img/c/$1$2$3.webp [NC,L]');
|
||||
$this->writeNl();
|
||||
}
|
||||
|
||||
protected function generateOtherImagesRewrite($domain): void
|
||||
{
|
||||
$domainRewriteCond = $this->getDomainRewriteCond($domain);
|
||||
|
||||
// WEBP FILE NOT EXISTS
|
||||
$this->writeMediaDomainsCondition();
|
||||
$this->write($domainRewriteCond);
|
||||
$this->write('RewriteCond %{REQUEST_FILENAME} !-f');
|
||||
$this->write('RewriteRule ^(.*)\.webp$ ' . $this->moduleWebpGeneratorFile . '?source=%{DOCUMENT_ROOT}/$1.webp [NC,L]');
|
||||
$this->writeNl();
|
||||
}
|
||||
|
||||
protected function generateHtaccessHeader(): void
|
||||
{
|
||||
$this->write("# {$this->wrapperBlockComments['COMMENT_START']} Do not remove this comment");
|
||||
$this->write('# Allow webp files to be sent by Apache 2.2');
|
||||
$this->write('<IfModule !mod_authz_core.c>');
|
||||
$this->write('<Files ~ "\\.(webp)$">', true, 1);
|
||||
$this->write('Allow from all', true, 2);
|
||||
$this->write('</Files>', true, 1);
|
||||
$this->write('</IfModule>');
|
||||
$this->writeNl();
|
||||
|
||||
$this->write('# Allow webp files to be sent by Apache 2.4');
|
||||
$this->write('<IfModule mod_authz_core.c>');
|
||||
$this->write('<Files ~ "\\.(webp)$">', true, 1);
|
||||
$this->write('Require all granted', true, 2);
|
||||
$this->write('allow from all', true, 2);
|
||||
$this->write('</Files>', true, 1);
|
||||
$this->write('</IfModule>');
|
||||
$this->writeNl();
|
||||
}
|
||||
|
||||
protected function write($line, $addEOL = true, $tabs = 0): void
|
||||
{
|
||||
$this->tempContent .= str_repeat("\t", $tabs) . $line . ($addEOL ? PHP_EOL : '');
|
||||
}
|
||||
|
||||
protected function writeNl(): void
|
||||
{
|
||||
$this->write('');
|
||||
}
|
||||
|
||||
protected function getHtaccessFilePath(): string
|
||||
{
|
||||
return _PS_ROOT_DIR_ . '/.htaccess';
|
||||
}
|
||||
}
|
||||
11
is_themecore/is_themecore/src/Core/Htaccess/index.php
Normal file
11
is_themecore/is_themecore/src/Core/Htaccess/index.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: ../');
|
||||
exit;
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\ListingDisplay;
|
||||
|
||||
use Oksydan\Module\IsThemeCore\Form\Settings\GeneralConfiguration;
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class ThemeListDisplay
|
||||
{
|
||||
private $cookieName = 'listingDisplayType';
|
||||
private $displayList = [
|
||||
'grid',
|
||||
'list',
|
||||
];
|
||||
|
||||
protected function getRequest(): Request
|
||||
{
|
||||
Request::setFactory(static function ($query, $request, $attributes, $cookies, $files, $server, $content) {
|
||||
return new Request($query, $request, $attributes, $cookies, [], $server, $content);
|
||||
});
|
||||
|
||||
return Request::createFromGlobals();
|
||||
}
|
||||
|
||||
public function setDisplay($display): Response
|
||||
{
|
||||
if (!in_array($display, $this->displayList)) {
|
||||
$display = \Configuration::get(GeneralConfiguration::THEMECORE_DISPLAY_LIST);
|
||||
}
|
||||
|
||||
$response = new Response();
|
||||
|
||||
$response->headers->setCookie(new Cookie(
|
||||
$this->cookieName,
|
||||
$display,
|
||||
(new \DateTime('now'))->modify('+ 30 days')->getTimestamp(),
|
||||
'/'
|
||||
));
|
||||
|
||||
return $response->sendHeaders();
|
||||
}
|
||||
|
||||
public function getDisplay()
|
||||
{
|
||||
$displayFromCookie = $this->getRequest()->cookies->get($this->cookieName);
|
||||
|
||||
if ($displayFromCookie) {
|
||||
return $displayFromCookie;
|
||||
}
|
||||
|
||||
return \Configuration::get(GeneralConfiguration::THEMECORE_DISPLAY_LIST);
|
||||
}
|
||||
|
||||
public function getDisplayOptions()
|
||||
{
|
||||
return $this->displayList;
|
||||
}
|
||||
}
|
||||
11
is_themecore/is_themecore/src/Core/ListingDisplay/index.php
Normal file
11
is_themecore/is_themecore/src/Core/ListingDisplay/index.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: ../');
|
||||
exit;
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\Partytown;
|
||||
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
|
||||
class FilesInstallation
|
||||
{
|
||||
private \Is_themecore $module;
|
||||
|
||||
public function __construct(
|
||||
\Is_themecore $module
|
||||
) {
|
||||
$this->module = $module;
|
||||
}
|
||||
|
||||
public function installFiles(): void
|
||||
{
|
||||
$this->installPartytown();
|
||||
}
|
||||
|
||||
protected function getFileSystem(): Filesystem
|
||||
{
|
||||
return new Filesystem();
|
||||
}
|
||||
|
||||
private function installPartytown(): void
|
||||
{
|
||||
$source = _PS_MODULE_DIR_ . $this->module->name . '/public/~partytown';
|
||||
$destination = _PS_ROOT_DIR_ . '/~partytown';
|
||||
$fileSystem = $this->getFileSystem();
|
||||
|
||||
if (!file_exists($source)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (file_exists($destination)) {
|
||||
$fileSystem->remove($destination);
|
||||
}
|
||||
|
||||
$fileSystem->mkdir($destination);
|
||||
|
||||
$directoryIterator = new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS);
|
||||
$iterator = new \RecursiveIteratorIterator($directoryIterator, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
|
||||
foreach ($iterator as $item) {
|
||||
if ($item->isDir()) {
|
||||
$fileSystem->mkdir($destination . DIRECTORY_SEPARATOR . $iterator->getSubPathName());
|
||||
} else {
|
||||
$fileSystem->copy($item, $destination . DIRECTORY_SEPARATOR . $iterator->getSubPathName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\Partytown;
|
||||
|
||||
class PartytownScript
|
||||
{
|
||||
private \Is_themecore $module;
|
||||
|
||||
public function __construct(
|
||||
\Is_themecore $module
|
||||
) {
|
||||
$this->module = $module;
|
||||
}
|
||||
|
||||
public function getScriptPath(): string
|
||||
{
|
||||
return _PS_MODULE_DIR_ . $this->module->name . '/public/partytown.js';
|
||||
}
|
||||
|
||||
public function getScriptUri(): string
|
||||
{
|
||||
return $this->module->getPathUri() . '/public/partytown.js';
|
||||
}
|
||||
|
||||
public function getScriptContent(): string
|
||||
{
|
||||
$script = '';
|
||||
$filePath = $this->getScriptPath();
|
||||
|
||||
if (file_exists($filePath)) {
|
||||
$script = file_get_contents($filePath);
|
||||
}
|
||||
|
||||
return $script;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\Partytown;
|
||||
|
||||
class PartytownScriptUriResolver
|
||||
{
|
||||
private \Context $context;
|
||||
|
||||
const PUBLIC_PARTYTOWN_PATH = '~partytown/';
|
||||
|
||||
public function __construct(
|
||||
\Context $context
|
||||
) {
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function getScriptUri(): string
|
||||
{
|
||||
return $this->context->shop->physical_uri . self::PUBLIC_PARTYTOWN_PATH;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\Smarty;
|
||||
|
||||
use Oksydan\Module\IsThemeCore\Core\Webp\WebpPictureGenerator;
|
||||
use Oksydan\Module\IsThemeCore\Form\Settings\WebpConfiguration;
|
||||
use PrestaShop\PrestaShop\Core\Util\ColorBrightnessCalculator;
|
||||
|
||||
class SmartyHelperFunctions
|
||||
{
|
||||
public static function generateImagesSources($params)
|
||||
{
|
||||
$image = $params['image'];
|
||||
$size = $params['size'];
|
||||
$lazyLoad = isset($params['lazyload']) ? $params['lazyload'] : true;
|
||||
$attributes = [];
|
||||
$highDpiImagesEnabled = (bool) \Configuration::get('PS_HIGHT_DPI');
|
||||
|
||||
$srcAttributePrefix = $lazyLoad ? 'data-' : '';
|
||||
|
||||
$img = $image['bySize'][$size]['url'];
|
||||
|
||||
if ($highDpiImagesEnabled) {
|
||||
$size2x = $size . '2x';
|
||||
$img2x = str_replace($size, $size2x, $img);
|
||||
$attributeName = $srcAttributePrefix . 'srcset';
|
||||
$attributes[$attributeName] = "$img, $img2x 2x";
|
||||
} else {
|
||||
$attributeName = $srcAttributePrefix . 'src';
|
||||
$attributes[$attributeName] = $img;
|
||||
}
|
||||
|
||||
if ($lazyLoad) {
|
||||
$width = $image['bySize'][$size]['width'];
|
||||
$height = $image['bySize'][$size]['height'];
|
||||
$placeholderSrc = self::generateImageSvgPlaceholder(['width' => $width, 'height' => $height]);
|
||||
|
||||
$attributes['src'] = $placeholderSrc;
|
||||
}
|
||||
|
||||
$attributesToPrint = [];
|
||||
|
||||
foreach ($attributes as $attr => $value) {
|
||||
$attributesToPrint[] = $attr . '="' . $value . '"';
|
||||
}
|
||||
|
||||
return implode(PHP_EOL, $attributesToPrint);
|
||||
}
|
||||
|
||||
public static function generateImageSvgPlaceholder($params)
|
||||
{
|
||||
$width = $params['width'];
|
||||
$height = $params['height'];
|
||||
|
||||
return "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='$width' height='$height' viewBox='0 0 1 1'%3E%3C/svg%3E";
|
||||
}
|
||||
|
||||
public static function appendParamToUrl($params)
|
||||
{
|
||||
list(
|
||||
'url' => $url,
|
||||
'key' => $key,
|
||||
'value' => $value
|
||||
) = $params;
|
||||
|
||||
$replace = false;
|
||||
|
||||
if (isset($params['replace'])) {
|
||||
$replace = $params['replace'];
|
||||
}
|
||||
|
||||
if (!is_array($value)) {
|
||||
$value = [$value];
|
||||
} else {
|
||||
$replace = false;
|
||||
}
|
||||
|
||||
foreach ($value as $qValue) {
|
||||
$query = parse_url($url, PHP_URL_QUERY);
|
||||
|
||||
if ($query) {
|
||||
if ($replace) {
|
||||
parse_str($query, $queryParams);
|
||||
$queryParams[$key] = $qValue;
|
||||
$url = str_replace("?$query", '?' . http_build_query($queryParams), $url);
|
||||
} else {
|
||||
$queryParams = [];
|
||||
$queryParams[$key] = $qValue;
|
||||
$url .= '&' . http_build_query($queryParams);
|
||||
}
|
||||
} else {
|
||||
$url .= '?' . urlencode($key) . '=' . urlencode($qValue);
|
||||
}
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
public static function imagesBlock($params, $content, $smarty)
|
||||
{
|
||||
$webpEnabled = isset($params['webpEnabled']) ? $params['webpEnabled'] : \Configuration::get(WebpConfiguration::THEMECORE_WEBP_ENABLED);
|
||||
|
||||
if ($webpEnabled && !empty($content)) {
|
||||
$pictureGenerator = new WebpPictureGenerator($content);
|
||||
|
||||
$pictureGenerator
|
||||
->loadContent()
|
||||
->generatePictureTags();
|
||||
|
||||
return $pictureGenerator->getContent();
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
public static function displayMobileBlock($params, $content, $smarty)
|
||||
{
|
||||
if (!empty($content) && \Context::getContext()->isMobile()) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public static function displayDesktopBlock($params, $content, $smarty)
|
||||
{
|
||||
if (!empty($content) && !\Context::getContext()->isMobile()) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public static function cmsImagesBlock($params, $content, $smarty)
|
||||
{
|
||||
$doc = new \DOMDocument();
|
||||
$doc->loadHTML('<meta http-equiv="Content-Type" content="charset=utf-8">' . $content);
|
||||
$context = \Context::getContext();
|
||||
|
||||
$images = $doc->getElementsByTagName('img');
|
||||
|
||||
$domains = \Tools::getDomains();
|
||||
$medias = [
|
||||
\Configuration::get('PS_MEDIA_SERVER_1'),
|
||||
\Configuration::get('PS_MEDIA_SERVER_2'),
|
||||
\Configuration::get('PS_MEDIA_SERVER_3'),
|
||||
];
|
||||
|
||||
$internalUrls = [];
|
||||
|
||||
foreach ($domains as $domain => $options) {
|
||||
$internalUrls[] = $domain;
|
||||
}
|
||||
|
||||
foreach ($medias as $media) {
|
||||
if ($media) {
|
||||
$internalUrls[] = $media;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($images as $image) {
|
||||
$newImg = $doc->createElement('img');
|
||||
$src = urldecode($image->attributes->getNamedItem('src')->nodeValue);
|
||||
|
||||
if (!preg_match('/' . implode('|', $internalUrls) . '/i', $src)) {
|
||||
$newImg->setAttribute('data-external-url', '');
|
||||
}
|
||||
|
||||
foreach ($image->attributes as $attribute) {
|
||||
$newImg->setAttribute($attribute->nodeName, $attribute->nodeValue);
|
||||
}
|
||||
|
||||
$image->parentNode->replaceChild($newImg, $image);
|
||||
}
|
||||
|
||||
$content = $doc->saveHTML();
|
||||
$content = str_replace('<meta http-equiv="Content-Type" content="charset=utf-8">', '', $content);
|
||||
|
||||
$webpEnabled = isset($params['webpEnabled']) ? $params['webpEnabled'] : \Configuration::get(WebpConfiguration::THEMECORE_WEBP_ENABLED);
|
||||
|
||||
if ($webpEnabled && !empty($content)) {
|
||||
$pictureGenerator = new WebpPictureGenerator($content);
|
||||
|
||||
$pictureGenerator
|
||||
->loadContent()
|
||||
->generatePictureTags();
|
||||
|
||||
return $pictureGenerator->getContent();
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* CUSTOM ADDITIONS BELOW */
|
||||
|
||||
|
||||
public static function isBright($hexColor)
|
||||
{
|
||||
$calculator = new ColorBrightnessCalculator();
|
||||
return $calculator->isBright($hexColor);
|
||||
}
|
||||
|
||||
public static function displaySvgIcon($params){
|
||||
/*
|
||||
Helper to display SVG icons stored in themes/falcon/_dev/img/icons/
|
||||
registered under public_html/modules/is_themecore/src/Hook/Smarty.php
|
||||
|
||||
Usage examples in a TPL file:
|
||||
|
||||
{svg_icon file='person.svg' fill='red' width='24' height='24'}
|
||||
{svg_icon file='person.svg'}
|
||||
*/
|
||||
|
||||
// Grab svg under falcon/_dev/img and display an error if not found
|
||||
if (empty($params['file'])) {
|
||||
return '';
|
||||
}
|
||||
$icon_path = _PS_THEME_DIR_ . '/_dev/img/' . basename($params['file']);
|
||||
|
||||
if (!is_file($icon_path)) {
|
||||
if (defined('_PS_MODE_DEV_') && _PS_MODE_DEV_) {
|
||||
return 'Invalid SVG path: ' . htmlspecialchars($icon_path);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
$svg_content = file_get_contents($icon_path);
|
||||
|
||||
/* Define an associative array of SVG properties
|
||||
If the property is a style, addd it in a style tag.
|
||||
If the property is an attribute, replace it in the svg code.
|
||||
|
||||
This reduces the risk of interfering with other attributes in the svg's code that can mess up the whole image. */
|
||||
$svg_props = array(
|
||||
'width' => 'style',
|
||||
'height' => 'style',
|
||||
'opacity' => 'style',
|
||||
'fill' => 'attribute',
|
||||
'stroke' => 'attribute',
|
||||
);
|
||||
|
||||
// Styles
|
||||
$styles = [];
|
||||
foreach ($svg_props as $prop => $type) {
|
||||
if ($type === 'style' && !empty($params[$prop])) {
|
||||
$value = $params[$prop];
|
||||
if (in_array($prop, ['width', 'height']) && is_numeric($value)) {
|
||||
$value .= 'px';
|
||||
}
|
||||
$styles[] = $prop . ': ' . $value;
|
||||
}
|
||||
}
|
||||
if (!empty($styles)) {
|
||||
$style_attr = 'style="' . implode('; ', $styles) . '"';
|
||||
$svg_content = preg_replace('/(<svg[^>]*?)>/', '$1 ' . $style_attr . '>', $svg_content, 1);
|
||||
}
|
||||
|
||||
// Attributes
|
||||
foreach ($svg_props as $prop => $type) {
|
||||
if ($type === 'attribute' && !empty($params[$prop])) {
|
||||
$value = $params[$prop];
|
||||
// Use word boundaries to avoid matching partial names like fill-opacity or stroke-width
|
||||
// Replace ALL occurrences of the attribute
|
||||
$svg_content = preg_replace(
|
||||
'/\b' . preg_quote($prop) . '\b\s*=\s*"[^"]*"/',
|
||||
$prop . '="' . $value . '"',
|
||||
$svg_content
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $svg_content;
|
||||
}
|
||||
|
||||
|
||||
public static function displayPriceSplit($price, $show_currency = false) {
|
||||
/* Helper to split the price into euros and cents to make it more stylish
|
||||
registered under public_html/modules/is_themecore/src/Hook/Smarty.php
|
||||
|
||||
Usage examples in a TPL file:
|
||||
{price_split price=$product.price}
|
||||
|
||||
*/
|
||||
//Convert to a string and only keep numbers, commas and dots
|
||||
$currency_name = \Context::getContext()->currency->name;
|
||||
$currency_symbol = \Context::getContext()->currency->symbol;
|
||||
$price_string = implode(',', $price);
|
||||
$price_string = preg_replace('/[^0-9,.]/', '', $price_string);
|
||||
|
||||
// Split by comma and wrap each part in a span
|
||||
$price_parts = explode(',', $price_string);
|
||||
if (count($price_parts) >= 2) {
|
||||
echo '<span class="currency" aria-label="' . htmlspecialchars($currency_name) . '">' . ($show_currency ? $currency_symbol : '') . $price_parts[0] . '</span>';
|
||||
echo '<sup class="cents" aria-label="cents">' . $price_parts[1] . '</sup>';
|
||||
} else {
|
||||
foreach ($price_parts as $part) {
|
||||
echo '<span>' . $part . '</span>';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\StructuredData;
|
||||
|
||||
use Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter\StructuredDataPresenterInterface;
|
||||
use Oksydan\Module\IsThemeCore\Core\StructuredData\Provider\StructuredDataProviderInterface;
|
||||
|
||||
abstract class AbstractStructuredData
|
||||
{
|
||||
private StructuredDataProviderInterface $provider;
|
||||
private StructuredDataPresenterInterface $presenter;
|
||||
|
||||
public function __construct(
|
||||
StructuredDataProviderInterface $provider,
|
||||
StructuredDataPresenterInterface $presenter
|
||||
) {
|
||||
$this->provider = $provider;
|
||||
$this->presenter = $presenter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return formatted json data
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFormattedData(): string
|
||||
{
|
||||
$data = $this->provider->getData();
|
||||
|
||||
$jsonData = $this->presenter->present($data);
|
||||
|
||||
\Hook::exec('actionStructuredData' . ucfirst($this->getStructuredDataType()),
|
||||
[
|
||||
'jsonData' => &$jsonData,
|
||||
'rawData' => $data,
|
||||
]
|
||||
);
|
||||
|
||||
if (empty($jsonData)) {
|
||||
return '';
|
||||
} else {
|
||||
return json_encode($jsonData, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\StructuredData;
|
||||
|
||||
class BreadcrumbStructuredData extends AbstractStructuredData implements StructuredDataInterface
|
||||
{
|
||||
public function getStructuredDataType(): string
|
||||
{
|
||||
return 'breadcrumb';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter;
|
||||
|
||||
class StructuredDataBreadcrumbPresenter implements StructuredDataPresenterInterface
|
||||
{
|
||||
private array $presentedData = [];
|
||||
private array $breadcrumbData = [];
|
||||
|
||||
public function present($data): array
|
||||
{
|
||||
$this->breadcrumbData = $data;
|
||||
|
||||
$this->presentBreadcrumbData();
|
||||
|
||||
return $this->presentedData;
|
||||
}
|
||||
|
||||
private function presentBreadcrumbData(): void
|
||||
{
|
||||
$breadcrumbs = $this->breadcrumbData['links'];
|
||||
|
||||
if ($this->breadcrumbData['count'] > 1) {
|
||||
$this->presentedData['@context'] = 'http://schema.org';
|
||||
$this->presentedData['@type'] = 'BreadcrumbList';
|
||||
$this->presentedData['itemListElement'] = [];
|
||||
|
||||
foreach ($breadcrumbs as $i => $breadcrumb) {
|
||||
$this->presentedData['itemListElement'][] = [
|
||||
'@type' => 'ListItem',
|
||||
'position' => $i + 1,
|
||||
'name' => $breadcrumb['title'],
|
||||
'item' => $breadcrumb['url'],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter;
|
||||
|
||||
interface StructuredDataPresenterInterface
|
||||
{
|
||||
/**
|
||||
* Return formatted data
|
||||
*
|
||||
* @param array $data Data from provider
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function present($data);
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter;
|
||||
|
||||
class StructuredDataProductPresenter implements StructuredDataPresenterInterface
|
||||
{
|
||||
private $presentedData = [];
|
||||
private $productData;
|
||||
private $context;
|
||||
|
||||
public function __construct(\Context $context)
|
||||
{
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function present($data): array
|
||||
{
|
||||
$this->productData = $data;
|
||||
|
||||
$this->getProductBasics();
|
||||
$this->getProductIdentifier();
|
||||
$this->getProductBrandData();
|
||||
$this->getProductReviewsData();
|
||||
$this->getProductOffers();
|
||||
|
||||
return $this->presentedData;
|
||||
}
|
||||
|
||||
private function getProductBasics(): void
|
||||
{
|
||||
$this->presentedData['@context'] = 'http://schema.org/';
|
||||
$this->presentedData['@type'] = 'Product';
|
||||
$this->presentedData['name'] = $this->productData['name'];
|
||||
$this->presentedData['category'] = $this->productData['category_name'];
|
||||
|
||||
if (!empty($this->productData['description_short'])) {
|
||||
$this->presentedData['description'] = strip_tags($this->productData['description_short']);
|
||||
}
|
||||
|
||||
if ($this->productData['default_image']) {
|
||||
$this->presentedData['image'] = $this->productData['default_image']['large']['url'];
|
||||
}
|
||||
|
||||
if ($this->productData['reference']) {
|
||||
$this->presentedData['sku'] = $this->productData['reference'];
|
||||
}
|
||||
|
||||
if (!empty($this->productData['weight']) && $this->productData['weight'] > 0) {
|
||||
$this->presentedData['weight'] = [
|
||||
'@context' => 'https://schema.org',
|
||||
'@type' => 'QuantitativeValue',
|
||||
'value' => $this->productData['weight'],
|
||||
'unitCode' => $this->productData['weight_unit'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
private function getProductBrandData(): void
|
||||
{
|
||||
if (empty($this->productData['id_manufacturer'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$productManufacturer = new \Manufacturer((int) $this->productData['id_manufacturer'], $this->context->language->id);
|
||||
|
||||
if (!empty($productManufacturer->name)) {
|
||||
$this->presentedData['brand'] = [
|
||||
'@type' => 'Brand',
|
||||
'name' => $productManufacturer->name,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
private function getProductIdentifier(): void
|
||||
{
|
||||
if (!empty($this->productData['ean13'])) {
|
||||
$this->presentedData['gtin13'] = $this->productData['ean13'];
|
||||
} elseif (!empty($this->productData['upc'])) {
|
||||
$this->presentedData['gtin13'] = '0' . $this->productData['upc'];
|
||||
} elseif (!empty($this->productData['isbn'])) {
|
||||
$this->presentedData['isbn'] = $this->productData['isbn'];
|
||||
} elseif (!empty($this->productData['reference'])) {
|
||||
$this->presentedData['mpn'] = $this->productData['reference'];
|
||||
}
|
||||
}
|
||||
|
||||
private function getProductOffers(): void
|
||||
{
|
||||
if (!$this->productData['show_price']) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->presentedData['offers'] = [
|
||||
'@type' => 'Offer',
|
||||
'name' => $this->productData['name'],
|
||||
'price' => $this->productData['price_amount'],
|
||||
'url' => $this->productData['url'],
|
||||
'priceCurrency' => $this->context->currency->iso_code,
|
||||
];
|
||||
|
||||
if (count($this->productData['images']) > 0) {
|
||||
$images = [];
|
||||
|
||||
foreach ($this->productData['images'] as $img) {
|
||||
$images[] = $img['large']['url'];
|
||||
}
|
||||
|
||||
$this->presentedData['offers']['image'] = $images;
|
||||
}
|
||||
|
||||
if ($this->productData['reference']) {
|
||||
$this->presentedData['offers']['sku'] = $this->productData['reference'];
|
||||
}
|
||||
|
||||
$this->presentedData['offers']['availability'] = $this->productData['quantity'] > 0 || $this->productData['allow_oosp'] ? 'http://schema.org/InStock' : 'http://schema.org/OutOfStock';
|
||||
|
||||
if ($this->productData['show_condition'] && isset($this->productData['condition'])) {
|
||||
$this->presentedData['offers']['itemCondition'] = $this->productData['condition']['schema_url'];
|
||||
}
|
||||
|
||||
if ($this->productData['specific_prices'] && $this->productData['specific_prices']['to'] > (new \DateTime())->format('Y-m-d H:i:s')) {
|
||||
$date = new \DateTime($this->productData['specific_prices']['to']);
|
||||
$this->presentedData['offers']['priceValidUntil'] = $date->format('Y-m-d');
|
||||
}
|
||||
}
|
||||
|
||||
private function getProductReviewsData(): void
|
||||
{
|
||||
if (empty($this->productData['productRating'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$reviews = [];
|
||||
|
||||
foreach ($this->productData['productRating']['reviews'] as $review) {
|
||||
$datePublished = new \DateTime($review['date_add']);
|
||||
|
||||
$reviews[] = [
|
||||
'@type' => 'Review',
|
||||
'author' => [
|
||||
'@type' => 'Person',
|
||||
'name' => $review['customer_name'],
|
||||
],
|
||||
'name' => $review['title'],
|
||||
'reviewBody' => $review['content'],
|
||||
'datePublished' => $datePublished->format(\DateTime::ATOM),
|
||||
'reviewRating' => [
|
||||
'@type' => 'Rating',
|
||||
'ratingValue' => $review['grade'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
$aggregateRating = [
|
||||
'@type' => 'AggregateRating',
|
||||
'ratingValue' => $this->productData['productRating']['averageGrade'],
|
||||
'ratingCount' => $this->productData['productRating']['commentsNb'],
|
||||
'reviewCount' => $this->productData['productRating']['commentsNb'],
|
||||
];
|
||||
|
||||
if ($reviews) {
|
||||
$this->presentedData['review'] = $reviews;
|
||||
}
|
||||
$this->presentedData['aggregateRating'] = $aggregateRating;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter;
|
||||
|
||||
class StructuredDataShopPresenter implements StructuredDataPresenterInterface
|
||||
{
|
||||
private $presentedData = [];
|
||||
private $shopData;
|
||||
private $context;
|
||||
|
||||
public function __construct(\Context $context)
|
||||
{
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function present($data): array
|
||||
{
|
||||
$this->shopData = $data;
|
||||
|
||||
$this->presentShopData();
|
||||
|
||||
return $this->presentedData;
|
||||
}
|
||||
|
||||
private function presentShopData(): void
|
||||
{
|
||||
$this->presentedData['@context'] = 'http://schema.org';
|
||||
$this->presentedData['@type'] = 'Organization';
|
||||
$this->presentedData['name'] = $this->shopData['name'];
|
||||
$this->presentedData['url'] = $this->context->link->getPageLink('index');
|
||||
$this->presentedData['logo'] = [
|
||||
'@type' => 'ImageObject',
|
||||
'url' => $this->shopData['logo'],
|
||||
];
|
||||
|
||||
if ($this->shopData['phone']) {
|
||||
$this->presentedData['contactPoint'] = [
|
||||
'@type' => 'ContactPoint',
|
||||
'telephone' => $this->shopData['phone'],
|
||||
'contactType' => 'customer service',
|
||||
];
|
||||
}
|
||||
|
||||
$address = $this->shopData['address'];
|
||||
$postalCode = $address['postcode'];
|
||||
$city = $address['city'];
|
||||
$country = $address['country'];
|
||||
$addressRegion = $address['state'];
|
||||
$streetAddress = $address['address1'];
|
||||
|
||||
if ($postalCode || $city || $country || $addressRegion || $streetAddress) {
|
||||
$this->presentedData['address'] = [
|
||||
'@type' => 'PostalAddress',
|
||||
];
|
||||
|
||||
if ($postalCode) {
|
||||
$this->presentedData['address']['postalCode'] = $postalCode;
|
||||
}
|
||||
if ($streetAddress) {
|
||||
$this->presentedData['address']['streetAddress'] = $streetAddress;
|
||||
}
|
||||
if ($country || $city) {
|
||||
$addressLocality = '';
|
||||
if ($city) {
|
||||
$addressLocality = $city;
|
||||
}
|
||||
if ($country) {
|
||||
$addressLocality .= ($addressLocality != '' ? ', ' : '') . $country;
|
||||
}
|
||||
|
||||
$this->presentedData['address']['addressLocality'] = $addressLocality;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\StructuredData\Presenter;
|
||||
|
||||
class StructuredDataWebsitePresenter implements StructuredDataPresenterInterface
|
||||
{
|
||||
private $presentedData = [];
|
||||
private $websiteData;
|
||||
private $context;
|
||||
|
||||
public function __construct(\Context $context)
|
||||
{
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function present($data): array
|
||||
{
|
||||
$this->websiteData = $data;
|
||||
|
||||
$this->presentShopData();
|
||||
|
||||
return $this->presentedData;
|
||||
}
|
||||
|
||||
private function presentShopData(): void
|
||||
{
|
||||
$this->presentedData['@context'] = 'http://schema.org';
|
||||
$this->presentedData['@type'] = 'WebSite';
|
||||
$this->presentedData['url'] = $this->context->link->getPageLink('index');
|
||||
$this->presentedData['image'] = [
|
||||
'@type' => 'ImageObject',
|
||||
'url' => $this->websiteData['logo'],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\StructuredData;
|
||||
|
||||
class ProductStructuredData extends AbstractStructuredData implements StructuredDataInterface
|
||||
{
|
||||
public function getStructuredDataType(): string
|
||||
{
|
||||
return 'product';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\StructuredData\Provider;
|
||||
|
||||
class StructuredDataBreadcrumbProvider implements StructuredDataProviderInterface
|
||||
{
|
||||
protected \Context $context;
|
||||
|
||||
public function __construct(\Context $context)
|
||||
{
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function getData(): array
|
||||
{
|
||||
return $this->context->controller->getBreadcrumb();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\StructuredData\Provider;
|
||||
|
||||
class StructuredDataProductProvider implements StructuredDataProviderInterface
|
||||
{
|
||||
private array $data = [];
|
||||
private \Context $context;
|
||||
|
||||
public function __construct(\Context $context)
|
||||
{
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
private function provideProductCommentsDataIfModuleEnabled(): void
|
||||
{
|
||||
$commentsData = [];
|
||||
|
||||
if (\Module::isEnabled('productcomments')) {
|
||||
$productCommentRepository = $this->context->controller->getContainer()->get('product_comment_repository');
|
||||
$commentsModerate = (bool) \Configuration::get('PRODUCT_COMMENTS_MODERATE');
|
||||
$commentsNb = $productCommentRepository->getCommentsNumber($this->data['id'], $commentsModerate);
|
||||
|
||||
if ($commentsNb > 0) {
|
||||
$averageGrade = $productCommentRepository->getAverageGrade($this->data['id'], $commentsModerate);
|
||||
$reviewsData = $productCommentRepository->paginate($this->data['id'], 1, 50, $commentsModerate); // get 50 reviews
|
||||
|
||||
$commentsData = [
|
||||
'averageGrade' => $averageGrade,
|
||||
'commentsNb' => $commentsNb,
|
||||
'reviews' => $reviewsData,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$this->data['productRating'] = $commentsData;
|
||||
}
|
||||
|
||||
public function getProductData(): void
|
||||
{
|
||||
$this->data = $this->context->controller->getTemplateVarProduct()->jsonSerialize();
|
||||
}
|
||||
|
||||
public function getData(): array
|
||||
{
|
||||
$this->getProductData();
|
||||
$this->provideProductCommentsDataIfModuleEnabled();
|
||||
|
||||
return $this->data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\StructuredData\Provider;
|
||||
|
||||
interface StructuredDataProviderInterface
|
||||
{
|
||||
/**
|
||||
* Provide data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getData();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\StructuredData\Provider;
|
||||
|
||||
class StructuredDataShopProvider implements StructuredDataProviderInterface
|
||||
{
|
||||
protected \Context $context;
|
||||
|
||||
public function __construct(\Context $context)
|
||||
{
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function getData(): array
|
||||
{
|
||||
return $this->context->smarty->getTemplateVars('shop');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\StructuredData\Provider;
|
||||
|
||||
class StructuredDataWebsiteProvider implements StructuredDataProviderInterface
|
||||
{
|
||||
protected \Context $context;
|
||||
|
||||
public function __construct(\Context $context)
|
||||
{
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function getData(): array
|
||||
{
|
||||
return $this->context->smarty->getTemplateVars('shop');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\StructuredData;
|
||||
|
||||
class ShopStructuredData extends AbstractStructuredData implements StructuredDataInterface
|
||||
{
|
||||
public function getStructuredDataType(): string
|
||||
{
|
||||
return 'shop';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\StructuredData;
|
||||
|
||||
interface StructuredDataInterface
|
||||
{
|
||||
/**
|
||||
* Return formatted json data
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFormattedData(): string;
|
||||
|
||||
public function getStructuredDataType(): string;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\StructuredData;
|
||||
|
||||
class WebsiteStructuredData extends AbstractStructuredData implements StructuredDataInterface
|
||||
{
|
||||
public function getStructuredDataType(): string
|
||||
{
|
||||
return 'website';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\ThemeAssets;
|
||||
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class ThemeAssetConfigProvider
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $fileContentRead = false;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $fileParsed = [];
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $themeAssetsFileDir;
|
||||
|
||||
public function __construct($themeDir)
|
||||
{
|
||||
$this->themeAssetsFileDir = $themeDir . 'config/assets.yml';
|
||||
}
|
||||
|
||||
public function getFileParsed(): array
|
||||
{
|
||||
if (!$this->fileContentRead) {
|
||||
if (file_exists($this->themeAssetsFileDir)) {
|
||||
$this->fileParsed = Yaml::parse(file_get_contents($this->themeAssetsFileDir));
|
||||
}
|
||||
|
||||
$this->fileContentRead = true;
|
||||
}
|
||||
|
||||
return $this->fileParsed;
|
||||
}
|
||||
|
||||
public function getCssAssets(): array
|
||||
{
|
||||
$cssAssets = [];
|
||||
|
||||
if (!empty($this->getFileParsed()['css'])) {
|
||||
$cssAssets = $this->getFileParsed()['css'];
|
||||
}
|
||||
|
||||
return $cssAssets;
|
||||
}
|
||||
|
||||
public function getJsAssets(): array
|
||||
{
|
||||
$jsAssets = [];
|
||||
|
||||
if (!empty($this->getFileParsed()['js'])) {
|
||||
$jsAssets = $this->getFileParsed()['js'];
|
||||
}
|
||||
|
||||
return $jsAssets;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\ThemeAssets;
|
||||
|
||||
class ThemeAssetsRegister
|
||||
{
|
||||
/**
|
||||
* @var ThemeAssetConfigProvider
|
||||
*/
|
||||
private $assetsDataProvider;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $currentPageName;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $themeName;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $cssAssets = [];
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $jsAssets = [];
|
||||
|
||||
public function __construct(ThemeAssetConfigProvider $assetsDataProvider, \Context $context)
|
||||
{
|
||||
$this->assetsDataProvider = $assetsDataProvider;
|
||||
$this->context = $context;
|
||||
$this->themeName = $this->context->shop->theme->getName();
|
||||
$this->currentPageName = $this->context->controller->getPageName();
|
||||
$this->themePath = 'themes/' . $this->themeName . '/assets/';
|
||||
$this->cssAssets = $assetsDataProvider->getCssAssets();
|
||||
$this->jsAssets = $assetsDataProvider->getJsAssets();
|
||||
}
|
||||
|
||||
private function getFilteredCssAssetsByPage(): array
|
||||
{
|
||||
return $this->filterAssetsArrayByPage($this->cssAssets);
|
||||
}
|
||||
|
||||
private function getFilteredJsAssetsByPage(): array
|
||||
{
|
||||
return $this->filterAssetsArrayByPage($this->jsAssets);
|
||||
}
|
||||
|
||||
private function filterAssetsArrayByPage($assetsArray): array
|
||||
{
|
||||
$pageName = $this->currentPageName;
|
||||
|
||||
return array_filter($assetsArray, function ($asset) use ($pageName) {
|
||||
if (empty($asset['include'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (in_array($pageName, $asset['include'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($asset['include'] as $matchType) {
|
||||
$regex = str_replace(
|
||||
['\*'],
|
||||
['.*', '.'],
|
||||
preg_quote($matchType)
|
||||
);
|
||||
|
||||
if (preg_match('/^' . $regex . '$/is', $pageName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
public function registerThemeAssets(): void
|
||||
{
|
||||
$this->registerJsAssets();
|
||||
$this->registerCssAssets();
|
||||
}
|
||||
|
||||
public function registerJsAssets(): void
|
||||
{
|
||||
$assetsToRegister = $this->getFilteredJsAssetsByPage();
|
||||
|
||||
$default_params = [
|
||||
'position' => \AbstractAssetManager::DEFAULT_JS_POSITION,
|
||||
'priority' => \AbstractAssetManager::DEFAULT_PRIORITY,
|
||||
'inline' => false,
|
||||
'attributes' => null,
|
||||
'server' => 'local',
|
||||
];
|
||||
|
||||
foreach ($assetsToRegister as $id => $asset) {
|
||||
$params = array_merge($default_params, $asset);
|
||||
$file = $params['server'] === 'local' ? $this->themePath . 'js/' . $asset['fileName'] : $asset['fileName'];
|
||||
|
||||
$this->context->controller->registerJavascript(
|
||||
'theme-' . $id,
|
||||
$file,
|
||||
[
|
||||
'position' => $params['position'],
|
||||
'priority' => $params['priority'],
|
||||
'inline' => $params['inline'],
|
||||
'attributes' => $params['attributes'],
|
||||
'server' => $params['server'],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function registerCssAssets(): void
|
||||
{
|
||||
$assetsToRegister = $this->getFilteredCssAssetsByPage();
|
||||
|
||||
$default_params = [
|
||||
'media' => \AbstractAssetManager::DEFAULT_MEDIA,
|
||||
'priority' => \AbstractAssetManager::DEFAULT_PRIORITY,
|
||||
'inline' => false,
|
||||
'server' => 'local',
|
||||
];
|
||||
|
||||
foreach ($assetsToRegister as $id => $asset) {
|
||||
$params = array_merge($default_params, $asset);
|
||||
$file = $params['server'] === 'local' ? $this->themePath . 'css/' . $asset['fileName'] : $asset['fileName'];
|
||||
|
||||
$this->context->controller->registerStylesheet(
|
||||
'theme-' . $id,
|
||||
$file,
|
||||
[
|
||||
'media' => $params['media'],
|
||||
'priority' => $params['priority'],
|
||||
'server' => $params['server'],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\Webp;
|
||||
|
||||
class RelatedImageFileFinder
|
||||
{
|
||||
protected $allowedImagesExtensions = ['jpg', 'png', 'jpeg'];
|
||||
|
||||
public function setAllowedImagesExtensions($allowedImagesExtensions)
|
||||
{
|
||||
$this->allowedImagesExtensions = $allowedImagesExtensions;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAllowedImagesExtensions()
|
||||
{
|
||||
return $this->allowedImagesExtensions;
|
||||
}
|
||||
|
||||
public function findFile($relatedFile)
|
||||
{
|
||||
$fileData = pathinfo($relatedFile);
|
||||
$possibleFiles = [];
|
||||
|
||||
$extensions = $this->getAllowedImagesExtensions();
|
||||
|
||||
foreach ($extensions as $ext) {
|
||||
$possibleFiles[] = $fileData['dirname'] . '/' . $fileData['filename'] . '.' . $ext;
|
||||
}
|
||||
|
||||
foreach ($possibleFiles as $file) {
|
||||
if (file_exists($file)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\Webp;
|
||||
|
||||
use WebPConvert\Convert\ConverterFactory;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailed\InvalidInput\ConverterNotFoundException;
|
||||
use WebPConvert\Convert\Exceptions\ConversionFailedException;
|
||||
|
||||
class WebpConvertLibraries
|
||||
{
|
||||
protected $converters = [
|
||||
'cwebp' => ['label' => 'Cwebp binary'],
|
||||
'vips' => ['label' => 'Vips PHP extension'],
|
||||
'imagick' => ['label' => 'Imagick PHP extension'],
|
||||
'gmagick' => ['label' => 'Gmagick PHP extension'],
|
||||
'imagemagick' => ['label' => 'Imagemagick binary'],
|
||||
'graphicsmagick' => ['label' => 'Graphicsmagick binary (gm)'],
|
||||
'gd' => ['label' => 'Gd PHP extension'],
|
||||
// NOT SUPPORTED
|
||||
// 'ewww' => ['label' => 'EWWW cloud service'],
|
||||
];
|
||||
|
||||
protected $exampleImgFile = _PS_MODULE_DIR_ . 'is_themecore/views/img/example.jpg';
|
||||
protected $exampleImgFileDesc = _PS_MODULE_DIR_ . 'is_themecore/views/img/example.webp';
|
||||
|
||||
public function getConvertersList(): array
|
||||
{
|
||||
$converters = $this->converters;
|
||||
|
||||
foreach ($converters as $converterId => $converterOptions) {
|
||||
$converters[$converterId]['id'] = $converterId;
|
||||
|
||||
try {
|
||||
$converterInstance = ConverterFactory::makeConverter($converterId, $this->exampleImgFile, $this->exampleImgFileDesc, []);
|
||||
$converterInstance->checkOperationality();
|
||||
$converterInstance->doConvert();
|
||||
$converters[$converterId]['disabled'] = false;
|
||||
} catch (ConversionFailedException $conversionFailedException) {
|
||||
$converters[$converterId]['disabled'] = true;
|
||||
} catch (ConverterNotFoundException $converterNotFoundException) {
|
||||
$converters[$converterId]['disabled'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $converters;
|
||||
}
|
||||
|
||||
public function getFirstAvailableConverter(): array
|
||||
{
|
||||
$list = $this->getConvertersList();
|
||||
|
||||
foreach ($list as $converter) {
|
||||
if (!$converter['disabled']) {
|
||||
return $converter;
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
79
is_themecore/is_themecore/src/Core/Webp/WebpFilesEraser.php
Normal file
79
is_themecore/is_themecore/src/Core/Webp/WebpFilesEraser.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\Webp;
|
||||
|
||||
use Symfony\Component\Finder\Finder;
|
||||
|
||||
class WebpFilesEraser
|
||||
{
|
||||
private $query = '';
|
||||
private $finder;
|
||||
private $files;
|
||||
private $excludeList = ['node_modules', 'vendor', 'app', 'var', 'classes', 'controllers', 'download'];
|
||||
private $filesCount = 0;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->finder = new Finder();
|
||||
}
|
||||
|
||||
public function setQuery($query)
|
||||
{
|
||||
$this->query = $query;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
public function setExcludeList(array $excludeList)
|
||||
{
|
||||
$this->excludeList = $excludeList;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getExcludeList()
|
||||
{
|
||||
return $this->excludeList;
|
||||
}
|
||||
|
||||
private function setFilesCount()
|
||||
{
|
||||
$this->filesCount = iterator_count($this->files);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFilesCount()
|
||||
{
|
||||
return $this->filesCount;
|
||||
}
|
||||
|
||||
private function findFiles()
|
||||
{
|
||||
$this->files = $this->finder
|
||||
->files()
|
||||
->ignoreUnreadableDirs()
|
||||
->in($this->query)
|
||||
->exclude($this->excludeList)
|
||||
->name('*.webp');
|
||||
}
|
||||
|
||||
public function eraseFiles()
|
||||
{
|
||||
$this->findFiles();
|
||||
$this->setFilesCount();
|
||||
|
||||
foreach ($this->files as $file) {
|
||||
try {
|
||||
unlink($file->getPathname());
|
||||
} catch (\Throwable $error) {
|
||||
throw $error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
110
is_themecore/is_themecore/src/Core/Webp/WebpGenerator.php
Normal file
110
is_themecore/is_themecore/src/Core/Webp/WebpGenerator.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\Webp;
|
||||
|
||||
use WebPConvert\WebPConvert;
|
||||
|
||||
class WebpGenerator
|
||||
{
|
||||
protected $fileFinder;
|
||||
protected $destinationFile = '';
|
||||
protected $converter = false;
|
||||
protected $debugEnabled = false;
|
||||
protected $sharpYuv = false;
|
||||
protected $quality = 90;
|
||||
|
||||
public function __construct(RelatedImageFileFinder $fileFinder)
|
||||
{
|
||||
$this->fileFinder = $fileFinder;
|
||||
}
|
||||
|
||||
public function setQuality($quality)
|
||||
{
|
||||
$this->quality = $quality;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getQuality(): int
|
||||
{
|
||||
return $this->quality;
|
||||
}
|
||||
|
||||
public function setConverter($converter)
|
||||
{
|
||||
$this->converter = $converter;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getConverter(): string
|
||||
{
|
||||
return $this->converter;
|
||||
}
|
||||
|
||||
public function setSharpYuv($sharpYuv)
|
||||
{
|
||||
$this->sharpYuv = $sharpYuv;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSharpYuv(): bool
|
||||
{
|
||||
return $this->sharpYuv;
|
||||
}
|
||||
|
||||
public function setDebugEnabled($debugEnabled)
|
||||
{
|
||||
$this->debugEnabled = $debugEnabled;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDebugEnabled(): bool
|
||||
{
|
||||
return $this->debugEnabled;
|
||||
}
|
||||
|
||||
public function setDestinationFile($destinationFile)
|
||||
{
|
||||
$this->destinationFile = $destinationFile;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDestinationFile(): string
|
||||
{
|
||||
return $this->destinationFile;
|
||||
}
|
||||
|
||||
public function findRelatedFile()
|
||||
{
|
||||
return $this->fileFinder->findFile($this->getDestinationFile());
|
||||
}
|
||||
|
||||
public function convertAndServe()
|
||||
{
|
||||
$sourceFile = $this->findRelatedFile();
|
||||
|
||||
WebPConvert::serveConverted($sourceFile, $this->destinationFile, [
|
||||
'fail' => 'original',
|
||||
'show-report' => $this->getDebugEnabled(),
|
||||
|
||||
'serve-image' => [
|
||||
'headers' => [
|
||||
'cache-control' => true,
|
||||
'vary-accept' => true,
|
||||
// other headers can be toggled...
|
||||
],
|
||||
'cache-control-header' => 'max-age=2',
|
||||
],
|
||||
'convert' => [
|
||||
'stack-converters' => [$this->getConverter()],
|
||||
'quality' => $this->getQuality(),
|
||||
'encoding' => 'auto',
|
||||
'sharp-yuv' => $this->getSharpYuv(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
}
|
||||
109
is_themecore/is_themecore/src/Core/Webp/WebpPictureGenerator.php
Normal file
109
is_themecore/is_themecore/src/Core/Webp/WebpPictureGenerator.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Core\Webp;
|
||||
|
||||
class WebpPictureGenerator
|
||||
{
|
||||
private $allowedExtensions = ['png', 'jpg', 'jpeg'];
|
||||
protected $content = '';
|
||||
private $doc;
|
||||
|
||||
public function __construct($content)
|
||||
{
|
||||
$this->content = $content;
|
||||
$this->doc = new \DOMDocument();
|
||||
}
|
||||
|
||||
public function loadContent()
|
||||
{
|
||||
@$this->doc->loadHTML('<?xml encoding="utf-8" ?>' . $this->content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function generatePictureTags(): void
|
||||
{
|
||||
$images = $this->doc->getElementsByTagName('img');
|
||||
|
||||
if (0 === count($images)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($images as $image) {
|
||||
if ($image->hasAttribute('data-external-url')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->generatePictureTagFromImg($image);
|
||||
}
|
||||
|
||||
$this->content = $this->doc->saveHTML();
|
||||
$this->content = str_replace('<?xml encoding="utf-8" ?>', '', $this->content);
|
||||
}
|
||||
|
||||
private function generatePictureTagFromImg($image)
|
||||
{
|
||||
$lazyLoad = !empty($params['lazyload']) ? $params['lazyload'] : (bool) preg_match('/' . implode('|', ['lazyload', 'swiper-lazy']) . '/i', $image->ownerDocument->saveHTML($image));
|
||||
$srcAttributePrefix = $lazyLoad ? 'data-' : '';
|
||||
$containSrcset = $image->hasAttribute($srcAttributePrefix . 'srcset');
|
||||
$srcAttribute = $srcAttributePrefix . ($containSrcset ? 'srcset' : 'src');
|
||||
|
||||
$src = $image->getAttribute($srcAttribute);
|
||||
$rawSrcArray = explode(',', $src);
|
||||
$imageSrcArray = [];
|
||||
|
||||
foreach ($rawSrcArray as $rawSrc) {
|
||||
$srcWithMediaArray = explode(' ', $rawSrc);
|
||||
|
||||
$srcWithMediaArray = array_values(array_filter($srcWithMediaArray, function ($elem) {
|
||||
return !empty($elem);
|
||||
}));
|
||||
|
||||
$imageSrcArray[] = [
|
||||
'file' => $srcWithMediaArray[0] ?? null,
|
||||
'media' => $srcWithMediaArray[1] ?? null,
|
||||
'ext' => isset($srcWithMediaArray[0]) ? pathinfo($srcWithMediaArray[0], PATHINFO_EXTENSION) : null,
|
||||
];
|
||||
}
|
||||
|
||||
$picture = $this->doc->createElement('picture');
|
||||
$pict_clone = $picture->cloneNode();
|
||||
$image->parentNode->replaceChild($pict_clone, $image);
|
||||
$pict_clone->appendChild($image);
|
||||
|
||||
$source = $this->doc->createElement('source');
|
||||
$source->setAttribute('type', 'image/webp');
|
||||
$sourceWebp = '';
|
||||
|
||||
$lastKey = array_key_last($imageSrcArray);
|
||||
|
||||
foreach ($imageSrcArray as $key => $imageSrc) {
|
||||
$ext = explode('?', $imageSrc['ext']);
|
||||
$ext = $ext[0] ?? null;
|
||||
|
||||
if (!in_array($ext, $this->allowedExtensions)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$newWebpSrc = str_replace('.' . $imageSrc['ext'], '.webp', $imageSrc['file']);
|
||||
|
||||
$sourceWebp .= $newWebpSrc . ($imageSrc['media'] ? ' ' . $imageSrc['media'] : '');
|
||||
|
||||
if ($key != $lastKey) {
|
||||
$sourceWebp .= ', ';
|
||||
}
|
||||
}
|
||||
|
||||
if ($sourceWebp) {
|
||||
$source->setAttribute($lazyLoad ? 'data-srcset' : 'srcset', $sourceWebp);
|
||||
$src_clone = $source->cloneNode();
|
||||
$image->parentNode->replaceChild($src_clone, $image);
|
||||
$src_clone->appendChild($image);
|
||||
}
|
||||
}
|
||||
|
||||
public function getContent(): string
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
}
|
||||
11
is_themecore/is_themecore/src/Core/Webp/index.php
Normal file
11
is_themecore/is_themecore/src/Core/Webp/index.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: ../');
|
||||
exit;
|
||||
11
is_themecore/is_themecore/src/Core/index.php
Normal file
11
is_themecore/is_themecore/src/Core/index.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: ../');
|
||||
exit;
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Form\ChoiceProvider;
|
||||
|
||||
use Oksydan\Module\IsThemeCore\Core\ListingDisplay\ThemeListDisplay;
|
||||
use PrestaShop\PrestaShop\Core\Form\FormChoiceProviderInterface;
|
||||
|
||||
class ListDisplayChoiceProvider implements FormChoiceProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var ThemeListDisplay
|
||||
*/
|
||||
protected $themeListDisplay;
|
||||
|
||||
/**
|
||||
* @param ThemeListDisplay $themeListDisplay
|
||||
*/
|
||||
public function __construct(ThemeListDisplay $themeListDisplay)
|
||||
{
|
||||
$this->themeListDisplay = $themeListDisplay;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getChoices(): array
|
||||
{
|
||||
$choices = [];
|
||||
|
||||
foreach ($this->themeListDisplay->getDisplayOptions() as $display) {
|
||||
$choices[$display] = $display;
|
||||
}
|
||||
|
||||
return $choices;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Form\ChoiceProvider;
|
||||
|
||||
use Oksydan\Module\IsThemeCore\Core\Webp\WebpConvertLibraries;
|
||||
use PrestaShop\PrestaShop\Core\Form\FormChoiceProviderInterface;
|
||||
|
||||
class WebpLibraryChoiceProvider implements FormChoiceProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var WebpConvertLibraries
|
||||
*/
|
||||
protected $webpConvertLibraries;
|
||||
|
||||
/**
|
||||
* @param WebpConvertLibraries $webpConvertLibraries
|
||||
*/
|
||||
public function __construct(WebpConvertLibraries $webpConvertLibraries)
|
||||
{
|
||||
$this->webpConvertLibraries = $webpConvertLibraries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getChoices(): array
|
||||
{
|
||||
$choices = [];
|
||||
|
||||
foreach ($this->webpConvertLibraries->getConvertersList() as $converter) {
|
||||
$choices[$converter['label']] = $converter['id'];
|
||||
}
|
||||
|
||||
return $choices;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getChoicesFull(): array
|
||||
{
|
||||
$choices = [];
|
||||
|
||||
foreach ($this->webpConvertLibraries->getConvertersList() as $converter) {
|
||||
$choices[$converter['id']] = $converter;
|
||||
}
|
||||
|
||||
return $choices;
|
||||
}
|
||||
}
|
||||
11
is_themecore/is_themecore/src/Form/ChoiceProvider/index.php
Normal file
11
is_themecore/is_themecore/src/Form/ChoiceProvider/index.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: ../');
|
||||
exit;
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Form\Settings;
|
||||
|
||||
use PrestaShop\PrestaShop\Core\Configuration\AbstractMultistoreConfiguration;
|
||||
use PrestaShopBundle\Service\Form\MultistoreCheckboxEnabler;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Configuration is used to save data to configuration table and retrieve from it
|
||||
*/
|
||||
final class GeneralConfiguration extends AbstractMultistoreConfiguration
|
||||
{
|
||||
private const CONFIGURATION_FIELDS = [
|
||||
'list_display_settings',
|
||||
'early_hints',
|
||||
'preload_css',
|
||||
'load_party_town',
|
||||
'debug_party_town',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const THEMECORE_DISPLAY_LIST = 'THEMECORE_DISPLAY_LIST';
|
||||
public const THEMECORE_EARLY_HINTS = 'THEMECORE_EARLY_HINTS';
|
||||
public const THEMECORE_PRELOAD_CSS = 'THEMECORE_PRELOAD_CSS';
|
||||
public const THEMECORE_LOAD_PARTY_TOWN = 'THEMECORE_LOAD_PARTY_TOWN';
|
||||
public const THEMECORE_DEBUG_PARTY_TOWN = 'THEMECORE_DEBUG_PARTY_TOWN';
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private array $fields = [
|
||||
'list_display_settings' => self::THEMECORE_DISPLAY_LIST,
|
||||
'early_hints' => self::THEMECORE_EARLY_HINTS,
|
||||
'preload_css' => self::THEMECORE_PRELOAD_CSS,
|
||||
'load_party_town' => self::THEMECORE_LOAD_PARTY_TOWN,
|
||||
'debug_party_town' => self::THEMECORE_DEBUG_PARTY_TOWN,
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getConfiguration(): array
|
||||
{
|
||||
$configurationValues = [];
|
||||
|
||||
foreach ($this->fields as $field => $configurationKey) {
|
||||
$configurationValues[$field] = $this->configuration->get($configurationKey);
|
||||
}
|
||||
|
||||
return $configurationValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param array<string, mixed> $configuration
|
||||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function updateConfiguration(array $configuration): array
|
||||
{
|
||||
$errors = [];
|
||||
|
||||
if (!$this->validateConfiguration($configuration)) {
|
||||
$errors[] = [
|
||||
'key' => 'Invalid configuration',
|
||||
'parameters' => [],
|
||||
'domain' => 'Admin.Notifications.Warning',
|
||||
];
|
||||
} else {
|
||||
$shopConstraint = $this->getShopConstraint();
|
||||
|
||||
try {
|
||||
foreach ($this->fields as $field => $configurationKey) {
|
||||
$this->updateConfigurationValue($configurationKey, $field, $configuration, $shopConstraint);
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
$errors[] = [
|
||||
'key' => $exception->getMessage(),
|
||||
'parameters' => [],
|
||||
'domain' => 'Admin.Notifications.Warning',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the parameters passed are valid.
|
||||
*
|
||||
* @param array<string, mixed> $configuration
|
||||
*
|
||||
* @return bool Returns true if no exception are thrown
|
||||
*/
|
||||
public function validateConfiguration(array $configuration): bool
|
||||
{
|
||||
foreach ($this->fields as $field => $configurationKey) {
|
||||
$multistoreKey = MultistoreCheckboxEnabler::MULTISTORE_FIELD_PREFIX . $field;
|
||||
$this->fields[$multistoreKey] = '';
|
||||
}
|
||||
|
||||
foreach ($configuration as $key => $value) {
|
||||
if (!key_exists($key, $this->fields)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OptionsResolver
|
||||
*/
|
||||
protected function buildResolver(): OptionsResolver
|
||||
{
|
||||
return (new OptionsResolver())
|
||||
->setDefined(self::CONFIGURATION_FIELDS)
|
||||
->setAllowedTypes('list_display_settings', ['string', 'null'])
|
||||
->setAllowedTypes('early_hints', 'bool')
|
||||
->setAllowedTypes('preload_css', 'bool')
|
||||
->setAllowedTypes('load_party_town', 'bool')
|
||||
->setAllowedTypes('debug_party_town', 'bool');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Form\Settings;
|
||||
|
||||
use PrestaShop\PrestaShop\Core\Configuration\DataConfigurationInterface;
|
||||
use PrestaShop\PrestaShop\Core\Form\FormDataProviderInterface;
|
||||
|
||||
/**
|
||||
* Class GeneralFormDataProvider
|
||||
*/
|
||||
class GeneralFormDataProvider implements FormDataProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var DataConfigurationInterface
|
||||
*/
|
||||
private $generalConfiguration;
|
||||
|
||||
/**
|
||||
* @param DataConfigurationInterface $generalConfiguration
|
||||
*/
|
||||
public function __construct(DataConfigurationInterface $generalConfiguration)
|
||||
{
|
||||
$this->generalConfiguration = $generalConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return array<string, mixed> The form data as an associative array
|
||||
*/
|
||||
public function getData(): array
|
||||
{
|
||||
return $this->generalConfiguration->getConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param array<string, mixed> $data
|
||||
*
|
||||
* @return array<int, array<string, mixed>> An array of errors messages if data can't persisted
|
||||
*/
|
||||
public function setData(array $data): array
|
||||
{
|
||||
return $this->generalConfiguration->updateConfiguration($data);
|
||||
}
|
||||
}
|
||||
104
is_themecore/is_themecore/src/Form/Settings/GeneralType.php
Normal file
104
is_themecore/is_themecore/src/Form/Settings/GeneralType.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Form\Settings;
|
||||
|
||||
use PrestaShopBundle\Form\Admin\Type\MultistoreConfigurationType;
|
||||
use PrestaShopBundle\Form\Admin\Type\SwitchType;
|
||||
use PrestaShopBundle\Form\Admin\Type\TranslatorAwareType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
|
||||
class GeneralType extends TranslatorAwareType
|
||||
{
|
||||
/**
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
private $translator;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $displayListChoices;
|
||||
|
||||
/**
|
||||
* GeneralType constructor.
|
||||
*
|
||||
* @param TranslatorInterface $translator
|
||||
* @param array $locales
|
||||
* @param array $displayListChoices
|
||||
*/
|
||||
public function __construct(
|
||||
$translator,
|
||||
array $locales,
|
||||
array $displayListChoices
|
||||
) {
|
||||
parent::__construct($translator, $locales);
|
||||
$this->displayListChoices = $displayListChoices;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param FormBuilderInterface<string, mixed> $builder
|
||||
* @param array<string, mixed> $options
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->add('list_display_settings',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => $this->displayListChoices,
|
||||
'label' => $this->trans('Default list display', 'Modules.isthemecore.Admin'),
|
||||
'multistore_configuration_key' => GeneralConfiguration::THEMECORE_DISPLAY_LIST,
|
||||
]
|
||||
)
|
||||
->add('early_hints',
|
||||
SwitchType::class,
|
||||
[
|
||||
'required' => false,
|
||||
'label' => $this->trans('Early hints (HTTP 103) enabled', 'Modules.isthemecore.Admin'),
|
||||
'help' => $this->trans('Cloudflare CDN, Early hints option have to enabled. <a href="https://developers.cloudflare.com/cache/about/early-hints/">More information</a>', 'Modules.isthemecore.Admin'),
|
||||
'multistore_configuration_key' => GeneralConfiguration::THEMECORE_EARLY_HINTS,
|
||||
]
|
||||
)
|
||||
->add('preload_css',
|
||||
SwitchType::class,
|
||||
[
|
||||
'required' => false,
|
||||
'label' => $this->trans('Preload css enabled, only working with CCC for css option enabled', 'Modules.isthemecore.Admin'),
|
||||
'multistore_configuration_key' => GeneralConfiguration::THEMECORE_PRELOAD_CSS,
|
||||
]
|
||||
)
|
||||
->add('load_party_town',
|
||||
SwitchType::class,
|
||||
[
|
||||
'required' => false,
|
||||
'label' => $this->trans('Load partytown script', 'Modules.isthemecore.Admin'),
|
||||
'help' => $this->trans('Be aware that partytown is still beta. Make sure that everything is working as expected before pushing it to your production store.', 'Modules.isthemecore.Admin'),
|
||||
'multistore_configuration_key' => GeneralConfiguration::THEMECORE_LOAD_PARTY_TOWN,
|
||||
]
|
||||
)
|
||||
->add('debug_party_town',
|
||||
SwitchType::class,
|
||||
[
|
||||
'required' => false,
|
||||
'label' => $this->trans('Enable debug mode for partytown', 'Modules.isthemecore.Admin'),
|
||||
'multistore_configuration_key' => GeneralConfiguration::THEMECORE_DEBUG_PARTY_TOWN,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see MultistoreConfigurationTypeExtension
|
||||
*/
|
||||
public function getParent(): string
|
||||
{
|
||||
return MultistoreConfigurationType::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Form\Settings;
|
||||
|
||||
use PrestaShop\PrestaShop\Adapter\Configuration;
|
||||
use PrestaShop\PrestaShop\Core\Configuration\DataConfigurationInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Configuration is used to save data to configuration table and retrieve from it
|
||||
*/
|
||||
final class WebpConfiguration implements DataConfigurationInterface
|
||||
{
|
||||
private const CONFIGURATION_FIELDS = [
|
||||
'webp_enabled',
|
||||
'webp_quality',
|
||||
'webp_converter',
|
||||
'webp_sharpyuv',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const THEMECORE_WEBP_ENABLED = 'THEMECORE_WEBP_ENABLED';
|
||||
public const THEMECORE_WEBP_QUALITY = 'THEMECORE_WEBP_QUALITY';
|
||||
public const THEMECORE_WEBP_CONVERTER = 'THEMECORE_WEBP_CONVERTER';
|
||||
public const THEMECORE_WEBP_SHARPYUV = 'THEMECORE_WEBP_SHARPYUV';
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private $fields = [
|
||||
'webp_enabled' => self::THEMECORE_WEBP_ENABLED,
|
||||
'webp_quality' => self::THEMECORE_WEBP_QUALITY,
|
||||
'webp_converter' => self::THEMECORE_WEBP_CONVERTER,
|
||||
'webp_sharpyuv' => self::THEMECORE_WEBP_SHARPYUV,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var Configuration
|
||||
*/
|
||||
protected $configuration;
|
||||
|
||||
public function __construct(Configuration $configuration)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getConfiguration(): array
|
||||
{
|
||||
$configurationValues = [];
|
||||
|
||||
foreach ($this->fields as $field => $configurationKey) {
|
||||
$configurationValues[$field] = $this->configuration->get($configurationKey);
|
||||
}
|
||||
|
||||
return $configurationValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param array<string, mixed> $configuration
|
||||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public function updateConfiguration(array $configuration): array
|
||||
{
|
||||
$errors = [];
|
||||
|
||||
if (!$this->validateConfiguration($configuration)) {
|
||||
$errors[] = [
|
||||
'key' => 'Invalid configuration',
|
||||
'parameters' => [],
|
||||
'domain' => 'Admin.Notifications.Warning',
|
||||
];
|
||||
} else {
|
||||
try {
|
||||
foreach ($this->fields as $field => $configurationKey) {
|
||||
$this->configuration->set($configurationKey, $configuration[$field]);
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
$errors[] = [
|
||||
'key' => $exception->getMessage(),
|
||||
'parameters' => [],
|
||||
'domain' => 'Admin.Notifications.Warning',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the parameters passed are valid.
|
||||
*
|
||||
* @param array<string, mixed> $configuration
|
||||
*
|
||||
* @return bool Returns true if no exception are thrown
|
||||
*/
|
||||
public function validateConfiguration(array $configuration): bool
|
||||
{
|
||||
foreach ($configuration as $key => $value) {
|
||||
if (!key_exists($key, $this->fields)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OptionsResolver
|
||||
*/
|
||||
protected function buildResolver(): OptionsResolver
|
||||
{
|
||||
return (new OptionsResolver())
|
||||
->setDefined(self::CONFIGURATION_FIELDS)
|
||||
->setAllowedTypes('webp_enabled', 'bool')
|
||||
->setAllowedTypes('webp_quality', 'string')
|
||||
->setAllowedTypes('webp_converter', 'string')
|
||||
->setAllowedTypes('webp_sharpyuv', 'bool');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Form\Settings;
|
||||
|
||||
use PrestaShop\PrestaShop\Core\Configuration\DataConfigurationInterface;
|
||||
use PrestaShop\PrestaShop\Core\Form\FormDataProviderInterface;
|
||||
|
||||
/**
|
||||
* Class WebpFormDataProvider
|
||||
*/
|
||||
class WebpFormDataProvider implements FormDataProviderInterface
|
||||
{
|
||||
/**
|
||||
* @var DataConfigurationInterface
|
||||
*/
|
||||
private $webpConfiguration;
|
||||
|
||||
/**
|
||||
* @param DataConfigurationInterface $webpConfiguration
|
||||
*/
|
||||
public function __construct(DataConfigurationInterface $webpConfiguration)
|
||||
{
|
||||
$this->webpConfiguration = $webpConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return array<string, mixed> The form data as an associative array
|
||||
*/
|
||||
public function getData(): array
|
||||
{
|
||||
return $this->webpConfiguration->getConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param array<string, mixed> $data
|
||||
*
|
||||
* @return array<int, array<string, mixed>> An array of errors messages if data can't persisted
|
||||
*/
|
||||
public function setData(array $data): array
|
||||
{
|
||||
return $this->webpConfiguration->updateConfiguration($data);
|
||||
}
|
||||
}
|
||||
247
is_themecore/is_themecore/src/Form/Settings/WebpType.php
Normal file
247
is_themecore/is_themecore/src/Form/Settings/WebpType.php
Normal file
@@ -0,0 +1,247 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Form\Settings;
|
||||
|
||||
use PrestaShopBundle\Form\Admin\Type\IconButtonType;
|
||||
use PrestaShopBundle\Form\Admin\Type\MultistoreConfigurationType;
|
||||
use PrestaShopBundle\Form\Admin\Type\SwitchType;
|
||||
use PrestaShopBundle\Form\Admin\Type\TranslatorAwareType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
use Symfony\Component\Translation\TranslatorInterface;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
use Symfony\Component\Validator\Constraints\Range;
|
||||
|
||||
class WebpType extends TranslatorAwareType
|
||||
{
|
||||
/**
|
||||
* @var TranslatorInterface
|
||||
*/
|
||||
private $translator;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $convertersList;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $convertersListFull;
|
||||
|
||||
/**
|
||||
* @var RouterInterface
|
||||
*/
|
||||
private $router;
|
||||
|
||||
/**
|
||||
* WebpType constructor.
|
||||
*
|
||||
* @param TranslatorInterface $translator
|
||||
* @param array $locales
|
||||
* @param array $convertersList
|
||||
* @param array $convertersListFull
|
||||
*/
|
||||
public function __construct(
|
||||
$translator,
|
||||
array $locales,
|
||||
array $convertersList,
|
||||
array $convertersListFull,
|
||||
RouterInterface $router
|
||||
) {
|
||||
parent::__construct($translator, $locales);
|
||||
$this->convertersList = $convertersList;
|
||||
$this->convertersListFull = $convertersListFull;
|
||||
$this->router = $router;
|
||||
}
|
||||
|
||||
private function allWebpConvertersDisabled(): bool
|
||||
{
|
||||
return array_reduce($this->convertersListFull, function ($carry, $item) {
|
||||
return $carry && $item['disabled'];
|
||||
}, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param FormBuilderInterface<string, mixed> $builder
|
||||
* @param array<string, mixed> $options
|
||||
*/
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$webpDisabled = $this->allWebpConvertersDisabled();
|
||||
$extraAttributes = [];
|
||||
|
||||
if ($webpDisabled) {
|
||||
$extraAttributes = [
|
||||
'alert_message' => $this->trans('Webp converters not available contact your admin or hosting provider.', 'Modules.isthemecore.Admin'),
|
||||
'alert_type' => 'danger',
|
||||
'alert_position' => 'append',
|
||||
];
|
||||
}
|
||||
|
||||
$builder
|
||||
->add('webp_enabled',
|
||||
SwitchType::class,
|
||||
array_merge(
|
||||
[
|
||||
'required' => false,
|
||||
'label' => $this->trans('Enable WEBP', 'Modules.isthemecore.Admin'),
|
||||
'disabled' => $webpDisabled,
|
||||
],
|
||||
$extraAttributes
|
||||
)
|
||||
)
|
||||
->add('webp_sharpyuv',
|
||||
SwitchType::class,
|
||||
[
|
||||
'required' => false,
|
||||
'label' => $this->trans('Enable better RGB->YUV color conversion', 'Modules.isthemecore.Admin'),
|
||||
'disabled' => $webpDisabled,
|
||||
]
|
||||
)
|
||||
->add('webp_quality',
|
||||
TextType::class,
|
||||
[
|
||||
'required' => false,
|
||||
'label' => $this->trans('Webp quality', 'Modules.isthemecore.Admin'),
|
||||
'help' => $this->trans('Range 1-100', 'Modules.isthemecore.Admin'),
|
||||
'disabled' => $webpDisabled,
|
||||
'constraints' => [
|
||||
$this->getRangeConstraint(1, 100),
|
||||
$this->getNotBlankConstraint(),
|
||||
],
|
||||
]
|
||||
)
|
||||
->add('webp_converter',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => $this->convertersList,
|
||||
'label' => $this->trans('Webp converter options', 'Modules.isthemecore.Admin'),
|
||||
'disabled' => $webpDisabled,
|
||||
'expanded' => true,
|
||||
'multiple' => false,
|
||||
'choice_attr' => function ($choice) {
|
||||
return ['disabled' => $this->convertersListFull[$choice]['disabled']];
|
||||
},
|
||||
'choice_label' => function ($choice) {
|
||||
return $this->convertersListFull[$choice]['label'] . ($this->convertersListFull[$choice]['disabled'] ? '<span class="ml-1 badge badge-danger">' . $this->trans('not available', 'Modules.isthemecore.Admin') . '</span>' : '');
|
||||
},
|
||||
]
|
||||
)
|
||||
->add('erase_all_webp', IconButtonType::class, [
|
||||
'label' => $this->trans('Erase all webp images', 'Modules.isthemecore.Admin'),
|
||||
'type' => 'link',
|
||||
'icon' => 'delete',
|
||||
'attr' => [
|
||||
'class' => 'btn-danger',
|
||||
'href' => $this->router->generate(
|
||||
'is_themecore_module_settings_webp_erase_all',
|
||||
[
|
||||
'type' => 'all',
|
||||
]
|
||||
),
|
||||
],
|
||||
])
|
||||
->add('erase_product_webp', IconButtonType::class, [
|
||||
'label' => $this->trans('Erase all product webp images', 'Modules.isthemecore.Admin'),
|
||||
'type' => 'link',
|
||||
'icon' => 'delete',
|
||||
'attr' => [
|
||||
'class' => 'btn-danger',
|
||||
'href' => $this->router->generate(
|
||||
'is_themecore_module_settings_webp_erase_all',
|
||||
[
|
||||
'type' => 'product',
|
||||
]
|
||||
),
|
||||
],
|
||||
])
|
||||
->add('erase_modules_webp', IconButtonType::class, [
|
||||
'label' => $this->trans('Erase all modules webp images', 'Modules.isthemecore.Admin'),
|
||||
'type' => 'link',
|
||||
'icon' => 'delete',
|
||||
'attr' => [
|
||||
'class' => 'btn-danger',
|
||||
'href' => $this->router->generate(
|
||||
'is_themecore_module_settings_webp_erase_all',
|
||||
[
|
||||
'type' => 'module',
|
||||
]
|
||||
),
|
||||
],
|
||||
])
|
||||
->add('erase_cms_webp', IconButtonType::class, [
|
||||
'label' => $this->trans('Erase all CMS webp images', 'Modules.isthemecore.Admin'),
|
||||
'type' => 'link',
|
||||
'icon' => 'delete',
|
||||
'attr' => [
|
||||
'class' => 'btn-danger',
|
||||
'href' => $this->router->generate(
|
||||
'is_themecore_module_settings_webp_erase_all',
|
||||
[
|
||||
'type' => 'cms',
|
||||
]
|
||||
),
|
||||
],
|
||||
])
|
||||
->add('erase_themes_webp', IconButtonType::class, [
|
||||
'label' => $this->trans('Erase all themes webp images', 'Modules.isthemecore.Admin'),
|
||||
'type' => 'link',
|
||||
'icon' => 'delete',
|
||||
'attr' => [
|
||||
'class' => 'btn-danger',
|
||||
'href' => $this->router->generate(
|
||||
'is_themecore_module_settings_webp_erase_all',
|
||||
[
|
||||
'type' => 'themes',
|
||||
]
|
||||
),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see MultistoreConfigurationTypeExtension
|
||||
*/
|
||||
public function getParent(): string
|
||||
{
|
||||
return MultistoreConfigurationType::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NotBlank
|
||||
*/
|
||||
private function getNotBlankConstraint()
|
||||
{
|
||||
return new NotBlank([
|
||||
'message' => $this->trans('This field cannot be empty.', 'Modules.isthemecore.Admin'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Range
|
||||
*/
|
||||
private function getRangeConstraint(int $min = 1, int $max = 100)
|
||||
{
|
||||
return new Range([
|
||||
'min' => $min,
|
||||
'max' => $max,
|
||||
'invalidMessage' => $this->trans(
|
||||
'This field value have to be between %min% and %max%.',
|
||||
'Modules.isthemecore.Admin',
|
||||
[
|
||||
'%min%' => $min,
|
||||
'%max%' => $max,
|
||||
]
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
11
is_themecore/is_themecore/src/Form/Settings/index.php
Normal file
11
is_themecore/is_themecore/src/Form/Settings/index.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: ../');
|
||||
exit;
|
||||
11
is_themecore/is_themecore/src/Form/index.php
Normal file
11
is_themecore/is_themecore/src/Form/index.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
|
||||
header('Location: ../');
|
||||
exit;
|
||||
25
is_themecore/is_themecore/src/Hook/AbstractHook.php
Normal file
25
is_themecore/is_themecore/src/Hook/AbstractHook.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Hook;
|
||||
|
||||
abstract class AbstractHook
|
||||
{
|
||||
public const HOOK_LIST = [];
|
||||
|
||||
protected $module;
|
||||
protected $context;
|
||||
|
||||
public function __construct(\Is_themecore $module)
|
||||
{
|
||||
$this->module = $module;
|
||||
$this->context = \Context::getContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAvailableHooks()
|
||||
{
|
||||
return static::HOOK_LIST;
|
||||
}
|
||||
}
|
||||
45
is_themecore/is_themecore/src/Hook/Assets.php
Normal file
45
is_themecore/is_themecore/src/Hook/Assets.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Hook;
|
||||
|
||||
use Oksydan\Module\IsThemeCore\Core\ThemeAssets\ThemeAssetConfigProvider;
|
||||
use Oksydan\Module\IsThemeCore\Core\ThemeAssets\ThemeAssetsRegister;
|
||||
|
||||
class Assets extends AbstractHook
|
||||
{
|
||||
public const HOOK_LIST = [
|
||||
'actionFrontControllerSetMedia',
|
||||
'actionProductSearchAfter',
|
||||
];
|
||||
|
||||
/**
|
||||
* Removing ps_faceted search module assets
|
||||
*/
|
||||
public function hookActionProductSearchAfter(): void
|
||||
{
|
||||
$this->context->controller->unregisterJavascript('facetedsearch_front');
|
||||
$this->context->controller->unregisterStylesheet('facetedsearch_front');
|
||||
|
||||
$needsJQueryUi = \Module::isEnabled('pm_advancedsearch4') && $this->context->controller instanceof \ProductListingFrontController;
|
||||
|
||||
if (!$needsJQueryUi) {
|
||||
$this->context->controller->unregisterJavascript('jquery-ui');
|
||||
$this->context->controller->unregisterStylesheet('jquery-ui');
|
||||
$this->context->controller->unregisterStylesheet('jquery-ui-theme');
|
||||
}
|
||||
}
|
||||
|
||||
public function hookActionFrontControllerSetMedia()
|
||||
{
|
||||
$assetsRegister = new ThemeAssetsRegister(
|
||||
new ThemeAssetConfigProvider(_PS_THEME_DIR_),
|
||||
$this->context
|
||||
);
|
||||
|
||||
$assetsRegister->registerThemeAssets();
|
||||
|
||||
\Media::addJsDef([
|
||||
'listDisplayAjaxUrl' => $this->context->link->getModuleLink($this->module->name, 'ajaxTheme'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
136
is_themecore/is_themecore/src/Hook/Header.php
Normal file
136
is_themecore/is_themecore/src/Hook/Header.php
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
namespace Oksydan\Module\IsThemeCore\Hook;
|
||||
|
||||
use Oksydan\Module\IsThemeCore\Core\Breadcrumbs\ThemeBreadcrumbs;
|
||||
use Oksydan\Module\IsThemeCore\Core\ListingDisplay\ThemeListDisplay;
|
||||
use Oksydan\Module\IsThemeCore\Core\Partytown\PartytownScript;
|
||||
use Oksydan\Module\IsThemeCore\Core\Partytown\PartytownScriptUriResolver;
|
||||
use Oksydan\Module\IsThemeCore\Core\StructuredData\BreadcrumbStructuredData;
|
||||
use Oksydan\Module\IsThemeCore\Core\StructuredData\ProductStructuredData;
|
||||
use Oksydan\Module\IsThemeCore\Core\StructuredData\ShopStructuredData;
|
||||
use Oksydan\Module\IsThemeCore\Core\StructuredData\StructuredDataInterface;
|
||||
use Oksydan\Module\IsThemeCore\Core\StructuredData\WebsiteStructuredData;
|
||||
use Oksydan\Module\IsThemeCore\Form\Settings\GeneralConfiguration;
|
||||
use Oksydan\Module\IsThemeCore\Form\Settings\WebpConfiguration;
|
||||
|
||||
class Header extends AbstractHook
|
||||
{
|
||||
public const HOOK_LIST = [
|
||||
'actionFrontControllerInitBefore',
|
||||
'displayHeader',
|
||||
];
|
||||
|
||||
public function hookActionFrontControllerInitBefore(): void
|
||||
{
|
||||
$themeListDisplay = new ThemeListDisplay();
|
||||
$this->context->smarty->assign([
|
||||
'listingDisplayType' => $themeListDisplay->getDisplay(),
|
||||
'preloadCss' => \Configuration::get(GeneralConfiguration::THEMECORE_PRELOAD_CSS),
|
||||
'webpEnabled' => \Configuration::get(WebpConfiguration::THEMECORE_WEBP_ENABLED),
|
||||
'loadPartytown' => (bool) \Configuration::get(GeneralConfiguration::THEMECORE_LOAD_PARTY_TOWN),
|
||||
'debugPartytown' => (bool) \Configuration::get(GeneralConfiguration::THEMECORE_DEBUG_PARTY_TOWN),
|
||||
]);
|
||||
}
|
||||
|
||||
public function hookDisplayHeader(): string
|
||||
{
|
||||
$themeListDisplay = new ThemeListDisplay();
|
||||
$breadcrumbs = (new ThemeBreadcrumbs())->getBreadcrumb();
|
||||
|
||||
if ($breadcrumbs['count']) {
|
||||
$this->context->smarty->assign([
|
||||
'breadcrumb' => $breadcrumbs,
|
||||
]);
|
||||
}
|
||||
|
||||
$this->context->smarty->assign([
|
||||
'jsonData' => $this->getStructuredData(),
|
||||
'partytownScript' => $this->getPartytownScript(),
|
||||
'partytownScriptUri' => $this->getPartytownScriptUri(),
|
||||
]);
|
||||
|
||||
return $this->module->fetch('module:is_themecore/views/templates/hook/head.tpl');
|
||||
}
|
||||
|
||||
private function getPartytownScriptUri(): string
|
||||
{
|
||||
try {
|
||||
$uriResolver = $this->module->get(PartytownScriptUriResolver::class);
|
||||
} catch (\Exception $e) {
|
||||
$uriResolver = null;
|
||||
}
|
||||
|
||||
if ($uriResolver) {
|
||||
return $uriResolver->getScriptUri();
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function getPartytownScript(): string
|
||||
{
|
||||
try {
|
||||
$partytownScript = $this->module->get(PartytownScript::class);
|
||||
} catch (\Exception $e) {
|
||||
$partytownScript = null;
|
||||
}
|
||||
|
||||
if ($partytownScript instanceof PartytownScript) {
|
||||
return $partytownScript->getScriptContent();
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function getStructuredData(): array
|
||||
{
|
||||
$dataArray = [];
|
||||
|
||||
if ($this->context->controller instanceof \ProductControllerCore && $this->context->controller->getProduct()->id !== null) {
|
||||
try {
|
||||
$productData = $this->module->get(ProductStructuredData::class);
|
||||
} catch (\Exception $e) {
|
||||
$productData = null;
|
||||
}
|
||||
|
||||
if ($productData instanceof StructuredDataInterface) {
|
||||
$dataArray[] = $productData->getFormattedData();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$breadcrumbData = $this->module->get(BreadcrumbStructuredData::class);
|
||||
} catch (\Exception $e) {
|
||||
$breadcrumbData = null;
|
||||
}
|
||||
|
||||
if ($breadcrumbData instanceof StructuredDataInterface) {
|
||||
$dataArray[] = $breadcrumbData->getFormattedData();
|
||||
}
|
||||
|
||||
try {
|
||||
$shopData = $this->module->get(ShopStructuredData::class);
|
||||
} catch (\Exception $e) {
|
||||
$shopData = null;
|
||||
}
|
||||
|
||||
if ($shopData instanceof StructuredDataInterface) {
|
||||
$dataArray[] = $shopData->getFormattedData();
|
||||
}
|
||||
|
||||
if ($this->context->controller->getPageName() === 'index') {
|
||||
try {
|
||||
$website = $this->module->get(WebsiteStructuredData::class);
|
||||
} catch (\Exception $e) {
|
||||
$website = null;
|
||||
}
|
||||
|
||||
if ($website instanceof StructuredDataInterface) {
|
||||
$dataArray[] = $website->getFormattedData();
|
||||
}
|
||||
}
|
||||
|
||||
return $dataArray;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user