bff::adminPanel(),
'lang' => $contentLang ?? Lang::current(),
'langInterface' => Lang::current(),
'paramsOnly' => false,
'width' => '575', # ширина число или "100%" (0,false = 100%)
'jsName' => '', # имя js объекта, для дальнейшего управления компонентом
'attr' => [],
'onInit' => null,
], $opts);
$toolbar = static::toolbar($type, $toolbar, $opts);
if ($opts['paramsOnly']) {
return $toolbar;
}
# name/id редактора
if (strpos($fieldName, ',') !== false) {
list($fieldID, $fieldName) = explode(',', $fieldName);
if (empty($fieldName)) {
$fieldName = $fieldID;
}
} else {
$fieldID = $fieldName;
$fieldID = str_replace(['[', ']', '.'], '', $fieldID);
}
# подключаем библиотеку редактора
static::scripts($type, $opts);
# формируем HTML
if (empty($opts['jsName'])) {
# формируем название js объекта на основе имени текстового поля
$opts['jsName'] = 'wysiwyg_' . trim(mb_strtolower(strtr($fieldName, ['[' => '_', ']' => '_', '.' => '_'])), '_');
}
$htmlTextarea = '';
$htmlJavascript = '';
if ($type !== static::TYPE_FCK)
{
# размеры редактора (ширина/высота)
$width = static::sizeToStyle($opts['width']);
$height = static::sizeToStyle($height);
$attr = [
'id' => $fieldID,
'name' => $fieldName,
'class' => 'wysiwyg',
'data-wytype' => $type,
'data-lang' => $opts['lang'],
'data-langrtl' => (Lang::isRTL($opts['lang']) ? 1 : 0),
'style' => 'height:' . $height . '!important; width:' . $width . ';',
];
if (! empty($opts['attr'])) {
foreach ($opts['attr'] as $k => $v) {
HTML::attributeAdd($attr, $k, $v);
}
}
$htmlTextarea = '';
}
switch ($type) {
case static::TYPE_DEFAULT: {
$htmlJavascript = '$(function(){
' . (!empty($opts['jsName']) ? $opts['jsName'] . ' = ' : '') . ' $(\'#' . $fieldID . '\')
.bffWysiwyg(' . func::php2js($toolbar) . ', true);
});';
} break;
case static::TYPE_TINYMCE: {
$handlers = [];
$toolbar['selector'] = '#' . $fieldID;
if (bff::adminPanel()) {
$urlParams = [
'type' => $type,
'absoluteUrl' => $opts['absoluteUrl'] ?? 0,
];
$toolbar['images_file_types'] = join(',', static::imageExtensions($type));
$handlers['file_picker_title'] = _t('@', 'Files Browser');
$handlers['file_picker_url'] = Admin::url('/site/wysiwygImagesBrowser', $urlParams);
$toolbar['file_picker_callback'] = function () use ($type) { return 'o.file_picker_callback'; };
$handlers['images_upload_url'] = Admin::url('/site/wysiwygImageUpload', $urlParams);
$toolbar['images_upload_handler'] = function () use ($type) { return 'o.images_upload_handler'; };
}
if (is_callable($opts['onInit'])) {
call_user_func($opts['onInit'], [
'toolbar' => & $toolbar,
'handlers' => & $handlers,
]);
}
$htmlJavascript = '$(function(){ tinymce.remove(\'#'.$fieldID.'\'); '.
'var o = bff.wysiwyg.tinymce.handlers(' . Input::php2js($handlers) . '); '.
' tinymce.init(' . Input::php2js($toolbar) . ').then(function(t) { ' .
$opts['jsName'] . ' = t[0]; $.data(t[0].targetElm, \'wysiwyg\', t[0]);
t[0].on("change", function(e){ tinymce.triggerSave(); $(e.target.targetElm).trigger("change"); });
});
});';
} break;
case static::TYPE_FCK: {
require_once modification(PATH_PUBLIC . 'js/bff/admin/fcke2/fckeditor.php');
$editor = new FCKeditor($fieldName);
$editor->BasePath = '../js/bff/admin/fcke2/';
$editor->Width = str_replace('px', '', $opts['width']);
$editor->Height = str_replace('px', '', $height);
$editor->ToolbarSet = $toolbar;
$editor->Config['SkinPath'] = '/js/bff/admin/fcke2/editor/skins/'.($opts['theme'] ?? 'sd').'/';
$editor->Value = $content;
return $editor->CreateHTML();
}
default: {
bff::hook('tpl.wysiwyg.' . $type . '.view', $fieldID, $toolbar, $opts);
} break;
}
if ($opts['adminPanel']) {
return $htmlTextarea . '';
}
js::start();
echo $htmlJavascript;
js::stop();
return $htmlTextarea;
}
/**
* Подключаем javascript-библиотеку редактора
* @param string $type тип редактора static::TYPE_
* @param array $opts
* @return array
*/
public static function scripts(string $type, array $opts = []): array
{
$scripts = [];
switch ($type) {
case static::TYPE_DEFAULT: {
$scripts[] = 'wysiwyg';
if (!empty($opts['reformator'])) {
$scripts[] = 'reformator/reformator';
}
} break;
case static::TYPE_TINYMCE: {
$scripts[] = 'tinymce';
} break;
case static::TYPE_FCK: {
// no scripts for fck
} break;
default: {
$scripts = bff::filter('tpl.wysiwyg.' . $type . '.scripts', $scripts, $opts);
} break;
}
if (! empty($opts['return'])) {
return $scripts;
}
View::script($scripts, true);
return $scripts;
}
/**
* Формируем параметры редактора
* @param string $type тип редактора static::TYPE_
* @param array|string $toolbar
* @param array $opts
* @return array|string
*/
public static function toolbar(string $type, $toolbar = [], array $opts = [])
{
switch ($type) {
case static::TYPE_DEFAULT: {
if (empty($toolbar) || ! is_array($toolbar)) {
$toolbar = [];
}
if (!empty($opts['adminPanel'])) {
$toolbar = array_merge([
'stretch' => true,
'autogrow' => false,
'controls' => [
'insertImageSimple' => ['visible' => true],
'fullscreen' => ['visible' => false],
],
], $toolbar);
} else {
$toolbar = array_merge([
'controls' => [
'insertImageSimple' => ['visible' => false],
'fullscreen' => ['visible' => false],
'html' => ['visible' => false],
'title' => ['visible' => false],
]
], $toolbar);
}
} break;
case static::TYPE_TINYMCE:
{
if (empty($toolbar) || ! is_array($toolbar)) {
$toolbar = [];
}
$toolbar = array_merge([
'plugins' => ['code','paste','lists','hr','link','image','imagetools','media','autoresize','autolink','table','wordcount'],
'language' => $opts['langInterface'] ?? Lang::current(), # язык интерфейса редактора
'menubar' => false,
'toolbar' => 'bold _format forecolor _headers _lists _align link image | media table removeformat code',
'toolbar_groups' => [
'_format' => [
'icon' => 'italic',
'items' => 'italic underline strikethrough superscript subscript',
],
'_headers' => [
'text' => 'H2',
'items' => 'h2 h3 h4 h5 h6',
],
'_lists' => [
'icon' => 'unordered-list',
'items' => 'bullist numlist',
],
'_align' => [
'icon' => 'align-left',
'items' => 'alignleft aligncenter alignright alignnone | indent outdent | blockquote hr',
],
],
'content_style' => 'body { font-size: 13px; }',
'imagetools_cors_hosts' => [SITEHOST],
'paste_data_images' => true,
'base_url' => bff::url('/js/bff/tinymce'),
'suffix' => '.min',
'convert_urls' => false,
], $toolbar);
if (isset($opts['lang']) && Lang::isRTL($opts['lang'])) {
$toolbar['directionality'] = 'rtl';
}
} break;
case static::TYPE_FCK: {
if (empty($toolbar) || ! is_string($toolbar)) {
$toolbar = 'Average';
}
switch (strtolower($toolbar)) {
case 'basic':
$toolbar = 'Basic';
break;
case 'mini':
$toolbar = 'Mini';
break;
case 'medium':
$toolbar = 'Medium';
break;
case 'average':
default:
$toolbar = 'Average';
break;
}
} break;
}
return bff::filter('tpl.wysiwyg.' . $type . '.toolbar', $toolbar, $opts);
}
/**
* Конвертируем размер редактора в формат для свойства style
* @param string $size размер, допустимые варианты: '100', '100px', '100%'
* @param string $default
* @return string
*/
public static function sizeToStyle(string $size, string $default = '100%'): string
{
if (empty($size)) {
return $default;
}
if (is_numeric($size)) {
return $size . 'px';
}
if (strpos($size, '%') !== false) {
return $size;
}
if (stripos($size, 'px') !== false) {
return $size;
}
return $default;
}
/**
* @param string $type тип редактора static::TYPE_
* @return mixed
*/
public static function imagePath(string $type)
{
return bff::filter('tpl.wysiwyg.' . $type . '.path', bff::path(static::IMAGES_FOLDER, 'images'));
}
/**
* @param string $type тип редактора static::TYPE_
* @param string $filename
* @return string
*/
public static function imageUrl(string $type, string $filename, array $opts = [])
{
$absoluteUrl = $opts['absoluteUrl'] ?? bff::input()->postget('absoluteUrl', TYPE_UINT);
$prefix = bff::filter('tpl.wysiwyg.' . $type . '.url', bff::url(static::IMAGES_FOLDER, 'images', [
'relative' => ! $absoluteUrl,
'scheme' => true,
]));
return $prefix . $filename;
}
/**
* @param string $type тип редактора static::TYPE_
* @return mixed
*/
public static function imageExtensions(string $type)
{
return bff::filter('tpl.wysiwyg.' . $type . '.extensions', ['jpg', 'jpeg', 'png', 'svg', 'gif']);
}
/**
* @param string $type тип редактора static::TYPE_
* @return bff\http\Response
*/
public static function imageUpload(string $type)
{
switch ($type) {
case static::TYPE_TINYMCE:
$filename = '';
do {
if (empty($_FILES)) {
break;
}
$name = '';
foreach ($_FILES as $k => $v) {
$name = $k;
break;
}
if (empty($name)) {
break;
}
$path = static::imagePath($type);
$attach = new Attachment(
$path,
bff::filter('tpl.wysiwyg.' . $type . '.maxSize', 10485760) # 10 Mb
);
$attach->setAllowedExtensions(static::imageExtensions($type));
$attach->setFiledataAsString(false);
$attach->setAssignErrors(true);
$data = $attach->uploadFILES($name);
if (! empty($data['filename'])) {
$filename = $data['filename'];
}
if (! empty($data['rfilename'])) {
$rfilename = func::translit(trim(basename($data['rfilename'], $data['extension']), '.'));
$rfilename .= '.' . $data['extension'];
if (file_exists($path . $rfilename)) {
Errors::setUploadError(Errors::FILE_ALREADY_EXISTS);
@unlink($path . $filename);
$filename = '';
break;
}
if (@rename($path . $filename, $path . $rfilename)) {
$filename = $rfilename;
} else {
Errors::setUploadError(Errors::FILE_ALREADY_EXISTS);
$filename = '';
}
}
} while(false);
$result = [];
if (! empty($filename)) {
$result['location'] = static::imageUrl($type, $filename);
}
$result['success'] = Errors::no();
$result['errors'] = Errors::get();
return Response::json($result);
}
return Response::empty();
}
/**
* @param string $type тип редактора static::TYPE_
* @param string $url
* @return string
*/
public static function jsImageSelect(string $type, string $url)
{
switch ($type) {
case static::TYPE_TINYMCE:
return 'window.parent.postMessage({
mceAction: \'select\',
url: \'' . $url .'\'
});';
}
return '';
}
/**
* @param string $type тип редактора static::TYPE_
* @param string $filetype
* @param array $opts
* @return array
*/
public static function imagesListing(string $type, string $filetype, array $opts = [])
{
if ($filetype !== 'image') {
return [];
}
$opts = array_merge([
'limit' => 0,
'offset' => 0,
], $opts);
$files = Files::getFiles(static::imagePath($type), '', false, false, static::imageExtensions($type));
sort($files);
$opts['total'] = count($files);
if ($opts['limit'] == 0) {
$opts['limit'] = count($files) + 1;
}
$result = [];
foreach ($files as $v) {
if ($opts['offset'] > 0) {
$opts['offset']--;
continue;
}
if ($opts['limit'] <= 0) {
break;
}
$opts['limit']--;
$result[] = [
'id' => $v,
'url' => static::imageUrl($type, $v),
];
}
return $result;
}
}