diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000000000..6a8d44476c790 --- /dev/null +++ b/.htaccess @@ -0,0 +1 @@ +All the documention is in the "doc" subdirectory. :-) diff --git a/README b/README new file mode 100644 index 0000000000000..6a8d44476c790 --- /dev/null +++ b/README @@ -0,0 +1 @@ +All the documention is in the "doc" subdirectory. :-) diff --git a/admin/cron.php b/admin/cron.php new file mode 100644 index 0000000000000..7b2851cfc5fc3 --- /dev/null +++ b/admin/cron.php @@ -0,0 +1,48 @@ +\n"; + + if (!isset($p)) { + $p = $GLOBALS[argv][1]; + } + + if ($p <> $PASSWORD) { + add_to_log("Error: bad cron password!"); + echo "Error: bad password.\n"; + die; + } + + $timenow = time(); + + if ($mods = get_records_sql("SELECT * FROM modules WHERE cron > 0 AND (($timenow - lastcron) > cron)")) { + foreach ($mods as $mod) { + $cronfile = "$CFG->dirroot/mod/$mod->name/cron.php"; + if (file_exists($cronfile)) { + include($cronfile); + if (! set_field("modules", "lastcron", $timenow, "id", $mod->id)) { + echo "Error: could not update timestamp for $mod->fullname\n"; + } + } + } + } + echo "Cron script completed correctly\n"; + +?> diff --git a/admin/index.php b/admin/index.php new file mode 100644 index 0000000000000..7a9bb9d64329e --- /dev/null +++ b/admin/index.php @@ -0,0 +1,97 @@ +wwwroot == "http://example.com") { + error("Moodle has not been configured yet. You need to to edit config.php first."); + } + + // Check databases and modules and install as needed. + if (! $db->Metatables() ) { + print_header("Setting up database", "Setting up database", "Setting up databases for the first time", ""); + if (modify_database("$CFG->dirroot/admin/moodle-core.sql")) { + notify("Main databases set up successfully"); + } else { + error("Error: Main databases NOT set up successfully"); + } + print_heading("Continue"); + die; + } + + // Find and check all modules and load them up. + $dir = opendir("$CFG->dirroot/mod"); + while ($mod = readdir($dir)) { + if ($mod == "." || $mod == "..") { + continue; + } + + $fullmod = "$CFG->dirroot/mod/$mod"; + if (filetype($fullmod) != "dir") { + continue; + } + + unset($module); + + include_once("$CFG->dirroot/mod/$mod/module.php"); # defines $module + + if (!isset($module)) { + continue; + } + + $module->name = $mod; // The name MUST match the directory + + if ($currmodule = get_record("modules", "name", $module->name)) { + if ($currmodule->version == $module->version) { + // do nothing + } else if ($currmodule->version < $module->version) { + notify("$module->name module needs upgrading"); // XXX do the upgrade here + } else { + error("Version mismatch: $module->name can't downgrade $currmodule->version -> $module->version !"); + } + + } else { // module not installed yet, so install it + if (modify_database("$fullmod/install.sql")) { + if ($module->id = insert_record("modules", $module)) { + notify("$module->name tables have been set up correctly"); + } else { + error("$module->name module could not be added to the module list!"); + } + } else { + error("$module->name tables could NOT be set up successfully!"); + } + } + } + + // Set up the overall site name etc. + if (! $course = get_record("course", "category", 0)) { + redirect("site.php"); + } + + if (!isadmin()) { + if (record_exists_sql("SELECT * FROM user_admins")) { + require_login(); + } else { + redirect("user.php"); + } + } + + + // At this point, the databases exist, and the user is an admin + + print_header("$course->fullname: Administration Page","$course->fullname: Administration Page", "Admin"); + + echo ""; + + + print_footer(); +?> + + diff --git a/admin/log.php b/admin/log.php new file mode 100644 index 0000000000000..539881fb7b267 --- /dev/null +++ b/admin/log.php @@ -0,0 +1,22 @@ +Execute("SELECT l.*, u.firstname, u.lastname, u.email FROM log l, user u WHERE l.user = u.id ORDER BY l.time ASC"); + + echo "" + while (! $logs->EOF) { + $log = (object)$logs->fields; + + echo ""; + echo ""; + + $logs->MoveNext(); + } + + echo "
".date("l, j F Y, g:i A T", $log->time); + echo "email\">$log->firstname $log->lastname"; + echo "$log->ip"; + echo "$log->url"; + echo "$log->message"; + echo "
"; + +?> diff --git a/admin/moodle-core.sql b/admin/moodle-core.sql new file mode 100755 index 0000000000000..8ba2123557c2c --- /dev/null +++ b/admin/moodle-core.sql @@ -0,0 +1,189 @@ +# phpMyAdmin MySQL-Dump +# version 2.2.1 +# http://phpwizard.net/phpMyAdmin/ +# http://phpmyadmin.sourceforge.net/ (download page) +# +# Host: localhost +# Generation Time: Nov 14, 2001 at 05:04 PM +# Server version: 3.23.36 +# PHP Version: 4.0.6 +# Database : `moodle` +# -------------------------------------------------------- + +# +# Table structure for table `course` +# + +CREATE TABLE course ( + id int(10) unsigned NOT NULL auto_increment, + category int(10) unsigned NOT NULL default '0', + password varchar(50) NOT NULL default '', + fullname varchar(254) NOT NULL default '', + shortname varchar(15) NOT NULL default '', + summary text NOT NULL, + format tinyint(4) NOT NULL default '1', + teacher varchar(100) NOT NULL default 'Teacher', + startdate int(10) unsigned NOT NULL default '0', + enddate int(10) unsigned NOT NULL default '0', + timemodified int(10) unsigned NOT NULL default '0', + PRIMARY KEY (id) +) TYPE=MyISAM; +# -------------------------------------------------------- + +# +# Table structure for table `course_categories` +# + +CREATE TABLE course_categories ( + id int(10) unsigned NOT NULL auto_increment, + name varchar(255) NOT NULL default '', + PRIMARY KEY (id), + UNIQUE KEY id (id) +) TYPE=MyISAM COMMENT='Course categories'; +# -------------------------------------------------------- + +# +# Table structure for table `course_modules` +# + +CREATE TABLE course_modules ( + id int(10) unsigned NOT NULL auto_increment, + course int(10) unsigned NOT NULL default '0', + module int(10) unsigned NOT NULL default '0', + instance int(10) unsigned NOT NULL default '0', + week int(10) unsigned NOT NULL default '0', + added int(10) unsigned NOT NULL default '0', + deleted tinyint(1) unsigned NOT NULL default '0', + score tinyint(4) NOT NULL default '0', + PRIMARY KEY (id), + UNIQUE KEY id (id) +) TYPE=MyISAM; +# -------------------------------------------------------- + +# +# Table structure for table `course_weeks` +# + +CREATE TABLE course_weeks ( + id int(10) unsigned NOT NULL auto_increment, + course int(10) unsigned NOT NULL default '0', + week int(10) unsigned NOT NULL default '0', + summary varchar(255) NOT NULL default '', + sequence varchar(255) NOT NULL default '', + PRIMARY KEY (id) +) TYPE=MyISAM; +# -------------------------------------------------------- + +# +# Table structure for table `logs` +# + +CREATE TABLE logs ( + id int(10) unsigned NOT NULL auto_increment, + time int(10) unsigned NOT NULL default '0', + user int(10) unsigned NOT NULL default '0', + course int(10) unsigned NOT NULL default '0', + ip varchar(15) NOT NULL default '', + url varchar(200) NOT NULL default '', + message varchar(255) NOT NULL default '', + PRIMARY KEY (id) +) TYPE=MyISAM; +# -------------------------------------------------------- + +# +# Table structure for table `modules` +# + +CREATE TABLE modules ( + id int(10) unsigned NOT NULL auto_increment, + name varchar(20) NOT NULL default '', + fullname varchar(255) NOT NULL default '', + version int(10) NOT NULL default '0', + cron int(10) unsigned NOT NULL default '0', + lastcron int(10) unsigned NOT NULL default '0', + search varchar(255) NOT NULL default '', + PRIMARY KEY (id), + UNIQUE KEY id (id) +) TYPE=MyISAM; +# -------------------------------------------------------- + +# +# Table structure for table `user` +# + +CREATE TABLE user ( + id int(10) unsigned NOT NULL auto_increment, + confirmed tinyint(1) NOT NULL default '0', + username varchar(100) NOT NULL default '', + password varchar(32) NOT NULL default '', + idnumber varchar(12) default NULL, + firstname varchar(20) NOT NULL default '', + lastname varchar(20) NOT NULL default '', + email varchar(100) NOT NULL default '', + icq varchar(15) default NULL, + phone1 varchar(20) default NULL, + phone2 varchar(20) default NULL, + institution varchar(40) default NULL, + department varchar(30) default NULL, + address varchar(70) default NULL, + city varchar(20) default NULL, + country char(2) default NULL, + firstaccess int(10) unsigned NOT NULL default '0', + lastaccess int(10) unsigned NOT NULL default '0', + lastlogin int(10) unsigned NOT NULL default '0', + currentlogin int(10) unsigned NOT NULL default '0', + lastIP varchar(15) default NULL, + personality varchar(5) default NULL, + picture tinyint(1) default NULL, + url varchar(255) default NULL, + description text, + research tinyint(1) unsigned NOT NULL default '0', + forwardmail tinyint(1) unsigned NOT NULL default '0', + timemodified int(10) unsigned NOT NULL default '0', + PRIMARY KEY (id), + UNIQUE KEY username (username), + UNIQUE KEY id (id) +) TYPE=MyISAM COMMENT='One record for each person'; +# -------------------------------------------------------- + +# +# Table structure for table `user_admins` +# + +CREATE TABLE user_admins ( + id int(10) unsigned NOT NULL auto_increment, + user int(10) unsigned NOT NULL default '0', + PRIMARY KEY (id), + UNIQUE KEY id (id) +) TYPE=MyISAM COMMENT='One record per administrator user'; +# -------------------------------------------------------- + +# +# Table structure for table `user_students` +# + +CREATE TABLE user_students ( + id int(10) unsigned NOT NULL auto_increment, + user int(10) unsigned NOT NULL default '0', + course int(10) unsigned NOT NULL default '0', + start int(10) unsigned NOT NULL default '0', + end int(10) unsigned NOT NULL default '0', + time int(10) unsigned NOT NULL default '0', + PRIMARY KEY (id), + UNIQUE KEY id (id) +) TYPE=MyISAM; +# -------------------------------------------------------- + +# +# Table structure for table `user_teachers` +# + +CREATE TABLE user_teachers ( + id int(10) unsigned NOT NULL auto_increment, + user int(10) unsigned NOT NULL default '0', + course int(10) unsigned NOT NULL default '0', + authority varchar(10) default NULL, + PRIMARY KEY (id), + UNIQUE KEY id (id) +) TYPE=MyISAM COMMENT='One record per teacher per course'; + diff --git a/admin/site.html b/admin/site.html new file mode 100644 index 0000000000000..9bb796fbf5a9f --- /dev/null +++ b/admin/site.html @@ -0,0 +1,28 @@ +
+ + + + + + + + + + + + + + + + + +

Full site name:

+ +

Short name for site (eg single word):

+ +

Front page description:

+ +
+ + +
diff --git a/admin/site.php b/admin/site.php new file mode 100644 index 0000000000000..0ef80daf43dab --- /dev/null +++ b/admin/site.php @@ -0,0 +1,84 @@ +timemodified = time(); + + if ($form->id) { + if (update_record("course", $form)) { + add_to_log("Updated site settings", $course->id); + redirect("$CFG->wwwroot/admin/", "Changes saved"); + } else { + error("Serious Error! Could not update the course record! (id = $form->id)"); + } + } else { + if ($newid = insert_record("course", $form)) { + $cat->name = "General"; + if (insert_record("course_categories", $cat)) { + add_to_log("Inserted a new course # $newid", $newid); + redirect("$CFG->wwwroot/admin/", "Changes saved", "1"); + } else { + error("Serious Error! Could not set up the default categories!"); + } + } else { + error("Serious Error! Could not set up the site!"); + } + } + die; + } else { + foreach ($err as $key => $value) { + $focus = "form.$key"; + } + + } + } + +/// Otherwise fill and print the form. + + if ($course && !$form) { + $form = $course; + } else { + $form->category = 0; + } + + print_header("Admin: Setting up site", "Administration: Setting up site", + "wwwroot/admin/\">Admin -> Setting up site", "$focus"); + + print_simple_box_start("center", "", "$THEME->cellheading"); + print_heading("Editing site settings"); + include("site.html"); + print_simple_box_end(); + print_footer(); + + exit; + +/// Functions ///////////////////////////////////////////////////////////////// + +function validate_form(&$form, &$err) { + + if (empty($form->fullname)) + $err["fullname"] = "Missing site name"; + + if (empty($form->shortname)) + $err["shortname"] = "Missing short site name"; + + if (empty($form->summary)) + $err["summary"] = "Missing site description"; + + return; +} + + +?> diff --git a/admin/teacher.php b/admin/teacher.php new file mode 100644 index 0000000000000..4aec93f917f9c --- /dev/null +++ b/admin/teacher.php @@ -0,0 +1,108 @@ +wwwroot/admin/"); + } + + require_login(); + + if (!isadmin()) { + error("You must be an administrator to edit users this way."); + } + + if (!$id) { + $courses = get_records_sql("SELECT * from course WHERE category > 0 ORDER BY fullname"); + + print_header("Add teachers to a course", "Add teachers to a course", "wwwroot/admin\">Admin -> Add teachers", ""); + print_heading("Choose a course to add teachers to"); + print_simple_box_start("CENTER"); + foreach ($courses as $course) { + echo "id\">$course->fullname
"; + } + print_simple_box_end(); + print_footer(); + exit; + } + + if (! $course = get_record("course", "id", $id)) { + error("Course ID was incorrect (can't find it)"); + } + + +/// If data submitted, then process and store. + + if (match_referer() && isset($HTTP_POST_VARS)) { + + $usernew = (object)$HTTP_POST_VARS; + + if (find_form_errors($user, $usernew, $err) ) { + $user = $usernew; + + } else { + + $usernew->timemodified = time(); + + if (update_record("user", $usernew)) { + redirect("index.php", "Changes saved"); + } else { + error("Could not update the user record ($user->id)"); + } + } + } + +/// Otherwise fill and print the form. + + XXXXXXX + + print_header("Edit user profile", "Edit user profile", "wwwroot/admin\">Admin -> Edit user", ""); + + print_simple_box_start("center", "", "$THEME->cellheading"); + echo "

User profile for $usernew->firstname $usernew->lastname

"; + include("user.html"); + print_simple_box_end(); + + print_footer(); + + + + +/// FUNCTIONS //////////////////// + +function find_form_errors(&$user, &$usernew, &$err) { + + if (empty($usernew->email)) + $err["email"] = "Missing email address"; + + else if (! validate_email($usernew->email)) + $err["email"] = "Invalid email address, check carefully"; + + else if ($otheruser = get_record("user", "email", $usernew->email)) { + if ($otheruser->id <> $user->id) { + $err["email"] = "Email address already in use by someone else."; + } + } + $user->email = $usernew->email; + + if (empty($user->password) && empty($usernew->password)) { + $err["password"] = "Must have a password"; + } + + if (empty($usernew->username)) + $err["username"] = "Must have a username"; + + if (empty($usernew->firstname)) + $err["firstname"] = "Must enter your first name"; + + if (empty($usernew->lastname)) + $err["lastname"] = "Must enter your last name"; + + return count($err); +} + + +?> diff --git a/admin/user.html b/admin/user.html new file mode 100644 index 0000000000000..deba34a0888e0 --- /dev/null +++ b/admin/user.html @@ -0,0 +1,82 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

First name:

+ +

Last name:

+ +

Username:

+ +

New Password:

+ +

Email:

+ +

ICQ Number:

+ +

Phone Number 1:

+ +

Phone Number 2:

+ +

Address:

+ +

Web Address:

+ +

Description:

+

New picture:

+ + + ( .jpg or .png ) + +
+ +
diff --git a/admin/user.php b/admin/user.php new file mode 100644 index 0000000000000..41321732f16d9 --- /dev/null +++ b/admin/user.php @@ -0,0 +1,211 @@ +firstname = "Admin"; + $user->lastname = "User"; + $user->username = "admin"; + $user->password = ""; + $user->email = "root@localhost"; + $user->confirmed = 1; + $user->timemodified = time(); + + if (! $id = insert_record("user", $user)) { + error("Could not create admin user record !!!"); + } + + $admin->user = $id; + + if (! insert_record("user_admins", $admin)) { + error("Could not make user $id an admin !!!"); + } + + if (! $user = get_record("user", "id", $id)) { + error("User ID was incorrect (can't find it)"); + } + + if (! $course = get_record("course", "category", 0)) { + error("Could not find site-level course"); + } + + $teacher->user = $user->id; + $teacher->course = $course->id; + $teacher->authority = 1; + if (! insert_record("user_teachers", $teacher)) { + error("Could not make user $id a teacher of site-level course !!!"); + } + + $USER = $user; + $USER->loggedin = true; + $USER->admin = true; + $USER->teacher["$course->id"] = true; + } + + require_login(); + + if (!isadmin()) { + error("You must be an administrator to edit users this way."); + } + + if (!$id) { + $users = get_records_sql("SELECT * from user ORDER BY firstname"); + + print_header("Edit users", "Edit users", "wwwroot/admin\">Admin -> Edit users", ""); + echo "
"; + foreach ($users as $user) { + echo "id\">$user->firstname $user->lastname
"; + } + echo "
"; + print_footer(); + exit; + } + + if (! $user = get_record("user", "id", $id)) { + error("User ID was incorrect (can't find it)"); + } + + +/// If data submitted, then process and store. + + if (match_referer() && isset($HTTP_POST_VARS)) { + + $usernew = (object)$HTTP_POST_VARS; + + if (find_form_errors($user, $usernew, $err) ) { + $user = $usernew; + + } else { + + $timenow = time(); + + if ($imagefile && $imagefile!="none") { + $imageinfo = GetImageSize($imagefile); + $image->width = $imageinfo[0]; + $image->height = $imageinfo[1]; + $image->type = $imageinfo[2]; + + switch ($image->type) { + case 2: $im = ImageCreateFromJPEG($imagefile); break; + case 3: $im = ImageCreateFromPNG($imagefile); break; + default: error("Image must be in JPG or PNG format"); + } + if (function_exists("ImageCreateTrueColor")) { + $im1 = ImageCreateTrueColor(100,100); + $im2 = ImageCreateTrueColor(35,35); + } else { + $im1 = ImageCreate(100,100); + $im2 = ImageCreate(35,35); + } + + $cx = $image->width / 2; + $cy = $image->height / 2; + + if ($image->width < $image->height) { + $half = floor($image->width / 2.0); + } else { + $half = floor($image->height / 2.0); + } + + if (!file_exists("$CFG->dataroot/users")) { + mkdir("$CFG->dataroot/users", 0777); + } + if (!file_exists("$CFG->dataroot/users/$USER->id")) { + mkdir("$CFG->dataroot/users/$USER->id", 0777); + } + + ImageCopyBicubic($im1, $im, 0, 0, $cx-$half, $cy-$half, 100, 100, $half*2, $half*2); + ImageCopyBicubic($im2, $im, 0, 0, $cx-$half, $cy-$half, 35, 35, $half*2, $half*2); + + // Draw borders over the top. + $black1 = ImageColorAllocate ($im1, 0, 0, 0); + $black2 = ImageColorAllocate ($im2, 0, 0, 0); + ImageLine ($im1, 0, 0, 0, 99, $black1); + ImageLine ($im1, 0, 99, 99, 99, $black1); + ImageLine ($im1, 99, 99, 99, 0, $black1); + ImageLine ($im1, 99, 0, 0, 0, $black1); + ImageLine ($im2, 0, 0, 0, 34, $black2); + ImageLine ($im2, 0, 34, 34, 34, $black2); + ImageLine ($im2, 34, 34, 34, 0, $black2); + ImageLine ($im2, 34, 0, 0, 0, $black2); + + ImageJpeg($im1, "$CFG->dataroot/users/$USER->id/f1.jpg", 90); + ImageJpeg($im2, "$CFG->dataroot/users/$USER->id/f2.jpg", 95); + $usernew->picture = "1"; + } else { + $usernew->picture = $user->picture; + } + + if ($usernew->password) { + $usernew->password = md5($usernew->password); + } else { + unset($usernew->password); + } + + $usernew->timemodified = time(); + + if (update_record("user", $usernew)) { + redirect("index.php", "Changes saved"); + } else { + error("Could not update the user record ($user->id)"); + } + } + } + +/// Otherwise fill and print the form. + + if (!$usernew) { + $usernew = $user; + $usernew->password = ""; + } + + print_header("Edit user profile", "Edit user profile", "wwwroot/admin\">Admin -> Edit user", ""); + + print_simple_box_start("center", "", "$THEME->cellheading"); + echo "

User profile for $usernew->firstname $usernew->lastname

"; + include("user.html"); + print_simple_box_end(); + + print_footer(); + + + + +/// FUNCTIONS //////////////////// + +function find_form_errors(&$user, &$usernew, &$err) { + + if (empty($usernew->email)) + $err["email"] = "Missing email address"; + + else if (! validate_email($usernew->email)) + $err["email"] = "Invalid email address, check carefully"; + + else if ($otheruser = get_record("user", "email", $usernew->email)) { + if ($otheruser->id <> $user->id) { + $err["email"] = "Email address already in use by someone else."; + } + } + $user->email = $usernew->email; + + if (empty($user->password) && empty($usernew->password)) { + $err["password"] = "Must have a password"; + } + + if (empty($usernew->username)) + $err["username"] = "Must have a username"; + + if (empty($usernew->firstname)) + $err["firstname"] = "Must enter your first name"; + + if (empty($usernew->lastname)) + $err["lastname"] = "Must enter your last name"; + + return count($err); +} + + +?> diff --git a/config.php b/config.php new file mode 100644 index 0000000000000..e9dfee8743f1f --- /dev/null +++ b/config.php @@ -0,0 +1,46 @@ +dbtype = "mysql"; // eg mysql, postgres, oracle, access etc +$CFG->dbhost = "localhost"; // eg localhost +$CFG->dbname = "moodle"; // eg moodle +$CFG->dbuser = "moodle"; +$CFG->dbpass = "moodle"; + + +// Next you need to tell Moodle where it is, and where it can save files. + +$CFG->wwwroot = "http://server.dougiamas.net/moodle"; +$CFG->dirroot = "/web/server/moodle"; +$CFG->dataroot = "/web/moodledata"; // Web-server writeable + + +// Choose a theme from the "themes" folder. Default theme is "standard". + +$CFG->theme = "standard"; + + +// Give the full name (eg mail.example.com) of an SMTP server that the +// web server machine has access to (to send mail). Default: "localhost". + +$CFG->smtphost = "dougiamas.com"; + + +// You should not need to change anything below this line +/////////////////////////////////////////////////////////////////////////// + +$CFG->libdir = "$CFG->dirroot/lib"; + +require("$CFG->libdir/setup.php"); // Sets up all libraries, sessions etc + +?> diff --git a/course/edit.html b/course/edit.html new file mode 100644 index 0000000000000..d6c47823b6dff --- /dev/null +++ b/course/edit.html @@ -0,0 +1,72 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Full name:

+ +

Short name:

+ +

Password:

+ +

Summary:

+ +

Category:

categories, "category", "$form->category"); + formerr($err["category"]); + ?> +

Format:

format"); + formerr($err["format"]); + ?> +

Word used to refer
to your role:

+ +

Start date:

days, "startday", "$form->startday"); + choose_from_menu ($form->months, "startmonth", "$form->startmonth"); + choose_from_menu ($form->years, "startyear", "$form->startyear"); + formerr($err["startdate"]); + ?>

End date:

days, "endday", "$form->endday"); + choose_from_menu ($form->months, "endmonth", "$form->endmonth"); + choose_from_menu ($form->years, "endyear", "$form->endyear"); + formerr($err["enddate"]); + ?>
+ +
diff --git a/course/edit.php b/course/edit.php new file mode 100644 index 0000000000000..3f4977f6e1dc7 --- /dev/null +++ b/course/edit.php @@ -0,0 +1,168 @@ +id); + + if (!isteacher($course->id)) { + error("Only teachers can edit the course!"); + } + } else { // Admin is creating a new course + require_login(); + + if (!isadmin()) { + error("Only administrators can use this page"); + } + } + + +/// If data submitted, then process and store. + + if (match_referer() && isset($HTTP_POST_VARS)) { + + $form = (object)$HTTP_POST_VARS; + + $form->startdate = mktime(0,0,0,(int)$form->startmonth,(int)$form->startday,(int)$form->startyear); + $form->enddate = mktime(0,0,0,(int)$form->endmonth,(int)$form->endday,(int)$form->endyear); + + validate_form($course, $form, $err); + + + if (count($err) == 0) { + + $form->timemodified = time(); + + if ($course) { + if (update_record("course", $form)) { + add_to_log("Updated course settings", $course->id); + redirect("view.php?id=$course->id", "Changes saved"); + } else { + error("Serious Error! Could not update the course record! (id = $form->id)"); + } + } else { + if ($newid = insert_record("course", $form)) { // Set up new course + $week->course = $newid; // Create a default week. + $week->week = 0; + $week->timemodified = time(); + $week->id = insert_record("course_weeks", $week); + + add_to_log("Inserted a new course # $newid", $newid); + redirect("$CFG->wwwroot/admin/teacher.php?id=$newid", "Changes saved"); + } else { + error("Serious Error! Could not create the new course!"); + } + } + die; + } else { + foreach ($err as $key => $value) { + $focus = "form.$key"; + } + + } + } + +/// Otherwise fill and print the form. + + if (!$form) { + if ($course) { + $form = $course; + $ts = getdate($course->startdate); + $te = getdate($course->enddate); + } else { + $ts = getdate(time() + 3600 * 24); + $te = getdate(time() + 3600 * 24 * 7 * 16); + } + + $form->startday = $ts[mday]; + $form->startmonth = $ts[mon]; + $form->startyear = $ts[year]; + + $form->endday = $te[mday]; + $form->endmonth = $te[mon]; + $form->endyear = $te[year]; + + if (!$course) { + $form->teacher = "Facilitator"; + $form->fullname = "Course Fullname 101"; + $form->shortname = "CF101"; + $form->summary = "Write a concise and interesting paragraph here that explains what this course is about."; + $form->format = 0; + $form->category = 1; + } + } + + for ($i=1;$i<=31;$i++) { + $form->days[$i] = "$i"; + } + for ($i=1;$i<=12;$i++) { + $form->months[$i] = date("F", mktime(0,0,0,$i,1,2000)); + } + for ($i=2000;$i<=2005;$i++) { + $form->years[$i] = $i; + } + + $form->categories = get_records_sql_menu("SELECT id,name FROM course_categories"); + + //$form->owners = get_records_sql_menu("SELECT u.id, CONCAT(u.firstname, " ", u.lastname) FROM users u, teachers t WHERE t.user = u.id"); + + if (isadmin()) { + print_header("Admin: Creating a new course", "$CFG->sitename: Administration", + "wwwroot/admin/\">Admin + -> Create a new course", $focus); + + } else { + print_header("Edit course settings", "$course->fullname", + "wwwroot/course/view.php?id=$course->id\">$course->shortname + -> Edit course settings", $focus); + } + + print_simple_box_start("center", "", "$THEME->cellheading"); + print_heading("Editing course settings"); + include("edit.html"); + print_simple_box_end(); + + print_footer($course); + + exit; + +/// Functions ///////////////////////////////////////////////////////////////// + +function validate_form($course, &$form, &$err) { + + if (empty($form->fullname)) + $err["fullname"] = "Missing full name"; + + if (empty($form->shortname)) + $err["shortname"] = "Missing short name"; + + if (empty($form->summary)) + $err["summary"] = "Missing summary"; + + if (empty($form->teacher)) + $err["teacher"] = "Missing Teacher/Tutor/Instructor/Facilitator"; + + + if ($form->startdate > $form->enddate) + $err["startdate"] = "Starts after it ends!"; + + if (($form->startdate < time()) && ($course->format <> $form->format)) { + $err["format"] = "Can't change the format now"; + $form->format = $course->format; + } + + if (! $form->category) + $err["category"] = "You need to choose a category"; + + return; +} + + +?> diff --git a/course/editweek.html b/course/editweek.html new file mode 100644 index 0000000000000..5a041e94e0ee4 --- /dev/null +++ b/course/editweek.html @@ -0,0 +1,12 @@ +
+
+

Summary of week week ?>

+ +
(Maximum of 255 characters) +

+ + + +

+
+
diff --git a/course/editweek.php b/course/editweek.php new file mode 100644 index 0000000000000..7684ef1f3f7c0 --- /dev/null +++ b/course/editweek.php @@ -0,0 +1,50 @@ +course)) { + error("Could not find the course!"); + } + + require_login($course->id); + + add_to_log("Edit week", $course->id); + + if (!isteacher($course->id)) { + error("Only teachers can edit this!"); + } + + +/// If data submitted, then process and store. + + if (match_referer() && isset($HTTP_POST_VARS)) { + + $timenow = time(); + + if (! set_field("course_weeks", "summary", $summary, "id", $week->id)) { + error("Could not update the summary!"); + } + + redirect("view.php?id=$course->id"); + exit; + } + +/// Otherwise fill and print the form. + + if (! $form ) { + $form = $week; + } + + print_header("Edit week $week->week", "Edit week $week->week", "", "form.summary"); + + include("editweek.html"); + + print_footer($course); + +?> diff --git a/course/email.html b/course/email.html new file mode 100644 index 0000000000000..919acff06951f --- /dev/null +++ b/course/email.html @@ -0,0 +1,32 @@ +
+ + + + + + + + + + + + +
+

Subject:

+
+ +
+

Email:

+
+ +
+  + +

+ + + +

+
+ +
diff --git a/course/email.php b/course/email.php new file mode 100644 index 0000000000000..c8f220c071cb0 --- /dev/null +++ b/course/email.php @@ -0,0 +1,48 @@ +id); + + if (!isteacher($course->id)) { + error("Only teachers can send mail this way!"); + } + + +/// If data submitted, then process and store. + + if (match_referer() && isset($HTTP_POST_VARS)) { + + $link = "$CFG->wwwroot/course/view.php?id=$course->id"; + + + if (! email_to_course($USER, $course, true, $subject, $message, "$link")) { + error("An error occurred while trying to send mail!"); + } + + add_to_log("Sent mail to everyone", $course->id); + + redirect("view.php?id=$course->id", "Email sent", 1); + exit; + } + + + $form->id = $course->id; + + print_header("$course->shortname: Mail", "$course->fullname", + "wwwroot/course/view.php?id=$course->id\">$course->shortname -> Send mail"); + + print_heading("Send an email to all participants"); + + include("email.html"); + + print_footer($course); + + +?> diff --git a/course/index.php b/course/index.php new file mode 100644 index 0000000000000..21a667956fc85 --- /dev/null +++ b/course/index.php @@ -0,0 +1,25 @@ + $course) { + print_course($course); + echo "
\n"; + } + + } else { + echo "

No courses have been defined yet

"; + } + + print_footer(); + +?> + + diff --git a/course/lib.php b/course/lib.php new file mode 100644 index 0000000000000..cdc52359d58cd --- /dev/null +++ b/course/lib.php @@ -0,0 +1,131 @@ + "Non-weekly layout", + "1" => "Weekly layout" + ); + + +function logdate($date) { + return date("l, j F Y, g:i A", $date); +} + +function print_log_selector_form($course, $selecteduser=0, $selecteddate="today") { + + // Get all the possible users + $users = array(); + if ($students = get_records_sql("SELECT u.* FROM user u, user_students s + WHERE s.course = '$course->id' AND s.user = u.id + ORDER BY u.lastaccess DESC")) { + foreach ($students as $student) { + $users["$student->id"] = "$student->firstname $student->lastname"; + } + } + if ($teachers = get_records_sql("SELECT u.* FROM user u, user_teachers t + WHERE t.course = '$course->id' AND t.user = u.id + ORDER BY u.lastaccess DESC")) { + foreach ($teachers as $teacher) { + $users["$teacher->id"] = "$teacher->firstname $teacher->lastname"; + } + } + + asort($users); + + // Get all the possible dates + $tt = getdate(time()); + $timemidnight = $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]); + $dates = array("$today" => "Today, ".date("j F Y", $today) ); + + while ($timemidnight > $course->startdate) { + $timemidnight = $timemidnight - 86400; + $dates["$timemidnight"] = date("l, j F Y", $timemidnight); + } + + if ($selecteddate == "today") { + $selecteddate = $today; + } + + echo "
"; + echo "
"; + echo "id\">"; + choose_from_menu ($users, "user", $selecteduser, "All participants"); + choose_from_menu ($dates, "date", $selecteddate, "Any day"); + echo ""; + echo "
"; + echo "
"; +} + +function print_log($course, $user=0, $date=0, $order="ORDER BY l.time ASC") { + + $selector = "WHERE l.course='$course->id' AND l.user = u.id"; + + if ($user) { + $selector .= " AND l.user = '$user'"; + } + + if ($date) { + $enddate = $date + 86400; + $selector .= " AND l.time > '$date' AND l.time < '$enddate'"; + } + + if (!$logs = get_records_sql("SELECT l.*, u.firstname, u.lastname, u.picture + FROM logs l, user u $selector $order")){ + notify("No logs found!"); + print_footer($course); + exit; + } + + $count=0; + $tt = getdate(time()); + $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]); + echo "

Displaying ".count($logs)." records

"; + echo ""; + foreach ($logs as $log) { + $count++; + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + } + echo "
".date("l", $log->time)."".date("j M Y, h:i A", $log->time)."$log->firstname $log->lastname"; + $log->message = addslashes($log->message); + link_to_popup_window("$log->url","popup","$log->message", 400, 600); + echo "
"; +} + + +function print_course($course) { + + if (! $site = get_record("course", "category", "0") ) { + error("Could not find a site!"); + } + + print_simple_box_start("CENTER", "80%"); + + echo ""; + echo ""; + echo "
"; + echo "

id\">$course->fullname

"; + if ($teachers = get_records_sql("SELECT u.* FROM user u, user_teachers t + WHERE u.id = t.user AND t.course = '$course->id' + ORDER BY t.authority DESC")) { + + echo "

\n"; + foreach ($teachers as $teacher) { + echo "$course->teacher: id&course=$site->id\">$teacher->firstname $teacher->lastname
"; + } + echo "

"; + } + echo "
"; + echo "

".text_to_html($course->summary)."

"; + echo "
"; + + print_simple_box_end(); +} + +?> diff --git a/course/log.php b/course/log.php new file mode 100644 index 0000000000000..2153f4d21ac3f --- /dev/null +++ b/course/log.php @@ -0,0 +1,65 @@ +id)) { + error("Only teachers can view logs"); + } + + + if (isset($user) || isset($date)) { + + $userinfo = "all users"; + $dateinfo = "any day"; + + if ($user) { + if (!$u = get_record("user", "id", $user) ) { + error("That's an invalid user!"); + } + $userinfo = "$u->firstname $u->lastname"; + } + if ($date) { + $dateinfo = date("l, j F Y", $date); + } + + print_header("$course->shortname: Logs", "$course->shortname : Logs", + "id\">$course->shortname -> + id\">Logs -> Logs for $userinfo, $dateinfo", ""); + + print_heading("Logs for $userinfo, $dateinfo"); + + print_log_selector_form($course, $user, $date); + + print_log($course, $user, $date, "ORDER BY l.time DESC"); + + + } else { + print_header("$course->shortname: Logs", "$course->shortname : Logs", + "id\">$course->shortname -> Logs", ""); + + print_heading("Choose which logs you want to look at"); + + print_log_selector_form($course); + + print_heading("Or see what is happening right now"); + + echo "

"; + link_to_popup_window("/course/loglive.php?id=$course->id","livelog","Live logs", 500, 800); + echo "

"; + + } + + print_footer($course); + + exit; + +?> diff --git a/course/login.html b/course/login.html new file mode 100644 index 0000000000000..47d6083d35a1a --- /dev/null +++ b/course/login.html @@ -0,0 +1,34 @@ +
+ + + + + + + + +
+

This course requires a "course entry key" - a one-time
+ password that you should have got from firstname $teacher->lastname") ?>.

+
+
+ + + + + + + +

Entry Key:

+ + +
  + + + +
+
+ +
+
+ diff --git a/course/login.php b/course/login.php new file mode 100644 index 0000000000000..b0db697111e43 --- /dev/null +++ b/course/login.php @@ -0,0 +1,93 @@ +id, $id); + add_to_log("Enrolled in course", $id); + + $USER->student["$id"] = true; + + if ($SESSION->wantsurl) { + $destination = $SESSION->wantsurl; + unset($SESSION->wantsurl); + } else { + $destination = "$CFG->wwwroot/course/view.php?id=$id"; + } + + redirect($destination); + + } else { + $errormsg = "That entry key was incorrect, please try again". + "
(Here's a hint - it starts with \"".substr($actual_password,0,1)."\")"; + } + } + + if (! $course = get_record("course", "id", $id) ) { + error("That's an invalid course id"); + } + + if (! $site = get_record("course", "category", "0") ) { + error("Could not find a site!"); + } + + if ($course->password == "") { // no password, so enrol + if (! enrol_student_in_course($USER->id, $course->id)) { + error("An error occurred while trying to enrol you."); + } + + add_to_log("Enrolled in course", $id); + + $USER->student["$id"] = true; + + if ($SESSION->wantsurl) { + $destination = $SESSION->wantsurl; + unset($SESSION->wantsurl); + } else { + $destination = "$CFG->wwwroot/course/view.php?id=$id"; + } + + redirect($destination); + } + + $teacher = get_teacher($course->id); + + print_header("Login to $course->shortname", "Login to $course->shortname", "Courses -> Login to $course->shortname", "form.password"); + + print_course($course); + + include("login.html"); + + print_footer(); + + +//// FUNCTIONS ///////////////////////////////////////////// + +function enrol_student_in_course($user, $course) { + + global $db; + + $timenow = time(); + + $rs = $db->Execute("INSERT INTO user_students (user, course, start, end, time) + VALUES ($user, $course, 0, 0, $timenow)"); + if ($rs) { + return true; + } else { + return false; + } +} + +?> diff --git a/course/loginas.php b/course/loginas.php new file mode 100644 index 0000000000000..97024a683c6bd --- /dev/null +++ b/course/loginas.php @@ -0,0 +1,40 @@ +id); + + if (!isteacher($course->id)) { + error("Only teachers can use this page!"); + } + + if (!isstudent($course->id, $user)) { + error("This student is not in your course!"); + } + + // Login as this student and return to course home page. + + $teacher_name = "$USER->firstname $USER->lastname"; + + $USER = get_user_info_from_db("id", $user); + $USER->loggedin = true; + + set_moodle_cookie($USER->username); + + $student_name = "$USER->firstname $USER->lastname"; + + add_to_log("$teacher_name logged in as $student_name", $course->id); + + notice("You are now logged in as $student_name", "$CFG->wwwroot/course/view.php?id=$course->id"); + + die; + +?> diff --git a/course/loglive.php b/course/loglive.php new file mode 100644 index 0000000000000..a3e22ff728c12 --- /dev/null +++ b/course/loglive.php @@ -0,0 +1,29 @@ +id)) { + error("Only teachers can view logs"); + } + + print_header("Activity within the last hour (updates every 60 secs)", + "Activity within the last hour (updates every 60 secs)", + "", "", ""); + + $user=0; + $date=time() - 3600; + + print_log($course, $user, $date, "ORDER BY l.time DESC"); + + exit; + +?> diff --git a/course/mod.php b/course/mod.php new file mode 100644 index 0000000000000..5558853ad50d7 --- /dev/null +++ b/course/mod.php @@ -0,0 +1,423 @@ +course); + + if (!isteacher($mod->course)) { + error("You can't modify this course!"); + } + + $modcode = "../mod/$mod->modulename/mod.php"; + if (file_exists($modcode)) { + include($modcode); + } else { + error("This module is missing important code! (mod.php)"); + } + + switch ($mod->mode) { + case "update": + if (! update_instance($mod)) { + error("Could not update the $mod->modulename"); + } + add_to_log("Updated $mod->modulename $mod->instance", $mod->course); + break; + + case "add": + if (! $mod->instance = add_instance($mod)) { + error("Could not add a new instance of $mod->modulename"); + } + // course_modules and course_weeks each contain a reference + // to each other, so we have to update one of them twice. + + if (! $mod->course_module = add_course_module($mod) ) { + error("Could not add a new course module"); + } + if (! $weekid = add_mod_to_week($mod) ) { + error("Could not add the new course module to that week"); + } + if (! set_field("course_modules", "week", $weekid, "id", $mod->course_module)) { + error("Could not update the course module with the correct week"); + } + add_to_log("Added $mod->modulename $mod->instance", $mod->course); + break; + case "delete": + if (! delete_instance($mod->instance)) { + error("Could not delete the $mod->modulename"); + } + if (! delete_course_module($mod->coursemodule)) { + error("Could not delete the $mod->modulename"); + } + if (! delete_mod_from_week($mod->coursemodule, "$mod->week")) { + error("Could not delete the $mod->modulename from that week"); + } + add_to_log("Deleted $mod->modulename $mod->instance", $mod->course); + break; + default: + error("No mode defined"); + + } + + redirect("view.php?id=$mod->course"); + exit; + } + + + if (isset($move)) { + + require_variable($id); + + move_module($id, $move); + + redirect($HTTP_REFERER); + exit; + + } else if (isset($delete)) { // value = course module + + if (! $cm = get_record("course_modules", "id", $delete)) { + error("This course module doesn't exist"); + } + + if (! $course = get_record("course", "id", $cm->course)) { + error("This course doesn't exist"); + } + + if (! $module = get_record("modules", "id", $cm->module)) { + error("This module doesn't exist"); + } + + require_login($course->id); + + if (!isteacher($course->id)) { + error("You can't modify this course!"); + } + + $form->coursemodule = $cm->id; + $form->week = $cm->week; + $form->course = $cm->course; + $form->instance = $cm->instance; + $form->modulename = $module->name; + + include("mod_delete.html"); + + exit; + + + } else if (isset($update)) { // value = course module + + if (! $cm = get_record("course_modules", "id", $update)) { + error("This course module doesn't exist"); + } + + if (! $course = get_record("course", "id", $cm->course)) { + error("This course doesn't exist"); + } + + if (! $module = get_record("modules", "id", $cm->module)) { + error("This module doesn't exist"); + } + + if (! $form = get_record($module->name, "id", $cm->instance)) { + error("The required instance of this module doesn't exist"); + } + + if (! $cw = get_record("course_weeks", "id", $cm->week)) { + error("This course week doesn't exist"); + } + + $form->week = $cm->week; // The week ID + $form->course = $course->id; + $form->module = $module->id; + $form->modulename = $module->name; + $form->instance = $cm->instance; + $form->mode = "update"; + + $pageheading = "Updating a $module->fullname in Week $cw->week"; + + + } else if (isset($add)) { + + if (!$add) { + redirect($HTTP_REFERER); + die; + } + + require_variable($id); + require_variable($week); + + if (! $course = get_record("course", "id", $id)) { + error("This course doesn't exist"); + } + + if (! $module = get_record("modules", "name", $add)) { + error("This module type doesn't exist"); + } + + $form->week = $week; // The week number itself + $form->course = $course->id; + $form->module = $module->id; + $form->modulename = $module->name; + $form->instance = $cm->instance; + $form->mode = "add"; + + if ($form->week) { + $pageheading = "Adding a new $module->fullname to Week $form->week"; + } else { + $pageheading = "Adding a new $module->fullname"; + } + + } else { + error("No action was specfied"); + } + + require_login($course->id); + + if (!isteacher($course->id)) { + error("You can't modify this course!"); + } + + print_header("$course->shortname: Editing a $module->fullname", "$course->shortname: Editing a $module->fullname", + "wwwroot/course/view.php?id=$course->id\">$course->shortname -> + Editing a $module->fullname", "form.name"); + + $modform = "../mod/$module->name/mod.html"; + + if (file_exists($modform)) { + + print_heading($pageheading); + print_simple_box_start("center", "", "$THEME->cellheading"); + include($modform); + print_simple_box_end(); + + } else { + notice("This module cannot be added to this course yet!", "$CFG->wwwroot/course/view.php?id=$course->id"); + } + + print_footer($course); + + exit; + + +/// FUNCTIONS ////////////////////////////////////////////////////////////////////// + +function add_course_module($mod) { + GLOBAL $db; + + $timenow = time(); + + if (!$rs = $db->Execute("INSERT into course_modules + SET course = '$mod->course', + module = '$mod->module', + instance = '$mod->instance', + week = '$mod->week', + added = '$timenow' ")) { + return 0; + } + + // Get it out again - this is the most compatible way to determine the ID + if ($rs = $db->Execute("SELECT id FROM course_modules + WHERE module = $mod->module AND added = $timenow")) { + return $rs->fields[0]; + } else { + return 0; + } + +} + +function add_mod_to_week($mod) { +// Returns the course_weeks ID where the mod is inserted + GLOBAL $db; + + if ($cw = get_record_sql("SELECT * FROM course_weeks + WHERE course = '$mod->course' AND week = '$mod->week'") ) { + + if ($cw->sequence) { + $newsequence = "$cw->sequence,$mod->course_module"; + } else { + $newsequence = "$mod->course_module"; + } + if (!$rs = $db->Execute("UPDATE course_weeks SET sequence = '$newsequence' WHERE id = '$cw->id'")) { + return 0; + } else { + return $cw->id; // Return course_weeks ID that was used. + } + + } else { // Insert a new record + if (!$rs = $db->Execute("INSERT into course_weeks + SET course = '$mod->course', + week = '$mod->week', + summary = '', + sequence = '$mod->course_module' ")) { + return 0; + } + // Get it out again - this is the most compatible way to determine the ID + if ($rs = $db->Execute("SELECT id FROM course_weeks + WHERE course = '$mod->course' AND week = '$mod->week'")) { + return $rs->fields[0]; + } else { + return 0; + } + } +} + +function delete_course_module($mod) { + return set_field("course_modules", "deleted", 1, "id", $mod); +} + +function delete_mod_from_week($mod, $week) { + GLOBAL $db; + + if ($cw = get_record("course_weeks", "id", "$week") ) { + + $modarray = explode(",", $cw->sequence); + + if ($key = array_keys ($modarray, $mod)) { + array_splice($modarray, $key[0], 1); + $newsequence = implode(",", $modarray); + return set_field("course_weeks", "sequence", $newsequence, "id", $cw->id); + } else { + return false; + } + + } else { + return false; + } +} + + +function move_module($id, $move) { + GLOBAL $db; + + if (!$move) { + return true; + } + + if (! $cm = get_record("course_modules", "id", $id)) { + error("This course module doesn't exist"); + } + + if (! $thisweek = get_record("course_weeks", "id", $cm->week)) { + error("This course week doesn't exist"); + } + + $mods = explode(",", $thisweek->sequence); + + $len = count($mods); + $pos = array_keys($mods, $cm->id); + $thepos = $pos[0]; + + if ($len == 0 || count($pos) == 0 ) { + error("Very strange. Could not find the required module in this week."); + } + + if ($len == 1) { + $first = true; + $last = true; + } else { + $first = ($thepos == 0); + $last = ($thepos == $len - 1); + } + + if ($move < 0) { // Moving the module up + + if ($first) { + if ($thisweek->week == 1) { // First week, do nothing + return true; + } else { // Push onto end of previous week + $prevweeknumber = $thisweek->week - 1; + if (! $prevweek = get_record_sql("SELECT * FROM course_weeks + WHERE course='$thisweek->course' + AND week='$prevweeknumber' ")) { + error("Previous week ($prevweek->id) doesn't exist"); + } + + if ($prevweek->sequence) { + $newsequence = "$prevweek->sequence,$cm->id"; + } else { + $newsequence = "$cm->id"; + } + + if (! set_field("course_weeks", "sequence", $newsequence, "id", $prevweek->id)) { + error("Previous week could not be updated"); + } + + if (! set_field("course_modules", "week", $prevweek->id, "id", $cm->id)) { + error("Module could not be updated"); + } + + array_splice($mods, 0, 1); + $newsequence = implode(",", $mods); + if (! set_field("course_weeks", "sequence", $newsequence, "id", $thisweek->id)) { + error("Module could not be updated"); + } + + return true; + + } + } else { // move up within this week + $swap = $mods[$thepos-1]; + $mods[$thepos-1] = $mods[$thepos]; + $mods[$thepos] = $swap; + + $newsequence = implode(",", $mods); + if (! set_field("course_weeks", "sequence", $newsequence, "id", $thisweek->id)) { + error("This week could not be updated"); + } + return true; + } + + } else { // Moving the module down + + if ($last) { + $nextweeknumber = $thisweek->week + 1; + if ($nextweek = get_record_sql("SELECT * FROM course_weeks + WHERE course='$thisweek->course' + AND week='$nextweeknumber' ")) { + + if ($nextweek->sequence) { + $newsequence = "$cm->id,$nextweek->sequence"; + } else { + $newsequence = "$cm->id"; + } + + if (! set_field("course_weeks", "sequence", $newsequence, "id", $nextweek->id)) { + error("Next week could not be updated"); + } + + if (! set_field("course_modules", "week", $nextweek->id, "id", $cm->id)) { + error("Module could not be updated"); + } + + array_splice($mods, $thepos, 1); + $newsequence = implode(",", $mods); + if (! set_field("course_weeks", "sequence", $newsequence, "id", $thisweek->id)) { + error("This week could not be updated"); + } + return true; + + } else { // There is no next week, so just return + return true; + + } + } else { // move down within this week + $swap = $mods[$thepos+1]; + $mods[$thepos+1] = $mods[$thepos]; + $mods[$thepos] = $swap; + + $newsequence = implode(",", $mods); + if (! set_field("course_weeks", "sequence", $newsequence, "id", $thisweek->id)) { + error("This week could not be updated"); + } + return true; + } + } +} + +?> + + diff --git a/course/mod_delete.html b/course/mod_delete.html new file mode 100644 index 0000000000000..c7ca08aa92685 --- /dev/null +++ b/course/mod_delete.html @@ -0,0 +1,16 @@ +
+ +
+
+modulename ?") ?> + + + + + + + +
+ +
+ diff --git a/course/new.php b/course/new.php new file mode 100644 index 0000000000000..1e6b3cd02b9c8 --- /dev/null +++ b/course/new.php @@ -0,0 +1,44 @@ +lastlogin + + require("../config.php"); + require("lib.php"); + + require_variable($id); // Course ID + + if (! $course = get_record("course", "id", $id)) { + error("Could not find the course!"); + } + + require_login($course->id); + + add_to_log("View Whats New", $course->id); + + print_header("$course->shortname: What's new", "$course->fullname", + "wwwroot/course/view.php?id=$course->id\">$course->shortname -> What's new"); + + print_heading("Recent activity since your last login"); + print_heading(logdate($USER->lastlogin)); + + print_simple_box_start("center"); + $modules = array ("users"); + + $mods = get_records_sql("SELECT * FROM modules"); + + foreach ($mods as $mod) { + $modules[] = "mod/$mod->name"; + } + + foreach ($modules as $module) { + $newfile = "$CFG->dirroot/$module/new.php"; + if (file_exists($newfile)) { + include($newfile); + } + } + + print_simple_box_end(); + print_footer($course); + +?> diff --git a/course/noweeks.php b/course/noweeks.php new file mode 100644 index 0000000000000..d22b675e46027 --- /dev/null +++ b/course/noweeks.php @@ -0,0 +1,158 @@ +"; + echo ""; + echo "
"; + + // Layout the left column + + print_side_block("id\">What's New!", + "", "...since your last login"); + + // Then, print all the news items. + + // XXXXX + + // Admin links and controls + + if ($USER->teacher[$course->id]) { + $admindata[]="id\">Course settings"; + $adminicon[]="\"Course\""; + $admindata[]="id\">Logs"; + $adminicon[]="\"Log\""; + $admindata[]="id\">Send mail"; + $adminicon[]="\"Email\""; + $admindata[]="id\">Files"; + $adminicon[]="\"Files\""; + print_side_block("Administration", $admindata, "", $adminicon); + } + + + // Start main column + echo ""; + + echo "


"; + echo ""; + echo ""; + echo "

"; + if ($USER->teacher[$course->id]) { + if ($USER->editing) { + echo "id&edit=off\">Turn editing off"; + } else { + echo "id&edit=on\">Turn editing on"; + } + } + //if ($USER->help) { + //echo "   id&help=off\">Turn help off"; + //} else { + //echo "   id&help=on\">Turn help on"; + //} + echo "

"; + + echo ""; + + // Forums + echo "
cellheading\">"; + echo "

Forums

"; + + echo ""; + if ($forums = get_all_instances_in_course("forum", $course->id)) { + foreach ($forums as $key => $ff) { + $forum = (object)$ff; + echo "\n"; + } + } + + if ($USER->editing) { + echo ""; + } + echo "
"; + echo "coursemodule\">"; + echo "\"Forum\""; + echo "

"; + echo "coursemodule\">$forum->name"; + if ($USER->editing) { + echo "  coursemodule>Delete + coursemodule>Update"; + } + echo "

 

"; + echo "id&week=0&add=forum\">Add forum..."; + echo "

"; + + + // Readings + echo "
cellheading\">"; + echo "

Readings

"; + + echo ""; + if ($readings = get_all_instances_in_course("reading", $course->id, "m.timemodified DESC")) { + + $count = 0; + foreach ($readings as $key => $rr) { + $reading = (object)$rr; + echo "\n"; + if ($count++ > 5) { + echo ""; + break; + } + } + } + + if ($USER->editing) { + echo ""; + } + echo "
"; + echo "coursemodule\">"; + echo "\"Forum\""; + echo "

"; + echo "coursemodule\">$reading->name"; + if ($USER->editing) { + echo "  coursemodule>Delete + coursemodule>Update"; + } + echo "

 

"; + echo "id\">See all readings..."; + echo "

 

"; + echo "id&week=0&add=reading\">Add reading..."; + echo "

"; + + + // Participants + echo "
cellheading\">"; + echo "

Participants

"; + + echo ""; + echo "\n"; + echo "\n"; + echo "
"; + echo "id\">"; + echo "\"Participants\""; + echo "

"; + echo "id\">List of all participants"; + echo "

"; + echo "id&course=$course->id\">"; + echo "\"Participants\""; + echo "

"; + echo "id&course=$course->id\">My details"; + echo "

"; + + // Then all the links to module types + + echo "
"; + echo ""; + + + echo ""; + + +?> diff --git a/course/user.php b/course/user.php new file mode 100644 index 0000000000000..6029181567096 --- /dev/null +++ b/course/user.php @@ -0,0 +1,46 @@ +id); + + if (!isteacher($course->id)) { + error("Only teachers can look at this page"); + } + + if (! $user = get_record("user", "id", $user)) { + error("User ID is incorrect"); + } + + add_to_log("View total report of $user->firstname $user->lastname", $course->id); + + print_header("$course->shortname: Report", "$course->fullname", + "id\">$course->shortname -> + id\">Participants -> + id&course=$course->id\">$user->firstname $user->lastname -> + Full Report", ""); + + if ($mods = get_records_sql("SELECT * FROM modules ORDER BY fullname")) { + foreach ($mods as $mod) { + $userfile = "$CFG->dirroot/mod/$mod->name/user.php"; + if (file_exists($userfile)) { + echo "

".$mod->fullname."s

"; + echo "
"; + include($userfile); + echo "
"; + echo "
"; + } + } + } + + print_footer($course); + +?> + diff --git a/course/view.php b/course/view.php new file mode 100644 index 0000000000000..3044a7b4c0d9e --- /dev/null +++ b/course/view.php @@ -0,0 +1,102 @@ +category) { // This course is not a real course. + redirect("$CFG->wwwroot"); + } + + add_to_log("View course: $course->shortname", $id); + + if ( $USER->teacher[$course->id] ) { + if ($edit == "on") { + $USER->editing = true; + } else if ($edit == "off") { + $USER->editing = false; + } + } + if ($help == "on") { + $USER->help = true; + } else if ($help == "off") { + $USER->help = false; + } + + print_header("Course: $course->fullname", "$course->fullname", "$course->shortname", ""); + + if (! $modtypes = get_records_sql_menu("SELECT name,fullname FROM modules ORDER BY fullname") ) { + error("No modules are installed!"); + } + + if ( $rawmods = get_records_sql("SELECT cm.*, m.name as modname, m.fullname as modfullname + FROM modules m, course_modules cm + WHERE cm.course = '$course->id' + AND cm.deleted = '0' + AND cm.module = m.id") ) { + + foreach($rawmods as $mod) { // Index the mods + $mods[$mod->id] = $mod; + $modtype[$mod->modname] = $mod->modfullname; + } + } + + switch ($course->format) { + case 0: + include("noweeks.php"); + break; + case 1: + include("weeks.php"); + break; + } + + print_footer($course); + + +/// FUNCTIONS //////// + + +function make_editing_buttons($moduleid) { + return "    + Delete + \"Move + \"Move + Update"; +} + +function print_side_block($heading="", $list=NULL, $footer="", $icons=NULL) { + + echo "\n"; + echo "\n"; + if ($list) { + foreach($list as $key => $string) { + echo "\n\n"; + } + } + if ($footer) { + echo "\n"; + } + echo "

$heading

"; + if ($icons[$key]) { + echo $icons[$key]; + } else { + echo ""; + } + echo ""; + echo "

$string

"; + echo "

$footer


\n\n"; +} + +?> diff --git a/course/weeks.php b/course/weeks.php new file mode 100644 index 0000000000000..85b07c250cf1d --- /dev/null +++ b/course/weeks.php @@ -0,0 +1,184 @@ +id) ) { + $week->course = $course->id; // Create a default week. + $week->week = 0; + $week->id = insert_record("course_weeks", $week); + if (! $rawweeks = get_records("course_weeks", "course", $course->id) ) { + error("Error finding or creating week structures for this course"); + } + } + + foreach($rawweeks as $cw) { //Index the weeks + $weeks[$cw->week] = $cw; + } + + // Layout the whole page as two big columns. + echo ""; + echo "
"; + echo "
"; + + // Layout the left column + + print_side_block("id\">What's New!", + "", "...since your last login"); + + // Then, print all the news items. + + include("../mod/discuss/lib.php"); + if ($news = get_course_news_forum($course->id)) { + print_simple_box_start("CENTER", "100%", "#FFFFFF", 5); + echo "

Latest News
"; + echo ""; + forum_latest_topics($news->id, 5, "minimal", "DESC", false); + echo ""; + print_simple_box_end(); + } + + // Now, print all the course links on the side + + // Then all the links to module types + + $moddata = array(); + $modicon = array(); + + if ($modtype) { + foreach ($modtype as $modname => $modfullname) { + $moddata[] = "id\">".$modfullname."s"; + $modicon[] = "\"$modfullname\""; + } + } + + $moddata[]="id\">Participants"; + $modicon[]="\"Participants\""; + + print_side_block("Activities", $moddata, "", $modicon); + + // Admin links and controls + + $admindata[]="id&course=$course->id\">My details"; + $adminicon[]="\"About"; + + if ($USER->teacher[$course->id]) { + $admindata[]="id\">Course settings"; + $adminicon[]="\"Course\""; + $admindata[]="id\">Logs"; + $adminicon[]="\"Log\""; + $admindata[]="id\">Send mail"; + $adminicon[]="\"Email\""; + $admindata[]="id\">Files"; + $adminicon[]="\"Files\""; + } + print_side_block("Administration", $admindata, "", $adminicon); + + + // Start main column + echo "

"; + + // Now all the weekly modules + + + $timenow = time(); + $weekdate = $course->startdate; // this should be 0:00 Monday of that week + $week = 1; + $weekofseconds = 604800; + + echo "


"; + echo ""; + echo "
"; + echo "Weekly Outline\n"; + + // Global switches + echo "

"; + if ($USER->teacher[$course->id]) { + if ($USER->editing) { + echo "id&edit=off\">Turn editing off"; + } else { + echo "id&edit=on\">Turn editing on"; + } + } + if ($USER->help) { + echo "   id&help=off\">Turn help off"; + } else { + echo "   id&help=on\">Turn help on"; + } + echo "

"; + + echo ""; + while ($weekdate < $course->enddate) { + echo ""; + + $nextweekdate = $weekdate + ($weekofseconds); + $thisweek = (($weekdate <= $timenow) && ($timenow < $nextweekdate)); + + $weekday = date("j F", $weekdate); + $endweekday = date("j F", $weekdate+(6*24*3600)); + + if ($thisweek) { + $highlightcolor = $THEME->cellheading2; + } else { + $highlightcolor = $THEME->cellheading; + } + + echo ""; + + echo ""; + echo ""; + echo ""; + echo ""; + + $week++; + $weekdate = $nextweekdate; + } + echo "
"; + echo "

$week

"; + echo "
cellcontent\">"; + echo "

cellheading2\">$weekday - $endweekday

"; + + if (! $thisweek = $weeks[$week]) { + $thisweek->course = $course->id; // Create a new week structure + $thisweek->week = $week; + $thisweek->summary = ""; + $thisweek->id = insert_record("course_weeks", $thisweek); + } + + if ($USER->editing) { + $thisweek->summary .= " id>\"Edit

"; + } + + echo text_to_html($thisweek->summary); + + echo "

"; + if ($thisweek->sequence) { + + $thisweekmods = explode(",", $thisweek->sequence); + + foreach ($thisweekmods as $modnumber) { + $mod = $mods[$modnumber]; + $instancename = get_field("$mod->modname", "name", "id", "$mod->instance"); + echo "modname/icon.gif\" HEIGHT=16 WIDTH=16 ALT=\"$mod->modfullname\"> modname/view.php?id=$mod->id\">$instancename"; + if ($USER->editing) { + echo make_editing_buttons($mod->id); + } + echo "
\n"; + } + } + echo "

\n"; + + if ($USER->editing) { + echo "
"; + popup_form("$CFG->wwwroot/course/mod.php?id=$course->id&week=$week&add=", + $modtypes, "week$week", "", "Add..."); + echo "
"; + } + + echo "
 
"; + echo "

"; + + + echo ""; + +?> diff --git a/doc/CHANGES b/doc/CHANGES new file mode 100644 index 0000000000000..de3a6549b0967 --- /dev/null +++ b/doc/CHANGES @@ -0,0 +1,8 @@ +0.1 +--- + +Starting to look more complete. +Added news posting and email forwarding of news items. +Changed listing of modules to A,B,C,D + + diff --git a/doc/COPYRIGHT b/doc/COPYRIGHT new file mode 100644 index 0000000000000..74c484d161f09 --- /dev/null +++ b/doc/COPYRIGHT @@ -0,0 +1,20 @@ +Moodle - Modular Object-Oriented Dynamic Learning Environment +http://moodle.com + +Copyright (C) 2000, 2001 Martin Dougiamas martin@dougiamas.com +http://dougiamas.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + diff --git a/doc/INSTALL b/doc/INSTALL new file mode 100644 index 0000000000000..871d481d4e976 --- /dev/null +++ b/doc/INSTALL @@ -0,0 +1,95 @@ +=========================================================================== +== MOODLE +== +== OPEN-SOURCE SOFTWARE FOR INTERNET-BASED EDUCATION +== +== Copyright (c) Martin Dougiamas, 2001 +== +== Freely available under the GNU License +== +=========================================================================== + + +Directory structure +=================== + +config.php - the only file you need to edit to get started + +lib - libraries of core Moodle code + +user - code to display and manage users +course - code to display and manage courses +login - code to handle login and account creation +admin - code to administrate the whole server +pix - Generic site graphics are in here + +mod - All Moodle modules are in here +theme - All Moodle themes are in here + + + +HOW TO INSTALL MOODLE +===================== + +1. SET UP A DATABASE + + Create an empty database (eg "moodle") in your database system + along with a special user (eg "moodle") that has access to that + database. (Don't use the "root" user for the moodle database - + it's a security hazard). + + eg for MySQL under a Unix system: + + # mysql -u root -p + > CREATE DATABASE moodle; + > GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,INDEX,ALTER ON moodle.* + TO moodle@localhost IDENTIFIED BY 'yourpassword'; + > quit + # mysqladmin -p reload + + +2. EDIT config.php + + Edit the configuration file, putting in the database details that you + just defined, as well as changing the site address and so on. + + Make sure you specify what type of database you are using. + + eg: + + $CFG->wwwroot = "http://example.com"; + $CFG->dbtype = "mysqlt"; // eg mysql, mysqlt, postgres ... etc + $CFG->dbhost = "localhost"; // eg localhost + $CFG->dbname = "moodle"; // eg moodle + $CFG->dbuser = "moodle"; + $CFG->dbpass = "yourpassword"; + + +3. GO TO THE ADMIN PAGE + + The admin page should now be working at: http://example.com/admin + + The first time you access this page, Moodle will automagically + create all the tables it needs within your database. + + You will then be asked to create an administration user for + future access to the admin pages.. + + All your further configuration of Moodle can now be done using the + administration web pages, including: + + - creating and deleting courses + - administering teacher accounts + - changing site-wide settings + - adding/deleting modules + + Configuration of each course is done by the teachers of that course. + See the teacher documentation for more information about that. + + Have fun and send me feedback so we can continue improving Moodle! + + +Cheers! + +Martin Dougiamas +martin@moodle.com diff --git a/doc/LICENCE b/doc/LICENCE new file mode 100644 index 0000000000000..5d18622b83b4b --- /dev/null +++ b/doc/LICENCE @@ -0,0 +1,281 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + diff --git a/doc/NOTES b/doc/NOTES new file mode 100644 index 0000000000000..b3731868c9b9d --- /dev/null +++ b/doc/NOTES @@ -0,0 +1,3 @@ +weeks dates start from 0:00 Monday morning + + diff --git a/doc/ROADMAP b/doc/ROADMAP new file mode 100644 index 0000000000000..1096744c5427a --- /dev/null +++ b/doc/ROADMAP @@ -0,0 +1,15 @@ +Roadmap +------- + +Here are some of the directions I would like to take +Moodle now it has been released. + +- Code audit and clean up. Standardise tabs, wordwrap. + Refactor a few bits. + +- Full internationalisation. To start with I've hardcoded + all the strings just to get things started and reduce + obfuscation during development. Modular "language packs" + need to implemented instead. eg using STPhp. + + diff --git a/error/index.php b/error/index.php new file mode 100644 index 0000000000000..5e48d3ff23934 --- /dev/null +++ b/error/index.php @@ -0,0 +1,37 @@ + $requested", "$text"); + + redirect("$CFG->wwwroot/course/", "Message sent, thanks", 3); + die; + } + + print_header("$CFG->sitename:Error", "$CFG->sitename: Error 404", "", "form.text"); + + add_to_log("Error: $HTTP_REFERER -> $REQUEST_URI"); + + print_simple_box("An unusual error occurred (tried to reach a page that doesn't exist).

$REQUEST_URI", "center", "", "$THEME->cellheading"); + +?> + +

+

If you have time, please let us know what you were trying + to do when the error occurred: +

+
+ + + +
+ diff --git a/file.php b/file.php new file mode 100644 index 0000000000000..bd787620d7216 --- /dev/null +++ b/file.php @@ -0,0 +1,43 @@ + 0) { + require_login($courseid); + } + + $pathname = "$CFG->dataroot$PATH_INFO"; + $filename = $args[$numargs-1]; + + $mimetype = mimeinfo("type", $filename); + + if (file_exists($pathname)) { + $lastmodified = filemtime($pathname); + + header("Last-Modified: " . gmdate("D, d M Y H:i:s", $lastmodified) . " GMT"); + header("Expires: " . gmdate("D, d M Y H:i:s", time() + $lifetime) . " GMT"); + header("Cache-control: max_age = $lifetime"); // a day + header("Pragma: "); + header("Content-Length: ".filesize($pathname)); + header("Content-type: $mimetype"); + readfile("$pathname"); + } else { + error("Sorry, but the file you are looking for was not found", "/course/view.php?id=$courseid"); + } + + exit; +?> diff --git a/files/index.php b/files/index.php new file mode 100644 index 0000000000000..03cfc25b4f6e8 --- /dev/null +++ b/files/index.php @@ -0,0 +1,646 @@ +id); + add_to_log("Files area", $course->id); + + if (! isteacher($course->id) ) { + error("Only teachers can edit files"); + } + + function html_footer() { + global $course; + echo ""; + print_footer($course); + } + + function html_header($formfield=""){ + global $course; + + print_header("$course->shortname: Files", "$course->shortname: Files", + "id\">$course->shortname -> Files", $formfield); + echo ""; + echo ""; + echo "\n"; +} + +function displaydir ($wdir) { +// $wdir == / or /a or /a/b/c/d etc + + global $basedir; + global $id; + global $USER; + + $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); + + + echo ""; + echo "
"; + echo "
"; + } + + if (! file_exists($CFG->dataroot)) { + if (! mkdir($CFG->dataroot, 0750)) { + error("You need to create the directory $CFG->dataroot with web server write access"); + } + } + $basedir = "$CFG->dataroot/$course->id"; + + if (! file_exists($basedir)) { + if (! mkdir($basedir, 0750)) { + error("Could not create a directory for this course ($basedir)"); + } + } + $baseweb = $CFG->wwwroot; + +// End of configuration and access control + + + require("mimetypes.php"); + + $regexp="\\.\\."; + if (ereg( $regexp, $file, $regs )| ereg( $regexp, $wdir,$regs )) { + $message = "Error: Directories can not contain \"..\""; + $wdir = "/"; + $action = ""; + } + + + if (!match_referer("$baseweb/files/index.php")) { // To stop spoofing + $action="cancel"; + $wdir="/"; + } + + if (!$wdir) { + $wdir="/"; + } + + + + switch ($action) { + + case "upload": + html_header(); + if ($save) { + if ($userfile == "none" || $userfile_size==0) { + echo "

Error: That was not a valid file."; + } else { + $userfile_name = clean_filename($userfile_name); + if ($userfile_name != "") { + $newfile = "$basedir$wdir/$userfile_name"; + copy ($userfile, $newfile); + chmod ($newfile, 0750); + echo "Uploaded $userfile_name ($userfile_type) to $wdir"; + } + } + displaydir($wdir); + + } else { + echo "

Upload a file into $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 ($confirm) { + html_header(); + 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(); + if (setfilelist($HTTP_POST_VARS)) { + echo "

You are about to delete:

"; + print_simple_box_start("center"); + printfilelist($USER->filelist); + print_simple_box_end(); + echo "
"; + notice_yesno ("Are you sure you want to delete these?", + "index.php?id=$id&wdir=$wdir&action=delete&confirm=1", + "index.php?id=$id&wdir=$wdir&action=cancel"); + } else { + displaydir($wdir); + } + html_footer(); + } + break; + + case "move": + html_header(); + if ($count = setfilelist($HTTP_POST_VARS)) { + $USER->fileop = $action; + $USER->filesource = $wdir; + echo "

$count files selected for moving. Now go to the destination and press \"Move files to here\".

"; + } + displaydir($wdir); + html_footer(); + break; + + case "paste": + html_header(); + if ($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 ($name) { + html_header(); + $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 { + html_header("form.name"); + echo "

Rename $file to:"; + echo "
"; + echo "
"; + echo " "; + echo " "; + echo " "; + echo " "; + echo " "; + echo " "; + echo "
"; + echo "
"; + echo "
"; + echo " "; + echo " "; + echo " "; + echo " "; + echo "
"; + echo "
"; + } + html_footer(); + break; + + case "mkdir": + if ($name) { + html_header(); + $name = clean_filename($name); + if (file_exists($basedir.$wdir."/".$name)) { + echo "Error: $name already exists!"; + } else if (!mkdir($basedir.$wdir."/".$name, 0750)) { + echo "Error: could not create $name"; + } + displaydir($wdir); + + } else { + html_header("form.name"); + echo "

Create folder in $wdir:"; + echo "
"; + echo "
"; + echo " "; + echo " "; + echo " "; + echo " "; + echo " "; + echo "
"; + echo "
"; + echo "
"; + echo " "; + echo " "; + echo " "; + echo " "; + echo "
"; + echo "
"; + } + html_footer(); + break; + + case "edit": + html_header(); + if (isset($text)) { + $fileptr = fopen($basedir.$file,"w"); + fputs($fileptr, stripslashes($text)); + fclose($fileptr); + displaydir($wdir); + + } else { + $fileptr = fopen($basedir.$file, "r"); + $contents = fread($fileptr, filesize($basedir.$file)); + fclose($fileptr); + + echo "

Editing $file:"; + echo "
"; + echo "
"; + echo " "; + echo " "; + echo " "; + echo " "; + echo ""; + echo "
"; + echo " "; + echo ""; + echo ""; + echo "
"; + echo " "; + echo " "; + echo " "; + echo " "; + echo "
"; + echo "
"; + } + html_footer(); + break; + + case "zip": + if ($name) { + html_header(); + $name = clean_filename($name); + $files = ""; + foreach ($USER->filelist as $file) { + $files .= basename($file); + $files .= " "; + } + $command = "cd $basedir/$wdir ; /usr/bin/zip -r $name $files"; + Exec($command); + clearfilelist(); + displaydir($wdir); + + } else { + html_header("form.name"); + if (setfilelist($HTTP_POST_VARS)) { + echo "

You are about create a zip file containing:

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

What do you want to call the zip file?"; + 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(); + if ($file) { + echo "

Unzipping $file:

"; + print_simple_box_start("center"); + echo "
";
+                $file = basename($file);
+                $command = "cd $basedir/$wdir ; /usr/bin/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 "cancel"; + clearfilelist(); + + default: + html_header(); + 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 clean_filename($string) { + $string = eregi_replace("\.\.", "", $string); + $string = eregi_replace("[^([:alnum:]|\.)]", "_", $string); + return eregi_replace("_+", "_", $string); +} + + + +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; + + foreach ($filelist as $file) { + if (is_dir($basedir.$file)) { + echo " $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 " $file
"; + } + } +} + + +function display_size($file) { + $file_size = filesize($file); + if ($file_size >= 1073741824) { + $file_size = round($file_size / 1073741824 * 100) / 100 . "g"; + } else if ($file_size >= 1048576) { + $file_size = round($file_size / 1048576 * 100) / 100 . "m"; + } else if ($file_size >= 1024) { + $file_size = round($file_size / 1024 * 100) / 100 . "k"; + } else { + $file_size = $file_size . "b"; + } + return $file_size; +} + + +function print_cell($alignment="center", $text=" ") { + echo "
"; + echo ""; + echo "$text"; + echo ""; + echo "
"; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo "\n"; + + if ($wdir == "/") { + $wdir = ""; + } else { + $updir = dirname($wdir); + echo ""; + print_cell("center", ""); + print_cell("left", "\"ParentUp to $updir"); + echo "\n"; + } + + + $count = 0; + + if ($dirlist) { + asort($dirlist); + foreach ($dirlist as $dir) { + + $count++; + + $filename = $fullpath."/".$dir; + $fileurl = rawurlencode($wdir."/".$dir); + $filesafe = rawurlencode($dir); + $filedate = date("d-m-Y H:i:s", filectime($filename)); + + echo ""; + + print_cell("center", ""); + print_cell("left", "\"Folder\"".htmlspecialchars($dir).""); + print_cell("right", "-"); + print_cell("right", $filedate); + print_cell("right", "rename"); + + echo ""; + } + } + + + if ($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 = date("d-m-Y H:i:s", filectime($filename)); + + echo ""; + + print_cell("center", ""); + echo ""; + + print_cell("right", display_size($filename)); + print_cell("right", $filedate); + if ($icon == "text.gif" || $icon == "html.gif") { + $edittext = "edit"; + } else if ($icon == "zip.gif") { + $edittext = "unzip"; + } else { + $edittext = ""; + } + print_cell("right", "$edittext rename"); + + echo ""; + } + } + echo "
 

Current folder: $wdir

"; + echo "
NameSizeModifiedAction
"; + link_to_popup_window ("/file.php/$id$fileurl", "display", + "\"File\"", + 480, 640); + echo ""; + link_to_popup_window ("/file.php/$id$fileurl", "display", + htmlspecialchars($file), + 480, 640); + echo "
"; + echo "
"; + + if (!$wdir) { + $wdir = "/"; + } + + echo ""; + echo ""; + echo ""; + echo "
"; + echo ""; + echo " "; + $options = array ( + "move" => "Move to another folder", + "delete" => "Delete completely", + "zip" => "Create zip archive" + ); + if ($count) { + choose_from_menu ($options, "action", "", $nothing="With chosen files...", "javascript:document.dirform.submit()"); + //echo ""; + } + + echo ""; + echo ""; + if (($USER->fileop == "move") && $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/files/mimetypes.php b/files/mimetypes.php new file mode 100644 index 0000000000000..1c0826ae031e5 --- /dev/null +++ b/files/mimetypes.php @@ -0,0 +1,37 @@ + array ("type"=>"document/unknown", "icon"=>"unknown.gif"), + "zip" => array ("type"=>"application/zip", "icon"=>"zip.gif"), + "jpeg" => array ("type"=>"image/jpeg", "icon"=>"image.gif"), + "jpg" => array ("type"=>"image/jpeg", "icon"=>"image.gif"), + "gif" => array ("type"=>"image/gif", "icon"=>"image.gif"), + "png" => array ("type"=>"image/png", "icon"=>"image.gif"), + "bmp" => array ("type"=>"image/bmp", "icon"=>"image.gif"), + "html" => array ("type"=>"text/html", "icon"=>"html.gif"), + "htm" => array ("type"=>"text/html", "icon"=>"html.gif"), + "txt" => array ("type"=>"text/plain", "icon"=>"text.gif"), + "wav" => array ("type"=>"audio/wav", "icon"=>"audio.gif"), + "mp3" => array ("type"=>"audio/mp3", "icon"=>"audio.gif"), + "au" => array ("type"=>"audio/au", "icon"=>"audio.gif"), + "swf" => array ("type"=>"application/x-shockwave-flash", "icon"=>"image.gif"), + "pdf" => array ("type"=>"application/pdf", "icon"=>"pdf.gif"), + "doc" => array ("type"=>"application/msword", "icon"=>"word.gif"), + "xls" => array ("type"=>"application/vnd.ms-excel", "icon"=>"excel.gif") + ); + +function mimeinfo($element, $filename) { + global $mimeinfo; + + if (eregi("\.([a-z0-9]+)$", $filename, $match)) { + $result = $mimeinfo[strtolower($match[1])][$element]; + } + + if ($result) { + return $result; + } else { + return $mimeinfo["xxx"][$element]; // By default + } +} + +?> diff --git a/files/pix/audio.gif b/files/pix/audio.gif new file mode 100755 index 0000000000000..7b4e7cc626c6c Binary files /dev/null and b/files/pix/audio.gif differ diff --git a/files/pix/edit.gif b/files/pix/edit.gif new file mode 100644 index 0000000000000..cd3aaec787422 Binary files /dev/null and b/files/pix/edit.gif differ diff --git a/files/pix/env.gif b/files/pix/env.gif new file mode 100644 index 0000000000000..3f8221390f49f Binary files /dev/null and b/files/pix/env.gif differ diff --git a/files/pix/excel.gif b/files/pix/excel.gif new file mode 100644 index 0000000000000..86c5e13bd12a4 Binary files /dev/null and b/files/pix/excel.gif differ diff --git a/files/pix/explore.gif b/files/pix/explore.gif new file mode 100644 index 0000000000000..a19c9bea17835 Binary files /dev/null and b/files/pix/explore.gif differ diff --git a/files/pix/files.gif b/files/pix/files.gif new file mode 100755 index 0000000000000..fee2f9a24129a Binary files /dev/null and b/files/pix/files.gif differ diff --git a/files/pix/folder.gif b/files/pix/folder.gif new file mode 100755 index 0000000000000..4e183891f2e69 Binary files /dev/null and b/files/pix/folder.gif differ diff --git a/files/pix/help.gif b/files/pix/help.gif new file mode 100644 index 0000000000000..1600716b179d2 Binary files /dev/null and b/files/pix/help.gif differ diff --git a/files/pix/html.gif b/files/pix/html.gif new file mode 100644 index 0000000000000..550df5db8c43d Binary files /dev/null and b/files/pix/html.gif differ diff --git a/files/pix/image.gif b/files/pix/image.gif new file mode 100644 index 0000000000000..deb1347d369f3 Binary files /dev/null and b/files/pix/image.gif differ diff --git a/files/pix/move.gif b/files/pix/move.gif new file mode 100644 index 0000000000000..776d89eb054f5 Binary files /dev/null and b/files/pix/move.gif differ diff --git a/files/pix/parent.gif b/files/pix/parent.gif new file mode 100755 index 0000000000000..885b2f99a891a Binary files /dev/null and b/files/pix/parent.gif differ diff --git a/files/pix/pdf.gif b/files/pix/pdf.gif new file mode 100644 index 0000000000000..fbead2fdb2335 Binary files /dev/null and b/files/pix/pdf.gif differ diff --git a/files/pix/text.gif b/files/pix/text.gif new file mode 100644 index 0000000000000..dc681ddf9c8c6 Binary files /dev/null and b/files/pix/text.gif differ diff --git a/files/pix/unknown.gif b/files/pix/unknown.gif new file mode 100644 index 0000000000000..fd34f3866b06f Binary files /dev/null and b/files/pix/unknown.gif differ diff --git a/files/pix/word.gif b/files/pix/word.gif new file mode 100644 index 0000000000000..ca4fecafa2f6f Binary files /dev/null and b/files/pix/word.gif differ diff --git a/files/pix/zip.gif b/files/pix/zip.gif new file mode 100755 index 0000000000000..854946fa6abf2 Binary files /dev/null and b/files/pix/zip.gif differ diff --git a/index.php b/index.php new file mode 100644 index 0000000000000..a54aa6bd81234 --- /dev/null +++ b/index.php @@ -0,0 +1,50 @@ +wwwroot/admin/"); + } + + print_header("$site->fullname", "$site->fullname", "", ""); + +?> + + + + + + + + +
+ cellheading"); ?> + +
  • Home
  • +
  • Courses
  • + + + +
    + cellheading"); ?> + +
    + + + +
    + summary, $align="", $width="100%", $color="$THEME->cellheading"); ?> +
    + +
    + + +

    + +

    + diff --git a/lib/class.phpmailer.php b/lib/class.phpmailer.php new file mode 100644 index 0000000000000..2a5bc3688aec4 --- /dev/null +++ b/lib/class.phpmailer.php @@ -0,0 +1,983 @@ + +// +// License: LGPL, see LICENSE +//////////////////////////////////////////////////// + +/** + * phpmailer - PHP email transport class + * @author Brent R. Matzelle + */ +class phpmailer +{ + ///////////////////////////////////////////////// + // PUBLIC VARIABLES + ///////////////////////////////////////////////// + + /** + * Email priority (1 = High, 3 = Normal, 5 = low). Default value is 3. + * @public + * @type int + */ + var $Priority = 3; + + /** + * Sets the CharSet of the message. Default value is "iso-8859-1". + * @public + * @type string + */ + var $CharSet = "iso-8859-1"; + + /** + * Sets the Content-type of the message. Default value is "text/plain". + * @public + * @type string + */ + var $ContentType = "text/plain"; + + /** + * Sets the Encoding of the message. Options for this are "8bit" (default), + * "7bit", "binary", "base64", and "quoted-printable". + * @public + * @type string + */ + var $Encoding = "8bit"; + + /** + * Holds the most recent mailer error message. Default value is "". + * @public + * @type string + */ + var $ErrorInfo = ""; + + /** + * Sets the From email of the message. Default value is "root@localhost". + * @public + * @type string + */ + var $From = "root@localhost"; + + /** + * Sets the From name of the message. Default value is "Root User". + * @public + * @type string + */ + var $FromName = "Root User"; + + /** + * Sets the Sender email of the message. If not empty, will be sent via -f to sendmail + * or as 'MAIL FROM' in smtp mode. Default value is "". + * @public + * @type string + */ + var $Sender = ""; + + /** + * Sets the Subject of the message. Default value is "". + * @public + * @type string + */ + var $Subject = ""; + + /** + * Sets the Body of the message. Default value is "". + * @public + * @type string + */ + var $Body = ""; + + /** + * Sets word wrapping on the message. Default value is false (off). + * @public + * @type string + */ + var $WordWrap = false; + + /** + * Method to send mail: ("mail", "sendmail", or "smtp"). + * Default value is "mail". + * @public + * @type string + */ + var $Mailer = "mail"; + + /** + * Sets the path of the sendmail program. Default value is + * "/usr/sbin/sendmail". + * @public + * @type string + */ + var $Sendmail = "/usr/sbin/sendmail"; + + /** + * Turns Microsoft mail client headers on and off. Default value is false (off). + * @public + * @type bool + */ + var $UseMSMailHeaders = false; + + /** + * Holds phpmailer version. + * @public + * @type string + */ + var $Version = "phpmailer [version 1.25]"; + + + ///////////////////////////////////////////////// + // SMTP VARIABLES + ///////////////////////////////////////////////// + + /** + * Sets the SMTP host. Default value is "localhost". + * @public + * @type string + */ + var $Host = "localhost"; + + /** + * Sets the SMTP server port. Default value is 25. + * @public + * @type int + */ + var $Port = 25; + + /** + * Sets the CharSet of the message. Default value is "localhost.localdomain". + * @public + * @type string + */ + var $Helo = "localhost.localdomain"; + + /** + * Sets the SMTP server timeout. Default value is 10. + * @public + * @type int + */ + var $Timeout = 10; // Socket timeout in sec. + + /** + * Sets SMTP class debugging on or off. Default value is false (off). + * @public + * @type bool + */ + var $SMTPDebug = false; + + + ///////////////////////////////////////////////// + // PRIVATE VARIABLES + ///////////////////////////////////////////////// + + /** + * Holds all "To" addresses. + * @type array + */ + var $to = array(); + + /** + * Holds all "CC" addresses. + * @type array + */ + var $cc = array(); + + /** + * Holds all "BCC" addresses. + * @type array + */ + var $bcc = array(); + + /** + * Holds all "Reply-To" addresses. + * @type array + */ + var $ReplyTo = array(); + + /** + * Holds all attachments. + * @type array + */ + var $attachment = array(); + + /** + * Holds all custom headers. + * @type array + */ + var $CustomHeader = array(); + + /** + * Holds the message boundary. Default is false. + * @type string + */ + var $boundary = false; + + ///////////////////////////////////////////////// + // VARIABLE METHODS + ///////////////////////////////////////////////// + + /** + * Sets message type to HTML. Returns void. + * @public + * @returns void + */ + function IsHTML($bool) { + if($bool == true) + $this->ContentType = "text/html"; + else + $this->ContentType = "text/plain"; + } + + /** + * Sets Mailer to use SMTP. Returns void. + * @public + * @returns void + */ + function IsSMTP() { + $this->Mailer = "smtp"; + } + + /** + * Sets Mailer to use PHP mail() function. Returns void. + * @public + * @returns void + */ + function IsMail() { + $this->Mailer = "mail"; + } + + /** + * Sets Mailer to use $Sendmail program. Returns void. + * @public + * @returns void + */ + function IsSendmail() { + $this->Mailer = "sendmail"; + } + + /** + * Sets Mailer to use qmail MTA. Returns void. + * @public + * @returns void + */ + function IsQmail() { + //$this->Sendmail = "/var/qmail/bin/qmail-inject"; + $this->Sendmail = "/var/qmail/bin/sendmail"; + $this->Mailer = "sendmail"; + } + + + ///////////////////////////////////////////////// + // RECIPIENT METHODS + ///////////////////////////////////////////////// + + /** + * Adds a "to" address. Returns void. + * @public + * @returns void + */ + function AddAddress($address, $name = "") { + $cur = count($this->to); + $this->to[$cur][0] = trim($address); + $this->to[$cur][1] = $name; + } + + /** + * Adds a "Cc" address. Returns void. + * @public + * @returns void + */ + function AddCC($address, $name = "") { + $cur = count($this->cc); + $this->cc[$cur][0] = trim($address); + $this->cc[$cur][1] = $name; + } + + /** + * Adds a "Bcc" address. Note: this function works + * with the SMTP mailer on win32, not with the "mail" + * mailer. This is a PHP bug that has been submitted + * on the Zend web site. The UNIX version of PHP + * functions correctly. + * Returns void. + * @public + * @returns void + */ + function AddBCC($address, $name = "") { + $cur = count($this->bcc); + $this->bcc[$cur][0] = trim($address); + $this->bcc[$cur][1] = $name; + } + + /** + * Adds a "Reply-to" address. Returns void. + * @public + * @returns void + */ + function AddReplyTo($address, $name = "") { + $cur = count($this->ReplyTo); + $this->ReplyTo[$cur][0] = trim($address); + $this->ReplyTo[$cur][1] = $name; + } + + + ///////////////////////////////////////////////// + // MAIL SENDING METHODS + ///////////////////////////////////////////////// + + /** + * Creates message and assigns Mailer. If the message is + * not sent successfully then it returns false. Returns bool. + * @public + * @returns bool + */ + function Send() { + if(count($this->to) < 1) + { + $this->error_handler("You must provide at least one recipient email address"); + return false; + } + + $header = $this->create_header(); + if(!$body = $this->create_body()) + return false; + + // Choose the mailer + if($this->Mailer == "sendmail") + { + if(!$this->sendmail_send($header, $body)) + return false; + } + elseif($this->Mailer == "mail") + { + if(!$this->mail_send($header, $body)) + return false; + } + elseif($this->Mailer == "smtp") + { + if(!$this->smtp_send($header, $body)) + return false; + } + else + { + $this->error_handler(sprintf("%s mailer is not supported", $this->Mailer)); + return false; + } + + return true; + } + + /** + * Sends mail using the $Sendmail program. Returns bool. + * @private + * @returns bool + */ + function sendmail_send($header, $body) { + if ($this->Sender != "") + $sendmail = sprintf("%s -f %s -t", $this->Sendmail, $this->Sender); + else + $sendmail = sprintf("%s -t", $this->Sendmail); + + if(!@$mail = popen($sendmail, "w")) + { + $this->error_handler(sprintf("Could not execute %s", $this->Sendmail)); + return false; + } + + fputs($mail, $header); + fputs($mail, $body); + pclose($mail); + + return true; + } + + /** + * Sends mail using the PHP mail() function. Returns bool. + * @private + * @returns bool + */ + function mail_send($header, $body) { + //$to = substr($this->addr_append("To", $this->to), 4, -2); + + // Cannot add Bcc's to the $to + $to = $this->to[0][0]; // no extra comma + for($i = 1; $i < count($this->to); $i++) + $to .= sprintf(",%s", $this->to[$i][0]); + + if ($this->Sender != "" && PHP_VERSION >= "4.0") + { + $old_from = ini_get("sendmail_from"); + ini_set("sendmail_from", $this->Sender); + } + + if ($this->Sender != "" && PHP_VERSION >= "4.0.5") + { + // The fifth parameter to mail is only available in PHP >= 4.0.5 + $params = sprintf("-f %s", $this->Sender); + $rt = @mail($to, $this->Subject, $body, $header, $params); + } + else + { + $rt = @mail($to, $this->Subject, $body, $header); + } + + if (isset($old_from)) + ini_set("sendmail_from", $old_from); + + if(!$rt) + { + $this->error_handler("Could not instantiate mail()"); + return false; + } + + return true; + } + + /** + * Sends mail via SMTP using PhpSMTP (Author: + * Chris Ryan). Returns bool. + * @private + * @returns bool + */ + function smtp_send($header, $body) { + // Include SMTP class code, but not twice + //include_once("class.smtp.php"); // Load code only if asked + + $smtp = new SMTP; + $smtp->do_debug = $this->SMTPDebug; + + // Try to connect to all SMTP servers + $hosts = explode(";", $this->Host); + $index = 0; + $connection = false; + + // Retry while there is no connection + while($index < count($hosts) && $connection == false) + { + if($smtp->Connect($hosts[$index], $this->Port, $this->Timeout)) + $connection = true; + //printf("%s host could not connect
    ", $hosts[$index]); //debug only + $index++; + } + if(!$connection) + { + $this->error_handler("SMTP Error: could not connect to SMTP host server(s)"); + return false; + } + + $smtp->Hello($this->Helo); + if ($this->Sender == "") + $smtp->Mail(sprintf("<%s>", $this->From)); + else + $smtp->Mail(sprintf("<%s>", $this->Sender)); + + for($i = 0; $i < count($this->to); $i++) + $smtp->Recipient(sprintf("<%s>", $this->to[$i][0])); + for($i = 0; $i < count($this->cc); $i++) + $smtp->Recipient(sprintf("<%s>", $this->cc[$i][0])); + for($i = 0; $i < count($this->bcc); $i++) + $smtp->Recipient(sprintf("<%s>", $this->bcc[$i][0])); + + if(!$smtp->Data(sprintf("%s%s", $header, $body))) + { + $this->error_handler("SMTP Error: Data not accepted"); + return false; + } + $smtp->Quit(); + + return true; + } + + + ///////////////////////////////////////////////// + // MESSAGE CREATION METHODS + ///////////////////////////////////////////////// + + /** + * Creates recipient headers. Returns string. + * @private + * @returns string + */ + function addr_append($type, $addr) { + $addr_str = ""; + $addr_str .= sprintf("%s: %s <%s>", $type, $addr[0][1], $addr[0][0]); + if(count($addr) > 1) + { + for($i = 1; $i < count($addr); $i++) + { + $addr_str .= sprintf(", %s <%s>", $addr[$i][1], $addr[$i][0]); + } + $addr_str .= "\r\n"; + } + else + $addr_str .= "\r\n"; + + return($addr_str); + } + + /** + * Wraps message for use with mailers that don't + * automatically perform wrapping and for quoted-printable. + * Original written by philippe. Returns string. + * @private + * @returns string + */ + function wordwrap($message, $length, $qp_mode = false) { + if ($qp_mode) + $soft_break = " =\r\n"; + else + $soft_break = "\r\n"; + + $message = $this->fix_eol($message); + if (substr($message, -1) == "\r\n") + $message = substr($message, 0, -2); + + $line = explode("\r\n", $message); + $message = ""; + for ($i=0 ;$i < count($line); $i++) + { + $line_part = explode(" ", trim($line[$i])); + $buf = ""; + for ($e = 0; $e $length)) + { + $space_left = $length - strlen($buf) - 1; + if ($e != 0) + { + if ($space_left > 20) + { + $len = $space_left; + if (substr($word, $len - 1, 1) == "=") + $len--; + elseif (substr($word, $len - 2, 1) == "=") + $len -= 2; + $part = substr($word, 0, $len); + $word = substr($word, $len); + $buf .= " " . $part; + $message .= $buf . "=\r\n"; + } + else + { + $message .= $buf . $soft_break; + } + $buf = ""; + } + while (strlen($word) > 0) + { + $len = $length; + if (substr($word, $len - 1, 1) == "=") + $len--; + elseif (substr($word, $len - 2, 1) == "=") + $len -= 2; + $part = substr($word, 0, $len); + $word = substr($word, $len); + + if (strlen($word) > 0) + $message .= $part . "=\r\n"; + else + $buf = $part; + } + } + else + { + $buf_o = $buf; + if ($e == 0) + $buf .= $word; + else + $buf .= " " . $word; + if (strlen($buf) > $length and $buf_o != "") + { + $message .= $buf_o . $soft_break; + $buf = $word; + } + } + } + $message .= $buf . "\r\n"; + } + + return ($message); + } + + /** + * Assembles message header. Returns a string if successful + * or false if unsuccessful. + * @private + * @returns string + */ + function create_header() { + $header = array(); + $header[] = sprintf("Date: %s\r\n", $this->rfc_date()); + + // To be created automatically by mail() + if($this->Mailer != "mail") + $header[] = $this->addr_append("To", $this->to); + + $header[] = sprintf("From: %s <%s>\r\n", $this->FromName, trim($this->From)); + if(count($this->cc) > 0) + $header[] = $this->addr_append("Cc", $this->cc); + + // sendmail and mail() extract Bcc from the header before sending + if((($this->Mailer == "sendmail") || ($this->Mailer == "mail")) && (count($this->bcc) > 0)) + $header[] = $this->addr_append("Bcc", $this->bcc); + + if(count($this->ReplyTo) > 0) + $header[] = $this->addr_append("Reply-to", $this->ReplyTo); + + // mail() sets the subject itself + if($this->Mailer != "mail") + $header[] = sprintf("Subject: %s\r\n", trim($this->Subject)); + + $header[] = sprintf("X-Priority: %d\r\n", $this->Priority); + $header[] = sprintf("X-Mailer: %s\r\n", $this->Version); + $header[] = sprintf("Return-Path: %s\r\n", trim($this->From)); + + // Add custom headers + for($index = 0; $index < count($this->CustomHeader); $index++) + $header[] = sprintf("%s\r\n", $this->CustomHeader[$index]); + + if($this->UseMSMailHeaders) + $header[] = $this->AddMSMailHeaders(); + + // Add all attachments + if(count($this->attachment) > 0) + { + // Set message boundary + $this->boundary = "_b" . md5(uniqid(time())); + + $header[] = sprintf("Content-Type: Multipart/Mixed; charset = \"%s\";\r\n", $this->CharSet); + $header[] = sprintf(" boundary=\"Boundary-=%s\"\r\n", $this->boundary); + } + else + { + $header[] = sprintf("Content-Transfer-Encoding: %s\r\n", $this->Encoding); + $header[] = sprintf("Content-Type: %s; charset = \"%s\";\r\n", $this->ContentType, $this->CharSet); + } + + $header[] = "MIME-Version: 1.0\r\n"; + + return(join("", $header)); + } + + /** + * Assembles the message body. Returns a string if successful + * or false if unsuccessful. + * @private + * @returns string + */ + function create_body() { + // wordwrap the message body if set + if($this->WordWrap) + $this->Body = $this->wordwrap($this->Body, $this->WordWrap); + + $this->Body = $this->encode_string($this->Body, $this->Encoding); + + if(count($this->attachment) > 0) + { + if(!$body = $this->attach_all()) + return false; + } + else + $body = $this->Body; + + return($body); + } + + + ///////////////////////////////////////////////// + // ATTACHMENT METHODS + ///////////////////////////////////////////////// + + /** + * Checks if attachment is valid and then adds + * the attachment to the list. + * Returns false if the file was not found. + * @public + * @returns bool + */ + function AddAttachment($path, $name = "", $encoding = "base64", $type = "application/octet-stream") { + if(!@is_file($path)) + { + $this->error_handler(sprintf("Could not find %s file on filesystem", $path)); + return false; + } + + $filename = basename($path); + if($name == "") + $name = $filename; + + // Append to $attachment array + $cur = count($this->attachment); + $this->attachment[$cur][0] = $path; + $this->attachment[$cur][1] = $filename; + $this->attachment[$cur][2] = $name; + $this->attachment[$cur][3] = $encoding; + $this->attachment[$cur][4] = $type; + + return true; + } + + /** + * Attaches text and binary attachments to body. Returns a + * string if successful or false if unsuccessful. + * @private + * @returns string + */ + function attach_all() { + // Return text of body + $mime = array(); + $mime[] = "This is a MIME message. If you are reading this text, you\r\n"; + $mime[] = "might want to consider changing to a mail reader that\r\n"; + $mime[] = "understands how to properly display MIME multipart messages.\r\n\r\n"; + $mime[] = sprintf("--Boundary-=%s\r\n", $this->boundary); + $mime[] = sprintf("Content-Type: %s; charset = \"%s\";\r\n", $this->ContentType, $this->CharSet); + $mime[] = sprintf("Content-Transfer-Encoding: %s\r\n\r\n", $this->Encoding); + $mime[] = sprintf("%s\r\n", $this->Body); + + // Add all attachments + for($i = 0; $i < count($this->attachment); $i++) + { + $path = $this->attachment[$i][0]; + $filename = $this->attachment[$i][1]; + $name = $this->attachment[$i][2]; + $encoding = $this->attachment[$i][3]; + $type = $this->attachment[$i][4]; + $mime[] = sprintf("--Boundary-=%s\r\n", $this->boundary); + $mime[] = sprintf("Content-Type: %s;\r\n", $type); + $mime[] = sprintf("name=\"%s\"\r\n", $name); + $mime[] = sprintf("Content-Transfer-Encoding: %s\r\n", $encoding); + $mime[] = sprintf("Content-Disposition: attachment; filename=\"%s\"\r\n\r\n", $name); + if(!$mime[] = sprintf("%s\r\n\r\n", $this->encode_file($path, $encoding))) + return false; + } + $mime[] = sprintf("\r\n--Boundary-=%s--\r\n", $this->boundary); + + return(join("", $mime)); + } + + /** + * Encodes attachment in requested format. Returns a + * string if successful or false if unsuccessful. + * @private + * @returns string + */ + function encode_file ($path, $encoding = "base64") { + if(!@$fd = fopen($path, "r")) + { + $this->error_handler(sprintf("File Error: Could not open file %s", $path)); + return false; + } + $file = fread($fd, filesize($path)); + $encoded = $this->encode_string($file, $encoding); + fclose($fd); + + return($encoded); + } + + /** + * Encodes string to requested format. Returns a + * string if successful or false if unsuccessful. + * @private + * @returns string + */ + function encode_string ($str, $encoding = "base64") { + switch(strtolower($encoding)) { + case "base64": + // chunk_split is found in PHP >= 3.0.6 + $encoded = chunk_split(base64_encode($str)); + break; + + case "7bit": + case "8bit": + $encoded = $this->fix_eol($str); + if (substr($encoded, -2) != "\r\n") + $encoded .= "\r\n"; + break; + + case "binary": + $encoded = $str; + break; + + case "quoted-printable": + $encoded = $this->encode_qp($str); + break; + + default: + $this->error_handler(sprintf("Unknown encoding: %s", $encoding)); + return false; + } + return($encoded); + } + + /** + * Encode string to quoted-printable. Returns a string. + * @private + * @returns string + */ + function encode_qp ($str) { + $encoded = $this->fix_eol($str); + if (substr($encoded, -2) != "\r\n") + $encoded .= "\r\n"; + + // Replace every high ascii, control and = characters + $encoded = preg_replace("/([\001-\010\013\014\016-\037\075\177-\377])/e", "'='.sprintf('%02X', ord('\\1'))", $encoded); + // Replace every spaces and tabs when it's the last character on a line + $encoded = preg_replace("/([\011\040])\r\n/e", "'='.sprintf('%02X', ord('\\1')).'\r\n'", $encoded); + + // Maximum line length of 76 characters before CRLF (74 + space + '=') + $encoded = $this->WordWrap($encoded, 74, true); + + return $encoded; + } + + ///////////////////////////////////////////////// + // MESSAGE RESET METHODS + ///////////////////////////////////////////////// + + /** + * Clears all recipients assigned in the TO array. Returns void. + * @public + * @returns void + */ + function ClearAddresses() { + $this->to = array(); + } + + /** + * Clears all recipients assigned in the CC array. Returns void. + * @public + * @returns void + */ + function ClearCCs() { + $this->cc = array(); + } + + /** + * Clears all recipients assigned in the BCC array. Returns void. + * @public + * @returns void + */ + function ClearBCCs() { + $this->bcc = array(); + } + + /** + * Clears all recipients assigned in the ReplyTo array. Returns void. + * @public + * @returns void + */ + function ClearReplyTos() { + $this->ReplyTo = array(); + } + + /** + * Clears all recipients assigned in the TO, CC and BCC + * array. Returns void. + * @public + * @returns void + */ + function ClearAllRecipients() { + $this->to = array(); + $this->cc = array(); + $this->bcc = array(); + } + + /** + * Clears all previously set attachments. Returns void. + * @public + * @returns void + */ + function ClearAttachments() { + $this->attachment = array(); + } + + /** + * Clears all custom headers. Returns void. + * @public + * @returns void + */ + function ClearCustomHeaders() { + $this->CustomHeader = array(); + } + + + ///////////////////////////////////////////////// + // MISCELLANEOUS METHODS + ///////////////////////////////////////////////// + + /** + * Adds the error message to the error container. + * Returns void. + * @private + * @returns void + */ + function error_handler($msg) { + $this->ErrorInfo = $msg; + } + + /** + * Returns the proper RFC 822 formatted date. Returns string. + * @private + * @returns string + */ + function rfc_date() { + $tz = date("Z"); + $tzs = ($tz < 0) ? "-" : "+"; + $tz = abs($tz); + $tz = $tz/36 + $tz % 3600; + $date = sprintf("%s %s%04d", date("D, j M Y H:i:s"), $tzs, $tz); + return $date; + } + + /** + * Changes every end of line from CR or LF to CRLF. Returns string. + * @private + * @returns string + */ + function fix_eol($str) { + $str = str_replace("\r\n", "\n", $str); + $str = str_replace("\r", "\n", $str); + $str = str_replace("\n", "\r\n", $str); + return $str; + } + + /** + * Adds a custom header. Returns void. + * @public + * @returns void + */ + function AddCustomHeader($custom_header) { + $this->CustomHeader[] = $custom_header; + } + + /** + * Adds all the Microsoft message headers. Returns string. + * @private + * @returns string + */ + function AddMSMailHeaders() { + $MSHeader = ""; + if($this->Priority == 1) + $MSPriority = "High"; + elseif($this->Priority == 5) + $MSPriority = "Low"; + else + $MSPriority = "Medium"; + + $MSHeader .= sprintf("X-MSMail-Priority: %s\r\n", $MSPriority); + $MSHeader .= sprintf("Importance: %s\r\n", $MSPriority); + + return($MSHeader); + } + +} +// End of class +?> diff --git a/lib/class.smtp.php b/lib/class.smtp.php new file mode 100644 index 0000000000000..a0ce998ac6c56 --- /dev/null +++ b/lib/class.smtp.php @@ -0,0 +1,939 @@ + + * Created: 03/26/2001 + * + * TODO: + * - Move all the duplicate code to a utility function + * Most of the functions have the first lines of + * code do the same processing. If this can be moved + * into a utility function then it would reduce the + * overall size of the code significantly. + */ + + /* + * STMP is rfc 821 compliant and implements all the rfc 821 SMTP + * commands except TURN which will always return a not implemented + * error. SMTP also provides some utility methods for sending mail + * to an SMTP server. + */ + class SMTP { + var $SMTP_PORT = 25; # the default SMTP PORT + var $CRLF = "\r\n"; # CRLF pair + + var $smtp_conn; # the socket to the server + var $error; # error if any on the last call + var $helo_rply; # the reply the server sent to us for HELO + + var $do_debug; # the level of debug to perform + + /* + * SMTP() + * + * Initialize the class so that the data is in a known state. + */ + function SMTP() { + $this->smtp_conn = 0; + $this->error = null; + $this->helo_rply = null; + + $this->do_debug = 0; + } + + /************************************************************ + * CONNECTION FUNCTIONS * + ***********************************************************/ + + /* + * Connect($host, $port=0, $tval=30) + * + * Connect to the server specified on the port specified. + * If the port is not specified use the default SMTP_PORT. + * If tval is specified then a connection will try and be + * established with the server for that number of seconds. + * If tval is not specified the default is 30 seconds to + * try on the connection. + * + * SMTP CODE SUCCESS: 220 + * SMTP CODE FAILURE: 421 + */ + function Connect($host,$port=0,$tval=30) { + # set the error val to null so there is no confusion + $this->error = null; + + # make sure we are __not__ connected + if($this->connected()) { + # ok we are connected! what should we do? + # for now we will just give an error saying we + # are already connected + $this->error = + array("error" => "Already connected to a server"); + return false; + } + + if(empty($port)) { + $port = $this->SMTP_PORT; + } + + #connect to the smtp server + $this->smtp_conn = fsockopen($host, # the host of the server + $port, # the port to use + $errno, # error number if any + $errstr, # error message if any + $tval); # give up after ? secs + # verify we connected properly + if(empty($this->smtp_conn)) { + $this->error = array("error" => "Failed to connect to server", + "errno" => $errno, + "errstr" => $errstr); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": $errstr ($errno)" . $this->CRLF; + } + return false; + } + + # sometimes the SMTP server takes a little longer to respond + # so we will give it a longer timeout for the first read + //if(function_exists("socket_set_timeout")) + // socket_set_timeout($this->smtp_conn, 1, 0); + + # get any announcement stuff + $announce = $this->get_lines(); + + # set the timeout of any socket functions at 1/10 of a second + //if(function_exists("socket_set_timeout")) + // socket_set_timeout($this->smtp_conn, 0, 100000); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $announce; + } + + return true; + } + + /* + * Connected() + * + * Returns true if connected to a server otherwise false + */ + function Connected() { + if(!empty($this->smtp_conn)) { + $sock_status = socket_get_status($this->smtp_conn); + if($sock_status["eof"]) { + # hmm this is an odd situation... the socket is + # valid but we aren't connected anymore + if($this->do_debug >= 1) { + echo "SMTP -> NOTICE:" . $this->CRLF . + "EOF caught while checking if connected"; + } + $this->Close(); + return false; + } + return true; # everything looks good + } + return false; + } + + /* + * Close() + * + * Closes the socket and cleans up the state of the class. + * It is not considered good to use this function without + * first trying to use QUIT. + */ + function Close() { + $this->error = null; # so there is no confusion + $this->helo_rply = null; + if(!empty($this->smtp_conn)) { + # close the connection and cleanup + fclose($this->smtp_conn); + $this->smtp_conn = 0; + } + } + + + /************************************************************** + * SMTP COMMANDS * + *************************************************************/ + + /* + * Data($msg_data) + * + * Issues a data command and sends the msg_data to the server + * finializing the mail transaction. $msg_data is the message + * that is to be send with the headers. Each header needs to be + * on a single line followed by a with the message headers + * and the message body being seperated by and additional . + * + * Implements rfc 821: DATA + * + * SMTP CODE INTERMEDIATE: 354 + * [data] + * . + * SMTP CODE SUCCESS: 250 + * SMTP CODE FAILURE: 552,554,451,452 + * SMTP CODE FAILURE: 451,554 + * SMTP CODE ERROR : 500,501,503,421 + */ + function Data($msg_data) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Data() without being connected"); + return false; + } + + fputs($this->smtp_conn,"DATA" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 354) { + $this->error = + array("error" => "DATA command not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + + # the server is ready to accept data! + # according to rfc 821 we should not send more than 1000 + # including the CRLF + # characters on a single line so we will break the data up + # into lines by \r and/or \n then if needed we will break + # each of those into smaller lines to fit within the limit. + # in addition we will be looking for lines that start with + # a period '.' and append and additional period '.' to that + # line. NOTE: this does not count towards are limit. + + # normalize the line breaks so we know the explode works + $msg_data = str_replace("\r\n","\n",$msg_data); + $msg_data = str_replace("\r","\n",$msg_data); + $lines = explode("\n",$msg_data); + + # we need to find a good way to determine is headers are + # in the msg_data or if it is a straight msg body + # currently I'm assuming rfc 822 definitions of msg headers + # and if the first field of the first line (':' sperated) + # does not contain a space then it _should_ be a header + # and we can process all lines before a blank "" line as + # headers. + $field = substr($lines[0],0,strpos($lines[0],":")); + $in_headers = false; + if(!empty($field) && !strstr($field," ")) { + $in_headers = true; + } + + $max_line_length = 998; # used below; set here for ease in change + + while(list(,$line) = @each($lines)) { + $lines_out = null; + if($line == "" && $in_headers) { + $in_headers = false; + } + # ok we need to break this line up into several + # smaller lines + while(strlen($line) > $max_line_length) { + $pos = strrpos(substr($line,0,$max_line_length)," "); + $lines_out[] = substr($line,0,$pos); + $line = substr($line,$pos + 1); + # if we are processing headers we need to + # add a LWSP-char to the front of the new line + # rfc 822 on long msg headers + if($in_headers) { + $line = "\t" . $line; + } + } + $lines_out[] = $line; + + # now send the lines to the server + while(list(,$line_out) = @each($lines_out)) { + if($line_out[0] == ".") { + $line_out = "." . $line_out; + } + fputs($this->smtp_conn,$line_out . $this->CRLF); + } + } + + # ok all the message data has been sent so lets get this + # over with aleady + fputs($this->smtp_conn, $this->CRLF . "." . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "DATA not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return true; + } + + /* + * Expand($name) + * + * Expand takes the name and asks the server to list all the + * people who are members of the _list_. Expand will return + * back and array of the result or false if an error occurs. + * Each value in the array returned has the format of: + * [ ] + * The definition of is defined in rfc 821 + * + * Implements rfc 821: EXPN + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE FAILURE: 550 + * SMTP CODE ERROR : 500,501,502,504,421 + */ + function Expand($name) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Expand() without being connected"); + return false; + } + + fputs($this->smtp_conn,"EXPN " . $name . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "EXPN not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + + # parse the reply and place in our array to return to user + $entries = explode($this->CRLF,$rply); + while(list(,$l) = @each($entries)) { + $list[] = substr($l,4); + } + + return $rval; + } + + /* + * Hello($host="") + * + * Sends the HELO command to the smtp server. + * This makes sure that we and the server are in + * the same known state. + * + * Implements from rfc 821: HELO + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE ERROR : 500, 501, 504, 421 + */ + function Hello($host="") { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Hello() without being connected"); + return false; + } + + # if a hostname for the HELO wasn't specified determine + # a suitable one to send + if(empty($host)) { + # we need to determine some sort of appopiate default + # to send to the server + $host = "localhost"; + } + + fputs($this->smtp_conn,"HELO " . $host . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "HELO not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + + $this->helo_rply = $rply; + + return true; + } + + /* + * Help($keyword="") + * + * Gets help information on the keyword specified. If the keyword + * is not specified then returns generic help, ussually contianing + * A list of keywords that help is available on. This function + * returns the results back to the user. It is up to the user to + * handle the returned data. If an error occurs then false is + * returned with $this->error set appropiately. + * + * Implements rfc 821: HELP [ ] + * + * SMTP CODE SUCCESS: 211,214 + * SMTP CODE ERROR : 500,501,502,504,421 + * + function Help($keyword="") { + $this->error = null; # to avoid confusion + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Help() without being connected"); + return false; + } + + $extra = ""; + if(!empty($keyword)) { + $extra = " " . $keyword; + } + + fputs($this->smtp_conn,"HELP" . $extra . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 211 && $code != 214) { + $this->error = + array("error" => "HELP not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + + return $rply; + } + + /* + * Mail($from) + * + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more Recipient + * commands may be called followed by a Data command. + * + * Implements rfc 821: MAIL FROM: + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE SUCCESS: 552,451,452 + * SMTP CODE SUCCESS: 500,501,421 + */ + function Mail($from) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Mail() without being connected"); + return false; + } + + fputs($this->smtp_conn,"MAIL FROM:" . $from . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "MAIL not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return true; + } + + /* + * Noop() + * + * Sends the command NOOP to the SMTP server. + * + * Implements from rfc 821: NOOP + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE ERROR : 500, 421 + */ + function Noop() { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Noop() without being connected"); + return false; + } + + fputs($this->smtp_conn,"NOOP" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "NOOP not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return true; + } + + /* + * Quit($close_on_error=true) + * + * Sends the quit command to the server and then closes the socket + * if there is no error or the $close_on_error argument is true. + * + * Implements from rfc 821: QUIT + * + * SMTP CODE SUCCESS: 221 + * SMTP CODE ERROR : 500 + */ + function Quit($close_on_error=true) { + $this->error = null; # so there is no confusion + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Quit() without being connected"); + return false; + } + + # send the quit command to the server + fputs($this->smtp_conn,"quit" . $this->CRLF); + + # get any good-bye messages + $byemsg = $this->get_lines(); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $byemsg; + } + + $rval = true; + $e = null; + + $code = substr($byemsg,0,3); + if($code != 221) { + # use e as a tmp var cause Close will overwrite $this->error + $e = array("error" => "SMTP server rejected quit command", + "smtp_code" => $code, + "smtp_rply" => substr($byemsg,4)); + $rval = false; + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $e["error"] . ": " . + $byemsg . $this->CRLF; + } + } + + if(empty($e) || $close_on_error) { + $this->Close(); + } + + return $rval; + } + + /* + * Recipient($to) + * + * Sends the command RCPT to the SMTP server with the TO: argument of $to. + * Returns true if the recipient was accepted false if it was rejected. + * + * Implements from rfc 821: RCPT TO: + * + * SMTP CODE SUCCESS: 250,251 + * SMTP CODE FAILURE: 550,551,552,553,450,451,452 + * SMTP CODE ERROR : 500,501,503,421 + */ + function Recipient($to) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Recipient() without being connected"); + return false; + } + + fputs($this->smtp_conn,"RCPT TO:" . $to . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250 && $code != 251) { + $this->error = + array("error" => "RCPT not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return true; + } + + /* + * Reset() + * + * Sends the RSET command to abort and transaction that is + * currently in progress. Returns true if successful false + * otherwise. + * + * Implements rfc 821: RSET + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE ERROR : 500,501,504,421 + */ + function Reset() { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Reset() without being connected"); + return false; + } + + fputs($this->smtp_conn,"RSET" . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "RSET failed", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + + return true; + } + + /* + * Send($from) + * + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more Recipient + * commands may be called followed by a Data command. This command + * will send the message to the users terminal if they are logged + * in. + * + * Implements rfc 821: SEND FROM: + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE SUCCESS: 552,451,452 + * SMTP CODE SUCCESS: 500,501,502,421 + */ + function Send($from) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Send() without being connected"); + return false; + } + + fputs($this->smtp_conn,"SEND FROM:" . $from . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "SEND not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return true; + } + + /* + * SendAndMail($from) + * + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more Recipient + * commands may be called followed by a Data command. This command + * will send the message to the users terminal if they are logged + * in and send them an email. + * + * Implements rfc 821: SAML FROM: + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE SUCCESS: 552,451,452 + * SMTP CODE SUCCESS: 500,501,502,421 + */ + function SendAndMail($from) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called SendAndMail() without being connected"); + return false; + } + + fputs($this->smtp_conn,"SAML FROM:" . $from . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "SAML not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return true; + } + + /* + * SendOrMail($from) + * + * Starts a mail transaction from the email address specified in + * $from. Returns true if successful or false otherwise. If True + * the mail transaction is started and then one or more Recipient + * commands may be called followed by a Data command. This command + * will send the message to the users terminal if they are logged + * in or mail it to them if they are not. + * + * Implements rfc 821: SOML FROM: + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE SUCCESS: 552,451,452 + * SMTP CODE SUCCESS: 500,501,502,421 + */ + function SendOrMail($from) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called SendOrMail() without being connected"); + return false; + } + + fputs($this->smtp_conn,"SOML FROM:" . $from . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250) { + $this->error = + array("error" => "SOML not accepted from server", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return true; + } + + /* + * Turn() + * + * This is an optional command for SMTP that this class does not + * support. This method is here to make the RFC821 Definition + * complete for this class and __may__ be implimented in the future + * + * Implements from rfc 821: TURN + * + * SMTP CODE SUCCESS: 250 + * SMTP CODE FAILURE: 502 + * SMTP CODE ERROR : 500, 503 + */ + function Turn() { + $this->error = array("error" => "This method, TURN, of the SMTP ". + "is not implemented"); + if($this->do_debug >= 1) { + echo "SMTP -> NOTICE: " . $this->error["error"] . $this->CRLF; + } + return false; + } + + /* + * Verify($name) + * + * Verifies that the name is recognized by the server. + * Returns false if the name could not be verified otherwise + * the response from the server is returned. + * + * Implements rfc 821: VRFY + * + * SMTP CODE SUCCESS: 250,251 + * SMTP CODE FAILURE: 550,551,553 + * SMTP CODE ERROR : 500,501,502,421 + */ + function Verify($name) { + $this->error = null; # so no confusion is caused + + if(!$this->connected()) { + $this->error = array( + "error" => "Called Verify() without being connected"); + return false; + } + + fputs($this->smtp_conn,"VRFY " . $name . $this->CRLF); + + $rply = $this->get_lines(); + $code = substr($rply,0,3); + + if($this->do_debug >= 2) { + echo "SMTP -> FROM SERVER:" . $this->CRLF . $rply; + } + + if($code != 250 && $code != 251) { + $this->error = + array("error" => "VRFY failed on name '$name'", + "smtp_code" => $code, + "smtp_msg" => substr($rply,4)); + if($this->do_debug >= 1) { + echo "SMTP -> ERROR: " . $this->error["error"] . + ": " . $rply . $this->CRLF; + } + return false; + } + return $rply; + } + + /****************************************************************** + * INTERNAL FUNCTIONS * + ******************************************************************/ + + /* + * get_lines() + * + * __internal_use_only__: read in as many lines as possible + * either before eof or socket timeout occurs on the operation. + * With SMTP we can tell if we have more lines to read if the + * 4th character is '-' symbol. If it is a space then we don't + * need to read anything else. + */ + function get_lines() { + $data = ""; + while($str = fgets($this->smtp_conn,515)) { + if($this->do_debug >= 4) { + echo "SMTP -> get_lines(): \$data was \"$data\"" . + $this->CRLF; + echo "SMTP -> get_lines(): \$str is \"$str\"" . + $this->CRLF; + } + $data .= $str; + if($this->do_debug >= 4) { + echo "SMTP -> get_lines(): \$data is \"$data\"" . $this->CRLF; + } + # if the 4th character is a space then we are done reading + # so just break the loop + if(substr($str,3,1) == " ") { break; } + } + return $data; + } + + } + + + ?> diff --git a/lib/fonts/arial.ttf b/lib/fonts/arial.ttf new file mode 100755 index 0000000000000..7e28e414bb237 Binary files /dev/null and b/lib/fonts/arial.ttf differ diff --git a/lib/fonts/vixar.ttf b/lib/fonts/vixar.ttf new file mode 100755 index 0000000000000..7e690c571a76b Binary files /dev/null and b/lib/fonts/vixar.ttf differ diff --git a/lib/graphlib.php b/lib/graphlib.php new file mode 100644 index 0000000000000..8cfbee93f97aa --- /dev/null +++ b/lib/graphlib.php @@ -0,0 +1,1704 @@ + 320, // default width of image + 'height' => 240, // default height of image + 'file_name' => 'none', // name of file for file to be saved as. + // NOTE: no suffix required. this is determined from output_format below. + 'output_format' => 'PNG', // image output format. 'GIF', 'PNG', 'JPEG'. default 'PNG'. + + 'seconds_to_live' => 0, // expiry time in seconds (for HTTP header) + 'hours_to_live' => 0, // expiry time in hours (for HTTP header) + 'path_to_fonts' => 'fonts/', // path to fonts folder. don't forget *trailing* slash!! + // for WINDOZE this may need to be the full path, not relative. + + 'title' => 'Graph Title', // text for graph title + 'title_font' => 'arial.ttf', // title text font. don't forget to set 'path_to_fonts' above. + 'title_size' => 16, // title text point size + 'title_colour' => 'black', // colour for title text + + 'x_label' => '', // if this is set then this text is printed on bottom axis of graph. + 'y_label_left' => '', // if this is set then this text is printed on left axis of graph. + 'y_label_right' => '', // if this is set then this text is printed on right axis of graph. + + 'label_size' => 12, // label text point size + 'label_font' => 'arial.ttf', // label text font. don't forget to set 'path_to_fonts' above. + 'label_colour' => 'gray33', // label text colour + 'y_label_angle' => 90, // rotation of y axis label + + 'x_label_angle' => 90, // rotation of y axis label + + 'outer_padding' => 8, // padding around outer text. i.e. title, y label, and x label. + 'inner_padding' => 6, // padding beteen axis text and graph. + 'outer_border' => 'none', // colour of border aound image, or 'none'. + 'inner_border' => 'black', // colour of border around actual graph, or 'none'. + 'inner_border_type' => 'box', // 'box' for all four sides, 'axis' for x/y axis only, + // 'y' or 'y-left' for y axis only, 'y-right' for right y axis only, + // 'x' for x axis only, 'u' for both left and right y axis and x axis. + 'outer_background' => 'none', // background colour of entire image. + 'inner_background' => 'none', // background colour of plot area. + + 'y_min_left' => 0, // this will be reset to minimum value if there is a value lower than this. + 'y_max_left' => 0, // this will be reset to maximum value if there is a value higher than this. + 'y_min_right' => 0, // this will be reset to minimum value if there is a value lower than this. + 'y_max_right' => 0, // this will be reset to maximum value if there is a value higher than this. + 'x_min' => 0, // only used if x axis is numeric. + 'x_max' => 0, // only used if x axis is numeric. + + 'y_resolution_left' => 1, // scaling for rounding of y axis max value. + // if max y value is 8645 then + // if y_resolution is 0, then y_max becomes 9000. + // if y_resolution is 1, then y_max becomes 8700. + // if y_resolution is 2, then y_max becomes 8650. + // if y_resolution is 3, then y_max becomes 8645. + // get it? + 'y_decimal_left' => 0, // number of decimal places for y_axis text. + 'y_resolution_right' => 2, // ... same for right hand side + 'y_decimal_right' => 0, // ... same for right hand side + 'x_resolution' => 2, // only used if x axis is numeric. + 'x_decimal' => 0, // only used if x axis is numeric. + + 'point_size' => 4, // default point size. use even number for diamond or triangle to get nice look. + 'brush_size' => 4, // default brush size for brush line. + 'brush_type' => 'circle', // type of brush to use to draw line. choose from the following + // 'circle', 'square', 'horizontal', 'vertical', 'slash', 'backslash' + 'bar_size' => 0.8, // size of bar to draw. <1 bars won't touch + // 1 is full width - i.e. bars will touch. + // >1 means bars will overlap. + 'bar_spacing' => 10, // space in pixels between group of bars for each x value. + 'shadow_offset' => 3, // draw shadow at this offset, unless overidden by data parameter. + 'shadow' => 'grayCC', // 'none' or colour of shadow. + 'shadow_below_axis' => true, // whether to draw shadows of bars and areas below the x/zero axis. + + + 'x_axis_gridlines' => 'auto', // if set to a number then x axis is treated as numeric. + 'y_axis_gridlines' => 6, // number of gridlines on y axis. + 'zero_axis' => 'none', // colour to draw zero-axis, or 'none'. + + + 'axis_font' => 'arial.ttf', // axis text font. don't forget to set 'path_to_fonts' above. + 'axis_size' => 12, // axis text font size in points + 'axis_colour' => 'gray33', // colour of axis text. + 'y_axis_angle' => 0, // rotation of axis text. + 'x_axis_angle' => 90, // rotation of axis text. + + 'y_axis_text_left' => 1, // whether to print left hand y axis text. if 0 no text, if 1 all ticks have text, + 'x_axis_text' => 1, // if 4 then print every 4th tick and text, etc... + 'y_axis_text_right' => 0, // behaviour same as above for right hand y axis. + + 'x_offset' => 0.5, // x axis tick offset from y axis as fraction of tick spacing. + 'y_ticks_colour' => 'black', // colour to draw y ticks, or 'none'. + 'x_ticks_colour' => 'black', // colour to draw x ticks, or 'none'. + 'y_grid' => 'line', // grid lines. set to 'line' or 'dash'... + 'x_grid' => 'line', // or if set to 'none' print nothing. + 'grid_colour' => 'grayEE', // default grid colour. + 'tick_length' => 4, // length of ticks in pixels. can be negative. i.e. outside data drawing area. + + 'legend' => 'none', // default. no legend. + // otherwise: 'top-left', 'top-right', 'bottom-left', 'bottom-right', + // 'outside-top', 'outside-bottom', 'outside-left', or 'outside-right'. + 'legend_offset' => 10, // offset in pixels from graph or outside border. + 'legend_padding' => 5, // padding around legend text. + 'legend_font' => 'arial.ttf', // legend text font. don't forget to set 'path_to_fonts' above. + 'legend_size' => 9, // legend text point size. + 'legend_colour' => 'black', // legend text colour. + 'legend_border' => 'none', // legend border colour, or 'none'. + + 'decimal_point' => '.', // symbol for decimal separation '.' or ',' *european support. + 'thousand_sep' => ',', // symbol for thousand separation ',' or '' + + ); + + + +// init all text - title, labels, and axis text. +function init() { + global $CFG; + $this->parameter['path_to_fonts'] = $CFG->dirroot."/lib/fonts/"; // Added for Moodle + + $this->calculated['outer_border'] = $this->calculated['boundary_box']; + + // outer padding + $this->calculated['boundary_box']['left'] += $this->parameter['outer_padding']; + $this->calculated['boundary_box']['top'] += $this->parameter['outer_padding']; + $this->calculated['boundary_box']['right'] -= $this->parameter['outer_padding']; + $this->calculated['boundary_box']['bottom'] -= $this->parameter['outer_padding']; + + $this->init_x_axis(); + $this->init_y_axis(); + $this->init_legend(); + $this->init_labels(); + + // take into account tick lengths + $this->calculated['bottom_inner_padding'] = $this->parameter['inner_padding']; + if (($this->parameter['x_ticks_colour'] != 'none') && ($this->parameter['tick_length'] < 0)) + $this->calculated['bottom_inner_padding'] -= $this->parameter['tick_length']; + $this->calculated['boundary_box']['bottom'] -= $this->calculated['bottom_inner_padding']; + + $this->calculated['left_inner_padding'] = $this->parameter['inner_padding']; + if ($this->parameter['y_axis_text_left']) { + if (($this->parameter['y_ticks_colour'] != 'none') && ($this->parameter['tick_length'] < 0)) + $this->calculated['left_inner_padding'] -= $this->parameter['tick_length']; + } + $this->calculated['boundary_box']['left'] += $this->calculated['left_inner_padding']; + + $this->calculated['right_inner_padding'] = $this->parameter['inner_padding']; + if ($this->parameter['y_axis_text_right']) { + if (($this->parameter['y_ticks_colour'] != 'none') && ($this->parameter['tick_length'] < 0)) + $this->calculated['right_inner_padding'] -= $this->parameter['tick_length']; + } + $this->calculated['boundary_box']['right'] -= $this->calculated['right_inner_padding']; + + // boundaryBox now has coords for plotting area. + $this->calculated['inner_border'] = $this->calculated['boundary_box']; + + $this->init_data(); + $this->init_x_ticks(); + $this->init_y_ticks(); +} + + +function draw_text() { + $colour = $this->parameter['outer_background']; + if ($colour != 'none') $this->draw_rectangle($this->calculated['outer_border'], $colour, 'fill'); // graph background + + // draw border around image + $colour = $this->parameter['outer_border']; + if ($colour != 'none') $this->draw_rectangle($this->calculated['outer_border'], $colour, 'box'); // graph border + + $this->draw_title(); + $this->draw_x_label(); + $this->draw_y_label_left(); + $this->draw_y_label_right(); + $this->draw_x_axis(); + $this->draw_y_axis(); + if ($this->calculated['y_axis_left']['has_data']) $this->draw_zero_axis_left(); // either draw zero axis on left + else if ($this->calculated['y_axis_right']['has_data']) $this->draw_zero_axis_right(); // ... or right. + $this->draw_legend(); + + // draw border around plot area + $colour = $this->parameter['inner_background']; + if ($colour != 'none') $this->draw_rectangle($this->calculated['inner_border'], $colour, 'fill'); // graph background + + // draw border around image + $colour = $this->parameter['inner_border']; + if ($colour != 'none') $this->draw_rectangle($this->calculated['inner_border'], $colour, $this->parameter['inner_border_type']); // graph border +} + +function draw_stack() { + $this->init(); + $this->draw_text(); + + $yOrder = $this->y_order; // save y_order data. + // iterate over each data set. order is very important if you want to see data correctly. remember shadows!! + foreach ($yOrder as $set) { + $this->y_order = array($set); + $this->init_data(); + $this->draw_data(); + } + $this->y_order = $yOrder; // revert y_order data. + + $this->output(); +} + +function draw() { + $this->init(); + $this->draw_text(); + $this->draw_data(); + $this->output(); +} + +// draw a data set +function draw_set($order, $set, $offset) { + if ($offset) @$this->init_variable($colour, $this->y_format[$set]['shadow'], $this->parameter['shadow']); + else $colour = $this->y_format[$set]['colour']; + @$this->init_variable($point, $this->y_format[$set]['point'], 'none'); + @$this->init_variable($pointSize, $this->y_format[$set]['point_size'], $this->parameter['point_size']); + @$this->init_variable($line, $this->y_format[$set]['line'], 'none'); + @$this->init_variable($brushType, $this->y_format[$set]['brush_type'], $this->parameter['brush_type']); + @$this->init_variable($brushSize, $this->y_format[$set]['brush_size'], $this->parameter['brush_size']); + @$this->init_variable($bar, $this->y_format[$set]['bar'], 'none'); + @$this->init_variable($barSize, $this->y_format[$set]['bar_size'], $this->parameter['bar_size']); + @$this->init_variable($area, $this->y_format[$set]['area'], 'none'); + + $lastX = 0; + $lastY = 'none'; + $fromX = 0; + $fromY = 'none'; + + //print "set $set
    "; + //expand_pre($this->calculated['y_plot']); + + foreach ($this->x_data as $index => $x) { + //print "index $index
    "; + $thisY = $this->calculated['y_plot'][$set][$index]; + $thisX = $this->calculated['x_plot'][$index]; + + //print "$thisX, $thisY
    "; + + if (($bar!='none') && (string)$thisY != 'none') { + // Moodle addition - next 5 lines and last parameter yoffset + if ($relatedset = $this->offset_relation[$set]) { + $yoffset = $this->calculated['y_plot'][$relatedset][$index]; + } else { + $yoffset = 0; + } + $this->bar($thisX, $thisY, $bar, $barSize, $colour, $offset, $set, $yoffset); + } + + if (($area!='none') && (((string)$lastY != 'none') && ((string)$thisY != 'none'))) + $this->area($lastX, $lastY, $thisX, $thisY, $area, $colour, $offset); + + if (($point!='none') && (string)$thisY != 'none') $this->plot($thisX, $thisY, $point, $pointSize, $colour, $offset); + + if (($line!='none') && ((string)$thisY != 'none')) { + if ((string)$fromY != 'none') + $this->line($fromX, $fromY, $thisX, $thisY, $line, $brushType, $brushSize, $colour, $offset); + + $fromY = $thisY; // start next line from here + $fromX = $thisX; // ... + } else { + $fromY = 'none'; + $fromX = 'none'; + } + + $lastX = $thisX; + $lastY = $thisY; + } +} + +function draw_data() { + // cycle thru y data to be plotted + // first check for drop shadows... + foreach ($this->y_order as $order => $set) { + @$this->init_variable($offset, $this->y_format[$set]['shadow_offset'], $this->parameter['shadow_offset']); + @$this->init_variable($colour, $this->y_format[$set]['shadow'], $this->parameter['shadow']); + if ($colour != 'none') $this->draw_set($order, $set, $offset); + + } + + // then draw data + foreach ($this->y_order as $order => $set) { + $this->draw_set($order, $set, 0); + } +} + +function draw_legend() { + $position = $this->parameter['legend']; + if ($position == 'none') return; // abort if no border + + $borderColour = $this->parameter['legend_border']; + $offset = $this->parameter['legend_offset']; + $padding = $this->parameter['legend_padding']; + $height = $this->calculated['legend']['boundary_box_all']['height']; + $width = $this->calculated['legend']['boundary_box_all']['width']; + $graphTop = $this->calculated['boundary_box']['top']; + $graphBottom = $this->calculated['boundary_box']['bottom']; + $graphLeft = $this->calculated['boundary_box']['left']; + $graphRight = $this->calculated['boundary_box']['right']; + $outsideRight = $this->calculated['outer_border']['right']; + $outsideBottom = $this->calculated['outer_border']['bottom']; + switch ($position) { + case 'top-left': + $top = $graphTop + $offset; + $bottom = $graphTop + $height + $offset; + $left = $graphLeft + $offset; + $right = $graphLeft + $width + $offset; + + break; + case 'top-right': + $top = $graphTop + $offset; + $bottom = $graphTop + $height + $offset; + $left = $graphRight - $width - $offset; + $right = $graphRight - $offset; + + break; + case 'bottom-left': + $top = $graphBottom - $height - $offset; + $bottom = $graphBottom - $offset; + $left = $graphLeft + $offset; + $right = $graphLeft + $width + $offset; + + break; + case 'bottom-right': + $top = $graphBottom - $height - $offset; + $bottom = $graphBottom - $offset; + $left = $graphRight - $width - $offset; + $right = $graphRight - $offset; + break; + + case 'outside-top' : + $top = $graphTop; + $bottom = $graphTop + $height; + $left = $outsideRight - $width - $offset; + $right = $outsideRight - $offset; + break; + + case 'outside-bottom' : + $top = $graphBottom - $height; + $bottom = $graphBottom; + $left = $outsideRight - $width - $offset; + $right = $outsideRight - $offset; + break; + + case 'outside-left' : + $top = $outsideBottom - $height - $offset; + $bottom = $outsideBottom - $offset; + $left = $graphLeft; + $right = $graphLeft + $width; + break; + + case 'outside-right' : + $top = $outsideBottom - $height - $offset; + $bottom = $outsideBottom - $offset; + $left = $graphRight - $width; + $right = $graphRight; + break; + default: // default is top left. no particular reason. + $top = $this->calculated['boundary_box']['top']; + $bottom = $this->calculated['boundary_box']['top'] + $this->calculated['legend']['boundary_box_all']['height']; + $left = $this->calculated['boundary_box']['left']; + $right = $this->calculated['boundary_box']['right'] + $this->calculated['legend']['boundary_box_all']['width']; + +} + // legend border + if($borderColour!='none') $this->draw_rectangle(array('top' => $top, + 'left' => $left, + 'bottom' => $bottom, + 'right' => $right), $this->parameter['legend_border'], 'box'); + + // legend text + $legendText = array('points' => $this->parameter['legend_size'], + 'angle' => 0, + 'font' => $this->parameter['legend_font'], + 'colour' => $this->parameter['legend_colour']); + + $box = $this->calculated['legend']['boundary_box_max']['height']; // use max height for legend square size. + $x = $left + $padding; + $x_text = $x + $box * 2; + $y = $top + $padding; + + foreach ($this->y_order as $set) { + $legendText['text'] = $this->calculated['legend']['text'][$set]; + if ($legendText['text'] != 'none') { + // if text exists then draw box and text + $boxColour = $this->colour[$this->y_format[$set]['colour']]; + + // draw box + ImageFilledRectangle($this->image, $x, $y, $x + $box, $y + $box, $boxColour); + + // draw text + $coords = array('x' => $x + $box * 2, 'y' => $y, 'reference' => 'top-left'); + $legendText['boundary_box'] = $this->calculated['legend']['boundary_box'][$set]; + $this->update_boundaryBox($legendText['boundary_box'], $coords); + $this->print_TTF($legendText); + $y += $padding + $box; + } + } + +} + +function draw_y_label_right() { + if (!$this->parameter['y_label_right']) return; + $x = $this->calculated['boundary_box']['right'] + $this->parameter['inner_padding']; + if ($this->parameter['y_axis_text_right']) $x += $this->calculated['y_axis_right']['boundary_box_max']['width'] + + $this->calculated['right_inner_padding']; + $y = ($this->calculated['boundary_box']['bottom'] + $this->calculated['boundary_box']['top']) / 2; + + $label = $this->calculated['y_label_right']; + $coords = array('x' => $x, 'y' => $y, 'reference' => 'left-center'); + $this->update_boundaryBox($label['boundary_box'], $coords); + $this->print_TTF($label); +} + + +function draw_y_label_left() { + if (!$this->parameter['y_label_left']) return; + $x = $this->calculated['boundary_box']['left'] - $this->parameter['inner_padding']; + if ($this->parameter['y_axis_text_left']) $x -= $this->calculated['y_axis_left']['boundary_box_max']['width'] + + $this->calculated['left_inner_padding']; + $y = ($this->calculated['boundary_box']['bottom'] + $this->calculated['boundary_box']['top']) / 2; + + $label = $this->calculated['y_label_left']; + $coords = array('x' => $x, 'y' => $y, 'reference' => 'right-center'); + $this->update_boundaryBox($label['boundary_box'], $coords); + $this->print_TTF($label); +} + +function draw_title() { + if (!$this->parameter['title']) return; + //$y = $this->calculated['outside_border']['top'] + $this->parameter['outer_padding']; + $y = $this->calculated['boundary_box']['top'] - $this->parameter['outer_padding']; + $x = ($this->calculated['boundary_box']['right'] + $this->calculated['boundary_box']['left']) / 2; + $label = $this->calculated['title']; + $coords = array('x' => $x, 'y' => $y, 'reference' => 'bottom-center'); + $this->update_boundaryBox($label['boundary_box'], $coords); + $this->print_TTF($label); +} + +function draw_x_label() { + if (!$this->parameter['x_label']) return; + $y = $this->calculated['boundary_box']['bottom'] + $this->parameter['inner_padding']; + if ($this->parameter['x_axis_text']) $y += $this->calculated['x_axis']['boundary_box_max']['height'] + + $this->calculated['bottom_inner_padding']; + $x = ($this->calculated['boundary_box']['right'] + $this->calculated['boundary_box']['left']) / 2; + $label = $this->calculated['x_label']; + $coords = array('x' => $x, 'y' => $y, 'reference' => 'top-center'); + $this->update_boundaryBox($label['boundary_box'], $coords); + $this->print_TTF($label); +} + +function draw_zero_axis_left() { + $colour = $this->parameter['zero_axis']; + if ($colour == 'none') return; + // draw zero axis on left hand side + $this->calculated['zero_axis'] = round($this->calculated['boundary_box']['top'] + ($this->calculated['y_axis_left']['max'] * $this->calculated['y_axis_left']['factor'])); + ImageLine($this->image, $this->calculated['boundary_box']['left'], $this->calculated['zero_axis'], $this->calculated['boundary_box']['right'], $this->calculated['zero_axis'], $this->colour[$colour]); +} + +function draw_zero_axis_right() { + $colour = $this->parameter['zero_axis']; + if ($colour == 'none') return; + // draw zero axis on right hand side + $this->calculated['zero_axis'] = round($this->calculated['boundary_box']['top'] + ($this->calculated['y_axis_right']['max'] * $this->calculated['y_axis_right']['factor'])); + ImageLine($this->image, $this->calculated['boundary_box']['left'], $this->calculated['zero_axis'], $this->calculated['boundary_box']['right'], $this->calculated['zero_axis'], $this->colour[$colour]); +} + +function draw_x_axis() { + $gridColour = $this->colour[$this->parameter['grid_colour']]; + $tickColour = $this->colour[$this->parameter['x_ticks_colour']]; + $axis_colour = $this->parameter['axis_colour']; + $xGrid = $this->parameter['x_grid']; + $gridTop = $this->calculated['boundary_box']['top']; + $gridBottom = $this->calculated['boundary_box']['bottom']; + + if ($this->parameter['tick_length'] >= 0) { + $tickTop = $this->calculated['boundary_box']['bottom'] - $this->parameter['tick_length']; + $tickBottom = $this->calculated['boundary_box']['bottom']; + $textBottom = $tickBottom + $this->calculated['bottom_inner_padding']; + } else { + $tickTop = $this->calculated['boundary_box']['bottom']; + $tickBottom = $this->calculated['boundary_box']['bottom'] - $this->parameter['tick_length']; + $textBottom = $tickBottom + $this->calculated['bottom_inner_padding']; + } + + $axis_font = $this->parameter['axis_font']; + $axis_size = $this->parameter['axis_size']; + $axis_angle = $this->parameter['x_axis_angle']; + + if ($axis_angle == 0) $reference = 'top-center'; + if ($axis_angle > 0) $reference = 'top-right'; + if ($axis_angle < 0) $reference = 'top-left'; + if ($axis_angle == 90) $reference = 'top-center'; + + //generic tag information. applies to all axis text. + $axisTag = array('points' => $axis_size, 'angle' => $axis_angle, 'font' => $axis_font, 'colour' => $axis_colour); + + foreach ($this->calculated['x_axis']['tick_x'] as $set => $tickX) { + // draw x grid if colour specified + if ($xGrid != 'none') { + switch ($xGrid) { + case 'line': + ImageLine($this->image, round($tickX), round($gridTop), round($tickX), round($gridBottom), $gridColour); + break; + case 'dash': + ImageDashedLine($this->image, round($tickX), round($gridTop), round($tickX), round($gridBottom), $gridColour); + break; + } + } + + if ($this->parameter['x_axis_text'] && !($set % $this->parameter['x_axis_text'])) { // test if tick should be displayed + // draw tick + if ($tickColour != 'none') + ImageLine($this->image, round($tickX), round($tickTop), round($tickX), round($tickBottom), $tickColour); + + // draw axis text + $coords = array('x' => $tickX, 'y' => $textBottom, 'reference' => $reference); + $axisTag['text'] = $this->calculated['x_axis']['text'][$set]; + $axisTag['boundary_box'] = $this->calculated['x_axis']['boundary_box'][$set]; + $this->update_boundaryBox($axisTag['boundary_box'], $coords); + $this->print_TTF($axisTag); + } + } +} + +function draw_y_axis() { + $gridColour = $this->colour[$this->parameter['grid_colour']]; + $tickColour = $this->colour[$this->parameter['y_ticks_colour']]; + $axis_colour = $this->parameter['axis_colour']; + $yGrid = $this->parameter['y_grid']; + $gridLeft = $this->calculated['boundary_box']['left']; + $gridRight = $this->calculated['boundary_box']['right']; + + // axis font information + $axis_font = $this->parameter['axis_font']; + $axis_size = $this->parameter['axis_size']; + $axis_angle = $this->parameter['y_axis_angle']; + $axisTag = array('points' => $axis_size, 'angle' => $axis_angle, 'font' => $axis_font, 'colour' => $axis_colour); + + + if ($this->calculated['y_axis_left']['has_data']) { + // LEFT HAND SIDE + // left and right coords for ticks + if ($this->parameter['tick_length'] >= 0) { + $tickLeft = $this->calculated['boundary_box']['left']; + $tickRight = $this->calculated['boundary_box']['left'] + $this->parameter['tick_length']; + } else { + $tickLeft = $this->calculated['boundary_box']['left'] + $this->parameter['tick_length']; + $tickRight = $this->calculated['boundary_box']['left']; + } + $textRight = $tickLeft - $this->calculated['left_inner_padding']; + + if ($axis_angle == 0) $reference = 'right-center'; + if ($axis_angle > 0) $reference = 'right-top'; + if ($axis_angle < 0) $reference = 'right-bottom'; + if ($axis_angle == 90) $reference = 'right-center'; + + foreach ($this->calculated['y_axis']['tick_y'] as $set => $tickY) { + // draw y grid if colour specified + if ($yGrid != 'none') { + switch ($yGrid) { + case 'line': + ImageLine($this->image, round($gridLeft), round($tickY), round($gridRight), round($tickY), $gridColour); + break; + case 'dash': + ImageDashedLine($this->image, round($gridLeft), round($tickY), round($gridRight), round($tickY), $gridColour); + break; + } + } + + // y axis text + if ($this->parameter['y_axis_text_left'] && !($set % $this->parameter['y_axis_text_left'])) { // test if tick should be displayed + // draw tick + if ($tickColour != 'none') + ImageLine($this->image, round($tickLeft), round($tickY), round($tickRight), round($tickY), $tickColour); + + // draw axis text... + $coords = array('x' => $textRight, 'y' => $tickY, 'reference' => $reference); + $axisTag['text'] = $this->calculated['y_axis_left']['text'][$set]; + $axisTag['boundary_box'] = $this->calculated['y_axis_left']['boundary_box'][$set]; + $this->update_boundaryBox($axisTag['boundary_box'], $coords); + $this->print_TTF($axisTag); + } + } + } + + if ($this->calculated['y_axis_right']['has_data']) { + // RIGHT HAND SIDE + // left and right coords for ticks + if ($this->parameter['tick_length'] >= 0) { + $tickLeft = $this->calculated['boundary_box']['right'] - $this->parameter['tick_length']; + $tickRight = $this->calculated['boundary_box']['right']; + } else { + $tickLeft = $this->calculated['boundary_box']['right']; + $tickRight = $this->calculated['boundary_box']['right'] - $this->parameter['tick_length']; + } + $textLeft = $tickRight+ $this->calculated['left_inner_padding']; + + if ($axis_angle == 0) $reference = 'left-center'; + if ($axis_angle > 0) $reference = 'left-bottom'; + if ($axis_angle < 0) $reference = 'left-top'; + if ($axis_angle == 90) $reference = 'left-center'; + + foreach ($this->calculated['y_axis']['tick_y'] as $set => $tickY) { + if (!$this->calculated['y_axis_left']['has_data'] && $yGrid != 'none') { // draw grid if not drawn already (above) + switch ($yGrid) { + case 'line': + ImageLine($this->image, round($gridLeft), round($tickY), round($gridRight), round($tickY), $gridColour); + break; + case 'dash': + ImageDashedLine($this->image, round($gridLeft), round($tickY), round($gridRight), round($tickY), $gridColour); + break; + } + } + + if ($this->parameter['y_axis_text_right'] && !($set % $this->parameter['y_axis_text_right'])) { // test if tick should be displayed + // draw tick + if ($tickColour != 'none') + ImageLine($this->image, round($tickLeft), round($tickY), round($tickRight), round($tickY), $tickColour); + + // draw axis text... + $coords = array('x' => $textLeft, 'y' => $tickY, 'reference' => $reference); + $axisTag['text'] = $this->calculated['y_axis_right']['text'][$set]; + $axisTag['boundary_box'] = $this->calculated['y_axis_left']['boundary_box'][$set]; + $this->update_boundaryBox($axisTag['boundary_box'], $coords); + $this->print_TTF($axisTag); + } + } + } +} + +function init_data() { + $this->calculated['y_plot'] = array(); // array to hold pixel plotting coords for y axis + $height = $this->calculated['boundary_box']['bottom'] - $this->calculated['boundary_box']['top']; + $width = $this->calculated['boundary_box']['right'] - $this->calculated['boundary_box']['left']; + + // calculate pixel steps between axis ticks. + $this->calculated['y_axis']['step'] = $height / ($this->parameter['y_axis_gridlines'] - 1); + + // calculate x ticks spacing taking into account x offset for ticks. + $extraTick = 2 * $this->parameter['x_offset']; // extra tick to account for padding + $numTicks = $this->calculated['x_axis']['num_ticks'] - 1; // number of x ticks + + $this->calculated['x_axis']['step'] = $width / ($numTicks + $extraTick); + $widthPlot = $width - ($this->calculated['x_axis']['step'] * $extraTick); + $this->calculated['x_axis']['step'] = $widthPlot / $numTicks; + + //calculate factor for transforming x,y physical coords to logical coords for right hand y_axis. + $y_range = $this->calculated['y_axis_right']['max'] - $this->calculated['y_axis_right']['min']; + $y_range = ($y_range ? $y_range : 1); + $this->calculated['y_axis_right']['factor'] = $height / $y_range; + + //calculate factor for transforming x,y physical coords to logical coords for left hand axis. + $yRange = $this->calculated['y_axis_left']['max'] - $this->calculated['y_axis_left']['min']; + $yRange = ($yRange ? $yRange : 1); + $this->calculated['y_axis_left']['factor'] = $height / $yRange; + if ($this->parameter['x_axis_gridlines'] != 'auto') { + $xRange = $this->calculated['x_axis']['max'] - $this->calculated['x_axis']['min']; + $xRange = ($xRange ? $xRange : 1); + $this->calculated['x_axis']['factor'] = $widthPlot / $xRange; + } + + //expand_pre($this->calculated['boundary_box']); + // cycle thru all data sets... + $this->calculated['num_bars'] = 0; + foreach ($this->y_order as $order => $set) { + // determine how many bars there are + if (isset($this->y_format[$set]['bar']) && ($this->y_format[$set]['bar'] != 'none')) { + $this->calculated['bar_offset_index'][$set] = $this->calculated['num_bars']; // index to relate bar with data set. + $this->calculated['num_bars']++; + } + + // calculate y coords for plotting data + foreach ($this->x_data as $index => $x) { + $this->calculated['y_plot'][$set][$index] = $this->y_data[$set][$index]; + + if ((string)$this->y_data[$set][$index] != 'none') { + + if (isset($this->y_format[$set]['y_axis']) && $this->y_format[$set]['y_axis'] == 'right') { + $this->calculated['y_plot'][$set][$index] = + round(($this->y_data[$set][$index] - $this->calculated['y_axis_right']['min']) + * $this->calculated['y_axis_right']['factor']); + } else { + //print "$set $index
    "; + $this->calculated['y_plot'][$set][$index] = + round(($this->y_data[$set][$index] - $this->calculated['y_axis_left']['min']) + * $this->calculated['y_axis_left']['factor']); + } + + } + } + } + //print "factor ".$this->calculated['x_axis']['factor']."
    "; + //expand_pre($this->calculated['x_plot']); + + // calculate bar parameters if bars are to be drawn. + if ($this->calculated['num_bars']) { + $xStep = $this->calculated['x_axis']['step']; + $totalWidth = $this->calculated['x_axis']['step'] - $this->parameter['bar_spacing']; + $barWidth = $totalWidth / $this->calculated['num_bars']; + + $barX = ($barWidth - $totalWidth) / 2; // starting x offset + for ($i=0; $i < $this->calculated['num_bars']; $i++) { + $this->calculated['bar_offset_x'][$i] = $barX; + $barX += $barWidth; // add width of bar to x offset. + } + $this->calculated['bar_width'] = $barWidth; + } + + +} + +function init_x_ticks() { + // get coords for x axis ticks and data plots + //$xGrid = $this->parameter['x_grid']; + $xStep = $this->calculated['x_axis']['step']; + $ticksOffset = $this->parameter['x_offset']; // where to start drawing ticks relative to y axis. + $gridLeft = $this->calculated['boundary_box']['left'] + ($xStep * $ticksOffset); // grid x start + $tickX = $gridLeft; // tick x coord + + foreach ($this->calculated['x_axis']['text'] as $set => $value) { + //print "index: $set
    "; + // x tick value + $this->calculated['x_axis']['tick_x'][$set] = $tickX; + // if num ticks is auto then x plot value is same as x tick + if ($this->parameter['x_axis_gridlines'] == 'auto') $this->calculated['x_plot'][$set] = round($tickX); + //print $this->calculated['x_plot'][$set].'
    '; + $tickX += $xStep; + } + + //print "xStep: $xStep
    "; + // if numeric x axis then calculate x coords for each data point. this is seperate from x ticks. + $gridX = $gridLeft; + $factor = $this->calculated['x_axis']['factor']; + $min = $this->calculated['x_axis']['min']; + + if ($this->parameter['x_axis_gridlines'] != 'auto') { + foreach ($this->x_data as $index => $x) { + //print "index: $index, x: $x
    "; + $offset = $x - $this->calculated['x_axis']['min']; + + //$gridX = ($offset * $this->calculated['x_axis']['factor']); + //print "offset: $offset
    "; + //$this->calculated['x_plot'][$set] = $gridLeft + ($offset * $this->calculated['x_axis']['factor']); + + $this->calculated['x_plot'][$index] = $gridLeft + ($x - $min) * $factor; + + //print $this->calculated['x_plot'][$set].'
    '; + } + } + //expand_pre($this->calculated['boundary_box']); + //print "factor ".$this->calculated['x_axis']['factor']."
    "; + //expand_pre($this->calculated['x_plot']); +} + +function init_y_ticks() { + // get coords for y axis ticks + + $yStep = $this->calculated['y_axis']['step']; + $gridBottom = $this->calculated['boundary_box']['bottom']; + $tickY = $gridBottom; // tick y coord + + for ($i = 0; $i < $this->parameter['y_axis_gridlines']; $i++) { + $this->calculated['y_axis']['tick_y'][$i] = $tickY; + $tickY -= $yStep; + } + +} + +function init_labels() { + if ($this->parameter['title']) { + $size = $this->get_boundaryBox( + array('points' => $this->parameter['title_size'], + 'angle' => 0, + 'font' => $this->parameter['title_font'], + 'text' => $this->parameter['title'])); + $this->calculated['title']['boundary_box'] = $size; + $this->calculated['title']['text'] = $this->parameter['title']; + $this->calculated['title']['font'] = $this->parameter['title_font']; + $this->calculated['title']['points'] = $this->parameter['title_size']; + $this->calculated['title']['colour'] = $this->parameter['title_colour']; + $this->calculated['title']['angle'] = 0; + + $this->calculated['boundary_box']['top'] += $size['height'] + $this->parameter['outer_padding']; + //$this->calculated['boundary_box']['top'] += $size['height']; + + } else $this->calculated['title']['boundary_box'] = $this->get_null_size(); + + if ($this->parameter['y_label_left']) { + $this->calculated['y_label_left']['text'] = $this->parameter['y_label_left']; + $this->calculated['y_label_left']['angle'] = $this->parameter['y_label_angle']; + $this->calculated['y_label_left']['font'] = $this->parameter['label_font']; + $this->calculated['y_label_left']['points'] = $this->parameter['label_size']; + $this->calculated['y_label_left']['colour'] = $this->parameter['label_colour']; + + $size = $this->get_boundaryBox($this->calculated['y_label_left']); + $this->calculated['y_label_left']['boundary_box'] = $size; + //$this->calculated['boundary_box']['left'] += $size['width'] + $this->parameter['inner_padding']; + $this->calculated['boundary_box']['left'] += $size['width']; + + } else $this->calculated['y_label_left']['boundary_box'] = $this->get_null_size(); + + if ($this->parameter['y_label_right']) { + $this->calculated['y_label_right']['text'] = $this->parameter['y_label_right']; + $this->calculated['y_label_right']['angle'] = $this->parameter['y_label_angle']; + $this->calculated['y_label_right']['font'] = $this->parameter['label_font']; + $this->calculated['y_label_right']['points'] = $this->parameter['label_size']; + $this->calculated['y_label_right']['colour'] = $this->parameter['label_colour']; + + $size = $this->get_boundaryBox($this->calculated['y_label_right']); + $this->calculated['y_label_right']['boundary_box'] = $size; + //$this->calculated['boundary_box']['right'] -= $size['width'] + $this->parameter['inner_padding']; + $this->calculated['boundary_box']['right'] -= $size['width']; + + } else $this->calculated['y_label_right']['boundary_box'] = $this->get_null_size(); + + if ($this->parameter['x_label']) { + $this->calculated['x_label']['text'] = $this->parameter['x_label']; + $this->calculated['x_label']['angle'] = $this->parameter['x_label_angle']; + $this->calculated['x_label']['font'] = $this->parameter['label_font']; + $this->calculated['x_label']['points'] = $this->parameter['label_size']; + $this->calculated['x_label']['colour'] = $this->parameter['label_colour']; + + $size = $this->get_boundaryBox($this->calculated['x_label']); + $this->calculated['x_label']['boundary_box'] = $size; + //$this->calculated['boundary_box']['bottom'] -= $size['height'] + $this->parameter['inner_padding']; + $this->calculated['boundary_box']['bottom'] -= $size['height']; + + } else $this->calculated['x_label']['boundary_box'] = $this->get_null_size(); + +} + + +function init_legend() { + $this->calculated['legend'] = array(); // array to hold calculated values for legend. + //$this->calculated['legend']['boundary_box_max'] = array('height' => 0, 'width' => 0); + $this->calculated['legend']['boundary_box_max'] = $this->get_null_size(); + if ($this->parameter['legend'] == 'none') return; + + $position = $this->parameter['legend']; + $numSets = 0; // number of data sets with legends. + $sumTextHeight = 0; // total of height of all legend text items. + $width = 0; + $height = 0; + + foreach ($this->y_order as $set) { + $text = isset($this->y_format[$set]['legend']) ? $this->y_format[$set]['legend'] : 'none'; + $size = $this->get_boundaryBox( + array('points' => $this->parameter['legend_size'], + 'angle' => 0, + 'font' => $this->parameter['legend_font'], + 'text' => $text)); + + $this->calculated['legend']['boundary_box'][$set] = $size; + $this->calculated['legend']['text'][$set] = $text; + //$this->calculated['legend']['font'][$set] = $this->parameter['legend_font']; + //$this->calculated['legend']['points'][$set] = $this->parameter['legend_size']; + //$this->calculated['legend']['angle'][$set] = 0; + + if ($text && $text!='none') { + $numSets++; + $sumTextHeight += $size['height']; + } + + if ($size['width'] > $this->calculated['legend']['boundary_box_max']['width']) + $this->calculated['legend']['boundary_box_max'] = $size; + } + + $offset = $this->parameter['legend_offset']; // offset in pixels of legend box from graph border. + $padding = $this->parameter['legend_padding']; // padding in pixels around legend text. + $textWidth = $this->calculated['legend']['boundary_box_max']['width']; // width of largest legend item. + $textHeight = $this->calculated['legend']['boundary_box_max']['height']; // use height as size to use for colour square in legend. + $width = $padding * 2 + $textWidth + $textHeight * 2; // left and right padding + maximum text width + space for square + $height = $padding * ($numSets + 1) + $sumTextHeight; // top and bottom padding + padding between text + text. + + + $this->calculated['legend']['boundary_box_all'] = array('width' => $width, + 'height' => $height, + 'offset' => $offset, + 'reference' => $position); + + switch ($position) { // move in right or bottom if legend is outside data plotting area. + case 'outside-top' : + $this->calculated['boundary_box']['right'] -= $offset + $width; // move in right hand side + break; + + case 'outside-bottom' : + $this->calculated['boundary_box']['right'] -= $offset + $width; // move in right hand side + break; + + case 'outside-left' : + $this->calculated['boundary_box']['bottom'] -= $offset + $height; // move in right hand side + break; + + case 'outside-right' : + $this->calculated['boundary_box']['bottom'] -= $offset + $height; // move in right hand side + break; + } +} + +function init_y_axis() { + $this->calculated['y_axis_left'] = array(); // array to hold calculated values for y_axis on left. + $this->calculated['y_axis_left']['boundary_box_max'] = $this->get_null_size(); + $this->calculated['y_axis_right'] = array(); // array to hold calculated values for y_axis on right. + $this->calculated['y_axis_right']['boundary_box_max'] = $this->get_null_size(); + + $axis_font = $this->parameter['axis_font']; + $axis_size = $this->parameter['axis_size']; + $axis_colour = $this->parameter['axis_colour']; + $axis_angle = $this->parameter['y_axis_angle']; + + $this->calculated['y_axis_left']['has_data'] = FALSE; + $this->calculated['y_axis_right']['has_data'] = FALSE; + + // find min and max y values. + $minLeft = $this->parameter['y_min_left']; + $maxLeft = $this->parameter['y_max_left']; + $minRight = $this->parameter['y_min_right']; + $maxRight = $this->parameter['y_max_right']; + $dataLeft = array(); + $dataRight = array(); + foreach ($this->y_order as $order => $set) { + if (isset($this->y_format[$set]['y_axis']) && $this->y_format[$set]['y_axis'] == 'right') { + $this->calculated['y_axis_right']['has_data'] = TRUE; + $dataRight = array_merge($dataRight, $this->y_data[$set]); + } else { + $this->calculated['y_axis_left']['has_data'] = TRUE; + $dataLeft = array_merge($dataLeft, $this->y_data[$set]); + } + } + $dataLeftRange = $this->find_range($dataLeft, $minLeft, $maxLeft, $this->parameter['y_resolution_left']); + $dataRightRange = $this->find_range($dataRight, $minRight, $maxRight, $this->parameter['y_resolution_right']); + $minLeft = $dataLeftRange['min']; + $maxLeft = $dataLeftRange['max']; + $minRight = $dataRightRange['min']; + $maxRight = $dataRightRange['max']; + + $this->calculated['y_axis_left']['min'] = $minLeft; + $this->calculated['y_axis_left']['max'] = $maxLeft; + $this->calculated['y_axis_right']['min'] = $minRight; + $this->calculated['y_axis_right']['max'] = $maxRight; + + $stepLeft = ($maxLeft - $minLeft) / ($this->parameter['y_axis_gridlines'] - 1); + $startLeft = $minLeft; + $step_right = ($maxRight - $minRight) / ($this->parameter['y_axis_gridlines'] - 1); + $start_right = $minRight; + + if ($this->parameter['y_axis_text_left']) { + for ($i = 0; $i < $this->parameter['y_axis_gridlines']; $i++) { // calculate y axis text sizes + // left y axis + $value = number_format($startLeft, $this->parameter['y_decimal_left'], $this->parameter['decimal_point'], $this->parameter['thousand_sep']); + $this->calculated['y_axis_left']['data'][$i] = $startLeft; + $this->calculated['y_axis_left']['text'][$i] = $value; // text is formatted raw data + + $size = $this->get_boundaryBox( + array('points' => $axis_size, + 'font' => $axis_font, + 'angle' => $axis_angle, + 'colour' => $axis_colour, + 'text' => $value)); + $this->calculated['y_axis_left']['boundary_box'][$i] = $size; + + if ($size['height'] > $this->calculated['y_axis_left']['boundary_box_max']['height']) + $this->calculated['y_axis_left']['boundary_box_max']['height'] = $size['height']; + if ($size['width'] > $this->calculated['y_axis_left']['boundary_box_max']['width']) + $this->calculated['y_axis_left']['boundary_box_max']['width'] = $size['width']; + + $startLeft += $stepLeft; + } + $this->calculated['boundary_box']['left'] += $this->calculated['y_axis_left']['boundary_box_max']['width'] + + $this->parameter['inner_padding']; + } + + if ($this->parameter['y_axis_text_right']) { + for ($i = 0; $i < $this->parameter['y_axis_gridlines']; $i++) { // calculate y axis text sizes + // right y axis + $value = number_format($start_right, $this->parameter['y_decimal_right'], $this->parameter['decimal_point'], $this->parameter['thousand_sep']); + $this->calculated['y_axis_right']['data'][$i] = $start_right; + $this->calculated['y_axis_right']['text'][$i] = $value; // text is formatted raw data + $size = $this->get_boundaryBox( + array('points' => $axis_size, + 'font' => $axis_font, + 'angle' => $axis_angle, + 'colour' => $axis_colour, + 'text' => $value)); + $this->calculated['y_axis_right']['boundary_box'][$i] = $size; + + if ($size['height'] > $this->calculated['y_axis_right']['boundary_box_max']['height']) + $this->calculated['y_axis_right']['boundary_box_max'] = $size; + if ($size['width'] > $this->calculated['y_axis_right']['boundary_box_max']['width']) + $this->calculated['y_axis_right']['boundary_box_max']['width'] = $size['width']; + + $start_right += $step_right; + } + $this->calculated['boundary_box']['right'] -= $this->calculated['y_axis_right']['boundary_box_max']['width'] + + $this->parameter['inner_padding']; + } +} + +function init_x_axis() { + $this->calculated['x_axis'] = array(); // array to hold calculated values for x_axis. + $this->calculated['x_axis']['boundary_box_max'] = array('height' => 0, 'width' => 0); + + $axis_font = $this->parameter['axis_font']; + $axis_size = $this->parameter['axis_size']; + $axis_colour = $this->parameter['axis_colour']; + $axis_angle = $this->parameter['x_axis_angle']; + + // check whether to treat x axis as numeric + if ($this->parameter['x_axis_gridlines'] == 'auto') { // auto means text based x_axis, not numeric... + $this->calculated['x_axis']['num_ticks'] = sizeof($this->x_data); + $data = $this->x_data; + for ($i=0; $i < $this->calculated['x_axis']['num_ticks']; $i++) { + $value = array_shift($data); // grab value from begin of array + $this->calculated['x_axis']['data'][$i] = $value; + $this->calculated['x_axis']['text'][$i] = $value; // raw data and text are both the same in this case + $size = $this->get_boundaryBox( + array('points' => $axis_size, + 'font' => $axis_font, + 'angle' => $axis_angle, + 'colour' => $axis_colour, + 'text' => $value)); + $this->calculated['x_axis']['boundary_box'][$i] = $size; + if ($size['height'] > $this->calculated['x_axis']['boundary_box_max']['height']) + $this->calculated['x_axis']['boundary_box_max'] = $size; + } + + } else { // x axis is numeric so find max min values... + $this->calculated['x_axis']['num_ticks'] = $this->parameter['x_axis_gridlines']; + + $min = $this->parameter['x_min']; + $max = $this->parameter['x_max']; + $data = array(); + $data = $this->find_range($this->x_data, $min, $max, $this->parameter['x_resolution']); + $min = $data['min']; + $max = $data['max']; + $this->calculated['x_axis']['min'] = $min; + $this->calculated['x_axis']['max'] = $max; + + $step = ($max - $min) / ($this->calculated['x_axis']['num_ticks'] - 1); + $start = $min; + + for ($i = 0; $i < $this->calculated['x_axis']['num_ticks']; $i++) { // calculate x axis text sizes + $value = number_format($start, $this->parameter['xDecimal'], $this->parameter['decimal_point'], $this->parameter['thousand_sep']); + $this->calculated['x_axis']['data'][$i] = $start; + $this->calculated['x_axis']['text'][$i] = $value; // text is formatted raw data + + $size = $this->get_boundaryBox( + array('points' => $axis_size, + 'font' => $axis_font, + 'angle' => $axis_angle, + 'colour' => $axis_colour, + 'text' => $value)); + $this->calculated['x_axis']['boundary_box'][$i] = $size; + + if ($size['height'] > $this->calculated['x_axis']['boundary_box_max']['height']) + $this->calculated['x_axis']['boundary_box_max'] = $size; + + $start += $step; + } + } + if ($this->parameter['x_axis_text']) + $this->calculated['boundary_box']['bottom'] -= $this->calculated['x_axis']['boundary_box_max']['height'] + + $this->parameter['inner_padding']; +} + +// find max and min values for a data array given the resolution. +function find_range($data, $min, $max, $resolution) { + if (sizeof($data) == 0 ) return array('min' => 0, 'max' => 0); + foreach ($data as $key => $value) { + if ($value=='none') continue; + if ($value > $max) $max = $value; + if ($value < $min) $min = $value; + } + + if ($max == 0) { + $factor = 1; + } else { + if ($max < 0) $factor = - pow(10, (floor(log10(abs($max))) + $resolution) ); + else $factor = pow(10, (floor(log10(abs($max))) - $resolution) ); + } + + $max = $factor * @ceil($max / $factor); + $min = $factor * @floor($min / $factor); + + //print "max=$max, min=$min
    "; + + return array('min' => $min, 'max' => $max); +} + +function graph() { + if (func_num_args() == 2) { + $this->parameter['width'] = func_get_arg(0); + $this->parameter['height'] = func_get_arg(1); + } + //$this->boundaryBox = array( + $this->calculated['boundary_box'] = array( + 'left' => 0, + 'top' => 0, + 'right' => $this->parameter['width'] - 1, + 'bottom' => $this->parameter['height'] - 1); + + $this->init_colours(); + + //ImageColorTransparent($this->image, $this->colour['white']); // colour for transparency +} + +function print_TTF($message) { + $points = $message['points']; + $angle = $message['angle']; + $text = $message['text']; + $colour = $this->colour[$message['colour']]; + $font = $this->parameter['path_to_fonts'].$message['font']; + + $x = $message['boundary_box']['x']; + $y = $message['boundary_box']['y']; + $offsetX = $message['boundary_box']['offsetX']; + $offsetY = $message['boundary_box']['offsetY']; + $height = $message['boundary_box']['height']; + $width = $message['boundary_box']['width']; + $reference = $message['boundary_box']['reference']; + + switch ($reference) { + case 'top-left': + case 'left-top': + $y += $height - $offsetY; + //$y += $offsetY; + $x += $offsetX; + break; + case 'left-center': + $y += ($height / 2) - $offsetY; + $x += $offsetX; + break; + case 'left-bottom': + $y -= $offsetY; + $x += $offsetX; + break; + case 'top-center': + $y += $height - $offsetY; + $x -= ($width / 2) - $offsetX; + break; + case 'top-right': + case 'right-top': + $y += $height - $offsetY; + $x -= $width - $offsetX; + break; + case 'right-center': + $y += ($height / 2) - $offsetY; + $x -= $width - $offsetX; + break; + case 'right-bottom': + $y -= $offsetY; + $x -= $width - $offsetX; + break; + case 'bottom-center': + $y -= $offsetY; + $x -= ($width / 2) - $offsetX; + break; + default: + $y = 0; + $x = 0; + break; + } + ImageTTFText($this->image, $points, $angle, $x, $y, $colour, $font, $text); +} + +// move boundaryBox to coordinates specified +function update_boundaryBox(&$boundaryBox, $coords) { + $width = $boundaryBox['width']; + $height = $boundaryBox['height']; + $x = $coords['x']; + $y = $coords['y']; + $reference = $coords['reference']; + switch ($reference) { + case 'top-left': + case 'left-top': + $top = $y; + $bottom = $y + $height; + $left = $x; + $right = $x + $width; + break; + case 'left-center': + $top = $y - ($height / 2); + $bottom = $y + ($height / 2); + $left = $x; + $right = $x + $width; + break; + case 'left-bottom': + $top = $y - $height; + $bottom = $y; + $left = $x; + $right = $x + $width; + break; + case 'top-center': + $top = $y; + $bottom = $y + $height; + $left = $x - ($width / 2); + $right = $x + ($width / 2); + break; + case 'right-top': + case 'top-right': + $top = $y; + $bottom = $y + $height; + $left = $x - $width; + $right = $x; + break; + case 'right-center': + $top = $y - ($height / 2); + $bottom = $y + ($height / 2); + $left = $x - $width; + $right = $x; + break; + case 'bottom=right': + case 'right-bottom': + $top = $y - $height; + $bottom = $y; + $left = $x - $width; + $right = $x; + break; + default: + $top = 0; + $bottom = $height; + $left = 0; + $right = $width; + break; + } + + $boundaryBox = array_merge($boundaryBox, array('top' => $top, + 'bottom' => $bottom, + 'left' => $left, + 'right' => $right, + 'x' => $x, + 'y' => $y, + 'reference' => $reference)); +} + +function get_null_size() { + return array('width' => 0, + 'height' => 0, + 'offsetX' => 0, + 'offsetY' => 0, + //'fontHeight' => 0 + ); +} + +function get_boundaryBox($message) { + $points = $message['points']; + $angle = $message['angle']; + $font = $this->parameter['path_to_fonts'].$message['font']; + $text = $message['text']; + + //print ('get_boundaryBox'); + //expandPre($message); + + // get font size + $bounds = ImageTTFBBox($points, $angle, $font, "W"); + if ($angle < 0) { + $fontHeight = abs($bounds[7]-$bounds[1]); + } else if ($angle > 0) { + $fontHeight = abs($bounds[1]-$bounds[7]); + } else { + $fontHeight = abs($bounds[7]-$bounds[1]); + } + + // get boundary box and offsets for printing at an angle + $bounds = ImageTTFBBox($points, $angle, $font, $text); + + if ($angle < 0) { + $width = abs($bounds[4]-$bounds[0]); + $height = abs($bounds[3]-$bounds[7]); + $offsetY = abs($bounds[3]-$bounds[1]); + $offsetX = 0; + + } else if ($angle > 0) { + $width = abs($bounds[2]-$bounds[6]); + $height = abs($bounds[1]-$bounds[5]); + $offsetY = 0; + $offsetX = abs($bounds[0]-$bounds[6]); + + } else { + $width = abs($bounds[4]-$bounds[6]); + $height = abs($bounds[7]-$bounds[1]); + $offsetY = 0; + $offsetX = 0; + } + + //return values + return array('width' => $width, + 'height' => $height, + 'offsetX' => $offsetX, + 'offsetY' => $offsetY, + //'fontHeight' => $fontHeight + ); +} + +function draw_rectangle($border, $colour, $type) { + $colour = $this->colour[$colour]; + switch ($type) { + case 'fill': // fill the rectangle + ImageFilledRectangle($this->image, $border['left'], $border['top'], $border['right'], $border['bottom'], $colour); + break; + case 'box': // all sides + ImageRectangle($this->image, $border['left'], $border['top'], $border['right'], $border['bottom'], $colour); + break; + case 'axis': // bottom x axis and left y axis + ImageLine($this->image, $border['left'], $border['top'], $border['left'], $border['bottom'], $colour); + ImageLine($this->image, $border['left'], $border['bottom'], $border['right'], $border['bottom'], $colour); + break; + case 'y': // left y axis only + case 'y-left': + ImageLine($this->image, $border['left'], $border['top'], $border['left'], $border['bottom'], $colour); + break; + case 'y-right': // right y axis only + ImageLine($this->image, $border['right'], $border['top'], $border['right'], $border['bottom'], $colour); + break; + case 'x': // bottom x axis only + ImageLine($this->image, $border['left'], $border['bottom'], $border['right'], $border['bottom'], $colour); + break; + case 'u': // u shaped. bottom x axis and both left and right y axis. + ImageLine($this->image, $border['left'], $border['top'], $border['left'], $border['bottom'], $colour); + ImageLine($this->image, $border['right'], $border['top'], $border['right'], $border['bottom'], $colour); + ImageLine($this->image, $border['left'], $border['bottom'], $border['right'], $border['bottom'], $colour); + break; + + } +} + +function init_colours() { + $this->image = ImageCreate($this->parameter['width'], $this->parameter['height']); + // standard colours + $this->colour['white'] = ImageColorAllocate ($this->image, 0xFF, 0xFF, 0xFF); // first colour is background colour. + $this->colour['black'] = ImageColorAllocate ($this->image, 0x00, 0x00, 0x00); + $this->colour['maroon'] = ImageColorAllocate ($this->image, 0x80, 0x00, 0x00); + $this->colour['green'] = ImageColorAllocate ($this->image, 0x00, 0x80, 0x00); + $this->colour['ltgreen'] = ImageColorAllocate ($this->image, 0x52, 0xF1, 0x7F); + $this->colour['ltltgreen']= ImageColorAllocate ($this->image, 0x99, 0xFF, 0x99); + $this->colour['olive'] = ImageColorAllocate ($this->image, 0x80, 0x80, 0x00); + $this->colour['navy'] = ImageColorAllocate ($this->image, 0x00, 0x00, 0x80); + $this->colour['purple'] = ImageColorAllocate ($this->image, 0x80, 0x00, 0x80); + $this->colour['gray'] = ImageColorAllocate ($this->image, 0x80, 0x80, 0x80); + $this->colour['red'] = ImageColorAllocate ($this->image, 0xFF, 0x00, 0x00); + $this->colour['lime'] = ImageColorAllocate ($this->image, 0x00, 0xFF, 0x00); + $this->colour['yellow'] = ImageColorAllocate ($this->image, 0xFF, 0xFF, 0x00); + $this->colour['blue'] = ImageColorAllocate ($this->image, 0x00, 0x00, 0xFF); + $this->colour['ltblue'] = ImageColorAllocate ($this->image, 0x00, 0xCC, 0xFF); + $this->colour['ltltblue'] = ImageColorAllocate ($this->image, 0x99, 0xFF, 0xFF); + $this->colour['fuchsia'] = ImageColorAllocate ($this->image, 0xFF, 0x00, 0xFF); + $this->colour['aqua'] = ImageColorAllocate ($this->image, 0x00, 0xFF, 0xFF); + //$this->colour['white'] = ImageColorAllocate ($this->image, 0xFF, 0xFF, 0xFF); + // shades of gray + $this->colour['grayF0'] = ImageColorAllocate ($this->image, 0xF0, 0xF0, 0xF0); + $this->colour['grayEE'] = ImageColorAllocate ($this->image, 0xEE, 0xEE, 0xEE); + $this->colour['grayDD'] = ImageColorAllocate ($this->image, 0xDD, 0xDD, 0xDD); + $this->colour['grayCC'] = ImageColorAllocate ($this->image, 0xCC, 0xCC, 0xCC); + $this->colour['gray33'] = ImageColorAllocate ($this->image, 0x33, 0x33, 0x33); + $this->colour['gray66'] = ImageColorAllocate ($this->image, 0x66, 0x66, 0x66); + $this->colour['gray99'] = ImageColorAllocate ($this->image, 0x99, 0x99, 0x99); + + $this->colour['none'] = 'none'; + return true; +} + +function output() { + if ($this->debug) { // for debugging purposes. + //expandPre($this->graph); + //expandPre($this->y_data); + //expandPre($this->x_data); + //expandPre($this->parameter); + } else { + + $expiresSeconds = $this->parameter['seconds_to_live']; + $expiresHours = $this->parameter['hours_to_live']; + + if ($expiresHours || $expiresSeconds) { + $now = mktime (date("H"),date("i"),date("s"),date("m"),date("d"),date("Y")); + $expires = mktime (date("H")+$expiresHours,date("i"),date("s")+$expiresSeconds,date("m"),date("d"),date("Y")); + $expiresGMT = gmdate('D, d M Y H:i:s', $expires).' GMT'; + $lastModifiedGMT = gmdate('D, d M Y H:i:s', $now).' GMT'; + + Header('Last-modified: '.$lastModifiedGMT); + Header('Expires: '.$expiresGMT); + } + + if ($this->parameter['file_name'] == 'none') { + switch ($this->parameter['output_format']) { + case 'GIF': + Header("Content-type: image/gif"); // GIF??. switch to PNG guys!! + ImageGIF($this->image); + break; + case 'JPEG': + Header("Content-type: image/jpeg"); // JPEG for line art??. included for completeness. + ImageJPEG($this->image); + break; + default: + Header("Content-type: image/png"); // preferred output format + ImagePNG($this->image); + break; + } + } else { + switch ($this->parameter['output_format']) { + case 'GIF': + ImageGIF($this->image, $this->parameter['file_name'].'.gif'); + break; + case 'JPEG': + ImageJPEG($this->image, $this->parameter['file_name'].'.jpg'); + break; + default: + ImagePNG($this->image, $this->parameter['file_name'].'.png'); + break; + } + } + + ImageDestroy($this->image); + } +} // function output + +function init_variable(&$variable, $value, $default) { + if (!empty($value)) $variable = $value; + else if (isset($default)) $variable = $default; + else unset($variable); +} + +// plot a point. options include square, circle, diamond, triangle, and dot. offset is used for drawing shadows. +// for diamonds and triangles the size should be an even number to get nice look. if odd the points are crooked. +function plot($x, $y, $type, $size, $colour, $offset) { + //print("drawing point of type: $type, at offset: $offset"); + $u = $x + $offset; + $v = $this->calculated['inner_border']['bottom'] - $y + $offset; + $half = $size / 2; + + switch ($type) { + case 'square': + ImageFilledRectangle($this->image, $u-$half, $v-$half, $u+$half, $v+$half, $this->colour[$colour]); + break; + case 'square-open': + ImageRectangle($this->image, $u-$half, $v-$half, $u+$half, $v+$half, $this->colour[$colour]); + break; + case 'circle': + ImageArc($this->image, $u, $v, $size, $size, 0, 360, $this->colour[$colour]); + ImageFillToBorder($this->image, $u, $v, $this->colour[$colour], $this->colour[$colour]); + break; + case 'circle-open': + ImageArc($this->image, $u, $v, $size, $size, 0, 360, $this->colour[$colour]); + break; + case 'diamond': + ImageFilledPolygon($this->image, array($u, $v-$half, $u+$half, $v, $u, $v+$half, $u-$half, $v), 4, $this->colour[$colour]); + break; + case 'diamond-open': + ImagePolygon($this->image, array($u, $v-$half, $u+$half, $v, $u, $v+$half, $u-$half, $v), 4, $this->colour[$colour]); + break; + case 'triangle': + ImageFilledPolygon($this->image, array($u, $v-$half, $u+$half, $v+$half, $u-$half, $v+$half), 3, $this->colour[$colour]); + break; + case 'triangle-open': + ImagePolygon($this->image, array($u, $v-$half, $u+$half, $v+$half, $u-$half, $v+$half), 3, $this->colour[$colour]); + break; + case 'dot': + ImageSetPixel($this->image, $u, $v, $this->colour[$colour]); + break; + } +} + +function bar($x, $y, $type, $size, $colour, $offset, $index, $yoffset) { + $index_offset = $this->calculated['bar_offset_index'][$index]; + if ( $yoffset ) { + $bar_offsetx = 0; + } else { + $bar_offsetx = $this->calculated['bar_offset_x'][$index_offset]; + } + //$this->dbug("drawing bar at offset = $offset : index = $index: bar_offsetx = $bar_offsetx"); + + $span = ($this->calculated['bar_width'] * $size) / 2; + $x_left = $x + $bar_offsetx - $span; + $x_right = $x + $bar_offsetx + $span; + + if ($this->parameter['zero_axis'] != 'none') { + $zero = $this->calculated['zero_axis']; + if ($this->parameter['shadow_below_axis'] ) $zero += $offset; + $u_left = $x_left + $offset; + $u_right = $x_right + $offset - 1; + $v = $this->calculated['boundary_box']['bottom'] - $y + $offset; + + if ($v > $zero) { + $top = $zero +1; + $bottom = $v; + } else { + $top = $v; + $bottom = $zero - 1; + } + + switch ($type) { + case 'open': + //ImageRectangle($this->image, round($u_left), $top, round($u_right), $bottom, $this->colour[$colour]); + if ($v > $zero) + ImageRectangle($this->image, round($u_left), $bottom, round($u_right), $bottom, $this->colour[$colour]); + else + ImageRectangle($this->image, round($u_left), $top, round($u_right), $top, $this->colour[$colour]); + ImageRectangle($this->image, round($u_left), $top, round($u_left), $bottom, $this->colour[$colour]); + ImageRectangle($this->image, round($u_right), $top, round($u_right), $bottom, $this->colour[$colour]); + break; + case 'fill': + ImageFilledRectangle($this->image, round($u_left), $top, round($u_right), $bottom, $this->colour[$colour]); + break; + } + + } else { + + $bottom = $this->calculated['boundary_box']['bottom']; + if ($this->parameter['shadow_below_axis'] ) $bottom += $offset; + if ($this->parameter['inner_border'] != 'none') $bottom -= 1; // 1 pixel above bottom if border is to be drawn. + $u_left = $x_left + $offset; + $u_right = $x_right + $offset - 1; + $v = $this->calculated['boundary_box']['bottom'] - $y + $offset; + + // Moodle addition, plus the function parameter yoffset + if ($yoffset) { + $yoffset = $yoffset - round(($bottom - $v) / 2.0); + $bottom -= $yoffset; + $v -= $yoffset; + } + + switch ($type) { + case 'open': + ImageRectangle($this->image, round($u_left), $v, round($u_right), $bottom, $this->colour[$colour]); + break; + case 'fill': + ImageFilledRectangle($this->image, round($u_left), $v, round($u_right), $bottom, $this->colour[$colour]); + break; + } + } +} + +function area($x_start, $y_start, $x_end, $y_end, $type, $colour, $offset) { + //dbug("drawing area type: $type, at offset: $offset"); + if ($this->parameter['zero_axis'] != 'none') { + $bottom = $this->calculated['boundary_box']['bottom']; + $zero = $this->calculated['zero_axis']; + if ($this->parameter['shadow_below_axis'] ) $zero += $offset; + $u_start = $x_start + $offset; + $u_end = $x_end + $offset; + $v_start = $bottom - $y_start + $offset; + $v_end = $bottom - $y_end + $offset; + switch ($type) { + case 'fill': + // draw it this way 'cos the FilledPolygon routine seems a bit buggy. + ImageFilledPolygon($this->image, array($u_start, $v_start, $u_end, $v_end, $u_end, $zero, $u_start, $zero), 4, $this->colour[$colour]); + ImagePolygon($this->image, array($u_start, $v_start, $u_end, $v_end, $u_end, $zero, $u_start, $zero), 4, $this->colour[$colour]); + break; + case 'open': + //ImagePolygon($this->image, array($u_start, $v_start, $u_end, $v_end, $u_end, $zero, $u_start, $zero), 4, $this->colour[$colour]); + ImageLine($this->image, $u_start, $v_start, $u_end, $v_end, $this->colour[$colour]); + ImageLine($this->image, $u_start, $v_start, $u_start, $zero, $this->colour[$colour]); + ImageLine($this->image, $u_end, $v_end, $u_end, $zero, $this->colour[$colour]); + break; + } + } else { + $bottom = $this->calculated['boundary_box']['bottom']; + $u_start = $x_start + $offset; + $u_end = $x_end + $offset; + $v_start = $bottom - $y_start + $offset; + $v_end = $bottom - $y_end + $offset; + + if ($this->parameter['shadow_below_axis'] ) $bottom += $offset; + if ($this->parameter['inner_border'] != 'none') $bottom -= 1; // 1 pixel above bottom if border is to be drawn. + switch ($type) { + case 'fill': + ImageFilledPolygon($this->image, array($u_start, $v_start, $u_end, $v_end, $u_end, $bottom, $u_start, $bottom), 4, $this->colour[$colour]); + break; + case 'open': + ImagePolygon($this->image, array($u_start, $v_start, $u_end, $v_end, $u_end, $bottom, $u_start, $bottom), 4, $this->colour[$colour]); + break; + } + } +} + +function line($x_start, $y_start, $x_end, $y_end, $type, $brush_type, $brush_size, $colour, $offset) { + //dbug("drawing line of type: $type, at offset: $offset"); + $u_start = $x_start + $offset; + $v_start = $this->calculated['boundary_box']['bottom'] - $y_start + $offset; + $u_end = $x_end + $offset; + $v_end = $this->calculated['boundary_box']['bottom'] - $y_end + $offset; + + switch ($type) { + case 'brush': + $this->draw_brush_line($u_start, $v_start, $u_end, $v_end, $brush_size, $brush_type, $colour); + break; + case 'line' : + ImageLine($this->image, $u_start, $v_start, $u_end, $v_end, $this->colour[$colour]); + break; + case 'dash': + ImageDashedLine($this->image, $u_start, $v_start, $u_end, $v_end, $this->colour[$colour]); + break; + } +} + +// function to draw line. would prefer to use gdBrush but this is not supported yet. +function draw_brush_line($x0, $y0, $x1, $y1, $size, $type, $colour) { + //$this->dbug("line: $x0, $y0, $x1, $y1"); + $dy = $y1 - $y0; + $dx = $x1 - $x0; + $t = 0; + $watchdog = 1024; // precaution to prevent infinite loops. + + $this->draw_brush($x0, $y0, $size, $type, $colour); + if (abs($dx) > abs($dy)) { // slope < 1 + //$this->dbug("slope < 1"); + $m = $dy / $dx; // compute slope + $t += $y0; + $dx = ($dx < 0) ? -1 : 1; + $m *= $dx; + while (round($x0) != round($x1)) { + if (!$watchdog--) break; + $x0 += $dx; // step to next x value + $t += $m; // add slope to y value + $y = round($t); + //$this->dbug("x0=$x0, x1=$x1, y=$y watchdog=$watchdog"); + $this->draw_brush($x0, $y, $size, $type, $colour); + + } + } else { // slope >= 1 + //$this->dbug("slope >= 1"); + $m = $dx / $dy; // compute slope + $t += $x0; + $dy = ($dy < 0) ? -1 : 1; + $m *= $dy; + while (round($y0) != round($y1)) { + if (!$watchdog--) break; + $y0 += $dy; // step to next y value + $t += $m; // add slope to x value + $x = round($t); + //$this->dbug("x=$x, y0=$y0, y1=$y1 watchdog=$watchdog"); + $this->draw_brush($x, $y0, $size, $type, $colour); + + } + } +} + +function draw_brush($x, $y, $size, $type, $colour) { + $x = round($x); + $y = round($y); + $half = round($size / 2); + switch ($type) { + case 'circle': + ImageArc($this->image, $x, $y, $size, $size, 0, 360, $this->colour[$colour]); + ImageFillToBorder($this->image, $x, $y, $this->colour[$colour], $this->colour[$colour]); + break; + case 'square': + ImageFilledRectangle($this->image, $x-$half, $y-$half, $x+$half, $y+$half, $this->colour[$colour]); + break; + case 'vertical': + ImageFilledRectangle($this->image, $x, $y-$half, $x+1, $y+$half, $this->colour[$colour]); + break; + case 'horizontal': + ImageFilledRectangle($this->image, $x-$half, $y, $x+$half, $y+1, $this->colour[$colour]); + break; + case 'slash': + ImageFilledPolygon($this->image, array($x+$half, $y-$half, + $x+$half+1, $y-$half, + $x-$half+1, $y+$half, + $x-$half, $y+$half + ), 4, $this->colour[$colour]); + break; + case 'backslash': + ImageFilledPolygon($this->image, array($x-$half, $y-$half, + $x-$half+1, $y-$half, + $x+$half+1, $y+$half, + $x+$half, $y+$half + ), 4, $this->colour[$colour]); + break; + default: + @eval($type); // user can create own brush script. + } +} + +} // class graph + + +?> diff --git a/lib/javascript.php b/lib/javascript.php new file mode 100644 index 0000000000000..8d74d4eb12bc6 --- /dev/null +++ b/lib/javascript.php @@ -0,0 +1,19 @@ + + + diff --git a/lib/moodlelib.php b/lib/moodlelib.php new file mode 100644 index 0000000000000..a8c181a28e06a --- /dev/null +++ b/lib/moodlelib.php @@ -0,0 +1,1163 @@ +dirroot/theme/$CFG->theme/styles.css")) { + $styles = "$CFG->wwwroot/theme/$CFG->theme/styles.css"; + } else { + $styles = "$CFG->wwwroot/theme/standard/styles.css"; + } + + if (!$cache) { // Do everything we can to prevent clients and proxies caching + @header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); + @header("Pragma: no-cache"); + $meta .= "\n"; + $meta .= "\n"; + } + + include ("$CFG->dirroot/theme/$CFG->theme/header.html"); +} + +function print_footer ($course=NULL) { +// Can provide a course object to make the footer contain a link to +// to the course home page, otherwise the link will go to the site home + global $USER, $CFG, $THEME; + + if ($course) { + $homelink = "wwwroot/course/view.php?id=$course->id\">$course->shortname"; + } else { + $homelink = "wwwroot\">Home"; + } + include ("$CFG->dirroot/theme/$CFG->theme/footer.html"); +} + +function print_navigation ($navigation) { + global $CFG; + + if ($navigation) { + echo "wwwroot/\">Home -> $navigation"; + } +} + +function print_heading($heading) { + echo "

    $heading

    "; +} + +function print_simple_box($message, $align="", $width="", $color="#FFFFFF", $padding=5) { + print_simple_box_start($align, $width, $color, $padding); + echo "

    $message

    "; + print_simple_box_end(); +} + +function print_simple_box_start($align="", $width="", $color="#FFFFFF", $padding=5) { + global $THEME; + + if ($align) { + $tablealign = "ALIGN=\"$align\""; + } + if ($width) { + $tablewidth = "WIDTH=\"$width\""; + $innertablewidth = "WIDTH=\"100%\""; + } + echo ""; + echo "
    borders\">\n"; + echo "
    "; +} + +function print_simple_box_end() { + echo "
    "; + echo "
    "; +} + +function print_single_button($link, $options, $label="OK") { + echo "
    "; + foreach ($options as $name => $value) { + echo ""; + } + echo "
    "; +} + +function print_user_picture($userid, $courseid, $picture, $large=false) { + global $CFG; + + echo "wwwroot/user/view.php?id=$userid&course=$courseid\">"; + if ($large) { + $file = "f1.jpg"; + $size = 100; + } else { + $file = "f2.jpg"; + $size = 35; + } + if ($picture) { + echo "wwwroot/user/pix.php/$userid/$file\" BORDER=0 WIDTH=$size HEIGHT=$size ALT=\"\">"; + } else { + echo "wwwroot/user/default/$file\" BORDER=0 WIDTH=$size HEIGHT=$size ALT=\"\">"; + } + echo ""; +} + +function print_table($table) { +// Prints a nicely formatted table. +// $table is an object with three properties. +// $table->head is an array of heading names. +// $table->align is an array of column alignments +// $table->data[] is an array of arrays containing the data. + + if ( $table->align) { + foreach ($table->align as $key => $aa) { + if ($aa) { + $align[$key] = "ALIGN=\"$aa\""; + } else { + $align[$key] = ""; + } + } + } + + echo "
    "; + + print_simple_box_start("CENTER","","#FFFFFF",0); + echo "\n"; + + if ($table->head) { + echo ""; + foreach ($table->head as $heading) { + echo ""; + } + echo "\n"; + } + + foreach ($table->data as $row) { + echo ""; + foreach ($row as $key => $item) { + echo ""; + } + echo "\n"; + } + echo "
    $heading
    $item
    \n"; + print_simple_box_end(); + + return true; +} + + +function moodledate($date) { + return date("l, j F Y, g:i A T", $date); +} + + +function error ($message, $link="") { + global $CFG, $SESSION; + + print_header("Error"); + echo "
    "; + print_simple_box($message, "center", "", "#FFBBBB"); + + if (!$link) { + if ( !empty($SESSION->fromurl) ) { + $link = "$SESSION->fromurl"; + unset($SESSION->fromurl); + } else { + $link = "$CFG->wwwroot"; + } + } + print_heading("Continue"); + print_footer(); + die; +} + +function notice ($message, $link="") { + global $THEME, $HTTP_REFERER; + + if (!$link) { + $link = $HTTP_REFERER; + } + + echo "
    "; + print_simple_box($message, "center", "", "$THEME->cellheading"); + print_heading("Continue"); + print_footer(); + die; +} + +function notice_yesno ($message, $linkyes, $linkno) { + global $THEME; + + print_simple_box_start("center", "", "$THEME->cellheading"); + echo "

    $message

    "; + echo "

    "; + echo "Yes"; + echo "      "; + echo "No"; + echo "

    "; + print_simple_box_end(); +} + +function redirect($url, $message="", $delay=0) { +// Uses META tags to redirect the user, after printing a notice + global $THEME; + + echo ""; + + if (!empty($message)) { + print_header(); + echo "
    "; + echo "

    $message

    "; + echo "

    ( Continue )

    "; + echo "
    "; + } + die; +} + +function notify ($message) { + echo "

    $message

    \n"; +} + + + +/// PARAMETER HANDLING //////////////////////////////////////////////////// + +function require_variable($var) { + + if (! isset($var)) { + error("A required parameter was missing"); + } +} + +function optional_variable(&$var, $default=0) { + if (! isset($var)) { + $var = $default; + } +} + + + + +/// DATABASE HANDLING //////////////////////////////////////////////// + +function execute_sql($command) { +// Completely general + + global $db; + + $result = $db->Execute("$command"); + + if ($result) { + echo "

    SUCCESS: $command

    "; + return true; + } else { + echo "

    ERROR: $command

    "; + return false; + } +} + +function modify_database($sqlfile) { +// Assumes that the input text file consists of a number +// of SQL statements ENDING WITH SEMICOLONS. The semicolons +// MUST be the last character in a line. +// Lines that are blank or that start with "#" are ignored. +// Only tested with mysql dump files (mysqldump -p -d moodle) + + + if (file_exists($sqlfile)) { + $success = true; + $lines = file($sqlfile); + $command = ""; + + while ( list($i, $line) = each($lines) ) { + $line = chop($line); + $length = strlen($line); + + if ($length && substr($line, 0, 1) <> "#") { + if (substr($line, $length-1, 1) == ";") { + $line = substr($line, 0, $length-1); // strip ; + $command .= $line; + if (! execute_sql($command)) { + $success = false; + } + $command = ""; + } else { + $command .= $line; + } + } + } + + } else { + $success = false; + echo "

    Tried to modify database, but \"$sqlfile\" doesn't exist!

    "; + } + + return $success; +} + + +function record_exists($table, $field, $value) { + global $db; + + $rs = $db->Execute("SELECT * FROM $table WHERE $field = '$value' LIMIT 1"); + if (!$rs) return false; + + if ( $rs->RecordCount() ) { + return true; + } else { + return false; + } +} + +function record_exists_sql($sql) { + global $db; + + $rs = $db->Execute($sql); + if (!$rs) return false; + + if ( $rs->RecordCount() ) { + return true; + } else { + return false; + } +} + + +function count_records($table, $selector, $value) { +// Get all the records and count them + global $db; + + $rs = $db->Execute("SELECT COUNT(*) FROM $table WHERE $selector = '$value'"); + if (!$rs) return 0; + + return $rs->fields[0]; +} + +function count_records_sql($sql) { +// Get all the records and count them + global $db; + + $rs = $db->Execute("$sql"); + if (!$rs) return 0; + + return $rs->fields[0]; +} + +function get_record($table, $selector, $value) { +// Get a single record as an object + global $db; + + $rs = $db->Execute("SELECT * FROM $table WHERE $selector = '$value'"); + if (!$rs) return false; + + if ( $rs->RecordCount() == 1 ) { + return (object)$rs->fields; + } else { + return false; + } +} + +function get_record_sql($sql) { +// Get a single record as an object +// The sql statement is provided as a string. + + global $db; + + $rs = $db->Execute("$sql"); + if (!$rs) return false; + + if ( $rs->RecordCount() == 1 ) { + return (object)$rs->fields; + } else { + return false; + } +} + +function get_records($table, $selector, $value, $sort="") { +// Get a number of records as an array of objects +// Can optionally be sorted eg "time ASC" or "time DESC" +// The "key" is the first column returned, eg usually "id" + global $db; + + if ($sort) { + $sortorder = "ORDER BY $sort"; + } + $sql = "SELECT * FROM $table WHERE $selector = '$value' $sortorder"; + + return get_records_sql($sql); +} + +function get_records_sql($sql) { +// Get a number of records as an array of objects +// The "key" is the first column returned, eg usually "id" +// The sql statement is provided as a string. + + global $db; + + $rs = $db->Execute("$sql"); + if (!$rs) return false; + + if ( $rs->RecordCount() > 0 ) { + $records = $rs->GetAssoc(true); + foreach ($records as $key => $record) { + $objects[$key] = (object) $record; + } + return $objects; + } else { + return false; + } +} + +function get_records_sql_menu($sql) { +// Given an SQL select, this function returns an associative +// array of the first two columns. This is most useful in +// combination with the choose_from_menu function to create +// a form menu. + + global $db; + + $rs = $db->Execute("$sql"); + if (!$rs) return false; + + if ( $rs->RecordCount() > 0 ) { + while (!$rs->EOF) { + $menu[$rs->fields[0]] = $rs->fields[1]; + $rs->MoveNext(); + } + return $menu; + + } else { + return false; + } +} + +function get_field($table, $field, $selector, $value) { + global $db; + + $rs = $db->Execute("SELECT $field FROM $table WHERE $selector = '$value'"); + if (!$rs) return false; + + if ( $rs->RecordCount() == 1 ) { + return $rs->fields["$field"]; + } else { + return false; + } +} + +function set_field($table, $field, $newvalue, $selector, $value) { + global $db; + + return $db->Execute("UPDATE $table SET $field = '$newvalue' WHERE $selector = '$value'"); +} + + +function delete_records($table, $selector, $value) { +// Delete one or more records from a table + global $db; + + return $db->Execute("DELETE FROM $table WHERE $selector = '$value'"); +} + +function insert_record($table, $dataobject) { +// Insert a record into a table and return the "id" field +// $dataobject is an object containing needed data + + global $db; + + // Determine all the fields needed + if (! $columns = $db->MetaColumns("$table")) { + return false; + } + + $data = (array)$dataobject; + + // Pull out data matching these fields + foreach ($columns as $column) { + if ($column->name <> "id" && $data[$column->name] ) { + $ddd[$column->name] = $data[$column->name]; + } + } + + // Construct SQL queries + if (! $numddd = count($ddd)) { + return 0; + } + + $count = 0; + $insert = ""; + $select = ""; + + foreach ($ddd as $key => $value) { + $count++; + $insert .= "$key = '$value'"; + $select .= "$key = '$value'"; + if ($count < $numddd) { + $insert .= ", "; + $select .= " AND "; + } + } + + if (! $rs = $db->Execute("INSERT INTO $table SET $insert")) { + return false; + } + + // Pull it out again to find the id. This is the most cross-platform method. + if ($rs = $db->Execute("SELECT id FROM $table WHERE $select")) { + return $rs->fields[0]; + } else { + return false; + } +} + + +function update_record($table, $dataobject) { +// Update a record in a table +// $dataobject is an object containing needed data + + global $db; + + if (! $dataobject->id) { + return false; + } + + // Determine all the fields in the table + $columns = $db->MetaColumns($table); + $data = (array)$dataobject; + + // Pull out data matching these fields + foreach ($columns as $column) { + if ($column->name <> "id" && $data[$column->name] ) { + $ddd[$column->name] = $data[$column->name]; + } + } + + // Construct SQL queries + $numddd = count($ddd); + $count = 0; + $update = ""; + + foreach ($ddd as $key => $value) { + $count++; + $update .= "$key = '$value'"; + if ($count < $numddd) { + $update .= ", "; + } + } + + if ($rs = $db->Execute("UPDATE $table SET $update WHERE id = '$dataobject->id'")) { + return true; + } else { + return false; + } +} + + + +/// USER DATABASE //////////////////////////////////////////////// + +function get_user_info_from_db($field, $value) { + + global $db; + + if (!$field || !$value) + return false; + + $result = $db->Execute("SELECT * FROM user WHERE $field = '$value'"); + + if ( $result->RecordCount() == 1 ) { + $user = (object)$result->fields; + + $rs = $db->Execute("SELECT course FROM user_students WHERE user = '$user->id' "); + while (!$rs->EOF) { + $course = $rs->fields["course"]; + $user->student["$course"] = true; + $rs->MoveNext(); + } + + $rs = $db->Execute("SELECT course FROM user_teachers WHERE user = '$user->id' "); + while (!$rs->EOF) { + $course = $rs->fields["course"]; + $user->teacher["$course"] = true; + $rs->MoveNext(); + } + + $rs = $db->Execute("SELECT * FROM user_admins WHERE user = '$user->id' "); + while (!$rs->EOF) { + $user->admin = true; + $rs->MoveNext(); + } + + if ($course = get_record("course", "category", 0)) { + // Everyone is always a member of the top course + $user->student["$course->id"] = true; + } + + return $user; + + } else { + return false; + } +} + +function update_user_in_db() { + + global $db, $USER, $REMOTE_ADDR; + + if (!isset($USER->id)) + return false; + + $timenow = time(); + if ($db->Execute("UPDATE LOW_PRIORITY user SET lastIP='$REMOTE_ADDR', lastaccess='$timenow' + WHERE id = '$USER->id' ")) { + return true; + } else { + return false; + } +} + +function require_login($course=0) { +// if they aren't already logged in, then send them off to login +// $course is optional - if left out then it just requires that +// that they have an account on the system. + + global $CFG, $SESSION, $USER, $FULLME, $HTTP_REFERER, $PHPSESSID; + + if (! (isset( $USER->loggedin ) && $USER->confirmed) ) { + $SESSION->wantsurl = $FULLME; + $SESSION->fromurl = $HTTP_REFERER; + if ($PHPSESSID) { // Cookies not enabled. + redirect("$CFG->wwwroot/login/?PHPSESSID=$PHPSESSID"); + } else { + redirect("$CFG->wwwroot/login/"); + } + die; + + } else if ($course) { + if (! ($USER->student[$course] || $USER->teacher[$course] || $USER->admin ) ) { + if (!record_exists("course", "id", $course)) { + error("That course doesn't exist"); + } + + $SESSION->wantsurl = $FULLME; + redirect("$CFG->wwwroot/course/login.php?id=$course"); + die; + } + } + + update_user_in_db(); +} + + + +function update_login_count() { + global $SESSION; + + $max_logins = 10; + + if (empty($SESSION->logincount)) { + $SESSION->logincount = 1; + } else { + $SESSION->logincount++; + } + + if ($SESSION->logincount > $max_logins) { + unset($SESSION->wantsurl); + error("Sorry, you have exceeded the allowed number of login attempts. Restart your browser."); + } +} + + +function isadmin($userid=0) { + global $USER; + + if (!$userid) { + return $USER->admin; + } + + return record_exists_sql("SELECT * FROM user_admins WHERE user='$userid'"); +} + +function isteacher($course, $userid=0) { + global $USER; + + if (!$userid) { + return $USER->teacher[$course]; + } + + return record_exists_sql("SELECT * FROM user_teachers WHERE user='$userid' AND course='$course'"); +} + + +function isstudent($course, $userid=0) { + global $USER; + + if (!$userid) { + return $USER->student[$course]; + } + + $timenow = time(); // todo: add time check below + + return record_exists_sql("SELECT * FROM user_students WHERE user='$userid' AND course='$course'"); +} + + +function reset_login_count() { + global $SESSION; + + $SESSION->logincount = 0; +} + + +function set_moodle_cookie($thing) { + + $days = 60; + $seconds = 60*60*24*$days; + + setCookie ('MOODLEID'); + setCookie ('MOODLEID', rc4encrypt($thing), time()+$seconds, "/"); +} + + +function get_moodle_cookie() { + global $MOODLEID; + return rc4decrypt($MOODLEID); +} + + + +function verify_login($username, $password) { + + $user = get_user_info_from_db("username", $username); + + if (! $user) { + return false; + } else if ( $user->password == md5($password) ) { + return $user; + } else { + return false; + } +} + +function get_site () { +// Returns $course object of the top-level site. + if ( $course = get_record("course", "category", 0)) { + return $course; + } else { + return false; + } +} + +function get_admin () { +// Returns $user object of the main admin user + + if ( $admins = get_records_sql("SELECT u.* FROM user u, user_admins a WHERE a.user = u.id ORDER BY u.id ASC")) { + foreach ($admins as $admin) { + return $admin; // ie the first one (yeah I know it's bodgy) + } + } else { + return false; + } +} + +function get_teacher($courseid) { +// Returns $user object of the main teacher for a course + if ( $teachers = get_records_sql("SELECT u.* FROM user u, user_teachers t + WHERE t.user = u.id AND t.course = '$courseid' + ORDER BY t.authority ASC")) { + foreach ($teachers as $teacher) { + return $teacher; // ie the first one (yeah I know it's bodgy) + } + } else { + return false; + } +} + + + +/// MODULE FUNCTIONS ///////////////////////////////////////////////// + +function get_coursemodule_from_instance($modulename, $instance, $course) { +// Given an instance of a module, finds the coursemodule description + + return get_record_sql("SELECT cm.*, m.name + FROM course_modules cm, modules md, $modulename m + WHERE cm.course = '$course' AND + cm.deleted = '0' AND + cm.instance = m.id AND + md.name = '$modulename' AND + md.id = cm.module AND + m.id = '$instance'"); + +} + +function get_all_instances_in_course($modulename, $course, $sort="cw.week") { +// Returns an array of all the active instances of a particular +// module in a given course. Returns false on any errors. + + return get_records_sql("SELECT m.*,cw.week,cm.id as coursemodule + FROM course_modules cm, course_weeks cw, modules md, $modulename m + WHERE cm.course = '$course' AND + cm.instance = m.id AND + cm.deleted = '0' AND + cm.week = cw.id AND + md.name = '$modulename' AND + md.id = cm.module + ORDER BY $sort"); + +} + +function print_update_module_icon($moduleid) { + global $CFG; + + echo "    + wwwroot/course/mod.php?update=$moduleid\" TARGET=_top>wwwroot/pix/t/edit.gif\" ALIGN=right BORDER=0 ALT=\"Update this module\">"; +} + + + + +/// CORRESPONDENCE //////////////////////////////////////////////// + + +function email_to_user($user, $from, $subject, $message) { + global $CFG; + + include_once("$CFG->libdir/class.smtp.php"); + include_once("$CFG->libdir/class.phpmailer.php"); + + $subject = stripslashes($subject); + $message = stripslashes($message); + + $mail = new phpmailer; + + $mail->IsSMTP(); // set mailer to use SMTP + $mail->From = "$from->email"; + $mail->FromName = "$from->firstname $from->lastname"; + $mail->Host = "$CFG->smtphost;localhost"; // specify main and backup server + $mail->AddReplyTo("$from->email","$from->firstname $from->lastname"); + $mail->AddAddress("$user->email","$user->firstname $user->lastname"); + $mail->WordWrap = 70; // set word wrap + $mail->IsHTML(false); // set email format to HTML + $mail->Subject = "$subject"; + + $mail->Body = "\n\n$message"; + + if (!$mail->Send()) { + return false; + } + + return true; +} + + +function email_to_users(&$users, $from, $subject, $message, $link, $footer="") { +// users is an array of user records as returned by get_records_sql + + global $CFG; + + include_once("$CFG->libdir/class.smtp.php"); + include_once("$CFG->libdir/class.phpmailer.php"); + + if (!$users) { + return false; + } + + $subject = stripslashes($subject); + $message = stripslashes($message); + + $mail = new phpmailer; + + $mail->IsSMTP(); // set mailer to use SMTP + $mail->From = "$from->email"; + $mail->FromName = "$from->firstname $from->lastname"; + $mail->Host = "$CFG->smtphost;localhost"; // specify main and backup server + $mail->AddAddress("","Subscribers"); + + foreach ($users as $user) { + $mail->AddBCC("$user->email","$user->firstname $user->lastname"); + } + + $mail->AddReplyTo("$from->email","$from->firstname $from->lastname"); + $mail->WordWrap = 70; // set word wrap + $mail->IsHTML(false); // set email format to HTML + $mail->Subject = "$subject"; + + $message .= "\n\n--\n$from->firstname $from->lastname"; + + if ($footer) { + $message .= "\n\n$footer\n"; + } + + if ($link) { + $message .= "Link: $link\n"; + } + + $mail->Body = "\n\n$message"; + + if (!$mail->Send()) { + //echo "Message was not sent

    "; + //echo "Mailer Error: " . $mail->ErrorInfo; + return false; + } + + return true; +} + + + +function email_to_course($from, $course, $emailall, $subject, $message, $link, $footer="") { + global $CFG; + + include_once("$CFG->libdir/class.smtp.php"); + include_once("$CFG->libdir/class.phpmailer.php"); + + + if (!$students = get_records_sql("SELECT u.* FROM user u, user_students s + WHERE s.course = '$course->id' + AND s.user = u.id")) { + notice("Could not find any students to mail to!"); + } + + if (!$teachers = get_records_sql("SELECT u.* FROM user u, user_teachers t + WHERE t.course = '$course->id' + AND t.user = u.id")) { + notice("Could not find any teachers to mail to!"); + } + + if (!$teachers && !$students) { + return false; + } + + $subject = stripslashes($subject); + $message = stripslashes($message); + + $mail = new phpmailer; + + $mail->IsSMTP(); // set mailer to use SMTP + $mail->From = "$from->email"; + $mail->FromName = "$from->firstname $from->lastname"; + $mail->Host = "$CFG->smtphost;localhost"; // specify main and backup server + + foreach ($students as $student) { + if ($emailall || $student->forwardmail) { + $mail->AddAddress("$student->email","$student->firstname $student->lastname"); + } + } + foreach ($teachers as $teacher) { + if ($emailall || $teacher->forwardmail) { + $mail->AddAddress("$teacher->email","$teacher->firstname $teacher->lastname"); + } + } + + $mail->AddReplyTo("$from->email","$from->firstname $from->lastname"); + $mail->WordWrap = 70; // set word wrap + $mail->IsHTML(false); // set email format to HTML + $mail->Subject = "$course->shortname: $subject"; + + $message .= "\n\n--\n$from->firstname $from->lastname"; + + if ($footer) { + $message .= "\n\n$footer"; + } + + if ($link) { + $message .= "\nLink: $link"; + } + + $mail->Body = "\n\n$message"; + + if (!$mail->Send()) { + //echo "Message was not sent

    "; + //echo "Mailer Error: " . $mail->ErrorInfo; + return false; + } + + return true; + +} + +/// FILE HANDLING ///////////////////////////////////////////// + +function get_directory_list( $rootdir ) { +// Returns an array with all the filenames in +// all subdirectories, relative to the given rootdir. + + $dirs = array(); + + $dir = opendir( $rootdir ); + + while( $file = readdir( $dir ) ) { + $fullfile = $rootdir."/".$file; + if ($file != "." and $file != "..") { + if (filetype($fullfile) == "dir") { + $subdirs = get_directory_list($fullfile); + foreach ($subdirs as $subdir) { + $dirs[] = $file."/".$subdir; + } + } else { + $dirs[] = $file; + } + } + } + + return $dirs; +} + + + +/// ENCRYPTION //////////////////////////////////////////////// + +function rc4encrypt($data) { + $password = "nfgjeingjk"; + return endecrypt($password, $data, ""); +} + +function rc4decrypt($data) { + $password = "nfgjeingjk"; + return endecrypt($password, $data, "de"); +} + +function endecrypt ($pwd, $data, $case) { +// Based on a class by Mukul Sabharwal [mukulsabharwal@yahoo.com] + + if ($case == 'de') { + $data = urldecode($data); + } + + $key[] = ""; + $box[] = ""; + $temp_swap = ""; + $pwd_length = 0; + + $pwd_length = strlen($pwd); + + for ($i = 0; $i <= 255; $i++) { + $key[$i] = ord(substr($pwd, ($i % $pwd_length), 1)); + $box[$i] = $i; + } + + $x = 0; + + for ($i = 0; $i <= 255; $i++) { + $x = ($x + $box[$i] + $key[$i]) % 256; + $temp_swap = $box[$i]; + $box[$i] = $box[$x]; + $box[$x] = $temp_swap; + } + + $temp = ""; + $k = ""; + + $cipherby = ""; + $cipher = ""; + + $a = 0; + $j = 0; + + for ($i = 0; $i < strlen($data); $i++) { + $a = ($a + 1) % 256; + $j = ($j + $box[$a]) % 256; + $temp = $box[$a]; + $box[$a] = $box[$j]; + $box[$j] = $temp; + $k = $box[(($box[$a] + $box[$j]) % 256)]; + $cipherby = ord(substr($data, $i, 1)) ^ $k; + $cipher .= chr($cipherby); + } + + if ($case == 'de') { + $cipher = urldecode(urlencode($cipher)); + } else { + $cipher = urlencode($cipher); + } + + return $cipher; +} + + +/// MISCELLANEOUS //////////////////////////////////////////////////////////////////// + +function getweek ($startdate, $thedate) { +// Given dates in seconds, how many weeks is the date from startdate +// The first week is 1, the second 2 etc ... + + if ($thedate < $startdate) { // error + return 0; + } + + return floor(($thedate - $startdate) / 604800.0) + 1; +} + +function add_to_log ($message, $course=0) { + global $db, $USER, $REMOTE_ADDR; + + $timenow = time(); + $me = me(); + $message = addslashes($message); + + $result = $db->Execute("INSERT DELAYED INTO logs + SET user = '$USER->id', time = '$timenow', course = '$course', + ip = '$REMOTE_ADDR', url = '$me', message = '$message'"); + if (!$result) { + error("Could not insert a new entry to the Moodle log"); + } +} + +function generate_password($maxlen=10) { +/* returns a randomly generated password of length $maxlen. inspired by + * http://www.phpbuilder.com/columns/jesus19990502.php3 */ + + global $CFG; + + $fillers = "1234567890!$-+"; + $wordlist = file($CFG->wordlist); + + srand((double) microtime() * 1000000); + $word1 = trim($wordlist[rand(0, count($wordlist) - 1)]); + $word2 = trim($wordlist[rand(0, count($wordlist) - 1)]); + $filler1 = $fillers[rand(0, strlen($fillers) - 1)]; + + return substr($word1 . $filler1 . $word2, 0, $maxlen); +} + + +function format_time($totalsecs) { + + $days = floor($totalsecs/86400); + $remainder = $totalsecs - ($days*86400); + $hours = floor($remainder/3600); + $remainder = $remainder - ($hours*3600); + $mins = floor($remainder/60); + $secs = $remainder - ($mins*60); + + if ($secs != 1) $ss = "s"; + if ($mins != 1) $ms = "s"; + if ($hours != 1) $hs = "s"; + if ($days != 1) $ds = "s"; + + if ($days) $odays = "$days day$ds"; + if ($hours) $ohours = "$hours hr$hs"; + if ($mins) $omins = "$mins min$ms"; + if ($secs) $osecs = "$secs sec$ss"; + + if ($days) return "$odays $ohours"; + if ($hours) return "$ohours $omins"; + if ($mins) return "$omins $osecs"; + if ($secs) return "$osecs"; + return "now"; +} + + +?> diff --git a/lib/psxlsgen.php b/lib/psxlsgen.php new file mode 100644 index 0000000000000..e977d9cdbc23d --- /dev/null +++ b/lib/psxlsgen.php @@ -0,0 +1,204 @@ + +* Version : 0.3 +* Copyright : GNU LGPL +* URL : http://psxlsgen.sourceforge.net +* Last modified : 13 Jun 2001 +* Description : This class is used to generate very simple +* MS Excel file (xls) via PHP. +* The generated xls file can be obtained by web as a stream +* file or can be written under $default_dir path. This package +* is also included mysql, pgsql, oci8 database interaction to +* generate xls files. +* Limitations: +* - Max character size of a text(label) cell is 255 +* ( due to MS Excel 5.0 Binary File Format definition ) +* +* Credits : This class is based on Christian Novak's small +* Excel library functions. +******************************************************************/ + +if( !defined( "PHP_SIMPLE_XLS_GEN" ) ) { + define( "PHP_SIMPLE_XLS_GEN", 1 ); + + class PhpSimpleXlsGen { + var $class_ver = "0.3"; // class version + var $xls_data = ""; // where generated xls be stored + var $default_dir = ""; // default directory to be saved file + var $filename = "psxlsgen"; // save filename + var $fname = ""; // filename with full path + var $crow = 0; // current row number + var $ccol = 0; // current column number + var $totalcol = 0; // total number of columns + var $get_type = 0; // 0=stream, 1=file + var $errno = 0; // 0=no error + var $error = ""; // error string + var $dirsep = "/"; // directory separator + var $header = 1; // 0=no header, 1=header line for xls table + + // Default constructor + function PhpSimpleXlsGen() + { + $os = getenv( "OS" ); + $temp = getenv( "TEMP"); + // check OS and set proper values for some vars. + if ( stristr( $os, "Windows" ) ) { + $this->default_dir = $temp; + $this->dirsep = "\\"; + } else { + // assume that is Unix/Linux + $this->default_dir = "/tmp"; + $this->dirsep = "/"; + } + // begin of the excel file header + $this->xls_data = pack( "ssssss", 0x809, 0x08, 0x00,0x10, 0x0, 0x0 ); + // check header text + if ( $this->header ) { + $this->Header(); + } + } + + function Header( $text="" ) { + if ( $text == "" ) { + $text = "This file was generated using PSXlsGen at ".date("D, d M Y H:i:s T"); + } + if ( $this->totalcol < 1 ) { + $this->totalcol = 1; + } + $this->InsertText( $text ); + $this->crow += 2; + $this->ccol = 0; + } + + // end of the excel file + function End() + { + $this->xls_data .= pack("sssssssC", 0x7D, 11, 3, 4, 25600,0,0,0); + $this->xls_data .= pack( "ss", 0x0A, 0x00 ); + return; + } + + // write a Number (double) into row, col + function WriteNumber_pos( $row, $col, $value ) + { + $this->xls_data .= pack( "sssss", 0x0203, 14, $row, $col, 0x00 ); + $this->xls_data .= pack( "d", $value ); + return; + } + + // write a label (text) into Row, Col + function WriteText_pos( $row, $col, $value ) + { + $len = strlen( $value ); + $this->xls_data .= pack( "s*", 0x0204, 8 + $len, $row, $col, 0x00, $len ); + $this->xls_data .= $value; + return; + } + + // insert a number, increment row,col automatically + function InsertNumber( $value ) + { + if ( $this->ccol == $this->totalcol ) { + $this->ccol = 0; + $this->crow++; + } + $this->WriteNumber_pos( $this->crow, $this->ccol, &$value ); + $this->ccol++; + return; + } + + // insert text, increment row,col automatically + function InsertText( $value ) + { + if ( $this->ccol == $this->totalcol ) { + $this->ccol = 0; + $this->crow++; + } + $this->WriteText_pos( $this->crow, $this->ccol, &$value ); + $this->ccol++; + return; + } + + // Change position of row,col + function ChangePos( $newrow, $newcol ) + { + $this->crow = $newrow; + $this->ccol = $newcol; + return; + } + + // new line + function NewLine() + { + $this->ccol = 0; + $this->crow++; + return; + } + + // send generated xls as stream file + function SendFile( $filename ) + { + $this->filename = $filename; + $this->SendFile(); + } + // send generated xls as stream file + function SendFile() + { + global $HTTP_USER_AGENT; + + $this->End(); + header ( "Expires: Mon, 1 Apr 1974 05:00:00 GMT" ); + header ( "Last-Modified: " . gmdate("D,d M YH:i:s") . " GMT" ); + header ( "Pragma: no-cache" ); + if (!strstr($HTTP_USER_AGENT,"MSIE")) { + $attachment=" attachment;"; + } + header("Content-Type: application/x-msexcel\r\n"); + header("Content-Disposition:$attachment filename=$this->filename.xls\r\n\r\n"); + header("Content-Transfer-Encoding: binary\r\n"); + header("Content-Description: Excel Spreadsheet" ); + print $this->xls_data; + } + + // change the default saving directory + function ChangeDefaultDir( $newdir ) + { + $this->default_dir = $newdir; + return; + } + + // Save generated xls file + function SaveFile( $filename ) + { + $this->filename = $filename; + $this->SaveFile(); + } + + // Save generated xls file + function SaveFile() + { + $this->End(); + $this->fname = $this->default_dir."$this->dirsep".$this->filename; + if ( !stristr( $this->fname, ".xls" ) ) { + $this->fname .= ".xls"; + } + $fp = fopen( $this->fname, "wb" ); + fwrite( $fp, $this->xls_data ); + fclose( $fp ); + return; + } + + // Get generated xls as specified type + function GetXls( $type = 0 ) { + if ( !$type && !$this->get_type ) { + $this->SendFile(); + } else { + $this->SaveFile(); + } + } + } // end of the class PHP_SIMPLE_XLS_GEN +} +// end of ifdef PHP_SIMPLE_XLS_GEN diff --git a/lib/setup.php b/lib/setup.php new file mode 100644 index 0000000000000..49896553068bf --- /dev/null +++ b/lib/setup.php @@ -0,0 +1,66 @@ +maxeditingtime = 1800; + +// Location of standard files + + $CFG->templatedir = "$CFG->dirroot/templates"; + $CFG->imagedir = "$CFG->wwwroot/images"; + $CFG->wordlist = "$CFG->libdir/wordlist.txt"; + $CFG->javascript = "$CFG->libdir/javascript.php"; + $CFG->stylesheet = "$CFG->wwwroot/theme/$CFG->theme/styles.css"; + $CFG->header = "$CFG->dirroot/theme/$CFG->theme/header.html"; + $CFG->footer = "$CFG->dirroot/theme/$CFG->theme/footer.html"; + +// Load up theme variables (colours etc) + + require("$CFG->dirroot/theme/$CFG->theme/config.php"); + + +// Load up standard libraries + + require("$CFG->libdir/weblib.php"); // Standard web page functions + require("$CFG->libdir/adodb/adodb.inc.php"); // Database access functions + require("$CFG->libdir/adodb/tohtml.inc.php");// Database display functions + require("$CFG->libdir/moodlelib.php"); // Various Moodle functions + +// Load up global environment variables + + class object {}; + + session_start(); + session_register("SESSION"); // Current session info + session_register("USER"); // Current user info + if (! isset($SESSION)) $SESSION = new object; + if (! isset($USER)) $USER = new object; + + $FULLME = qualified_me(); + $ME = strip_querystring($FULLME); + +// Connect to the database using adodb + + ADOLoadCode($CFG->dbtype); + $db = &ADONewConnection(); + $db->PConnect($CFG->dbhost,$CFG->dbuser,$CFG->dbpass,$CFG->dbname); + + + +?> diff --git a/lib/weblib.php b/lib/weblib.php new file mode 100644 index 0000000000000..10e1d7ce3e94e --- /dev/null +++ b/lib/weblib.php @@ -0,0 +1,291 @@ +", etc.) properly quoted, +// or if $var is undefined, will return an empty string. note this function +// must be called with a variable, for normal strings or functions use o() + + return isset($var) ? htmlSpecialChars(stripslashes($var)) : ""; +} + +function pv(&$var) { +// prints $var with the HTML characters (like "<", ">", etc.) properly quoted, +// or if $var is undefined, will print an empty string. note this function +// must be called with a variable, for normal strings or functions use p() + + echo isset($var) ? htmlSpecialChars(stripslashes($var)) : ""; +} + +function o($var) { +// returns $var with HTML characters (like "<", ">", etc.) properly quoted, +// or if $var is empty, will return an empty string. + + return empty($var) ? "" : htmlSpecialChars(stripslashes($var)); +} + +function p($var) { +// prints $var with HTML characters (like "<", ">", etc.) properly quoted, +// or if $var is empty, will print an empty string. + + echo empty($var) ? "" : htmlSpecialChars(stripslashes($var)); +} + + +function strip_querystring($url) { +// takes a URL and returns it without the querystring portion + + if ($commapos = strpos($url, '?')) { + return substr($url, 0, $commapos); + } else { + return $url; + } +} + +function get_referer() { +// returns the URL of the HTTP_REFERER, less the querystring portion + + $HTTP_REFERER = getenv("HTTP_REFERER"); + return strip_querystring(nvl($HTTP_REFERER)); +} + + +function me() { +// returns the name of the current script, WITH the querystring portion. +// this function is necessary because PHP_SELF and REQUEST_URI and PATH_INFO +// return different things depending on a lot of things like your OS, Web +// server, and the way PHP is compiled (ie. as a CGI, module, ISAPI, etc.) + + if (getenv("REQUEST_URI")) { + $me = getenv("REQUEST_URI"); + + } elseif (getenv("PATH_INFO")) { + $me = getenv("PATH_INFO"); + + } elseif ($GLOBALS["PHP_SELF"]) { + $me = $GLOBALS["PHP_SELF"]; + } + + return $me; +} + + + +function qualified_me() { +// like me() but returns a full URL + + $HTTPS = getenv("HTTPS"); + $SERVER_PROTOCOL = getenv("SERVER_PROTOCOL"); + $HTTP_HOST = getenv("HTTP_HOST"); + + $protocol = (isset($HTTPS) && $HTTPS == "on") ? "https://" : "http://"; + $url_prefix = "$protocol$HTTP_HOST"; + return $url_prefix . me(); +} + + +function match_referer($good_referer = "") { +// returns true if the referer is the same as the good_referer. If +// good_refer is not specified, use qualified_me as the good_referer + + if ($good_referer == "") { $good_referer = qualified_me(); } + return $good_referer == get_referer(); +} + + +function read_template($filename, &$var) { +// return a (big) string containing the contents of a template file with all +// the variables interpolated. all the variables must be in the $var[] array or +// object (whatever you decide to use). +// +// WARNING: do not use this on big files!! + + $temp = str_replace("\\", "\\\\", implode(file($filename), "")); + $temp = str_replace('"', '\"', $temp); + eval("\$template = \"$temp\";"); + return $template; +} + +function checked(&$var, $set_value = 1, $unset_value = 0) { +// if variable is set, set it to the set_value otherwise set it to the +// unset_value. used to handle checkboxes when you are expecting them from +// a form + + if (empty($var)) { + $var = $unset_value; + } else { + $var = $set_value; + } +} + +function frmchecked(&$var, $true_value = "checked", $false_value = "") { +// prints the word "checked" if a variable is true, otherwise prints nothing, +// used for printing the word "checked" in a checkbox form input + + if ($var) { + echo $true_value; + } else { + echo $false_value; + } +} + + +function link_to_popup_window ($url, $name="popup", $linkname="click here", $height=400, $width=500) { +// This will create a HTML link that will work on both +// Javascript and non-javascript browsers. +// Relies on the Javascript function openpopup in javascript.php +// $url must be relative to home page eg /mod/survey/stuff.php + + echo "\n"; + echo "\n\n"; + +} + +function close_window_button() { + echo "

    "; + echo ""; + echo "
    "; +} + + +function choose_from_menu ($options, $name, $selected="", $nothing="Choose...", $script="") { +// $options["value"]["label"] + + if ($script) { + $javascript = "onChange=\"$script\""; + } + echo "\n"; +} + +function popup_form ($common, $options, $formname, $selected="", $nothing="Choose...") { +// Implements a complete little popup form +// $common = the URL up to the point of the variable that changes +// $options = A list of value-label pairs for the popup list +// $formname = name must be unique on the page +// $selected = the option that is already selected +// $nothing = The label for the "no choice" option + + echo "
    "; + echo "
    \n"; +} + + + +function formerr($error) { + if (!empty($error)) { + echo "$error"; + } +} + + +function validate_email ($address) { +// Validates an email to make it makes sense. + return (ereg('^[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+'. + '@'. + '[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+\.'. + '[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+$', + $address)); +} + + +function get_slash_arguments($i=0) { +// Extracts arguments from "/foo/bar/something" +// eg http://mysite.com/script.php/foo/bar/something +// Might only work on Apache + + global $PATH_INFO; + + if (!isset($PATH_INFO)) { + return false; + } + + $args = explode("/", $PATH_INFO); + + if ($i) { // return just the required argument + return $args[$i]; + + } else { // return the whole array + array_shift($args); // get rid of the empty first one + return $args; + } +} + + +function text_to_html($text) { + global $CFG; + +// Given plain text, makes it into HTML as nicely as possible. + + // Make URLs into links. eg http://moodle.com/ + $text = eregi_replace("([[:alnum:]]+)://([^[:space:]]*)([[:alnum:]#?/&=])", + "\\1://\\2\\3", $text); + + // eg www.moodle.com + $text = eregi_replace("([[:space:]])www.([^[:space:]]*)([[:alnum:]#?/&=])", + "\\1www.\\2\\3", $text); + + // Make returns into HTML newlines. + $text = nl2br($text); + + // Turn smileys into images. + + $text = ereg_replace(":-)", "smilewwwroot/pix/s/smiley.gif>", $text); + $text = ereg_replace(":-D", "grinwwwroot/pix/s/biggrin.gif>", $text); + $text = ereg_replace(";-)", "winkwwwroot/pix/s/wink.gif>", $text); + $text = ereg_replace("8-)", "wide-eyedwwwroot/pix/s/wideeyes.gif>", $text); + $text = ereg_replace(":-\(", "sadwwwroot/pix/s/sad.gif>", $text); + $text = ereg_replace(":-P", "tongue-outwwwroot/pix/s/tongueout.gif>", $text); + $text = ereg_replace(":-/", "mixedwwwroot/pix/s/mixed.gif>", $text); + $text = ereg_replace(":-o", "surprisedwwwroot/pix/s/surprise.gif>", $text); + $text = ereg_replace("B-)", "coolwwwroot/pix/s/cool.gif>", $text); + + return "

    ".$text."

    "; +} + + +?> diff --git a/lib/wordlist.txt b/lib/wordlist.txt new file mode 100644 index 0000000000000..8ccb521ceff07 --- /dev/null +++ b/lib/wordlist.txt @@ -0,0 +1,17 @@ +baby +bull +camel +car +cat +elephant +fence +flower +frog +gate +grass +music +peg +pillow +rabbit +rock +tree diff --git a/login/change_password.php b/login/change_password.php new file mode 100644 index 0000000000000..162d785f17ccb --- /dev/null +++ b/login/change_password.php @@ -0,0 +1,86 @@ +username; + $password = $frm->newpassword1; + + if (! set_field("user", "password", md5($frm->newpassword1), "username", $frm->username)) { + error("Could not set the new password"); + } + + unset($USER); + + $USER = get_user_info_from_db("username", $username); + $USER->loggedin = true; + + set_moodle_cookie($USER->username); + + add_to_log("Changed password"); + reset_login_count(); + + print_header("Changed password", "Password changed successfully", "Changed Password", ""); + notice("Password changed successfully", "$CFG->wwwroot/course/"); + print_footer(); + exit; + } + } + + + + if (!$frm->username) + $frm->username = get_moodle_cookie(); + + if ($frm->username) { + $focus = "form.password"; + } else { + $focus = "form.username"; + } + + + print_header("Change password", "Change Password", "Change Password", "$focus"); + print_simple_box_start("center", "", $THEME->cellheading); + include("change_password_form.html"); + print_simple_box_end(); + print_footer(); + + + + +/****************************************************************************** + * FUNCTIONS + *****************************************************************************/ +function validate_form($frm, &$err) { + + if (empty($frm->username)) + $err->username = "Missing username"; + + else if (empty($frm->password)) + $err->password = "Missing password"; + + else if (!verify_login($frm->username, $frm->password)) + $err->password = "Incorrect password for this username"; + + if (empty($frm->newpassword1)) + $err->newpassword1 = "Missing new password"; + + if (empty($frm->newpassword2)) + $err->newpassword2 = "Missing new password"; + + else if ($frm->newpassword1 <> $frm->newpassword2) + $err->newpassword2 = "Passwords not the same"; + + return; +} + +?> diff --git a/login/change_password_form.html b/login/change_password_form.html new file mode 100644 index 0000000000000..e66953c403715 --- /dev/null +++ b/login/change_password_form.html @@ -0,0 +1,33 @@ +

    All fields are required

    + +
    + + + + + + + + + + + + + + + + + + + + +

    Username:

    + username) ?> +

    Password:

    + password) ?> +

    New Password:

    + newpassword1) ?> +

    New Password (again):

    + newpassword2) ?> +
    +
    diff --git a/login/confirm.php b/login/confirm.php new file mode 100644 index 0000000000000..27a9ca490fd69 --- /dev/null +++ b/login/confirm.php @@ -0,0 +1,54 @@ +username == $s) { + + if ($user->confirmed) { + print_header("Registration already confirmed", "Already confirmed", "Confirmed", ""); + echo "

    Thanks, ". $USER->firstname ." ". $USER->lastname . "

    \n"; + echo "

    Your registration has already been confirmed.

    \n"; + echo "

    wwwroot/course/\">Proceed to the courses

    \n"; + print_footer(); + exit; + } + + $USER = $user; + + $timenow = time(); + + $rs = $db->Execute("UPDATE user SET confirmed=1, lastIP='$REMOTE_ADDR', + firstaccess='$timenow', lastaccess='$timenow' + WHERE id = '$USER->id' "); + if (!$rs) error("Could not update this user while confirming"); + + set_moodle_cookie($USER->username); + + $USER->loggedin = true; + $USER->confirmed = 1; + + if ( ! empty($SESSION["wantsurl"]) ) { + $goto = $SESSION["wantsurl"]; + redirect("$goto"); + } + + print_header("Registration confirmed", "Confirmed", "Confirmed", ""); + echo "

    Thanks, ". $USER->firstname ." ". $USER->lastname . "

    \n"; + echo "

    Your registration is now confirmed.

    \n"; + echo "

    wwwroot/course/\">Show me the courses

    \n"; + print_footer(); + } else { + error("Invalid confirmation data"); + } + } + + } else { + redirect("$CFG->wwwroot"); + } + +?> diff --git a/login/countries.php b/login/countries.php new file mode 100644 index 0000000000000..42d86d8a7a238 --- /dev/null +++ b/login/countries.php @@ -0,0 +1,256 @@ + "Select...", +"AF" => "Afghanistan", +"AL" => "Albania", +"DZ" => "Algeria", +"AS" => "American Samoa", +"AD" => "Andorra", +"AO" => "Angola", +"AI" => "Anguilla", +"AQ" => "Antarctica", +"AG" => "Antigua and Barbuda", +"AR" => "Argentina", +"AM" => "Armenia", +"AW" => "Aruba", +"AU" => "Australia", +"AT" => "Austria", +"AZ" => "Azerbaijan", +"BS" => "Bahamas", +"BH" => "Bahrain", +"BD" => "Bangladesh", +"BB" => "Barbados", +"BY" => "Belarus", +"BE" => "Belgium", +"BZ" => "Belize", +"BJ" => "Benin", +"BM" => "Bermuda", +"BT" => "Bhutan", +"BO" => "Bolivia", +"BA" => "Bosnia and Herzegowina", +"BW" => "Botswana", +"BV" => "Bouvet Island", +"BR" => "Brazil", +"IO" => "British Indian Ocean Territory", +"BN" => "Brunei Darussalam", +"BG" => "Bulgaria", +"BF" => "Burkina Faso", +"BI" => "Burundi", +"KH" => "Cambodia", +"CM" => "Cameroon", +"CA" => "Canada", +"CV" => "Cape Verde", +"KY" => "Cayman Islands", +"CF" => "Central African Republic", +"TD" => "Chad", +"CL" => "Chile", +"CN" => "China", +"CX" => "Christmas Island", +"CC" => "Cocos (Keeling) Islands", +"CO" => "Colombia", +"KM" => "Comoros", +"CG" => "Congo", +"CK" => "Cook Islands", +"CR" => "Costa Rica", +"CI" => "Cote D'Ivoire", +"HR" => "Croatia (Hrvatska)", +"CU" => "Cuba", +"CY" => "Cyprus", +"CZ" => "Czech Republic", +"DK" => "Denmark", +"DJ" => "Djibouti", +"DM" => "Dominica", +"DO" => "Dominican Republic", +"TP" => "East Timor", +"EC" => "Ecuador", +"EG" => "Egypt", +"SV" => "El Salvador", +"GQ" => "Equatorial Guinea", +"ER" => "Eritrea", +"EE" => "Estonia", +"ET" => "Ethiopia", +"FK" => "Falkland Islands (Malvinas)", +"FO" => "Faroe Islands", +"FJ" => "Fiji", +"FI" => "Finland", +"FR" => "France", +"FX" => "France, Metropolitan", +"GF" => "French Guiana", +"PF" => "French Polynesia", +"TF" => "French Southern Territories", +"GA" => "Gabon", +"GM" => "Gambia", +"GE" => "Georgia", +"DE" => "Germany", +"GH" => "Ghana", +"GI" => "Gibraltar", +"GR" => "Greece", +"GL" => "Greenland", +"GD" => "Grenada", +"GP" => "Guadeloupe", +"GU" => "Guam", +"GT" => "Guatemala", +"GN" => "Guinea", +"GW" => "Guinea-Bissau", +"GY" => "Guyana", +"HT" => "Haiti", +"HM" => "Heard and Mc Donald Islands", +"HN" => "Honduras", +"HK" => "Hong Kong", +"HU" => "Hungary", +"IS" => "Iceland", +"IN" => "India", +"ID" => "Indonesia", +"IR" => "Iran (Islamic Republic of)", +"IQ" => "Iraq", +"IE" => "Ireland", +"IL" => "Israel", +"IT" => "Italy", +"JM" => "Jamaica", +"JP" => "Japan", +"JO" => "Jordan", +"KZ" => "Kazakhstan", +"KE" => "Kenya", +"KI" => "Kiribati", +"KP" => "Korea, Democratic People's Republic of", +"KR" => "Korea, Republic of", +"KW" => "Kuwait", +"KG" => "Kyrgyzstan", +"LA" => "Lao People's Democratic Republic", +"LV" => "Latvia", +"LB" => "Lebanon", +"LS" => "Lesotho", +"LR" => "Liberia", +"LY" => "Libyan Arab Jamahiriya", +"LI" => "Liechtenstein", +"LT" => "Lithuania", +"LU" => "Luxembourg", +"MO" => "Macau", +"MK" => "Macedonia", +"MG" => "Madagascar", +"MW" => "Malawi", +"MY" => "Malaysia", +"MV" => "Maldives", +"ML" => "Mali", +"MT" => "Malta", +"MH" => "Marshall Islands", +"MQ" => "Martinique", +"MR" => "Mauritania", +"MU" => "Mauritius", +"YT" => "Mayotte", +"MX" => "Mexico", +"FM" => "Micronesia, Federated States of", +"MD" => "Moldova, Republic of", +"MC" => "Monaco", +"MN" => "Mongolia", +"MS" => "Montserrat", +"MA" => "Morocco", +"MZ" => "Mozambique", +"MM" => "Myanmar", +"NA" => "Namibia", +"NR" => "Nauru", +"NP" => "Nepal", +"NL" => "Netherlands", +"AN" => "Netherlands Antilles", +"NC" => "New Caledonia", +"NZ" => "New Zealand", +"NI" => "Nicaragua", +"NE" => "Niger", +"NG" => "Nigeria", +"NU" => "Niue", +"NF" => "Norfolk Island", +"MP" => "Northern Mariana Islands", +"NO" => "Norway", +"OM" => "Oman", +"PK" => "Pakistan", +"PW" => "Palau", +"PA" => "Panama", +"PG" => "Papua New Guinea", +"PY" => "Paraguay", +"PE" => "Peru", +"PH" => "Philippines", +"PN" => "Pitcairn", +"PL" => "Poland", +"PT" => "Portugal", +"PR" => "Puerto Rico", +"QA" => "Qatar", +"RE" => "Reunion", +"RO" => "Romania", +"RU" => "Russian Federation", +"RW" => "Rwanda", +"KN" => "Saint Kitts and Nevis", +"LC" => "Saint Lucia", +"VC" => "Saint Vincent and the Grenadines", +"WS" => "Samoa", +"SM" => "San Marino", +"ST" => "Sao Tome and Principe", +"SA" => "Saudi Arabia", +"SN" => "Senegal", +"SC" => "Seychelles", +"SL" => "Sierra Leone", +"SG" => "Singapore", +"SK" => "Slovakia (Slovak Republic)", +"SI" => "Slovenia", +"SB" => "Solomon Islands", +"SO" => "Somalia", +"ZA" => "South Africa", +"ES" => "Spain", +"LK" => "Sri Lanka", +"SH" => "St. Helena", +"PM" => "St. Pierre and Miquelon", +"SD" => "Sudan", +"SR" => "Suriname", +"SJ" => "Svalbard and Jan Mayen Islands", +"SZ" => "Swaziland", +"SE" => "Sweden", +"CH" => "Switzerland", +"SY" => "Syrian Arab Republic", +"TW" => "Taiwan", +"TJ" => "Tajikistan", +"TZ" => "Tanzania, United Republic of", +"TH" => "Thailand", +"TG" => "Togo", +"TK" => "Tokelau", +"TO" => "Tonga", +"TT" => "Trinidad and Tobago", +"TN" => "Tunisia", +"TR" => "Turkey", +"TM" => "Turkmenistan", +"TC" => "Turks and Caicos Islands", +"TV" => "Tuvalu", +"UG" => "Uganda", +"UA" => "Ukraine", +"AE" => "United Arab Emirates", +"GB" => "United Kingdom", +"US" => "United States of America", +"UY" => "Uruguay", +"UZ" => "Uzbekistan", +"VU" => "Vanuatu", +"VA" => "Vatican City State (Holy See)", +"VE" => "Venezuela", +"VN" => "Vietnam", +"VG" => "Virgin Islands (British)", +"VI" => "Virgin Islands (U.S.)", +"WF" => "Wallis And Futuna Islands", +"EH" => "Western Sahara", +"YE" => "Yemen", +"YU" => "Yugoslavia", +"ZR" => "Zaire", +"ZM" => "Zambia", +"ZW" => "Zimbabwe"); + +echo "\n"; + + +} diff --git a/login/forgot_password.php b/login/forgot_password.php new file mode 100644 index 0000000000000..7d7b343113657 --- /dev/null +++ b/login/forgot_password.php @@ -0,0 +1,102 @@ +email)) { + error("No such user with this address: $frm->email"); + } + + if (! reset_password_and_mail($user)) { + error("Could not reset password and mail the new one to you"); + } + + reset_login_count(); + print_header("Password has been sent", "Password has been sent", "Password Sent", ""); + include("forgot_password_change.html"); + exit; + } + } + + if ( empty($frm->email) ) { + if ( $username = get_moodle_cookie() ) { + $frm->email = get_field("user", "email", "username", "$username"); + } + } + + print_header("Forgot password?", "Have a new password sent to you", "", "form.email"); + + include("forgot_password_form.html"); + + +/****************************************************************************** + * FUNCTIONS + *****************************************************************************/ + +function validate_form($frm, &$err) { + + if (empty($frm->email)) + $err->email = "Missing email address"; + + else if (! validate_email($frm->email)) + $err->email = "Invalid email address"; + + else if (! record_exists("user", "email", $frm->email)) + $err->email = "No such email address on file"; + +} + + +function reset_password_and_mail($user) { + + global $CFG; + + $site = get_site(); + $from = get_admin(); + + $newpassword = generate_password(); + + if (! set_field("user", "password", md5($newpassword), "id", $user->id) ) { + error("Could not set user password!"); + } + + $message = "Hi $user->firstname,\n\n"; + + $message .= "Your account password at '$site->fullname' has been reset\n"; + $message .= "and you have been issued with a new temporary password.\n\n"; + + $message .= "Your current login information is now:\n\n"; + + $message .= " username: $user->username\n"; + $message .= " password: $newpassword\n\n"; + + $message .= "Please go to this page to change your password:\n\n"; + + $message .= "$CFG->wwwroot/login/change_password.php\n\n"; + + $message .= "In most mail programs, this should appear as a blue link\n"; + $message .= "which you can just click on. If that doesn't work, \n"; + $message .= "then cut and paste the address into the address\n"; + $message .= "line at the top of your web browser window.\n\n"; + + $message .= "Cheers from the '$site->fullname' administrator,\n"; + $message .= "$from->firstname $from->lastname ($from->email)\n"; + + $subject = "$site->fullname: Changed password"; + + return email_to_user($user, $from, $subject, $message); + +} + + + +?> diff --git a/login/forgot_password_change.html b/login/forgot_password_change.html new file mode 100644 index 0000000000000..bff2199e6bf16 --- /dev/null +++ b/login/forgot_password_change.html @@ -0,0 +1,15 @@ +
    + +

    An email has been sent to your address at email) ?>

    + +

    Please check your email for your new password + +

    The new password was automatically generated, so you might like to + change it to something easier to remember. +

    + +
    +
    +

    Home

    + + diff --git a/login/forgot_password_form.html b/login/forgot_password_form.html new file mode 100644 index 0000000000000..93bacaae6a3ed --- /dev/null +++ b/login/forgot_password_form.html @@ -0,0 +1,34 @@ + + + + + + +
    +

    Enter in your email address to reset your password, and + have the new password sent to you via email. + +

    Otherwise, you can return to the + login screen or the + home page now. +

    +
    + + + + + + + +
    Email: + email); ?> +
    + +
    +
    +
    + +
    +
    +

    Home

    + diff --git a/login/index.php b/login/index.php new file mode 100644 index 0000000000000..87b4314baeba2 --- /dev/null +++ b/login/index.php @@ -0,0 +1,87 @@ +username, $frm->password); + + update_login_count(); + + if ($user) { + if (! $user->confirmed ) { // they never confirmed via email + print_header("Need to confirm", "Not confirmed yet", "", ""); + include("index_confirm.html"); + die; + } + + $USER = $user; + $USER->loggedin = true; + + if (!update_user_in_db()) { + error("Weird error: User not found"); + } + + if (!update_user_login_times()) { + error("Wierd error: could not update login records"); + } + + set_moodle_cookie($USER->username); + + + if (empty($SESSION->wantsurl)) { + header("Location: $CFG->wwwroot"); + } else { + header("Location: $SESSION->wantsurl"); + unset($SESSION->wantsurl); + } + + reset_login_count(); + add_to_log("Logged in"); + + if ($CFG->smsnotify) { + $time = date("H:i D j M", time()); + $smstring = "$time - $USER->firstname $USER->lastname logged in to $CFG->sitename"; + system("echo \"$smstring \" | /opt/bin/sendsms &> /dev/null &"); + } + + die; + + } else { + $errormsg = "Invalid login, please try again"; + } + } + + if (empty($SESSION->wantsurl)) { + $SESSION->wantsurl = $HTTP_REFERER; + } + + if (!$frm->username) + $frm->username = get_moodle_cookie(); + + if ($frm->username) { + $focus = "form.password"; + } else { + $focus = "form.username"; + } + + print_header("Login to the site", "Login to the site", "Login", $focus); + + include("index_form.html"); + + exit; + + // No footer on this page + +function update_user_login_times() { + global $db, $USER; + + $USER->lastlogin = $USER->currentlogin; + $USER->currentlogin = time(); + + return $db->Execute("UPDATE user + SET lastlogin='$USER->lastlogin', currentlogin='$USER->currentlogin' + WHERE id = '$USER->id'"); +} +?> diff --git a/login/index_confirm.html b/login/index_confirm.html new file mode 100644 index 0000000000000..5a1568b586cfa --- /dev/null +++ b/login/index_confirm.html @@ -0,0 +1,20 @@ +
    + +

    Sorry, firstname) ?>, but you can't log in until you have confirmed your registration

    + +

    Please check your email!

    + +

    +An email should have been sent to your address at email) ?> + +

    +

    +It contains easy instructions to complete your registration. +

    + +

    If you continue to have difficulty, contact the site administrator. + +


    +
    +

    Home

    + diff --git a/login/index_form.html b/login/index_form.html new file mode 100644 index 0000000000000..8cb3a14e695b4 --- /dev/null +++ b/login/index_form.html @@ -0,0 +1,63 @@ +
    + + + + + + + + + + +
    cellheading2?>> +

    Returning to this web site?

    +
    cellheading2?>> +

    Is this your first time here?

    +
    + +

    Log in using your username and password:

    +

    +
    +
    + + + + + +

    Username:

    +

    Password:

    +
    +
    + + +
    + +

     

    +

    If you've logged in before but can't remember your username or password, + then you can have your details sent to you via email.

    +
    +
    + +

    Hi! You need to create a new account for yourself on this server so we know who + you are. Each of the individual courses may also have a one-time + "course entry key", which you won't need until later. Here are + the steps:

    +
      +
    1. Fill out the New Account form with your details. +
    2. An email will be immediately sent to your email address. +
    3. Read your email, and click on the web link it contains. +
    4. Your account will be confirmed and you will be logged in. +
    5. Now, select the course you want to participate in. +
    6. If you are prompted for a "course entry key" - use the one + that your teacher has given you. This will "enrol" you in the + course. +
    7. You can now access the full course. From now on you will only need + to enter your personal username and password (in the form on this page) + to log in and access any course you have enrolled in. +

      Start now by creating a new account!

      +
      +
    + +
    +
    +

    Home

    diff --git a/login/logout.php b/login/logout.php new file mode 100644 index 0000000000000..33a8192d1f74c --- /dev/null +++ b/login/logout.php @@ -0,0 +1,11 @@ + diff --git a/login/signup.php b/login/signup.php new file mode 100644 index 0000000000000..e6ba76b66d734 --- /dev/null +++ b/login/signup.php @@ -0,0 +1,129 @@ +password = md5($user->password); + $user->confirmed = 0; + $user->firstaccess = time(); + + if (! ($user->id = insert_record("user", $user)) ) { + error("Could not add your record to the database!"); + } + + if (! send_confirmation_email($user)) { + error("Tried to send you an email but failed!"); + } + + print_header("Check your email", "Check your email", "Confirm", ""); + include("signup_confirm.html"); + exit; + } + } + + if ($err) { + foreach ((array)$err as $key => $value) { + $focus = "form.$key"; + } + } + + print_header("New account", "New account", + "Login -> New Account", $focus); + + include("signup_form.php"); + + + +/****************************************************************************** + * FUNCTIONS + *****************************************************************************/ + +function validate_form($user, &$err) { + + if (empty($user->username)) + $err->username = "Missing username"; + + else if (record_exists("user", "username", $user->username)) + $err->username = "This username already exists, choose another"; + + else { + $string = eregi_replace("[^([:alnum:])]", "", $user->username); + if (strcmp($user->username, $string)) + $err->username = "Must only contain alphabetical characters"; + } + + + if (empty($user->password)) + $err->password = "Missing password"; + + if (empty($user->firstname)) + $err->firstname = "Missing first name"; + + if (empty($user->lastname)) + $err->lastname = "Missing last name"; + + + if (empty($user->email)) + $err->email = "Missing email address"; + + else if (! validate_email($user->email)) + $err->email = "Invalid email address, check carefully"; + + else if (record_exists("user", "email", $user->email)) + $err->email = "Email address already registered. New password?"; + + + if (empty($user->phone)) + $err->phone = "Missing phone number"; + + if (empty($user->city)) + $err->city = "Missing city"; + + if (empty($user->country)) + $err->country = "Missing country"; + + return; +} + + +function send_confirmation_email($user) { + + global $CFG; + + $site = get_site(); + $from = get_admin(); + + $message = "Hi $user->firstname,\n\n"; + + $message .= "A new account has been requested at '$site->fullname'\n"; + $message .= "using your email address.\n\n"; + + $message .= "To confirm your new account, please go to the \n"; + $message .= "following web address:\n\n"; + + $message .= "$CFG->wwwroot/login/confirm.php?x=$user->id&s=$user->username\n\n"; + + $message .= "In most mail programs, this should appear as a blue link\n"; + $message .= "which you can just click on. If that doesn't work, \n"; + $message .= "then cut and paste the address into the address\n"; + $message .= "line at the top of your web browser window.\n\n"; + + $message .= "Cheers from the '$site->fullname' administrator,\n"; + $message .= "$from->firstname $from->lastname ($from->email)\n"; + + $subject = "$site->fullname account confirmation"; + + return email_to_user($user, $from, $subject, $message); + +} + + + +?> diff --git a/login/signup_confirm.html b/login/signup_confirm.html new file mode 100644 index 0000000000000..a4a8203c29a87 --- /dev/null +++ b/login/signup_confirm.html @@ -0,0 +1,19 @@ +
    + +

    Thanks, firstname) ?>

    +

    Your new account is almost finished

    + +

    Please check your email!

    + +

    +An email has been sent to your address at email) ?> + +

    +

    +It contains easy instructions to confirm your new account. +

    + +
    +
    +

    Home

    + diff --git a/login/signup_form.php b/login/signup_form.php new file mode 100644 index 0000000000000..cd41e77168f83 --- /dev/null +++ b/login/signup_form.php @@ -0,0 +1,77 @@ +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    Create a new username and password to log in with:

    New username:

    + username) ?> +

    New password:

    + password) ?> +

    Please supply some information about yourself:

    First name:

    + firstname) ?> +

    Last name:

    + lastname) ?> +

    Curtin ID Number:

    (optional) + idnumber) ?> +

    Email address:

    + email) ?> +

    Phone number:

    + phone) ?> +

    City or town:

    + city) ?> +

    Country:

    country) ?> + country) ?> +
    +
    + +
    + +
    +
    +

    Home

    + diff --git a/mod/assignment/README b/mod/assignment/README new file mode 100644 index 0000000000000..0e47ab9707cd7 --- /dev/null +++ b/mod/assignment/README @@ -0,0 +1,6 @@ +Describes the assignment (eg an essay) that needs to be completed +then collects and datestamps it. Later, shows the grade. + +Teacher view, show class list, allows download and grades. + + diff --git a/mod/assignment/module.php b/mod/assignment/module.php new file mode 100644 index 0000000000000..9f60400b99916 --- /dev/null +++ b/mod/assignment/module.php @@ -0,0 +1,10 @@ + + diff --git a/mod/choice/icon.gif b/mod/choice/icon.gif new file mode 100644 index 0000000000000..5ee2c50a1fa2d Binary files /dev/null and b/mod/choice/icon.gif differ diff --git a/mod/choice/index.php b/mod/choice/index.php new file mode 100644 index 0000000000000..51f07adaa1454 --- /dev/null +++ b/mod/choice/index.php @@ -0,0 +1,62 @@ +id); + + add_to_log("View list of all choices", $course->id); + + print_header("$course->shortname: Choices", "$course->fullname", + "id>$course->shortname -> Choices", ""); + + + if (! $choices = get_all_instances_in_course("choice", $course->id, "cw.week ASC")) { + notice("There are no choices", "../../course/view.php?id=$course->id"); + } + + if ( $allanswers = get_records_sql("SELECT * FROM choice_answers WHERE user='$USER->id'")) { + foreach ($allanswers as $aa) { + $answers[$aa->choice] = $aa; + } + + } else { + $answers = array () ; + } + + + $timenow = time(); + + $table->head = array ("Week", "Question", "Answer"); + $table->align = array ("CENTER", "LEFT", "CENTER"); + + foreach ($choices as $choice) { + $answer = $answers[$choice->id]; + switch ($answer->answer) { + case 1: + $aa = "$choice->answer1"; + break; + case 2: + $aa = "$choice->answer2"; + break; + default: + $aa = "Undecided"; + break; + } + + $table->data[] = array ("

    $choice->week

    ", + "

    coursemodule\">$choice->name

    ", + "

    $aa

    "); + } + print_table($table); + + print_footer($course); + + +?> + diff --git a/mod/choice/install.sql b/mod/choice/install.sql new file mode 100755 index 0000000000000..6e747a47efe06 --- /dev/null +++ b/mod/choice/install.sql @@ -0,0 +1,43 @@ +# phpMyAdmin MySQL-Dump +# version 2.2.1 +# http://phpwizard.net/phpMyAdmin/ +# http://phpmyadmin.sourceforge.net/ (download page) +# +# Host: localhost +# Generation Time: Nov 14, 2001 at 04:44 PM +# Server version: 3.23.36 +# PHP Version: 4.0.6 +# Database : `moodle` +# -------------------------------------------------------- + +# +# Table structure for table `choice` +# + +CREATE TABLE choice ( + id int(10) unsigned NOT NULL auto_increment, + course int(10) unsigned NOT NULL default '0', + name varchar(255) NOT NULL default '', + text text NOT NULL, + answer1 varchar(255) NOT NULL default 'Yes', + answer2 varchar(255) NOT NULL default 'No', + timemodified int(10) unsigned NOT NULL default '0', + PRIMARY KEY (id), + UNIQUE KEY id (id) +) TYPE=MyISAM; +# -------------------------------------------------------- + +# +# Table structure for table `choice_answers` +# + +CREATE TABLE choice_answers ( + id int(10) unsigned NOT NULL auto_increment, + choice int(10) unsigned NOT NULL default '0', + user int(10) unsigned NOT NULL default '0', + answer tinyint(4) NOT NULL default '0', + timemodified int(10) NOT NULL default '0', + PRIMARY KEY (id), + UNIQUE KEY id (id) +) TYPE=MyISAM; + diff --git a/mod/choice/mod.html b/mod/choice/mod.html new file mode 100644 index 0000000000000..2f592bd9f6dd5 --- /dev/null +++ b/mod/choice/mod.html @@ -0,0 +1,54 @@ +
    + + + + + + + + + + + + + + + + + + + + + + + +

    Choice Name:

    + +

    Choice Text:

    + +

    First choice:

    + "> +

    Second choice:

    + "> +
    + +
    + + + + + + + +
    +
    diff --git a/mod/choice/mod.php b/mod/choice/mod.php new file mode 100644 index 0000000000000..59374eb8d1752 --- /dev/null +++ b/mod/choice/mod.php @@ -0,0 +1,81 @@ +Execute("INSERT into choice + SET course = '$form->course', + name = '$form->name', + text = '$form->text', + answer1 = '$form->answer1', + answer2 = '$form->answer2', + timemodified = '$timenow'")) { + return 0; + } + + // Get it out again - this is the most compatible way to determine the ID + if ($rs = $db->Execute("SELECT id FROM choice + WHERE course = $form->course AND timemodified = '$timenow'")) { + return $rs->fields[0]; + } else { + return 0; + } +} + + +function update_instance($form) { +// Given an object containing all the necessary data, +// (defined by the form in mod.html) this function +// will update an existing instance with new data. +// + GLOBAL $db; + + $timenow = time(); + + if (!$rs = $db->Execute("UPDATE choice + SET course = '$form->course', + name = '$form->name', + text = '$form->text', + answer1 = '$form->answer1', + answer2 = '$form->answer2', + timemodified = '$timenow' + WHERE id = '$form->instance' ")) { + return false; + } + return true; +} + + +function delete_instance($id) { +// Given an ID of an instance of this module, +// this function will permanently delete the instance +// and any data that depends on it. +// + GLOBAL $db; + + if (!$rs = $db->Execute("DELETE from choice WHERE id = '$id' ")) { + return false; + } + + return true; + +} + + +?> diff --git a/mod/choice/module.php b/mod/choice/module.php new file mode 100644 index 0000000000000..6256b8dc33486 --- /dev/null +++ b/mod/choice/module.php @@ -0,0 +1,14 @@ +fullname = "Choice"; + $module->version = "20011110"; + $module->cron = 0; + $module->search = ""; + +?> + diff --git a/mod/choice/report.php b/mod/choice/report.php new file mode 100644 index 0000000000000..7c4049c9b7907 --- /dev/null +++ b/mod/choice/report.php @@ -0,0 +1,94 @@ +course)) { + error("Course module is misconfigured"); + } + + require_login($course->id); + + if (!isteacher($course->id)) { + error("Only teachers can look at this page"); + } + + if (! $choice = get_record("choice", "id", $cm->instance)) { + error("Course module is incorrect"); + } + + add_to_log("View choices report", $course->id); + + print_header("$course->shortname: $choice->name: Responses", "$course->fullname", + "id>$course->shortname -> + id>Choices -> + id>$choice->name -> Responses", ""); + + + if (! $participants = get_records_sql("SELECT u.* FROM user u, user_students s, user_teachers t + WHERE (s.course = '$course->id' AND s.user = u.id) + OR (t.course = '$course->id' AND t.user = u.id) + ORDER BY u.lastaccess DESC")) { + + notify("No participants (strange)", "/course/view.php?id=$course->id"); + die; + } + + if ( $allanswers = get_records_sql("SELECT * FROM choice_answers WHERE choice='$choice->id'")) { + foreach ($allanswers as $aa) { + $answers[$aa->user] = $aa; + } + + } else { + $answers = array () ; + } + + + + $timenow = time(); + + echo ""; + foreach ($participants as $user) { + $answer = $answers[$user->id]; + + echo ""; + + echo ""; + + echo ""; + echo ""; + + echo ""; + } + echo "
    body\" WIDTH=35 VALIGN=TOP>"; + print_user_picture($user->id, $course->id, $user->picture); + echo "cellheading\">$user->firstname $user->lastname

     "; + if ($answer->timemodified) { + echo moodledate($answer->timemodified); + } + + echo "

    cellcontent\">

    "; + switch ($answer->answer) { + case 1: + echo "$choice->answer1"; + break; + case 2: + echo "$choice->answer2"; + break; + default: + echo "Undecided"; + break; + + } + echo "

    "; + + print_footer($course); + + +?> + diff --git a/mod/choice/view.html b/mod/choice/view.html new file mode 100644 index 0000000000000..286232c422fc3 --- /dev/null +++ b/mod/choice/view.html @@ -0,0 +1,19 @@ +
    +

    +

    + + + +
    + > + answer1) ?> + + > + answer2) ?> +
    +

    + + +

    +
    +
    diff --git a/mod/choice/view.php b/mod/choice/view.php new file mode 100644 index 0000000000000..901d92831327e --- /dev/null +++ b/mod/choice/view.php @@ -0,0 +1,92 @@ +course)) { + error("Course is misconfigured"); + } + + require_login($course->id); + + if (! $choice = get_record("choice", "id", $cm->instance)) { + error("Course module is incorrect"); + } + + if ($current = get_record_sql("SELECT * FROM choice_answers + WHERE choice='$choice->id' AND user='$USER->id'")) { + if ($current->answer == "1") { + $answer1checked = "CHECKED"; + } else if ($current->answer == "2") { + $answer2checked = "CHECKED"; + } + } + + if (match_referer() && isset($HTTP_POST_VARS)) { // form submitted + $form = (object)$HTTP_POST_VARS; + if ($current) { + add_to_log("Update choice: $choice->name", $course->id); + if (! update_choice_in_database($current, $form->answer)) { + error("Could not update your choice"); + } + } else { + add_to_log("Save choice: $choice->name", $course->id); + if (! add_new_choice_to_database($choice, $form->answer)) { + error("Could not save your choice"); + } + } + redirect("$CFG->wwwroot/course/view.php?id=$course->id"); + exit; + } + + add_to_log("View choice: $choice->name", $course->id); + print_header("$course->shortname: $choice->name", "$course->fullname", + "id>$course->shortname -> + id>Choices -> $choice->name", ""); + + if ($USER->editing) { + print_update_module_icon($cm->id); + } + + if (isteacher($course->id)) { + echo "

    id\">View all responses

    "; + } + + print_simple_box( text_to_html($choice->text) , "center"); + + require("view.html"); + + print_footer($course); + + + +// Functions ///////////////////////////////////////////////// + +function add_new_choice_to_database($choice, $answer) { + global $db; + global $USER; + + $timenow = time(); + + $rs = $db->Execute("INSERT INTO choice_answers (choice, user, answer, timemodified) + VALUES ( '$choice->id', '$USER->id', '$answer', '$timenow')"); + return $rs; +} + +function update_choice_in_database($current, $answer) { + global $db; + + $timenow = time(); + + $rs = $db->Execute("UPDATE choice_answers + SET answer='$answer', timemodified='$timenow' + WHERE id = '$current->id'"); + return $rs; +} + +?> diff --git a/mod/journal/edit.html b/mod/journal/edit.html new file mode 100644 index 0000000000000..b554b4f1b25b1 --- /dev/null +++ b/mod/journal/edit.html @@ -0,0 +1,10 @@ +
    +
    + +

    + + + +

    +
    +
    diff --git a/mod/journal/edit.php b/mod/journal/edit.php new file mode 100644 index 0000000000000..ea2cfc3cacf91 --- /dev/null +++ b/mod/journal/edit.php @@ -0,0 +1,75 @@ +course)) { + error("Course is misconfigured"); + } + + require_login($course->id); + + if (! $journal = get_record("journal", "id", $cm->instance)) { + error("Course module is incorrect"); + } + + $entry = get_record_sql("SELECT * FROM journal_entries + WHERE user='$USER->id' AND journal='$journal->id'"); + + +/// If data submitted, then process and store. + + if (match_referer() && isset($HTTP_POST_VARS)) { + + $timenow = time(); + + if ($entry) { + $newentry->id = $entry->id; + $newentry->text = $text; + $newentry->modified = $timenow; + if (! update_record("journal_entries", $newentry)) { + error("Could not update your journal"); + } + add_to_log("Update journal: $journal->name", $course->id); + } else { + $newentry->user = $USER->id; + $newentry->journal = $journal->id; + $newentry->modified = $timenow; + $newentry->text = $text; + if (! insert_record("journal_entries", $newentry)) { + error("Could not insert a new journal entry"); + } + add_to_log("Add journal: $journal->name", $course->id); + } + + redirect("view.php?id=$cm->id"); + die; + } + +/// Otherwise fill and print the form. + + if (! $entry ) { + $entry->text = ""; + } + + print_header("$course->shortname: $journal->name", "$course->fullname", + "id>$course->shortname -> + id>Journals -> + id\">$journal->name -> Edit", "form.text"); + + echo "
    \n"; + + print_simple_box( text_to_html($journal->intro) , "center"); + + echo "
    "; + + include("edit.html"); + + print_footer($course); + +?> diff --git a/mod/journal/icon.gif b/mod/journal/icon.gif new file mode 100755 index 0000000000000..a32d346e0c2f5 Binary files /dev/null and b/mod/journal/icon.gif differ diff --git a/mod/journal/index.php b/mod/journal/index.php new file mode 100644 index 0000000000000..014f249e21c0e --- /dev/null +++ b/mod/journal/index.php @@ -0,0 +1,60 @@ +id); + add_to_log("View all journals", $course->id); + + print_header("$course->shortname: Journals", "$course->fullname", + "id>$course->shortname -> Journals", ""); + + + if (! $journals = get_all_instances_in_course("journal", $course->id, "cw.week ASC")) { + notice("There are no journals", "../../course/view.php?id=$course->id"); + die; + } + + $timenow = time(); + + $table->head = array ("Week", "Question", "Answer"); + $table->align = array ("CENTER", "LEFT", "LEFT"); + + foreach ($journals as $journal) { + + $entry = get_record_sql("SELECT text FROM journal_entries + WHERE user='$USER->id' AND journal='$journal->id'"); + + $journal->timestart = $course->startdate + (($journal->week - 1) * 608400); + if ($journal->daysopen) { + $journal->timefinish = $journal->timestart + (3600 * 24 * $journal->daysopen); + } else { + $journal->timefinish = 9999999999; + } + $journalopen = ($journal->timestart < $timenow && $timenow < $journal->timefinish); + + + $text = text_to_html($entry->text)."

    coursemodule\">"; + if ($journalopen) { + $text .= "Edit

    "; + } else { + $text .= "View

    "; + } + $table->data[] = array ("$journal->week", + text_to_html($journal->intro), + $text); + } + + print_table($table); + + print_footer($course); + + +?> + diff --git a/mod/journal/install.sql b/mod/journal/install.sql new file mode 100755 index 0000000000000..d33e36f502c95 --- /dev/null +++ b/mod/journal/install.sql @@ -0,0 +1,45 @@ +# phpMyAdmin MySQL-Dump +# version 2.2.1 +# http://phpwizard.net/phpMyAdmin/ +# http://phpmyadmin.sourceforge.net/ (download page) +# +# Host: localhost +# Generation Time: Nov 14, 2001 at 04:44 PM +# Server version: 3.23.36 +# PHP Version: 4.0.6 +# Database : `moodle` +# -------------------------------------------------------- + +# +# Table structure for table `journal` +# + +CREATE TABLE journal ( + id int(10) unsigned NOT NULL auto_increment, + course int(10) unsigned NOT NULL default '0', + name varchar(255) default NULL, + intro text, + days smallint(5) unsigned NOT NULL default '7', + assessed tinyint(1) NOT NULL default '1', + timemodified int(10) unsigned NOT NULL default '0', + PRIMARY KEY (id) +) TYPE=MyISAM; +# -------------------------------------------------------- + +# +# Table structure for table `journal_entries` +# + +CREATE TABLE journal_entries ( + id int(10) unsigned NOT NULL auto_increment, + journal int(10) unsigned NOT NULL default '0', + user int(10) unsigned NOT NULL default '0', + modified int(10) unsigned NOT NULL default '0', + text text NOT NULL, + rating tinyint(4) default '0', + comment text, + teacher int(10) unsigned NOT NULL default '0', + timemarked int(10) unsigned NOT NULL default '0', + PRIMARY KEY (id) +) TYPE=MyISAM COMMENT='All the journal entries of all people'; + diff --git a/mod/journal/lib.php b/mod/journal/lib.php new file mode 100644 index 0000000000000..952b36b9ee630 --- /dev/null +++ b/mod/journal/lib.php @@ -0,0 +1,11 @@ + "Outstanding", + "2" => "Satisfactory", + "1" => "Not satisfactory"); + +function journaldate($date) { + return date("l, j F Y, g:i A T", $date); +} + +?> diff --git a/mod/journal/mod.html b/mod/journal/mod.html new file mode 100644 index 0000000000000..6ee349e1581d9 --- /dev/null +++ b/mod/journal/mod.html @@ -0,0 +1,46 @@ +
    + + + + + + + + + + + + + + +

    Journal Name:

    + +

    Journal Question:

    + +

    Days available:

    + days == "") { + $form->days == "14"; + } + choose_from_menu($options, "days", "$form->days"); + ?> +
    +
    + + + + + + + +
    +
    diff --git a/mod/journal/mod.php b/mod/journal/mod.php new file mode 100644 index 0000000000000..7de31ca56b997 --- /dev/null +++ b/mod/journal/mod.php @@ -0,0 +1,83 @@ +Execute("INSERT into journal + SET course = '$form->course', + name = '$form->name', + intro = '$form->intro', + days = '$form->days', + timemodified = '$timenow'")) { + return 0; + } + + // Get it out again - this is the most compatible way to determine the ID + if ($rs = $db->Execute("SELECT id FROM journal + WHERE course = $form->course AND timemodified = '$timenow'")) { + return $rs->fields[0]; + } else { + return 0; + } +} + + +function update_instance($form) { +// Given an object containing all the necessary data, +// (defined by the form in mod.html) this function +// will update an existing instance with new data. +// + GLOBAL $db; + + $timenow = time(); + + if (!$rs = $db->Execute("UPDATE journal + SET course = '$form->course', + name = '$form->name', + intro = '$form->intro', + days = '$form->days', + timemodified = '$timenow' + WHERE id = '$form->instance' ")) { + return false; + } + return true; +} + + +function delete_instance($id) { +// Given an ID of an instance of this module, +// this function will permanently delete the instance +// and any data that depends on it. +// + GLOBAL $db; + + if (!$rs = $db->Execute("DELETE from journal_entries WHERE journal = '$id' ")) { + return false; + } + + if (!$rs = $db->Execute("DELETE from journal WHERE id = '$id' ")) { + return false; + } + + return true; + +} + + +?> diff --git a/mod/journal/module.php b/mod/journal/module.php new file mode 100644 index 0000000000000..608216d1d322d --- /dev/null +++ b/mod/journal/module.php @@ -0,0 +1,14 @@ +fullname = "Journal"; + $module->version = "20011110"; + $module->cron = 0; + $module->search = ""; + +?> + diff --git a/mod/journal/new.php b/mod/journal/new.php new file mode 100644 index 0000000000000..fe00064270692 --- /dev/null +++ b/mod/journal/new.php @@ -0,0 +1,39 @@ +id)) { + + foreach ($journals as $journal) { + if ($entry = get_record_sql("SELECT * FROM journal_entries + WHERE timemarked > '$USER->lastlogin' + AND journal = '$journal->id' + AND user = $USER->id")) { + echo "

    Journal feedback: $journal->name

    "; + echo ""; + } + + if ($entries = get_records_sql("SELECT j.*, u.id as userid, u.firstname, u.lastname + FROM journal_entries j, user u + WHERE modified > '$USER->lastlogin' + AND journal = '$journal->id' + AND j.user = u.id ORDER by j.modified")) { + echo "

    Journal entries: coursemodule\">$journal->name

    "; + echo "
      "; + foreach ($entries as $entry) { + echo "
    • $entry->firstname $entry->lastname edited their journal"; + echo ", ".moodledate($entry->modified); + } + echo "
    "; + } + } + } + +?> diff --git a/mod/journal/report.php b/mod/journal/report.php new file mode 100644 index 0000000000000..3ae4513a005f7 --- /dev/null +++ b/mod/journal/report.php @@ -0,0 +1,151 @@ +course)) { + error("Course module is misconfigured"); + } + + require_login($course->id); + + if (!isteacher($course->id)) { + error("Only teachers can look at this page"); + } + + if (! $journal = get_record("journal", "id", $cm->instance)) { + error("Course module is incorrect"); + } + + // make some easy ways to access the entries. + if ( $eee = get_records_sql("SELECT * FROM journal_entries WHERE journal='$journal->id'")) { + foreach ($eee as $ee) { + $entrybystudent[$ee->user] = $ee; + $entrybyentry[$ee->id] = $ee; + } + + } else { + $entrybystudent = array () ; + $entrybyentry = array () ; + } + + print_header("$course->shortname: Journals", "$course->fullname", + "id>$course->shortname -> + id>Journals -> + id>$journal->name -> Responses", "", + "", true); + + if (match_referer() && isset($HTTP_POST_VARS)) { // Feedback submitted + + $feedback = array(); + + // Peel out all the data from variable names. + foreach ($HTTP_POST_VARS as $key => $val) { + if ($key <> "id") { + $type = substr($key,0,1); + $num = substr($key,1); + $feedback[$num][$type] = $val; + } + } + + $timenow = time(); + $count = 0; + foreach ($feedback as $num => $vals) { + $entry = $entrybyentry[$num]; + // Only update entries where feedback has actually changed. + if (($vals[r] <> $entry->rating) || ($vals[c] <> addslashes($entry->comment))) { + if (!$rs = $db->Execute("UPDATE journal_entries + SET rating='$vals[r]', comment='$vals[c]', + teacher='$USER->id', timemarked='$timenow' + WHERE id = '$num'")) { + error("Failed to update the journal feedback!"); + } + $entrybystudent[$entry->user]->comment = $vals[c]; + $entrybystudent[$entry->user]->rating = $vals[r]; + $entrybystudent[$entry->user]->timemarked = $timenow; + $entrybystudent[$entry->user]->teacher = $USER->id; + $count++; + } + } + add_to_log("Updated journal feedback: $journal->name ($count students)", $course->id); + notify("Journal feedback updated for $count students."); + } else { + add_to_log("View journal responses: $journal->name", $course->id); + } + + + if (! $students = get_records_sql("SELECT u.* FROM user u, user_students s + WHERE s.course = '$course->id' AND s.user = u.id + ORDER BY u.lastaccess DESC")) { + notify("No students", "/course/view.php?id=$course->id"); + die; + } + + if (! $teachers = get_records_sql("SELECT u.* FROM user u, user_teachers t + WHERE t.course = '$course->id' AND t.user = u.id + ORDER BY u.lastaccess DESC")) { + notify("No teachers", "/course/view.php?id=$course->id"); + die; + } + + echo "
    \n"; + foreach ($students as $student) { + $entry = $entrybystudent[$student->id]; + + echo "\n"; + + echo "\n"; + echo "\n"; + echo ""; + + echo "\n"; + + if ($entry) { + echo "\n"; + echo ""; + } + echo "
    body\" WIDTH=35 VALIGN=TOP>"; + print_user_picture($student->id, $course->id, $student->picture); + echo "cellheading\">$student->firstname $student->lastname"; + if ($entry) { + echo "  Last edited: ".journaldate($entry->modified).""; + } + echo "
    cellcontent\">"; + if ($entry) { + echo text_to_html($entry->text); + } else { + echo "No entry"; + } + echo "
    "; + if (!$entry->teacher) { + $entry->teacher = $USER->id; + } + print_user_picture($entry->teacher, $course->id, $teachers[$entry->teacher]->picture); + echo "cellheading\">Teacher Feedback:"; + choose_from_menu($RATING, "r$entry->id", $entry->rating, "Rate..."); + if ($entry->timemarked) { + echo "  ".journaldate($entry->timemarked).""; + } + echo "

    "; + echo "

    \n"; + + } + echo "
    "; + echo "id\">"; + echo ""; + echo "
    "; + echo "
    "; + + print_footer($course); + + +?> + diff --git a/mod/journal/user.php b/mod/journal/user.php new file mode 100644 index 0000000000000..7e12fb2770367 --- /dev/null +++ b/mod/journal/user.php @@ -0,0 +1,67 @@ +dirroot/mod/journal/lib.php"); + + if (! $journals = get_all_instances_in_course("journal", $course->id)) { + notify("There are no journals"); + die; + } + + if (! $teachers = get_records_sql("SELECT u.* FROM user u, user_teachers t + WHERE t.course = '$course->id' AND t.user = u.id + ORDER BY u.lastaccess DESC")) { + notify("No teachers"); + die; + } + + $timenow = time(); + + echo ""; + echo ""; + foreach ($journals as $journal) { + + $journal->timestart = $course->startdate + (($journal->week - 1) * 608400); + if ($journal->daysopen) { + $journal->timefinish = $journal->timestart + (3600 * 24 * $journal->daysopen); + } else { + $journal->timefinish = 9999999999; + } + + $entry = get_record_sql("SELECT * FROM journal_entries + WHERE user='$user->id' AND journal='$journal->id'"); + + if ($entry->text) { + echo ""; + $journalopen = ($journal->timestart < $timenow && $timenow < $journal->timefinish); + if ($journalopen) { + echo ""; + echo ""; + } + } + echo "
    WeekJournal
    cellheading2\">"; + } else { + echo "cellheading\">"; + } + echo "$journal->weekcellcontent\">"; + echo "

    wwwroot/mod/journal/view.php?id=$journal->coursemodule\">$journal->name

    "; + echo text_to_html($entry->text); + if ($entry->teacher) { + echo "\n
    "; + echo "
    "; + print_user_picture($entry->teacher, $course->id, $teachers[$entry->teacher]->picture); + echo "cellheading\">".$RATING[$entry->rating]; + if ($entry->timemarked) { + echo "  ".moodledate($entry->timemarked).""; + } + echo "
    "; + echo text_to_html($entry->comment); + echo "
    "; + echo "
    "; + } + echo "
    "; + +?> + diff --git a/mod/journal/view.php b/mod/journal/view.php new file mode 100644 index 0000000000000..75d603ac963b2 --- /dev/null +++ b/mod/journal/view.php @@ -0,0 +1,141 @@ +course)) { + error("Course is misconfigured"); + } + + require_login($course->id); + + + if (! $journal = get_record("journal", "id", $cm->instance)) { + error("Course module is incorrect"); + } + + add_to_log("View journal: $journal->name", $course->id); + + if (! $cw = get_record("course_weeks", "id", $cm->week)) { + error("Course module is incorrect"); + } + + print_header("$course->shortname: $journal->name", "$course->fullname", + "id>$course->shortname -> + id>Journals -> $journal->name", ""); + + if ($USER->editing) { + print_update_module_icon($cm->id); + } + + if (isteacher($course->id)) { + echo "

    id\">View all responses

    "; + } + + echo "
    \n"; + + print_simple_box( text_to_html($journal->intro) , "center"); + + echo "
    "; + + $timenow = time(); + $timestart = $course->startdate + (($cw->week - 1) * 608400); + if ($journal->days) { + $timefinish = $timestart + (3600 * 24 * $journal->days); + } else { + $timefinish = $course->enddate; + } + + if ($timenow > $timestart) { + + + print_simple_box_start("center"); + + if ($timenow < $timefinish) { + $options = array ("id" => "$cm->id"); + echo "
    "; + print_single_button("edit.php", $options, "Start or edit my journal entry"); + echo "
    "; + } + + if ($entry = get_record_sql("SELECT * FROM journal_entries + WHERE user='$USER->id' AND journal='$journal->id'")) { + + if (empty($entry->text)) { + echo "

    Blank entry

    "; + } else { + echo text_to_html($entry->text); + } + + } else { + echo "You have not started this journal yet."; + } + + print_simple_box_end(); + + if ($timenow < $timefinish) { + if ($entry->modified) { + echo "

    Last edited: "; + echo journaldate($entry->modified)."

    "; + } + if ($journal->days) { + echo "

    Editing period ends: "; + echo journaldate($timefinish)."

    "; + } + } else { + echo "

    Editing period has ended: "; + echo journaldate($timefinish)."

    "; + } + + if ($entry->comment || $entry->rating) { + print_heading("Feedback"); + print_feedback($course, $entry); + } + + + } else { + echo "

    This journal won't be open until: "; + echo journaldate($timestart)."

    "; + } + + print_footer($course); + +// Functions + +function print_feedback($course, $entry) { + global $CFG, $THEME, $RATING; + + if (! $teacher = get_record("user", "id", $entry->teacher)) { + error("Weird journal error"); + } + + echo "\n"; + + echo "\n"; + echo "\n"; + echo ""; + + echo "\n
    body\" WIDTH=35 VALIGN=TOP>"; + print_user_picture($teacher->id, $course->id, $teacher->picture); + echo "cellheading\">$teacher->firstname $teacher->lastname"; + echo "  ".journaldate($entry->timemarked).""; + echo "
    cellcontent\">"; + echo "

    Overall: "; + if ($RATING[$entry->rating]) { + echo $RATING[$entry->rating]; + } else { + echo "Error"; + } + echo "

    "; + echo text_to_html($entry->comment); + echo "
    "; + +} + +?> diff --git a/mod/survey/details.php b/mod/survey/details.php new file mode 100644 index 0000000000000..189d796fd3edd --- /dev/null +++ b/mod/survey/details.php @@ -0,0 +1,63 @@ +course)) { + error("This course doesn't exist"); + } + + require_login($course->id); + + if (!isteacher($course->id)) { + error("You can't modify this course!"); + } + + print_header("$course->shortname: Editing a survey", "$course->shortname: Editing a survey", + "wwwroot/course/view.php?id=$course->id\">$course->shortname -> + Editing a survey", ""); + + print_simple_box_start("center", "", "$THEME->cellheading"); + ?> +
    + + + + + + + + +

    Name:

    name) ?>

    +

    Introduction Text:

    +
    + +
    + + + + + + + + + +
    + +
    +
    + diff --git a/mod/survey/download.php b/mod/survey/download.php new file mode 100644 index 0000000000000..83fc25635b05e --- /dev/null +++ b/mod/survey/download.php @@ -0,0 +1,214 @@ +course)) { + error("Course is misconfigured"); + } + + require_login($course->id); + + if (!isteacher($course->id)) { + error("Sorry, only teachers can see this."); + } + + if (! $survey = get_record("survey", "id", $cm->instance)) { + error("Survey ID was incorrect"); + } + + add_to_log("Downloaded report as $type: $survey->name", $course->id); + + +// Get all the questions and their proper order + + $questions = get_records_sql("SELECT * FROM survey_questions WHERE id in ($survey->questions)"); + $order = explode(",", $survey->questions); + + foreach ($order as $key => $qid) { // Do we have virtual scales? + $question = $questions[$qid]; + if ($question->type < 0) { + $virtualscales = true; + break; + } + } + + $fullorderlist = ""; + foreach ($order as $key => $qid) { // build up list of actual questions + $question = $questions[$qid]; + + if (!(empty($fullorderlist))) { + $fullorderlist .= ","; + } + + if ($question->multi) { + $addlist = $question->multi; + } else { + $addlist = $qid; + } + + if ($virtualscales && ($question->type < 0)) { // only use them + $fullorderlist .= $addlist; + + } else if (!$virtualscales && ($question->type >= 0)){ // ignore them + $fullorderlist .= $addlist; + } + } + + $fullquestions = get_records_sql("SELECT * FROM survey_questions WHERE id in ($fullorderlist)"); + +// Question type of multi-questions overrides the type of single questions + foreach ($order as $key => $qid) { + $question = $questions[$qid]; + + if ($question->multi) { + $subs = explode(",", $question->multi); + while (list ($skey, $sqid) = each ($subs)) { + $fullquestions["$sqid"]->type = $question->type; + } + } + } + + $order = explode(",", $fullorderlist); + $questions = $fullquestions; + + +// Get and collate all the results in one big array + + if (! $aaa = get_records("survey_answers", "survey", "$survey->id", "time ASC")) { + error("There are no answers for this survey yet."); + } + + foreach ($aaa as $a) { + if (!$results["$a->user"]) { // init new array + $results["$a->user"]["time"] = $a->time; + foreach ($order as $key => $qid) { + $results["$a->user"]["$qid"]["answer1"] = ""; + $results["$a->user"]["$qid"]["answer2"] = ""; + } + } + $results["$a->user"]["$a->question"]["answer1"] = $a->answer1; + $results["$a->user"]["$a->question"]["answer2"] = $a->answer2; + } + + +// Output the file as a valid Excel spreadsheet if required + + if ($type == "xls") { + include( "$CFG->libdir/psxlsgen.php" ); + + + $myxls = new PhpSimpleXlsGen(); + $myxls->totalcol = count($order) + 100; + $header = array("surveyid","surveyname","userid","firstname","lastname","email","idnumber","time", "notes"); + $myxls->ChangePos(0,0); + foreach ($header as $item) { + $myxls->InsertText($item); + } + foreach ($order as $key => $qid) { + $question = $questions["$qid"]; + if ($question->type == "0" || $question->type == "1" || $question->type == "3") { + $myxls->InsertText("$question->text"); + } + if ($question->type == "2" || $question->type == "3") { + $myxls->InsertText("$question->text (preferred)"); + } + } + + $i = 0; + foreach ($results as $user => $rest) { + $i++; + $myxls->ChangePos($i,0); + if (! $u = get_record("user", "id", $user)) { + error("Error finding student # $user"); + } + if ($n = get_record_sql("SELECT * FROM survey_analysis WHERE survey='$survey->id' AND user='$user'")) { + $notes = $n->notes; + } else { + $notes = "No notes made"; + } + $myxls->InsertText($survey->id); + $myxls->InsertText($survey->name); + $myxls->InsertText($user); + $myxls->InsertText($u->firstname); + $myxls->InsertText($u->lastname); + $myxls->InsertText($u->email); + $myxls->InsertText($u->idnumber); + $myxls->InsertText( date("d-M-Y h:i:s A", $results["$user"]["time"]) ); + $myxls->InsertText($notes); + + foreach ($order as $key => $qid) { + $question = $questions["$qid"]; + if ($question->type == "0" || $question->type == "1" || $question->type == "3") { + $myxls->InsertText( $results["$user"]["$qid"]["answer1"] ); + } + if ($question->type == "2" || $question->type == "3") { + $myxls->InsertText( $results["$user"]["$qid"]["answer2"] ); + } + } + } + $myxls->SendFile("surveyreport"); + + exit; + } + +// Otherwise, return the text file. + +// Print header to force download + + header("Content-Type: application/download\n"); + header("Content-Disposition: attachment; filename=\"$survey->name results.txt\""); + +// Print names of all the fields + + echo "surveyid surveyname userid firstname lastname email idnumber time "; + foreach ($order as $key => $qid) { + $question = $questions["$qid"]; + if ($question->type == "0" || $question->type == "1" || $question->type == "3") { + echo "$question->text "; + } + if ($question->type == "2" || $question->type == "3") { + echo "$question->text (preferred) "; + } + } + echo "\n"; + +// Print all the lines of data. + + foreach ($results as $user => $rest) { + if (! $u = get_record_sql("SELECT firstname,lastname,email,idnumber FROM user WHERE id = '$user'")) { + error("Error finding student # $user"); + } + echo $survey->id." "; + echo $survey->name." "; + echo $user." "; + echo $u->firstname." "; + echo $u->lastname." "; + echo $u->email." "; + echo $u->idnumber." "; + echo date("d-M-Y h:i:s A", $results["$user"]["time"])." "; + + foreach ($order as $key => $qid) { + $question = $questions["$qid"]; + if ($question->type == "0" || $question->type == "1" || $question->type == "3") { + echo $results["$user"]["$qid"]["answer1"]." "; + } + if ($question->type == "2" || $question->type == "3") { + echo $results["$user"]["$qid"]["answer2"]." "; + } + } + echo "\n"; + } + exit; + + +?> + diff --git a/mod/survey/edit.php b/mod/survey/edit.php new file mode 100644 index 0000000000000..40e97d3b89a62 --- /dev/null +++ b/mod/survey/edit.php @@ -0,0 +1,288 @@ +name\" was updated."); + add_to_log("Survey update: $survey->name"); + unset($edit); + } + } else { + notify("Error: This page was called wrongly."); + } + } + + if ($edit == "delete") { + if ($id) { + + if (get_responses_for_survey($id) > 0) { + notify("Could not delete survey as it has responses"); + + } else if ($ss = $db->Execute("DELETE FROM surveys WHERE owner = $USER->id AND id = $id")) { + notify("The survey was deleted."); + add_to_log("Survey deleted: $id"); + + } else { + notify("Serious error: could not find any templates."); + } + + } else { + notify("Serious error: could not find any templates."); + } + unset($edit); + } + + + if ($edit == "add") { + if ($template) { + if ($tt = $db->Execute("SELECT * FROM surveys WHERE id = $template")) { + $survey = (object)$tt->fields; + $survey->owner = $USER->id; + $survey->name = ""; + $survey->template = $template; + add_survey($survey); + add_to_log("Survey added: $survey->name"); + } else { + notify("Serious error: could not find template $template"); + } + } else { + unset($edit); + } + } + + if ($edit == "edit") { + if ($id) { + $survey = get_survey($id); + } else { + notify("Error: script called badly."); + die; + } + } + + if ($edit) { + $survey->status = get_survey_status($survey); + include("edit_form.phtml"); + + } else { + clean_up_surveys(); + print_list_of_my_surveys(); + print_additional_commands(); + } + print_footer($course); + + + + +/// FUNCTIONS ////////////////// + +function print_additional_commands() { + echo "

    Add a new survey

    "; +} + +function validate_form(&$survey, &$err) { + + if (empty($survey->id)) { + notify("A serious error occurred."); + die; + } + + if (empty($survey->name)) + $err["name"] = "Missing name"; + + if (empty($survey->password)) + $err["password"] = "Missing password"; + + else if ($survey->password == "changeme") + $err["password"] = "You should change this password"; + + settype($survey->days, "integer"); + + if ($survey->days < 0 || $survey->days > 365) + $err["days"] = "Must be a number between 0 and 365"; + + +} + +function add_survey(&$survey) { + + global $db, $USER; + + $timenow = time(); + + $survey->intro = addslashes($survey->intro); // to make sure + + $rs = $db->Execute("INSERT INTO surveys SET + timecreated = $timenow, + timemodified = $timenow, + template = '$survey->template', + course = '$survey->course', + owner = '$survey->owner', + name = '$survey->name', + password = '$survey->password', + intro = '$survey->intro', + url = '$survey->url', + questions = '$survey->questions' "); + + if (!$rs) { + notify("Could not insert a record"); + die; + } + +// Now get it out again (most compatible way to get the id) + + $rs = $db->Execute("SELECT * FROM surveys WHERE owner = $survey->owner AND timecreated = $timenow"); + if ($rs) { + $survey = (object) $rs->fields; + $survey->intro = stripslashes($survey->intro); + } else { + notify("Error: Could not find the record I just inserted!"); + die; + } +} + +function release_survey($id) { + + global $db; + + if ($ss = $db->Execute("SELECT * FROM surveys WHERE id = $id")) { + $survey = (object)$ss->fields; + } else { + notify("Serious error: could not find survey $id"); + die; + } + + $timenow = time(); + $timeend = $timenow + ($survey->days * 86400); + + if ($ss = $db->Execute("UPDATE surveys SET locked=1, timeopen = $timenow, timeclose = $timeend + WHERE id = $survey->id")) { + notify("The survey \"$survey->name\" was released and can no longer be edited."); + } else { + notify("An error occurred while releasing \"$survey->name\""); + } +} + + +function update_survey($survey) { + + global $db, $USER; + + $timenow = time(); + + $rs = $db->Execute("UPDATE surveys SET + timemodified = $timenow, + name = '$survey->name', + password = '$survey->password', + intro = '$survey->intro', + url = '$survey->url', + days = $survey->days + WHERE + id = $survey->id AND + owner = $USER->id "); + + if ($rs) { + return true; + } else { + notify("Could not update the survey!"); + die; + } +} + +function get_survey($id) { + global $db, $USER; + + if ($ss = $db->Execute("SELECT * FROM surveys WHERE id = $id AND owner = $USER->id")) { + $survey = (object)$ss->fields; + $survey->intro = stripslashes($survey->intro); + $survey->name = stripslashes($survey->name); + $survey->password = stripslashes($survey->password); + return $survey; + } else { + notify("Serious error: could not find specified survey."); + die; + } +} + + +function make_survey_menu($chosenid) { + global $db; + + $chosenname = get_template_name($chosenid); + if ($ss = $db->Execute("SELECT name,id FROM surveys WHERE owner = 0 ORDER BY id")) { + print $ss->GetMenu("template", $chosenname, true); + } else { + notify("Serious error: could not find any templates."); + } +} + +function clean_up_surveys() { + global $db, $USER; + + if (!$rs = $db->Execute("DELETE FROM surveys WHERE owner = $USER->id AND name = ''")) { + notify("Error: could not clean up surveys"); + } +} + + +function print_list_of_my_surveys() { + global $db, $USER; + + if ($rs = $db->Execute("SELECT * FROM surveys WHERE owner = $USER->id ORDER BY id")) { + if ($rs->RowCount()) { + echo "

    Existing surveys

    "; + echo ""; + echo ""; + while (!$rs->EOF) { + $survey = (object)$rs->fields; + + $numresponses = get_responses_for_survey($survey->id); + $templatename = get_template_name($survey->template); + $status = get_survey_status($survey); + + echo ""; + echo ""; + $rs->MoveNext(); + } + echo "

    Survey name

    Survey type

    Details

    Status

    id\">$survey->name"; + echo "

    $templatename"; + if ($status == "editing") { + echo "

    id\">Edit"; + echo "

    $status"; + echo "

    id\">Release to students"; + } else { + echo "

    id\">View"; + echo "

    $status"; + echo "

    id\">$numresponses responses"; + } + echo "

    "; + } else { + echo "

    You don't have any surveys yet

    "; + } + } else { + notify("Error: could not list surveys"); + } + +} + +?> diff --git a/mod/survey/edit_form.html b/mod/survey/edit_form.html new file mode 100644 index 0000000000000..00e8afeb41a79 --- /dev/null +++ b/mod/survey/edit_form.html @@ -0,0 +1,66 @@ +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    :

    template) ?> + +

    :

    + +

    :

    + +

    :

    + +

    :

    + +

    :

    +
    +
    + + + status == "editing") { ?> + + + + + id) == 0) { ?> +
    + + + +
    + +
    +
    + +
    +
    +
    + +
    + diff --git a/mod/survey/edit_form.phtml b/mod/survey/edit_form.phtml new file mode 100644 index 0000000000000..a0e5ebc57bcf1 --- /dev/null +++ b/mod/survey/edit_form.phtml @@ -0,0 +1,70 @@ +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    (Click on the links below
    for help on each field)

     

    Survey type:

    template) ?> + +

    :

    + +

    :

    + +

    :

    + +

    :

    + +

    :

    +
    +
    + + + status == "editing") { ?> + + + + + id) == 0) { ?> +
    + + + +
    + +
    +
    + +
    +
    +
    + +
    + diff --git a/mod/survey/edit_new.html b/mod/survey/edit_new.html new file mode 100644 index 0000000000000..ff37e05bc0451 --- /dev/null +++ b/mod/survey/edit_new.html @@ -0,0 +1,14 @@ +
    +

    Choose a template for your new survey

    + +

    + +
    + + + + +

    + +

    +
    diff --git a/mod/survey/edit_new.phtml b/mod/survey/edit_new.phtml new file mode 100644 index 0000000000000..9d86be8a7b202 --- /dev/null +++ b/mod/survey/edit_new.phtml @@ -0,0 +1,14 @@ +
    +

    Choose a template for your new survey

    + +

    + +
    + + + + +

    + +

    +
    diff --git a/mod/survey/graph.php b/mod/survey/graph.php new file mode 100644 index 0000000000000..1f41e97e64773 --- /dev/null +++ b/mod/survey/graph.php @@ -0,0 +1,620 @@ +libdir/graphlib.php"); + require("lib.php"); + + require_variable($id); // Course Module ID + + if (! $cm = get_record("course_modules", "id", $id)) { + error("Course Module ID was incorrect"); + } + + if (! $course = get_record("course", "id", $cm->course)) { + error("Course is misconfigured"); + } + + require_login($course->id); + + if (!isteacher($course->id)) { + error("Sorry, only teachers can see this."); + } + + if (! $survey = get_record("survey", "id", $cm->instance)) { + error("Survey ID was incorrect"); + } + + switch ($type) { + + case "question.png": + + $question = get_record("survey_questions", "id", $qid); + + $options = explode(",",$question->options); + + while (list($key,) = each($options)) { + $buckets1[$key] = 0; + $buckets2[$key] = 0; + } + + $aa = $db->Execute("SELECT * FROM survey_answers WHERE survey = $cm->instance AND question = $qid"); + + while (!$aa->EOF) { + if ($a1 = $aa->fields["answer1"]) { + $buckets1[$a1 - 1]++; + } + if ($a2 = $aa->fields["answer2"]) { + $buckets2[$a2 - 1]++; + } + $aa->MoveNext(); + } + + $maxbuckets1 = max($buckets1); + $maxbuckets2 = max($buckets2); + $maxbuckets = ($maxbuckets1 > $maxbuckets2) ? $maxbuckets1 : $maxbuckets2; + + $graph = new graph($GWIDTH,$GHEIGHT); + $graph->parameter['title'] = "$question->text"; + + $graph->x_data = $options; + + $graph->y_data['answers1'] = $buckets1; + $graph->y_format['answers1'] = array('colour' => 'blue','bar' => 'fill','legend' =>'actual','bar_size' => 0.4); + $graph->y_data['answers2'] = $buckets2; + $graph->y_format['answers2'] = array('colour'=>'green','bar' => 'fill','legend' =>'preferred','bar_size' => 0.2); + + $graph->parameter['legend'] = 'outside-top'; + $graph->parameter['legend_border'] = 'black'; + $graph->parameter['legend_offset'] = 4; + + if (($maxbuckets1 > 0.0) && ($maxbuckets2 > 0.0)) { + $graph->y_order = array('answers1', 'answers2'); + } else if ($maxbuckets1 > 0.0) { + $graph->y_order = array('answers1'); + } else { + $graph->y_order = array('answers2'); + } + + $graph->parameter['y_axis_gridlines']= $maxbuckets + 1; + $graph->parameter['y_resolution_left']= 1; + $graph->parameter['y_decimal_left'] = 0; + $graph->parameter['x_axis_angle'] = 0; + $graph->parameter['shadow'] = 'none'; + + $graph->draw_stack(); + + break; + + + + case "multiquestion.png": + + $question = get_record("survey_questions", "id", $qid); + + $options = explode(",",$question->options); + $questionorder = explode( ",", $question->multi); + + $qqq = get_records_sql("SELECT * FROM survey_questions WHERE id in ($question->multi)"); + + foreach ($questionorder as $i => $val) { + $names[$i] = shorten_name($qqq["$val"]->text, 4); + $buckets1[$i] = 0; + $buckets2[$i] = 0; + $count1[$i] = 0; + $count2[$i] = 0; + $indexof[$val] = $i; + } + + $aaa = get_records_sql("SELECT * FROM survey_answers WHERE ((survey = $cm->instance) AND (question in ($question->multi)))"); + + foreach ($aaa as $a) { + $index = $indexof[$a->question]; + if ($a->answer1) { + $buckets1[$index] += $a->answer1; + $count1[$index]++; + } + if ($a->answer2) { + $buckets2[$index] += $a->answer2; + $count2[$index]++; + } + } + + foreach ($questionorder as $i => $val) { + if ($count1[$i]) { + $buckets1[$i] = (float)$buckets1[$i] / (float)$count1[$i]; + } + if ($count2[$i]) { + $buckets2[$i] = (float)$buckets2[$i] / (float)$count2[$i]; + } + } + + foreach ($aaa as $a) { + $index = $indexof[$a->question]; + if ($a->answer1) { + $difference = (float) ($a->answer1 - $buckets1[$index]); + $stdev1[$index] += ($difference * $difference); + } + if ($a->answer2) { + $difference = (float) ($a->answer2 - $buckets2[$index]); + $stdev2[$index] += ($difference * $difference); + } + } + + foreach ($questionorder as $i => $val) { + if ($count1[$i]) { + $stdev1[$i] = sqrt( (float)$stdev1[$i] / ((float)$count1[$i])); + } + if ($count2[$i]) { + $stdev2[$i] = sqrt( (float)$stdev2[$i] / ((float)$count2[$i])); + } + } + + + + $maxbuckets1 = max($buckets1); + $maxbuckets2 = max($buckets2); + + + $graph = new graph($GWIDTH,$GHEIGHT); + $graph->parameter['title'] = "$question->text"; + + $graph->x_data = $names; + $graph->y_data['answers1'] = $buckets1; + $graph->y_format['answers1'] = array('colour' => 'ltblue', 'line' => 'line', 'point' => 'square', + 'shadow_offset' => 4, 'legend' => 'actual'); + $graph->y_data['answers2'] = $buckets2; + $graph->y_format['answers2'] = array('colour' => 'ltgreen', 'line' => 'line', 'point' => 'square', + 'shadow_offset' => 4, 'legend' => 'preferred'); + $graph->y_data['stdev1'] = $stdev1; + $graph->y_format['stdev1'] = array('colour' => 'ltltblue', 'bar' => 'fill', + 'shadow_offset' => '4', 'legend' => 'none'); + $graph->y_data['stdev2'] = $stdev2; + $graph->y_format['stdev2'] = array('colour' => 'ltltgreen', 'bar' => 'fill', + 'shadow_offset' => '4', 'legend' => 'none'); + $graph->offset_relation['stdev1'] = 'answers1'; + $graph->offset_relation['stdev2'] = 'answers2'; + + $graph->parameter['bar_size'] = 0.15; + + $graph->parameter['legend'] = 'outside-top'; + $graph->parameter['legend_border'] = 'black'; + $graph->parameter['legend_offset'] = 4; + + if (($maxbuckets1 > 0.0) && ($maxbuckets2 > 0.0)) { + $graph->y_order = array('stdev1', 'answers1', 'stdev2', 'answers2'); + } else if ($maxbuckets1 > 0.0) { + $graph->y_order = array('stdev1', 'answers1'); + } else { + $graph->y_order = array('stdev2', 'answers2'); + } + + $graph->parameter['y_max_left']= count($options); + $graph->parameter['y_axis_gridlines']= count($options) + 1; + $graph->parameter['y_resolution_left']= 1; + $graph->parameter['y_decimal_left']= 1; + $graph->parameter['x_axis_angle'] = 0; + + $graph->draw(); + + break; + + + + case "overall.png": + + $qqq = get_records_sql("SELECT * FROM survey_questions WHERE id in ($survey->questions) AND multi <> ''"); + + foreach ($qqq as $qq) { + if ($qq->type < 0) { + $virtualscales = true; + } + } + foreach ($qqq as $qq) { // if any virtual, then use JUST virtual, else use JUST nonvirtual + if ($virtualscales && $qq->type < 0) { + $question[] = $qq; + } else if (!$virtualscales && $qq->type > 0) { + $question[] = $qq; + } + } + $numquestions = count($question); + + $options = explode(",",$question[0]->options); + $numoptions = count($options); + + for ($i=0; $i<$numquestions; $i++) { + $names[$i] = $question[$i]->text; + $buckets1[$i] = 0.0; + $buckets2[$i] = 0.0; + $stdev1[$i] = 0.0; + $stdev2[$i] = 0.0; + $count1[$i] = 0; + $count2[$i] = 0; + $subquestions = $question[$i]->multi; // otherwise next line doesn't work + $aaa = get_records_sql("SELECT * FROM survey_answers WHERE ((survey = $cm->instance) AND (question in ($subquestions)))"); + + foreach ($aaa as $a) { + if ($a->answer1) { + $buckets1[$i] += $a->answer1; + $count1[$i]++; + } + if ($a->answer2) { + $buckets2[$i] += $a->answer2; + $count2[$i]++; + } + } + + if ($count1[$i]) { + $buckets1[$i] = (float)$buckets1[$i] / (float)$count1[$i]; + } + if ($count2[$i]) { + $buckets2[$i] = (float)$buckets2[$i] / (float)$count2[$i]; + } + + // Calculate the standard devaiations + foreach ($aaa as $a) { + if ($a->answer1) { + $difference = (float) ($a->answer1 - $buckets1[$i]); + $stdev1[$i] += ($difference * $difference); + } + if ($a->answer2) { + $difference = (float) ($a->answer2 - $buckets2[$i]); + $stdev2[$i] += ($difference * $difference); + } + } + + if ($count1[$i]) { + $stdev1[$i] = sqrt( (float)$stdev1[$i] / ((float)$count1[$i])); + } + if ($count2[$i]) { + $stdev2[$i] = sqrt( (float)$stdev2[$i] / ((float)$count2[$i])); + } + + + } + + $maxbuckets1 = max($buckets1); + $maxbuckets2 = max($buckets2); + + + $graph = new graph($GWIDTH,$GHEIGHT); + $graph->parameter['title'] = "$survey->name"; + + $graph->x_data = $names; + + $graph->y_data['answers1'] = $buckets1; + $graph->y_format['answers1'] = array('colour' => 'ltblue', 'line' => 'line', 'point' => 'square', + 'shadow_offset' => 4, 'legend' => 'actual'); + $graph->y_data['answers2'] = $buckets2; + $graph->y_format['answers2'] = array('colour' => 'ltgreen', 'line' => 'line', 'point' => 'square', + 'shadow_offset' => 4, 'legend' => 'preferred'); + + $graph->y_data['stdev1'] = $stdev1; + $graph->y_format['stdev1'] = array('colour' => 'ltltblue', 'bar' => 'fill', + 'shadow_offset' => '4', 'legend' => 'none'); + $graph->y_data['stdev2'] = $stdev2; + $graph->y_format['stdev2'] = array('colour' => 'ltltgreen', 'bar' => 'fill', + 'shadow_offset' => '4', 'legend' => 'none'); + $graph->offset_relation['stdev1'] = 'answers1'; + $graph->offset_relation['stdev2'] = 'answers2'; + + $graph->parameter['bar_size'] = 0.15; + $graph->parameter['legend'] = 'outside-top'; + $graph->parameter['legend_border'] = 'black'; + $graph->parameter['legend_offset'] = 4; + + if (($maxbuckets1 > 0.0) && ($maxbuckets2 > 0.0)) { + $graph->y_order = array('stdev1', 'answers1', 'stdev2', 'answers2'); + } else if ($maxbuckets1 > 0.0) { + $graph->y_order = array('stdev1', 'answers1'); + } else { + $graph->y_order = array('stdev2', 'answers2'); + } + + $graph->parameter['y_max_left']= $numoptions; + $graph->parameter['y_axis_gridlines']= $numoptions + 1; + $graph->parameter['y_resolution_left']= 1; + $graph->parameter['y_decimal_left']= 1; + $graph->parameter['x_axis_angle'] = 0; + + $graph->draw(); + + break; + + + + case "student.png": + + $qqq = get_records_sql("SELECT * FROM survey_questions WHERE id in ($survey->questions) AND multi <> ''"); + + foreach ($qqq as $qq) { + if ($qq->type < 0) { + $virtualscales = true; + } + } + foreach ($qqq as $qq) { // if any virtual, then use JUST virtual, else use JUST nonvirtual + if ($virtualscales && $qq->type < 0) { + $question[] = $qq; + } else if (!$virtualscales && $qq->type > 0) { + $question[] = $qq; + } + } + $numquestions= count($question); + + $options = explode(",",$question[0]->options); + $numoptions = count($options); + + for ($i=0; $i<$numquestions; $i++) { + $names[$i] = $question[$i]->text; + $buckets1[$i] = 0.0; + $buckets2[$i] = 0.0; + $count1[$i] = 0; + $count2[$i] = 0; + $studbuckets1[$i] = 0.0; + $studbuckets2[$i] = 0.0; + $studcount1[$i] = 0; + $studcount2[$i] = 0; + $subquestions = $question[$i]->multi; // otherwise next line doesn't work + $aaa = get_records_sql("SELECT * FROM survey_answers WHERE ((survey = $cm->instance) AND (question in ($subquestions)))"); + + foreach ($aaa as $a) { + if ($a->user == $sid) { + if ($a->answer1) { + $studbuckets1[$i] += $a->answer1; + $studcount1[$i]++; + } + if ($a->answer2) { + $studbuckets2[$i] += $a->answer2; + $studcount2[$i]++; + } + } + if ($a->answer1) { + $buckets1[$i] += $a->answer1; + $count1[$i]++; + } + if ($a->answer2) { + $buckets2[$i] += $a->answer2; + $count2[$i]++; + } + } + + if ($count1[$i]) { + $buckets1[$i] = (float)$buckets1[$i] / (float)$count1[$i]; + } + if ($count2[$i]) { + $buckets2[$i] = (float)$buckets2[$i] / (float)$count2[$i]; + } + if ($studcount1[$i]) { + $studbuckets1[$i] = (float)$studbuckets1[$i] / (float)$studcount1[$i]; + } + if ($studcount2[$i]) { + $studbuckets2[$i] = (float)$studbuckets2[$i] / (float)$studcount2[$i]; + } + + // Calculate the standard devaiations + foreach ($aaa as $a) { + if ($a->answer1) { + $difference = (float) ($a->answer1 - $buckets1[$i]); + $stdev1[$i] += ($difference * $difference); + } + if ($a->answer2) { + $difference = (float) ($a->answer2 - $buckets2[$i]); + $stdev2[$i] += ($difference * $difference); + } + } + + if ($count1[$i]) { + $stdev1[$i] = sqrt( (float)$stdev1[$i] / ((float)$count1[$i])); + } + if ($count2[$i]) { + $stdev2[$i] = sqrt( (float)$stdev2[$i] / ((float)$count2[$i])); + } + + } + + $maxbuckets1 = max($buckets1); + $maxbuckets2 = max($buckets2); + + + $graph = new graph($GWIDTH,$GHEIGHT); + $graph->parameter['title'] = "$survey->name"; + + $graph->x_data = $names; + + $graph->y_data['answers1'] = $buckets1; + $graph->y_format['answers1'] = array('colour' => 'ltblue', 'line' => 'line', 'point' => 'square', + 'shadow_offset' => 4, 'legend' => 'class actual'); + $graph->y_data['answers2'] = $buckets2; + $graph->y_format['answers2'] = array('colour' => 'ltgreen', 'line' => 'line', 'point' => 'square', + 'shadow_offset' => 4, 'legend' => 'class preferred'); + $graph->y_data['studanswers1'] = $studbuckets1; + $graph->y_format['studanswers1'] = array('colour' => 'blue', 'line' => 'line', 'point' => 'square', + 'shadow_offset' => 4, 'legend' => 'student actual'); + $graph->y_data['studanswers2'] = $studbuckets2; + $graph->y_format['studanswers2'] = array('colour' => 'green', 'line' => 'line', 'point' => 'square', + 'shadow_offset' => 4, 'legend' => 'student preferred'); + $graph->y_data['stdev1'] = $stdev1; + $graph->y_format['stdev1'] = array('colour' => 'ltltblue', 'bar' => 'fill', + 'shadow_offset' => '4', 'legend' => 'none'); + $graph->y_data['stdev2'] = $stdev2; + $graph->y_format['stdev2'] = array('colour' => 'ltltgreen', 'bar' => 'fill', + 'shadow_offset' => '4', 'legend' => 'none'); + $graph->offset_relation['stdev1'] = 'answers1'; + $graph->offset_relation['stdev2'] = 'answers2'; + + $graph->parameter['bar_size'] = 0.15; + + $graph->parameter['legend'] = 'outside-top'; + $graph->parameter['legend_border'] = 'black'; + $graph->parameter['legend_offset'] = 4; + + if (($maxbuckets1 > 0.0) && ($maxbuckets2 > 0.0)) { + $graph->y_order = array('stdev1', 'answers1', 'stdev2', 'answers2', 'studanswers1', 'studanswers2'); + } else if ($maxbuckets1 > 0.0) { + $graph->y_order = array('stdev1', 'answers1', 'studanswers1'); + } else { + $graph->y_order = array('stdev2', 'answers2', 'studanswers2'); + } + + $graph->parameter['y_max_left']= $numoptions; + $graph->parameter['y_axis_gridlines']= $numoptions + 1; + $graph->parameter['y_resolution_left']= 1; + $graph->parameter['y_decimal_left']= 1; + $graph->parameter['x_axis_angle'] = 0; + + $graph->draw(); + break; + + + + case "studentmultiquestion.png": + + $question = get_record("survey_questions", "id", $qid); + + $options = explode(",",$question->options); + $questionorder = explode( ",", $question->multi); + + $qqq = get_records_sql("SELECT * FROM survey_questions WHERE id in ($question->multi)"); + + foreach ($questionorder as $i => $val) { + $names[$i] = shorten_name($qqq["$val"]->text, 4); + $buckets1[$i] = 0; + $buckets2[$i] = 0; + $count1[$i] = 0; + $count2[$i] = 0; + $indexof[$val] = $i; + $studbuckets1[$i] = 0.0; + $studbuckets2[$i] = 0.0; + $studcount1[$i] = 0; + $studcount2[$i] = 0; + } + + $aaa = get_records_sql("SELECT * FROM survey_answers WHERE ((survey = $cm->instance) AND (question in ($question->multi)))"); + + foreach ($aaa as $a) { + $index = $indexof[$a->question]; + if ($a->user == $sid) { + if ($a->answer1) { + $studbuckets1[$index] += $a->answer1; + $studcount1[$index]++; + } + if ($a->answer2) { + $studbuckets2[$index] += $a->answer2; + $studcount2[$index]++; + } + } + if ($a->answer1) { + $buckets1[$index] += $a->answer1; + $count1[$index]++; + } + if ($a->answer2) { + $buckets2[$index] += $a->answer2; + $count2[$index]++; + } + } + + foreach ($questionorder as $i => $val) { + if ($count1[$i]) { + $buckets1[$i] = (float)$buckets1[$i] / (float)$count1[$i]; + } + if ($count2[$i]) { + $buckets2[$i] = (float)$buckets2[$i] / (float)$count2[$i]; + } + if ($studcount1[$i]) { + $studbuckets1[$i] = (float)$studbuckets1[$i] / (float)$studcount1[$i]; + } + if ($studcount2[$i]) { + $studbuckets2[$i] = (float)$studbuckets2[$i] / (float)$studcount2[$i]; + } + } + + foreach ($aaa as $a) { + $index = $indexof[$a->question]; + if ($a->answer1) { + $difference = (float) ($a->answer1 - $buckets1[$index]); + $stdev1[$index] += ($difference * $difference); + } + if ($a->answer2) { + $difference = (float) ($a->answer2 - $buckets2[$index]); + $stdev2[$index] += ($difference * $difference); + } + } + + foreach ($questionorder as $i => $val) { + if ($count1[$i]) { + $stdev1[$i] = sqrt( (float)$stdev1[$i] / ((float)$count1[$i])); + } + if ($count2[$i]) { + $stdev2[$i] = sqrt( (float)$stdev2[$i] / ((float)$count2[$i])); + } + } + + + + $maxbuckets1 = max($buckets1); + $maxbuckets2 = max($buckets2); + + + $graph = new graph($GWIDTH,$GHEIGHT); + $graph->parameter['title'] = "$question->text"; + + $graph->x_data = $names; + $graph->y_data['answers1'] = $buckets1; + $graph->y_format['answers1'] = array('colour' => 'ltblue', 'line' => 'line', 'point' => 'square', + 'shadow_offset' => 4, 'legend' => 'class actual'); + $graph->y_data['answers2'] = $buckets2; + $graph->y_format['answers2'] = array('colour' => 'ltgreen', 'line' => 'line', 'point' => 'square', + 'shadow_offset' => 4, 'legend' => 'class preferred'); + $graph->y_data['studanswers1'] = $studbuckets1; + $graph->y_format['studanswers1'] = array('colour' => 'blue', 'line' => 'line', 'point' => 'square', + 'shadow_offset' => 4, 'legend' => 'student actual'); + $graph->y_data['studanswers2'] = $studbuckets2; + $graph->y_format['studanswers2'] = array('colour' => 'green', 'line' => 'line', 'point' => 'square', + 'shadow_offset' => 4, 'legend' => 'student preferred'); + $graph->y_data['stdev1'] = $stdev1; + $graph->y_format['stdev1'] = array('colour' => 'ltltblue', 'bar' => 'fill', + 'shadow_offset' => '4', 'legend' => 'none'); + $graph->y_data['stdev2'] = $stdev2; + $graph->y_format['stdev2'] = array('colour' => 'ltltgreen', 'bar' => 'fill', + 'shadow_offset' => '4', 'legend' => 'none'); + $graph->offset_relation['stdev1'] = 'answers1'; + $graph->offset_relation['stdev2'] = 'answers2'; + + $graph->parameter['bar_size'] = 0.15; + + $graph->parameter['legend'] = 'outside-top'; + $graph->parameter['legend_border'] = 'black'; + $graph->parameter['legend_offset'] = 4; + + if (($maxbuckets1 > 0.0) && ($maxbuckets2 > 0.0)) { + $graph->y_order = array('stdev1', 'answers1', 'studanswers1', 'stdev2', 'answers2', 'studanswers2'); + } else if ($maxbuckets1 > 0.0) { + $graph->y_order = array('stdev1', 'answers1', 'studanswers1'); + } else { + $graph->y_order = array('stdev2', 'answers2', 'studanswers2'); + } + + $graph->parameter['y_max_left']= count($options); + $graph->parameter['y_axis_gridlines']= count($options) + 1; + $graph->parameter['y_resolution_left']= 1; + $graph->parameter['y_decimal_left']= 1; + $graph->parameter['x_axis_angle'] = 0; + + $graph->draw(); + + break; + + default: + break; + } + + exit; + +function shorten_name ($name, $numwords) { + $words = explode(" ", $name); + for ($i=0; $i < $numwords; $i++) { + $output .= $words[$i]." "; + } + return $output; +} + +?> diff --git a/mod/survey/icon.gif b/mod/survey/icon.gif new file mode 100755 index 0000000000000..732bd1ee3d719 Binary files /dev/null and b/mod/survey/icon.gif differ diff --git a/mod/survey/icon2.gif b/mod/survey/icon2.gif new file mode 100644 index 0000000000000..cda1916163e56 Binary files /dev/null and b/mod/survey/icon2.gif differ diff --git a/mod/survey/index.php b/mod/survey/index.php new file mode 100644 index 0000000000000..1f70c7096158c --- /dev/null +++ b/mod/survey/index.php @@ -0,0 +1,40 @@ +id); + add_to_log("View all surveys", $course->id); + + print_header("$course->shortname: Surveys", "$course->fullname", + "id>$course->shortname -> Surveys", ""); + + + if (! $surveys = get_all_instances_in_course("survey", $course->id, "cw.week ASC")) { + notice("There are no surveys.", "../../course/view.php?id=$course->id"); + } + + $table->head = array ("Week", "Name", "Status"); + $table->align = array ("CENTER", "LEFT", "LEFT"); + + foreach ($surveys as $survey) { + if (survey_already_done($survey->id, $USER->id)) { + $ss = "Done"; + } else { + $ss = "coursemodule\">Not done yet"; + } + $table->data[] = array ("$survey->week", + "coursemodule\">$survey->name", + "$ss"); + } + + print_table($table); + print_footer($course); + +?> diff --git a/mod/survey/install.sql b/mod/survey/install.sql new file mode 100755 index 0000000000000..2baf77188e863 --- /dev/null +++ b/mod/survey/install.sql @@ -0,0 +1,167 @@ +# phpMyAdmin MySQL-Dump +# version 2.2.1 +# http://phpwizard.net/phpMyAdmin/ +# http://phpmyadmin.sourceforge.net/ (download page) +# +# Host: localhost +# Generation Time: Nov 14, 2001 at 04:39 PM +# Server version: 3.23.36 +# PHP Version: 4.0.6 +# Database : `moodle` +# -------------------------------------------------------- + +# +# Table structure for table `survey` +# + +CREATE TABLE survey ( + id int(10) unsigned NOT NULL auto_increment, + course int(10) unsigned NOT NULL default '0', + template int(10) unsigned NOT NULL default '0', + days smallint(6) NOT NULL default '0', + timecreated int(10) unsigned NOT NULL default '0', + timemodified int(10) unsigned NOT NULL default '0', + name varchar(255) NOT NULL default '', + intro text, + questions varchar(255) default NULL, + PRIMARY KEY (id) +) TYPE=MyISAM COMMENT='all surveys'; + +# +# Dumping data for table `survey` +# + +INSERT INTO survey VALUES (1, 0, 0, 0, 985017600, 985017600, 'COLLES (Actual)', 'The purpose of this questionnaire is to help us understand how well the online delivery of this unit enabled you to learn. \r\n\r\nEach one of the 24 statements below asks about your experience in this unit.\r\n\r\nThere are no \'right\' or \'wrong\' answers; we are interested only in your opinion. Please be assured that your responses will be treated with a high degree of confidentiality, and will not affect your assessment.\r\n\r\nYour carefully considered responses will help us improve the way this unit is presented online in the future.\r\n\r\nThanks very much.\r\n', '25,26,27,28,29,30,43,44'); +INSERT INTO survey VALUES (2, 0, 0, 0, 985017600, 985017600, 'COLLES (Preferred)', 'The purpose of this questionnaire is to help us understand what you value in an online learning experience.\r\n\r\nEach one of the 24 statements below asks about your preferred (ideal) experience in this unit.\r\n\r\nThere are no \'right\' or \'wrong\' answers; we are interested only in your opinion. Please be assured that your responses will be treated with a high degree of confidentiality, and will not affect your assessment.\r\n\r\nYour carefully considered responses will help us improve the way this unit is presented online in the future.\r\n\r\nThanks very much.\r\n', '31,32,33,34,35,36,43,44'); +INSERT INTO survey VALUES (3, 0, 0, 0, 985017600, 985017600, 'COLLES (Preferred and Actual)', 'The purpose of this questionnaire is to help us understand how well the online delivery of this unit enabled you to learn. \r\n\r\nEach one of the 24 statements below asks you to compare your preferred (ideal) and actual experience in this unit.\r\n\r\nThere are no \'right\' or \'wrong\' answers; we are interested only in your opinion. Please be assured that your responses will be treated with a high degree of confidentiality, and will not affect your assessment.\r\n\r\nYour carefully considered responses will help us improve the way this unit is presented online in the future.\r\n\r\nThanks very much.\r\n', '37,38,39,40,41,42,43,44'); +INSERT INTO survey VALUES (4, 0, 0, 0, 985017600, 985017600, 'ATTLS (20 item version)', 'The purpose of this questionnaire is to help \r\nus evaluate your attitudes towards thinking and learning.\r\n', '65,67,68'); +# -------------------------------------------------------- + +# +# Table structure for table `survey_analysis` +# + +CREATE TABLE survey_analysis ( + id int(10) unsigned NOT NULL auto_increment, + survey int(10) unsigned NOT NULL default '0', + user int(10) unsigned NOT NULL default '0', + notes text NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY id (id) +) TYPE=MyISAM; + +# +# Dumping data for table `survey_analysis` +# + +# -------------------------------------------------------- + +# +# Table structure for table `survey_answers` +# + +CREATE TABLE survey_answers ( + id int(10) unsigned NOT NULL auto_increment, + user int(10) unsigned NOT NULL default '0', + survey int(10) unsigned NOT NULL default '0', + question int(10) unsigned NOT NULL default '0', + time int(10) unsigned default NULL, + answer1 char(255) default NULL, + answer2 char(255) default NULL, + PRIMARY KEY (id), + UNIQUE KEY id (id) +) TYPE=MyISAM; + +# +# Dumping data for table `survey_answers` +# + +# -------------------------------------------------------- + +# +# Table structure for table `survey_questions` +# + +CREATE TABLE survey_questions ( + id int(10) unsigned NOT NULL auto_increment, + owner int(10) unsigned NOT NULL default '0', + text varchar(255) NOT NULL default '', + multi varchar(100) NOT NULL default '', + intro varchar(50) default NULL, + type tinyint(3) NOT NULL default '0', + options text, + PRIMARY KEY (id) +) TYPE=MyISAM; + +# +# Dumping data for table `survey_questions` +# + +INSERT INTO survey_questions VALUES (1, 0, 'my learning focuses on issues that interest me.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r\n'); +INSERT INTO survey_questions VALUES (2, 0, 'what I learn is important for my professional practice.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r\n'); +INSERT INTO survey_questions VALUES (3, 0, 'I learn how to improve my professional practice.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (4, 0, 'what I learn connects well with my professional practice.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r\n'); +INSERT INTO survey_questions VALUES (5, 0, 'I think critically about how I learn.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (6, 0, 'I think critically about my own ideas.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (7, 0, 'I think critically about other students\' ideas.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (8, 0, 'I think critically about ideas in the readings.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (9, 0, 'I explain my ideas to other students.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (10, 0, 'I ask other students to explain their ideas.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (11, 0, 'other students ask me to explain my ideas.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r\n'); +INSERT INTO survey_questions VALUES (12, 0, 'other students respond to my ideas.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r\n'); +INSERT INTO survey_questions VALUES (13, 0, 'the tutor stimulates my thinking.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r\n'); +INSERT INTO survey_questions VALUES (14, 0, 'the tutor encourages me to participate.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r\n'); +INSERT INTO survey_questions VALUES (15, 0, 'the tutor models good discourse.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r\n'); +INSERT INTO survey_questions VALUES (16, 0, 'the tutor models critical self-reflection.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r\n'); +INSERT INTO survey_questions VALUES (17, 0, 'other students encourage my participation.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r\n'); +INSERT INTO survey_questions VALUES (18, 0, 'other students praise my contribution.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r\n'); +INSERT INTO survey_questions VALUES (19, 0, 'other students value my contribution.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r\n'); +INSERT INTO survey_questions VALUES (20, 0, 'other students empathise with my struggle to learn.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r\n'); +INSERT INTO survey_questions VALUES (21, 0, 'I make good sense of other students\' messages.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (22, 0, 'other students make good sense of my messages.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r\n'); +INSERT INTO survey_questions VALUES (23, 0, 'I make good sense of the tutor\'s messages.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (24, 0, 'the tutor makes good sense of my messages.', '', '', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r\n'); +INSERT INTO survey_questions VALUES (25, 0, 'Relevance', '1,2,3,4', 'In this online unit...', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (26, 0, 'Reflective Thinking', '5,6,7,8', 'In this online unit...', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (27, 0, 'Interactivity', '9,10,11,12', 'In this online unit...', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (28, 0, 'Tutor Support', '13,14,15,16', 'In this online unit...', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (29, 0, 'Peer Support', '17,18,19,20', 'In this online unit...', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (30, 0, 'Interpretation', '21,22,23,24', 'In this online unit...', 1, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (31, 0, 'Relevance', '1,2,3,4', 'In this online unit, I prefer that...', 2, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (32, 0, 'Reflective Thinking', '5,6,7,8', 'In this online unit, I prefer that...', 2, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (33, 0, 'Interactivity', '9,10,11,12', 'In this online unit, I prefer that...', 2, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (34, 0, 'Tutor Support', '13,14,15,16', 'In this online unit, I prefer that...', 2, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (35, 0, 'Peer Support', '17,18,19,20', 'In this online unit, I prefer that...', 2, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (36, 0, 'Interpretation', '21,22,23,24', 'In this online unit, I prefer that...', 2, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (37, 0, 'Relevance', '1,2,3,4', 'In this online unit...', 3, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (38, 0, 'Reflective Thinking', '5,6,7,8', 'In this online unit...', 3, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (39, 0, 'Interactivity', '9,10,11,12', 'In this online unit...', 3, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (40, 0, 'Tutor Support', '13,14,15,16', 'In this online unit...', 3, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (41, 0, 'Peer Support', '17,18,19,20', 'In this online unit...', 3, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (42, 0, 'Interpretation', '21,22,23,24', 'In this online unit...', 3, 'Almost Never,Seldom,Sometimes,Often,Almost Always\r'); +INSERT INTO survey_questions VALUES (43, 0, 'How long did this survey take you to complete?', '', '', 1, 'under 1 min,1-2 min,2-3 min,3-4 min,4-5-min,5-10 min,more than 10\r'); +INSERT INTO survey_questions VALUES (44, 0, 'Do you have any other comments?', '', '', 0, '\r'); +INSERT INTO survey_questions VALUES (64, 0, 'I spend time figuring out what\'s "wrong" with things. For example, I\'ll look for something in a literary interpretation that isn\'t argued well enough.', '', '', 1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (58, 0, 'I try to point out weaknesses in other people\'s thinking to help them clarify their arguments.', '', '', 1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (59, 0, 'I tend to put myself in other people\'s shoes when discussing controversial issues, to see why they think the way they do.', '', '', 1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (60, 0, 'One could call my way of analysing things "putting them on trial" because I am careful to consider all the evidence.', '', '', 1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (61, 0, 'I value the use of logic and reason over the incorporation of my own concerns when solving problems.', '', '', 1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (62, 0, 'I can obtain insight into opinions that differ from mine through empathy.', '', '', 1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (63, 0, 'When I encounter people whose opinions seem alien to me, I make a deliberate effort to "extend" myself into that person, to try to see how they could have those opinions.', '', '', 1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (56, 0, 'I have certain criteria I use in evaluating arguments.', '', '', 1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (57, 0, 'I\'m more likely to try to understand someone else\'s opinion that to try to evaluate it.', '', '', 1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (55, 0, 'I try to think with people instead of against them.', '', '', 1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (54, 0, 'It\'s important for me to remain as objective as possible when I analyze something.', '', '', 1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (53, 0, 'I often find myself arguing with the authors of books that I read, trying to logically figure out why they\'re wrong.', '', '', 1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (52, 0, 'I am always interested in knowing why people say and believe the things they do.', '', '', 1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (51, 0, 'I find that I can strengthen my own position through arguing with someone who disagrees with me.', '', '', 1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (50, 0, 'I enjoy hearing the opinions of people who come from backgrounds different to mine - it helps me to understand how the same things can be seen in such different ways.', '', '', 1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (49, 0, 'I feel that the best way for me to achieve my own identity is to interact with a variety of other people.', '', '', 1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (48, 0, 'The most important part of my education has been learning to understand people who are very different to me.', '', '', 1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (47, 0, 'I like to understand where other people are "coming from", what experiences have led them to feel the way they do.', '', '', 1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (45, 0, 'In evaluating what someone says, I focus on the quality of their argument, not on the person who\'s presenting it.', '', '', 1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (46, 0, 'I like playing devil\'s advocate - arguing the opposite of what someone is saying.', '', '', 1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (65, 0, 'Attitudes Towards Thinking and Learning', '45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64', 'In discussion ...', 1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (67, 0, 'Connected Learning', '63,62,59,57,55,49,52,50,48,47', 'Connected knowers...', -1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); +INSERT INTO survey_questions VALUES (68, 0, 'Separate Learning', '46,54,45,51,60,53,56,58,61,64', 'Separate knowers...', -1, 'Strongly disagree,Somewhat disagree,Neither agree nor disagree,Somewhat agree,Strongly agree'); + diff --git a/mod/survey/lib.php b/mod/survey/lib.php new file mode 100644 index 0000000000000..83202371dc913 --- /dev/null +++ b/mod/survey/lib.php @@ -0,0 +1,79 @@ + "Virtual Actual and Preferred", + "-2" => "Virtual Preferred", + "-1" => "Virtual Actual", + "0" => "Text", + "1" => "Actual", + "2" => "Preferred", + "3" => "Actual and Preferred", + ); + +function survey_already_done($survey, $user) { + return record_exists_sql("SELECT * FROM survey_answers WHERE survey='$survey' AND user='$user'"); +} + +function get_survey_status($survey) { + + $timenow = time(); + if ($survey->locked) { + if (($survey->timeopen <= $timenow) && ($timenow <= $survey->timeclose)) { + return "released"; + } else if ($survey->timenow >= $survey->timeclose) { + return "finished"; + } else { + return "error"; + } + } else { + return "editing"; + } + +} + +function get_responses_for_survey($surveyid) { + global $db; + + if ($aa = $db->Execute("SELECT user FROM survey_answers WHERE survey = $surveyid GROUP BY user")) { + if ($aa) { + return $aa->RowCount(); + } else { + return -1; + } + } else { + return -1; + } +} + + +function get_template_name($templateid) { + global $db; + + if ($templateid) { + if ($ss = $db->Execute("SELECT name FROM surveys WHERE id = $templateid")) { + return $ss->fields["name"]; + } + } else { + return ""; + } +} + +function update_survey_analysis($survey, $user, $notes) { + global $db; + + return $db->Execute("UPDATE survey_analysis SET notes='$notes' WHERE survey='$survey' and user='$user'"); +} + +function add_survey_analysis($survey, $user, $notes) { + global $db; + + return $db->Execute("INSERT INTO survey_analysis SET notes='$notes', survey='$survey', user='$user'"); +} + + + +?> diff --git a/mod/survey/login_form.html b/mod/survey/login_form.html new file mode 100644 index 0000000000000..871a890373a14 --- /dev/null +++ b/mod/survey/login_form.html @@ -0,0 +1,65 @@ +
    +

    You need to log in before you can fill out this survey

    + + + + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + +

    Survey Password:
    (from your teacher)

    + " > + +

    Your first name:

    + "> + +

    Your last name:

    + "> + +

    Your email address:

    + "> + +

    Student ID Number:
    (optional)

    + "> + +
      +
    + + +
    + +
    +
    +
    +
    + +
    +
    + +

    Home

    + + diff --git a/mod/survey/mod.html b/mod/survey/mod.html new file mode 100644 index 0000000000000..e9dcb4d569619 --- /dev/null +++ b/mod/survey/mod.html @@ -0,0 +1,34 @@ +
    + + + + + + + + + +

    Survey Name:

    + +

    Survey Type:

    + template); + } else { + echo "No surveys to choose from!"; + } + ?> +
    +
    + + + + + + + + + + +
    +
    diff --git a/mod/survey/mod.php b/mod/survey/mod.php new file mode 100644 index 0000000000000..973bff3771b16 --- /dev/null +++ b/mod/survey/mod.php @@ -0,0 +1,91 @@ +template)) { + return 0; + } + + if (!$rs = $db->Execute("INSERT into survey + SET course = '$survey->course', + name = '$survey->name', + days = '$survey->days', + intro = '$survey->intro', + template = '$template->id', + questions = '$template->questions', + timecreated = '$timenow', + timemodified = '$timenow' ")) { + return 0; + } + + // Get it out again - this is the most compatible way to determine the ID + if ($rs = $db->Execute("SELECT id FROM survey + WHERE course = $survey->course AND timemodified = '$timenow'")) { + return $rs->fields[0]; + } else { + return 0; + } +} + + +function update_instance($survey) { +// Given an object containing all the necessary data, +// (defined by the form in mod.html) this function +// will update an existing instance with new data. +// + GLOBAL $db; + + $timenow = time(); + + if (!$template = get_record("survey", "id", $survey->template)) { + return 0; + } + + if (!$rs = $db->Execute("UPDATE survey + SET name = '$survey->name', + days = '$survey->days', + intro = '$survey->intro', + template = '$template->id', + questions = '$template->questions', + timemodified = '$timenow' + WHERE id = '$survey->instance' ")) { + return false; + } + return true; +} + + +function delete_instance($id) { +// Given an ID of an instance of this module, +// this function will permanently delete the instance +// and any data that depends on it. +// + GLOBAL $db; + + if (!$rs = $db->Execute("DELETE from survey WHERE id = '$id' ")) { + return false; + } + + return true; + +} + + +?> diff --git a/mod/survey/module.php b/mod/survey/module.php new file mode 100644 index 0000000000000..82319a6f10804 --- /dev/null +++ b/mod/survey/module.php @@ -0,0 +1,14 @@ +fullname = "Survey"; + $module->version = "20011110"; + $module->cron = 0; + $module->search = ""; + +?> + diff --git a/mod/survey/report.php b/mod/survey/report.php new file mode 100644 index 0000000000000..a0a16a23c472f --- /dev/null +++ b/mod/survey/report.php @@ -0,0 +1,356 @@ +course)) { + error("Course is misconfigured"); + } + + require_login($course->id); + + if (!isteacher($course->id)) { + error("Sorry, only teachers can see this."); + } + + if (! $survey = get_record("survey", "id", $cm->instance)) { + error("Survey ID was incorrect"); + } + + + + $ME = qualified_me()."?id=$id"; + + if (!$action) { + $display = "summary"; + } + + if ($display) { // Display the frame containing something. + + add_to_log("View survey report: $survey->name", $course->id); + echo "Report: $survey->name\n"; + echo " "; + echo " \n"; + echo " \n"; + echo "\n"; + exit; + } + + switch ($action) { + case "menu": + print_header("Survey Report", "Survey Report"); + //echo ""; + //echo "

    Survey Report

    "; + echo "

    Summary

    "; + echo "

    Scales

    "; + echo "

    Questions

    "; + echo "

    Students

    "; + if ($users = get_survey_responses($survey->id)) { + foreach ($users as $user) { + echo "
  • "; + echo "id&id=$id\">"; + echo "$user->firstname $user->lastname"; + echo "
  • "; + } + } + echo "

    Download

    "; + echo "
    "; + echo "

    wwwroot/course/view.php?id=$course->id\">$course->shortname

    "; + break; + + case "summary": + add_to_log("View survey report summary: $survey->name", $course->id); + print_header("Overall Summary", "$survey->name: Overall Summary", "", ""); + + print_heading("All scales, all students"); + + echo "

    \"Click"; + print_footer($course); + break; + + case "scales": + add_to_log("View survey report scales: $survey->name", $course->id); + print_header("Scales", "$survey->name: Scales", "", ""); + + print_heading("All scales, all students"); + + $questions = get_records_sql("SELECT * FROM survey_questions WHERE id in ($survey->questions)"); + $questionorder = explode(",", $survey->questions); + + foreach ($questionorder as $key => $val) { + $question = $questions[$val]; + if ($question->type < 0) { // We have some virtual scales. Just show them. + $virtualscales = true; + break; + } + } + + foreach ($questionorder as $key => $val) { + $question = $questions[$val]; + if ($question->multi) { + if ($virtualscales && $question->type > 0) { // Don't show non-virtual scales if virtual + continue; + } + echo "

    multi>"; + echo "\"Clickid&type=multiquestion.png\">"; + echo "


    "; + } + } + + print_footer($course); + break; + + case "questions": + add_to_log("View survey report questions: $survey->name", $course->id); + print_header("Analysis by Question", "$survey->name: Questions", "", ""); + + if ($qid) { // just get one multi-question + $questions = get_records_sql("SELECT * FROM survey_questions WHERE id in ($qid)"); + $questionorder = explode(",", $qid); + + print_heading("Selected questions from a scale, all students"); + + } else { // get all top-level questions + $questions = get_records_sql("SELECT * FROM survey_questions WHERE id in ($survey->questions)"); + $questionorder = explode(",", $survey->questions); + + print_heading("All questions in order, all students"); + } + + foreach ($questionorder as $key => $val) { + $question = $questions[$val]; + if ($question->type < 0) { // We have some virtual scales. DON'T show them. + $virtualscales = true; + break; + } + } + + foreach ($questionorder as $key => $val) { + $question = $questions[$val]; + + if ($question->type < 0) { // We have some virtual scales. DON'T show them. + continue; + } + + if ($question->multi) { + echo "

    $question->text :

    "; + + $subquestions = get_records_sql("SELECT * FROM survey_questions WHERE id in ($question->multi)"); + $subquestionorder = explode(",", $question->multi); + foreach ($subquestionorder as $key => $val) { + $subquestion = $subquestions[$val]; + if ($subquestion->type > 0) { + echo "

    id\"> + \"Clickid&type=question.png\">

    "; + } + } + } else if ($question->type > 0 ) { + echo "

    id\"> + \"Clickid&type=question.png\">

    "; + } else { + echo "

    $question->text

    "; + if ($aaa = get_records_sql("SELECT sa.*, u.firstname,u.lastname FROM survey_answers sa, user u WHERE survey = '$survey->id' AND question = $question->id and sa.user = u.id")) { + echo "
      "; + foreach ($aaa as $a) { + echo "
    • $a->firstname $a->lastname: $a->answer1"; + } + echo "
    "; + } + } + } + + print_footer($course); + break; + + case "question": + add_to_log("View survey report question: $survey->name", $course->id); + if (!$question = get_record("survey_questions", "id", $qid)) { + error("Question doesn't exist"); + } + + $answers = explode(",", $question->options); + + print_header("All answers for a particular question", "$survey->name: Question Answers", "", ""); + + print_heading("$question->text"); + + $aaa = get_records_sql("SELECT sa.*,u.firstname,u.lastname,u.picture FROM survey_answers sa, user u WHERE sa.survey = '$survey->id' AND sa.question = $question->id AND u.id = sa.user ORDER by sa.answer1,sa.answer2 ASC"); + + echo ""; + foreach ($aaa as $a) { + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + + } + echo "
     NameTimeActualPreferred
    "; + print_user_picture($a->user, $course->id, $a->picture, false); + echo "

    user\">$a->firstname $a->lastname

    ".date("j M Y h:i A",$a->time)."

    cellcontent\">

    "; + if ($a->answer1) { + echo "$a->answer1 - ".$answers[$a->answer1 - 1]; + } else { + echo " "; + } + echo "

    cellcontent\">

    "; + if ($a->answer2) { + echo "$a->answer2 - ".$answers[$a->answer2 - 1]; + } else { + echo " "; + } + echo "

    "; + + + print_footer($course); + break; + + case "students": + + add_to_log("View survey report students: $survey->name", $course->id); + print_header("Analysis by Student", "$survey->name: Students", "", ""); + + if (! $results = get_survey_responses($survey->id) ) { + notify("There are no responses for this survey."); + } else { + print_all_responses($cm->id, $results); + } + + print_footer($course); + break; + + case "student": + if (!$user = get_record("user", "id", $student)) { + error("Student doesn't exist"); + } + + + print_header("Analysis of $user->firstname $user->lastname", "$survey->name: Analysis of a student", "", ""); + if (isset($notes)) { + if (record_exists_sql("SELECT * FROM survey_analysis WHERE survey='$survey->id' and user='$user->id'")) { + if (! update_survey_analysis($survey->id, $user->id, $notes)) { + notify("An error occurred while saving your notes. Sorry."); + } + } else { + if (!add_survey_analysis($survey->id, $user->id, $notes)) { + notify("An error occurred while saving your notes. Sorry."); + } + } + add_to_log("Update survey analysis for $user->firstname $user->lastname: $survey->name", $course->id); + } else { + add_to_log("View survey report $user->firstname $user->lastname: $survey->name", $course->id); + } + + print_heading("$user->firstname $user->lastname"); + + echo "

    "; + print_user_picture($user->id, $course->id, $user->picture, true); + echo "

    "; + + // Print overall summary + echo "

    "; + + // Print scales + $questions = get_records_sql("SELECT * FROM survey_questions WHERE id in ($survey->questions)"); + $questionorder = explode(",", $survey->questions); + + foreach ($questionorder as $key => $val) { + $question = $questions[$val]; + if ($question->type < 0) { // We have some virtual scales. Just show them. + $virtualscales = true; + break; + } + } + + foreach ($questionorder as $key => $val) { + $question = $questions[$val]; + if ($question->multi) { + if ($virtualscales && $question->type > 0) { // Don't show non-virtual scales if virtual + continue; + } + echo "

    multi>"; + echo "\"Clickid&sid=$student&type=studentmultiquestion.png\">"; + echo "


    "; + } + } + + if ($rs = get_record_sql("SELECT notes from survey_analysis WHERE survey='$survey->id' and user='$user->id'")) { + $notes = $rs->notes; + } else { + $notes = ""; + } + echo "
    "; + echo "
    "; + echo "
    "; + echo "

    Your private analysis/notes:

    "; + echo "
    "; + echo "
    "; + echo ""; + echo ""; + echo "id>"; + echo ""; + echo "
    "; + echo "
    "; + echo "
    "; + + + print_footer($course); + break; + + case "download": + print_header("Download Data", "$survey->name: Download Data", "", ""); + + echo "

    You can download the complete raw data for this survey in a form suitable + for analysis in Excel, SPSS or other package.

    "; + + echo "

    Download data as Excel spreadsheet

    "; + echo "

    Download data as a plain text file

    "; + + print_footer($course); + break; + + } + +/// FUNCTIONS ////////////////////////////////////////////////////////////// + +function print_all_responses($survey, $results) { + + global $THEME; + + echo ""; + echo ""; + + foreach ($results as $a) { + + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + } + echo "
    NameTimeAnswered
    id&id=$survey\">$a->firstname $a->lastname".date("j M Y, h:i A",$a->time)."$a->numanswers
    "; +} + + +function get_survey_responses($survey) { + return get_records_sql("SELECT a.time as time, count(*) as numanswers, u.* + FROM survey_answers AS a, user AS u + WHERE a.answer1 <> '0' AND a.answer2 <> '0' + AND a.survey = $survey + AND a.user = u.id + GROUP BY a.user ORDER BY a.time ASC"); +} diff --git a/mod/survey/save.php b/mod/survey/save.php new file mode 100644 index 0000000000000..2722740e525f4 --- /dev/null +++ b/mod/survey/save.php @@ -0,0 +1,82 @@ +course)) { + error("Course is misconfigured"); + } + + require_login($course->id); + + if (! $survey = get_record("survey", "id", $cm->instance)) { + error("Survey ID was incorrect"); + } + + add_to_log("Submit survey $survey->name", $course->id); + + if (survey_already_done($survey->id, $USER->id)) { + notice("You've already submitted this survey!", $HTTP_REFERER); + exit; + } + + +// Sort through the data and arrange it +// This is necessary because some of the questions +// may have two answers, eg Question 1 -> 1 and P1 + + $answers = array(); + + foreach ($HTTP_POST_VARS as $key => $val) { + if ($key <> "userid" && $key <> "id") { + if ( substr($key,0,1) == "q") { + $key = substr($key,1); // keep everything but the 'q' + } + if ( substr($key,0,1) == "P") { + $realkey = substr($key,1); + $answers[$realkey][1] = $val; + } else { + $answers[$key][0] = $val; + } + } + } + + +// Now store the data. + + $timenow = time(); + foreach ($answers as $key => $val) { + $val1 = $val[0]; $val2 = $val[1]; + if (! $result = $db->Execute("INSERT INTO survey_answers + (time, user, survey, question, answer1, answer2) + VALUES ('$timenow', '$USER->id', '$survey->id', '$key', '$val1', '$val2')") ) { + error("Encountered a problem trying to store your results. Sorry."); + } + } + +// Print the page and finish up. + + print_header("$course->shortname: Survey sent", "$course->fullname", + "id>$course->shortname -> + id>Surveys -> $survey->name -> Survey sent", ""); + + + notice("Thanks for your answers, $USER->firstname.", "$CFG->wwwroot/course/view.php?id=$course->id"); + + exit; + + +?> diff --git a/mod/survey/test.php b/mod/survey/test.php new file mode 100644 index 0000000000000..025a8ae4500b4 --- /dev/null +++ b/mod/survey/test.php @@ -0,0 +1,10 @@ +totalcol = 2; +for ($i=0; $i<10; $i++) { + $myxls->WriteText_pos($i, $i, "$i stuff"); +} +$myxls->SendFile(); +?> diff --git a/mod/survey/view.php b/mod/survey/view.php new file mode 100644 index 0000000000000..2421e428b48d6 --- /dev/null +++ b/mod/survey/view.php @@ -0,0 +1,245 @@ +course)) { + error("Course is misconfigured"); + } + + require_login($course->id); + + if (! $survey = get_record("survey", "id", $cm->instance)) { + error("Survey ID was incorrect"); + } + + add_to_log("View survey $survey->name", $course->id); + + print_header("$course->shortname: $survey->name", "$course->fullname", + "id>$course->shortname -> + id>Surveys -> $survey->name", ""); + + + if ($USER->editing) { + print_update_module_icon($cm->id); + } + + if (isteacher($course->id)) { + echo "

    id\">View all responses

    "; + } + + +// Check the survey hasn't already been filled out. + + if (survey_already_done($survey->id, $USER->id)) { + notice("You've already done this survey, $USER->firstname!", $HTTP_REFERER); + exit; + } + +// Start the survey form + + echo "
    "; + echo ""; + + print_simple_box(text_to_html($survey->intro), "center", "80%"); + +// Get all the major questions and their proper order + if (! $questions = get_records_sql("SELECT * FROM survey_questions WHERE id in ($survey->questions)")) { + error("Couldn't find any questions in this survey!!"); + } + $questionorder = explode( ",", $survey->questions); + +// Cycle through all the questions in order and print them + + $qnum = 0; + foreach ($questionorder as $key => $val) { + $question = $questions["$val"]; + $question->id = $val; + + if ($question->type > 0) { + if ($question->multi) { + print_multi($question); + } else { + print_single($question); + } + } + } + + +// End the survey page + echo "

     

    "; + if ($ownerpreview) { + echo "(Because this is only a preview, the button below will not send data)
    \n"; + echo ""; + } else { + echo "\n"; +?> + + + + +"; + + print_footer($course); + + exit; + + +////////////////////////////////////////////////////////////////////////////////////// + + +function print_multi($question) { + GLOBAL $db, $qnum, $checklist, $THEME; + + + echo "

     

    \n"; + echo "

    $question->text

    "; + + echo ""; + + $options = explode( ",", $question->options); + $numoptions = count($options); + + $oneanswer = ($question->type == 1 || $question->type == 2) ? true : false; + if ($question->type == 2) { + $P = "P"; + } else { + $P = ""; + } + + if ($oneanswer) { + echo ""; + } else { + echo ""; + } + + while (list ($key, $val) = each ($options)) { + echo "\n"; + } + echo "\n"; + + $subquestions = get_records_sql("SELECT * FROM survey_questions WHERE id in ($question->multi) "); + + foreach ($subquestions as $q) { + $qnum++; + $bgcolor = question_color($qnum); + + echo ""; + if ($oneanswer) { + echo ""; + echo ""; + for ($i=1;$i<=$numoptions;$i++) { + echo ""; + } + echo ""; + $checklist["q$P$q->id"] = $numoptions; + + } else { + echo ""; + echo ""; + echo ""; + for ($i=1;$i<=$numoptions;$i++) { + echo ""; + } + echo ""; + echo ""; + + echo ""; + echo ""; + for ($i=1;$i<=$numoptions;$i++) { + echo ""; + } + echo ""; + $checklist["qP$q->id"] = $numoptions; + $checklist["q$q->id"] = $numoptions; + } + echo "\n"; + } + echo "

    $question->intro

    $question->intro

    $val

    body\"> 

    $qnum

    $q->text

    id VALUE=$i>id VALUE=0 checked>

    $qnum

    I prefer that 

    $q->text

    id VALUE=$i>body\">id VALUE=0 checked>

    I found that 

    id VALUE=$i>body\">id VALUE=0 checked>
    "; +} + + + +function print_single($question) { + GLOBAL $db, $qnum; + + $bgcolor = question_color(0); + + $qnum++; + + echo "

     

    \n"; + echo "\n"; + echo ""; + echo ""; + echo "\n"; + echo "
    $qnum

    $question->text

    \n"; + + + if ($question->type == 0) { // Plain text field + echo ""; + + } else if ($question->type > 0) { // Choose one of a number + echo ""; + + } else if ($question->type < 0) { // Choose several of a number + $options = explode( ",", $question->options); + echo "

    THIS TYPE OF QUESTION NOT SUPPORTED YET

    "; + } + + echo "
    "; + +} + +function question_color($qnum) { + global $THEME; + + if ($qnum) { + return $qnum % 2 ? $THEME->cellcontent : $THEME->cellcontent2; + //return $qnum % 2 ? "#CCFFCC" : "#CCFFFF"; + } else { + return $THEME->cellcontent; + } +} + +?> diff --git a/pix/b.gif b/pix/b.gif new file mode 100755 index 0000000000000..f7df4a31baf2b Binary files /dev/null and b/pix/b.gif differ diff --git a/pix/i/ICONS-16x16 b/pix/i/ICONS-16x16 new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/pix/i/email.gif b/pix/i/email.gif new file mode 100755 index 0000000000000..effd0ca5586e9 Binary files /dev/null and b/pix/i/email.gif differ diff --git a/pix/i/log.gif b/pix/i/log.gif new file mode 100644 index 0000000000000..55d67fab2ba47 Binary files /dev/null and b/pix/i/log.gif differ diff --git a/pix/i/new.gif b/pix/i/new.gif new file mode 100755 index 0000000000000..ede003ba5681d Binary files /dev/null and b/pix/i/new.gif differ diff --git a/pix/i/news.gif b/pix/i/news.gif new file mode 100755 index 0000000000000..33e61792d87fd Binary files /dev/null and b/pix/i/news.gif differ diff --git a/pix/i/settings.gif b/pix/i/settings.gif new file mode 100755 index 0000000000000..fb12c602c4324 Binary files /dev/null and b/pix/i/settings.gif differ diff --git a/pix/madewithmoodle.gif b/pix/madewithmoodle.gif new file mode 100755 index 0000000000000..8c75321fe30d1 Binary files /dev/null and b/pix/madewithmoodle.gif differ diff --git a/pix/madewithmoodle1.gif b/pix/madewithmoodle1.gif new file mode 100755 index 0000000000000..d9c468589a134 Binary files /dev/null and b/pix/madewithmoodle1.gif differ diff --git a/pix/madewithmoodle2.gif b/pix/madewithmoodle2.gif new file mode 100644 index 0000000000000..54f054701ae3a Binary files /dev/null and b/pix/madewithmoodle2.gif differ diff --git a/pix/poo.html b/pix/poo.html new file mode 100644 index 0000000000000..8f52ae96cdfb3 --- /dev/null +++ b/pix/poo.html @@ -0,0 +1,252 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + +
    Heading
    +
    + + + + + + + + + +
    Thing One
    +
    + + + + + +
    Thing Two
    +
    + + + + + +
    Thing Three
    +
    + + + + + +
    Thing Four
    +
    + + + + + +
    Thing Five
    +
    + + + + + + + + +
    + + + + + +
    Welcome to CodeLifter...
    +
    + + + +
    + + + + + + + + diff --git a/pix/s/SMILEYS b/pix/s/SMILEYS new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/pix/s/biggrin.gif b/pix/s/biggrin.gif new file mode 100755 index 0000000000000..63aa5b2ce4ffb Binary files /dev/null and b/pix/s/biggrin.gif differ diff --git a/pix/s/cool.gif b/pix/s/cool.gif new file mode 100755 index 0000000000000..8446edf932ab5 Binary files /dev/null and b/pix/s/cool.gif differ diff --git a/pix/s/cross.gif b/pix/s/cross.gif new file mode 100755 index 0000000000000..31d2995158f52 Binary files /dev/null and b/pix/s/cross.gif differ diff --git a/pix/s/mixed.gif b/pix/s/mixed.gif new file mode 100755 index 0000000000000..91a9838ffeeed Binary files /dev/null and b/pix/s/mixed.gif differ diff --git a/pix/s/sad.gif b/pix/s/sad.gif new file mode 100755 index 0000000000000..bd5b8d35e4752 Binary files /dev/null and b/pix/s/sad.gif differ diff --git a/pix/s/smiley.gif b/pix/s/smiley.gif new file mode 100755 index 0000000000000..2438c71a2dcb3 Binary files /dev/null and b/pix/s/smiley.gif differ diff --git a/pix/s/surprise.gif b/pix/s/surprise.gif new file mode 100755 index 0000000000000..2902bece00742 Binary files /dev/null and b/pix/s/surprise.gif differ diff --git a/pix/s/tongueout.gif b/pix/s/tongueout.gif new file mode 100755 index 0000000000000..66d1719e6d51c Binary files /dev/null and b/pix/s/tongueout.gif differ diff --git a/pix/s/wideeyes.gif b/pix/s/wideeyes.gif new file mode 100755 index 0000000000000..206b992dabb85 Binary files /dev/null and b/pix/s/wideeyes.gif differ diff --git a/pix/s/wink.gif b/pix/s/wink.gif new file mode 100755 index 0000000000000..173cb0c65fd6f Binary files /dev/null and b/pix/s/wink.gif differ diff --git a/pix/spacer.gif b/pix/spacer.gif new file mode 100755 index 0000000000000..e91556e79c750 Binary files /dev/null and b/pix/spacer.gif differ diff --git a/pix/t/TINY-ICONS b/pix/t/TINY-ICONS new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/pix/t/delete.gif b/pix/t/delete.gif new file mode 100644 index 0000000000000..dd2b01f4e39cf Binary files /dev/null and b/pix/t/delete.gif differ diff --git a/pix/t/down.gif b/pix/t/down.gif new file mode 100644 index 0000000000000..e867c016e03d4 Binary files /dev/null and b/pix/t/down.gif differ diff --git a/pix/t/edit.gif b/pix/t/edit.gif new file mode 100644 index 0000000000000..bc8382c9570ae Binary files /dev/null and b/pix/t/edit.gif differ diff --git a/pix/t/search.gif b/pix/t/search.gif new file mode 100755 index 0000000000000..bfbf78696c3d3 Binary files /dev/null and b/pix/t/search.gif differ diff --git a/pix/t/up.gif b/pix/t/up.gif new file mode 100644 index 0000000000000..e2cab75b4e8fa Binary files /dev/null and b/pix/t/up.gif differ diff --git a/pix/webding.png b/pix/webding.png new file mode 100755 index 0000000000000..2a7ad4e44b2b5 Binary files /dev/null and b/pix/webding.png differ diff --git a/theme/standard/config.php b/theme/standard/config.php new file mode 100644 index 0000000000000..6806d64d175f2 --- /dev/null +++ b/theme/standard/config.php @@ -0,0 +1,10 @@ +body = "#FFEECE"; // Main page color +$THEME->cellheading = "#FFD991"; // Standard headings of big tables +$THEME->cellheading2 = "#FFB62D"; // Highlight headings of tables +$THEME->cellcontent = "#FFFFFF"; // For areas with text +$THEME->cellcontent2 = "#FFD991"; // Alternate colour +$THEME->borders = "#555555"; // Table borders + +?> diff --git a/theme/standard/footer.html b/theme/standard/footer.html new file mode 100644 index 0000000000000..ad8b555c8167b --- /dev/null +++ b/theme/standard/footer.html @@ -0,0 +1,18 @@ + +
    +
    +

    +id) { + echo "You are logged in as $USER->firstname $USER->lastname. "; + if ($USER->survey) { + echo " (Student)
    To log out you need to quit the browser."; + } else { + echo " (wwwroot/login/logout.php>Logout)"; + } + } else { + echo "You are not logged in. wwwroot/login/>Log in here."; + } +?> +

    +

    + diff --git a/theme/standard/header.html b/theme/standard/header.html new file mode 100644 index 0000000000000..b1139c7cfa2d6 --- /dev/null +++ b/theme/standard/header.html @@ -0,0 +1,26 @@ + + + <?=$title ?> + + + + javascript"); ?> + + + bgcolor="body ?>" link="#000066" vlink="#222288"> + + + + + + + +
    + + +
    + + +
    + + diff --git a/theme/standard/styles.css b/theme/standard/styles.css new file mode 100644 index 0000000000000..c04315005b99c --- /dev/null +++ b/theme/standard/styles.css @@ -0,0 +1,13 @@ +A:link { font-family: "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif; text-decoration: none; color: blue} +A:visited { font-family: "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif; text-decoration: none; color: blue} +A:hover { font-family: "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif; text-decoration: underline; color: red} + +BODY {font-family: "Trebuchet MS",Verdana, Arial, Helvetica, sans-serif} +P {font-family: "Trebuchet MS",Verdana, Arial, Helvetica, sans-serif} +H1 {font-family: "Trebuchet MS",Verdana, Arial, Helvetica, sans-serif} +H2 {font-family: "Trebuchet MS",Verdana, Arial, Helvetica, sans-serif} +H3 {font-family: "Trebuchet MS",Verdana, Arial, Helvetica, sans-serif} +H4 {font-family: "Trebuchet MS",Verdana, Arial, Helvetica, sans-serif} +TH {font-family: "Trebuchet MS",Verdana, Arial, Helvetica, sans-serif; font-weight: bold; background-color: #FFD991;} +TD {font-family: "Trebuchet MS",Verdana, Arial, Helvetica, sans-serif} + diff --git a/user/default/f1.jpg b/user/default/f1.jpg new file mode 100755 index 0000000000000..e92dd78c5cfdf Binary files /dev/null and b/user/default/f1.jpg differ diff --git a/user/default/f2.jpg b/user/default/f2.jpg new file mode 100755 index 0000000000000..2f05acd1ff7de Binary files /dev/null and b/user/default/f2.jpg differ diff --git a/user/edit.html b/user/edit.html new file mode 100644 index 0000000000000..1ca1848340f94 --- /dev/null +++ b/user/edit.html @@ -0,0 +1,60 @@ +

    Items marked private will not be made visible to other students

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    New picture:

    + + ( .jpg or .png ) + +

    Email:

    + +

    ICQ Number:

    + +

    Phone Number 1:

    (private) + +

    Phone Number 2:

    (private) + +

    Address:

    (private) + +

    Web Address:

    + +

    Description:

    +
    + + + diff --git a/user/edit.php b/user/edit.php new file mode 100644 index 0000000000000..12aac784a0211 --- /dev/null +++ b/user/edit.php @@ -0,0 +1,148 @@ +id); + + if ($USER->id <> $user->id) { + error("You can only edit your own information"); + } + + +/// If data submitted, then process and store. + + if (match_referer() && isset($HTTP_POST_VARS)) { + + $usernew = (object)$HTTP_POST_VARS; + + if (!find_form_errors($user, $usernew, $err) ) { + + $timenow = time(); + + if ($imagefile && $imagefile!="none") { + $imageinfo = GetImageSize($imagefile); + $image->width = $imageinfo[0]; + $image->height = $imageinfo[1]; + $image->type = $imageinfo[2]; + + switch ($image->type) { + case 2: $im = ImageCreateFromJPEG($imagefile); break; + case 3: $im = ImageCreateFromPNG($imagefile); break; + default: error("Image must be in JPG or PNG format"); + } + if (function_exists("ImageCreateTrueColor")) { + $im1 = ImageCreateTrueColor(100,100); + $im2 = ImageCreateTrueColor(35,35); + } else { + $im1 = ImageCreate(100,100); + $im2 = ImageCreate(35,35); + } + + $cx = $image->width / 2; + $cy = $image->height / 2; + + if ($image->width < $image->height) { + $half = floor($image->width / 2.0); + } else { + $half = floor($image->height / 2.0); + } + + if (!file_exists("$CFG->dataroot/users")) { + mkdir("$CFG->dataroot/users", 0777); + } + if (!file_exists("$CFG->dataroot/users/$USER->id")) { + mkdir("$CFG->dataroot/users/$USER->id", 0777); + } + + ImageCopyBicubic($im1, $im, 0, 0, $cx-$half, $cy-$half, 100, 100, $half*2, $half*2); + ImageCopyBicubic($im2, $im, 0, 0, $cx-$half, $cy-$half, 35, 35, $half*2, $half*2); + + // Draw borders over the top. + $black1 = ImageColorAllocate ($im1, 0, 0, 0); + $black2 = ImageColorAllocate ($im2, 0, 0, 0); + ImageLine ($im1, 0, 0, 0, 99, $black1); + ImageLine ($im1, 0, 99, 99, 99, $black1); + ImageLine ($im1, 99, 99, 99, 0, $black1); + ImageLine ($im1, 99, 0, 0, 0, $black1); + ImageLine ($im2, 0, 0, 0, 34, $black2); + ImageLine ($im2, 0, 34, 34, 34, $black2); + ImageLine ($im2, 34, 34, 34, 0, $black2); + ImageLine ($im2, 34, 0, 0, 0, $black2); + + ImageJpeg($im1, "$CFG->dataroot/users/$USER->id/f1.jpg", 90); + ImageJpeg($im2, "$CFG->dataroot/users/$USER->id/f2.jpg", 95); + $usernew->picture = "1"; + } else { + $usernew->picture = $user->picture; + } + + $usernew->timemodified = time(); + + if (update_record("user", $usernew)) { + add_to_log("Updated own profile", $course->id); + redirect("view.php?id=$user->id&course=$course->id", "Changes saved"); + } else { + error("Could not update the user record ($user->id)"); + } + } + } + +/// Otherwise fill and print the form. + + if ($course->category) { + print_header("Edit user profile", "Edit user profile", + "wwwroot/course/view.php?id=$course->id\">$course->shortname + -> id\">Participants + -> id&course=$course->id\">$USER->firstname $USER->lastname + -> Edit profile", ""); + } else { + print_header("Edit user profile", "Edit user profile", + "id&course=$course->id\">$USER->firstname $USER->lastname + -> Edit profile", ""); + } + + print_simple_box_start("center", "", "$THEME->cellheading"); + echo "

    User profile for $user->firstname $user->lastname

    "; + include("edit.html"); + print_simple_box_end(); + + print_footer($course); + + + + +/// FUNCTIONS //////////////////// + +function find_form_errors(&$user, &$usernew, &$err) { + + if (empty($usernew->email)) + $err["email"] = "Missing email address"; + + else if (! validate_email($usernew->email)) + $err["email"] = "Invalid email address, check carefully"; + + else if ($otheruser = get_record("user", "email", $usernew->email)) { + if ($otheruser->id <> $user->id) { + $err["email"] = "Email address already in use by someone else."; + } + } + + $user->email = $usernew->email; + + return count($err); +} + + +?> diff --git a/user/index.php b/user/index.php new file mode 100644 index 0000000000000..23df5e52e036e --- /dev/null +++ b/user/index.php @@ -0,0 +1,93 @@ +id); + + add_to_log("View list of all profiles", $course->id); + + if ($course->category) { + print_header("$course->shortname: Participants", "$course->fullname", + "id>$course->shortname -> Participants", ""); + } else { + print_header("$course->shortname: Participants", "$course->fullname", "Participants", ""); + } + + + $teacherlinks = isteacher($course->id); + + echo "

    ".$course->teacher."s

    "; + + if ( $teachers = get_records_sql("SELECT u.* FROM user u, user_teachers t + WHERE t.course = '$course->id' AND t.user = u.id + ORDER BY t.authority")) { + foreach ($teachers as $teacher) { + print_user($teacher, $course, $teacherlinks); + } + } else { + notify("None yet"); + } + + echo "

    Students

    "; + if ($students = get_records_sql("SELECT u.* FROM user u, user_students s + WHERE s.course = '$course->id' AND s.user = u.id + ORDER BY u.lastaccess DESC")) { + foreach ($students as $student) { + print_user($student, $course, $teacherlinks); + } + } else { + notify("None yet"); + } + + print_footer($course); + + +/// FUNCTIONS ////////////////// + +function print_user($user, $course, $teacherlinks) { + + echo "
    "; + echo ""; + echo "
    "; + echo "id&course=$course->id\">"; + if ($user->picture) { + echo "id/f1.jpg\">"; + } else { + echo ""; + } + echo ""; + echo ""; + echo ""; + echo "$user->firstname $user->lastname"; + echo "

    Email: email\">$user->email
    "; + echo "Last access: ".userdate($user->lastaccess); + echo "  (".format_time(time() - $user->lastaccess).")"; + echo "

    "; + + echo ""; + if ($teacherlinks) { + $tt = getdate(time()); + $today = mktime (0, 0, 0, $tt["mon"], $tt["mday"], $tt["year"]); + echo "id&user=$user->id\">Contributions

    "; + echo "id&user=$user->id&date=$today\">Today's logs
    "; + echo "id&user=$user->id\">All logs

    "; + if (isstudent($course->id, $user->id)) { + echo "id&user=$user->id\">Login as

    "; + } + } + echo "id&course=$course->id\">Full profile..."; + echo "
    "; + + echo "
    "; +} + +?> diff --git a/user/lib.php b/user/lib.php new file mode 100644 index 0000000000000..41000602a9718 --- /dev/null +++ b/user/lib.php @@ -0,0 +1,51 @@ += 2.0.1 as well + return ImageCopyResampled($dst_img, $src_img, $dst_x, $dst_y, $src_x, $src_y, + $dst_w, $dst_h, $src_w, $src_h); + } + + $totalcolors = imagecolorstotal($src_img); + for ($i=0; $i<$totalcolors; $i++) { + if ($colors = ImageColorsForIndex($src_img, $i)) { + ImageColorAllocate($dst_img, $colors['red'], $colors['green'], $colors['blue']); + } + } + + $scaleX = ($src_w - 1) / $dst_w; + $scaleY = ($src_h - 1) / $dst_h; + + $scaleX2 = $scaleX / 2.0; + $scaleY2 = $scaleY / 2.0; + + for ($j = 0; $j < $dst_h; $j++) { + $sY = $j * $scaleY; + + for ($i = 0; $i < $dst_w; $i++) { + $sX = $i * $scaleX; + + $c1 = ImageColorsForIndex($src_img,ImageColorAt($src_img,(int)$sX,(int)$sY+$scaleY2)); + $c2 = ImageColorsForIndex($src_img,ImageColorAt($src_img,(int)$sX,(int)$sY)); + $c3 = ImageColorsForIndex($src_img,ImageColorAt($src_img,(int)$sX+$scaleX2,(int)$sY+$scaleY2)); + $c4 = ImageColorsForIndex($src_img,ImageColorAt($src_img,(int)$sX+$scaleX2,(int)$sY)); + + $red = (int) (($c1['red'] + $c2['red'] + $c3['red'] + $c4['red']) / 4); + $green = (int) (($c1['green'] + $c2['green'] + $c3['green'] + $c4['green']) / 4); + $blue = (int) (($c1['blue'] + $c2['blue'] + $c3['blue'] + $c4['blue']) / 4); + + $color = ImageColorClosest ($dst_img, $red, $green, $blue); + ImageSetPixel ($dst_img, $i + $dst_x, $j + $dst_y, $color); + } + } +} + + +?> diff --git a/user/new.php b/user/new.php new file mode 100644 index 0000000000000..4beec74fafb7f --- /dev/null +++ b/user/new.php @@ -0,0 +1,41 @@ +id' + AND u.timemodified > '$USER->lastlogin' ")) { + + echo "

    Updated User Profiles

    "; + echo ""; + } + + if ($users = get_records_sql("SELECT u.* FROM user u, user_students s + WHERE u.id = s.user + AND s.course = '$course->id' + AND u.currentlogin > '$USER->lastlogin' ")) { + + echo "

    User logins

    "; + echo ""; + } + +?> diff --git a/user/pix.php b/user/pix.php new file mode 100644 index 0000000000000..7c56d644735b1 --- /dev/null +++ b/user/pix.php @@ -0,0 +1,36 @@ +dataroot/users/$userid/$image"; + $lastmodified = filemtime($pathname); + + header("Last-Modified: " . gmdate("D, d M Y H:i:s", $lastmodified) . " GMT"); + header("Expires: " . gmdate("D, d M Y H:i:s", time() + $lifetime) . " GMT"); + header("Cache-control: max_age = $lifetime"); // a day + header("Pragma: "); + header("Content-Length: ".filesize($pathname)); + header("Content-type: image/jpeg"); + readfile("$pathname"); + + exit; +?> diff --git a/user/user.gif b/user/user.gif new file mode 100755 index 0000000000000..33b256ece5d71 Binary files /dev/null and b/user/user.gif differ diff --git a/user/users.gif b/user/users.gif new file mode 100755 index 0000000000000..287335258e7ed Binary files /dev/null and b/user/users.gif differ diff --git a/user/view.php b/user/view.php new file mode 100644 index 0000000000000..8dd7855d5fc16 --- /dev/null +++ b/user/view.php @@ -0,0 +1,115 @@ +category) { + require_login($course->id); + } + + $fullname = "$user->firstname $user->lastname"; + + add_to_log("View profile: $fullname", $course); + + if ($course->category) { + print_header("Personal profile: $fullname", "Personal profile: $fullname", + "id\">$course->shortname -> + id\">Participants -> $fullname", ""); + } else { + print_header("Personal profile: $fullname", "Personal profile: $fullname", "$fullname", ""); + } + + + echo "
    "; + echo ""; + echo "
    body\" VALIGN=top>"; + if ($user->picture) { + echo "id/f1.jpg\">"; + } else { + echo ""; + } + echo ""; + + + // Print name and edit button across top + + echo "
    "; + echo "

    $user->firstname $user->lastname

    "; + echo "
    "; + if ($id == $USER->id) { + echo "

    "; + echo ""; + echo "id\">"; + echo ""; + echo "

    "; + } + echo "
    "; + + + + // Print the description + + if ($user->description) { + echo "

    ".text_to_html($user->description)."


    "; + } + + + + // Print all the little details in a list + + echo "city, $user->country"); + + if (isteacher($course->id)) { + if ($user->address) { + print_row("Address:", "$user->address"); + } + if ($user->phone1) { + print_row("Phone:", "$user->phone1"); + } + if ($user->phone2) { + print_row("Phone:", "$user->phone2"); + } + } + + print_row("Email:", "email\">$user->email"); + + if ($user->url) { + print_row("Web page:", "url\">$user->url"); + } + + if ($user->icq) { + print_row("ICQ:","icq\">$user->icq icq&img=5\" WIDTH=18 HEIGHT=18 BORDER=0>"); + } + + $datestring = userdate($user->lastaccess)."  (".format_time(time() - $user->lastaccess).")"; + print_row("Last access:", $datestring); + + echo "
    "; + + echo "
    "; + + print_footer($course); + +/// Functions /////// + +function print_row($left, $right) { + echo "

    $left

    $right

    "; +} + +?>