Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Controller/Adminhtml/Mass/Translate/Locked.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ public function execute()

$this->messageManager->addNotice(
__(
'This option is available in Magefan Translation Extra plan only. Please upgrade at <a href="%1" target="_blank">' .$url. '</a>.',
'This option is available in Magefan Translation Extra plan only.
Please <a href="%1" target="_blank">upgrade plan</a> to use this feature.',
$url . $params
)
);
Expand Down
2 changes: 2 additions & 0 deletions Model/Config/Source/ScheduleSource.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public function toOptionArray()
['value' => EntityManager::SECONDBLOG_CATEGORY_ID, 'label' => __('Magefan Second Blog Category')],
['value' => EntityManager::SECONDBLOG_AUTHOR_ID, 'label' => __('Magefan Second Blog Author')],

['value' => EntityManager::REVIEW_ID, 'label' => __('Product Review')],

['value' => EntityManager::TRANSLATE_ID, 'label' => __('Phrases from Search And Translate')]
];

Expand Down
8 changes: 7 additions & 1 deletion Model/EntityManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class EntityManager
public const SECONDBLOG_CATEGORY_ID = 13;
public const SECONDBLOG_AUTHOR_ID = 14;

public const REVIEW_ID = 15;

public const TYPE_CATEGORY = 'category';
public const TYPE_PRODUCT = 'product';
public const TYPE_PAGE = 'page';
Expand All @@ -45,6 +47,8 @@ class EntityManager
public const TYPE_SECONDBLOG_CATEGORY = 'secondblog_category';
public const TYPE_SECONDBLOG_AUTHOR = 'secondblog_author';

public const TYPE_REVIEW = 'review';

public const TYPES = [
self::TYPE_PRODUCT => self::PRODUCT_ID,
self::TYPE_CATEGORY => self::CATEGORY_ID,
Expand All @@ -61,7 +65,9 @@ class EntityManager
self::TYPE_SECONDBLOG_POST => self::SECONDBLOG_POST_ID,
self::TYPE_SECONDBLOG_TAG => self::SECONDBLOG_TAG_ID,
self::TYPE_SECONDBLOG_CATEGORY => self::SECONDBLOG_CATEGORY_ID,
self::TYPE_SECONDBLOG_AUTHOR => self::SECONDBLOG_AUTHOR_ID
self::TYPE_SECONDBLOG_AUTHOR => self::SECONDBLOG_AUTHOR_ID,

self::TYPE_REVIEW => self::REVIEW_ID
];

/**
Expand Down
25 changes: 25 additions & 0 deletions Model/TranslatableData.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ public function getList(?string $type = null): array
$data[EntityManager::TYPE_SECONDBLOG_AUTHOR] = $this->getBlogAuthorFields();
break;

case EntityManager::TYPE_REVIEW:
$data[EntityManager::TYPE_REVIEW] = $this->getReviewFields();
break;

default:
// If no type or unrecognized type is passed, return all
$data[EntityManager::TYPE_CATEGORY] = $this->getCatalogAttributes($this->categoryAttributeRepository);
Expand All @@ -157,6 +161,10 @@ public function getList(?string $type = null): array
$data[EntityManager::TYPE_SECONDBLOG_AUTHOR] = $this->getBlogAuthorFields();
}

if ($this->moduleManager->isEnabled('Magento_Review')) {
$data[EntityManager::TYPE_REVIEW] = $this->getReviewFields();
}

break;
}

Expand Down Expand Up @@ -351,6 +359,23 @@ private function getBlogCategoryFields(): array
];
}

/**
* Product review fields.
*
* Nickname is intentionally not listed - it holds the reviewer's name,
* which must not be machine translated. It is still stored per store view
* so it can be corrected manually.
*
* @return array[]
*/
private function getReviewFields(): array
{
return [
['label' => __('Summary of Review'), 'code' => 'title', 'type' => 'text'],
['label' => __('Review'), 'code' => 'detail', 'type' => 'text']
];
}

/**
* @return array[]
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ class Massaction
'secondblog_category_index',
'secondblog_tag_index',
'secondblogauthor_author_index',
'review_product_index',
'review_product_pending',
];

/**
Expand Down
68 changes: 68 additions & 0 deletions Plugin/Backend/Magento/Review/Block/Adminhtml/Edit.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php
/**
* Copyright © Magefan (support@magefan.com). All rights reserved.
* Please visit Magefan.com for license details (https://magefan.com/end-user-license-agreement).
*/

declare(strict_types=1);

namespace Magefan\Translation\Plugin\Backend\Magento\Review\Block\Adminhtml;

use Magefan\Translation\Model\Config;

