From 3938b22aa596ba5168bd78d332e43947ec17166b Mon Sep 17 00:00:00 2001 From: Giel Berkers Date: Wed, 31 Jan 2018 10:55:09 +0100 Subject: [PATCH 1/5] GITHUB-144: Exclude .idea folder --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index cad17f35..ff665f69 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ vendor composer.phar composer.lock docker-compose.yml +.idea/ \ No newline at end of file From a94a7c6987025397d029788ca27b125b98885342 Mon Sep 17 00:00:00 2001 From: Giel Berkers Date: Wed, 31 Jan 2018 12:04:43 +0100 Subject: [PATCH 2/5] GITHUB-144: Allow for localizable properties --- .../ReferenceDataProcessor.php | 52 ++++++++++++++++--- .../Normalization/ReferenceDataProcessor.php | 36 ++++++++++--- Resources/config/connectors.yml | 1 + Updater/Updater.php | 6 ++- 4 files changed, 82 insertions(+), 13 deletions(-) diff --git a/Connector/Processor/Denormalization/ReferenceDataProcessor.php b/Connector/Processor/Denormalization/ReferenceDataProcessor.php index 1ede82e0..2b0f673c 100644 --- a/Connector/Processor/Denormalization/ReferenceDataProcessor.php +++ b/Connector/Processor/Denormalization/ReferenceDataProcessor.php @@ -10,6 +10,7 @@ use Akeneo\Component\StorageUtils\Detacher\ObjectDetacherInterface; use Akeneo\Component\StorageUtils\Updater\ObjectUpdaterInterface; use Doctrine\ORM\EntityManagerInterface; +use Pim\Bundle\CatalogBundle\Doctrine\ORM\Repository\LocaleRepository; use Pim\Bundle\CustomEntityBundle\Configuration\Registry; use Pim\Bundle\CustomEntityBundle\Entity\Repository\CustomEntityRepository; use Pim\Component\Connector\Exception\InvalidItemFromViolationsException; @@ -44,25 +45,32 @@ class ReferenceDataProcessor implements ItemProcessorInterface, StepExecutionAwa /** @var StepExecution */ protected $stepExecution; + /** @var LocaleRepository */ + protected $localeRepository; + /** + * ReferenceDataProcessor constructor. * @param Registry $confRegistry * @param EntityManagerInterface $em * @param ObjectUpdaterInterface $updater * @param ValidatorInterface $validator * @param ObjectDetacherInterface $detacher + * @param LocaleRepository $localeRepository */ public function __construct( Registry $confRegistry, EntityManagerInterface $em, ObjectUpdaterInterface $updater, ValidatorInterface $validator, - ObjectDetacherInterface $detacher + ObjectDetacherInterface $detacher, + LocaleRepository $localeRepository ) { - $this->confRegistry = $confRegistry; - $this->em = $em; - $this->updater = $updater; - $this->validator = $validator; - $this->detacher = $detacher; + $this->confRegistry = $confRegistry; + $this->em = $em; + $this->updater = $updater; + $this->validator = $validator; + $this->detacher = $detacher; + $this->localeRepository = $localeRepository; } /** @@ -75,6 +83,11 @@ public function process($item) } $entity = $this->findOrCreateObject($item); + + if ($entity instanceof \Pim\Bundle\CustomEntityBundle\Entity\AbstractTranslatableCustomEntity) { + $item = $this->denormalizeTranslations($item); + } + try { $this->updater->update($entity, $item); } catch (\Exception $e) { @@ -90,6 +103,33 @@ public function process($item) return $entity; } + /** + * @param array $item + * @return array + */ + protected function denormalizeTranslations(array $item):array + { + $activatedLocales = $this->localeRepository->getActivatedLocaleCodes(); + + foreach ($item as $key => $value) { + foreach ($activatedLocales as $localeCode) { + if (preg_match('/.*-(' . preg_quote($localeCode) . ')$/', $key, $match) === 1) { + $attributeCode = str_replace('-' . $localeCode, '', $key); + + $item[$attributeCode] = $item[$attributeCode] ?? []; + + if (!empty($value)) { + $item[$attributeCode][$localeCode] = $value; + } + + unset($item[$key]); + } + } + } + + return $item; + } + /** * {@inheritdoc} */ diff --git a/Connector/Processor/Normalization/ReferenceDataProcessor.php b/Connector/Processor/Normalization/ReferenceDataProcessor.php index 5c8eec38..305ec7ba 100644 --- a/Connector/Processor/Normalization/ReferenceDataProcessor.php +++ b/Connector/Processor/Normalization/ReferenceDataProcessor.php @@ -23,16 +23,15 @@ class ReferenceDataProcessor implements ItemProcessorInterface protected $normalizer; /** @var string[] */ - protected $skippedFields = ['id', 'created', 'updated']; + protected $skippedFields = ['id', 'created', 'updated', 'locale']; /** * @param PropertyAccessorInterface $propertyAccessor * @param NormalizerInterface $normalizer */ - public function __construct(PropertyAccessorInterface $propertyAccessor, NormalizerInterface $normalizer) - { + public function __construct(PropertyAccessorInterface $propertyAccessor, NormalizerInterface $normalizer) { $this->propertyAccessor = $propertyAccessor; - $this->normalizer = $normalizer; + $this->normalizer = $normalizer; } /** @@ -48,13 +47,38 @@ public function process($item) continue; } - $value = $this->propertyAccessor->getValue($item, $property->getName()); - $normalizedData[$property->getName()] = $this->normalizer->normalize($value, 'flat'); + $value = $this->normalizer + ->normalize($this->propertyAccessor->getValue($item, $property->getName()), 'flat'); + + if (is_array($value)) { + $normalizedData = array_merge($normalizedData, $this->normalizeArray($value)); + } else { + $normalizedData[$property->getName()] = $value; + } } return $normalizedData; } + /** + * @param array $values + * @return array + */ + protected function normalizeArray(array $values): array + { + $returnValue = []; + + foreach ($values as $key => $value) { + if (is_array($value)) { + $returnValue = array_merge($returnValue, $this->normalizeArray($value)); + } else { + $returnValue[$key] = $value; + } + } + + return $returnValue; + } + /** * @param array $skippedFields */ diff --git a/Resources/config/connectors.yml b/Resources/config/connectors.yml index bb325490..1b67db25 100644 --- a/Resources/config/connectors.yml +++ b/Resources/config/connectors.yml @@ -28,6 +28,7 @@ services: - '@pim_custom_entity.updater.custom_entity' - '@validator' - '@akeneo_storage_utils.doctrine.object_detacher' + - '@pim_catalog.repository.locale' pim_custom_entity.processor.normalization.reference_data: class: '%pim_custom_entity.processor.normalization.reference_data.class%' diff --git a/Updater/Updater.php b/Updater/Updater.php index 8af59830..46dfd5e3 100644 --- a/Updater/Updater.php +++ b/Updater/Updater.php @@ -192,8 +192,12 @@ protected function updateTranslations(AbstractTranslatableCustomEntity $referenc sprintf('Locale "%s" is not activated', $locale) ); } + $translation = $referenceData->getTranslation($locale); - $translation->setLabel($value); + + if ($this->propertyAccessor->isWritable($translation, $propertyPath)) { + $this->propertyAccessor->setValue($translation, $propertyPath, $value); + } } } From bb5a5bf29be2c835f47d2e9db41afe68085c1761 Mon Sep 17 00:00:00 2001 From: Giel Berkers Date: Wed, 31 Jan 2018 12:13:22 +0100 Subject: [PATCH 3/5] GITHUB-144: Added JavaScript and Templates for localization of custom entities --- Resources/config/requirejs.yml | 6 ++ .../public/js/form/localizable/switcher.js | 65 +++++++++++++ Resources/public/js/form/localizable/text.js | 94 +++++++++++++++++++ .../public/js/form/localizable/textarea.js | 10 ++ .../templates/form/localizable/text.html | 41 ++++++++ .../templates/form/localizable/textarea.html | 40 ++++++++ 6 files changed, 256 insertions(+) create mode 100644 Resources/public/js/form/localizable/switcher.js create mode 100644 Resources/public/js/form/localizable/text.js create mode 100644 Resources/public/js/form/localizable/textarea.js create mode 100644 Resources/public/templates/form/localizable/text.html create mode 100644 Resources/public/templates/form/localizable/textarea.html diff --git a/Resources/config/requirejs.yml b/Resources/config/requirejs.yml index 781a6144..8cf5404a 100644 --- a/Resources/config/requirejs.yml +++ b/Resources/config/requirejs.yml @@ -11,6 +11,12 @@ config: custom_entity/controller/edit: pimcustomentity/js/controller/custom_entity-edit custom_entity/fetcher: pimcustomentity/js/fetcher/custom_entity-fetcher custom_entity/remover/reference-data: pimcustomentity/js/remover/reference-data-remover + custom_entity/form/localizable/switcher: pimcustomentity/js/form/localizable/switcher + custom_entity/form/localizable/text: pimcustomentity/js/form/localizable/text + custom_entity/form/localizable/textarea: pimcustomentity/js/form/localizable/textarea + custom_entity/template/localizable/switcher: pimcustomentity/templates/form/localizable/switcher.html + custom_entity/template/localizable/text: pimcustomentity/templates/form/localizable/text.html + custom_entity/template/localizable/textarea: pimcustomentity/templates/form/localizable/textarea.html config: pim/fetcher-registry: diff --git a/Resources/public/js/form/localizable/switcher.js b/Resources/public/js/form/localizable/switcher.js new file mode 100644 index 00000000..1138374c --- /dev/null +++ b/Resources/public/js/form/localizable/switcher.js @@ -0,0 +1,65 @@ +define([ + 'pim/product-edit-form/locale-switcher', + 'underscore', + 'pim/i18n', + 'oro/translator', + 'pim/user-context', +], function (LocalSwitcher, _, i18n, __, UserContext) { + "use strict"; + + return LocalSwitcher.extend({ + currentLocaleCode: null, + + /** + * {@inheritdoc} + */ + configure: function () { + this.update(UserContext.get('uiLocale')); + }, + + /** + * Method triggered on the 'change locale' event + * + * @param {Object} event + */ + changeLocale: function (event) { + this.update(event.target.dataset.locale); + }, + + /** + * @param {String} localeCode + */ + update: function(localeCode) { + this.currentLocaleCode = localeCode; + this.getRoot().trigger('custom_entity:form:custom:translatable:switcher:change', {localeCode: localeCode}); + this.render(); + }, + + /** + * {@inheritdoc} + */ + render: function () { + this.getDisplayedLocales() + .done(function (locales) { + + if (!this.currentLocaleCode) { + this.currentLocaleCode = _.first(locales).code; + } + + this.$el.html( + this.template({ + locales: locales, + currentLocale: _.findWhere(locales, {code: this.currentLocaleCode}), + i18n: i18n, + displayInline: this.displayInline, + displayLabel: this.displayLabel, + label: __('pim_enrich.entity.product.meta.locale') + }) + ); + this.delegateEvents(); + }.bind(this)); + + return this; + }, + }); +}); \ No newline at end of file diff --git a/Resources/public/js/form/localizable/text.js b/Resources/public/js/form/localizable/text.js new file mode 100644 index 00000000..a2893328 --- /dev/null +++ b/Resources/public/js/form/localizable/text.js @@ -0,0 +1,94 @@ +define([ + 'pim/common/properties/translation', + 'custom_entity/template/localizable/text', + 'pim/form', + 'underscore', + 'jquery', + 'pim/user-context', +], function (Translation, template, BaseForm, _, $, UserContext) { + "use strict"; + + return Translation.extend({ + template: _.template(template), + localeCode: null, + + /** + * {@inheritdoc} + */ + configure: function () { + this.listenTo( + this.getRoot(), + 'pim_enrich:form:entity:pre_save', + this.onPreSave + ); + + this.listenTo( + this.getRoot(), + 'pim_enrich:form:entity:bad_request', + this.onValidationError + ); + + this.listenTo( + this.getRoot(), + 'pim_enrich:form:entity:locales_updated', + this.onLocalesUpdated.bind(this) + ); + + this.listenTo( + this.getRoot(), + 'custom_entity:form:custom:translatable:switcher:change', + this.onLocaleChanged.bind(this) + ); + + return $.when( + this.getLocales(true) + .then(function (locales) { + this.locales = locales; + this.localeCode = UserContext.get('uiLocale'); + }.bind(this)), + BaseForm.prototype.configure.apply(this, arguments) + ); + }, + + /** + * {@inheritdoc} + */ + render: function () { + this.$el.html(this.template({ + model: this.getFormData(), + locales: this.locales, + currentLocaleCode: this.localeCode, + errors: this.validationErrors, + label: this.config.label, + fieldName: this.config.fieldName, + isReadOnly: this.isReadOnly() + })); + + this.delegateEvents(); + this.renderExtensions(); + }, + + /** + * @param {{}} context + */ + onLocaleChanged: function (context) { + this.localeCode = context.localeCode; + this.render(); + }, + + /** + * @param {Object} event + */ + updateModel: function (event) { + var data = this.getFormData(); + + if (Array.isArray(data[this.config.fieldName])) { + data[this.config.fieldName] = {}; + } + + data[this.config.fieldName][event.target.dataset.locale] = event.target.value; + + this.setData(data); + }, + }); +}); \ No newline at end of file diff --git a/Resources/public/js/form/localizable/textarea.js b/Resources/public/js/form/localizable/textarea.js new file mode 100644 index 00000000..9dd8574b --- /dev/null +++ b/Resources/public/js/form/localizable/textarea.js @@ -0,0 +1,10 @@ +define([ + 'custom_entity/form/localizable/text', + 'custom_entity/template/localizable/textarea' +], function (Text, template) { + "use strict"; + + return Text.extend({ + template: _.template(template) + }); +}); \ No newline at end of file diff --git a/Resources/public/templates/form/localizable/text.html b/Resources/public/templates/form/localizable/text.html new file mode 100644 index 00000000..bbb55b70 --- /dev/null +++ b/Resources/public/templates/form/localizable/text.html @@ -0,0 +1,41 @@ +<% _.each(locales, function (locale) { %> + <% if (currentLocaleCode === locale.code) { %> +
+
+ + <% var langCode = locale.code.split('_')[1].toLowerCase() %> + + + + + + <%- langCode %> + + + + +
+
+
+
+ + > +
+ <% if (errors[locale.code]) { %> + + <% } %> +
+ <% } %> +<% }) %> diff --git a/Resources/public/templates/form/localizable/textarea.html b/Resources/public/templates/form/localizable/textarea.html new file mode 100644 index 00000000..af2bbc60 --- /dev/null +++ b/Resources/public/templates/form/localizable/textarea.html @@ -0,0 +1,40 @@ +<% _.each(locales, function (locale) { %> + <% if (currentLocaleCode === locale.code) { %> +
+
+ + <% var langCode = locale.code.split('_')[1].toLowerCase() %> + + + + + + <%- langCode %> + + + + +
+
+
+
+ +
+ <% if (errors[locale.code]) { %> + + <% } %> +
+ <% } %> +<% }) %> From 1d22902315af91ce456e50e21c7bdd5180084c15 Mon Sep 17 00:00:00 2001 From: Giel Berkers Date: Wed, 31 Jan 2018 15:25:41 +0100 Subject: [PATCH 4/5] [TASK] Remove unused requirejs dependency --- Resources/config/requirejs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/Resources/config/requirejs.yml b/Resources/config/requirejs.yml index 8cf5404a..359c66a3 100644 --- a/Resources/config/requirejs.yml +++ b/Resources/config/requirejs.yml @@ -14,7 +14,6 @@ config: custom_entity/form/localizable/switcher: pimcustomentity/js/form/localizable/switcher custom_entity/form/localizable/text: pimcustomentity/js/form/localizable/text custom_entity/form/localizable/textarea: pimcustomentity/js/form/localizable/textarea - custom_entity/template/localizable/switcher: pimcustomentity/templates/form/localizable/switcher.html custom_entity/template/localizable/text: pimcustomentity/templates/form/localizable/text.html custom_entity/template/localizable/textarea: pimcustomentity/templates/form/localizable/textarea.html From 916fd198b663c8a6b32c4a23a9a63e1c7f85cc1a Mon Sep 17 00:00:00 2001 From: Giel Berkers Date: Thu, 1 Feb 2018 09:50:58 +0100 Subject: [PATCH 5/5] GITHUB-144: Translate label --- Resources/public/js/form/localizable/text.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Resources/public/js/form/localizable/text.js b/Resources/public/js/form/localizable/text.js index a2893328..5fa0e440 100644 --- a/Resources/public/js/form/localizable/text.js +++ b/Resources/public/js/form/localizable/text.js @@ -5,7 +5,8 @@ define([ 'underscore', 'jquery', 'pim/user-context', -], function (Translation, template, BaseForm, _, $, UserContext) { + 'oro/translator', +], function (Translation, template, BaseForm, _, $, UserContext, __) { "use strict"; return Translation.extend({ @@ -59,7 +60,7 @@ define([ locales: this.locales, currentLocaleCode: this.localeCode, errors: this.validationErrors, - label: this.config.label, + label: __(this.config.label), fieldName: this.config.fieldName, isReadOnly: this.isReadOnly() }));