607 lines
13 KiB
PHP
607 lines
13 KiB
PHP
<?php
|
|
|
|
namespace Bitrix\Translate;
|
|
|
|
use Bitrix\Main;
|
|
use Bitrix\Main\Error;
|
|
use Bitrix\Main\Localization;
|
|
use Bitrix\Main\Localization\Loc;
|
|
use Bitrix\Translate;
|
|
|
|
|
|
abstract class ComponentBase
|
|
extends \CBitrixComponent
|
|
implements Translate\IErrorable
|
|
{
|
|
use Translate\Error;
|
|
use Translate\Warning;
|
|
|
|
const STATUS_SUCCESS = 'success';
|
|
const STATUS_DENIED = 'denied';
|
|
const STATUS_ERROR = 'error';
|
|
|
|
const TEMPLATE_ERROR = 'error';
|
|
|
|
/** @var string */
|
|
protected $path = '';
|
|
|
|
/** @var int Session tab counter. */
|
|
protected $tabId = 0;
|
|
|
|
|
|
/**
|
|
* @return boolean
|
|
*/
|
|
protected function checkModuleAvailability()
|
|
{
|
|
if (!Main\Loader::includeModule('translate'))
|
|
{
|
|
if ($this->isAjaxRequest())
|
|
{
|
|
$this->sendJsonResponse(new Error('Module "translate" is not installed.', self::STATUS_ERROR));
|
|
}
|
|
else
|
|
{
|
|
$this->addError(new Error('Module "translate" is not installed.', self::STATUS_ERROR));
|
|
$this->includeComponentTemplate(self::TEMPLATE_ERROR);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Checks if user has permission to view language file.
|
|
* @param \CUser $user User to check permissions.
|
|
* @return boolean
|
|
*/
|
|
protected function hasUserPermissionView($user)
|
|
{
|
|
return Translate\Permission::canView($user);
|
|
}
|
|
|
|
/**
|
|
* Checks if user has permission to edit language file.
|
|
* @param \CUser $user User to check permissions.
|
|
* @return boolean
|
|
*/
|
|
protected function hasUserPermissionEdit($user)
|
|
{
|
|
return Translate\Permission::canEdit($user);
|
|
}
|
|
/**
|
|
* Checks if user has permission to edit source language file.
|
|
* @param \CUser $user User to check permissions.
|
|
* @return boolean
|
|
*/
|
|
protected function hasUserPermissionEditSource($user)
|
|
{
|
|
return Translate\Permission::canEditSource($user);
|
|
}
|
|
|
|
/**
|
|
* Checks if user has permission to view language file.
|
|
* @return boolean
|
|
*/
|
|
protected function checkPermissionView()
|
|
{
|
|
if (!$this->hasUserPermissionView($this->getUser()))
|
|
{
|
|
if ($this->isAjaxRequest())
|
|
{
|
|
$this->sendJsonResponse(new Error(Loc::getMessage('TRANSLATE_FILTER_ERROR_ACCESS_DENIED'), self::STATUS_DENIED));
|
|
}
|
|
else
|
|
{
|
|
$this->addError(new Error(Loc::getMessage('TRANSLATE_FILTER_ERROR_ACCESS_DENIED'), self::STATUS_DENIED));
|
|
$this->includeComponentTemplate(self::TEMPLATE_ERROR);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Checks if user has permission to edit language file.
|
|
* @return boolean
|
|
*/
|
|
protected function checkPermissionEdit()
|
|
{
|
|
if (!$this->hasUserPermissionEdit($this->getUser()))
|
|
{
|
|
if ($this->isAjaxRequest())
|
|
{
|
|
$this->sendJsonResponse(new Error(Loc::getMessage('TRANSLATE_FILTER_ERROR_WRITING_RIGHTS'), self::STATUS_DENIED));
|
|
}
|
|
else
|
|
{
|
|
$this->addError(new Error(Loc::getMessage('TRANSLATE_FILTER_ERROR_WRITING_RIGHTS'), self::STATUS_DENIED));
|
|
$this->includeComponentTemplate(self::TEMPLATE_ERROR);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Checks if user has permission to edit source language file.
|
|
* @return boolean
|
|
*/
|
|
protected function checkPermissionEditPhp()
|
|
{
|
|
if (!$this->hasUserPermissionEditSource($this->getUser()))
|
|
{
|
|
if ($this->isAjaxRequest())
|
|
{
|
|
$this->sendJsonResponse(new Error(Loc::getMessage('TRANSLATE_FILTER_ERROR_PHP_EDIT_RIGHTS'), self::STATUS_DENIED));
|
|
}
|
|
else
|
|
{
|
|
$this->addError(new Error(Loc::getMessage('TRANSLATE_FILTER_ERROR_PHP_EDIT_RIGHTS'), self::STATUS_DENIED));
|
|
$this->includeComponentTemplate(self::TEMPLATE_ERROR);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Checks some mysql config variables.
|
|
* @return void
|
|
*/
|
|
protected function checkModuleStepper(): void
|
|
{
|
|
$stepper = \Bitrix\Main\Update\Stepper::getHtml('translate', Loc::getMessage('TRANSLATE_INDEX_STEPPER'));
|
|
if (!empty($stepper))
|
|
{
|
|
$this->arResult['STEPPER'] = $stepper;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks FTS if tables exists.
|
|
* @return void
|
|
*/
|
|
protected function checkFtsTables(): void
|
|
{
|
|
Translate\Index\Internals\PhraseFts::checkTables();
|
|
}
|
|
|
|
/**
|
|
* Checks some mysql config variables.
|
|
* @return void
|
|
*/
|
|
protected function checkMysqlConfig(): void
|
|
{
|
|
$majorVersion = (int)\mb_substr(\Bitrix\Main\Application::getConnection()->getVersion()[0], 0, 1);
|
|
|
|
if ($majorVersion >= 8)
|
|
{
|
|
$conf = Main\Application::getConnection()->query("SHOW VARIABLES LIKE 'regexp_time_limit'")->fetch();
|
|
if ($conf['Variable_name'] == 'regexp_time_limit')
|
|
{
|
|
if ((int)$conf['Value'] <= 0)
|
|
{
|
|
$this->addWarning(new Error(Loc::getMessage('TRANSLATE_MYSQL_CONFIG_ERROR_REGEXP_TIME_LIMIT'), self::STATUS_ERROR));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
protected function prepareParams()
|
|
{
|
|
$params =& $this->getParams();
|
|
if (empty($params['CURRENT_LANG']))
|
|
{
|
|
$params['CURRENT_LANG'] = Loc::getCurrentLang();
|
|
}
|
|
if (empty($params['LIST_PATH']))
|
|
{
|
|
$params['LIST_PATH'] = '/bitrix/admin/translate_list.php';
|
|
}
|
|
if (empty($params['EDIT_PATH']))
|
|
{
|
|
$params['EDIT_PATH'] = '/bitrix/admin/translate_edit.php';
|
|
}
|
|
if (empty($params['SHOW_SOURCE_PATH']))
|
|
{
|
|
$params['SHOW_SOURCE_PATH'] = '/bitrix/admin/translate_show_php.php';
|
|
}
|
|
if (empty($params['EDIT_SOURCE_PATH']))
|
|
{
|
|
$params['EDIT_SOURCE_PATH'] = '/bitrix/admin/translate_edit_php.php';
|
|
}
|
|
|
|
$params['SET_TITLE'] = isset($params['SET_TITLE']) ? $params['SET_TITLE'] === 'Y' : true;
|
|
|
|
$this->arResult['IS_AJAX_REQUEST'] = $this->isAjaxRequest();
|
|
|
|
$this->arResult['ALLOW_VIEW'] = $this->hasUserPermissionView($this->getUser());
|
|
$this->arResult['ALLOW_EDIT'] = $this->hasUserPermissionEdit($this->getUser());
|
|
$this->arResult['ALLOW_EDIT_SOURCE'] = $this->hasUserPermissionEditSource($this->getUser());
|
|
}
|
|
|
|
/**
|
|
* Moves current language to the first position.
|
|
*
|
|
* @param string[] $languageList
|
|
* @param string $currentLangId
|
|
*
|
|
* @return string[]
|
|
*/
|
|
protected function rearrangeLanguages($languageList, $currentLangId)
|
|
{
|
|
$inx = \array_search($currentLangId, $languageList, true);
|
|
if ($inx !== false)
|
|
{
|
|
unset($languageList[$inx]);
|
|
}
|
|
|
|
\array_unshift($languageList, $currentLangId);
|
|
|
|
return $languageList;
|
|
}
|
|
|
|
/**
|
|
* @return string[]
|
|
*/
|
|
protected function getLanguages()
|
|
{
|
|
static $languagesList;
|
|
|
|
if (empty($languagesList))
|
|
{
|
|
$languagesList = Translate\Config::getEnabledLanguages();
|
|
}
|
|
|
|
return $languagesList;
|
|
}
|
|
|
|
/**
|
|
* Returns list of language names from the site settings.
|
|
*
|
|
* @param string[] $languageIds Languages list to get name.
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function getLanguagesTitle($languageIds)
|
|
{
|
|
$titles = Translate\Config::getLanguagesTitle($languageIds);
|
|
array_walk($titles, function(&$title, $langId) { $title = "{$title} ({$langId})"; });
|
|
|
|
return $titles;
|
|
}
|
|
|
|
/**
|
|
* Return languages compatible by their encoding.
|
|
*
|
|
* @return string[]
|
|
*/
|
|
protected function getCompatibleLanguages()
|
|
{
|
|
static $languages = array();
|
|
if (empty($languages))
|
|
{
|
|
$currentEncoding = Localization\Translation::getCurrentEncoding();
|
|
$currentLang = Loc::getCurrentLang();
|
|
$limitEncoding = !($currentEncoding == 'utf-8' || Localization\Translation::useTranslationRepository());
|
|
|
|
$isEncodingCompatible = function ($langId) use ($limitEncoding, $currentEncoding, $currentLang)
|
|
{
|
|
$compatible = true;
|
|
if ($limitEncoding)
|
|
{
|
|
$compatible = (
|
|
$langId == $currentLang ||
|
|
Translate\Config::getCultureEncoding($langId) == $currentEncoding ||
|
|
$langId == 'en'
|
|
);
|
|
}
|
|
|
|
return $compatible;
|
|
};
|
|
|
|
$enabledLanguages = $this->getLanguages();
|
|
foreach ($enabledLanguages as $langId)
|
|
{
|
|
if ($limitEncoding && !$isEncodingCompatible($langId))
|
|
{
|
|
continue;
|
|
}
|
|
$languages[] = $langId;
|
|
}
|
|
}
|
|
|
|
return $languages;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
protected function detectTabId()
|
|
{
|
|
$tabId = $this->request->get('tabId');
|
|
if (!empty($tabId) && (int)$tabId > 0)
|
|
{
|
|
$this->tabId = (int)$tabId;
|
|
}
|
|
elseif ($this->isAjaxRequest())
|
|
{
|
|
$this->tabId = Translate\Filter::getTabId(false);
|
|
}
|
|
else
|
|
{
|
|
$this->tabId = Translate\Filter::getTabId();
|
|
}
|
|
|
|
return $this->tabId;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
protected function detectStartingPath(?string $path = ''): string
|
|
{
|
|
$home = Translate\Config::getDefaultPath();
|
|
|
|
$initPaths = Translate\Config::getInitPath();
|
|
if (count($initPaths) > 0)
|
|
{
|
|
$home = $initPaths[0];
|
|
if (!empty($path))
|
|
{
|
|
foreach ($initPaths as $initPath)
|
|
{
|
|
if (\mb_strpos($path, $initPath) === 0)
|
|
{
|
|
$home = $initPath;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $home;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns component calling params by reference.
|
|
* @return array
|
|
*/
|
|
public function &getParams()
|
|
{
|
|
return $this->arParams;
|
|
}
|
|
|
|
/**
|
|
* Returns component resulting array by reference.
|
|
* @return array
|
|
*/
|
|
public function &getResult()
|
|
{
|
|
return $this->arResult;
|
|
}
|
|
|
|
|
|
/**
|
|
* @return \CUser
|
|
*/
|
|
protected function getUser()
|
|
{
|
|
/** @global \CUser $USER */
|
|
global $USER;
|
|
return $USER;
|
|
}
|
|
|
|
/**
|
|
* @return \CMain
|
|
*/
|
|
protected function getApplication()
|
|
{
|
|
/** @global \CMain $APPLICATION */
|
|
global $APPLICATION;
|
|
return $APPLICATION;
|
|
}
|
|
|
|
/**
|
|
* Returns whether this is an AJAX (XMLHttpRequest) request.
|
|
* @return boolean
|
|
*/
|
|
protected function isAjaxRequest()
|
|
{
|
|
return
|
|
($this->request->isAjaxRequest() || $this->request->get('AJAX_CALL') !== null) &&
|
|
$this->request->getRequestMethod() == 'POST';
|
|
}
|
|
|
|
/**
|
|
* Sends Json response to client.
|
|
*
|
|
* @param array|object|Main\Error $response Response to send.
|
|
*
|
|
* @throws Main\ArgumentException
|
|
* @return void
|
|
*/
|
|
protected function sendJsonResponse($response)
|
|
{
|
|
$this->getApplication()->restartBuffer();
|
|
|
|
$answer = Main\Application::getInstance()->getContext()->getResponse();
|
|
|
|
if ($response instanceof Main\Error)
|
|
{
|
|
$this->addError($response);
|
|
$response = array();
|
|
}
|
|
|
|
$response['result'] = true;
|
|
if ($this->hasErrors())
|
|
{
|
|
$answer->setStatus('500 Internal Server Error');
|
|
|
|
$response['status'] = self::STATUS_ERROR;
|
|
$errors = array();
|
|
foreach ($this->getErrors() as $error)
|
|
{
|
|
/** @var Main\Error $error */
|
|
$errors[] = array(
|
|
'message' => $error->getMessage(),
|
|
'code' => $error->getCode(),
|
|
);
|
|
}
|
|
$response['result'] = false;
|
|
$response['errors'] = $errors;
|
|
}
|
|
elseif (!isset($response['status']))
|
|
{
|
|
$response['status'] = self::STATUS_SUCCESS;
|
|
}
|
|
|
|
$answer->addHeader('Content-Type', 'application/x-javascript; charset=UTF-8');
|
|
echo Main\Web\Json::encode($response);
|
|
|
|
\CMain::finalActions();
|
|
}
|
|
|
|
/**
|
|
* Drops saved options.
|
|
* @param string $category Group option name.
|
|
* @param string $nameMask Option name mask.
|
|
* @return void
|
|
*/
|
|
protected function clearSavedOptions($category, $nameMask)
|
|
{
|
|
$res = \CUserOptions::getList(false,['CATEGORY' => $category, 'USER_ID' => $this->getUser()->getId(), 'NAME_MASK' => $nameMask]);
|
|
while ($opt = $res->fetch())
|
|
{
|
|
\CUserOptions::deleteOption($category, $opt['NAME']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Finds way to get back.
|
|
*
|
|
* @param string $path Path to analise.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function detectPathBack($path)
|
|
{
|
|
static $pathBackCache = array();;
|
|
if (!isset($pathBackCache[$path]))
|
|
{
|
|
$pathBack = \dirname($path);
|
|
$slash = \explode('/', $pathBack);
|
|
if (\is_array($slash))
|
|
{
|
|
$slashTmp = $slash;
|
|
$langKey = \array_search('lang', $slash) + 1;
|
|
unset($slashTmp[$langKey]);
|
|
if ($langKey == \count($slash) - 1)
|
|
{
|
|
unset($slash[$langKey]);
|
|
$pathBack = \implode('/', $slash);
|
|
}
|
|
}
|
|
$pathBackCache[$path] = $pathBack;
|
|
}
|
|
|
|
return $pathBackCache[$path];
|
|
}
|
|
|
|
/**
|
|
* Get data for chain links.
|
|
*
|
|
* @param string $path Path to analise.
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function generateChainLinks($path)
|
|
{
|
|
static $chainCache = [];
|
|
if (!isset($chainCache[$path]))
|
|
{
|
|
$params =& $this->getParams();
|
|
$chain = array();
|
|
$slash = \explode('/', \dirname($path));
|
|
if (\is_array($slash))
|
|
{
|
|
$langKey = \array_search('lang', $slash) + 1;
|
|
$slash[$langKey] = $params['CURRENT_LANG'];
|
|
if ($langKey == \count($slash) - 1)
|
|
{
|
|
unset($slash[$langKey]);
|
|
}
|
|
$i = 0;
|
|
$pathList = array();
|
|
foreach ($slash as $dir)
|
|
{
|
|
$i++;
|
|
if ($i == 1)
|
|
{
|
|
$chain[] = array(
|
|
'link' => $params['LIST_PATH'].
|
|
'?lang='.$params['CURRENT_LANG'].
|
|
'&tabId='.$this->tabId.
|
|
'&path=/',
|
|
'title' => '..'
|
|
);
|
|
}
|
|
else
|
|
{
|
|
$pathList[] = \htmlspecialcharsbx($dir);
|
|
$chain[] = array(
|
|
'link' => $params['LIST_PATH'].
|
|
'?lang='.$params['CURRENT_LANG'].
|
|
'&tabId='.$this->tabId.
|
|
'&path=/'.\implode('/', $pathList).'/',
|
|
'title' => \htmlspecialcharsbx($dir)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
$chainCache[$path] = $chain;
|
|
}
|
|
|
|
return $chainCache[$path];
|
|
}
|
|
|
|
public function getTopIndexedFolders(int $depth = 2): array
|
|
{
|
|
static $list;
|
|
if ($list === null)
|
|
{
|
|
$list = [0 => ''];
|
|
$res = Index\Internals\PathIndexTable::getList([
|
|
'select' => ['ID', 'PATH'],
|
|
'filter' => [
|
|
'=INDEXED' => 'Y',
|
|
'=IS_DIR' => 'Y',
|
|
'<=DEPTH_LEVEL' => $depth,
|
|
],
|
|
'order' => [
|
|
'DEPTH_LEVEL' => 'ASC',
|
|
'PATH' => 'ASC',
|
|
]
|
|
]);
|
|
while ($row = $res->fetch())
|
|
{
|
|
$list[$row['ID']] = $row['PATH'];
|
|
}
|
|
}
|
|
|
|
return $list;
|
|
}
|
|
} |