/**
* Basic-tier teaser for the review "Auto Translate" button. Registered under the same
* plugin name as Magefan\TranslationExtra\Plugin\Backend\Magento\Review\Block\Adminhtml\Edit
* on the same target class - Extra loads after Translation, so its plugin definition
* overrides this one (real button instead of the upgrade popup) when Extra is installed.
*/
class Edit
{
/**
* @var Config
*/
private $config;

/**
* @param Config $config
*/
public function __construct(
Config $config
) {
$this->config = $config;
}

/**
* @param \Magento\Review\Block\Adminhtml\Edit $subject
* @param \Magento\Framework\View\Layout $layout
* @return array
*/
public function beforeSetLayout(
\Magento\Review\Block\Adminhtml\Edit $subject,
$layout
) {
if ($this->config->isEnabled() && $subject->getRequest()->getParam('id')) {
$subject->addButton(
'mftranslation_locked_button',
[
'label' => __('Auto Translate'),
'on_click' => $this->getOnClick(),
'class' => 'mf_auto_translate',
],
10
);
}

return [$layout];
}

/**
* @return string
*/
private function getOnClick(): string
{
return "require(['Magefan_Translation/js/mf-upgrade-plan-popup'], function(mfPopup){"
. "mfPopup('Extra', 'auto-translate', 'button');"
. "});";
}
}
183 changes: 183 additions & 0 deletions Plugin/Backend/Magento/Review/Block/Adminhtml/Edit/FormPlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
<?php
/**
* Copyright © Magefan (support@magefan.com). All rights reserved.
* Please visit Magefan.com for license details (https://magefan.com/end-user-license-agreement).
*/
declare(strict_types=1);

namespace Magefan\Translation\Plugin\Backend\Magento\Review\Block\Adminhtml\Edit;

use Magefan\Community\Api\SecureHtmlRendererInterface;
use Magefan\Translation\Model\Config;

/**
* Basic-tier teaser for the "Exclude From Auto Translation" review field. Registered
* under the same plugin name ("magefan_translation_plus_review_edit_form_use_default")
* as Magefan\TranslationPlus\Plugin\Magento\Review\Block\Adminhtml\Edit\FormPlugin on
* the same target class - TranslationPlus loads after Translation, so its plugin
* definition overrides this one entirely (the real, working fieldset) when Plus and
* Extra are both installed.
*
* That subclass also reuses addAutoTranslationFieldset() and
* addLockedExcludeAutoTranslationField() below for its own Extra-less fallback (Plus
* alone doesn't act on this flag - see that class), which is why they're protected
* rather than private.
*
* Mirrors Magefan\Translation\Plugin\Backend\Magento\Review\Block\Adminhtml\Edit (the
* "Auto Translate" button teaser) and the locked "Auto Translation (Extra)" fieldset
* category/product forms already show (Magefan_Translation/js/form/components/locked-fieldset) -
* this is the same idea, hand-built, because the review edit form is a legacy
* Widget\Form\Generic, not a UI component.
*/
class FormPlugin
{
/**
* @var Config
*/
protected $config;

/**
* @var SecureHtmlRendererInterface
*/
protected $mfSecureRenderer;

/**
* @param Config $config
* @param SecureHtmlRendererInterface $mfSecureRenderer
*/
public function __construct(
Config $config,
SecureHtmlRendererInterface $mfSecureRenderer
) {
$this->config = $config;
$this->mfSecureRenderer = $mfSecureRenderer;
}

/**
* @param \Magento\Review\Block\Adminhtml\Edit\Form $subject
* @param \Magento\Framework\Data\Form|null $form
* @return \Magento\Framework\Data\Form|null
*/
public function afterGetForm($subject, $form)
{
if (!$form || !$this->config->isEnabled()) {
return $form;
}

$this->addLockedExcludeAutoTranslationField($form);

return $form;
}

/**
* Adds the "Auto Translation (Extra)" fieldset right after review_details, or
* returns null without touching the form if it's already there (getForm() runs
* more than once per page render) or review_details itself isn't present to
* insert after.
*
* Shared between this class's own locked field below and
* Magefan\TranslationPlus\...\FormPlugin's real one, so the guard only lives once.
*
* @param \Magento\Framework\Data\Form $form
* @return \Magento\Framework\Data\Form\Element\Fieldset|null
*/
protected function addAutoTranslationFieldset($form)
{
if (!$form->getElement('review_details') || $form->getElement('mf_auto_translation')) {
return null;
}

return $form->addFieldset(
'mf_auto_translation',
['legend' => __('Auto Translation (Extra)')],
'review_details'
);
}

/**
* Adds the locked "Exclude From Auto Translation" field: the same toggle a working
* install would show, disabled, with a click-anywhere overlay that opens the
* upgrade popup instead of doing anything.
*
* @param \Magento\Framework\Data\Form $form
* @return void
*/
protected function addLockedExcludeAutoTranslationField($form)
{
$fieldset = $this->addAutoTranslationFieldset($form);

if (!$fieldset) {
return;
}

$fieldset->addField(
'mf_exclude_auto_translation',
'note',
[
'label' => __('Exclude From Auto Translation'),
'text' => $this->getLockedToggleHtml(),
]
);
}

/**
* Renders a locked stand-in for a toggle-switch field: the same toggle +
* "Use Default Value" markup the real, working field would use, both inert, with a
* transparent overlay on top that shows the upgrade popup on any click.
*
* @return string
*/
private function getLockedToggleHtml()
{
$yes = $this->escapeAttr(__('Yes'));
$no = $this->escapeAttr(__('No'));

return '<div style="position:relative;display:inline-block;">'
. '<div>'
. '<div class="admin__actions-switch" data-role="switcher">'
. '<input type="checkbox" class="admin__actions-switch-checkbox" id="mf_locked_toggle" disabled="disabled" />'
. '<label class="admin__actions-switch-label" for="mf_locked_toggle">'
. '<span class="admin__actions-switch-text" data-text-on="' . $yes . '" data-text-off="' . $no . '"></span>'
. '</label>'
. '</div>'
. '<label style="display:block;margin-top:8px;font-weight:normal;">'
. '<input type="checkbox" class="checkbox" checked="checked" disabled="disabled" /> '
. $this->escapeAttr(__('Use Default Value'))
. '</label>'
. '</div>'
. '<div class="mf-locked-toggle-overlay"'
. ' style="position:absolute;top:0;left:0;right:0;bottom:0;cursor:pointer;z-index:1;"></div>'
. '</div>'
. $this->getLockedClickHandlerHtml();
}

/**
* @param \Magento\Framework\Phrase $phrase
* @return string
*/
private function escapeAttr($phrase)
{
return htmlspecialchars((string)$phrase, ENT_QUOTES);
}

/**
* Delegated on document rather than an inline onclick attribute - inline handlers
* are blocked under a strict CSP. classList.contains() rather than a className
* substring match: className is an SVGAnimatedString (no .indexOf) on an SVG
* target, e.g. any of the admin icon sprites elsewhere on the page.
*
* @return string
*/
private function getLockedClickHandlerHtml()
{
$script = "document.addEventListener('click', function (event) {"
. "if (!event.target.classList || !event.target.classList.contains('mf-locked-toggle-overlay')) {"
. " return; }"
. "require(['Magefan_Translation/js/mf-upgrade-plan-popup'], function (mfPopup) {"
. " mfPopup('Extra', 'review-edit', 'fieldset');"
. "});"
. "});";

return $this->mfSecureRenderer->renderTag('script', [], $script, false);
}
}
6 changes: 6 additions & 0 deletions Setup/Recurring.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ public function install(SchemaSetupInterface $setup, ModuleContextInterface $con
'magefan_second_blog_category',
'magefan_second_blog_tag',
'magefan_second_blog_author',
// "review" and not "review_detail": ResourceModel\Review::_afterSave()
// writes only title/detail/nickname to review_detail, while the main table
// is saved through AbstractDb::_prepareDataForSave() and picks the column up.
// Handled here rather than in db_schema.xml because Magento_Review can be
// disabled, and declaring a column on an undeclared table breaks upgrade.
'review',
];

