]> git.mjollnir.org Git - moodle.git/commitdiff
Merge from 19_STABLE for MDL-10639.
authorscyrma <scyrma>
Tue, 5 Feb 2008 08:42:34 +0000 (08:42 +0000)
committerscyrma <scyrma>
Tue, 5 Feb 2008 08:42:34 +0000 (08:42 +0000)
grade/edit/outcome/export.php [new file with mode: 0644]
grade/edit/outcome/import.php [new file with mode: 0644]
grade/edit/outcome/index.php
lang/en_utf8/grades.php

diff --git a/grade/edit/outcome/export.php b/grade/edit/outcome/export.php
new file mode 100644 (file)
index 0000000..5c7aff8
--- /dev/null
@@ -0,0 +1,106 @@
+<?php // $Id$
+      // Exports selected outcomes in CSV format. 
+
+require_once '../../../config.php';
+require_once $CFG->dirroot.'/grade/lib.php';
+require_once $CFG->libdir.'/gradelib.php';
+
+$courseid = optional_param('id', 0, PARAM_INT);
+$action   = optional_param('action', '', PARAM_ALPHA);
+$export   = required_param('export', PARAM_INT);
+
+/// Make sure they can even access this course
+if ($courseid) {
+    if (!$course = get_record('course', 'id', $courseid)) {
+        print_error('nocourseid');
+    }
+    require_login($course);
+    $context = get_context_instance(CONTEXT_COURSE, $course->id);
+    require_capability('moodle/grade:manage', $context);
+
+    if (empty($CFG->enableoutcomes)) {
+        redirect('../../index.php?id='.$courseid);
+    }
+
+} else {
+    require_once $CFG->libdir.'/adminlib.php';
+    admin_externalpage_setup('outcomes');
+}
+
+if (!confirm_sesskey()) {
+    break;
+}
+// $outcome = grade_outcome::fetch(array('id'=>$outcomeid));
+
+$systemcontext = get_context_instance(CONTEXT_SYSTEM);
+
+header("Content-Type: text/csv; charset=utf-8");
+// TODO: make the filename more useful, include a date, a specific name, something... 
+header('Content-Disposition: attachment; filename=outcomes.csv');
+
+// sending header with clear names, to make 'what is what' as easy as possible to understand
+$header = array('outcome_name', 'outcome_shortname', 'outcome_description', 'scale_name', 'scale_items', 'scale_description');
+echo format_csv($header, ';', '"');
+
+foreach($export as $outcome_id) {
+
+    $outcome = grade_outcome::fetch(array('id' => $outcome_id));
+
+    $line = array();
+
+    $line[] = $outcome->get_name();
+    $line[] = $outcome->get_shortname();
+    $line[] = $outcome->description;
+    
+    $scale = $outcome->load_scale();
+    $line[] = $scale->get_name();
+    $line[] = $scale->compact_items();
+    $line[] = $scale->description;
+    
+    echo format_csv($line, ';', '"');
+}
+
+/**
+ * Formats and returns a line of data, in CSV format. This code
+ * is from http://au2.php.net/manual/en/function.fputcsv.php#77866
+ *
+ * @params array-of-string $fields data to be exported
+ * @params char $delimiter char to be used to separate fields
+ * @params char $enclosure char used to enclose strings that contains newlines, spaces, tabs or the delimiter char itself
+ * @returns string one line of csv data
+ */
+function format_csv($fields = array(), $delimiter = ';', $enclosure = '"') {
+    $str = '';
+    $escape_char = '\\';
+    foreach ($fields as $value) {
+        if (strpos($value, $delimiter) !== false ||
+                strpos($value, $enclosure) !== false ||
+                strpos($value, "\n") !== false ||
+                strpos($value, "\r") !== false ||
+                strpos($value, "\t") !== false ||
+                strpos($value, ' ') !== false) {
+            $str2 = $enclosure;
+            $escaped = 0;
+            $len = strlen($value);
+            for ($i=0;$i<$len;$i++) {
+                if ($value[$i] == $escape_char) {
+                    $escaped = 1;
+                } else if (!$escaped && $value[$i] == $enclosure) {
+                    $str2 .= $enclosure;
+                } else {
+                    $escaped = 0;
+                }
+                $str2 .= $value[$i];
+            }
+            $str2 .= $enclosure;
+            $str .= $str2.$delimiter;
+        } else {
+            $str .= $value.$delimiter;
+        }
+    }
+    $str = substr($str,0,-1);
+    $str .= "\n";
+
+    return $str;
+}
+
diff --git a/grade/edit/outcome/import.php b/grade/edit/outcome/import.php
new file mode 100644 (file)
index 0000000..ff7ebfb
--- /dev/null
@@ -0,0 +1,250 @@
+<?php // $Id$
+// Allows a user to import outcomes (and associated scales)
+
+///////////////////////////////////////////////////////////////////////////
+//                                                                       //
+// NOTICE OF COPYRIGHT                                                   //
+//                                                                       //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment         //
+//          http://moodle.com                                            //
+//                                                                       //
+// Copyright (C) 1999 onwards  Martin Dougiamas  http://moodle.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:                          //
+//                                                                       //
+//          http://www.gnu.org/copyleft/gpl.html                         //
+//                                                                       //
+///////////////////////////////////////////////////////////////////////////
+
+require_once '../../../config.php';
+require_once $CFG->dirroot.'/grade/lib.php';
+require_once $CFG->libdir.'/gradelib.php';
+
+$courseid = optional_param('id', 0, PARAM_INT);
+$action   = optional_param('action', '', PARAM_ALPHA);
+$scope    = optional_param('scope', 'global', PARAM_ALPHA);
+
+/// Make sure they can even access this course
+if ($courseid) {
+    if (!$course = get_record('course', 'id', $courseid)) {
+        print_error('nocourseid');
+    }
+    require_login($course);
+    $context = get_context_instance(CONTEXT_COURSE, $course->id);
+
+    if (empty($CFG->enableoutcomes)) {
+        redirect('../../index.php?id='.$courseid);
+    }
+
+} else {
+    require_once $CFG->libdir.'/adminlib.php';
+    admin_externalpage_setup('outcomes');
+    $context = get_context_instance(CONTEXT_SYSTEM);
+}
+
+require_capability('moodle/grade:manageoutcomes', $context);
+
+$strgrades = get_string('grades');
+$pagename  = get_string('outcomes', 'grades');
+
+$navigation = grade_build_nav(__FILE__, $pagename, $courseid);
+
+$strshortname        = get_string('shortname');
+$strfullname         = get_string('fullname');
+$strscale            = get_string('scale');
+$strstandardoutcome  = get_string('outcomesstandard', 'grades');
+$strcustomoutcomes   = get_string('outcomescustom', 'grades');
+$strdelete           = get_string('delete');
+$stredit             = get_string('edit');
+$srtcreatenewoutcome = get_string('outcomecreate', 'grades');
+$stritems            = get_string('items', 'grades');
+$strcourses          = get_string('courses');
+$stredit             = get_string('edit');
+$strexport           = get_string('export', 'grades');
+
+if (!confirm_sesskey()) {
+    break;
+}
+
+$systemcontext = get_context_instance(CONTEXT_SYSTEM);
+$caneditsystemscales = has_capability('moodle/course:managescales', $systemcontext);
+
+if ($courseid) {
+    /// Print header
+    print_header_simple($strgrades.': '.$pagename, ': '.$strgrades, $navigation, '', '', true, '', navmenu($course));
+    /// Print the plugin selector at the top
+    print_grade_plugin_selector($courseid, 'edit', 'outcome');
+
+    $caneditcoursescales = has_capability('moodle/course:managescales', $context);
+
+    $currenttab = 'outcomes';
+    require('tabs.php');
+
+} else {
+    admin_externalpage_print_header();
+    $caneditcoursescales = $caneditsystemscales;
+}
+
+if ($_FILES['userfile']['size'] == 0) {
+    redirect('index.php'. ($courseid ? "?id=$courseid" : ''), get_string('importfilemissing', 'grades'));
+}
+
+/// which scope are we importing the outcomes in?
+if (isset($courseid) && ($scope  == 'local')) {
+    // custom scale
+    $local_scope = true;
+} elseif (($scope == 'global') && has_capability('moodle/grade:manage', get_context_instance(CONTEXT_SYSTEM))) {
+    // global scale
+    $local_scope = false;
+} else {
+    // shouldn't happen .. user might be trying to access this script without the right permissions.
+    redirect('index.php', get_string('importerror', 'grades'));
+}
+
+// open the file, start importing data
+if ($handle = fopen($_FILES['userfile']['tmp_name'], 'r')) {
+    $line = 0; // will keep track of current line, to give better error messages. 
+    $file_headers = '';
+
+    // $csv_data needs to have at least these columns, the value is the default position in the data file.
+    $headers = array('outcome_name' => 0, 'outcome_shortname' => 1, 'scale_name' => 3, 'scale_items' => 4);
+    $optional_headers = array('outcome_description'=>2, 'scale_description' => 5);
+    $imported_headers = array(); // will later be initialized with the values found in the file
+
+    // data should be separated by a ';'.  *NOT* by a comma!  TODO: version 2.0 
+    // or whenever we can depend on PHP5, set the second parameter (8192) to 0 (unlimited line length) : the database can store over 128k per line.
+    while ( $csv_data = fgetcsv($handle, 8192, ';', '"')) { // if the line is over 8k, it won't work...
+        $line++;
+        
+        // be tolerant on input, as fgetcsv returns "an array comprising a single null field" on blank lines
+        if ($csv_data == array(null)) {
+            continue;
+        }
+        
+        // on first run, grab and analyse the header
+        if ($file_headers == '') {
+            
+            $file_headers = array_flip($csv_data); // save the header line ... TODO: use the header line to let import work with columns in arbitrary order
+            
+            $error = false;
+            foreach($headers as $key => $value) {
+                // sanity check #1: make sure the file contains all the mandatory headers
+                if (!array_key_exists($key, $file_headers)) {
+                    $error = true;
+                    break;
+                }
+            }
+            if ($error) {
+                print_box(get_string('importoutcomenofile', 'grades', $line));
+                break;
+            }
+
+            foreach(array_merge($headers, $optional_headers) as $header => $position) {
+                // match given columns to expected columns *into* $headers
+                $imported_headers[$header] = $file_headers[$header];
+            }
+
+            continue; // we don't import headers
+        }
+
+        // sanity check #2: every line must have the same number of columns as there are 
+        // headers.  If not, processing stops.
+        if ( count($csv_data) != count($file_headers) ) {  
+            print_box(get_string('importoutcomenofile', 'grades', $line));
+            //print_box(var_export($csv_data, true) ."<br />". var_export($header, true));
+            break;
+        } 
+
+        // sanity check #3: all required fields must be present on the current line.
+        foreach ($headers as $header => $position) {
+            if ($csv_data[$imported_headers[$header]] == '') {
+                print_box(get_string('importoutcomenofile', 'grades', $line));
+                break;
+            }
+        }
+
+        //var_dump($csv_data);
+        //$db->debug = 3498723498237; // .. very large randomly-typed random value
+
+        if ($local_scope) {
+            $outcome = get_records_select('grade_outcomes', 'shortname = "'. $csv_data[$imported_headers['outcome_shortname']] .'" and courseid = '. $courseid );
+        } else {
+            $outcome = get_records_select('grade_outcomes', 'shortname = "'. $csv_data[$imported_headers['outcome_shortname']] .'" and courseid is null');
+        }
+        //var_export($outcome);
+
+        if ($outcome) {
+            // already exists, print a message and skip.
+            print_box(get_string('importskippedoutcome', 'grades', $csv_data[$imported_headers['outcome_shortname']]));
+            continue;
+        }
+
+        // new outcome will be added, search for compatible existing scale...
+        $scale = get_records_select('scale', 'name ="'. $csv_data[$imported_headers['scale_name']] .'" and scale ="'. $csv_data[$imported_headers['scale_items']] .'" and (courseid = '. $courseid .' or courseid = 0)');
+
+        if ($scale) {
+            // already exists in the right scope: use it.
+            $scale_id = key($scale);
+        } else {
+            if (!has_capability('moodle/course:managescales', $context)) {
+                print_box(get_string('importskippednomanagescale', 'grades', $csv_data[$imported_headers['outcome_shortname']]));
+                continue;
+            } else { 
+                // scale doesn't exists : create it.
+                $scale_data = array('name' => $csv_data[$imported_headers['scale_name']], 
+                        'scale' => $csv_data[$imported_headers['scale_items']], 
+                        'description' => $csv_data[$imported_headers['scale_description']], 
+                        'userid' => $USER->id);
+
+                if ($local_scope) {
+                    $scale_data['courseid'] = $courseid;
+                } else {
+                    $scale_data['courseid'] = 0; // 'global' : scale use '0', outcomes use null
+                }
+                $scale = new grade_scale($scale_data);
+                $scale_id = $scale->insert();
+            }
+        }
+
+        // add outcome
+        $outcome_data = array('shortname' => $csv_data[$imported_headers['outcome_shortname']], 
+                'fullname' => $csv_data[$imported_headers['outcome_name']], 
+                'scaleid' => $scale_id, 
+                'description' => $csv_data[$imported_headers['outcome_description']], 
+                'usermodified' => $USER->id);
+
+        if ($local_scope) {
+            $outcome_data['courseid'] = $courseid;
+        } else {
+            $outcome_data['courseid'] = null; // 'global' : scale use '0', outcomes use null
+        }
+        $outcome = new grade_outcome($outcome_data);
+        $outcome_id = $outcome->insert();
+
+        $outcome_success_strings = new StdClass();
+        $outcome_success_strings->name = $outcome_data['fullname'];
+        $outcome_success_strings->id = $outcome_id;
+        print_box(get_string('importoutcomesuccess', 'grades', $outcome_success_strings));
+    }
+} else {
+    print_box(get_string('importoutcomenofile', 'grades', 0));
+}
+
+// finish
+fclose($handle);
+
+if ($courseid) {
+    print_footer($course);
+} else {
+    admin_externalpage_print_footer();
+}
+
+?>
index 9d554dd7765b496fe45c24bc8a193e8f539aaedb..4c8376d6cf6c50ed0983c3d95429c675a40183e0 100644 (file)
@@ -38,7 +38,7 @@ if ($courseid) {
     }
     require_login($course);
     $context = get_context_instance(CONTEXT_COURSE, $course->id);
