From: julmis Date: Sat, 21 Aug 2004 10:12:09 +0000 (+0000) Subject: Add editor settings feature and Ludo's (Marc Alier) spellchecking functionality. X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=eb2042f6856a1078971f38e1e5d2166a366ef7aa;p=moodle.git Add editor settings feature and Ludo's (Marc Alier) spellchecking functionality. - Needs testing and should consider unstable. --- diff --git a/admin/configure.php b/admin/configure.php index d802d03407..f51b70fb59 100644 --- a/admin/configure.php +++ b/admin/configure.php @@ -41,6 +41,9 @@ get_string("adminhelpbackup")); } + $table->data[]= array("". get_string("editorsettings") ."", + get_string("adminhelpeditorsettings")); + print_table($table); print_footer($site); diff --git a/admin/editor.html b/admin/editor.html new file mode 100644 index 0000000000..65cfba9719 --- /dev/null +++ b/admin/editor.html @@ -0,0 +1,60 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $fontvalue) { + ?> + + + + + + + + + + + + +
:
:
:
:
:
">
+
\ No newline at end of file diff --git a/admin/editor.php b/admin/editor.php new file mode 100644 index 0000000000..9fae276cbe --- /dev/null +++ b/admin/editor.php @@ -0,0 +1,95 @@ +wwwroot/$CFG->admin/editor.php", get_string("changessaved"), 1); + + } else { + // Generate edit form + + $fontlist = editor_convert_to_array($CFG->editorfontlist); + + $stradmin = get_string("administration"); + $strconfiguration = get_string("configuration"); + $streditorsettings = get_string("editorsettings"); + $streditorsettingshelp = get_string("adminhelpeditorsettings"); + print_header("Editor settings","Editor settings", + "$stradmin -> ". + "$strconfiguration -> $streditorsettings"); + print_heading($streditorsettings); + print_simple_box("
$streditorsettingshelp
","center","50%"); + print("
\n"); + print_simple_box_start("center", "", "$THEME->cellheading"); + include("editor.html"); + print_simple_box_end(); + print_footer(); + } + + +/// FUNCTIONS + +function editor_convert_to_array ($string) { +/// Converts $CFG->editorfontlist to array + + if(empty($string) || !is_string($string)) { + return false; + } + $fonts = array(); + + $lines = explode(";", $string); + foreach($lines as $line) { + if(!empty($line)) { + list($fontkey, $fontvalue) = explode(":", $line); + $fonts[$fontkey] = $fontvalue; + } + } + + return $fonts; +} + +function editor_update_config ($data) { +/// Updates the editor config values. + + if(!is_object($data)) { + return false; + } + + // make font string + for($i = 0; $i < count($data->fontname); $i++) { + if(!empty($data->fontname[$i])) { + $fontlist .= $data->fontname[$i] .":"; + $fontlist .= $data->fontnamevalue[$i] .";"; + } + } + // strip last semicolon + $fontlist = substr($fontlist, 0, strlen($fontlist) - 1); + + // make array of values to update + $updatedata = array(); + $updatedata['editorbackgroundcolor'] = $data->backgroundcolor; + $updatedata['editorfontfamily'] = $data->fontfamily; + $updatedata['editorfontsize'] = $data->fontsize; + $updatedata['editorkillword'] = $data->killword; + $updatedata['editorspelling'] = $data->spelling; + $updatedata['editorfontlist'] = $fontlist; + + foreach($updatedata as $name => $value) { + if(!(set_config($name, $value))) { + return false; + } + } + + return true; +} +?> \ No newline at end of file diff --git a/admin/index.php b/admin/index.php index 4a189a3c5e..db0b1e7843 100644 --- a/admin/index.php +++ b/admin/index.php @@ -288,6 +288,8 @@ $configdata .= " ".get_string("backup")." - ". get_string("adminhelpbackup")."
"; } + $configdata .= " ". get_string("editorsettings") ." - ". + get_string("adminhelpeditorsettings")."
"; $table->data[] = array("".get_string("configuration")."", $configdata); diff --git a/config-dist.php b/config-dist.php index c4dfa56db8..aec2c3d9f2 100644 --- a/config-dist.php +++ b/config-dist.php @@ -45,26 +45,26 @@ unset($CFG); // Ignore this line // A special case exists when using PostgreSQL databases via sockets. // // Define dbhost as follows, leaving dbname, dbuser, dbpass BLANK!: // // $CFG->dbhost = " user='muser' password='mpass' dbname='mdata'"; // -// +// $CFG->dbtype = 'mysql'; // mysql or postgres7 (for now) -$CFG->dbhost = 'localhost'; // eg localhost or db.isp.com +$CFG->dbhost = 'localhost'; // eg localhost or db.isp.com $CFG->dbname = 'moodle'; // database name, eg moodle $CFG->dbuser = 'username'; // your database username $CFG->dbpass = 'password'; // your database password $CFG->prefix = 'mdl_'; // Prefix to use for all table names $CFG->dbpersist = false; // Should database connections be reused? - // "false" is the most stable setting - // "true" can improve performance sometimes + // "false" is the most stable setting + // "true" can improve performance sometimes //========================================================================= // 2. WEB SITE LOCATION //========================================================================= // Now you need to tell Moodle where it is located. Specify the full -// web address to where moodle has been installed. If your web site -// is accessible via multiple URLs then choose the most natural one +// web address to where moodle has been installed. If your web site +// is accessible via multiple URLs then choose the most natural one // that your students would use. Do not include a trailing slash $CFG->wwwroot = 'http://example.com/moodle'; @@ -87,8 +87,8 @@ $CFG->dirroot = '/home/example/public_html/moodle'; // 4. DATA FILES LOCATION //========================================================================= // Now you need a place where Moodle can save uploaded files. This -// directory should be readable AND WRITEABLE by the web server user -// (usually 'nobody' or 'apache'), but it should not be accessible +// directory should be readable AND WRITEABLE by the web server user +// (usually 'nobody' or 'apache'), but it should not be accessible // directly via the web. // // - On hosting systems you might need to make sure that your "group" has @@ -103,9 +103,9 @@ $CFG->dataroot = '/home/example/moodledata'; // 5. DATA FILES PERMISSIONS //========================================================================= // The following parameter sets the permissions of new directories -// created by Moodle within the data directory. The format is in +// created by Moodle within the data directory. The format is in // octal format (as used by the Unix utility chmod, for example). -// The default is usually OK, but you may want to change it to 0750 +// The default is usually OK, but you may want to change it to 0750 // if you are concerned about world-access to the files (you will need // to make sure the web server process (eg Apache) can access the files. // NOTE: the prefixed 0 is important, and don't use quotes. @@ -116,10 +116,10 @@ $CFG->directorypermissions = 0777; //========================================================================= // 6. DIRECTORY LOCATION (most people can just ignore this setting) //========================================================================= -// A very few webhosts use /admin as a special URL for you to access a -// control panel or something. Unfortunately this conflicts with the -// standard location for the Moodle admin pages. You can fix this by -// renaming the admin directory in your installation, and putting that +// A very few webhosts use /admin as a special URL for you to access a +// control panel or something. Unfortunately this conflicts with the +// standard location for the Moodle admin pages. You can fix this by +// renaming the admin directory in your installation, and putting that // new name here. eg "moodleadmin". This will fix admin links in Moodle. $CFG->admin = 'admin'; @@ -133,7 +133,7 @@ $CFG->admin = 'admin'; // // // Prevent users from updating their profile images -// $CFG->disableuserimages = true; +// $CFG->disableuserimages = true; // // Prevent scheduled backups from operating (and hide the GUI for them) // Useful for webhost operators who have alternate methods of backups @@ -144,8 +144,8 @@ $CFG->admin = 'admin'; // $CFG->restrictusers = 'teacher,fred,jim'; // // Turning this on will make Moodle filter more than usual, including -// forum subjects, activity names and so on (in ADDITION to the normal -// texts like forum postings, journals etc). This is mostly only useful +// forum subjects, activity names and so on (in ADDITION to the normal +// texts like forum postings, journals etc). This is mostly only useful // when using the multilang filter. This feature may not be complete. // $CFG->filterall = true; // @@ -157,17 +157,20 @@ $CFG->admin = 'admin'; // The names here should all be existing blocks in the "blocks" directory. // $CFG->defaultblocks = "participants,activity_modules,search_forums,admin,course_list:news_items,calendar_upcoming,recent_activity"; // -// This setting will put Moodle in Unicode mode. It's very new and +// This setting will put Moodle in Unicode mode. It's very new and // most likely doesn't work yet. THIS IS FOR DEVELOPERS ONLY, IT IS // NOT RECOMMENDED FOR PRODUCTION SITES // $CFG->unicode = true; // -// Turning this on will make Moodle use a https connection just for the -// login page (providing a secure login), and then afterwards revert -// back to the normal http URL. Requires https to be enabled on the +// Turning this on will make Moodle use a https connection just for the +// login page (providing a secure login), and then afterwards revert +// back to the normal http URL. Requires https to be enabled on the // web server. // $CFG->loginhttps = true; - +// +// Aspell path. Required aspell installed on your server (0.50.1 or newer) +// $CFG->aspellpath '/usr/bin/aspell'; // *nix +// $CFG->aspellpath '"c:\path\to\aspell\aspell.exe"'; // Windows //========================================================================= @@ -186,6 +189,6 @@ if (file_exists("$CFG->dirroot/lib/setup.php")) { // Do not edit } die; } -// MAKE SURE WHEN YOU EDIT THIS FILE THAT THERE ARE NO SPACES, BLANK LINES, +// MAKE SURE WHEN YOU EDIT THIS FILE THAT THERE ARE NO SPACES, BLANK LINES, // RETURNS, OR ANYTHING ELSE AFTER THE TWO CHARACTERS ON THE NEXT LINE. ?> diff --git a/lang/en/moodle.php b/lang/en/moodle.php index a9f33dd04c..8dda36bc33 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -1,4 +1,4 @@ -firstname, -Someone (probably you) has requested a new password for your +Someone (probably you) has requested a new password for your account on \'$a->sitename\'. To confirm this and have a new password sent to you via email, @@ -386,7 +386,7 @@ $string['enrolmentnotyet'] = 'Sorry, you can not access this course until
$string['enrolments'] = 'Enrolments'; $string['enrolperiod'] = 'Enrolment period'; $string['entercourse'] = 'Click to enter this course'; -$string['enteremailaddress'] = 'Enter in your email address to reset your +$string['enteremailaddress'] = 'Enter in your email address to reset your password and have the new password sent to you via email.'; $string['entries'] = 'Entries'; $string['error'] = 'Error'; @@ -564,7 +564,7 @@ $string['login'] = 'Login'; $string['loginas'] = 'Login as'; $string['loginguest'] = 'Login as a guest'; $string['loginsite'] = 'Login to the site'; -$string['loginsteps'] = 'Hi! For full access to courses you\'ll need to take +$string['loginsteps'] = 'Hi! For full access to courses you\'ll need to take a minute to create a new account for yourself on this web site. Each of the individual courses may also have a one-time "enrolment key", which you won\'t need until later. Here are @@ -786,10 +786,10 @@ $string['refreshingevents'] = 'Refreshing events'; $string['registration'] = 'Moodle Registration'; $string['registrationemail'] = 'Email notifications'; $string['registrationinfo'] = '

This page allows you to register your Moodle site with moodle.org. Registration is free. -The main benefit of registering is that you will be added to a low-volume mailing list +The main benefit of registering is that you will be added to a low-volume mailing list for important notifications such as security alerts and new releases of Moodle. -

By default, your information will be kept private, and will never be sold or passed on to anyone else. The only - reason for collecting this information is for support purposes, and to help build up a statistical +

By default, your information will be kept private, and will never be sold or passed on to anyone else. The only + reason for collecting this information is for support purposes, and to help build up a statistical picture of the Moodle community as a whole.

If you choose, you can allow your site name, country and URL to be added to the public list of Moodle Sites.

All new registrations are verified manually before they are added to the list, but once you are added you can update your registration (and your entry on the public list) at any time by resubmitting this form.'; @@ -1019,5 +1019,19 @@ $string['yourlastlogin'] = 'Your last login was'; $string['yourself'] = 'yourself'; $string['yourteacher'] = 'your $a'; $string['zippingbackup'] = 'Zipping backup'; - +$string['editorsettings'] = 'Editor settings'; +$string['adminhelpeditorsettings'] = 'Define basic settings for HTML editor'; +$string['editorcommonsettings'] = 'Common settings'; +$string['editorbgcolor'] = 'Background-color'; +$string['editordefaultfont'] = 'Default font'; +$string['editorfontsize'] = 'Default font-size'; +$string['editorcleanonpaste'] = 'Clean Word HTML on paste'; +$string['editorenablespelling'] = 'Enable spellchecking'; +$string['editorfontlist'] = 'Fontlist'; +$string['edhelpbgcolor'] = 'Define editarea\'s background-color.
Valid values are for example: #ffffff or white'; +$string['edhelpfontfamily'] = 'The font-family property is a list of font family names and/or generic family names. Family names must be seperated with comma.'; +$string['edhelpfontsize'] = 'The default font-size sets the size of a font.
Valid values are for example: medium, large, smaller, larger, 10pt, 11px.'; +$string['edhelpcleanword'] = 'This setting enables or disables Word specific format filtering.'; +$string['edhelpenablespelling'] = 'Enable or disable spellchecking. When enabled, aspell must be installed on the server.'; +$string['edhelpfontlist'] = 'Define the fonts used on editors dropdown menu.'; ?> diff --git a/lib/defaults.php b/lib/defaults.php index c39c7f9829..7c8b69a785 100644 --- a/lib/defaults.php +++ b/lib/defaults.php @@ -17,6 +17,12 @@ "enablerssfeeds" => 0, "enrol" => 'internal', "extendedusernamechars" => false, + "editorbackgroundcolor" => '#ffffff', + "editorfontfamily" => 'Times New Roman, Times', + "editorfontsize" => '', + "editorkillword" => 1, + "editorspelling" => 0, + "editorfontlist" => "Arial:arial,helvetica,sans-serif;Courier New:courier new,courier,monospace;Georgia:georgia,times new roman,times,serif;Tahoma:tahoma,arial,helvetica,sans-serif;Times New Roman:times new roman,times,serif;Verdana:verdana,arial,helvetica,sans-serif;Impact:impact;Wingdings:wingdings;", "filteruploadedfiles" => true, "forcelogin" => false, "forceloginforprofiles" => false, diff --git a/lib/javascript.php b/lib/javascript.php index 10bd3ac17c..79496bcd03 100644 --- a/lib/javascript.php +++ b/lib/javascript.php @@ -1,90 +1,183 @@ - - - - + +aspellpath)) { + echo "\n"; + } +?> + + + + diff --git a/lib/speller/blank.html b/lib/speller/blank.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lib/speller/changes.txt b/lib/speller/changes.txt new file mode 100644 index 0000000000..4e99bf76bb --- /dev/null +++ b/lib/speller/changes.txt @@ -0,0 +1,51 @@ +Add $CFG->aspellpath = '/path/to/aspell' to config.php + +copy speller folder to /moodle/lib/ + +weblib.php +function use_html_editor($name="",$courseid=0) { + +/// Sets up the HTML editor on textareas in the current page. +/// If a field name is provided, then it will only be +/// applied to that field - otherwise it will be used +/// on every textarea in the page. +/// +/// In most cases no arguments need to be supplied + +// Modification by Ludo ( Marc Alier to provide Spell Checking to the +// html editor. August 2004 malier@lsi.upc.es +/// START LUDO + global $CFG; + if(!empty($CFG->aspellpath)) { + echo ""; + } + +/// END LUDO + + echo "\n"; +} \ No newline at end of file diff --git a/lib/speller/controlWindow.js b/lib/speller/controlWindow.js new file mode 100644 index 0000000000..db9ea4a3e2 --- /dev/null +++ b/lib/speller/controlWindow.js @@ -0,0 +1,86 @@ +//////////////////////////////////////////////////// +// controlWindow object +//////////////////////////////////////////////////// +function controlWindow( controlForm ) { + // private properties + this._form = controlForm; + + // public properties + this.windowType = "controlWindow"; + this.noSuggestionSelection = "- No suggestions -"; + // set up the properties for elements of the given control form + this.suggestionList = this._form.sugg; + this.evaluatedText = this._form.misword; + this.replacementText = this._form.txtsugg; + this.undoButton = this._form.btnUndo; + + // public methods + this.addSuggestion = addSuggestion; + this.clearSuggestions = clearSuggestions; + this.selectDefaultSuggestion = selectDefaultSuggestion; + this.resetForm = resetForm; + this.setSuggestedText = setSuggestedText; + this.enableUndo = enableUndo; + this.disableUndo = disableUndo; +} + +function resetForm() { + if( this._form ) { + this._form.reset(); + } +} + +function setSuggestedText() { + var slct = this.suggestionList; + var txt = this.replacementText; + var str = ""; + if( (slct.options[0].text) && slct.options[0].text != this.noSuggestionSelection ) { + str = slct.options[slct.selectedIndex].text; + } + txt.value = str; +} + +function selectDefaultSuggestion() { + var slct = this.suggestionList; + var txt = this.replacementText; + if( slct.options.length == 0 ) { + this.addSuggestion( this.noSuggestionSelection ); + } else { + slct.options[0].selected = true; + } + this.setSuggestedText(); +} + +function addSuggestion( sugg_text ) { + var slct = this.suggestionList; + if( sugg_text ) { + var i = slct.options.length; + var newOption = new Option( sugg_text, 'sugg_text'+i ); + slct.options[i] = newOption; + } +} + +function clearSuggestions() { + var slct = this.suggestionList; + for( var j = slct.length - 1; j > -1; j-- ) { + if( slct.options[j] ) { + slct.options[j] = null; + } + } +} + +function enableUndo() { + if( this.undoButton ) { + if( this.undoButton.disabled == true ) { + this.undoButton.disabled = false; + } + } +} + +function disableUndo() { + if( this.undoButton ) { + if( this.undoButton.disabled == false ) { + this.undoButton.disabled = true; + } + } +} diff --git a/lib/speller/controls.html b/lib/speller/controls.html new file mode 100644 index 0000000000..931108b4c2 --- /dev/null +++ b/lib/speller/controls.html @@ -0,0 +1,153 @@ + + + + + + + + + + + + +

+ + + + + + + + + + + + + + + + + + + +
Not in dictionary:
Change to:
+ + + + + + + +
+ +
+ +
+
   + + + + + + + + + + + + + + + + + + + + + + + +
+ +    + +
+ +    + +
+ +    + +
+
+ +
+ + diff --git a/lib/speller/lib.php b/lib/speller/lib.php new file mode 100644 index 0000000000..5e8e1d294c --- /dev/null +++ b/lib/speller/lib.php @@ -0,0 +1,17 @@ +wwwroot ."/lib/speller/spellchecker.html\";\n"; + echo "\tspeller.spellCheckScript = \"". $CFG->wwwroot ."/lib/speller/server-scripts/spellchecker.php?id=$courseid\";\n"; + echo "\tspeller._moogle_edit=1;\n"; + echo "\tspeller._editor=editor;\n"; + echo "\tspeller.openChecker();\n"; + echo "}\n"; + echo "config.registerButton(\"spell-check\", \"spell-check\", \"". $CFG->wwwroot ."/lib/speller/spell.gif\", false, spellClickHandler);\n"; + echo "config.toolbar.push([\"spell-check\"]);\n"; +} + +?> \ No newline at end of file diff --git a/lib/speller/server-scripts/spellchecker.php b/lib/speller/server-scripts/spellchecker.php new file mode 100644 index 0000000000..d3574ab240 --- /dev/null +++ b/lib/speller/server-scripts/spellchecker.php @@ -0,0 +1,236 @@ +aspellpath; +$spellercss = $CFG->wwwroot .'/lib/speller/spellerStyle.css'; +$word_win_src = $CFG->wwwroot .'/lib/speller/wordWindow.js'; + + +$textinputs = $_POST['textinputs']; // array + +if(!($lang = check_language($aspell_prog))) { + error_handler("No suitable dictionary found installed on your server!"); + exit; +} + +$aspell_opts = "-a --lang=$lang --encoding=utf-8"; +$tempfiledir = "./"; +$input_separator = "A"; + +function check_language($cmd) { +/// return users current language if its +/// dictionary is found installed in system +/// and always return english if user's own +/// language is not in the list. If english dictionary +/// isn't found, then false is returned. + + error_reporting(E_ALL); // for debug, final version shouldn't have this... + clearstatcache(); + $current_lang = current_language(); + $output = ''; + + if(!($handle = popen($cmd .' dump dicts', 'r'))) { + error_handler("Couldn't create handle!"); + exit; + } + + while(!feof($handle)) { + $output .= fread($handle, 1024); + } + @pclose($handle); + + $dicts = explode(chr(10), $output); + + if(is_array($dicts)) { + if(in_array($current_lang,$dicts)) { + return $current_lang; + } + + if(in_array("en", $dicts)) { + return "en"; + } + } + return false; +} + +// set the JavaScript variable to the submitted text. +// textinputs is an array, each element corresponding to the (url-encoded) +// value of the text control submitted for spell-checking +function print_textinputs_var() { + global $textinputs; + foreach( $textinputs as $key=>$val ) { + // $val = str_replace( "'", "%27", $val ); + echo "textinputs[$key] = decodeURIComponent(\"" . $val . "\");\n"; + } +} + +// make declarations for the text input index +function print_textindex_decl( $text_input_idx ) { + echo "words[$text_input_idx] = [];\n"; + echo "suggs[$text_input_idx] = [];\n"; +} + +// set an element of the JavaScript 'words' array to a misspelled word +function print_words_elem( $word, $index, $text_input_idx ) { + echo "words[$text_input_idx][$index] = '" . escape_quote( $word ) . "';\n"; +} + + +// set an element of the JavaScript 'suggs' array to a list of suggestions +function print_suggs_elem( $suggs, $index, $text_input_idx ) { + echo "suggs[$text_input_idx][$index] = ["; + foreach( $suggs as $key=>$val ) { + if( $val ) { + echo "'" . escape_quote( $val ) . "'"; + if ( $key+1 < count( $suggs )) { + echo ", "; + } + } + } + echo "];\n"; +} + +// escape single quote +function escape_quote( $str ) { + return preg_replace ( "/'/", "\\'", $str ); +} + + +// handle a server-side error. +function error_handler( $err ) { + echo "error = '" . escape_quote( $err ) . "';\n"; +} + +// get the list of misspelled words. Put the results in the javascript words array +// for each misspelled word, get suggestions and put in the javascript suggs array +function print_checker_results() { + + global $aspell_prog; + global $aspell_opts; + global $tempfiledir; + global $textinputs; + global $input_separator; + $aspell_err = ""; + // create temp file + $tempfile = tempnam( $tempfiledir, 'aspell_data_' ); + + // open temp file, add the submitted text. + if( $fh = fopen( $tempfile, 'w' )) { + for( $i = 0; $i < count( $textinputs ); $i++ ) { + $text = urldecode( $textinputs[$i] ); + $lines = explode( "\n", $text ); + fwrite ( $fh, "%\n" ); // exit terse mode + fwrite ( $fh, "^$input_separator\n" ); + fwrite ( $fh, "!\n" ); // enter terse mode + foreach( $lines as $key=>$value ) { + // use carat on each line to escape possible aspell commands + fwrite( $fh, "^$value\n" ); + } + } + fclose( $fh ); + + // exec aspell command - redirect STDERR to STDOUT + $cmd = "$aspell_prog $aspell_opts < $tempfile 2>&1"; + if( $aspellret = shell_exec( $cmd )) { + $linesout = explode( "\n", $aspellret ); + $index = 0; + $text_input_index = -1; + // parse each line of aspell return + foreach( $linesout as $key=>$val ) { + $chardesc = substr( $val, 0, 1 ); + // if '&', then not in dictionary but has suggestions + // if '#', then not in dictionary and no suggestions + // if '*', then it is a delimiter between text inputs + // if '@' then version info + if( $chardesc == '&' || $chardesc == '#' ) { + $line = explode( " ", $val, 5 ); + print_words_elem( $line[1], $index, $text_input_index ); + if( isset( $line[4] )) { + $suggs = explode( ", ", $line[4] ); + } else { + $suggs = array(); + } + print_suggs_elem( $suggs, $index, $text_input_index ); + $index++; + } elseif( $chardesc == '*' ) { + $text_input_index++; + print_textindex_decl( $text_input_index ); + $index = 0; + } elseif( $chardesc != '@' && $chardesc != "" ) { + // assume this is error output + $aspell_err .= $val; + } + } + if( $aspell_err ) { + $aspell_err = "Error executing `$cmd`\\n$aspell_err"; + error_handler( $aspell_err ); + } + } else { + error_handler( "System error: Aspell program execution failed (`$cmd`)" ); + } + } else { + error_handler( "System error: Could not open file '$tempfile' for writing" ); + } + + // close temp file, delete file + unlink( $tempfile ); +} + + +?> + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/speller/spell.gif b/lib/speller/spell.gif new file mode 100644 index 0000000000..29bd11ae98 Binary files /dev/null and b/lib/speller/spell.gif differ diff --git a/lib/speller/spellChecker.js b/lib/speller/spellChecker.js new file mode 100644 index 0000000000..8e80a887e9 --- /dev/null +++ b/lib/speller/spellChecker.js @@ -0,0 +1,458 @@ +//////////////////////////////////////////////////// +// spellChecker.js +// +// spellChecker object +// +// This file is sourced on web pages that have a textarea object to evaluate +// for spelling. It includes the implementation for the spellCheckObject. +// +// Modified by LUDO (Marc Alier) for integration with the moogle environment +// august 2004 malier@lsi.upc.es +// +//////////////////////////////////////////////////// + + +// constructor +function spellChecker( textObject) { + // public properties - configurable + //this.popUpUrl = '/moodle/lib/speller/spellchecker.html'; + this.popUpName = 'spellchecker'; + this.popUpProps = "menu=no,width=440,height=350,top=70,left=120,resizable=yes,status=yes"; + //this.spellCheckScript = '/moodle/lib/speller/server-scripts/spellchecker.php'; + //this.spellCheckScript = '/cgi-bin/spellchecker.pl'; + + // values used to keep track of what happened to a word + this.replWordFlag = "R"; // single replace + this.ignrWordFlag = "I"; // single ignore + this.replAllFlag = "RA"; // replace all occurances + this.ignrAllFlag = "IA"; // ignore all occurances + this.fromReplAll = "~RA"; // an occurance of a "replace all" word + this.fromIgnrAll = "~IA"; // an occurance of a "ignore all" word + // properties set at run time + this.wordFlags = new Array(); + this.currentTextIndex = 0; + this.currentWordIndex = 0; + this.spellCheckerWin = null; + this.controlWin = null; + this.wordWin = null; + this.textArea = textObject; // deprecated + this.textInputs = arguments; + + this._editor=""; // BY LUDO + // private methods + this._spellcheck = _spellcheck; + this._getSuggestions = _getSuggestions; + this._setAsIgnored = _setAsIgnored; + this._getTotalReplaced = _getTotalReplaced; + this._setWordText = _setWordText; + this._getFormInputs = _getFormInputs; + + // public methods + this.openChecker = openChecker; + this.startCheck = startCheck; + this.checkTextBoxes = checkTextBoxes; + this.checkTextAreas = checkTextAreas; + this.spellCheckAll = spellCheckAll; + this.ignoreWord = ignoreWord; + this.ignoreAll = ignoreAll; + this.replaceWord = replaceWord; + this.replaceAll = replaceAll; + this.terminateSpell = terminateSpell; + this.undo = undo; + + // set the current window's "speller" property to the instance of this class. + // this object can now be referenced by child windows/frames. + window.speller = this; +} + +// call this method to check all text boxes (and only text boxes) in the HTML document +function checkTextBoxes() { + this.textInputs = this._getFormInputs( "^text$" ); + this.openChecker(); +} + +// call this method to check all textareas (and only textareas ) in the HTML document +function checkTextAreas() { + this.textInputs = this._getFormInputs( "^textarea$" ); + this.openChecker(); +} + +// call this method to check all text boxes and textareas in the HTML document +function spellCheckAll() { + this.textInputs = this._getFormInputs( "^text(area)?$" ); + this.openChecker(); +} + +// call this method to check text boxe(s) and/or textarea(s) that were passed in to the +// object's constructor or to the textInputs property +function openChecker() { + this.spellCheckerWin = window.open( this.popUpUrl, this.popUpName, this.popUpProps ); + if( !this.spellCheckerWin.opener ) { + this.spellCheckerWin.opener = window; + } +} + +function startCheck( wordWindowObj, controlWindowObj ) { + + // set properties from args + this.wordWin = wordWindowObj; + this.controlWin = controlWindowObj; + + // reset properties + this.wordWin.resetForm(); + this.controlWin.resetForm(); + this.currentTextIndex = 0; + this.currentWordIndex = 0; + // initialize the flags to an array - one element for each text input + this.wordFlags = new Array( this.wordWin.textInputs.length ); + // each element will be an array that keeps track of each word in the text + for( var i=0; i wi ) || i > ti ) { + // future word: set as "from ignore all" if + // 1) do not already have a flag and + // 2) have the same value as current word + if(( this.wordWin.getTextVal( i, j ) == s_word_to_repl ) + && ( !this.wordFlags[i][j] )) { + this._setAsIgnored( i, j, this.fromIgnrAll ); + } + } + } + } + + // finally, move on + this.currentWordIndex++; + this._spellcheck(); +} + +function replaceWord() { + var wi = this.currentWordIndex; + var ti = this.currentTextIndex; + if( !this.wordWin ) { + alert( 'Error: Word frame not available.' ); + return false; + } + if( !this.wordWin.getTextVal( ti, wi )) { + alert( 'Error: "Not in dictionary" text is missing' ); + return false; + } + if( !this.controlWin.replacementText ) { + return; + } + var txt = this.controlWin.replacementText; + if( txt.value ) { + var newspell = new String( txt.value ); + if( this._setWordText( ti, wi, newspell, this.replWordFlag )) { + this.currentWordIndex++; + this._spellcheck(); + } + } +} + +function replaceAll() { + var ti = this.currentTextIndex; + var wi = this.currentWordIndex; + if( !this.wordWin ) { + alert( 'Error: Word frame not available.' ); + return false; + } + var s_word_to_repl = this.wordWin.getTextVal( ti, wi ); + if( !s_word_to_repl ) { + alert( 'Error: "Not in dictionary" text is missing' ); + return false; + } + var txt = this.controlWin.replacementText; + if( !txt.value ) return; + var newspell = new String( txt.value ); + + // set this word as a "replace all" word. + this._setWordText( ti, wi, newspell, this.replAllFlag ); + + // loop through all the words after this word + for( var i = ti; i < this.wordWin.textInputs.length; i++ ) { + for( var j = 0; j < this.wordWin.totalWords( i ); j++ ) { + if(( i == ti && j > wi ) || i > ti ) { + // future word: set word text to s_word_to_repl if + // 1) do not already have a flag and + // 2) have the same value as s_word_to_repl + if(( this.wordWin.getTextVal( i, j ) == s_word_to_repl ) + && ( !this.wordFlags[i][j] )) { + this._setWordText( i, j, newspell, this.fromReplAll ); + } + } + } + } + + // finally, move on + this.currentWordIndex++; + this._spellcheck(); +} + +function terminateSpell() { + // called when we have reached the end of the spell checking. + var msg = "Spell check complete:\n\n"; + var numrepl = this._getTotalReplaced(); + if( numrepl == 0 ) { + // see if there were no misspellings to begin with + if( !this.wordWin ) { + msg = ""; + } else { + if( this.wordWin.totalMisspellings() ) { + msg += "No words changed."; + } else { + msg += "No misspellings found."; + } + } + } else if( numrepl == 1 ) { + msg += "One word changed."; + } else { + msg += numrepl + " words changed."; + } + if( msg ) { + msg += "\n"; + alert( msg ); + } + + if( numrepl > 0 ) { + // update the text field(s) on the opener window + for( var i = 0; i < this.textInputs.length; i++ ) { + // this.textArea.value = this.wordWin.text; + if( this.wordWin ) { + if( this.wordWin.textInputs[i] ) { + this.textInputs[i].value = this.wordWin.textInputs[i]; + } + } + } + // START LUDO + try { + this._editor.setHTML(this._editor._textArea.value); + } catch (e) { + // If were not in editor, just continue. + } + //LUDO END + } + + // return back to the calling window + this.spellCheckerWin.close(); + + return true; +} + +function undo() { + // skip if this is the first word! + var ti = this.currentTextIndex; + var wi = this.currentWordIndex + + if( this.wordWin.totalPreviousWords( ti, wi ) > 0 ) { + this.wordWin.removeFocus( ti, wi ); + + // go back to the last word index that was acted upon + do { + // if the current word index is zero then reset the seed + if( this.currentWordIndex == 0 && this.currentTextIndex > 0 ) { + this.currentTextIndex--; + this.currentWordIndex = this.wordWin.totalWords( this.currentTextIndex )-1; + if( this.currentWordIndex < 0 ) this.currentWordIndex = 0; + } else { + if( this.currentWordIndex > 0 ) { + this.currentWordIndex--; + } + } + } while ( + this.wordWin.totalWords( this.currentTextIndex ) == 0 + || this.wordFlags[this.currentTextIndex][this.currentWordIndex] == this.fromIgnrAll + || this.wordFlags[this.currentTextIndex][this.currentWordIndex] == this.fromReplAll + ); + + var text_idx = this.currentTextIndex; + var idx = this.currentWordIndex; + var preReplSpell = this.wordWin.originalSpellings[text_idx][idx]; + + // if we got back to the first word then set the Undo button back to disabled + if( this.wordWin.totalPreviousWords( text_idx, idx ) == 0 ) { + this.controlWin.disableUndo(); + } + + // examine what happened to this current word. + switch( this.wordFlags[text_idx][idx] ) { + // replace all: go through this and all the future occurances of the word + // and revert them all to the original spelling and clear their flags + case this.replAllFlag : + for( var i = text_idx; i < this.wordWin.textInputs.length; i++ ) { + for( var j = 0; j < this.wordWin.totalWords( i ); j++ ) { + if(( i == text_idx && j >= idx ) || i > text_idx ) { + var origSpell = this.wordWin.originalSpellings[i][j]; + if( origSpell == preReplSpell ) { + this._setWordText ( i, j, origSpell, undefined ); + } + } + } + } + break; + + // ignore all: go through all the future occurances of the word + // and clear their flags + case this.ignrAllFlag : + for( var i = text_idx; i < this.wordWin.textInputs.length; i++ ) { + for( var j = 0; j < this.wordWin.totalWords( i ); j++ ) { + if(( i == text_idx && j >= idx ) || i > text_idx ) { + var origSpell = this.wordWin.originalSpellings[i][j]; + if( origSpell == preReplSpell ) { + this.wordFlags[i][j] = undefined; + } + } + } + } + break; + + // replace: revert the word to its original spelling + case this.replWordFlag : + this._setWordText ( text_idx, idx, preReplSpell, undefined ); + break; + } + + // For all four cases, clear the wordFlag of this word. re-start the process + this.wordFlags[text_idx][idx] = undefined; + this._spellcheck(); + } +} + +function _spellcheck() { + var ww = this.wordWin; + + // check if this is the last word in the current text element + if( this.currentWordIndex == ww.totalWords( this.currentTextIndex) ) { + this.currentTextIndex++; + this.currentWordIndex = 0; + // keep going if we're not yet past the last text element + if( this.currentTextIndex < this.wordWin.textInputs.length ) { + this._spellcheck(); + return; + } else { + this.terminateSpell(); + return; + } + } + + // if this is after the first one make sure the Undo button is enabled + if( this.currentWordIndex > 0 ) { + this.controlWin.enableUndo(); + } + + // skip the current word if it has already been worked on + if( this.wordFlags[this.currentTextIndex][this.currentWordIndex] ) { + // increment the global current word index and move on. + this.currentWordIndex++; + this._spellcheck(); + } else { + var evalText = ww.getTextVal( this.currentTextIndex, this.currentWordIndex ); + if( evalText ) { + this.controlWin.evaluatedText.value = evalText; + ww.setFocus( this.currentTextIndex, this.currentWordIndex ); + this._getSuggestions( this.currentTextIndex, this.currentWordIndex ); + } + } +} + +function _getSuggestions( text_num, word_num ) { + this.controlWin.clearSuggestions(); + // add suggestion in list for each suggested word. + // get the array of suggested words out of the + // three-dimensional array containing all suggestions. + var a_suggests = this.wordWin.suggestions[text_num][word_num]; + if( a_suggests ) { + // got an array of suggestions. + for( var ii = 0; ii < a_suggests.length; ii++ ) { + this.controlWin.addSuggestion( a_suggests[ii] ); + } + } + this.controlWin.selectDefaultSuggestion(); +} + +function _setAsIgnored( text_num, word_num, flag ) { + // set the UI + this.wordWin.removeFocus( text_num, word_num ); + // do the bookkeeping + this.wordFlags[text_num][word_num] = flag; + return true; +} + +function _getTotalReplaced() { + var i_replaced = 0; + for( var i = 0; i < this.wordFlags.length; i++ ) { + for( var j = 0; j < this.wordFlags[i].length; j++ ) { + if(( this.wordFlags[i][j] == this.replWordFlag ) + || ( this.wordFlags[i][j] == this.replAllFlag ) + || ( this.wordFlags[i][j] == this.fromReplAll )) { + i_replaced++; + } + } + } + return i_replaced; +} + +function _setWordText( text_num, word_num, newText, flag ) { + // set the UI and form inputs + this.wordWin.setText( text_num, word_num, newText ); + // keep track of what happened to this word: + this.wordFlags[text_num][word_num] = flag; + return true; +} + +function _getFormInputs( inputPattern ) { + var inputs = new Array(); + for( var i = 0; i < document.forms.length; i++ ) { + for( var j = 0; j < document.forms[i].elements.length; j++ ) { + if( document.forms[i].elements[j].type.match( inputPattern )) { + inputs[inputs.length] = document.forms[i].elements[j]; + } + } + } + return inputs; +} diff --git a/lib/speller/spellchecker.html b/lib/speller/spellchecker.html new file mode 100644 index 0000000000..658fb27ced --- /dev/null +++ b/lib/speller/spellchecker.html @@ -0,0 +1,71 @@ + + + + + + +Speller Pages + + + + + + diff --git a/lib/speller/spellerStyle.css b/lib/speller/spellerStyle.css new file mode 100644 index 0000000000..01f3b7fff9 --- /dev/null +++ b/lib/speller/spellerStyle.css @@ -0,0 +1,47 @@ +.blend { + font-family: courier new; + font-size: 10pt; + border: 0; + margin-bottom:-1; +} +.normalLabel { + font-size:8pt; +} +.normalText { + font-family:arial, helvetica, sans-serif; + font-size:10pt; + color:000000; + background-color:FFFFFF; +} +.plainText { + font-family: courier new, courier, monospace; + font-size: 10pt; + color:000000; + background-color:FFFFFF; +} +.controlWindowBody { + font-family:arial, helvetica, sans-serif; + font-size:8pt; + color:000000; + background-color:DADADA; +} +.readonlyInput { + background-color:DADADA; + color:000000; + font-size:8pt; + width:392px; +} +.textDefault { + font-size:8pt; + width: 200px; +} +.buttonDefault { + width:90px; + height:22px; + font-size:8pt; +} +.suggSlct { + width:200px; + margin-top:2; + font-size:8pt; +} \ No newline at end of file diff --git a/lib/speller/wordWindow.js b/lib/speller/wordWindow.js new file mode 100644 index 0000000000..68df5c0ca5 --- /dev/null +++ b/lib/speller/wordWindow.js @@ -0,0 +1,269 @@ +//////////////////////////////////////////////////// +// wordWindow object +//////////////////////////////////////////////////// +function wordWindow() { + // private properties + this._forms = []; + + // private methods + this._getWordObject = _getWordObject; + //this._getSpellerObject = _getSpellerObject; + this._wordInputStr = _wordInputStr; + this._adjustIndexes = _adjustIndexes; + this._isWordChar = _isWordChar; + this._lastPos = _lastPos; + + // public properties + this.wordChar = /[a-zA-Z]/; + this.windowType = "wordWindow"; + this.originalSpellings = new Array(); + this.suggestions = new Array(); + this.checkWordBgColor = "pink"; + this.normWordBgColor = "white"; + this.text = ""; + this.textInputs = new Array(); + this.indexes = new Array(); + //this.speller = this._getSpellerObject(); + + // public methods + this.resetForm = resetForm; + this.totalMisspellings = totalMisspellings; + this.totalWords = totalWords; + this.totalPreviousWords = totalPreviousWords; + //this.getTextObjectArray = getTextObjectArray; + this.getTextVal = getTextVal; + this.setFocus = setFocus; + this.removeFocus = removeFocus; + this.setText = setText; + //this.getTotalWords = getTotalWords; + this.writeBody = writeBody; + this.printForHtml = printForHtml; +} + +function resetForm() { + if( this._forms ) { + for( var i = 0; i < this._forms.length; i++ ) { + this._forms[i].reset(); + } + } + return true; +} + +function totalMisspellings() { + var total_words = 0; + for( var i = 0; i < this.textInputs.length; i++ ) { + total_words += this.totalWords( i ); + } + return total_words; +} + +function totalWords( textIndex ) { + return this.originalSpellings[textIndex].length; +} + +function totalPreviousWords( textIndex, wordIndex ) { + var total_words = 0; + for( var i = 0; i <= textIndex; i++ ) { + for( var j = 0; j < this.totalWords( i ); j++ ) { + if( i == textIndex && j == wordIndex ) { + break; + } else { + total_words++; + } + } + } + return total_words; +} + +//function getTextObjectArray() { +// return this._form.elements; +//} + +function getTextVal( textIndex, wordIndex ) { + var word = this._getWordObject( textIndex, wordIndex ); + if( word ) { + return word.value; + } +} + +function setFocus( textIndex, wordIndex ) { + var word = this._getWordObject( textIndex, wordIndex ); + if( word ) { + if( word.type == "text" ) { + word.focus(); + word.style.backgroundColor = this.checkWordBgColor; + } + } +} + +function removeFocus( textIndex, wordIndex ) { + var word = this._getWordObject( textIndex, wordIndex ); + if( word ) { + if( word.type == "text" ) { + word.blur(); + word.style.backgroundColor = this.normWordBgColor; + } + } +} + +function setText( textIndex, wordIndex, newText ) { + var word = this._getWordObject( textIndex, wordIndex ); + var beginStr; + var endStr; + if( word ) { + var pos = this.indexes[textIndex][wordIndex]; + var oldText = word.value; + // update the text given the index of the string + beginStr = this.textInputs[textIndex].substring( 0, pos ); + endStr = this.textInputs[textIndex].substring( + pos + oldText.length, + this.textInputs[textIndex].length + ); + this.textInputs[textIndex] = beginStr + newText + endStr; + + // adjust the indexes on the stack given the differences in + // length between the new word and old word. + var lengthDiff = newText.length - oldText.length; + this._adjustIndexes( textIndex, wordIndex, lengthDiff ); + + word.size = newText.length; + word.value = newText; + this.removeFocus( textIndex, wordIndex ); + } +} + + +function writeBody() { + var d = window.document; + var is_html = false; + + d.open(); + + // iterate through each text input. + for( var txtid = 0; txtid < this.textInputs.length; txtid++ ) { + var end_idx = 0; + var begin_idx = 0; + d.writeln( '
' ); + var wordtxt = this.textInputs[txtid]; + this.indexes[txtid] = []; + + if( wordtxt ) { + var orig = this.originalSpellings[txtid]; + if( !orig ) break; + + //!!! plain text, or HTML mode? + d.writeln( '
' ); + // iterate through each occurrence of a misspelled word. + for( var i = 0; i < orig.length; i++ ) { + // find the position of the current misspelled word, + // starting at the last misspelled word. + // and keep looking if it's a substring of another word + do { + begin_idx = wordtxt.indexOf( orig[i], end_idx ); + end_idx = begin_idx + orig[i].length; + // word not found? messed up! + if( begin_idx == -1 ) break; + // look at the characters immediately before and after + // the word. If they are word characters we'll keep looking. + var before_char = wordtxt.charAt( begin_idx - 1 ); + var after_char = wordtxt.charAt( end_idx ); + } while ( + this._isWordChar( before_char ) + || this._isWordChar( after_char ) + ); + + // keep track of its position in the original text. + this.indexes[txtid][i] = begin_idx; + + // write out the characters before the current misspelled word + for( var j = this._lastPos( txtid, i ); j < begin_idx; j++ ) { + // !!! html mode? make it html compatible + d.write( this.printForHtml( wordtxt.charAt( j ))); + } + + // write out the misspelled word. + d.write( this._wordInputStr( orig[i] )); + + // if it's the last word, write out the rest of the text + if( i == orig.length-1 ){ + d.write( printForHtml( wordtxt.substr( end_idx ))); + } + } + + d.writeln( '
' ); + + } + d.writeln( '
' ); + } + //for ( var j = 0; j < d.forms.length; j++ ) { + // alert( d.forms[j].name ); + // for( var k = 0; k < d.forms[j].elements.length; k++ ) { + // alert( d.forms[j].elements[k].name + ": " + d.forms[j].elements[k].value ); + // } + //} + + // set the _forms property + this._forms = d.forms; + d.close(); +} + +// return the character index in the full text after the last word we evaluated +function _lastPos( txtid, idx ) { + if( idx > 0 ) + return this.indexes[txtid][idx-1] + this.originalSpellings[txtid][idx-1].length; + else + return 0; +} + +function printForHtml( n ) { + var htmlstr = n; + if( htmlstr.length == 1 ) { + // do simple case statement if it's just one character + switch ( n ) { + case "\n": + htmlstr = '
'; + break; + case "<": + htmlstr = '<'; + break; + case ">": + htmlstr = '>'; + break; + } + return htmlstr; + } else { + htmlstr = htmlstr.replace( //g, '>' ); + htmlstr = htmlstr.replace( /\n/g, '
' ); + return htmlstr; + } +} + +function _isWordChar( letter ) { + if( letter.search( this.wordChar ) == -1 ) { + return false; + } else { + return true; + } +} + +function _getWordObject( textIndex, wordIndex ) { + if( this._forms[textIndex] ) { + if( this._forms[textIndex].elements[wordIndex] ) { + return this._forms[textIndex].elements[wordIndex]; + } + } + return null; +} + +function _wordInputStr( word ) { + var str = ''; + return str; +} + +function _adjustIndexes( textIndex, wordIndex, lengthDiff ) { + for( var i = wordIndex + 1; i < this.originalSpellings[textIndex].length; i++ ) { + this.indexes[textIndex][i] = this.indexes[textIndex][i] + lengthDiff; + } +} diff --git a/lib/weblib.php b/lib/weblib.php index a444478aa0..73e1112675 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -1821,10 +1821,11 @@ function use_html_editor($name="") { /// In most cases no arguments need to be supplied echo "\n"; } @@ -2516,6 +2517,84 @@ function print_side_block_end() { echo "\n"; } +function print_editor_config() { +/// prints out the editor config. + global $CFG; + + // print new config + echo "var config = new HTMLArea.Config();\n"; + echo "config.pageStyle = \"body {"; + if(!(empty($CFG->editorbackgroundcolor))) { + echo " background-color: $CFG->editorbackgroundcolor;"; + } + + if(!(empty($CFG->editorfontfamily))) { + echo " font-family: $CFG->editorfontfamily;"; + } + + if(!(empty($CFG->editorfontsize))) { + echo " font-size: $CFG->editorfontsize;"; + } + + echo " }\";\n"; + echo "config.killWordOnPaste = "; + echo(!$CFG->editorkillword) ? "false":"true"; + echo ";\n"; + echo "config.fontname = {\n"; + + $fontlist = explode(";", $CFG->editorfontlist); + $i = 1; // Counter is used to get rid of the last comma. + $count = count($fontlist); // Otherwise IE doesn't load the editor. + + foreach($fontlist as $fontline) { + if(!empty($fontline)) { + list($fontkey, $fontvalue) = split(":", $fontline); + echo "\"". $fontkey ."\":\t'". $fontvalue ."'"; + if($i < $count) { + echo ",\n"; + } + } + $i++; + } + echo "};"; + + print_speller_code($usehtmleditor=true); +} + +function print_speller_code ($usehtmleditor=false) { +/// Prints out code needed for spellchecking. +/// Original idea by Ludo (Marc Alier). + global $CFG; + + if(!$usehtmleditor) { + echo "\n\n"; + } else { + echo "\nfunction spellClickHandler(editor, buttonId) {\n"; + echo "\teditor._textArea.value = editor.getHTML();\n"; + echo "\tvar speller = new spellChecker( editor._textArea );\n"; + echo "\tspeller.popUpUrl = \"" . $CFG->wwwroot ."/lib/speller/spellchecker.html\";\n"; + echo "\tspeller.spellCheckScript = \"". $CFG->wwwroot ."/lib/speller/server-scripts/spellchecker.php\";\n"; + echo "\tspeller._moogle_edit=1;\n"; + echo "\tspeller._editor=editor;\n"; + echo "\tspeller.openChecker();\n"; + echo "}\n"; + echo "config.registerButton(\"spell-check\", \"spell-check\", \"". $CFG->wwwroot ."/lib/speller/spell.gif\", false, spellClickHandler);\n"; + echo "config.toolbar.push([\"spell-check\"]);\n"; + } +} + +function print_speller_button () { +// print button for spellchecking +// when editor is disabled + echo "\n"; +} // vim:autoindent:expandtab:shiftwidth=4:tabstop=4:tw=140: ?> diff --git a/mod/forum/mod.html b/mod/forum/mod.html index 6a8f5cfa54..a2c1789787 100644 --- a/mod/forum/mod.html +++ b/mod/forum/mod.html @@ -86,16 +86,20 @@
+<<<<<<< mod.html + +======= intro); ?> +>>>>>>> 1.33

student")) ?>:

- open, ""); - helpbutton("allowdiscussions", get_string("allowdiscussions", + helpbutton("allowdiscussions", get_string("allowdiscussions", "forum", moodle_strtolower("$course->student")), "forum"); ?> @@ -103,9 +107,9 @@

:

- forcesubscribe, ""); helpbutton("subscription", get_string("forcesubscribeq", "forum"), "forum"); @@ -113,8 +117,8 @@ -

:

- +

:

+ maxbytes, $course->maxbytes); $choices[1] = get_string("uploadnotallowed"); @@ -122,10 +126,10 @@ choose_from_menu ($choices, "maxbytes", $form->maxbytes, ""); helpbutton("maxattachmentsize", get_string("maxattachmentsize", "forum"), "forum"); ?> - + -enablerssfeeds && $CFG->forum_enablerssfeeds) { echo ""; echo "

".get_string("rsstype").":

"; @@ -165,7 +169,7 @@

:

- "; echo " var subitemstime = ['startday','startmonth','startyear','starthour', 'startminute',". "'finishday','finishmonth','finishyear','finishhour','finishminute'];";