$localizationTables = [
Expand Down
16 changes: 16 additions & 0 deletions etc/adminhtml/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,20 @@
<type name="Magento\Backend\Block\Widget\Grid\Massaction">
<plugin name="add_auto_translate_mass_actions_to_blog_grid" type="Magefan\Translation\Plugin\Backend\Magento\Backend\Block\Widget\Grid\Massaction" sortOrder="5" disabled="false"/>
</type>

<!-- Widget\Grid\Extended-based grids (e.g. the review grid) default to this
massaction block instead of the plain Massaction class above. -->
<type name="Magento\Backend\Block\Widget\Grid\Massaction\Extended">
<plugin name="add_auto_translate_mass_actions_to_extended_grid" type="Magefan\Translation\Plugin\Backend\Magento\Backend\Block\Widget\Grid\Massaction" sortOrder="5" disabled="false"/>
</type>

<type name="Magento\Review\Block\Adminhtml\Edit">
<plugin name="add_auto_translation_button_on_review_form" type="Magefan\Translation\Plugin\Backend\Magento\Review\Block\Adminhtml\Edit" sortOrder="5" disabled="false"/>
</type>

<!-- Teaser only - Magefan\TranslationPlus\Plugin\Magento\Review\Block\Adminhtml\Edit\FormPlugin
registers under this same plugin name and fully overrides it when Plus is installed. -->
<type name="Magento\Review\Block\Adminhtml\Edit\Form">
<plugin name="magefan_translation_plus_review_edit_form_use_default" type="Magefan\Translation\Plugin\Backend\Magento\Review\Block\Adminhtml\Edit\FormPlugin" sortOrder="10" disabled="false"/>
</type>
</config>
Loading