From 50bdc74de2d3fa40647081e0fc0291f825254e83 Mon Sep 17 00:00:00 2001 From: moodler Date: Sun, 12 Oct 2003 17:12:02 +0000 Subject: [PATCH] A new feature to allow teachers to browse course files to find images to insert into Richtext editor windows. This hack was started by Janne Mikkonen, and I further refined it to fit better into Moodle. See: http://moodle.org/mod/forum/discuss.php?d=2126 --- lib/rte/README_MOODLE | 21 +- lib/rte/courseimages.php | 851 ++++++++++++++++++++++++++++++++++++++ lib/rte/dlg_ins_image.php | 214 ++++++++++ lib/rte/richedit.php | 340 +++++++++++++++ lib/rte/rte.php | 633 ++++++++++++++++++++++++++++ lib/weblib.php | 20 +- 6 files changed, 2075 insertions(+), 4 deletions(-) create mode 100644 lib/rte/courseimages.php create mode 100644 lib/rte/dlg_ins_image.php create mode 100644 lib/rte/richedit.php create mode 100644 lib/rte/rte.php diff --git a/lib/rte/README_MOODLE b/lib/rte/README_MOODLE index d42a940a88..b8d4a9035b 100644 --- a/lib/rte/README_MOODLE +++ b/lib/rte/README_MOODLE @@ -1,7 +1,26 @@ +10/10/2002: + This version of the Richtext editor started out as 0.3 beta 1, and I've hacked richedit.html to make the toolbars more compact. I've also changed the smilies to refer to Moodle smilies, and added/removed some. -Martin Dougiamas, 10/10/2002 + + +12/10/2003: + +I've just added and further hacked some changes from Janne +Mikkonen (http://moodle.org/mod/forum/discuss.php?d=2126) +to allow a popup so that images can be selected from the +course files area. + +This includes these PHP files in this folder: + + -- courseimages.php + -- dlg_ins_image.php + -- richedit.php + -- rte.php + + +Martin Dougiamas diff --git a/lib/rte/courseimages.php b/lib/rte/courseimages.php new file mode 100644 index 0000000000..67e55b96c0 --- /dev/null +++ b/lib/rte/courseimages.php @@ -0,0 +1,851 @@ +id); + + if (! isteacher($course->id) ) { + error("Only teachers can edit files"); + } + + function html_footer() { + echo ""; + } + + function html_header($course, $wdir, $formfield=""){ + + global $CFG; + + if (! $site = get_site()) { + error("Invalid site!"); + } + + if ($course->id == $site->id) { + $strfiles = get_string("sitefiles"); + } else { + $strfiles = get_string("files"); + } + + if ($wdir == "/") { + $fullnav = "$strfiles"; + } else { + $dirs = explode("/", $wdir); + $numdirs = count($dirs); + $link = ""; + $navigation = ""; + for ($i=1; $i<$numdirs; $i++) { + $navigation .= " -> "; + $link .= "/".urlencode($dirs[$i]); + $navigation .= "id&wdir=$link\">".$dirs[$i].""; + } + $fullnav = "id&wdir=/\">$strfiles $navigation"; + } + + print_header(); + ?> + + '; + echo ''; + echo ''; + echo ''."$course->shortname -> $fullnav".''; + echo ''; + echo ''; + echo ''; + + if ($course->id == $site->id) { + print_heading(get_string("publicsitefileswarning"), "center", 2); + } + + echo ""; + echo ""; + echo "\n"; +} + +function displaydir ($wdir) { +// $wdir == / or /a or /a/b/c/d etc + + global $basedir; + global $id; + global $USER, $CFG; + + $fullpath = $basedir.$wdir; + + $directory = opendir($fullpath); // Find all files + while ($file = readdir($directory)) { + if ($file == "." || $file == "..") { + continue; + } + + if (is_dir($fullpath."/".$file)) { + $dirlist[] = $file; + } else { + $filelist[] = $file; + } + } + closedir($directory); + + $strname = get_string("name"); + $strsize = get_string("size"); + $strmodified = get_string("modified"); + $straction = get_string("action"); + $strmakeafolder = get_string("makeafolder"); + $struploadafile = get_string("uploadafile"); + $strwithchosenfiles = get_string("withchosenfiles"); + $strmovetoanotherfolder = get_string("movetoanotherfolder"); + $strmovefilestohere = get_string("movefilestohere"); + $strdeletecompletely = get_string("deletecompletely"); + $strcreateziparchive = get_string("createziparchive"); + $strrename = get_string("rename"); + $stredit = get_string("edit"); + $strunzip = get_string("unzip"); + $strlist = get_string("list"); + $strchoose = get_string("choose"); + + + echo ""; + echo "
"; + } + + if (! $basedir = make_upload_directory("$course->id")) { + error("The site administrator needs to fix the file permissions"); + } + + $baseweb = $CFG->wwwroot; + +// End of configuration and access control + + + $regexp="\\.\\."; + if (ereg( $regexp, $file, $regs )| ereg( $regexp, $wdir,$regs )) { + $message = "Error: Directories can not contain \"..\""; + $wdir = "/"; + $action = ""; + } + + if (!$wdir) { + $wdir="/"; + } + + + switch ($action) { + + case "upload": + html_header($course, $wdir); + + if (!empty($_FILES['userfile'])) { + $userfile = $_FILES['userfile']; + } else { + $save = false; + } + if (!empty($save)) { + if (!is_uploaded_file($userfile['tmp_name']) or $userfile['size'] == 0) { + notify(get_string("uploadnofilefound")); + } else { + $userfile_name = clean_filename($userfile['name']); + if ($userfile_name) { + $newfile = "$basedir$wdir/$userfile_name"; + if (move_uploaded_file($userfile['tmp_name'], $newfile)) { + chmod($newfile, 0666); + $a = NULL; + $a->file = "$userfile_name (".$userfile['type'].")"; + $a->directory = $wdir; + print_string("uploadedfileto", "", $a); + } else { + notify(get_string("uploadproblem", "", $userfile_name)); + } + } + } + displaydir($wdir); + + } else { + $upload_max_filesize = get_max_upload_file_size(); + $filesize = display_size($upload_max_filesize); + + $struploadafile = get_string("uploadafile"); + $struploadthisfile = get_string("uploadthisfile"); + $strmaxsize = get_string("maxsize", "", $filesize); + $strcancel = get_string("cancel"); + + echo "

$struploadafile ($strmaxsize) --> $wdir"; + echo "
"; + echo "
"; + echo " "; + echo " "; + echo " "; + echo " "; + echo " "; + echo "
"; + echo " "; + echo ""; + echo ""; + echo "
"; + echo " "; + echo " "; + echo " "; + echo " "; + echo "
"; + echo "
"; + } + html_footer(); + break; + + case "delete": + if (!empty($confirm)) { + html_header($course, $wdir); + foreach ($USER->filelist as $file) { + $fullfile = $basedir.$file; + if (! fulldelete($fullfile)) { + echo "
Error: Could not delete: $fullfile"; + } + } + clearfilelist(); + displaydir($wdir); + html_footer(); + + } else { + html_header($course, $wdir); + if (setfilelist($_POST)) { + echo "

".get_string("deletecheckwarning").":

"; + print_simple_box_start("center"); + printfilelist($USER->filelist); + print_simple_box_end(); + echo "
"; + notice_yesno (get_string("deletecheckfiles"), + "".basename($_SERVER['PHP_SELF'])."?id=$id&wdir=$wdir&action=delete&confirm=1", + "".basename($_SERVER['PHP_SELF'])."?id=$id&wdir=$wdir&action=cancel"); + } else { + displaydir($wdir); + } + html_footer(); + } + break; + + case "move": + html_header($course, $wdir); + if ($count = setfilelist($_POST)) { + $USER->fileop = $action; + $USER->filesource = $wdir; + echo "

"; + print_string("selectednowmove", "moodle", $count); + echo "

"; + } + displaydir($wdir); + html_footer(); + break; + + case "paste": + html_header($course, $wdir); + if (isset($USER->fileop) and $USER->fileop == "move") { + foreach ($USER->filelist as $file) { + $shortfile = basename($file); + $oldfile = $basedir.$file; + $newfile = $basedir.$wdir."/".$shortfile; + if (!rename($oldfile, $newfile)) { + echo "

Error: $shortfile not moved"; + } + } + } + clearfilelist(); + displaydir($wdir); + html_footer(); + break; + + case "rename": + if (!empty($name)) { + html_header($course, $wdir); + $name = clean_filename($name); + if (file_exists($basedir.$wdir."/".$name)) { + echo "Error: $name already exists!"; + } else if (!rename($basedir.$wdir."/".$oldname, $basedir.$wdir."/".$name)) { + echo "Error: could not rename $oldname to $name"; + } + displaydir($wdir); + + } else { + $strrename = get_string("rename"); + $strcancel = get_string("cancel"); + $strrenamefileto = get_string("renamefileto", "moodle", $file); + html_header($course, $wdir, "form.name"); + echo "

$strrenamefileto:"; + echo "
"; + echo "
"; + echo " "; + echo " "; + echo " "; + echo " "; + echo " "; + echo " "; + echo "
"; + echo "
"; + echo "
"; + echo " "; + echo " "; + echo " "; + echo " "; + echo "
"; + echo "
"; + } + html_footer(); + break; + + case "mkdir": + if (!empty($name)) { + html_header($course, $wdir); + $name = clean_filename($name); + if (file_exists("$basedir$wdir/$name")) { + echo "Error: $name already exists!"; + } else if (! make_upload_directory("$course->id/$wdir/$name")) { + echo "Error: could not create $name"; + } + displaydir($wdir); + + } else { + $strcreate = get_string("create"); + $strcancel = get_string("cancel"); + $strcreatefolder = get_string("createfolder", "moodle", $wdir); + html_header($course, $wdir, "form.name"); + echo "

$strcreatefolder:"; + echo "
"; + echo "
"; + echo " "; + echo " "; + echo " "; + echo " "; + echo " "; + echo "
"; + echo "
"; + echo "
"; + echo " "; + echo " "; + echo " "; + echo " "; + echo "
"; + echo "
"; + } + html_footer(); + break; + + case "edit": + html_header($course, $wdir); + if (isset($text)) { + $fileptr = fopen($basedir.$file,"w"); + fputs($fileptr, stripslashes($text)); + fclose($fileptr); + displaydir($wdir); + + } else { + $streditfile = get_string("edit", "", "$file"); + $fileptr = fopen($basedir.$file, "r"); + $contents = fread($fileptr, filesize($basedir.$file)); + fclose($fileptr); + + if (mimeinfo("type", $file) == "text/html") { + if ($usehtmleditor = can_use_richtext_editor()) { + $onsubmit = "onsubmit=\"copyrichtext(document.form.text);\""; + } else { + $onsubmit = ""; + } + } else { + $usehtmleditor = false; + $onsubmit = ""; + } + + print_heading("$streditfile"); + + echo "
"; + echo "
"; + echo " "; + echo " "; + echo " "; + echo " "; + print_textarea($usehtmleditor, 25, 80, 680, 400, "text", $contents); + echo "
"; + echo " "; + echo ""; + echo ""; + echo "
"; + echo " "; + echo " "; + echo " "; + echo " "; + echo "
"; + echo "
"; + + if ($usehtmleditor) { + print_richedit_javascript("form", "text", "yes"); + } + + + } + html_footer(); + break; + + case "zip": + if (!empty($name)) { + html_header($course, $wdir); + $name = clean_filename($name); + if (empty($CFG->zip)) { // Use built-in php-based zip function + $files = array(); + foreach ($USER->filelist as $file) { + $files[] = "$basedir/$file"; + } + include_once('../pclzip/pclzip.lib.php'); + $archive = new PclZip("$basedir/$wdir/$name"); + if (($list = $archive->create($files,'',"$basedir/$wdir/")) == 0) { + error($archive->errorInfo(true)); + } + } else { // Use external zip program + $files = ""; + foreach ($USER->filelist as $file) { + $files .= basename($file); + $files .= " "; + } + $command = "cd $basedir/$wdir ; $CFG->zip -r $name $files"; + Exec($command); + } + clearfilelist(); + displaydir($wdir); + + } else { + html_header($course, $wdir, "form.name"); + + if (setfilelist($_POST)) { + echo "

