$extra = array();
$this->addElement('select', 'editor_id', ts('WYSIWYG Editor'), $wysiwyg_options, $extra);
+ $this->addElement('submit', 'ckeditor_config', ts('Configure CKEditor'));
$editOptions = CRM_Core_OptionGroup::values('contact_edit_options', FALSE, FALSE, FALSE, 'AND v.filter = 0');
$this->assign('editOptions', $editOptions);
$this->_config->editor_id = $this->_params['editor_id'];
$this->postProcessCommon();
+
+ // If "Configure CKEditor" button was clicked
+ if (!empty($this->_params['ckeditor_config'])) {
+ // Suppress the "Saved" status message and redirect to the CKEditor Config page
+ $session = CRM_Core_Session::singleton();
+ $session->getStatus(TRUE);
+ $url = CRM_Utils_System::url('civicrm/admin/ckeditor', 'reset=1');
+ $session->pushUserContext($url);
+ }
}
}
--- /dev/null
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.6 |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2015 |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM. |
+ | |
+ | CiviCRM is free software; you can copy, modify, and distribute it |
+ | under the terms of the GNU Affero General Public License |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
+ | |
+ | CiviCRM is distributed in the hope that it will be useful, but |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
+ | See the GNU Affero General Public License for more details. |
+ | |
+ | You should have received a copy of the GNU Affero General Public |
+ | License and the CiviCRM Licensing Exception along |
+ | with this program; if not, contact CiviCRM LLC |
+ | at info[AT]civicrm[DOT]org. If you have questions about the |
+ | GNU Affero General Public License or the licensing of CiviCRM, |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC (c) 2004-2015
+ * $Id$
+ *
+ */
+
+/**
+ * Page for configuring CKEditor options
+ *
+ * Note that while this is implemented as a CRM_Core_Page, it is actually a form.
+ * Because the form needs to be submitted and refreshed via javascrit, it seemed like
+ * Quickform and CRM_Core_Form/Controller might get in the way.
+ */
+class CRM_Admin_Page_CKEditorConfig extends CRM_Core_Page {
+
+ const CONFIG_FILENAME = 'crm-ckeditor-config.js';
+
+ /**
+ * Default settings if config file has not been initialized
+ *
+ * @var array
+ */
+ public $defaultSettings = array(
+ 'skin' => 'moono',
+ 'extraPlugins' => '',
+ );
+
+ /**
+ * @return string
+ */
+ public function run() {
+ // If the form was submitted, take appropriate action.
+ if (!empty($_POST['revert'])) {
+ self::deleteConfigFile();
+ }
+ elseif (!empty($_POST['config'])) {
+ $this->save($_POST);
+ }
+
+ CRM_Core_Resources::singleton()
+ ->addScriptFile('civicrm', 'bower_components/ckeditor/samples/toolbarconfigurator/js/fulltoolbareditor.js', 1)
+ ->addScriptFile('civicrm', 'bower_components/ckeditor/samples/toolbarconfigurator/js/abstracttoolbarmodifier.js', 2)
+ ->addScriptFile('civicrm', 'bower_components/ckeditor/samples/toolbarconfigurator/js/toolbarmodifier.js', 3)
+ ->addScriptFile('civicrm', 'js/wysiwyg/admin.ckeditor-configurator.js', 10)
+ ->addStyleFile('civicrm', 'bower_components/ckeditor/samples/toolbarconfigurator/css/fontello.css')
+ ->addStyleFile('civicrm', 'bower_components/ckeditor/samples/css/samples.css')
+ ->addVars('ckConfig', array(
+ 'plugins' => array_values($this->getCKPlugins()),
+ ));
+
+ $this->assign('skins', $this->getCKSkins());
+ $this->assign('skin', $this->getConfigSetting('skin'));
+ $this->assign('extraPlugins', $this->getConfigSetting('extraPlugins'));
+ $this->assign('configUrl', self::getConfigUrl());
+ $this->assign('revertConfirm', htmlspecialchars(ts('Are you sure you want to revert all changes?', array('escape' => 'js'))));
+
+ CRM_Utils_System::appendBreadCrumb(array(array(
+ 'url' => CRM_Utils_System::url('civicrm/admin/setting/preferences/display', 'reset=1'),
+ 'title' => ts('Display Preferences'),
+ )));
+
+ return parent::run();
+ }
+
+ /**
+ * Generate the config js file based on posted data.
+ *
+ * @param array $params
+ */
+ public function save($params) {
+ $config = "/**\n"
+ . " * CKEditor config file auto-generated by CiviCRM.\n"
+ . " *\n"
+ . " * Note: This file will be overwritten if settings are modified at:\n"
+ . " * @link " . CRM_Utils_System::url(CRM_Utils_System::currentPath(), NULL, TRUE, NULL, FALSE) . "\n"
+ . " */\n\n"
+ // Standardize line-endings
+ . preg_replace('~\R~u', "\n", $params['config']);
+
+ // Use defaultSettings as a whitelist so we don't just insert any old junk into the file
+ foreach ($this->defaultSettings as $key => $default) {
+ if (isset($params[$key]) && strlen($params[$key])) {
+ $pos = strrpos($config, '};');
+ $setting = "\n\tconfig.$key = '{$params[$key]}';\n";
+ $config = substr_replace($config, $setting, $pos, 0);
+ }
+ }
+ self::saveConfigFile($config);
+ if (!empty($params['save'])) {
+ CRM_Core_Session::setStatus(ts("You may need to clear your browser's cache to see the changes in CiviCRM."), ts('CKEditor Saved'), 'success');
+ }
+ }
+
+ /**
+ * @return array
+ */
+ private function getCKPlugins() {
+ $plugins = array();
+ global $civicrm_root;
+ $pluginDir = CRM_Utils_file::addTrailingSlash($civicrm_root, '/') . 'bower_components/ckeditor/plugins';
+
+ foreach (glob($pluginDir . '/*', GLOB_ONLYDIR) as $dir) {
+ $dir = rtrim(str_replace('\\', '/', $dir), '/');
+ $name = substr($dir, strrpos($dir, '/') + 1);
+ $dir = CRM_Utils_file::addTrailingSlash($dir, '/');
+ if (is_file($dir . 'plugin.js')) {
+ $plugins[$name] = array(
+ 'id' => $name,
+ 'text' => ucfirst($name),
+ 'icon' => NULL,
+ );
+ if (is_dir($dir . "icons")) {
+ if (is_file($dir . "icons/$name.png")) {
+ $plugins[$name]['icon'] = "bower_components/ckeditor/plugins/$name/icons/$name.png";
+ }
+ elseif (glob($dir . "icons/*.png")) {
+ $icon = CRM_Utils_Array::first(glob($dir . "icons/*.png"));
+ $icon = rtrim(str_replace('\\', '/', $icon), '/');
+ $plugins[$name]['icon'] = "bower_components/ckeditor/plugins/$name/icons/" . substr($icon, strrpos($icon, '/') + 1);
+ }
+ }
+ }
+ }
+
+ return $plugins;
+ }
+
+ /**
+ * @return array
+ */
+ private function getCKSkins() {
+ $skins = array();
+ global $civicrm_root;
+ $skinDir = CRM_Utils_file::addTrailingSlash($civicrm_root, '/') . 'bower_components/ckeditor/skins';
+ foreach (glob($skinDir . '/*', GLOB_ONLYDIR) as $dir) {
+ $dir = rtrim(str_replace('\\', '/', $dir), '/');
+ $skins[] = substr($dir, strrpos($dir, '/') + 1);
+ }
+ return $skins;
+ }
+
+ /**
+ * @param $setting
+ * @return string
+ */
+ private function getConfigSetting($setting) {
+ $value = CRM_Utils_Array::value($setting, $this->defaultSettings, '');
+ $file = self::getConfigFile();
+ if ($file) {
+ $contents = file_get_contents($file);
+ $matches = array();
+ preg_match("/\sconfig\.$setting\s?=\s?'([^']*)'/", $contents, $matches);
+ if ($matches) {
+ $value = $matches[1];
+ }
+ }
+ return $value;
+ }
+
+ /**
+ * @return null|string
+ */
+ public static function getConfigUrl() {
+ if (self::getConfigFile()) {
+ // FIXME: Basing file path off imageUploadURL sucks, but it's all we got
+ $url = CRM_Utils_file::addTrailingSlash(CRM_Core_Config::singleton()->imageUploadURL, '/');
+ $url = str_replace('/persist/contribute/', '/persist/', $url);
+ return $url . SELF::CONFIG_FILENAME;
+ }
+ return NULL;
+ }
+
+ /**
+ * @param bool $checkIfFileExists
+ * If false, this fn will return fileName even if it doesn't exist
+ *
+ * @return null|string
+ */
+ public static function getConfigFile($checkIfFileExists = TRUE) {
+ // FIXME: Basing file path off imageUploadDir sucks, but it's all we got
+ $dir = CRM_Core_Config::singleton()->imageUploadDir;
+ $dir = CRM_Utils_file::addTrailingSlash(str_replace('\\', '/', $dir), '/');
+ $dir = str_replace('/persist/contribute/', '/persist/', $dir);
+ $fileName = $dir . SELF::CONFIG_FILENAME;
+ return !$checkIfFileExists || is_file($fileName) ? $fileName : NULL;
+ }
+
+ /**
+ * @param string $contents
+ */
+ public static function saveConfigFile($contents) {
+ $file = self::getConfigFile(FALSE);
+ file_put_contents($file, $contents);
+ }
+
+ /**
+ * Delete SELF::CONFIG_FILENAME
+ */
+ public static function deleteConfigFile() {
+ $file = self::getConfigFile();
+ if ($file) {
+ unlink($file);
+ }
+ }
+
+}
// Add resources from coreResourceList
$jsWeight = -9999;
- foreach ($this->coreResourceList() as $file) {
- if (substr($file, -2) == 'js') {
+ foreach ($this->coreResourceList() as $item) {
+ if (is_array($item)) {
+ $this->addSetting($item);
+ }
+ elseif (substr($item, -2) == 'js') {
// Don't bother looking for ts() calls in packages, there aren't any
- $translate = (substr($file, 0, 3) == 'js/');
- $this->addScriptFile('civicrm', $file, $jsWeight++, $region, $translate);
+ $translate = (substr($item, 0, 3) == 'js/');
+ $this->addScriptFile('civicrm', $item, $jsWeight++, $region, $translate);
}
else {
- $this->addStyleFile('civicrm', $file, -100, $region);
+ $this->addStyleFile('civicrm', $item, -100, $region);
}
}
if ($editor == "CKEditor") {
$items[] = "bower_components/ckeditor/ckeditor.js";
$items[] = "js/wysiwyg/crm.ckeditor.js";
+ $ckConfig = CRM_Admin_Page_CKEditorConfig::getConfigUrl();
+ if ($ckConfig) {
+ $items[] = array('config' => array('CKEditorCustomConfig' => $ckConfig));
+ }
}
// These scripts are only needed by back-office users
<page_callback>CRM_Badge_Form_Layout</page_callback>
<access_arguments>administer CiviCRM</access_arguments>
</item>
+ <item>
+ <path>civicrm/admin/ckeditor</path>
+ <title>Configure CKEditor</title>
+ <page_callback>CRM_Admin_Page_CKEditorConfig</page_callback>
+ <access_arguments>administer CiviCRM</access_arguments>
+ </item>
</menu>
$q = "reset=1&log_conn_id={$this->log_conn_id}&log_date={$this->log_date}";
$this->assign('revertURL', CRM_Report_Utils_Report::getNextUrl($this->detail, "$q&revert=1", FALSE, TRUE));
- $this->assign('revertConfirm', ts('Are you sure you want to revert all these changes?'));
+ $this->assign('revertConfirm', ts('Are you sure you want to revert all changes?'));
}
}
--- /dev/null
+// https://civicrm.org/licensing
+(function($, _) {
+ 'use strict';
+
+ // Weird conflict with drupal styles
+ $('body').removeClass('toolbar');
+
+ function format(item) {
+ var icon = '<span class="ui-icon ui-icon-gear"></span>';
+ if (item.icon) {
+ icon = '<img src="' + CRM.config.resourceBase + item.icon + '" />';
+ }
+ return icon + ' ' + item.text;
+ }
+
+ $('#extraPlugins').crmSelect2({
+ multiple: true,
+ closeOnSelect: false,
+ data: CRM.vars.ckConfig.plugins,
+ escapeMarkup: _.identity,
+ formatResult: format,
+ formatSelection: format
+ });
+
+ var toolbarModifier = new ToolbarConfigurator.ToolbarModifier( 'editor-basic' );
+
+ toolbarModifier.init(_.noop);
+
+ CKEDITOR.document.getById( 'toolbarModifierWrapper' ).append( toolbarModifier.mainContainer );
+
+ $('.toolbar button:last', '#toolbarModifierWrapper').hide();
+
+ $(function() {
+ var selectorOpen = false,
+ changedWhileOpen = false;
+
+ $('#toolbarModifierForm')
+ // The buttons in the configurator are not submit buttons!
+ .on('click', 'button', function(e) {
+ e.preventDefault();
+ })
+ .on('submit', function(e) {
+ $('.toolbar button:last', '#toolbarModifierWrapper')[0].click();
+ $('.configContainer textarea', '#toolbarModifierWrapper').attr('name', 'config');
+ })
+ .on('change', '.config-param', function(e) {
+ changedWhileOpen = true;
+ if (!selectorOpen) {
+ $('#toolbarModifierForm').submit().block();
+ }
+ })
+ // Debounce the change event so it only fires after the multiselect is closed
+ .on('select2-open', 'input.config-param', function(e) {
+ selectorOpen = true;
+ changedWhileOpen = false;
+ })
+ .on('select2-close', 'input.config-param', function(e) {
+ selectorOpen = false;
+ if (changedWhileOpen) {
+ $(this).change();
+ }
+ });
+ });
+
+})(CRM.$, CRM._);
\ No newline at end of file
browseUrl = CRM.config.userFrameworkResourceURL + "packages/kcfinder/browse.php?cms=civicrm",
uploadUrl = CRM.config.userFrameworkResourceURL + "packages/kcfinder/upload.php?cms=civicrm";
if ($(item).length) {
- editor = CKEDITOR.replace($(item)[0]);
+ editor = CKEDITOR.replace($(item)[0], {
+ filebrowserBrowseUrl: browseUrl + '&type=files',
+ filebrowserImageBrowseUrl: browseUrl + '&type=images',
+ filebrowserFlashBrowseUrl: browseUrl + '&type=flash',
+ filebrowserUploadUrl: uploadUrl + '&type=files',
+ filebrowserImageUploadUrl: uploadUrl + '&type=images',
+ filebrowserFlashUploadUrl: uploadUrl + '&type=flash',
+ customConfig: CRM.config.CKEditorCustomConfig
+ });
}
if (editor) {
- editor.config.filebrowserBrowseUrl = browseUrl + '&type=files';
- editor.config.filebrowserImageBrowseUrl = browseUrl + '&type=images';
- editor.config.filebrowserFlashBrowseUrl = browseUrl + '&type=flash';
- editor.config.filebrowserUploadUrl = uploadUrl + '&type=files';
- editor.config.filebrowserImageUploadUrl = uploadUrl + '&type=images';
- editor.config.filebrowserFlashUploadUrl = uploadUrl + '&type=flash';
editor.on('focus', function() {
$(item).trigger('focus');
});
</tr>
<tr class="crm-preferences-display-form-block-editor_id">
<td class="label">{$form.editor_id.label}</td>
- <td>{$form.editor_id.html}</td>
+ <td>
+ {$form.editor_id.html}
+
+ <span class="crm-button crm-icon-button" style="display:inline-block;vertical-align:middle;float:none!important;">
+ <span class="crm-button-icon ui-icon-gear"> </span>
+ {$form.ckeditor_config.html}
+ </span>
+ </td>
</tr>
<tr class="crm-preferences-display-form-block-description">
<td> </td>
placeholder: 'ui-state-highlight',
update: getSorting
});
+
+ function showCKEditorConfig() {
+ console.log($(this).val());
+ $('.crm-preferences-display-form-block-editor_id .crm-button').toggle($(this).val() == '2');
+ }
+ $('select[name=editor_id]').each(showCKEditorConfig).change(showCKEditorConfig);
});
</script>
{/literal}
--- /dev/null
+{*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.6 |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2015 |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM. |
+ | |
+ | CiviCRM is free software; you can copy, modify, and distribute it |
+ | under the terms of the GNU Affero General Public License |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
+ | |
+ | CiviCRM is distributed in the hope that it will be useful, but |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
+ | See the GNU Affero General Public License for more details. |
+ | |
+ | You should have received a copy of the GNU Affero General Public |
+ | License and the CiviCRM Licensing Exception along |
+ | with this program; if not, contact CiviCRM LLC |
+ | at info[AT]civicrm[DOT]org. If you have questions about the |
+ | GNU Affero General Public License or the licensing of CiviCRM, |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+*}
+<style>{literal}
+ .select2-results .ui-icon,
+ .select2-container .ui-icon,
+ .select2-results img,
+ .select2-container img {
+ display: inline-block;
+ position: relative;
+ top: 2px;
+ }
+ #toolbarModifierWrapper .toolbar button:last-child,
+ #toolbarModifierWrapper .toolbar button[data-group=config] {
+ display: none;
+ }
+{/literal}</style>
+{* Force the custom config file to reload by appending a new query string *}
+<script type="text/javascript">
+ {if $configUrl}CKEDITOR.config.customConfig = '{$configUrl}?{php}print str_replace(array(' ', '.'), array('', '='), microtime());{/php}'{/if};
+</script>
+
+<form method="post" action="{crmURL}" id="toolbarModifierForm">
+ <div class="crm-block crm-form-block">
+ <label for="skin">{ts}Skin{/ts}</label>
+ <select id="skin" name="skin" class="crm-select2 eight config-param">
+ {foreach from=$skins item='s'}
+ <option value="{$s}" {if $s == $skin}selected{/if}>{$s|ucfirst}</option>
+ {/foreach}
+ </select>
+
+ <label for="extraPlugins">{ts}Plugins{/ts}</label>
+ <input id="extraPlugins" name="extraPlugins" class="huge config-param" value="{$extraPlugins}" placeholder="{ts}Select optional extra features{/ts}">
+ </div>
+
+ <div class="editors-container">
+ <div id="editor-basic"></div>
+ <div id="editor-advanced"></div>
+ </div>
+
+ <div class="configurator">
+ <div>
+ <div id="toolbarModifierWrapper" class="active"></div>
+ </div>
+ </div>
+
+ <div class="crm-submit-buttons">
+ <span class="crm-button crm-icon-button">
+ <span class="crm-button-icon ui-icon-check"> </span> <input type="submit" value="{ts}Save{/ts}" name="save" class="crm-form-submit" accesskey="S"/>
+ </span>
+ <span class="crm-button crm-icon-button">
+ <span class="crm-button-icon ui-icon-cancel"> </span> <input type="submit" value="{ts}Revert to Default{/ts}" name="revert" class="crm-form-submit" onclick="return confirm('{$revertConfirm}');"/>
+ </span>
+ </div>
+</form>