409 lines
12 KiB
PHP
409 lines
12 KiB
PHP
<?php namespace RainLab\Translate\Classes;
|
|
|
|
use App;
|
|
use Str;
|
|
use File;
|
|
use Cms\Classes\Page;
|
|
use Cms\Classes\Content;
|
|
use System\Classes\MailManager;
|
|
use System\Classes\PluginManager;
|
|
use RainLab\Translate\Models\Message;
|
|
use RainLab\Translate\Models\Locale as LocaleModel;
|
|
use RainLab\Translate\Classes\Translator;
|
|
use RainLab\Translate\Classes\ThemeScanner;
|
|
use Exception;
|
|
|
|
/**
|
|
* EventRegistry for bootstrapping events
|
|
*
|
|
* @package october\system
|
|
* @author Alexey Bobkov, Samuel Georges
|
|
*/
|
|
class EventRegistry
|
|
{
|
|
use \October\Rain\Support\Traits\Singleton;
|
|
|
|
//
|
|
// Editor
|
|
//
|
|
|
|
/**
|
|
* extendEditorPageToolbar
|
|
*/
|
|
public function extendEditorPageToolbar($dataHolder)
|
|
{
|
|
if (!LocaleModel::isAvailable()) {
|
|
return;
|
|
}
|
|
|
|
$locales = LocaleModel::listAvailable();
|
|
$defaultLocale = LocaleModel::getDefault()->code ?? null;
|
|
|
|
$properties = [];
|
|
foreach ($locales as $locale => $label) {
|
|
if ($locale == $defaultLocale) {
|
|
continue;
|
|
}
|
|
|
|
$properties[] = [
|
|
'property' => 'localeUrl.'.$locale,
|
|
'title' => 'cms::lang.editor.url',
|
|
'tab' => $label,
|
|
'type' => 'string',
|
|
];
|
|
|
|
$properties[] = [
|
|
'property' => 'localeTitle.'.$locale,
|
|
'title' => 'cms::lang.editor.title',
|
|
'tab' => $label,
|
|
'type' => 'string',
|
|
];
|
|
|
|
$properties[] = [
|
|
'property' => 'localeDescription.'.$locale,
|
|
'title' => 'cms::lang.editor.description',
|
|
'tab' => $label,
|
|
'type' => 'text',
|
|
];
|
|
|
|
$properties[] = [
|
|
'property' => 'localeMeta_title.'.$locale,
|
|
'title' => 'cms::lang.editor.meta_title',
|
|
'tab' => $label,
|
|
'type' => 'string',
|
|
];
|
|
|
|
$properties[] = [
|
|
'property' => 'localeMeta_description.'.$locale,
|
|
'title' => 'cms::lang.editor.meta_description',
|
|
'tab' => $label,
|
|
'type' => 'text',
|
|
];
|
|
}
|
|
|
|
$dataHolder->buttons[] = [
|
|
'button' => 'rainlab.translate::lang.plugin.name',
|
|
'icon' => 'octo-icon-globe',
|
|
'popupTitle' => 'Translate Page Properties',
|
|
'properties' => $properties
|
|
];
|
|
}
|
|
|
|
//
|
|
// Utility
|
|
//
|
|
|
|
/**
|
|
* registerFormFieldReplacements
|
|
*/
|
|
public function registerFormFieldReplacements($widget)
|
|
{
|
|
// Replace with ML Controls for translatable attributes
|
|
$this->registerModelTranslation($widget);
|
|
|
|
// Handle URL translations
|
|
$this->registerPageUrlTranslation($widget);
|
|
|
|
// Handle RainLab.Pages MenuItem translations
|
|
if (PluginManager::instance()->exists('RainLab.Pages')) {
|
|
$this->registerMenuItemTranslation($widget);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* registerMenuItemTranslation for RainLab.Pages MenuItem data
|
|
* @param Backend\Widgets\Form $widget
|
|
*/
|
|
public function registerMenuItemTranslation($widget)
|
|
{
|
|
if ($widget->model instanceof \RainLab\Pages\Classes\MenuItem) {
|
|
$defaultLocale = LocaleModel::getDefault();
|
|
$availableLocales = LocaleModel::listAvailable();
|
|
$fieldsToTranslate = ['title', 'url'];
|
|
|
|
// Replace specified fields with multilingual versions
|
|
foreach ($fieldsToTranslate as $fieldName) {
|
|
$widget->fields[$fieldName]['type'] = 'mltext';
|
|
|
|
foreach ($availableLocales as $code => $locale) {
|
|
if (!$defaultLocale || $defaultLocale->code === $code) {
|
|
continue;
|
|
}
|
|
|
|
// Add data locker fields for the different locales under the `viewBag[locale]` property
|
|
$widget->fields["viewBag[locale][$code][$fieldName]"] = [
|
|
'cssClass' => 'hidden',
|
|
'attributes' => [
|
|
'data-locale' => $code,
|
|
'data-field-name' => $fieldName,
|
|
],
|
|
];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Translate URLs
|
|
//
|
|
|
|
/**
|
|
* registerPageUrlTranslation
|
|
*/
|
|
public function registerPageUrlTranslation($widget)
|
|
{
|
|
if (!$model = $widget->model) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
$model instanceof Page &&
|
|
isset($widget->fields['settings[url]'])
|
|
) {
|
|
$widget->fields['settings[url]']['type'] = 'mltext';
|
|
}
|
|
elseif (
|
|
$model instanceof \RainLab\Pages\Classes\Page &&
|
|
isset($widget->fields['viewBag[url]'])
|
|
) {
|
|
$widget->fields['viewBag[url]']['type'] = 'mltext';
|
|
}
|
|
}
|
|
|
|
//
|
|
// Translatable behavior
|
|
//
|
|
|
|
/**
|
|
* registerModelTranslation automatically replaces form fields for multi-lingual equivalents
|
|
*/
|
|
public function registerModelTranslation($widget)
|
|
{
|
|
if ($widget->isNested) {
|
|
return;
|
|
}
|
|
|
|
if (!$model = $widget->model) {
|
|
return;
|
|
}
|
|
|
|
if (!method_exists($model, 'isClassExtendedWith')) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
!$model->isClassExtendedWith(\RainLab\Translate\Behaviors\TranslatableModel::class) &&
|
|
!$model->isClassExtendedWith(\RainLab\Translate\Behaviors\TranslatablePage::class) &&
|
|
!$model->isClassExtendedWith(\RainLab\Translate\Behaviors\TranslatableCmsObject::class)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
if (!$model->hasTranslatableAttributes()) {
|
|
return;
|
|
}
|
|
|
|
if (!empty($widget->fields)) {
|
|
$widget->fields = $this->processFormMLFields($widget->fields, $model);
|
|
}
|
|
|
|
if (!empty($widget->tabs['fields'])) {
|
|
$widget->tabs['fields'] = $this->processFormMLFields($widget->tabs['fields'], $model);
|
|
}
|
|
|
|
if (!empty($widget->secondaryTabs['fields'])) {
|
|
$widget->secondaryTabs['fields'] = $this->processFormMLFields($widget->secondaryTabs['fields'], $model);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* processFormMLFields function to replace standard fields with multi-lingual equivalents
|
|
* @param array $fields
|
|
* @param Model $model
|
|
* @return array
|
|
*/
|
|
protected function processFormMLFields($fields, $model)
|
|
{
|
|
$typesMap = [
|
|
'text' => 'mltext',
|
|
'textarea' => 'mltextarea',
|
|
'richeditor' => 'mlricheditor',
|
|
'markdown' => 'mlmarkdowneditor',
|
|
'repeater' => 'mlrepeater',
|
|
'nestedform' => 'mlnestedform',
|
|
'mediafinder' => 'mlmediafinder',
|
|
];
|
|
|
|
$translatable = array_flip($model->getTranslatableAttributes());
|
|
|
|
/*
|
|
* Special: A custom field "markup_html" is used for Content templates.
|
|
*/
|
|
if ($model instanceof Content && array_key_exists('markup', $translatable)) {
|
|
$translatable['markup_html'] = true;
|
|
}
|
|
|
|
foreach ($fields as $name => $config) {
|
|
if (!array_key_exists($name, $translatable)) {
|
|
continue;
|
|
}
|
|
|
|
$type = array_get($config, 'type', 'text');
|
|
|
|
if (array_key_exists($type, $typesMap)) {
|
|
$fields[$name]['type'] = $typesMap[$type];
|
|
}
|
|
}
|
|
|
|
return $fields;
|
|
}
|
|
|
|
//
|
|
// Theme
|
|
//
|
|
|
|
/**
|
|
* importMessagesFromTheme
|
|
*/
|
|
public function importMessagesFromTheme($themeCode)
|
|
{
|
|
try {
|
|
(new ThemeScanner)->scanThemeConfigForMessages($themeCode);
|
|
}
|
|
catch (Exception $ex) {}
|
|
}
|
|
|
|
//
|
|
// CMS objects
|
|
//
|
|
|
|
/**
|
|
* setMessageContext for translation caching.
|
|
*/
|
|
public function setMessageContext($page)
|
|
{
|
|
if (!$page) {
|
|
return;
|
|
}
|
|
|
|
$translator = Translator::instance();
|
|
|
|
Message::setContext($translator->getLocale(), $page->url);
|
|
}
|
|
|
|
/**
|
|
* findTranslatedContentFile adds language suffixes to content files.
|
|
* @return string|null
|
|
*/
|
|
public function findTranslatedContentFile($controller, $fileName)
|
|
{
|
|
if (!strlen(File::extension($fileName))) {
|
|
$fileName .= '.htm';
|
|
}
|
|
|
|
/*
|
|
* Splice the active locale in to the filename
|
|
* - content.htm -> content.en.htm
|
|
*/
|
|
$locale = Translator::instance()->getLocale();
|
|
$fileName = substr_replace($fileName, '.'.$locale, strrpos($fileName, '.'), 0);
|
|
if (($content = Content::loadCached($controller->getTheme(), $fileName)) !== null) {
|
|
return $content;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Static pages
|
|
//
|
|
|
|
/**
|
|
* pruneTranslatedContentTemplates removes localized content files from templates collection
|
|
* @param \October\Rain\Database\Collection $templates
|
|
* @return \October\Rain\Database\Collection
|
|
*/
|
|
public function pruneTranslatedContentTemplates($templates)
|
|
{
|
|
$locales = LocaleModel::listAvailable();
|
|
|
|
$extensions = array_map(function($ext) {
|
|
return '.'.$ext;
|
|
}, array_keys($locales));
|
|
|
|
return $templates->filter(function($template) use ($extensions) {
|
|
return !Str::endsWith($template->getBaseFileName(), $extensions);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* findLocalizedMailViewContent adds language suffixes to mail view files.
|
|
* @param \October\Rain\Mail\Mailer $mailer
|
|
* @param \Illuminate\Mail\Message $message
|
|
* @param string $view
|
|
* @param array $data
|
|
* @param string $raw
|
|
* @param string $plain
|
|
* @return bool|void Will return false if the translation process successfully replaced the original message with a translated version to prevent the original version from being processed.
|
|
*/
|
|
public function findLocalizedMailViewContent($mailer, $message, $view, $data, $raw, $plain)
|
|
{
|
|
// Raw content cannot be localized at this level
|
|
if (!empty($raw)) {
|
|
return;
|
|
}
|
|
|
|
// Get the locale to use for this template
|
|
$locale = !empty($data['_current_locale']) ? $data['_current_locale'] : App::getLocale();
|
|
|
|
$factory = $mailer->getViewFactory();
|
|
|
|
if (!empty($view)) {
|
|
$view = $this->getLocalizedView($factory, $view, $locale);
|
|
}
|
|
|
|
if (!empty($plain)) {
|
|
$plain = $this->getLocalizedView($factory, $plain, $locale);
|
|
}
|
|
|
|
$code = $view ?: $plain;
|
|
if (empty($code)) {
|
|
return null;
|
|
}
|
|
|
|
$plainOnly = empty($view);
|
|
|
|
if (MailManager::instance()->addContentToMailer($message, $code, $data, $plainOnly)) {
|
|
// the caller who fired the event is expecting a FALSE response to halt the event
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* getLocalizedView searches mail view files based on locale
|
|
* @param \October\Rain\Mail\Mailer $mailer
|
|
* @param \Illuminate\Mail\Message $message
|
|
* @param string $code
|
|
* @param string $locale
|
|
* @return string|null
|
|
*/
|
|
public function getLocalizedView($factory, $code, $locale)
|
|
{
|
|
$locale = strtolower($locale);
|
|
|
|
$searchPaths[] = $locale;
|
|
|
|
if (str_contains($locale, '-')) {
|
|
list($lang) = explode('-', $locale);
|
|
$searchPaths[] = $lang;
|
|
}
|
|
|
|
foreach ($searchPaths as $path) {
|
|
$localizedView = sprintf('%s-%s', $code, $path);
|
|
|
|
if ($factory->exists($localizedView)) {
|
|
return $localizedView;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|