1563 lines
45 KiB
PHP
1563 lines
45 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @copyright Copyright (c) 2009-2022 Ryan Demmer. All rights reserved
|
|
* @license GNU/GPL 2 or later - http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
* JCE is free software. This version may have been modified pursuant
|
|
* to the GNU General Public License, and as distributed it includes or
|
|
* is derivative of works licensed under the GNU General Public License or
|
|
* other free or open source software licenses
|
|
*/
|
|
defined('JPATH_PLATFORM') or die;
|
|
|
|
class WFEditor
|
|
{
|
|
// Editor instance
|
|
protected static $instances;
|
|
|
|
/**
|
|
* Profile object.
|
|
*
|
|
* @var object
|
|
*/
|
|
private $profile = null;
|
|
|
|
/**
|
|
* Context hash.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $context = '';
|
|
|
|
/**
|
|
* Array of linked scripts.
|
|
*
|
|
* @var array
|
|
*/
|
|
private $scripts = array();
|
|
|
|
/**
|
|
* Array of linked style sheets.
|
|
*
|
|
* @var array
|
|
*/
|
|
private $stylesheets = array();
|
|
|
|
/**
|
|
* Array of included style declarations.
|
|
*
|
|
* @var array
|
|
*/
|
|
private $styles = array();
|
|
|
|
/**
|
|
* Array of scripts placed in the header.
|
|
*
|
|
* @var array
|
|
*/
|
|
private $javascript = array();
|
|
|
|
/**
|
|
* Array of core plugins
|
|
*
|
|
* @var array
|
|
*/
|
|
private static $plugins = array('core', 'help', 'autolink', 'effects', 'cleanup', 'code', 'format', 'importcss', 'colorpicker', 'blobupload', 'upload', 'figure', 'ui', 'noneditable', 'branding');
|
|
|
|
/**
|
|
* Initialization state
|
|
*
|
|
* @var boolean
|
|
*/
|
|
public $initialized = false;
|
|
|
|
private function addScript($url)
|
|
{
|
|
$url = $this->addAssetVersion($url);
|
|
$this->scripts[] = $url;
|
|
}
|
|
|
|
private function addStyleSheet($url)
|
|
{
|
|
$url = $this->addAssetVersion($url);
|
|
$this->stylesheets[] = $url;
|
|
}
|
|
|
|
private function addScriptDeclaration($text)
|
|
{
|
|
$this->javascript[] = $text;
|
|
}
|
|
|
|
private function addStyleDeclaration($text)
|
|
{
|
|
$this->styles[] = $text;
|
|
}
|
|
|
|
public function getScripts()
|
|
{
|
|
return $this->scripts;
|
|
}
|
|
|
|
public function getStyleSheets()
|
|
{
|
|
return $this->stylesheets;
|
|
}
|
|
|
|
public function getScriptDeclaration()
|
|
{
|
|
return $this->javascript;
|
|
}
|
|
|
|
public function __construct($config = array())
|
|
{
|
|
$app = JFactory::getApplication();
|
|
$wf = WFApplication::getInstance();
|
|
|
|
if (!isset($config['plugin'])) {
|
|
$config['plugin'] = '';
|
|
}
|
|
|
|
if (!isset($config['profile_id'])) {
|
|
$config['profile_id'] = 0;
|
|
}
|
|
|
|
// trigger event
|
|
$app->triggerEvent('onBeforeWfEditorLoad');
|
|
|
|
// set profile
|
|
$this->profile = $wf->getProfile($config['plugin'], $config['profile_id']);
|
|
|
|
// set context
|
|
$this->context = $wf->getContext();
|
|
}
|
|
|
|
/**
|
|
* Returns a reference to a editor object.
|
|
*
|
|
* This method must be invoked as:
|
|
* <pre> $editor =WFEditor::getInstance();</pre>
|
|
*
|
|
* @return JCE The editor object
|
|
*/
|
|
public static function getInstance($config = array())
|
|
{
|
|
$signature = md5(serialize($config));
|
|
|
|
if (empty(self::$instances[$signature])) {
|
|
self::$instances[$signature] = new self($config);
|
|
}
|
|
|
|
return self::$instances[$signature];
|
|
}
|
|
|
|
private function addAssetVersion($url)
|
|
{
|
|
$version = md5(self::getVersion());
|
|
|
|
if (strpos($url, '?') === false) {
|
|
$url .= '?' . $version;
|
|
} else {
|
|
$url .= '&' . $version;
|
|
}
|
|
|
|
return $url;
|
|
}
|
|
|
|
public function init()
|
|
{
|
|
if ($this->initialized) {
|
|
return $this;
|
|
}
|
|
|
|
$this->initialized = true;
|
|
|
|
$settings = $this->getSettings();
|
|
|
|
JFactory::getApplication()->triggerEvent('onBeforeWfEditorRender', array(&$settings));
|
|
|
|
$this->render($settings);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Legacy function to build the editor
|
|
*
|
|
* @return String
|
|
*/
|
|
public function buildEditor()
|
|
{
|
|
$this->init()->getOutput();
|
|
}
|
|
|
|
/**
|
|
* Legacy function to get the editor settings
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getEditorSettings()
|
|
{
|
|
return $this->getSettings();
|
|
}
|
|
|
|
private function getCompressionOptions()
|
|
{
|
|
$wf = WFApplication::getInstance();
|
|
|
|
// check for joomla debug mode
|
|
$debug = JFactory::getConfig()->get('debug');
|
|
|
|
// default compression states
|
|
$options = array(
|
|
'javascript' => 0,
|
|
'css' => 0,
|
|
);
|
|
|
|
// set compression states, only if debug mode is off
|
|
if ((int) $debug === 0) {
|
|
$options = array(
|
|
'javascript' => (int) $wf->getParam('editor.compress_javascript', 0, 0),
|
|
'css' => (int) $wf->getParam('editor.compress_css', 0, 0),
|
|
);
|
|
}
|
|
|
|
return $options;
|
|
}
|
|
|
|
private function assignEditorSkin(&$settings)
|
|
{
|
|
// get an editor instance
|
|
$wf = WFApplication::getInstance();
|
|
|
|
// assign skin - new default is "modern"
|
|
$settings['skin'] = $wf->getParam('editor.toolbar_theme', 'modern');
|
|
|
|
if (empty($settings['skin'])) {
|
|
$settings['skin'] = 'modern';
|
|
}
|
|
|
|
if (strpos($settings['skin'], '.') !== false) {
|
|
list($settings['skin'], $settings['skin_variant']) = explode('.', $settings['skin']);
|
|
}
|
|
|
|
// classic has been removed
|
|
if ($settings['skin'] == 'classic') {
|
|
$settings['skin'] = 'default';
|
|
}
|
|
|
|
if ($settings['skin'] == 'mobile') {
|
|
$settings['skin'] = 'modern';
|
|
$settings['skin_variant'] = 'touch';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Porcess and assign custom configuration variables
|
|
*
|
|
* @param [Array] $settings
|
|
* @return void
|
|
*/
|
|
private function getCustomConfig(&$settings)
|
|
{
|
|
// get an editor instance
|
|
$wf = WFApplication::getInstance();
|
|
|
|
// Other - user specified
|
|
$userParams = $wf->getParam('editor.custom_config', '');
|
|
|
|
if ($userParams) {
|
|
// legacy format, eg: key:value;key:value
|
|
if (!WFUtility::isJson($userParams)) {
|
|
$userParams = explode(';', $userParams);
|
|
} else {
|
|
$userParams = json_decode($userParams, true);
|
|
}
|
|
|
|
// Remove values with invalid key
|
|
$userParams = array_filter($userParams, function ($key) {
|
|
return !is_numeric($key);
|
|
}, ARRAY_FILTER_USE_KEY);
|
|
|
|
foreach ($userParams as $userParam) {
|
|
$name = '';
|
|
$value = '';
|
|
|
|
// legacy string
|
|
if (is_string($userParam)) {
|
|
list($name, $value) = explode(':', $userParam);
|
|
}
|
|
|
|
// json associative array
|
|
if (is_array($userParam) && array_key_exists('name', $userParam)) {
|
|
extract($userParam);
|
|
}
|
|
|
|
if ($name && $value !== '') {
|
|
$settings[$name] = trim($value, " \t\n\r\0\x0B'\"");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private function isSkinRtl()
|
|
{
|
|
$language = JFactory::getLanguage();
|
|
|
|
if ($language->getTag() === WFLanguage::getTag()) {
|
|
return $language->isRTL();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private function getLanguageDirection()
|
|
{
|
|
$user = JFactory::getUser();
|
|
$params = JComponentHelper::getParams('com_languages');
|
|
$locale = $user->getParam('language', $params->get('site', 'en-GB'));
|
|
|
|
$language = JLanguage::getInstance($locale);
|
|
|
|
return $language->isRTL() ? 'rtl' : 'ltr';
|
|
}
|
|
|
|
protected function getLanguageCode()
|
|
{
|
|
return WFLanguage::getCode();
|
|
}
|
|
|
|
protected function getLanguageTag()
|
|
{
|
|
return WFLanguage::getTag();
|
|
}
|
|
|
|
public function getSettings()
|
|
{
|
|
// get an editor instance
|
|
$wf = WFApplication::getInstance();
|
|
|
|
// create token
|
|
$token = JSession::getFormToken();
|
|
|
|
// get editor version
|
|
$version = self::getVersion();
|
|
|
|
// get form token
|
|
$token = JSession::getFormToken();
|
|
|
|
$settings = array(
|
|
'token' => JSession::getFormToken(),
|
|
'base_url' => JURI::root(),
|
|
'language' => $this->getLanguageCode(),
|
|
'directionality' => $this->getLanguageDirection(),
|
|
'theme' => 'none',
|
|
'plugins' => '',
|
|
'skin' => 'default',
|
|
'query' => array(
|
|
$token => 1,
|
|
'context' => $this->context,
|
|
),
|
|
);
|
|
|
|
// if a profile is set
|
|
if (is_object($this->profile)) {
|
|
jimport('joomla.filesystem.folder');
|
|
|
|
$settings['query']['profile_id'] = $this->profile->id;
|
|
|
|
$settings = array_merge($settings, array('theme' => 'advanced'), $this->getToolbar());
|
|
|
|
// add plugins
|
|
$plugins = $this->getPlugins();
|
|
|
|
// add core plugins
|
|
if (!empty($plugins['core'])) {
|
|
$settings['plugins'] = array_values($plugins['core']);
|
|
}
|
|
|
|
// add external plugins
|
|
if (!empty($plugins['external'])) {
|
|
$settings['external_plugins'] = $plugins['external'];
|
|
}
|
|
|
|
// Theme and skins
|
|
$theme = array(
|
|
'toolbar_location' => array('top', 'top', 'string'),
|
|
'toolbar_align' => array('left', 'left', 'string'),
|
|
'statusbar_location' => array('bottom', 'bottom', 'string'),
|
|
'path' => array(1, 1, 'boolean'),
|
|
'resizing' => array(1, 0, 'boolean'),
|
|
'resize_horizontal' => array(1, 1, 'boolean'),
|
|
);
|
|
|
|
// set rows key to pass to plugin config
|
|
$settings['rows'] = $this->profile->rows;
|
|
|
|
foreach ($theme as $k => $v) {
|
|
$settings['theme_' . $k] = $wf->getParam('editor.' . $k, $v[0], $v[1], $v[2]);
|
|
}
|
|
|
|
$settings['width'] = $wf->getParam('editor.width');
|
|
$settings['height'] = $wf->getParam('editor.height');
|
|
|
|
// process and assign the editor skin
|
|
$this->assignEditorSkin($settings);
|
|
|
|
// get body class if any
|
|
$body_class = $wf->getParam('editor.body_class', '');
|
|
|
|
// check for editor reset - options are 1, 0, auto
|
|
$settings['content_style_reset'] = $wf->getParam('editor.content_style_reset', 'auto');
|
|
|
|
// if enabled, add the "mceContentReset" class to the body
|
|
$content_reset = $settings['content_style_reset'] == 1 ? 'mceContentReset' : '';
|
|
|
|
// combine body class and reset
|
|
$settings['body_class'] = trim($body_class . ' ' . $content_reset);
|
|
|
|
// set body id
|
|
$settings['body_id'] = $wf->getParam('editor.body_id', '');
|
|
|
|
// get stylesheets
|
|
$stylesheets = (array) self::getTemplateStyleSheets();
|
|
|
|
// set stylesheets as string
|
|
$settings['content_css'] = implode(',', $stylesheets);
|
|
|
|
if (WF_EDITOR_PRO) {
|
|
// Editor Toggle
|
|
$settings['toggle'] = $wf->getParam('editor.toggle', 0, 0);
|
|
$settings['toggle_label'] = htmlspecialchars($wf->getParam('editor.toggle_label', ''));
|
|
$settings['toggle_state'] = $wf->getParam('editor.toggle_state', 1, 1);
|
|
}
|
|
|
|
// use cookies to store state
|
|
$settings['use_state_cookies'] = (bool) $wf->getParam('editor.use_cookies', 1);
|
|
|
|
// Set active tab
|
|
$settings['active_tab'] = 'wf-editor-' . $wf->getParam('editor.active_tab', 'wysiwyg');
|
|
|
|
$settings['invalid_elements'] = array();
|
|
|
|
// Get all optional plugin configuration options
|
|
$this->getPluginConfig($settings);
|
|
|
|
// clean up invalid_elements
|
|
if (!empty($settings['invalid_elements'])) {
|
|
$settings['invalid_elements'] = array_values($settings['invalid_elements']);
|
|
}
|
|
|
|
} else {
|
|
$settings['readonly'] = true;
|
|
}
|
|
|
|
// get compression options stylesheet
|
|
$settings['compress'] = $this->getCompressionOptions();
|
|
|
|
// set css compression
|
|
if ($settings['compress']['css']) {
|
|
$this->addStyleSheet(JURI::base(true) . '/index.php?option=com_jce&task=editor.pack&type=css&' . http_build_query((array) $settings['query']));
|
|
} else {
|
|
// CSS
|
|
$this->addStyleSheet($this->getURL(true) . '/libraries/css/editor.min.css');
|
|
|
|
// load default skin
|
|
$this->addStyleSheet($this->getURL(true) . '/tiny_mce/themes/advanced/skins/default/ui.css');
|
|
|
|
// load other skin
|
|
if ($settings['skin'] != 'default') {
|
|
$this->addStyleSheet($this->getURL(true) . '/tiny_mce/themes/advanced/skins/' . $settings['skin'] . '/ui.css');
|
|
}
|
|
|
|
// load variant
|
|
if (isset($settings['skin_variant'])) {
|
|
$this->addStyleSheet($this->getURL(true) . '/tiny_mce/themes/advanced/skins/' . $settings['skin'] . '/ui_' . $settings['skin_variant'] . '.css');
|
|
}
|
|
}
|
|
|
|
if ($this->isSkinRtl()) {
|
|
$settings['skin_directionality'] = 'rtl';
|
|
}
|
|
|
|
// set javascript compression script
|
|
if ($settings['compress']['javascript']) {
|
|
$this->addScript(JURI::base(true) . '/index.php?option=com_jce&task=editor.pack&' . http_build_query((array) $settings['query']));
|
|
} else {
|
|
// Tinymce
|
|
$this->addScript($this->getURL(true) . '/tiny_mce/tiny_mce.js');
|
|
|
|
// Editor
|
|
$this->addScript($this->getURL(true) . '/libraries/js/editor.min.js');
|
|
}
|
|
|
|
// language
|
|
$this->addScript(JURI::base(true) . '/index.php?option=com_jce&task=editor.loadlanguages&lang=' . $settings['language'] . '&' . http_build_query((array) $settings['query']));
|
|
|
|
$this->getCustomConfig($settings);
|
|
|
|
// process settings
|
|
array_walk($settings, function (&$value, $key) {
|
|
// remove 'rows' key from $settings
|
|
if ($key == "rows") {
|
|
$value = '';
|
|
}
|
|
|
|
// implode standard arrays
|
|
if (is_array($value) && $value === array_values($value)) {
|
|
$value = implode(',', $value);
|
|
}
|
|
|
|
// convert json strings to objects to prevent encoding
|
|
if (is_string($value)) {
|
|
// decode string
|
|
$val = json_decode($value);
|
|
|
|
// valid json
|
|
if ($val) {
|
|
$value = $val;
|
|
}
|
|
}
|
|
|
|
// convert stringified booleans to booleans
|
|
if (is_string($value) && $value == 'true') {
|
|
$value = true;
|
|
}
|
|
|
|
if (is_string($value) && $value == 'false') {
|
|
$value = false;
|
|
}
|
|
});
|
|
|
|
// Remove empty values
|
|
$settings = array_filter($settings, function ($value) {
|
|
return $value !== '';
|
|
});
|
|
|
|
return $settings;
|
|
}
|
|
|
|
public function render($settings)
|
|
{
|
|
// get an editor instance
|
|
$wf = WFApplication::getInstance();
|
|
|
|
// encode as json string
|
|
$tinymce = json_encode($settings, JSON_NUMERIC_CHECK | JSON_UNESCAPED_SLASHES);
|
|
|
|
$this->addScriptDeclaration("try{WfEditor.init(" . $tinymce . ");}catch(e){console.debug(e);}");
|
|
|
|
if (is_object($this->profile)) {
|
|
if ($wf->getParam('editor.callback_file')) {
|
|
$this->addScript(JURI::root(true) . '/' . $wf->getParam('editor.callback_file'));
|
|
}
|
|
// add callback file if exists
|
|
if (is_file(JPATH_SITE . '/media/jce/js/editor.js')) {
|
|
$this->addScript(JURI::root(true) . '/media/jce/js/editor.js');
|
|
}
|
|
|
|
// add custom editor.css if exists
|
|
if (is_file(JPATH_SITE . '/media/jce/css/editor.css')) {
|
|
$this->addStyleSheet(JURI::root(true) . '/media/jce/css/editor.css');
|
|
}
|
|
}
|
|
}
|
|
|
|
private function getOutput()
|
|
{
|
|
$document = JFactory::getDocument();
|
|
|
|
$end = $document->_getLineEnd();
|
|
$tab = $document->_getTab();
|
|
|
|
$version = self::getVersion();
|
|
|
|
$output = '';
|
|
|
|
foreach ($this->stylesheets as $stylesheet) {
|
|
|
|
// don't add hash to dynamic php url
|
|
if (strpos($stylesheet, 'index.php') === false) {
|
|
$version = md5(basename($stylesheet) . $version);
|
|
|
|
if (strpos($stylesheet, '?') === false) {
|
|
$stylesheet .= '?' . $version;
|
|
} else {
|
|
$stylesheet .= '&' . $version;
|
|
}
|
|
}
|
|
|
|
$output .= $tab . '<link rel="stylesheet" href="' . $stylesheet . '" type="text/css" />' . $end;
|
|
}
|
|
|
|
foreach ($this->scripts as $script) {
|
|
|
|
// don't add hash to dynamic php url
|
|
if (strpos($script, 'index.php') === false) {
|
|
$version = md5(basename($script) . $version);
|
|
|
|
if (strpos($script, '?') === false) {
|
|
$script .= '?' . $version;
|
|
} else {
|
|
$script .= '&' . $version;
|
|
}
|
|
}
|
|
$output .= $tab . '<script data-cfasync="false" type="text/javascript" src="' . $script . '" defer></script>' . $end;
|
|
}
|
|
|
|
foreach ($this->javascript as $script) {
|
|
$output .= $tab . '<script data-cfasync="false" type="text/javascript">' . $script . '</script>' . $end;
|
|
}
|
|
|
|
foreach ($this->styles as $style) {
|
|
$output .= $tab . '<style type="text/css">' . $style . '</style>' . $end;
|
|
}
|
|
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Get the current version from the editor manifest.
|
|
*
|
|
* @return Version
|
|
*/
|
|
private static function getVersion()
|
|
{
|
|
return WF_VERSION;
|
|
}
|
|
|
|
/**
|
|
* Check if an icon already exists in a toolbar row
|
|
*
|
|
* @param [Array] $rows
|
|
* @param [Mixed] $icon
|
|
* @return Boolean
|
|
*/
|
|
private function rowHasIcon($rows, $icon)
|
|
{
|
|
$found = false;
|
|
|
|
foreach ($rows as $key => $row) {
|
|
if (in_array($icon, $row)) {
|
|
$found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $found;
|
|
}
|
|
|
|
/**
|
|
* Return a list of icons for each JCE editor row.
|
|
*
|
|
* @param string The number of rows
|
|
*
|
|
* @return The row array
|
|
*/
|
|
private function getToolbar()
|
|
{
|
|
$wf = WFApplication::getInstance();
|
|
$rows = array('theme_buttons1' => array(), 'theme_buttons2' => array(), 'theme_buttons3' => array());
|
|
|
|
// we need a profile object and some defined rows
|
|
if (!is_object($this->profile) || empty($this->profile->rows)) {
|
|
return $rows;
|
|
}
|
|
|
|
// get plugins
|
|
$plugins = JcePluginsHelper::getPlugins();
|
|
|
|
// get core commands
|
|
$commands = JcePluginsHelper::getCommands();
|
|
|
|
// merge plugins and commands
|
|
$icons = array_merge($commands, $plugins);
|
|
|
|
// create an array of rows
|
|
$lists = explode(';', $this->profile->rows);
|
|
|
|
// backwards compatability map
|
|
$map = array(
|
|
'paste' => 'clipboard',
|
|
'spacer' => '|',
|
|
'forecolor' => 'fontcolor',
|
|
'backcolor' => 'backcolor',
|
|
);
|
|
|
|
$x = 0;
|
|
|
|
for ($i = 1; $i <= count($lists); ++$i) {
|
|
$buttons = array();
|
|
$items = explode(',', $lists[$x]);
|
|
|
|
// map legacy values etc.
|
|
array_walk($items, function (&$item) use ($map) {
|
|
if (array_key_exists($item, $map)) {
|
|
$item = $map[$item];
|
|
}
|
|
});
|
|
|
|
// remove duplicates
|
|
$items = array_unique($items);
|
|
|
|
foreach ($items as $item) {
|
|
// set the plugin/command name
|
|
$name = $item;
|
|
|
|
// check if button should be in toolbar
|
|
if ($item !== '|') {
|
|
if (array_key_exists($item, $icons) === false) {
|
|
continue;
|
|
}
|
|
|
|
// assign icon
|
|
$item = $icons[$item]->icon;
|
|
}
|
|
|
|
// check for custom plugin buttons
|
|
if (array_key_exists($name, $plugins)) {
|
|
$custom = $wf->getParam($name . '.buttons');
|
|
|
|
if (!empty($custom)) {
|
|
$custom = array_filter((array) $custom);
|
|
|
|
if (empty($custom)) {
|
|
$item = '';
|
|
} else {
|
|
$a = array();
|
|
|
|
foreach (explode(',', $item) as $s) {
|
|
if (in_array($s, $custom) || $s == '|') {
|
|
$a[] = $s;
|
|
}
|
|
}
|
|
|
|
$item = implode(',', $a);
|
|
|
|
// remove leading or trailing |
|
|
$item = trim($item, '|');
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!empty($item)) {
|
|
// remove double spacer
|
|
$item = preg_replace('#(\|,)+#', '|,', $item);
|
|
|
|
if ($this->rowHasIcon($rows, $item)) {
|
|
continue;
|
|
}
|
|
|
|
$buttons[] = $item;
|
|
}
|
|
}
|
|
|
|
if (!empty($buttons)) {
|
|
$rows['theme_buttons' . $i] = $buttons;
|
|
}
|
|
|
|
++$x;
|
|
}
|
|
|
|
return $rows;
|
|
}
|
|
|
|
/**
|
|
* Get dependencies for each plugin from its config.php file.
|
|
*
|
|
* @param string $plugin Plugin name
|
|
* @param string $path Optional pah to plugin folder
|
|
*
|
|
* @return mixed Array of dependencies or false
|
|
*/
|
|
protected static function getDependencies($plugin, $path)
|
|
{
|
|
$file = $path . '/' . $plugin . '/config.php';
|
|
|
|
// check if plugin has a config file
|
|
if (is_file($file)) {
|
|
include_once $file;
|
|
// create className
|
|
$classname = 'WF' . ucwords($plugin, '_') . 'PluginConfig';
|
|
|
|
if (method_exists($classname, 'getDependencies')) {
|
|
return (array) $classname::getDependencies();
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Add dependencies for each plugin to the main plugins array.
|
|
*
|
|
* @param array $items Array of plugin names
|
|
* @param string $path Optional path to check, defaults to TinyMCE plugin path
|
|
*/
|
|
protected static function addDependencies(&$items, $path = '')
|
|
{
|
|
// set default path
|
|
if (empty($path)) {
|
|
$path = WF_EDITOR_PLUGINS;
|
|
}
|
|
|
|
$x = count($items);
|
|
|
|
// loop backwards through items
|
|
while ($x--) {
|
|
// get dependencies for each item
|
|
$dependencies = self::getDependencies($items[$x], $path);
|
|
|
|
if (!empty($dependencies)) {
|
|
foreach ($dependencies as $dependency) {
|
|
// add to beginning of items
|
|
array_unshift($items, $dependency);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Determine whether the editor has a profile assigned
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function hasProfile()
|
|
{
|
|
return is_object($this->profile);
|
|
}
|
|
|
|
/**
|
|
* Determine whether a plugin is loaded
|
|
*
|
|
* @param [string] $name
|
|
* @return boolean
|
|
*/
|
|
public function hasPlugin($name)
|
|
{
|
|
$plugins = $this->getPlugins();
|
|
|
|
if (in_array($name, $plugins['core'])) {
|
|
return true;
|
|
}
|
|
|
|
if (!empty($plugins['external'])) {
|
|
if (array_key_exists($name, $plugins['external'])) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Return a list of published JCE plugins.
|
|
*
|
|
* @return string list
|
|
*/
|
|
public function getPlugins()
|
|
{
|
|
static $plugins;
|
|
|
|
$wf = WFApplication::getInstance();
|
|
|
|
if (is_object($this->profile)) {
|
|
if (!is_array($plugins)) {
|
|
// get plugin items from profile
|
|
$items = explode(',', $this->profile->plugins);
|
|
|
|
// get core and installed plugins list
|
|
$list = JcePluginsHelper::getPlugins();
|
|
|
|
// check that the plugin is available
|
|
$items = array_filter($items, function ($item) use ($list) {
|
|
return in_array($item, array_keys($list));
|
|
});
|
|
|
|
// add advlists plugin if lists are loaded
|
|
if (in_array('lists', $items)) {
|
|
$items[] = 'advlist';
|
|
}
|
|
|
|
// Load wordcount if path is enabled
|
|
if ($wf->getParam('editor.path', 1)) {
|
|
$items[] = 'wordcount';
|
|
}
|
|
|
|
// reset index
|
|
$items = array_values($items);
|
|
|
|
// add plugin dependencies
|
|
self::addDependencies($items);
|
|
|
|
// add core plugins
|
|
$items = array_merge(self::$plugins, $items);
|
|
|
|
// remove duplicates and empty values
|
|
$items = array_unique(array_filter($items));
|
|
|
|
// create plugins array
|
|
$plugins = array('core' => array(), 'external' => array());
|
|
|
|
// check installed plugins are valid
|
|
foreach ($list as $name => $attribs) {
|
|
// skip core plugins
|
|
if ($attribs->core) {
|
|
continue;
|
|
}
|
|
|
|
// find plugin key in plugins list
|
|
$pos = array_search($name, $items);
|
|
|
|
// check it is in profile plugin list
|
|
if ($pos === false) {
|
|
continue;
|
|
}
|
|
|
|
// remove from items array
|
|
unset($items[$pos]);
|
|
|
|
// reset index
|
|
$items = array_values($items);
|
|
|
|
// add to array
|
|
$plugins['external'][$name] = JURI::root(true) . '/' . $attribs->url . '/editor_plugin.js';
|
|
}
|
|
|
|
// remove missing plugins
|
|
$items = array_filter($items, function ($item) {
|
|
if (WF_EDITOR_PRO && $item == 'branding') {
|
|
return false;
|
|
}
|
|
|
|
return is_file(WF_EDITOR_PLUGINS . '/' . $item . '/editor_plugin.js');
|
|
});
|
|
|
|
// update core plugins
|
|
$plugins['core'] = $items;
|
|
}
|
|
}
|
|
|
|
return $plugins;
|
|
}
|
|
|
|
/**
|
|
* Get all loaded plugins config options.
|
|
*
|
|
* @param array $settings passed by reference
|
|
*/
|
|
private function getPluginConfig(&$settings)
|
|
{
|
|
$core = (array) $settings['plugins'];
|
|
$items = array();
|
|
|
|
// Core plugins
|
|
foreach ($core as $plugin) {
|
|
$file = WF_EDITOR_PLUGINS . '/' . $plugin . '/config.php';
|
|
|
|
if (is_file($file)) {
|
|
require_once $file;
|
|
// add plugin name to array
|
|
$items[] = $plugin;
|
|
}
|
|
}
|
|
|
|
// Installed plugins
|
|
if (array_key_exists('external_plugins', $settings)) {
|
|
$installed = (array) $settings['external_plugins'];
|
|
|
|
foreach ($installed as $plugin => $path) {
|
|
$path = dirname($path);
|
|
$root = JURI::root(true);
|
|
|
|
if (empty($root)) {
|
|
$path = WFUtility::makePath(JPATH_SITE, $path);
|
|
} else {
|
|
$path = str_replace($root, JPATH_SITE, $path);
|
|
}
|
|
|
|
$file = $path . '/config.php';
|
|
|
|
// try legacy path...
|
|
if (!is_file($file)) {
|
|
$file = $path . '/classes/config.php';
|
|
}
|
|
|
|
if (is_file($file)) {
|
|
require_once $file;
|
|
// add plugin name to array
|
|
$items[] = $plugin;
|
|
}
|
|
}
|
|
}
|
|
|
|
$delim = array('-', '_');
|
|
|
|
// loop through list and create/call method
|
|
foreach ($items as $plugin) {
|
|
$name = str_replace($delim, ' ', $plugin);
|
|
|
|
// Create class name
|
|
$classname = 'WF' . ucwords($name) . 'PluginConfig';
|
|
|
|
// remove space
|
|
$classname = str_replace(' ', '', $classname);
|
|
|
|
// Check class and method are callable, and call
|
|
if (class_exists($classname) && method_exists($classname, 'getConfig')) {
|
|
call_user_func_array(array($classname, 'getConfig'), array(&$settings));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove keys from an array.
|
|
*/
|
|
public function removeKeys(&$array, $keys)
|
|
{
|
|
if (!is_array($keys)) {
|
|
$keys = array($keys);
|
|
}
|
|
|
|
$array = array_diff($array, $keys);
|
|
}
|
|
|
|
/**
|
|
* Add keys to an array.
|
|
*
|
|
* @return The string list with added key or the key
|
|
*
|
|
* @param string The array
|
|
* @param string The keys to add
|
|
*/
|
|
public function addKeys(&$array, $keys)
|
|
{
|
|
if (!is_array($keys)) {
|
|
$keys = array($keys);
|
|
}
|
|
$array = array_unique(array_merge($array, $keys));
|
|
}
|
|
|
|
/**
|
|
* Get a list of editor font families.
|
|
*
|
|
* @return string font family list
|
|
*
|
|
* @param string $add Font family to add
|
|
* @param string $remove Font family to remove
|
|
*
|
|
* Deprecated in 2.3.4
|
|
*/
|
|
public function getEditorFonts()
|
|
{
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Return the current site template name.
|
|
*/
|
|
private static function getSiteTemplates()
|
|
{
|
|
$db = JFactory::getDBO();
|
|
$app = JFactory::getApplication();
|
|
$id = 0;
|
|
|
|
// only process when front-end editing
|
|
if ($app->getClientId() == 0) {
|
|
$menus = $app->getMenu();
|
|
$menu = $menus->getActive();
|
|
|
|
if ($menu) {
|
|
$id = isset($menu->template_style_id) ? $menu->template_style_id : $menu->id;
|
|
}
|
|
}
|
|
|
|
$query = $db->getQuery(true);
|
|
$query->select('id, template AS name, params, home')->from('#__template_styles')->where(array('client_id = 0'));
|
|
|
|
$db->setQuery($query);
|
|
$templates = $db->loadObjectList();
|
|
|
|
$assigned = array();
|
|
|
|
foreach ($templates as $template) {
|
|
// default template
|
|
if ((string) $template->home == '1') {
|
|
$assigned[] = $template;
|
|
continue;
|
|
}
|
|
|
|
// assigned template
|
|
if ($id == $template->id) {
|
|
array_unshift($assigned, $template);
|
|
}
|
|
}
|
|
|
|
// return templates
|
|
return $assigned;
|
|
}
|
|
|
|
private static function hasEditorStylesheet($name)
|
|
{
|
|
// editor.css file is not suitable
|
|
if ($name == 'cassiopeia') {
|
|
return false;
|
|
}
|
|
|
|
// search for editor.css file using JPath
|
|
$file = JPath::find(array(
|
|
JPATH_SITE . '/templates/' . $name . '/css',
|
|
JPATH_SITE . '/media/templates/site/' . $name . '/css'
|
|
), 'editor.css');
|
|
|
|
if ($file && filesize($file) > 0) {
|
|
// make relative
|
|
$file = str_replace(JPATH_SITE, '', $file);
|
|
|
|
// remove leading slash
|
|
$file = trim($file, '/');
|
|
|
|
return $file;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static function getTemplateStyleSheetsList($absolute = false)
|
|
{
|
|
jimport('joomla.filesystem.folder');
|
|
jimport('joomla.filesystem.file');
|
|
|
|
// set default url as empty value
|
|
$url = '';
|
|
// set default template as empty value
|
|
$template = (object) array('name' => '');
|
|
// use editor default styles
|
|
$styles = '';
|
|
// stylesheets
|
|
$stylesheets = array();
|
|
// files
|
|
$files = array();
|
|
|
|
// get templates
|
|
$templates = self::getSiteTemplates();
|
|
|
|
foreach ($templates as $item) {
|
|
// Template CSS
|
|
$path = JPATH_SITE . '/templates/' . $item->name;
|
|
|
|
// get the first path that exists
|
|
if (is_dir($path)) {
|
|
// assign template
|
|
$template = $item;
|
|
break;
|
|
}
|
|
}
|
|
|
|
require_once WF_EDITOR_CLASSES . '/editor.php';
|
|
|
|
$wf = WFApplication::getInstance();
|
|
|
|
$global = intval($wf->getParam('editor.content_css', 1));
|
|
$profile = intval($wf->getParam('editor.profile_content_css', 2));
|
|
|
|
switch ($global) {
|
|
// Custom template css files
|
|
case 0:
|
|
// use getParam so result is cleaned
|
|
$global_custom = $wf->getParam('editor.content_css_custom', '');
|
|
|
|
if (is_string($global_custom)) {
|
|
$global_custom = explode(',', $global_custom);
|
|
}
|
|
|
|
foreach ($global_custom as $tmp) {
|
|
$tmp = trim($tmp);
|
|
|
|
if (empty($tmp)) {
|
|
continue;
|
|
}
|
|
|
|
// external url
|
|
if (strpos($tmp, '://') !== false) {
|
|
$files[] = $tmp;
|
|
continue;
|
|
}
|
|
|
|
// clean slashes
|
|
$tmp = preg_replace('#[/\\\\]+#', '/', $tmp);
|
|
|
|
// Replace $template variable with site template name
|
|
$tmp = str_replace('$template', $template->name, $tmp);
|
|
|
|
$file = JPATH_SITE . '/' . $tmp;
|
|
$list = array();
|
|
|
|
// check if path is a file
|
|
if (is_file($file)) {
|
|
$list[] = $file;
|
|
// find files using pattern
|
|
} else {
|
|
$list = glob($file);
|
|
}
|
|
|
|
if (!empty($list)) {
|
|
foreach ($list as $item) {
|
|
if (is_file($item) && preg_match('#\.(css|less)$#', $item)) {
|
|
$files[] = substr($item, strlen(JPATH_SITE) + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
// Template css (template.css or template_css.css)
|
|
case 1:
|
|
$files = array();
|
|
|
|
// check editor.css file first
|
|
$file = self::hasEditorStylesheet($template->name);
|
|
|
|
if ($file) {
|
|
$files[] = $file;
|
|
} else {
|
|
JFactory::getApplication()->triggerEvent('onWfGetTemplateStylesheets', array(&$files, $template));
|
|
}
|
|
|
|
break;
|
|
// Nothing, use editor default
|
|
case 2:
|
|
break;
|
|
}
|
|
|
|
switch ($profile) {
|
|
// add to global config value
|
|
case 0:
|
|
case 1:
|
|
$profile_custom = $wf->getParam('editor.profile_content_css_custom', '');
|
|
|
|
if (is_string($profile_custom)) {
|
|
$profile_custom = explode(',', $profile_custom);
|
|
}
|
|
|
|
$custom = array();
|
|
|
|
foreach ($profile_custom as $tmp) {
|
|
$tmp = trim($tmp);
|
|
|
|
if (empty($tmp)) {
|
|
continue;
|
|
}
|
|
|
|
// external url
|
|
if (strpos($tmp, '://') !== false) {
|
|
$custom[] = $tmp;
|
|
continue;
|
|
}
|
|
|
|
// clean slashes
|
|
$tmp = preg_replace('#[/\\\\]+#', '/', $tmp);
|
|
|
|
// Replace $template variable with site template name (defaults to 'system')
|
|
$tmp = str_replace('$template', $template->name, $tmp);
|
|
|
|
$list = array();
|
|
|
|
$file = JPATH_SITE . '/' . $tmp;
|
|
|
|
// check if path is a file
|
|
if (is_file($file)) {
|
|
$list[] = $file;
|
|
// find files using pattern
|
|
} else {
|
|
$list = glob($file);
|
|
}
|
|
|
|
if (!empty($list)) {
|
|
foreach ($list as $item) {
|
|
if (is_file($item) && preg_match('#\.(css|less)$#', $item)) {
|
|
$custom[] = substr($item, strlen(JPATH_SITE) + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// add to existing list
|
|
if ($profile === 0) {
|
|
$files = array_merge($files, $custom);
|
|
// overwrite global config value
|
|
} else {
|
|
$files = (array) $custom;
|
|
}
|
|
break;
|
|
// inherit global config value
|
|
case 2:
|
|
break;
|
|
}
|
|
|
|
// remove duplicates
|
|
$files = array_unique(array_filter($files));
|
|
|
|
// get the root directory
|
|
$root = $absolute ? JPATH_SITE : JURI::root(true);
|
|
|
|
// check for existence of each file and make array of stylesheets
|
|
foreach ($files as $file) {
|
|
if (empty($file)) {
|
|
continue;
|
|
}
|
|
|
|
// full path
|
|
if (strpos($file, '://') !== false) {
|
|
$stylesheets[] = $file;
|
|
continue;
|
|
}
|
|
|
|
// remove leading slash
|
|
$file = ltrim($file, '/');
|
|
|
|
$fullpath = JPATH_SITE . '/' . $file;
|
|
|
|
if (JFile::exists($fullpath)) {
|
|
// less
|
|
if (pathinfo($file, PATHINFO_EXTENSION) == 'less') {
|
|
$stylesheets[] = $fullpath;
|
|
continue;
|
|
}
|
|
|
|
$stylesheets[] = $root . '/' . $file;
|
|
}
|
|
}
|
|
|
|
// remove duplicates
|
|
$stylesheets = array_unique(array_filter($stylesheets));
|
|
|
|
return $stylesheets;
|
|
}
|
|
|
|
/**
|
|
* Get an array of stylesheets used by the editor.
|
|
* References the WFEditor class.
|
|
* If the list contains any LESS stylesheets, the list is returned as a URL to compile.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function getTemplateStyleSheets()
|
|
{
|
|
$stylesheets = self::getTemplateStyleSheetsList();
|
|
|
|
// check for less files in the array
|
|
$less = preg_grep('#\.less$#', $stylesheets);
|
|
|
|
// process less files etc.
|
|
if (!empty($less)) {
|
|
// create token
|
|
$token = JSession::getFormToken();
|
|
$version = self::getVersion();
|
|
|
|
return JURI::base(true) . '/index.php?option=com_jce&task=editor.compileless&' . $token . '=1';
|
|
}
|
|
|
|
return $stylesheets;
|
|
}
|
|
|
|
/**
|
|
* Get the URL of the editor.
|
|
*
|
|
* @param bool $relative
|
|
*
|
|
* @return string
|
|
*/
|
|
private function getURL($relative = false)
|
|
{
|
|
if ($relative) {
|
|
return JURI::root(true) . '/components/com_jce/editor';
|
|
}
|
|
|
|
return JURI::root() . 'components/com_jce/editor';
|
|
}
|
|
|
|
/**
|
|
* Pack / compress editor files.
|
|
*/
|
|
public function pack()
|
|
{
|
|
require_once WF_EDITOR_CLASSES . '/packer.php';
|
|
require_once WF_EDITOR_CLASSES . '/language.php';
|
|
|
|
$wf = WFApplication::getInstance();
|
|
$type = $wf->input->getWord('type', 'javascript');
|
|
|
|
// javascript
|
|
$packer = new WFPacker(array('type' => $type));
|
|
|
|
$themes = 'none';
|
|
$plugins = array();
|
|
|
|
$suffix = $wf->input->getWord('suffix', '');
|
|
|
|
// if a profile is set
|
|
if ($this->profile) {
|
|
$themes = 'advanced';
|
|
$plugins = $this->getPlugins();
|
|
}
|
|
|
|
$themes = explode(',', $themes);
|
|
|
|
// toolbar theme
|
|
$toolbar = explode('.', $wf->getParam('editor.toolbar_theme', 'default'));
|
|
|
|
// base skin value
|
|
$skin = $toolbar[0];
|
|
|
|
switch ($type) {
|
|
case 'language':
|
|
$files = array();
|
|
|
|
$data = $this->loadLanguages(array(), array(), '(^dlg$|_dlg$)', true);
|
|
$packer->setText($data);
|
|
|
|
break;
|
|
case 'javascript':
|
|
$files = array();
|
|
|
|
// add core file
|
|
$files[] = WF_EDITOR . '/tiny_mce/tiny_mce' . $suffix . '.js';
|
|
|
|
// Add themes in dev mode
|
|
foreach ($themes as $theme) {
|
|
$files[] = WF_EDITOR . '/tiny_mce/themes/' . $theme . '/editor_template' . $suffix . '.js';
|
|
}
|
|
|
|
// Add core plugins
|
|
foreach ($plugins['core'] as $plugin) {
|
|
if (in_array($plugin, self::$plugins)) {
|
|
continue;
|
|
}
|
|
|
|
$files[] = WF_EDITOR_PLUGINS . '/' . $plugin . '/editor_plugin' . $suffix . '.js';
|
|
}
|
|
|
|
// add external plugins
|
|
foreach ($plugins['external'] as $plugin => $path) {
|
|
$files[] = JPATH_SITE . '/plugins/jce/editor-' . $plugin . '/editor_plugin' . $suffix . '.js';
|
|
}
|
|
|
|
// add Editor file
|
|
$files[] = WF_EDITOR . '/libraries/js/editor.min.js';
|
|
|
|
break;
|
|
case 'css':
|
|
$slot = $wf->input->getCmd('slot', 'editor');
|
|
|
|
if ($slot == 'content') {
|
|
$files = array();
|
|
|
|
$files[] = WF_EDITOR_THEMES . '/' . $themes[0] . '/skins/' . $skin . '/content.css';
|
|
|
|
// get template stylesheets
|
|
$styles = self::getTemplateStyleSheetsList(true);
|
|
|
|
foreach ($styles as $style) {
|
|
if (JFile::exists($style)) {
|
|
$files[] = $style;
|
|
}
|
|
}
|
|
|
|
// Add core plugins
|
|
foreach ($plugins['core'] as $plugin) {
|
|
$content = WF_EDITOR_PLUGINS . '/' . $plugin . '/css/content.css';
|
|
|
|
if (JFile::exists($content)) {
|
|
$files[] = $content;
|
|
}
|
|
}
|
|
|
|
// add external plugins
|
|
foreach ($plugins['external'] as $plugin => $path) {
|
|
$content = JPATH_SITE . '/plugins/jce/editor-' . $plugin . '/css/content.css';
|
|
|
|
if (JFile::exists($content)) {
|
|
$files[] = $content;
|
|
}
|
|
}
|
|
} elseif ($slot == 'preview') {
|
|
$files = array();
|
|
$files[] = WF_EDITOR_PLUGINS . '/preview/css/preview.css';
|
|
// get template stylesheets
|
|
$styles = self::getTemplateStyleSheetsList(true);
|
|
foreach ($styles as $style) {
|
|
if (JFile::exists($style)) {
|
|
$files[] = $style;
|
|
}
|
|
}
|
|
} else {
|
|
$files = array();
|
|
|
|
$files[] = WF_EDITOR_LIBRARIES . '/css/editor.min.css';
|
|
|
|
$variant = '';
|
|
|
|
if (count($toolbar) > 1) {
|
|
$variant = $toolbar[1];
|
|
}
|
|
|
|
// load 'default'
|
|
$files[] = WF_EDITOR_THEMES . '/' . $themes[0] . '/skins/default/ui.css';
|
|
|
|
if ($skin !== 'default') {
|
|
$files[] = WF_EDITOR_THEMES . '/' . $themes[0] . '/skins/' . $skin . '/ui.css';
|
|
}
|
|
|
|
if (isset($variant)) {
|
|
$files[] = WF_EDITOR_THEMES . '/' . $themes[0] . '/skins/' . $skin . '/ui_' . $variant . '.css';
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
$cache_validation = (bool) $this->getParam('editor.compress_cache_validation', true);
|
|
|
|
$packer->setFiles($files);
|
|
$packer->pack(true, $cache_validation);
|
|
}
|
|
|
|
public function loadlanguages()
|
|
{
|
|
$parser = new WFLanguageParser(array('language' => $this->getLanguageTag(), 'plugins' => $this->getPlugins()));
|
|
$data = $parser->load();
|
|
$parser->output($data);
|
|
}
|
|
|
|
public function compileless()
|
|
{
|
|
$files = self::getTemplateStyleSheetsList(true);
|
|
|
|
if (!empty($files)) {
|
|
$packer = new WFPacker(array('files' => $files, 'type' => 'css'));
|
|
$packer->pack(false);
|
|
}
|
|
}
|
|
|
|
public function getToken($id)
|
|
{
|
|
return '<input type="hidden" name="' . JSession::getFormToken() . '" value="1" />';
|
|
}
|
|
|
|
/**
|
|
* Proxy function for legacy compatablity with 3rd party extensions that access the API directly
|
|
*
|
|
* @param string $key
|
|
* @param string $fallback
|
|
* @param string $default
|
|
* @param string $type
|
|
* @param boolean $allowempty
|
|
* @return void
|
|
*/
|
|
public function getParam($key, $fallback = '', $default = '', $type = 'string', $allowempty = true)
|
|
{
|
|
$wf = WFApplication::getInstance();
|
|
return $wf->getParam($key, $fallback, $default, $type, $allowempty);
|
|
}
|
|
}
|