".get_string("youareabouttocreatezip").":

"; + print_simple_box_start("center"); + printfilelist($USER->filelist); + print_simple_box_end(); + echo "
"; + echo "

".get_string("whattocallzip"); + echo "
"; + echo "
"; + echo " "; + echo " "; + echo " "; + echo " "; + echo " "; + echo "
"; + echo "
"; + echo "
"; + echo " "; + echo " "; + echo " "; + echo " "; + echo "
"; + echo "
"; + } else { + displaydir($wdir); + clearfilelist(); + } + } + html_footer(); + break; + + case "unzip": + html_header($course, $wdir); + if (!empty($file)) { + $strname = get_string("name"); + $strsize = get_string("size"); + $strmodified = get_string("modified"); + $strstatus = get_string("status"); + $strok = get_string("ok"); + $strunpacking = get_string("unpacking", "", $file); + + echo "

$strunpacking:

"; + + $file = basename($file); + + if (empty($CFG->unzip)) { // Use built-in php-based unzip function + include_once('../pclzip/pclzip.lib.php'); + $archive = new PclZip("$basedir/$wdir/$file"); + if (!$list = $archive->extract("$basedir/$wdir")) { + error($archive->errorInfo(true)); + } else { // print some output + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + foreach ($list as $item) { + echo ""; + $item['filename'] = str_replace("$basedir/$wdir/", "", $item['filename']); + print_cell("left", $item['filename']); + if (! $item['folder']) { + print_cell("right", display_size($item['size'])); + } else { + echo ""; + } + $filedate = userdate($item['mtime'], get_string("strftimedatetime")); + print_cell("right", $filedate); + print_cell("right", $item['status']); + echo ""; + } + echo "
$strname$strsize$strmodified$strstatus
 
