From 7680da6ce859590c503b423e9ffd903e9ab64949 Mon Sep 17 00:00:00 2001 From: David Mudrak Date: Sat, 23 Oct 2010 18:40:11 +0000 Subject: [PATCH] MDL-24777 new emoticon_manager and its admin settings --- admin/resetemoticons.php | 45 +++++++ admin/settings/appearance.php | 2 + lang/en/admin.php | 12 +- lib/adminlib.php | 272 +++++++++++++++++++++++++----------------- lib/moodlelib.php | 110 +++++++++++++++++ theme/base/style/admin.css | 2 + 6 files changed, 334 insertions(+), 109 deletions(-) create mode 100644 admin/resetemoticons.php diff --git a/admin/resetemoticons.php b/admin/resetemoticons.php new file mode 100644 index 00000000000..09a44bbb23b --- /dev/null +++ b/admin/resetemoticons.php @@ -0,0 +1,45 @@ +. + +/** + * Resets the emoticons mapping into the default value + * + * @package core + * @copyright 2010 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require(dirname(dirname(__FILE__)) . '/config.php'); +require_once($CFG->libdir.'/adminlib.php'); + +admin_externalpage_setup('resetemoticons'); + +$confirm = optional_param('confirm', false, PARAM_BOOL); + +if (!$confirm or !confirm_sesskey()) { + echo $OUTPUT->header(); + echo $OUTPUT->heading(get_string('confirmation', 'admin')); + echo $OUTPUT->confirm(get_string('emoticonsreset', 'admin'), + new moodle_url($PAGE->url, array('confirm' => 1)), + new moodle_url('/admin/settings.php', array('section' => 'htmlsettings'))); + echo $OUTPUT->footer(); + die(); +} + +$manager = get_emoticon_manager(); +set_config('emoticons', $manager->encode_stored_config($manager->default_emoticons())); +redirect(new moodle_url('/admin/settings.php', array('section' => 'htmlsettings'))); diff --git a/admin/settings/appearance.php b/admin/settings/appearance.php index 5ff571dbe5e..4c5ef50747f 100644 --- a/admin/settings/appearance.php +++ b/admin/settings/appearance.php @@ -97,6 +97,8 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page $temp->add(new admin_setting_configcheckbox('formatstringstriptags', get_string('stripalltitletags', 'admin'), get_string('configstripalltitletags', 'admin'), 1)); $temp->add(new admin_setting_emoticons()); $ADMIN->add('appearance', $temp); + $ADMIN->add('appearance', new admin_externalpage('resetemoticons', get_string('emoticonsreset', 'admin'), + new moodle_url('/admin/resetemoticons.php'), 'moodle/site:config', true)); // "documentation" settingpage $temp = new admin_settingpage('documentation', get_string('moodledocs')); diff --git a/lang/en/admin.php b/lang/en/admin.php index a5fa5b3663f..5d39eee5735 100755 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -188,7 +188,6 @@ $string['configdoctonewwindow'] = 'If you enable this, then links to Moodle Docs $string['configeditordictionary'] = 'This value will be used if aspell doesn\'t have dictionary for users own language.'; $string['configeditorfontlist'] = 'Select the fonts that should appear in the editor\'s drop-down list.'; $string['configemailchangeconfirmation'] = 'Require an email confirmation step when users change their email address in their profile.'; -$string['configemoticons'] = 'Change the code on the left that relates to the name of the emoticon on the right. To add new emoticons, add a code and a name, then add an image as name.gif in /pix/s.'; $string['configenableajax'] = 'This setting allows you to control the use of AJAX (advanced client/server interfaces using Javascript) across the whole site. With this setting enabled users can still make a choice in their profile, otherwise AJAX is disabled for everybody.'; $string['configenablecalendarexport'] = 'Enable exporting or subscribing to calendars.'; $string['configenablecomments'] = 'Enable comments'; @@ -463,7 +462,18 @@ $string['editorspelling'] = 'Editor spelling'; $string['editorspellinghelp'] = 'Enable or disable spell-checking. When enabled, aspell must be installed on the server.'; $string['editstrings'] = 'Edit words or phrases'; $string['emailchangeconfirmation'] = 'Email change confirmation'; +$string['emoticontext'] = 'Text'; +$string['emoticonimagename'] = 'Image name'; +$string['emoticonalt'] = 'Alternative text'; +$string['emoticoncomponent'] = 'Image component'; $string['emoticons'] = 'Emoticons'; +$string['emoticons_desc'] = 'This form defines the emoticons (or smileys) used at your site. To remove a row from the table, save the form with an empty value in any of the required fields. To register a new emoticon, fill the fields in the last blank row. To reset all the fields into default values, follow the link above. + +* Text (required) - This text will be replaced with the emoticon image. It must be at least two characters long. +* Image name (required) - The emoticon image file name without the extension, relative to the component pix folder. +* Image component (required) - The component providing the icon. +* Alternative text (optional) - String identifier and component of the alternative text of the emoticon.'; +$string['emoticonsreset'] = 'Reset emoticons setting to default values'; $string['emptysettingvalue'] = 'Empty'; $string['enableajax'] = 'Enable AJAX'; $string['enablecalendarexport'] = 'Enable calendar export'; diff --git a/lib/adminlib.php b/lib/adminlib.php index 9faf603cf74..a7b762ef0a3 100644 --- a/lib/adminlib.php +++ b/lib/adminlib.php @@ -3204,7 +3204,7 @@ class admin_setting_special_frontpagedesc extends admin_setting { } /** - * Special settings for emoticons + * Administration interface for emoticon_manager settings. * * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -3215,66 +3215,10 @@ class admin_setting_emoticons extends admin_setting { */ public function __construct() { global $CFG; - $name = 'emoticons'; - $visiblename = get_string('emoticons', 'admin'); - $description = get_string('configemoticons', 'admin'); - $defaults = array('k0' => ':-)', - 'v0' => 'smiley', - 'k1' => ':)', - 'v1' => 'smiley', - 'k2' => ':-D', - 'v2' => 'biggrin', - 'k3' => ';-)', - 'v3' => 'wink', - 'k4' => ':-/', - 'v4' => 'mixed', - 'k5' => 'V-.', - 'v5' => 'thoughtful', - 'k6' => ':-P', - 'v6' => 'tongueout', - 'k7' => 'B-)', - 'v7' => 'cool', - 'k8' => '^-)', - 'v8' => 'approve', - 'k9' => '8-)', - 'v9' => 'wideeyes', - 'k10' => ':o)', - 'v10' => 'clown', - 'k11' => ':-(', - 'v11' => 'sad', - 'k12' => ':(', - 'v12' => 'sad', - 'k13' => '8-.', - 'v13' => 'shy', - 'k14' => ':-I', - 'v14' => 'blush', - 'k15' => ':-X', - 'v15' => 'kiss', - 'k16' => '8-o', - 'v16' => 'surprise', - 'k17' => 'P-|', - 'v17' => 'blackeye', - 'k18' => '8-[', - 'v18' => 'angry', - 'k19' => 'xx-P', - 'v19' => 'dead', - 'k20' => '|-.', - 'v20' => 'sleepy', - 'k21' => '}-]', - 'v21' => 'evil', - 'k22' => '(h)', - 'v22' => 'heart', - 'k23' => '(heart)', - 'v23' => 'heart', - 'k24' => '(y)', - 'v24' => 'yes', - 'k25' => '(n)', - 'v25' => 'no', - 'k26' => '(martin)', - 'v26' => 'martin', - 'k27' => '( )', - 'v27' => 'egg'); - parent::__construct($name, $visiblename, $description, $defaults); + + $manager = get_emoticon_manager(); + $defaults = $this->prepare_form_data($manager->default_emoticons()); + parent::__construct('emoticons', get_string('emoticons', 'admin'), get_string('emoticons_desc', 'admin'), $defaults); } /** @@ -3284,20 +3228,15 @@ class admin_setting_emoticons extends admin_setting { */ public function get_setting() { global $CFG; - $result = $this->config_read($this->name); - if (is_null($result)) { - return NULL; - } - $i = 0; - $currentsetting = array(); - $items = explode('{;}', $result); - foreach ($items as $item) { - $item = explode('{:}', $item); - $currentsetting['k'.$i] = $item[0]; - $currentsetting['v'.$i] = $item[1]; - $i++; + + $manager = get_emoticon_manager(); + $config = $this->config_read($this->name); + + if (is_null($config)) { + return null; } - return $currentsetting; + + return $this->prepare_form_data($manager->decode_stored_config($config)); } /** @@ -3308,29 +3247,18 @@ class admin_setting_emoticons extends admin_setting { */ public function write_setting($data) { - // there miiight be an easier way to do this :) - // if this is changed, make sure the $defaults array above is modified so that this - // function processes it correctly - - $keys = array(); - $values = array(); + $manager = get_emoticon_manager(); + $emoticons = $this->process_form_data($data); - foreach ($data as $key => $value) { - if (substr($key,0,1) == 'k') { - $keys[substr($key,1)] = $value; - } elseif (substr($key,0,1) == 'v') { - $values[substr($key,1)] = $value; - } + if ($emoticons === false) { + return false; } - $result = array(); - for ($i = 0; $i < count($keys); $i++) { - if (($keys[$i] !== '') && ($values[$i] !== '')) { - $result[] = clean_param($keys[$i],PARAM_NOTAGS).'{:}'.clean_param($values[$i], PARAM_NOTAGS); - } + if ($this->config_write($this->name, $manager->encode_stored_config($emoticons))) { + return ''; // success + } else { + return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br'); } - - return ($this->config_write($this->name, implode('{;}', $result)) ? '' : get_string('errorsetting', 'admin').$this->visiblename.'
'); } /** @@ -3340,24 +3268,152 @@ class admin_setting_emoticons extends admin_setting { * @return string XHTML string for the fields and wrapping div(s) */ public function output_html($data, $query='') { - $fullname = $this->get_full_name(); - $return = '
'; - for ($i = 0; $i < count($data) / 2; $i++) { - $return .= ''; - $return .= '  '; - $return .= '
'; - } - $return .= ''; - $return .= '  '; - $return .= '
'; - $return .= ''; - $return .= '  '; - $return .= ''; - $return .= '
'; + global $OUTPUT; - return format_admin_setting($this, $this->visiblename, $return, $this->description, false, '', NULL, $query); + $out = html_writer::start_tag('table', array('border' => 1, 'class' => 'generaltable')); + $out .= html_writer::start_tag('thead'); + $out .= html_writer::start_tag('tr'); + $out .= html_writer::tag('th', get_string('emoticontext', 'admin')); + $out .= html_writer::tag('th', get_string('emoticonimagename', 'admin')); + $out .= html_writer::tag('th', get_string('emoticoncomponent', 'admin')); + $out .= html_writer::tag('th', get_string('emoticonalt', 'admin'), array('colspan' => 2)); + $out .= html_writer::tag('th', ''); + $out .= html_writer::end_tag('tr'); + $out .= html_writer::end_tag('thead'); + $out .= html_writer::start_tag('tbody'); + $i = 0; + foreach($data as $field => $value) { + switch ($i) { + case 0: + $out .= html_writer::start_tag('tr'); + $current_text = $value; + $current_filename = ''; + $current_imagecomponent = ''; + $current_altidentifier = ''; + $current_altcomponent = ''; + case 1: + $current_filename = $value; + case 2: + $current_imagecomponent = $value; + case 3: + $current_altidentifier = $value; + case 4: + $current_altcomponent = $value; + } + + $out .= html_writer::tag('td', + html_writer::empty_tag('input', + array( + 'type' => 'text', + 'class' => 'form-text', + 'name' => $this->get_full_name().'['.$field.']', + 'value' => $value, + ) + ), array('class' => 'c'.$i) + ); + + if ($i == 4) { + if (get_string_manager()->string_exists($current_altidentifier, $current_altcomponent)) { + $alt = get_string($current_altidentifier, $current_altcomponent); + } else { + $alt = $current_text; + } + if ($current_filename) { + $out .= html_writer::tag('td', $OUTPUT->render(new pix_emoticon($current_filename, $alt, $current_imagecomponent))); + } else { + $out .= html_writer::tag('td', ''); + } + $out .= html_writer::end_tag('tr'); + $i = 0; + } else { + $i++; + } + + } + $out .= html_writer::end_tag('tbody'); + $out .= html_writer::end_tag('table'); + $out = html_writer::tag('div', $out, array('class' => 'form-group')); + $out .= html_writer::tag('div', html_writer::link(new moodle_url('/admin/resetemoticons.php'), get_string('emoticonsreset', 'admin'))); + + return format_admin_setting($this, $this->visiblename, $out, $this->description, false, '', NULL, $query); + } + + /** + * Converts the array of emoticon objects provided by {@see emoticon_manager} into admin settings form data + * + * @see self::process_form_data() + * @param array $emoticons array of emoticon objects as returned by {@see emoticon_manager} + * @return array of form fields and their values + */ + protected function prepare_form_data(array $emoticons) { + + $form = array(); + $i = 0; + foreach ($emoticons as $emoticon) { + $form['text'.$i] = $emoticon->text; + $form['imagename'.$i] = $emoticon->imagename; + $form['imagecomponent'.$i] = $emoticon->imagecomponent; + $form['altidentifier'.$i] = $emoticon->altidentifier; + $form['altcomponent'.$i] = $emoticon->altcomponent; + $i++; + } + // add one more blank field set for new object + $form['text'.$i] = ''; + $form['imagename'.$i] = ''; + $form['imagecomponent'.$i] = ''; + $form['altidentifier'.$i] = ''; + $form['altcomponent'.$i] = ''; + + return $form; } + /** + * Converts the data from admin settings form into an array of emoticon objects + * + * @see self::prepare_form_data() + * @param array $data array of admin form fields and values + * @return false|array of emoticon objects + */ + protected function process_form_data(array $form) { + + $count = count($form); // number of form field values + + if ($count % 5) { + // we must get five fields per emoticon object + return false; + } + + $emoticons = array(); + for ($i = 0; $i < $count / 5; $i++) { + $emoticon = new stdClass(); + $emoticon->text = clean_param(trim($form['text'.$i]), PARAM_NOTAGS); + $emoticon->imagename = clean_param(trim($form['imagename'.$i]), PARAM_PATH); + $emoticon->imagecomponent = clean_param(trim($form['imagecomponent'.$i]), PARAM_SAFEDIR); + $emoticon->altidentifier = clean_param(trim($form['altidentifier'.$i]), PARAM_STRINGID); + $emoticon->altcomponent = clean_param(trim($form['altcomponent'.$i]), PARAM_SAFEDIR); + + if (strpos($emoticon->text, ':/') !== false or strpos($emoticon->text, '//') !== false) { + // prevent from breaking http://url.addresses by accident + $emoticon->text = ''; + } + + if (strlen($emoticon->text) < 2) { + // do not allow single character emoticons + $emoticon->text = ''; + } + + if (preg_match('/^[a-zA-Z]+[a-zA-Z0-9]*$/', $emoticon->text)) { + // emoticon text must contain some non-alphanumeric character to prevent + // breaking HTML tags + $emoticon->text = ''; + } + + if ($emoticon->text !== '' and $emoticon->imagename !== '' and $emoticon->imagecomponent !== '') { + $emoticons[] = $emoticon; + } + } + return $emoticons; + } } /** diff --git a/lib/moodlelib.php b/lib/moodlelib.php index 3263a9f9c7f..ffaac5fb072 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -6689,6 +6689,116 @@ function get_list_of_timezones() { return $timezones; } +/** + * Factory function for emoticon_manager + * + * @return emoticon_manager singleton + */ +function get_emoticon_manager() { + static $singleton = null; + + if (is_null($singleton)) { + $singleton = new emoticon_manager(); + } + + return $singleton; +} + +/** + * Provides core support for plugins that have to deal with + * emoticons (like HTML editor or emoticon filter). + * + * @see admin_setting_emoticons + */ +class emoticon_manager { + + /** + * Encodes the array of emoticon objects into a string storable in config table + * + * @see self::decode_stored_config() + * @param array $emoticons array of emtocion objects + * @return string + */ + public function encode_stored_config(array $emoticons) { + return json_encode($emoticons); + } + + /** + * Decodes the string into an array of emoticon objects + * + * @see self::encode_stored_config() + * @param string $encoded + * @return string|null + */ + public function decode_stored_config($encoded) { + $decoded = json_decode($encoded); + if (!is_array($decoded)) { + return null; + } + return $decoded; + } + + /** + * Returns default set of emoticons supported by Moodle + * + * @return array of sdtClasses + */ + public function default_emoticons() { + return array( + $this->prepare_emoticon_object(":-)", 's/smiley', 'smiley'), + $this->prepare_emoticon_object(":)", 's/smiley', 'smiley'), + $this->prepare_emoticon_object(":-D", 's/biggrin', 'biggrin'), + $this->prepare_emoticon_object(";-)", 's/wink', 'wink'), + $this->prepare_emoticon_object(":-/", 's/mixed', 'mixed'), + $this->prepare_emoticon_object("V-.", 's/thoughtful', 'thoughtful'), + $this->prepare_emoticon_object(":-P", 's/tongueout', 'tongueout'), + $this->prepare_emoticon_object(":-p", 's/tongueout', 'tongueout'), + $this->prepare_emoticon_object("B-)", 's/cool', 'cool'), + $this->prepare_emoticon_object("^-)", 's/approve', 'approve'), + $this->prepare_emoticon_object("8-)", 's/wideeyes', 'wideeyes'), + $this->prepare_emoticon_object(":o)", 's/clown', 'clown'), + $this->prepare_emoticon_object(":-(", 's/sad', 'sad'), + $this->prepare_emoticon_object(":(", 's/sad', 'sad'), + $this->prepare_emoticon_object("8-.", 's/shy', 'shy'), + $this->prepare_emoticon_object(":-I", 's/blush', 'blush'), + $this->prepare_emoticon_object(":-X", 's/kiss', 'kiss'), + $this->prepare_emoticon_object("8-o", 's/surprise', 'surprise'), + $this->prepare_emoticon_object("P-|", 's/blackeye', 'blackeye'), + $this->prepare_emoticon_object("8-[", 's/angry', 'angry'), + $this->prepare_emoticon_object("(grr)", 's/angry', 'angry'), + $this->prepare_emoticon_object("xx-P", 's/dead', 'dead'), + $this->prepare_emoticon_object("|-.", 's/sleepy', 'sleepy'), + $this->prepare_emoticon_object("}-]", 's/evil', 'evil'), + $this->prepare_emoticon_object("(h)", 's/heart', 'heart'), + $this->prepare_emoticon_object("(heart)", 's/heart', 'heart'), + $this->prepare_emoticon_object("(y)", 's/yes', 'yes', 'core'), + $this->prepare_emoticon_object("(n)", 's/no', 'no', 'core'), + $this->prepare_emoticon_object("(martin)", 's/martin', 'martin'), + $this->prepare_emoticon_object("( )", 's/egg', 'egg'), + ); + } + + /** + * Helper method preparing the stdClass with the emoticon properties + * + * @param string|array $text or array of strings + * @param string $imagename to be used by {@see pix_emoticon} + * @param string $altidentifier alternative string identifier, null for no alt + * @param array $altcomponent where the alternative string is defined + * @param string $imagecomponent to be used by {@see pix_emoticon} + * @return stdClass + */ + protected function prepare_emoticon_object($text, $imagename, $altidentifier = null, $altcomponent = 'core_pix', $imagecomponent = 'core') { + return (object)array( + 'text' => $text, + 'imagename' => $imagename, + 'imagecomponent' => $imagecomponent, + 'altidentifier' => $altidentifier, + 'altcomponent' => $altcomponent, + ); + } +} + /// ENCRYPTION //////////////////////////////////////////////// /** diff --git a/theme/base/style/admin.css b/theme/base/style/admin.css index d48ee00b996..94116e6408d 100644 --- a/theme/base/style/admin.css +++ b/theme/base/style/admin.css @@ -147,6 +147,8 @@ #adminsettings .form-item .form-description {display: block;margin: 0.5em 0 0em 14.25em;text-align: left;} #adminsettings .form-item .pathok, #adminsettings .form-item .patherror {margin-left: 0.5em;} +#adminsettings #admin-emoticons table td input {width: 8em;} +#adminsettings #admin-emoticons table td.c0 input {width: 4em;} #adminthemeselector .selectedtheme td.c0 {border:1px solid;border-right-width:0;} #adminthemeselector .selectedtheme td.c1 {border:1px solid;border-left-width:0;} -- 2.11.4.GIT