837 lines
26 KiB
PHP
837 lines
26 KiB
PHP
<?php namespace ProcessWire;
|
||
|
||
/**
|
||
* InputfieldTinyMCETools
|
||
*
|
||
* Helper for managing TinyMCE settings and defaults
|
||
*
|
||
* ProcessWire 3.x, Copyright 2023 by Ryan Cramer
|
||
* https://processwire.com
|
||
*
|
||
*/
|
||
class InputfieldTinyMCESettings extends InputfieldTinyMCEClass {
|
||
|
||
/**
|
||
* Runtime caches shared among all instances
|
||
*
|
||
* @var array
|
||
*
|
||
*/
|
||
static protected $caches = array(
|
||
'defaults' => array(),
|
||
'settings' => array(),
|
||
'alignClasses' => array(),
|
||
'renderReadyInline' => array(),
|
||
'langSettings' => array(),
|
||
'addDefaults' => array(),
|
||
'originalDefaults' => array(),
|
||
);
|
||
|
||
/**
|
||
* Get settings from Inputfield vary from the $defaults
|
||
*
|
||
* @param array|null $defaults Default settings Default settings or omit to pull automatically
|
||
* @param string $cacheKey Optionally cache with this key
|
||
* @return array
|
||
*
|
||
*/
|
||
public function getSettings($defaults = null, $cacheKey = '') {
|
||
|
||
$inputfield = $this->inputfield;
|
||
|
||
if($cacheKey && isset(self::$caches['settings'][$cacheKey])) return self::$caches['settings'][$cacheKey];
|
||
if($defaults === null) $defaults = $this->getDefaults();
|
||
|
||
$settings = array();
|
||
$features = $inputfield->features;
|
||
$formats = $this->formats();
|
||
|
||
foreach($defaults as $name => $defaultValue) {
|
||
if($name === 'menubar') {
|
||
if(in_array($name, $features)) {
|
||
$value = $inputfield->get('menubar');
|
||
if(empty($value) || $value === 'default') $value = $defaultValue;
|
||
} else {
|
||
$value = false;
|
||
}
|
||
} else if($name === 'statusbar') {
|
||
$value = true;
|
||
} else if($name === 'browser_spellcheck') {
|
||
$value = in_array('spellcheck', $features);
|
||
} else if($name === 'toolbar') {
|
||
$value = in_array($name, $features) ? $inputfield->get($name) : '';
|
||
} else if($name === 'toolbar_sticky') {
|
||
$value = in_array('stickybars', $features);
|
||
} else if($name === 'content_css') {
|
||
$value = $inputfield->get($name);
|
||
if($value === 'custom') {
|
||
$value = $inputfield->get('content_css_url');
|
||
if(empty($value)) continue;
|
||
}
|
||
} else if($name === 'directionality') {
|
||
$value = $inputfield->getDirectionality();
|
||
} else if($name === 'style_formats') {
|
||
$value = $formats->getStyleFormats($defaults);
|
||
} else if($name === 'block_formats') {
|
||
$value = $formats->getBlockFormats();
|
||
} else if($name === 'invalid_styles') {
|
||
$value = $formats->getInvalidStyles($inputfield->invalid_styles, $defaultValue);
|
||
} else if($name === 'formats') {
|
||
// overlaps with native formats property so use data rather than get
|
||
$value = $inputfield->data('formats');
|
||
} else if($name === 'templates') {
|
||
// overlaps with API variable
|
||
$value = $inputfield->data($name);
|
||
} else {
|
||
$value = $inputfield->get($name);
|
||
if($value === 'default') $value = $defaultValue;
|
||
}
|
||
if($name === 'removed_menuitems' && strpos($value, 'print') === false) {
|
||
// the print option is not useful in inline mode
|
||
if($inputfield->inlineMode) $value = trim("$value print");
|
||
}
|
||
if($value !== null && $value != $defaultValue) {
|
||
$settings[$name] = $value;
|
||
}
|
||
}
|
||
|
||
$this->applySkin($settings, $defaults);
|
||
$this->applyPlugins($settings, $defaults);
|
||
|
||
if(isset($defaults['style_formats'])) {
|
||
$styleFormatsCSS = $inputfield->get('styleFormatsCSS');
|
||
if($styleFormatsCSS) {
|
||
$formats->applyStyleFormatsCSS($styleFormatsCSS, $settings, $defaults);
|
||
}
|
||
}
|
||
|
||
if($cacheKey) self::$caches['settings'][$cacheKey] = $settings;
|
||
|
||
return $settings;
|
||
}
|
||
|
||
/**
|
||
* Default settings for ProcessWire.config.InputfieldTinyMCE
|
||
*
|
||
* This should have no field-specific settings (no dynamic values)
|
||
*
|
||
* @property string $key
|
||
* @return array
|
||
*
|
||
*/
|
||
public function getDefaults($key = '') {
|
||
|
||
if(!empty(self::$caches['defaults'])) {
|
||
if($key) return isset(self::$caches['defaults'][$key]) ? self::$caches['defaults'][$key] : null;
|
||
return self::$caches['defaults'];
|
||
}
|
||
|
||
$config = $this->wire()->config;
|
||
$root = $config->urls->root;
|
||
$url = $config->urls($this->inputfield);
|
||
$tools = $this->tools();
|
||
|
||
// root relative, i.e. '/site/modules/InputfieldTinyMCE/'
|
||
$url = substr($url, strlen($root)-1);
|
||
$alignClasses = $this->getAlignClasses();
|
||
$mceSettingNames = $this->inputfield->getSettingNames('tinymce');
|
||
$optionalSettingNames = $this->inputfield->getSettingNames('optionals');
|
||
$optionals = $this->inputfield->optionals;
|
||
|
||
// selector of elements that can be used with align commands
|
||
|
||
$replacements = array(
|
||
'{url}' => $url,
|
||
'{alignleft}' => $alignClasses['left'],
|
||
'{aligncenter}' => $alignClasses['center'],
|
||
'{alignright}' => $alignClasses['right'],
|
||
'{alignfull}' => $alignClasses['full'],
|
||
);
|
||
|
||
$json = file_get_contents(__DIR__ . '/defaults.json');
|
||
$json = str_replace(array_keys($replacements), array_values($replacements), $json);
|
||
$defaults = $tools->jsonDecode($json, 'defaults.json');
|
||
|
||
// defaults JSON file
|
||
$file = $this->inputfield->defaultsFile;
|
||
if($file) {
|
||
$file = $config->paths->root . ltrim($file, '/');
|
||
$data = $tools->jsonDecodeFile($file, 'default settings file for module');
|
||
if(is_array($data) && !empty($data)) $defaults = array_merge($defaults, $data);
|
||
}
|
||
|
||
// defaults JSON text
|
||
$json = $this->inputfield->defaultsJSON;
|
||
if($json) {
|
||
$data = $tools->jsonDecode($json, 'defaults JSON module setting');
|
||
if(is_array($data) && !empty($data)) $defaults = array_merge($defaults, $data);
|
||
}
|
||
|
||
// extra CSS module setting
|
||
$extraCSS = $this->inputfield->extraCSS;
|
||
if(strlen($extraCSS)) {
|
||
$contentStyle = isset($defaults['content_style']) ? $defaults['content_style'] : '';
|
||
$contentStyle .= "\n$extraCSS";
|
||
$defaults['content_style'] = $contentStyle;
|
||
}
|
||
|
||
// optionals
|
||
foreach($optionalSettingNames as $name) {
|
||
if(in_array($name, $optionals)) continue; // configured with field (not module)
|
||
if(!in_array($name, $mceSettingNames)) continue; // not a direct TinyMCE setting
|
||
$value = $this->inputfield->get($name);
|
||
if($value === 'default' || $value === null) continue;
|
||
if($name === 'invalid_styles' && is_string($value)) {
|
||
$value = $this->formats()->invalidStylesStrToArray($value);
|
||
}
|
||
if(isset($defaults[$name]) && $defaults[$name] !== $value) {
|
||
self::$caches['originalDefaults'][$name] = $defaults[$name];
|
||
}
|
||
$defaults[$name] = $value;
|
||
}
|
||
|
||
$languageSettings = $this->getLanguageSettings();
|
||
if(!empty($languageSettings)) $defaults = array_merge($defaults, $languageSettings);
|
||
|
||
foreach($defaults as $k => $value) {
|
||
if(strpos($k, 'add_') === 0 || strpos($k, 'append_') === 0 || strpos($k, 'replace_') === 0) {
|
||
self::$caches['addDefaults'][$k] = $value;
|
||
unset($defaults[$k]);
|
||
}
|
||
}
|
||
|
||
self::$caches['defaults'] = $defaults;
|
||
|
||
if($key) return isset($defaults[$key]) ? $defaults[$key] : null;
|
||
|
||
return $defaults;
|
||
}
|
||
|
||
/**
|
||
* Get original defaults from source JSON, prior to being overriden by module default settings
|
||
*
|
||
* @param string $key
|
||
* @return array|mixed|null
|
||
*
|
||
*/
|
||
public function getOriginalDefaults($key = '') {
|
||
$defaults = $this->getDefaults();
|
||
if($key) {
|
||
if(isset(self::$caches['originalDefaults'][$key])) {
|
||
return self::$caches['originalDefaults'][$key];
|
||
} else {
|
||
return isset($defaults[$key]) ? $defaults[$key] : null;
|
||
}
|
||
}
|
||
return array_merge($defaults, self::$caches['originalDefaults']);
|
||
}
|
||
|
||
/**
|
||
* Get 'add_' or 'replace_' default settings
|
||
*
|
||
* @return array|mixed
|
||
*
|
||
*/
|
||
public function getAddDefaults() {
|
||
return self::$caches['addDefaults'];
|
||
}
|
||
|
||
/**
|
||
* Apply plugins settings
|
||
*
|
||
* @param array $settings
|
||
* @param array $defaults
|
||
*
|
||
*/
|
||
protected function applyPlugins( array &$settings, array $defaults) {
|
||
$extPlugins = $this->inputfield->get('extPlugins');
|
||
|
||
if(!empty($extPlugins)) {
|
||
$value = $defaults['external_plugins'];
|
||
foreach($extPlugins as $url) {
|
||
$name = basename($url, '.js');
|
||
$value[$name] = $url;
|
||
}
|
||
$settings['external_plugins'] = $value;
|
||
}
|
||
|
||
if(isset($defaults['plugins'])) {
|
||
$plugins = $this->inputfield->get('plugins');
|
||
if(empty($plugins) && !empty($defaults['plugins'])) $plugins = $defaults['plugins'];
|
||
if(!is_array($plugins)) $plugins = explode(' ', $plugins);
|
||
if(!in_array('pwlink', $plugins)) {
|
||
unset($settings['external_plugins']['pwlink']);
|
||
if(isset($settings['menu'])) {
|
||
$settings['menu']['insert']['items'] = str_replace('pwlink', 'link', $settings['menu']['insert']['items']);
|
||
}
|
||
}
|
||
if(!in_array('pwimage', $plugins)) {
|
||
unset($settings['external_plugins']['pwimage']);
|
||
if(isset($settings['menu'])) {
|
||
$settings['menu']['insert']['items'] = str_replace('pwimage', 'image', $settings['menu']['insert']['items']);
|
||
}
|
||
}
|
||
$settings['plugins'] = implode(' ', $plugins);
|
||
if($settings['plugins'] === $defaults['plugins']) unset($settings['plugins']);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Apply skin or skin_url directly to given settings/defaults
|
||
*
|
||
* @param array $settings
|
||
* @param array $defaults
|
||
*
|
||
*/
|
||
protected function applySkin(&$settings, $defaults) {
|
||
$skin = $this->inputfield->skin;
|
||
if($skin === 'custom') {
|
||
$skinUrl = rtrim($this->inputfield->skin_url, '/');
|
||
if(strlen($skinUrl)) {
|
||
if(strpos($skinUrl, '//') === false) {
|
||
$skinUrl = $this->wire()->config->urls->root . ltrim($skinUrl, '/');
|
||
}
|
||
if(!isset($defaults['skin_url']) || $defaults['skin_url'] != $skinUrl) {
|
||
$settings['skin_url'] = $skinUrl;
|
||
}
|
||
unset($settings['skin']);
|
||
}
|
||
} else {
|
||
if(isset($defaults['skin']) && $defaults['skin'] != $skin) {
|
||
$settings['skin'] = $skin;
|
||
}
|
||
unset($settings['skin_url']);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Get image alignment classes
|
||
*
|
||
* @return array
|
||
*
|
||
*/
|
||
public function getAlignClasses() {
|
||
if(empty(self::$caches['alignClasses'])) {
|
||
$data = $this->wire()->modules->getModuleConfigData('ProcessPageEditImageSelect');
|
||
self::$caches['alignClasses'] = array(
|
||
'left' => (empty($data['alignLeftClass']) ? 'align_left' : $data['alignLeftClass']),
|
||
'right' => (empty($data['alignRightClass']) ? 'align_right' : $data['alignRightClass']),
|
||
'center' => (empty($data['alignCenterClass']) ? 'align_center' : $data['alignCenterClass']),
|
||
'full' => 'align_full',
|
||
);
|
||
}
|
||
return self::$caches['alignClasses'];
|
||
}
|
||
|
||
/**
|
||
* Get settings from custom settings file
|
||
*
|
||
* @return array
|
||
*
|
||
*/
|
||
protected function getFromSettingsFile() {
|
||
$file = $this->inputfield->get('settingsFile');
|
||
if(empty($file)) return array();
|
||
$file = $this->wire()->config->paths->root . ltrim($file, '/');
|
||
return $this->tools()->jsonDecodeFile($file, 'settingsFile');
|
||
}
|
||
|
||
/**
|
||
* Get settings from custom JSON
|
||
*
|
||
* @return array
|
||
*
|
||
*/
|
||
protected function getFromSettingsJSON() {
|
||
$json = trim((string) $this->inputfield->get('settingsJSON'));
|
||
if(empty($json)) return array();
|
||
return $this->tools()->jsonDecode($json, 'settingsJSON');
|
||
}
|
||
|
||
/**
|
||
* Get content_css URL
|
||
*
|
||
* @param string $content_css
|
||
* @return string
|
||
*
|
||
*/
|
||
public function getContentCssUrl($content_css = '') {
|
||
|
||
$config = $this->wire()->config;
|
||
$rootUrl = $config->urls->root;
|
||
$defaultUrl = $config->urls($this->inputfield) . 'content_css/wire.css';
|
||
|
||
if($this->inputfield->useFeature('document')) {
|
||
$content_css = 'document';
|
||
}
|
||
|
||
if(empty($content_css)) {
|
||
if($this->inputfield->useFeature('document')) {
|
||
$content_css = 'document';
|
||
} else {
|
||
$content_css = $this->inputfield->content_css;
|
||
}
|
||
}
|
||
|
||
if($content_css === 'wire' || empty($content_css)) {
|
||
// default
|
||
$url = $defaultUrl;
|
||
|
||
} else if(strpos($content_css, '/') !== false) {
|
||
// custom file
|
||
$url = $rootUrl . ltrim($content_css, '/');
|
||
|
||
} else if($content_css === 'custom') {
|
||
// custom file (alternate/fallback)
|
||
$content_css_url = $this->inputfield->content_css_url;
|
||
if(empty($content_css_url) || strpos($content_css_url, '/') === false) {
|
||
$url = $defaultUrl;
|
||
} else {
|
||
$url = $rootUrl . ltrim($content_css_url, '/');
|
||
}
|
||
|
||
} else if($content_css) {
|
||
// defined
|
||
$content_css = basename($content_css, '.css');
|
||
$url = $config->urls($this->inputfield) . "content_css/$content_css.css";
|
||
|
||
} else {
|
||
$url = $defaultUrl;
|
||
}
|
||
|
||
return $url;
|
||
}
|
||
|
||
/**
|
||
* Prepare given settings ready for output
|
||
*
|
||
* This converts relative URLs to absolute, etc.
|
||
*
|
||
* @param array $settings
|
||
* @return array
|
||
*
|
||
*/
|
||
public function prepareSettingsForOutput(array $settings) {
|
||
$config = $this->wire()->config;
|
||
$rootUrl = $config->urls->root;
|
||
//$inline = $this->inputfield->inlineMode > 0;
|
||
|
||
/*
|
||
if($inline) {
|
||
// content_css not loaded here
|
||
//$settings['content_css'] = '';
|
||
*/
|
||
|
||
if(isset($settings['content_css'])) {
|
||
// convert content_css setting to URL
|
||
$settings['content_css'] = $this->getContentCssUrl($settings['content_css']);
|
||
}
|
||
|
||
if(!empty($settings['external_plugins'])) {
|
||
foreach($settings['external_plugins'] as $name => $url) {
|
||
$settings['external_plugins'][$name] = $rootUrl . ltrim($url, '/');
|
||
}
|
||
}
|
||
if(isset($settings['height'])) {
|
||
$settings['height'] = "$settings[height]px";
|
||
}
|
||
|
||
if(isset($settings['toolbar']) && is_string($settings['toolbar'])) {
|
||
$splitTools = array('styles', 'blocks');
|
||
foreach($splitTools as $name) {
|
||
$settings['toolbar'] = str_replace("$name ", "$name | ", $settings['toolbar']);
|
||
}
|
||
}
|
||
|
||
if(empty($settings['invalid_styles'])) {
|
||
// for empty invalid_styles use blank string rather than blank array
|
||
$settings['invalid_styles'] = '';
|
||
}
|
||
|
||
if(!empty($settings['content_style'])) {
|
||
// namespace content_style for .mce_content_body
|
||
$contentStyle = $settings['content_style'];
|
||
$contentStyle = str_replace('}', "}\n", $contentStyle);
|
||
$contentStyle = preg_replace('![\s\r\n]+\{!', '{', $contentStyle);
|
||
$lines = explode("\n", $contentStyle);
|
||
foreach($lines as $k => $line) {
|
||
$line = trim($line);
|
||
if(empty($line)) {
|
||
unset($lines[$k]);
|
||
} else if(strpos($line, '.mce-content-body') !== false) {
|
||
continue;
|
||
} else if(strpos($line, '{')) {
|
||
$lines[$k] = ".mce-content-body $line";
|
||
}
|
||
}
|
||
$contentStyle = implode(' ', $lines);
|
||
while(strpos($contentStyle, ' ') !== false) $contentStyle = str_replace(' ', ' ', $contentStyle);
|
||
$contentStyle = str_replace(['{ ', ' }'], ['{', '}'], $contentStyle);
|
||
$contentStyle = str_replace('@', "\\@", $contentStyle);
|
||
$settings['content_style'] = $contentStyle;
|
||
}
|
||
|
||
/*
|
||
if(isset($settings['plugins']) && is_array($settings['plugins'])) {
|
||
$settings['plugins'] = implode(' ', $settings['plugins']);
|
||
}
|
||
*/
|
||
|
||
// ensure blank object properties resolve to {} in JSON rather than []
|
||
foreach($this->tools()->jsonBlankObjectProperties as $name) {
|
||
if(!isset($settings[$name]) || !empty($settings[$name]) || !is_array($settings[$name])) continue;
|
||
$settings[$name] = (object) $settings[$name];
|
||
}
|
||
|
||
return $settings;
|
||
}
|
||
|
||
/**
|
||
* Get language pack code
|
||
*
|
||
* @return string
|
||
*
|
||
*/
|
||
public function getLanguagePackCode() {
|
||
|
||
$default = 'en_US';
|
||
$languages = $this->wire()->languages;
|
||
$sanitizer = $this->wire()->sanitizer;
|
||
$path = __DIR__ . '/langs/';
|
||
|
||
if(!$languages) return $default;
|
||
|
||
$language = $this->wire()->user->language;
|
||
|
||
// attempt to get from module setting
|
||
$value = $this->inputfield->get("lang_$language->name");
|
||
if($value) return $value;
|
||
|
||
// attempt to get from non-default language name
|
||
if(!$language->isDefault() && is_file("$path$language->name.js")) {
|
||
return $language->name;
|
||
}
|
||
|
||
// attempt to get from admin theme
|
||
$adminTheme = $this->wire()->adminTheme;
|
||
if($adminTheme) {
|
||
$value = $sanitizer->name($adminTheme->_('en'));
|
||
if($value !== 'en' && is_file("$path$value.js")) return $value;
|
||
}
|
||
|
||
$value = $languages->getLocale();
|
||
|
||
// attempt to get from locale setting
|
||
if($value !== 'C') {
|
||
if(strpos($value, '.')) list($value,) = explode('.', $value, 2);
|
||
if(is_file("$path$value.js")) return $value;
|
||
if(strpos($value, '_')) {
|
||
list($value,) = explode('_', $value, 2);
|
||
if(is_file("$path$value.js")) return $value;
|
||
}
|
||
}
|
||
|
||
// attempt to get from CKEditor static translation
|
||
$textdomain = '/wire/modules/Inputfield/InputfieldCKEditor/InputfieldCKEditor.module';
|
||
if(is_file($this->wire()->config->paths->root . ltrim($textdomain, '/'))) {
|
||
$value = _x('en', 'language-pack', $textdomain);
|
||
if($value !== 'en') {
|
||
$value = $sanitizer->name($value);
|
||
if($value && is_file("$path$value.js")) return $value;
|
||
}
|
||
}
|
||
|
||
return $default;
|
||
}
|
||
|
||
/**
|
||
* Get language pack settings
|
||
*
|
||
* @return array
|
||
*
|
||
*/
|
||
public function getLanguageSettings() {
|
||
if(!$this->wire()->languages) return array();
|
||
$language = $this->wire()->user->language;
|
||
if(isset(self::$caches['langSettings'][$language->id])) {
|
||
return self::$caches['langSettings'][$language->id];
|
||
}
|
||
$code = $this->getLanguagePackCode();
|
||
if($code === 'en_US') {
|
||
$value = array();
|
||
} else {
|
||
$value = array(
|
||
'language' => $code,
|
||
'language_url' => $this->wire()->config->urls($this->inputfield) . "langs/$code.js"
|
||
);
|
||
}
|
||
self::$caches['langSettings'][$language->id] = $value;
|
||
return $value;
|
||
}
|
||
|
||
/**
|
||
* Apply 'add_*' settings in $addSettings, plus merge all $addSettings into given $settings
|
||
*
|
||
* This updates the $settings and $addSettings variables directly
|
||
*
|
||
* @param array $settings
|
||
* @param array $addSettings
|
||
* @param array $defaults
|
||
*
|
||
*/
|
||
protected function applyAddSettings(array &$settings, array &$addSettings, array $defaults) {
|
||
|
||
// apply add_style_formats when present
|
||
if(isset($addSettings['add_style_formats'])) {
|
||
$styleFormats = isset($settings['style_formats']) ? $settings['style_formats'] : $defaults['style_formats'];
|
||
$settings['style_formats'] = $this->formats()->mergeStyleFormats($styleFormats, $addSettings['add_style_formats']);
|
||
unset($addSettings['add_style_formats']);
|
||
}
|
||
|
||
// find other add_* properties, i.e. 'add_formats', 'add_invalid_styles', 'add_plugins'
|
||
// these append rather than replace, i.e. 'add_formats' appends to 'formats'
|
||
// also find any replace_* properties and replace setting values rather than append
|
||
foreach($addSettings as $key => $addValue) {
|
||
if(strpos($key, 'replace_') === 0) {
|
||
list(,$k) = explode('replace_', $key, 2);
|
||
if(!isset($addSettings[$k]) && $addValue !== null) $addSettings[$k] = $addValue;
|
||
unset($addSettings[$key]);
|
||
continue;
|
||
}
|
||
if(strpos($key, 'append_') === 0) {
|
||
unset($addSettings[$key]);
|
||
$key = str_replace('append_', 'add_', $key);
|
||
}
|
||
if(strpos($key, 'add_') !== 0) continue;
|
||
list(,$name) = explode('add_', $key, 2);
|
||
unset($addSettings[$key]);
|
||
if(isset($settings[$name])) {
|
||
// present in settings
|
||
$value = $settings[$name];
|
||
} else if(isset($defaults[$name])) {
|
||
// present in defaults
|
||
$value = $defaults[$name];
|
||
} else {
|
||
// not present, add it to settings
|
||
$addSettings[$name] = $addValue;
|
||
continue;
|
||
}
|
||
$addSettings[$name] = $this->mergeSetting($value, $addValue);
|
||
}
|
||
|
||
$settings = array_merge($settings, $addSettings);
|
||
}
|
||
|
||
/**
|
||
* Merge two setting values into one that combines them
|
||
*
|
||
* @param string|array|mixed $value
|
||
* @param string|array|mixed $addValue
|
||
* @return string|array|mixed
|
||
*
|
||
*/
|
||
protected function mergeSetting($value, $addValue) {
|
||
if(is_string($value) && is_string($addValue)) {
|
||
$value .= " $addValue";
|
||
} else if(is_array($addValue) && is_array($value)) {
|
||
foreach($addValue as $k => $v) {
|
||
if(is_int($k)) {
|
||
// append
|
||
$value[] = $v;
|
||
} else {
|
||
// append or replace
|
||
$value[$k] = $v;
|
||
}
|
||
}
|
||
} else {
|
||
$value = $addValue;
|
||
}
|
||
return $value;
|
||
}
|
||
|
||
/**
|
||
* Merge all settings in given array and combine those with "add_" prefix
|
||
*
|
||
* @param array $settings1
|
||
* @param array $settings2 Optionally specify this to merge/combine with those in $settings1
|
||
* @return array
|
||
*
|
||
*/
|
||
protected function mergeSettings(array $settings1, array $settings2 = array()) {
|
||
$settings = array_merge($settings1, $settings2);
|
||
$addSettings = array();
|
||
foreach($settings1 as $key => $value) {
|
||
if(strpos($key, 'add_') !== 0) continue;
|
||
$addSettings[$key] = $value;
|
||
}
|
||
foreach($settings2 as $key => $value) {
|
||
if(strpos($key, 'add_') !== 0) continue;
|
||
if(isset($addSettings[$key])) {
|
||
$addSettings[$key] = $this->mergeSetting($addSettings[$key], $value);
|
||
} else {
|
||
$addSettings[$key] = $value;
|
||
}
|
||
}
|
||
if(count($addSettings)) $settings = array_merge($settings, $addSettings);
|
||
return $settings;
|
||
}
|
||
|
||
/**
|
||
* Determine which settings go where and apply to Inputfield
|
||
*
|
||
* @param array $addSettings Optionally add this settings on top of those that would otherwise be used
|
||
*
|
||
*/
|
||
public function applyRenderReadySettings(array $addSettings = array()) {
|
||
|
||
$config = $this->wire()->config;
|
||
$inputfield = $this->inputfield;
|
||
$configName = $inputfield->getConfigName();
|
||
|
||
// default settings
|
||
$defaults = $this->getDefaults();
|
||
$addDefaults = $this->getAddDefaults();
|
||
$fileSettings = $this->getFromSettingsFile();
|
||
$jsonSettings = $this->getFromSettingsJSON();
|
||
|
||
if(count($fileSettings)) $addDefaults = $this->mergeSettings($addDefaults, $fileSettings);
|
||
if(count($jsonSettings)) $addDefaults = $this->mergeSettings($addDefaults, $jsonSettings);
|
||
if(count($addSettings)) $addDefaults = $this->mergeSettings($addDefaults, $addSettings);
|
||
$addSettings = $addDefaults;
|
||
|
||
if($configName && $configName !== 'default') {
|
||
$js = $config->js($inputfield->className());
|
||
|
||
// get settings that differ between field and defaults, then set to new named config
|
||
$diffSettings = $this->getSettings($defaults, $configName);
|
||
$mergedSettings = array_merge($defaults, $diffSettings);
|
||
//$contentStyle = isset($mergedSettings['content_style']) ? $mergedSettings['content_style'] : '';
|
||
|
||
if(count($addSettings)) {
|
||
// merges $addSettings into $diffSettings
|
||
$this->applyAddSettings($diffSettings, $addSettings, $defaults);
|
||
}
|
||
|
||
if(!isset($js['settings'][$configName])) {
|
||
$js['settings'][$configName] = $this->prepareSettingsForOutput($diffSettings);
|
||
$config->js($inputfield->className(), $js);
|
||
}
|
||
|
||
// get settings that will go in data-settings attribute
|
||
// remove settings that cannot be set for field/template context
|
||
unset($mergedSettings['style_formats'], $mergedSettings['content_style'], $mergedSettings['content_css']);
|
||
$dataSettings = $this->getSettings($mergedSettings);
|
||
|
||
} else {
|
||
// no configName in use, data-settings attribute will hold all non-default settings
|
||
$dataSettings = $this->getSettings($defaults);
|
||
//$contentStyle = isset($dataSettings['content_style']) ? $dataSettings['content_style'] : '';
|
||
if(count($addSettings)) {
|
||
$this->applyAddSettings($dataSettings, $addSettings, $defaults);
|
||
}
|
||
}
|
||
|
||
if($inputfield->inlineMode) {
|
||
if($inputfield->inlineMode < 2) unset($dataSettings['height']);
|
||
$dataSettings['inline'] = true;
|
||
/*
|
||
if($contentStyle && $adminTheme) {
|
||
$cssName = $configName;
|
||
if(empty($cssName)) {
|
||
$cssName = substr(md5($contentStyle), 0, 4) . strlen($contentStyle);
|
||
}
|
||
$inputfield->addClass("tmcei-$cssName", 'wrapClass');
|
||
if(!isset(self::$caches['renderReadyInline'][$cssName])) {
|
||
// inline mode content_style settings, ensure they are visible before inline init
|
||
//$ns = ".tmcei-$cssName .mce-content-body ";
|
||
//$contentStyle = $ns . str_replace('}', "} $ns", $contentStyle) . '{}';
|
||
//$adminTheme->addExtraMarkup('head', "<style>$contentStyle</style>");
|
||
self::$caches['renderReadyInline'][$cssName] = $cssName;
|
||
}
|
||
}
|
||
*/
|
||
}
|
||
|
||
$dataSettings = count($dataSettings) ? $this->prepareSettingsForOutput($dataSettings) : array();
|
||
if($inputfield->renderValueMode) $dataSettings['readonly'] = true;
|
||
|
||
$features = array('imgUpload', 'imgResize', 'pasteFilter');
|
||
foreach($features as $key => $feature) {
|
||
if(!$inputfield->useFeature($feature)) unset($features[$key]);
|
||
}
|
||
if($inputfield->lazyMode) $features[] = "lazyMode$inputfield->lazyMode";
|
||
|
||
$inputfield->wrapAttr('data-configName', $configName);
|
||
$inputfield->wrapAttr('data-settings', $this->tools()->jsonEncode($dataSettings, 'data-settings', false));
|
||
$inputfield->wrapAttr('data-features', implode(',', $features));
|
||
}
|
||
|
||
/**
|
||
* Apply settings settings to $this->inputfield to inherit from another field
|
||
*
|
||
* This is called from the main InputfieldTinyMCE class.
|
||
*
|
||
* @param string $fieldName Field name or 'fieldName:id' string
|
||
* @return bool|Field Returns false or field inherited from
|
||
*
|
||
*/
|
||
public function applySettingsField($fieldName) {
|
||
|
||
$fieldId = 0;
|
||
$error = '';
|
||
$hasField = $this->inputfield->hasField;
|
||
$hasPage = $this->inputfield->hasPage;
|
||
|
||
if(strpos($fieldName, ':')) {
|
||
list($fieldName, $fieldId) = explode(':', $fieldName);
|
||
} else if(ctype_digit("$fieldName")) {
|
||
$fieldName = (int) $fieldName; // since fields.get also accepts IDs
|
||
}
|
||
|
||
// no need to inherit from oneself
|
||
if("$fieldName" === "$hasField") return false;
|
||
|
||
$field = $this->wire()->fields->get($fieldName);
|
||
|
||
if(!$field) {
|
||
$error = "Cannot find settings field '$fieldName'";
|
||
} else if(!$field->type instanceof FieldtypeTextarea) {
|
||
$error = "Settings field '$fieldName' is not of type FieldtypeTextarea";
|
||
$field = null;
|
||
} else if(!wireInstanceOf($field->get('inputfieldClass'), $this->inputfield->className())) {
|
||
$error = "Settings field '$fieldName' is not using TinyMCE";
|
||
$field = null;
|
||
}
|
||
|
||
if(!$field && $fieldId && $fieldName) {
|
||
// try again with field ID only, which won't go recursive again
|
||
return $this->applySettingsField($fieldId);
|
||
}
|
||
|
||
if(!$field) {
|
||
if($error) $this->error($this->inputfield->attr('name') . ": $error");
|
||
return false;
|
||
}
|
||
|
||
if($field->flags & Field::flagFieldgroupContext) {
|
||
// field already in fieldgroup context
|
||
} else if($hasPage && $hasPage->template->fieldgroup->hasFieldContext($field)) {
|
||
// get in context of current page template’s fieldgroup, if applicable
|
||
$field = $hasPage->template->fieldgroup->getFieldContext($field->id);
|
||
}
|
||
|
||
// identify settings to apply
|
||
$data = array();
|
||
|
||
foreach($this->inputfield->getSettingNames(array('tinymce', 'field')) as $name) {
|
||
$value = $field->get($name);
|
||
if($value !== null) $data[$name] = $value;
|
||
}
|
||
|
||
// apply settings
|
||
$this->inputfield->data($data);
|
||
|
||
return $field;
|
||
}
|
||
|
||
}
|