"; + } + + } else { // Use external unzip program + print_simple_box_start("center"); + echo "
";
+                    $command = "cd $basedir/$wdir ; $CFG->unzip -o $file 2>&1";
+                    passthru($command);
+                    echo "
"; + print_simple_box_end(); + } + + echo "
"; + echo " "; + echo " "; + echo " "; + echo " "; + echo "
"; + echo "
"; + } else { + displaydir($wdir); + } + html_footer(); + break; + + case "listzip": + html_header($course, $wdir); + if (!empty($file)) { + $strname = get_string("name"); + $strsize = get_string("size"); + $strmodified = get_string("modified"); + $strok = get_string("ok"); + $strlistfiles = get_string("listfiles", "", $file); + + echo "

$strlistfiles:

"; + $file = basename($file); + + include_once('../lib/pclzip/pclzip.lib.php'); + $archive = new PclZip("$basedir/$wdir/$file"); + if (!$list = $archive->listContent("$basedir/$wdir")) { + notify($archive->errorInfo(true)); + + } else { + echo ""; + echo ""; + foreach ($list as $item) { + echo ""; + print_cell("left", $item['filename']); + if (! $item['folder']) { + print_cell("right", display_size($item['size'])); + } else { + echo ""; + } + $filedate = userdate($item['mtime'], get_string("strftimedatetime")); + print_cell("right", $filedate); + echo ""; + } + echo "
$strname$strsize$strmodified
 
