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 = '' . $content . ''; } 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; } }