-    require_capability('moodle/grade:manage', $context);
+    require_capability('moodle/grade:manageoutcomes', $context);
 
     if (empty($CFG->enableoutcomes)) {
         redirect('../../index.php?id='.$courseid);
@@ -69,6 +69,7 @@ $srtcreatenewoutcome = get_string('outcomecreate', 'grades');
 $stritems            = get_string('items', 'grades');
 $strcourses          = get_string('courses');
 $stredit             = get_string('edit');
+$strexport           = get_string('export', 'grades');
 
 switch ($action) {
     case 'delete':
@@ -115,7 +116,7 @@ if ($courseid) {
     $caneditcoursescales = $caneditsystemscales;
 }
 
-
+print('<form action="export.php" method="post">' ."\n");
 
 if ($courseid and $outcomes = grade_outcome::fetch_all_local($courseid)) {
 
@@ -157,19 +158,25 @@ if ($courseid and $outcomes = grade_outcome::fetch_all_local($courseid)) {
                         " src=\"$CFG->pixpath/t/delete.gif\" class=\"iconsmall\" alt=\"$strdelete\" /></a> ";
         }
         $line[] = $buttons;
+        
+        $buttons = '<input type="checkbox" name="export[]" value="'. $outcome->id .'">';
+        $line[] = $buttons;
+
         $data[] = $line;
     }
     $table = new object();
-    $table->head  = array($strfullname, $strshortname, $strscale, $stritems, $stredit);
-    $table->size  = array('30%', '20%', '20%', '20%', '10%');
-    $table->align = array('left', 'left', 'left', 'center', 'center');
+    $table->head  = array($strfullname, $strshortname, $strscale, $stritems, $stredit, $strexport);
+    $table->size  = array('30%', '18%', '18%', '18%', '8%', '8%' );
+    $table->align = array('left', 'left', 'left', 'center', 'center', 'center');
     $table->width = '90%';
     $table->data  = $data;
     print_table($table);
 }
 
+
 if ($outcomes = grade_outcome::fetch_all_global()) {
-    print_heading($strstandardoutcome);
+    
+    print_heading($strstandardoutcome); 
     $data = array();
     foreach($outcomes as $outcome) {
         $line = array();
@@ -210,12 +217,16 @@ if ($outcomes = grade_outcome::fetch_all_global()) {
                         " src=\"$CFG->pixpath/t/delete.gif\" class=\"iconsmall\" alt=\"$strdelete\" /></a> ";
         }
         $line[] = $buttons;
+
+        $buttons = '<input type="checkbox" name="export[]" value="'. $outcome->id .'">';
+        $line[] = $buttons;
+
         $data[] = $line;
     }
     $table = new object();
-    $table->head  = array($strfullname, $strshortname, $strscale, $strcourses, $stritems, $stredit);
-    $table->size  = array('30%', '20%', '20%', '10%', '10%', '10%');
-    $table->align = array('left', 'left', 'left', 'center', 'center', 'center');
+    $table->head  = array($strfullname, $strshortname, $strscale, $strcourses, $stritems, $stredit, $strexport);
+    $table->size  = array('30%', '19%', '19%', '8%', '8%', '8%', '8%');
+    $table->align = array('left', 'left', 'left', 'center', 'center', 'center', 'center');
     $table->width = '90%';
     $table->data  = $data;
     print_table($table);
@@ -223,9 +234,44 @@ if ($outcomes = grade_outcome::fetch_all_global()) {
 
 
 echo '<div class="buttons">';
+echo "<input type=\"hidden\" name=\"sesskey\" value=\"$USER->sesskey\" />";
+print('<input type="submit" value="'. get_string('exportselectedoutcomes', 'grades') .'" name="export_outcomes"></form>');
 print_single_button('edit.php', array('courseid'=>$courseid), $srtcreatenewoutcome);
 echo '</div>';
 
+echo '<div>'; 
+$upload_max_filesize = get_max_upload_file_size($CFG->maxbytes);
+$filesize = display_size($upload_max_filesize);
+
+$strimportoutcomes = get_string('importoutcomes', 'grades');
+$struploadthisfile = get_string('uploadthisfile');
+$strimportcustom = get_string('importcustom', 'grades');
+$strimportstandard = get_string('importstandard', 'grades');
+$strmaxsize = get_string("maxsize", "", $filesize);
+
+require_once($CFG->dirroot.'/lib/uploadlib.php');
+
+echo '<div>';
+echo '<form enctype="multipart/form-data" method="post" action="import.php">';
+echo '<input type="hidden" name="action" value="upload" />';
+echo '<input type="hidden" name="id" value="'. $courseid .'" />';
+echo '<input type="hidden" name="sesskey" value="'. $USER->sesskey .'" />';
+echo '<table class="generalbox boxaligncenter" width="50%" cellspacing="1" cellpadding="5">';
+if ($courseid && has_capability('moodle/grade:manage', get_context_instance(CONTEXT_SYSTEM))) {
+    echo '<tr><td><ul style="list-style-type:none;">';
+    echo '<li><label><input type="radio" name="scope" value="local" checked="checked">'. $strimportcustom .'</label>';
+    echo '<li><label><input type="radio" name="scope" value="global">'. $strimportstandard .'</label>';
+    echo '</ul></td></tr>';
+}
+echo '<tr><td><p>'. $strimportoutcomes .'('. $strmaxsize .')</p></td></tr>';
+echo '<tr><td>'. 
+    upload_print_form_fragment(1,array('userfile'),null,false,null,$upload_max_filesize,0,true) .
+    '<input type="submit" name="save" value="'. $struploadthisfile .'" /></td></tr>';
+echo '</table>';
+echo '</div>';
+echo '</form>';
+echo '</div>';
+
 if ($courseid) {
     print_footer($course);
 } else {
index 2ebea12b4aba27a305a5e46591019637c174865a..e920164c05e4687c5b8cfb5d98d30839cda2a9c8 100644 (file)
@@ -158,6 +158,7 @@ $string['expand'] = 'Expand Category';
 $string['export'] = 'Export';
 $string['exportfeedback'] = 'Include feedback in export';
 $string['exportplugins'] = 'Export plugins';
+$string['exportselectedoutcomes'] = 'Export selected outcomes';
 $string['extracredit'] = 'Extra Credit';
 $string['extracreditwarning'] = 'Note: Setting all items for a category to extra credit will effectively remove them from the grade calculation. Since there will be no point total';
 $string['feedback'] = 'Feedback';
@@ -253,11 +254,20 @@ $string['idnumbers'] = 'Id numbers';
 $string['identifier'] = 'Identify user by';
 $string['import'] = 'Import';
 $string['importcsv'] = 'Import CSV';
+$string['importcustom'] = 'Import as custom outcomes (only this course)';
+$string['importerror'] = 'An error occured, this script wasn\'t called with the right parameters.';
 $string['importfailed'] = 'Import failed';
 $string['importfeedback'] = 'Import feedback';
 $string['importfile'] = 'Import file';
+$string['importfilemissing'] = 'No file was received, go back to the form and make sure to upload a valid file.';
+$string['importoutcomenofile'] = 'The uploaded file is empty or corrupted.  Please verify this is a valid file. The problem was detected at line $a; this is triggered by the data lines not having as many columns as the first line (the header line) or if the imported file is missing expected headers.  Look at the exported file for an example of a file with valid header.';
+$string['importoutcomes'] = 'Import outcomes';
+$string['importoutcomesuccess'] = 'Imported outcome \"$a->name\" with ID #$a->id';
 $string['importplugins'] = 'Import plugins';
 $string['importpreview'] = 'Import preview';
+$string['importstandard'] = 'Import as standard outcomes';
+$string['importskippednomanagescale'] = 'You don\'t have permission to add a new scale, so outcome "$a" was skipped as it required creating a new scale';
+$string['importskippedoutcome'] = 'An outcome with shortname \"$a\" already exists in this context, the one in the imported file was skipped.';
 $string['importsuccess'] = 'Grade import success';
 $string['importxml'] = 'Import XML';
 $string['incorrectcourseid'] = 'Course ID was incorrect';