"; + } + echo "
"; + echo " "; + echo " "; + echo " "; + echo " "; + echo "
"; + echo "
"; + } else { + displaydir($wdir); + } + html_footer(); + break; + + case "torte": + if($_POST) + { + while(list($key, $val) = each($_POST)) + { + if(ereg("file([0-9]+)", $key, $regs)) + { + $file = $val; + } + } + if(@filetype($CFG->dataroot ."/". $course->id . $file) == "file") + { + if(mimeinfo("icon", $file) == "image.gif") + { + $url = $CFG->wwwroot ."/file.php?file=/" .$course->id . $file; + runjavascript($url); + } + else + { + print "File is not a image!"; + } + } + else + { + print "You cannot insert FOLDER into richtext editor!!!"; + } + } + break; + case "cancel"; + clearfilelist(); + + default: + html_header($course, $wdir); + displaydir($wdir); + html_footer(); + break; +} + + +/// FILE FUNCTIONS /////////////////////////////////////////////////////////// + + +function fulldelete($location) { + if (is_dir($location)) { + $currdir = opendir($location); + while ($file = readdir($currdir)) { + if ($file <> ".." && $file <> ".") { + $fullfile = $location."/".$file; + if (is_dir($fullfile)) { + if (!fulldelete($fullfile)) { + return false; + } + } else { + if (!unlink($fullfile)) { + return false; + } + } + } + } + closedir($currdir); + if (! rmdir($location)) { + return false; + } + + } else { + if (!unlink($location)) { + return false; + } + } + return true; +} + + + +function setfilelist($VARS) { + global $USER; + + $USER->filelist = array (); + $USER->fileop = ""; + + $count = 0; + foreach ($VARS as $key => $val) { + if (substr($key,0,4) == "file") { + $count++; + $USER->filelist[] = rawurldecode($val); + } + } + return $count; +} + +function clearfilelist() { + global $USER; + + $USER->filelist = array (); + $USER->fileop = ""; +} + + +function printfilelist($filelist) { + global $basedir, $CFG; + + foreach ($filelist as $file) { + if (is_dir($basedir.$file)) { + echo "wwwroot/files/pix/folder.gif\" HEIGHT=16 WIDTH=16> $file
"; + $subfilelist = array(); + $currdir = opendir($basedir.$file); + while ($subfile = readdir($currdir)) { + if ($subfile <> ".." && $subfile <> ".") { + $subfilelist[] = $file."/".$subfile; + } + } + printfilelist($subfilelist); + + } else { + $icon = mimeinfo("icon", $file); + echo "wwwroot/files/pix/$icon\" HEIGHT=16 WIDTH=16> $file
"; + } + } +} + + +function print_cell($alignment="center", $text=" ") { + echo "
"; + echo ""; + echo "$text"; + echo ""; + echo "
"; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo "\n"; + + if ($wdir == "/") { + $wdir = ""; + } + + $count = 0; + + if (!empty($dirlist)) { + asort($dirlist); + foreach ($dirlist as $dir) { + + $count++; + + $filename = $fullpath."/".$dir; + $fileurl = rawurlencode($wdir."/".$dir); + $filesafe = rawurlencode($dir); + $filedate = userdate(filectime($filename), "%d %b %Y, %I:%M %p"); + + echo ""; + + print_cell("center", ""); + print_cell("left", "wwwroot/files/pix/folder.gif\" HEIGHT=16 WIDTH=16 BORDER=0 ALT=\"Folder\">".htmlspecialchars($dir).""); + print_cell("right", "-"); + print_cell("right", $filedate); + print_cell("right", "$strrename"); + + echo ""; + } + } + + + if (!empty($filelist)) { + asort($filelist); + foreach ($filelist as $file) { + + $icon = mimeinfo("icon", $file); + + $count++; + $filename = $fullpath."/".$file; + $fileurl = "$wdir/$file"; + $filesafe = rawurlencode($file); + $fileurlsafe = rawurlencode($fileurl); + $filedate = userdate(filectime($filename), "%d %b %Y, %I:%M %p"); + + echo ""; + + print_cell("center", ""); + echo ""; + + $file_size = filesize($filename); + print_cell("right", display_size($file_size)); + print_cell("right", $filedate); + + if ($icon == "text.gif" || $icon == "html.gif") { + $edittext = "$stredit"; + } else if ($icon == "zip.gif") { + $edittext = "$strunzip "; + $edittext .= "$strlist "; + } else if ($icon == "image.gif") { + $edittext = "wwwroot/$ffurl')\" href=\"\">$strchoose"; + } else { + $edittext = ""; + } + print_cell("right", "$edittext $strrename"); + + echo ""; + } + } + echo "
$strname$strsize$strmodified$straction
"; + if ($CFG->slasharguments) { + $ffurl = "/file.php/$id$fileurl"; + } else { + $ffurl = "/file.php?file=/$id$fileurl"; + } + link_to_popup_window ($ffurl, "display", + "wwwroot/files/pix/$icon\" HEIGHT=16 WIDTH=16 BORDER=0 ALT=\"File\">", + 480, 640); + echo ""; + link_to_popup_window ($ffurl, "display", + htmlspecialchars($file), + 480, 640); + echo "
"; + echo "
"; + + if (empty($wdir)) { + $wdir = "/"; + } + + echo ""; + echo ""; + echo ""; + echo "
"; + echo ""; + echo " "; + $options = array ( + "move" => "$strmovetoanotherfolder", + "delete" => "$strdeletecompletely", + "zip" => "$strcreateziparchive" + ); + if (!empty($count)) { + choose_from_menu ($options, "action", "", "$strwithchosenfiles...", "javascript:document.dirform.submit()"); + } + + echo ""; + echo ""; + if (!empty($USER->fileop) and ($USER->fileop == "move") and ($USER->filesource <> $wdir)) { + echo "
"; + echo " "; + echo " "; + echo " "; + echo " "; + echo "
"; + } + echo "
"; + echo "
"; + echo " "; + echo " "; + echo " "; + echo " "; + echo "
"; + echo "
"; + echo "
"; + echo " "; + echo " "; + echo " "; + echo " "; + echo "
"; + echo "
"; + echo "
"; + +} + +?> diff --git a/lib/rte/dlg_ins_image.php b/lib/rte/dlg_ins_image.php new file mode 100644 index 0000000000..240b9b29aa --- /dev/null +++ b/lib/rte/dlg_ins_image.php @@ -0,0 +1,214 @@ + +fullname = ""; // Just to keep display happy, though browsing may fail + } + +?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Picture Info 
 Url:
 Alt Text:Align: +
  + + + + + + +
Border: Width:Height:Vspace:Hspace:
+

  + +
+ + diff --git a/lib/rte/richedit.php b/lib/rte/richedit.php new file mode 100644 index 0000000000..275b682a23 --- /dev/null +++ b/lib/rte/richedit.php @@ -0,0 +1,340 @@ + + + + + + + + + + + + + + + + + + + + + + +
+
+ Loading ... +
+
+ + + + + + + + + + + + + + + +
+ + @{Bold} + @{Italic} + @{Underline} + @{Strikethrough} + + @{AlignLeft} + @{Center} + @{AlignRight} + @{AlignBlock} + + @{NumberedList} + @{BulettedList} + + @{DecreaseIndent} + @{IncreaseIndent} + @{SelectAll} + + + @{HistoryBack} + @{HistoryForward} + + + @{InsertLink} + @{RemoveLink} + + @{InsertImage} + @{InsertTable} + @{EditTable} + @{InsertLine} + @{InsertSmily} + @{InsertCharacter} + +   + + @{Source} + + +
+   + + @{Style} + + + + + @{Font} + + + + + @{Size} + + + + + @{TextColor} + @{BackgroundColor} + +
+
+
+
+ + + + + + + + diff --git a/lib/rte/rte.php b/lib/rte/rte.php new file mode 100644 index 0000000000..ec214c660a --- /dev/null +++ b/lib/rte/rte.php @@ -0,0 +1,633 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// HTML Text Editing Component for hosting in Web Pages +// Copyright (C) 2001 Ramesys (Contracting Services) Limited +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU LesserGeneral Public License +// along with this program; if not a copy can be obtained from +// +// http://www.gnu.org/copyleft/lesser.html +// +// or by writing to: +// +// Free Software Foundation, Inc. +// 59 Temple Place - Suite 330, +// Boston, +// MA 02111-1307, +// USA. +// +// Original Developer: +// +// Austin David France +// Ramesys (Contracting Services) Limited +// Mentor House +// Ainsworth Street +// Blackburn +// Lancashire +// BB1 6AY +// United Kingdom +// email: Austin.France@Ramesys.com +// +// Home Page: http://richtext.sourceforge.net/ +// Support: http://richtext.sourceforge.net/ +// +//////////////////////////////////////////////////////////////////////////////// +// +// Authors & Contributers: +// +// OZ Austin David France [austin.france@ramesys.com] +// Primary Developer +// +// LEON Leon Reinders [leonreinders@hetnet.nl] +// Author of View Source, History and Extended Style Functions +// +// DIRK Dirk Datzert [Dirk.Datzert@rasselstein-hoesch.de] +// Justify Full Option +// +// BC Bill Chalmers [bill_paula@btinternet.com] +// Font Selection +// +// History: +// +// OZ 21-01-2002 +// Fix a bug in applyOptions() that was not detecting a truth setting +// properly. Changed substr(eq) to substr(eq+1). Also, set the +// runtime style property, not the style property. +// +// OZ 22-01-2002 +// Moved initEditor() function into here from richedit.html +// +// OZ 22-01-2002 +// Added handleDrag() method to handle drag and drop events within the +// html of the editor. Drag and drop is currently disabled until we +// can find a practicle use for it. +// +// OZ 10-02-2002 +// Added code to handle the new Full Justify Option. Implementation of +// a mod to the editor made by Dirk Datzert who supplied the code and +// the Image. +// +// OZ 11-02-2002 +// Startup with text area set to contenteditable="false". The content +// is made editable when the editor has been initialised. +// +// OZ 12-02-2002 +// Fix handling of mouse hover when hover over a button that is in the +// down state. The down state of the button was being lost. This is +// a re-introduction of an earlier bug which I thought was fixed. +// The bug also occured when the button was pressed in some +// circumstances. The fix implemented is to have a button state +// property which is set when the state of a button is set in the +// setState() routine and this is used to restore the button state when +// the button is released or mouse moves out. +// +// OZ 12-06-2002 [richtext-Bugs-567960] Text area of editor window not get focus +// Ensure the doc element (the DIV) has focus once initialisation is +// complete. This ensures that when no HTML is supplied via the docHtml +// property, that focus is where the user expects. +// +// BC 10-07-2002 +// added getfontface() function to retrieve the new "web style" font selection +// this function is called from reset() in the same way as getStyle() +// +//////////////////////////////////////////////////////////////////////////////// + +// Internal (private) properties. +// RichEditor is the global RichEditor object (function) of which there is only +// 1 instance. +RichEditor.txtView = true; // WYSIWYG mode. false == View Source + +// initEditor(): Initialise the editor (called on window load, see below) +function initEditor() +{ + // Apply style data if supplied + if (!public_description.styleData) { + public_description.put_styleData(null); + } + + // Apply default editor options + var strDefaults = 'dragdrop=no;source=yes'; + strDefaults += ';history=' + (document.queryCommandSupported('Undo') ? 'yes' : 'no'); + applyOptions(strDefaults); + + // Prepare the editable region + loading.style.display = 'none'; + doc.contentEditable = "true"; + editor.style.visibility = 'visible'; + + // OZ - 12-06-2002 + // Put focus into the document (required when no HTML is supplied via docHtml property) + doc.focus(); +} + +// checkRange(): make sure our pretend document (the content editable +// DIV with id of "doc") has focus and that a text range exists (which +// is what execCommand() operates on). +function checkRange() +{ + RichEditor.selectedImage = null; + if (!RichEditor.txtView) return; // Disabled in View Source mode + doc.focus(); + if (document.selection.type == "None") { + document.selection.createRange(); + } +var r = document.selection.createRange(); + DBG(1, 'RANGE Bounding(' + + 'top='+r.boundingHeight + + ', left='+r.boundingHeight + + ', width='+r.boundingWidth + + ', height='+r.boundingHeight + ')' + + ', Offset(' + + 'top='+r.offsetTop + + ', left='+r.offsetLeft + ')' + + ', Text=(' + r.text + ')' + + ', HTML=(' + r.htmlText + ')' + ); +} + +// post(): Called in response to clicking the post button in the +// toolbar. It fires an event in the container named post, passing the +// HTML of our newly edited document as the data argument. +function post() +{ + DBG(1, 'Raise "post" event'); + window.external.raiseEvent("post", doc.innerHTML); +} + +// insert(): called in response to clicking the insert table, image, +// smily icons in the toolbar. Loads up an appropriate dialog to +// prompt for information, the dialog then returns the HTML code or +// NULL. We paste the HTML code into the document. +function insert(what) +{ + if (!RichEditor.txtView) return; // Disabled in View Source mode + + DBG(1, 'insert(' + what + ')'); + + // Chose action based on what is being inserted. + switch(what) + { + case "table": + strPage = "dlg_ins_table.html"; + strAttr = "status:no;dialogWidth:340px;dialogHeight:360px;help:no"; + break; + case "smile": + strPage = "dlg_ins_smile.html"; + strAttr = "status:no;dialogWidth:300px;dialogHeight:350px;help:no"; + break; + case "char": + strPage = "dlg_ins_char.html"; + strAttr = "status:no;dialogWidth:450px;dialogHeight:290px;help:no"; + break; + case "image": + strPage = "dlg_ins_image.php?id="; + strAttr = "status:no;dialogWidth:400px;dialogHeight:200px;help:no";' ' + break; + case "about": + strPage = "dlg_about.html"; + strAttr = "status:no;dialogWidth:500px;dialogHeight:405px;help:no";' ' + break; + } + + // run the dialog that implements this type of element + html = showModalDialog(strPage, window, strAttr); + + // and insert any result into the document. + if (html) { + insertHtml(html); + } +} + +// insertHtml(): Insert the supplied HTML into the current position +// within the document. +function insertHtml(html) +{ + doc.focus(); + var sel = document.selection.createRange(); + // don't try to insert HTML into a control selection (ie. image or table) + if (document.selection.type == 'Control') { + return; + } + sel.pasteHTML(html); +} + +// doStyle(): called to handle the simple style commands such a bold, +// italic etc. These require no special handling, just a call to +// execCommand(). We also call reset so that the toolbar represents +// the state of the current text. +// +// 2002-07-30 Updated based on patch submitted by Michael Keck (mkkeck) +// +function doStyle(s){ + if(!RichEditor.txtView) return; + /* Disabled in View Source mode */ + DBG(1, 'doStyle(' + s + ')'); + checkRange(); + if(s!='InsertHorizontalRule'){ + /* what command string? */ + document.execCommand(s); + } else if( s=='InsertHorizontalRule') { + /* if s=='InsertHorizontalRule then use this command */ + document.execCommand(s,false, null); + + /* Note: + In your source view the
has an ID like this +
+ */ + } + reset(); +} + + +// link(): called to insert a hyperlink. It will use the selected text +// if there is some, or the URL entered if not. If clicked when over a +// link, that link is allowed to be edited. +function link(on) +{ + if (!RichEditor.txtView) return; // Disabled in View Source mode + + var strURL = "http://"; + var strText; + + // First, pick up the current selection. + doc.focus(); + var r = document.selection.createRange(); + var el = r.parentElement(); + + // Is this aready a link? + if (el && el.nodeName == "A") { + r.moveToElementText(el); + if (!on) { // If removing the link, then replace all with + r.pasteHTML(el.innerHTML); + return; + } + strURL = el.href; + } + + // Get the text associated with this link + strText = r.text; + + // Prompt for the URL + strURL = window.prompt("Enter URL", strURL); + if (strURL) { + // Default the TEXT to the url if non selected + if (!strText || !strText.length) { + strText = strURL; + } + + // Replace with new URL + r.pasteHTML('' + strText + ''); + } + + reset(); +} + +// sel(); similar to doStyle() but called from the dropdown list boxes +// for font and style commands. +function sel(el) +{ + if (!RichEditor.txtView) return; // Disabled in View Source mode + checkRange(); + switch(el.id) + { + case "ctlFont": + document.execCommand('FontName', false, el[el.selectedIndex].value); + break; + case "ctlSize": + document.execCommand('FontSize', false, el[el.selectedIndex].value); + break; + case "ctlStyle": + document.execCommand('FormatBlock', false, el[el.selectedIndex].text); + break; + } + doc.focus(); + reset(); +} + +// pickColor(): called when the text or fill color icons are clicked. Displays +// the color chooser control. The color setting is completed by the event +// handler of this control (see richedit.html) +function pickColor(fg) +{ + if (!RichEditor.txtView) return; // Disabled in View Source mode + checkRange(); + var el = window.event.srcElement; + if (el && el.nodeName == "IMG") { + setState(el, true); + } + color.style.top = window.event.clientY + 10; + color.style.left = window.event.clientX - 250; + color.style.display = 'block'; + color._fg = fg; +} + +// setColor(): called from the fore/back color selection dialog event handler +// to set/reset the fore/background color. +function setColor(name, data) +{ + color.style.display = 'none'; + checkRange(); + if (!data) { + removeFormat(document.selection.createRange(), color._fg); + } else { + document.execCommand(color._fg, false, data); + } + setState(btnText, false); + setState(btnFill, false); + doc.focus(); +} + +// removeFormat(): Called to remove specific formats from the selected text. +// The 'removeFormat' command removes all formatting. The principle behind +// this routine is to have a list of the possible formats the selection may +// have, check the selection for the current formats, ignoreing the one we +// want to use, then remove all formatting and then re-apply all but the +// one we wanted to remove. +function removeFormat(r, name) +{ + var cmd = [ "Bold", "Italic", "Underline", "Strikethrough", "FontName", "FontSize", "ForeColor", "BackColor" ]; + var on = new Array(cmd.length); + for (var i = 0; i < cmd.length; i++) { + on[i] = name == cmd[i] ? null : r.queryCommandValue(cmd[i]); + } + r.execCommand('RemoveFormat'); + for (var i = 0; i < cmd.length; i++) { + if (on[i]) r.execCommand(cmd[i], false, on[i]); + } +} + +// setValue(): called from reset() to make a select list show the current font +// or style attributes +function selValue(el, str) +{ + if (!RichEditor.txtView) return; // Disabled in View Source mode + for (var i = 0; i < el.length; i++) { + if ((!el[i].value && el[i].text == str) || el[i].value == str) { + el.selectedIndex = i; + return; + } + } + el.selectedIndex = 0; +} + +// setState(): called from reset() to make a button represent the state +// of the current text. Pressed is on, unpressed is off. +function setState(el, on) +{ + if (!RichEditor.txtView) return; // Disabled in View Source mode + if (!el.disabled) { + if (on) { + el.defaultState = el.className = "down"; + } else { + el.defaultState = el.className = null; + } + } +} + +// getStyle(): called to obtain the class or type of formatting applied to an element, +// This is used by reset() to set the state of the toolbar to indicate the class of +// the current element. +function getStyle() { + var style = document.queryCommandValue('FormatBlock'); + if (style == "Normal") { + doc.focus(); + var rng = document.selection.createRange(); + if (typeof(rng.parentElement) != "undefined") { + var el = rng.parentElement(); + var tag = el.nodeName.toUpperCase(); + var str = el.className.toLowerCase(); + if (!(tag == "DIV" && el.id == "doc" && str == "textedit")) { + if (tag == "SPAN") { + style = "." + str; + } else if (str == "") { + style = tag; + } else { + style = tag + "." + str; + } + } + return style; + } + } + return style; +} + +// getfontface(): called to obtain the face attribute applied to a font tag, +// This is used by reset() to set the state of the toolbar to indicate the class of +// the current element. +function getfontface() +{ +var family = document.selection.createRange(); //create text range + +// don't get font face for image or table +if (document.selection.type == 'Control') { + return; +} + +var el = family.parentElement(); //get parent element +var tag = el.nodeName.toUpperCase(); //convert tag element to upper case + + if (typeof(el.parentElement) != "undefined" && tag == "FONT") { //only do function if tag is font - this is for greater execution speed + var elface = el.getAttribute('FACE'); //get the font tags FACE attribute + return elface; //return the value of the face attribute to the reset() function + } +} + +// markSelectedElement(): called by onClick and onKeyup events +// on the contectEditable area +function markSelectedElement() { + + RichEditor.selectedImage = null; + + var r = document.selection.createRange(); + + if (document.selection.type != 'Text') { + if (r.length == 1) { + if (r.item(0).tagName == "IMG") { + RichEditor.selectedImage = r.item(0); + } + } + } +} + +// reset(): called from all over the place to make the toolbar +// represent the current text. If el specified, it was called from +// hover(off) +function reset(el) +{ + if (!RichEditor.txtView) return; // Disabled in View Source mode + if (!el) color.style.display = 'none'; + if (!el || el == ctlStyle) selValue(ctlStyle, getStyle()); + if (!el || el == ctlFont) selValue(ctlFont, getfontface()); + if (!el || el == ctlSize) selValue(ctlSize, document.queryCommandValue('FontSize')); + if (!el || el == btnBold) setState(btnBold, document.queryCommandValue('Bold')); + if (!el || el == btnItalic) setState(btnItalic, document.queryCommandValue('Italic')); + if (!el || el == btnUnderline) setState(btnUnderline, document.queryCommandValue('Underline')); + if (!el || el == btnStrikethrough) setState(btnStrikethrough, document.queryCommandValue('Strikethrough')); + if (!el || el == btnLeftJustify) setState(btnLeftJustify, document.queryCommandValue('JustifyLeft')); + if (!el || el == btnCenter) setState(btnCenter, document.queryCommandValue('JustifyCenter')); + if (!el || el == btnRightJustify) setState(btnRightJustify, document.queryCommandValue('JustifyRight')); + if (!el || el == btnFullJustify) setState(btnFullJustify, document.queryCommandValue('JustifyFull')); + if (!el || el == btnNumList) setState(btnNumList, document.queryCommandValue('InsertOrderedList')); + if (!el || el == btnBulList) setState(btnBulList, document.queryCommandValue('InsertUnorderedList')); +} + +// hover(): Handles mouse hovering over toolbar buttons +function hover(on) +{ + if (!RichEditor.txtView) return; // Disabled in View Source mode + var el = window.event.srcElement; + if (el && !el.disabled && el.nodeName == "IMG" && el.className != "spacer") { + if (on) { + el.className = "hover"; + } else { + el.className = el.defaultState ? el.defaultState : null; + } + } +} +// hover(): Handles mouse clicks on toolbar buttons +function press(on) +{ + if (!RichEditor.txtView) return; // Disabled in View Source mode + var el = window.event.srcElement; + if (el && !el.disabled && el.nodeName == "IMG" && el.className != "spacer") { + if (on) { + el.className = "down"; + } else { + el.className = el.className == "down" ? "hover" : el.defaultState ? el.defaultState : null; + } + } +} + +// addTag(): This is the handler for the style dropdown. This takes value +// selected and interprates it and makes the necessary changes to the HTML to +// apply this style. +function addTag(obj) { + + if (!RichEditor.txtView) return; // Disabled in View Source mode + + // Determine the type of element we are dealing with. + // TYPE 0 IS NORMAL-TAG, 1 IS CLASS, 2 IS SUBCLASS, 3 = Format Block command + var value = obj[obj.selectedIndex].value; + if (!value) { // Format Block + sel(obj); + return; + } + + var type = 0; // TAG + + if (value.indexOf(".") == 0) { // .className + type = 1; + } else if (value.indexOf(".") != -1) { // TAG.className + type = 2; + } + + doc.focus(); + + // Pick up the highlighted text + var r = document.selection.createRange(); + r.select(); + var s = r.htmlText; + + // If we have some selected text, then ignore silly selections + if (s == " " || s == " ") { + return; + } + + // How we apply formatting is based upon the type of formitting being + // done. + switch(type) + { + case 1: + // class: Wrap the selected text with a span of the specified + // class name + value = value.substring(1,value.length); + r.pasteHTML("" + r.htmlText + "") + break; + + case 2: + // subclass: split the value into tag + class + value = value.split("."); + r.pasteHTML('<' + value[0] + ' class="' + value[1] +'">' + + r.htmlText + + '' + ); + break; + + default: + // TAG: wrap up the highlighted text with the specified tag + r.pasteHTML("<"+value+">"+r.htmlText+"") + break; + } +} + +// initStyleDropdown(): This takes the passed styleList and generates the style +// dropdown list box from it. +function initStyleDropdown(styleList) { + + // Build the option list for the styles dropdown from the passed styles + for (var i = 0; i < styleList.length; i++) { + var oOption = document.createElement("OPTION"); + if (styleList[i][0]) oOption.value = styleList[i][0]; + oOption.text = styleList[i][1]; + oOption.style.backgroundColor = 'white'; + document.all.ctlStyle.add(oOption); + } +} + +// applyOptions(): This takes the passed options string and actions them. +// Called during the init process. +function applyOptions(str) +{ + var options = str.split(";"); + for (var i = 0; i < options.length; i++) { + var eq = options[i].indexOf('='); + var on = eq == -1 ? true : "yes;true;1".indexOf(options[i].substr(eq+1).toLowerCase()) != -1; + var name = eq == -1 ? options[i] : options[i].substr(0,eq); + var el = document.all("feature" + name); + if (el) { + el.runtimeStyle.display = (on ? 'inline' : 'none'); + } else { + if (!RichEditor.aOptions) RichEditor.aOptions = new Array; + RichEditor.aOptions[name] = on; + } + } +} + +// getOption(): Get the value for a previously set option or return undefined if +// the option is not set. +function getOption(name) +{ + if (RichEditor.aOptions) return RichEditor.aOptions[name]; + return; // Undefined +} + +// Handle drag and drop events into the editor window. Until we +// work out how to handle these better (which requires co-operation +// from the code being dragged from as far as I can tell) we simply +// disable the functionality. +function handleDrag(n) +{ + // if drag and drop is disabled, then cancel the dragdrop + // events + if (!getOption("dragdrop")) + { + switch(n) { + case 0: // ondragenter + window.event.dataTransfer.dropEffect = "none"; + break; + } + // Cancel the event + window.event.returnValue = false; + } +} + diff --git a/lib/weblib.php b/lib/weblib.php index 1011fa395b..4976b4cd73 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -1222,13 +1222,27 @@ function make_table($table) { return $output; } -function print_textarea($richedit, $rows, $cols, $width, $height, $name, $value="") { +function print_textarea($richedit, $rows, $cols, $width, $height, $name, $value="", $courseid=0) { /// Prints a richtext field or a normal textarea - global $CFG, $THEME; + global $CFG, $THEME, $course; + + if (!$courseid) { + if (!empty($course->id)) { // search for it in global context + $courseid = $course->id; + } + } if ($richedit) { + if ($courseid) { + if (isteacher($courseid)) { + $richediturl = "$CFG->wwwroot/lib/rte/richedit.php?id=$courseid&wwwroot=$CFG->wwwroot"; + } else { + $richediturl = "$CFG->wwwroot/lib/rte/richedit.html"; + } + } + echo "wwwroot/lib/rte/richedit.html\""; + echo " data=\"$richediturl\""; echo " width=\"$width\" height=\"$height\" "; echo " type=\"text/x-scriptlet\" VIEWASTEXT=\"true\">\n"; echo "