MDL-41286 report_outline: updated report to use logging api
authorMark Nelson <markn@moodle.com>
Mon, 10 Mar 2014 01:11:53 +0000 (18:11 -0700)
committerMark Nelson <markn@moodle.com>
Wed, 16 Apr 2014 05:42:59 +0000 (22:42 -0700)
AMOS START
 MOV [neverseen, mod_folder],[neverseen, report_outline]
 MOV [neverseen, mod_page],[neverseen, report_outline]
 MOV [neverseen, mod_resource],[neverseen, report_outline]
 MOV [neverseen, mod_url],[neverseen, report_outline]
AMOS END

19 files changed:
mod/book/lib.php
mod/chat/lib.php
mod/folder/lang/en/folder.php
mod/folder/lib.php
mod/imscp/lib.php
mod/page/lang/en/page.php
mod/page/lib.php
mod/resource/lang/en/resource.php
mod/resource/lib.php
mod/upgrade.txt
mod/url/lang/en/url.php
mod/url/lib.php
mod/wiki/lib.php
report/outline/index.php
report/outline/lang/en/report_outline.php
report/outline/locallib.php
report/outline/tests/behat/outline.feature [new file with mode: 0644]
report/outline/tests/behat/user.feature [new file with mode: 0644]
report/outline/user.php

index 3eacef5..1139da0 100644 (file)
@@ -112,47 +112,6 @@ function book_delete_instance($id) {
     return true;
 }
 
-/**
- * Return use outline
- *
- * @param stdClass $course
- * @param stdClass $user
- * @param stdClass $mod
- * @param object $book
- * @return object|null
- */
-function book_user_outline($course, $user, $mod, $book) {
-    global $DB;
-
-    if ($logs = $DB->get_records('log', array('userid'=>$user->id, 'module'=>'book',
-                                              'action'=>'view', 'info'=>$book->id), 'time ASC')) {
-
-        $numviews = count($logs);
-        $lastlog = array_pop($logs);
-
-        $result = new stdClass();
-        $result->info = get_string('numviews', '', $numviews);
-        $result->time = $lastlog->time;
-
-        return $result;
-    }
-    return null;
-}
-
-/**
- * Print a detailed representation of what a  user has done with
- * a given particular instance of this module, for user activity reports.
- *
- * @param stdClass $course
- * @param stdClass $user
- * @param stdClass $mod
- * @param stdClass $book
- * @return bool
- */
-function book_user_complete($course, $user, $mod, $book) {
-    return true;
-}
-
 /**
  * Given a course and a time, this module should find recent activity
  * that has occurred in book activities and print it out.
index 4a93983..7fd094a 100644 (file)
@@ -203,39 +203,6 @@ function chat_delete_instance($id) {
     return $result;
 }
 
-/**
- * Return a small object with summary information about what a
- * user has done with a given particular instance of this module
- * Used for user activity reports.
- * <code>
- * $return->time = the time they did it
- * $return->info = a short text description
- * </code>
- *
- * @param object $course
- * @param object $user
- * @param object $mod
- * @param object $chat
- * @return void
- */
-function chat_user_outline($course, $user, $mod, $chat) {
-    return NULL;
-}
-
-/**
- * Print a detailed representation of what a  user has done with
- * a given particular instance of this module, for user activity reports.
- *
- * @param object $course
- * @param object $user
- * @param object $mod
- * @param object $chat
- * @return bool
- */
-function chat_user_complete($course, $user, $mod, $chat) {
-    return true;
-}
-
 /**
  * Given a course and a date, prints a summary of all chat rooms past and present
  * This function is called from block_recent_activity
index 5a42a15..f17e18a 100644 (file)
@@ -39,7 +39,6 @@ A folder may be used
 * To provide a shared uploading space for teachers on the course page (keeping the folder hidden so that only teachers can see it)';
 $string['modulename_link'] = 'mod/folder/view';
 $string['modulenameplural'] = 'Folders';
-$string['neverseen'] = 'Never seen';
 $string['page-mod-folder-x'] = 'Any folder module page';
 $string['page-mod-folder-view'] = 'Folder module main page';
 $string['pluginadministration'] = 'Folder administration';
index 3cf7675..b2014f6 100644 (file)
@@ -168,57 +168,6 @@ function folder_delete_instance($id) {
     return true;
 }
 
-/**
- * Return use outline
- * @param object $course
- * @param object $user
- * @param object $mod
- * @param object $folder
- * @return object|null
- */
-function folder_user_outline($course, $user, $mod, $folder) {
-    global $DB;
-
-    if ($logs = $DB->get_records('log', array('userid'=>$user->id, 'module'=>'folder',
-                                              'action'=>'view', 'info'=>$folder->id), 'time ASC')) {
-
-        $numviews = count($logs);
-        $lastlog = array_pop($logs);
-
-        $result = new stdClass();
-        $result->info = get_string('numviews', '', $numviews);
-        $result->time = $lastlog->time;
-
-        return $result;
-    }
-    return NULL;
-}
-
-/**
- * Return use complete
- * @param object $course
- * @param object $user
- * @param object $mod
- * @param object $folder
- */
-function folder_user_complete($course, $user, $mod, $folder) {
-    global $CFG, $DB;
-
-    if ($logs = $DB->get_records('log', array('userid'=>$user->id, 'module'=>'folder',
-                                              'action'=>'view', 'info'=>$folder->id), 'time ASC')) {
-        $numviews = count($logs);
-        $lastlog = array_pop($logs);
-
-        $strmostrecently = get_string('mostrecently');
-        $strnumviews = get_string('numviews', '', $numviews);
-
-        echo "$strnumviews - $strmostrecently ".userdate($lastlog->time);
-
-    } else {
-        print_string('neverseen', 'folder');
-    }
-}
-
 /**
  * Lists all browsable file areas
  *
index d14013f..97c6d51 100644 (file)
@@ -219,57 +219,6 @@ function imscp_delete_instance($id) {
     return true;
 }
 
-/**
- * Return use outline
- * @param object $course
- * @param object $user
- * @param object $mod
- * @param object $imscp
- * @return object|null
- */
-function imscp_user_outline($course, $user, $mod, $imscp) {
-    global $DB;
-
-    if ($logs = $DB->get_records('log', array('userid'=>$user->id, 'module'=>'imscp',
-                                              'action'=>'view', 'info'=>$imscp->id), 'time ASC')) {
-
-        $numviews = count($logs);
-        $lastlog = array_pop($logs);
-
-        $result = new stdClass();
-        $result->info = get_string('numviews', '', $numviews);
-        $result->time = $lastlog->time;
-
-        return $result;
-    }
-    return NULL;
-}
-
-/**
- * Return use complete
- * @param object $course
- * @param object $user
- * @param object $mod
- * @param object $imscp
- */
-function imscp_user_complete($course, $user, $mod, $imscp) {
-    global $CFG, $DB;
-
-    if ($logs = $DB->get_records('log', array('userid'=>$user->id, 'module'=>'imscp',
-                                              'action'=>'view', 'info'=>$imscp->id), 'time ASC')) {
-        $numviews = count($logs);
-        $lastlog = array_pop($logs);
-
-        $strmostrecently = get_string('mostrecently');
-        $strnumviews = get_string('numviews', '', $numviews);
-
-        echo "$strnumviews - $strmostrecently ".userdate($lastlog->time);
-
-    } else {
-        print_string('neverseen', 'imscp');
-    }
-}
-
 /**
  * Lists all browsable file areas
  *
index e992842..44c7215 100644 (file)
@@ -46,7 +46,6 @@ A page may be used
 * To embed several videos or sound files together with some explanatory text';
 $string['modulename_link'] = 'mod/page/view';
 $string['modulenameplural'] = 'Pages';
-$string['neverseen'] = 'Never seen';
 $string['optionsheader'] = 'Display options';
 $string['page-mod-page-x'] = 'Any page module page';
 $string['page:addinstance'] = 'Add a new page resource';
index e775e58..c36031a 100644 (file)
@@ -191,57 +191,6 @@ function page_delete_instance($id) {
     return true;
 }
 
-/**
- * Return use outline
- * @param object $course
- * @param object $user
- * @param object $mod
- * @param object $page
- * @return object|null
- */
-function page_user_outline($course, $user, $mod, $page) {
-    global $DB;
-
-    if ($logs = $DB->get_records('log', array('userid'=>$user->id, 'module'=>'page',
-                                              'action'=>'view', 'info'=>$page->id), 'time ASC')) {
-
-        $numviews = count($logs);
-        $lastlog = array_pop($logs);
-
-        $result = new stdClass();
-        $result->info = get_string('numviews', '', $numviews);
-        $result->time = $lastlog->time;
-
-        return $result;
-    }
-    return NULL;
-}
-
-/**
- * Return use complete
- * @param object $course
- * @param object $user
- * @param object $mod
- * @param object $page
- */
-function page_user_complete($course, $user, $mod, $page) {
-    global $CFG, $DB;
-
-    if ($logs = $DB->get_records('log', array('userid'=>$user->id, 'module'=>'page',
-                                              'action'=>'view', 'info'=>$page->id), 'time ASC')) {
-        $numviews = count($logs);
-        $lastlog = array_pop($logs);
-
-        $strmostrecently = get_string('mostrecently');
-        $strnumviews = get_string('numviews', '', $numviews);
-
-        echo "$strnumviews - $strmostrecently ".userdate($lastlog->time);
-
-    } else {
-        print_string('neverseen', 'page');
-    }
-}
-
 /**
  * Given a course_module object, this function returns any
  * "extra" information that may be needed when printing
index da2bce8..ee8de87 100644 (file)
@@ -75,7 +75,6 @@ A file may be used
 * To provide draft files of certain software programs (eg Photoshop .psd) so students can edit and submit them for assessment';
 $string['modulename_link'] = 'mod/resource/view';
 $string['modulenameplural'] = 'Files';
-$string['neverseen'] = 'Never seen';
 $string['notmigrated'] = 'This legacy resource type ({$a}) was not yet migrated, sorry.';
 $string['optionsheader'] = 'Display options';
 $string['page-mod-resource-x'] = 'Any file module page';
index 903e227..3fcb1bc 100644 (file)
@@ -177,57 +177,6 @@ function resource_delete_instance($id) {
     return true;
 }
 
-/**
- * Return use outline
- * @param object $course
- * @param object $user
- * @param object $mod
- * @param object $resource
- * @return object|null
- */
-function resource_user_outline($course, $user, $mod, $resource) {
-    global $DB;
-
-    if ($logs = $DB->get_records('log', array('userid'=>$user->id, 'module'=>'resource',
-                                              'action'=>'view', 'info'=>$resource->id), 'time ASC')) {
-
-        $numviews = count($logs);
-        $lastlog = array_pop($logs);
-
-        $result = new stdClass();
-        $result->info = get_string('numviews', '', $numviews);
-        $result->time = $lastlog->time;
-
-        return $result;
-    }
-    return NULL;
-}
-
-/**
- * Return use complete
- * @param object $course
- * @param object $user
- * @param object $mod
- * @param object $resource
- */
-function resource_user_complete($course, $user, $mod, $resource) {
-    global $CFG, $DB;
-
-    if ($logs = $DB->get_records('log', array('userid'=>$user->id, 'module'=>'resource',
-                                              'action'=>'view', 'info'=>$resource->id), 'time ASC')) {
-        $numviews = count($logs);
-        $lastlog = array_pop($logs);
-
-        $strmostrecently = get_string('mostrecently');
-        $strnumviews = get_string('numviews', '', $numviews);
-
-        echo "$strnumviews - $strmostrecently ".userdate($lastlog->time);
-
-    } else {
-        print_string('neverseen', 'resource');
-    }
-}
-
 /**
  * Given a course_module object, this function returns any
  * "extra" information that may be needed when printing
index 7c037a8..f761107 100644 (file)
@@ -8,6 +8,10 @@ information provided here is intended especially for developers.
   $plugin instead. The support for the legacy notation will be dropped in Moodle 2.10.
 * xxx_get_view_actions() and xxx_get_post_actions() will be ignored by new logging system for
   participation report. view_action and post_action will be detected by event's crud and edulevel.
+* The functions xxx_user_outline() and xxx_user_complete() have been removed from the majority of core modules (see MDL-41286),
+  except for those that require unique functionality. These functions are used by the outline report, but now if they no longer
+  exist, the default behaviour is chosen, which supports the legacy and standard log storages introduced in 2.7 (see MDL-41266).
+  It is highly recommended you remove these functions from your module if they are simply performing the default behaviour.
 
 === 2.6 ===
 
index f59ba18..d9a4026 100644 (file)
@@ -54,7 +54,6 @@ There are a number of display options for the URL, such as embedded or opening i
 Note that URLs can also be added to any other resource or activity type through the text editor.';
 $string['modulename_link'] = 'mod/url/view';
 $string['modulenameplural'] = 'URLs';
-$string['neverseen'] = 'Never seen';
 $string['page-mod-url-x'] = 'Any URL module page';
 $string['parameterinfo'] = '&amp;parameter=variable';
 $string['parametersheader'] = 'URL variables';
index b33b6e4..64d7ce0 100644 (file)
@@ -193,57 +193,6 @@ function url_delete_instance($id) {
     return true;
 }
 
-/**
- * Return use outline
- * @param object $course
- * @param object $user
- * @param object $mod
- * @param object $url
- * @return object|null
- */
-function url_user_outline($course, $user, $mod, $url) {
-    global $DB;
-
-    if ($logs = $DB->get_records('log', array('userid'=>$user->id, 'module'=>'url',
-                                              'action'=>'view', 'info'=>$url->id), 'time ASC')) {
-
-        $numviews = count($logs);
-        $lastlog = array_pop($logs);
-
-        $result = new stdClass();
-        $result->info = get_string('numviews', '', $numviews);
-        $result->time = $lastlog->time;
-
-        return $result;
-    }
-    return NULL;
-}
-
-/**
- * Return use complete
- * @param object $course
- * @param object $user
- * @param object $mod
- * @param object $url
- */
-function url_user_complete($course, $user, $mod, $url) {
-    global $CFG, $DB;
-
-    if ($logs = $DB->get_records('log', array('userid'=>$user->id, 'module'=>'url',
-                                              'action'=>'view', 'info'=>$url->id), 'time ASC')) {
-        $numviews = count($logs);
-        $lastlog = array_pop($logs);
-
-        $strmostrecently = get_string('mostrecently');
-        $strnumviews = get_string('numviews', '', $numviews);
-
-        echo "$strnumviews - $strmostrecently ".userdate($lastlog->time);
-
-    } else {
-        print_string('neverseen', 'url');
-    }
-}
-
 /**
  * Given a course_module object, this function returns any
  * "extra" information that may be needed when printing
index 2b21413..8db22c8 100644 (file)
@@ -194,32 +194,6 @@ function wiki_reset_course_form_definition(&$mform) {
     $mform->addElement('advcheckbox', 'reset_wiki_comments', get_string('deleteallcomments'));
 }
 
-/**
- * Return a small object with summary information about what a
- * user has done with a given particular instance of this module
- * Used for user activity reports.
- * $return->time = the time they did it
- * $return->info = a short text description
- *
- * @return null
- * @todo Finish documenting this function
- **/
-function wiki_user_outline($course, $user, $mod, $wiki) {
-    $return = NULL;
-    return $return;
-}
-
-/**
- * Print a detailed representation of what a user has done with
- * a given particular instance of this module, for user activity reports.
- *
- * @return boolean
- * @todo Finish documenting this function
- **/
-function wiki_user_complete($course, $user, $mod, $wiki) {
-    return true;
-}
-
 /**
  * Indicates API features that the wiki supports.
  *
index 91c3b6a..f3733c7 100644 (file)
@@ -60,11 +60,32 @@ $PAGE->set_heading($course->fullname);
 echo $OUTPUT->header();
 echo $OUTPUT->heading(format_string($course->fullname));
 
-if (!$logstart = $DB->get_field_sql("SELECT MIN(time) FROM {log}")) {
-    print_error('logfilenotavailable');
+list($uselegacyreader, $useinternalreader, $minloginternalreader, $logtable) = report_outline_get_common_log_variables();
+
+// If no legacy and no internal log then don't proceed.
+if (!$uselegacyreader && !$useinternalreader) {
+    echo $OUTPUT->box_start('generalbox', 'notice');
+    echo $OUTPUT->notification(get_string('nologreaderenabled', 'report_outline'));
+    echo $OUTPUT->box_end();
+    echo $OUTPUT->footer();
+    die();
 }
 
-echo $OUTPUT->container(get_string('computedfromlogs', 'admin', userdate($logstart)), 'loginfo');
+// We want to display the time we are beginning to get logs from in the heading.
+// If we are using the legacy reader check the minimum time in that log table.
+if ($uselegacyreader) {
+    $minlog = $DB->get_field_sql('SELECT min(time) FROM {log}');
+}
+
+// If we are using the internal reader check the minimum time in that table.
+if ($useinternalreader) {
+    // If new log table has older data then don't use the minimum time obtained from the legacy table.
+    if (empty($minlog) || ($minloginternalreader <= $minlog)) {
+        $minlog = $minloginternalreader;
+    }
+}
+
+echo $OUTPUT->container(get_string('computedfromlogs', 'admin', userdate($minlog)), 'loginfo');
 
 $outlinetable = new html_table();
 $outlinetable->attributes['class'] = 'generaltable boxaligncenter';
@@ -82,13 +103,65 @@ if ($showlastaccess) {
 
 $modinfo = get_fast_modinfo($course);
 
-$sql = "SELECT cm.id, COUNT('x') AS numviews, MAX(time) AS lasttime
-          FROM {course_modules} cm
-               JOIN {modules} m ON m.id = cm.module
-               JOIN {log} l     ON l.cmid = cm.id
-         WHERE cm.course = ? AND l.action LIKE 'view%' AND m.visible = 1
-      GROUP BY cm.id";
-$views = $DB->get_records_sql($sql, array($course->id));
+// If using legacy log then get users from old table.
+if ($uselegacyreader) {
+    // If we are going to use the internal (not legacy) log table, we should only get records
+    // from the legacy table that exist before we started adding logs to the new table.
+    $limittime = '';
+    if (!empty($minloginternalreader)) {
+        $limittime = ' AND time < :timeto ';
+        $params['timeto'] = $minloginternalreader;
+    }
+    // Check if we need to show the last access.
+    $sqllasttime = '';
+    if ($showlastaccess) {
+        $sqllasttime = ", MAX(time) AS lasttime";
+    }
+    $logactionlike = $DB->sql_like('l.action', ':action');
+    $sql = "SELECT cm.id, COUNT('x') AS numviews $sqllasttime
+              FROM {course_modules} cm
+              JOIN {modules} m
+                ON m.id = cm.module
+              JOIN {log} l
+                ON l.cmid = cm.id
+             WHERE cm.course = :courseid
+               AND $logactionlike
+               AND m.visible = :visible $limittime
+          GROUP BY cm.id";
+    $params = array('courseid' => $course->id, 'action' => 'view%', 'visible' => 1);
+    $views = $DB->get_records_sql($sql, $params);
+}
+
+// Get record from sql_internal_reader and merge with records obtained from legacy log (if needed).
+if ($useinternalreader) {
+    // Check if we need to show the last access.
+    $sqllasttime = '';
+    if ($showlastaccess) {
+        $sqllasttime = ", MAX(timecreated) AS lasttime";
+    }
+    $sql = "SELECT contextinstanceid as cmid, COUNT('x') AS numviews $sqllasttime
+              FROM {" . $logtable . "} l
+             WHERE courseid = :courseid
+               AND anonymous = 0
+               AND crud = 'r'
+               AND contextlevel = :contextmodule
+          GROUP BY contextinstanceid";
+    $params = array('courseid' => $course->id, 'contextmodule' => CONTEXT_MODULE);
+    $v = $DB->get_records_sql($sql, $params);
+
+    if (empty($views)) {
+        $views = $v;
+    } else {
+        // Merge two view arrays.
+        foreach ($v as $key => $value) {
+            if (isset($views[$key]) && !empty($views[$key]->numviews)) {
+                $views[$key]->numviews += $value->numviews;
+            } else {
+                $views[$key] = $value;
+            }
+        }
+    }
+}
 
 $prevsecctionnum = 0;
 foreach ($modinfo->sections as $sectionnum=>$section) {
index 7e6a0fc..828e2d9 100644 (file)
@@ -25,6 +25,8 @@
 
 $string['eventactivityreportviewed'] = 'Activity report viewed';
 $string['eventoutlinereportviewed'] = 'Outline report viewed';
+$string['neverseen'] = 'Never seen';
+$string['nologreaderenabled'] = 'No log reader enabled';
 $string['outline:view'] = 'View activity report';
 $string['page-report-outline-x'] = 'Any outline report';
 $string['page-report-outline-index'] = 'Course outline report';
index ba4761c..e301bec 100644 (file)
@@ -53,3 +53,209 @@ function report_outline_print_row($mod, $instance, $result) {
     }
     echo "</tr>";
 }
+
+/**
+ * Returns an array of the commonly used log variables by the outline report.
+ *
+ * @return array the array of variables used
+ */
+function report_outline_get_common_log_variables() {
+    global $DB;
+
+    static $uselegacyreader;
+    static $useinternalreader;
+    static $minloginternalreader;
+    static $logtable = null;
+
+    if (isset($uselegacyreader) && isset($useinternalreader) && isset($minloginternalreader)) {
+        return array($uselegacyreader, $useinternalreader, $minloginternalreader, $logtable);
+    }
+
+    $uselegacyreader = false; // Flag to determine if we should use the legacy reader.
+    $useinternalreader = false; // Flag to determine if we should use the internal reader.
+    $minloginternalreader = 0; // Set this to 0 for now.
+
+    // Get list of readers.
+    $logmanager = get_log_manager();
+    $readers = $logmanager->get_readers();
+
+    // Get preferred reader.
+    if (!empty($readers)) {
+        foreach ($readers as $readerpluginname => $reader) {
+            // If legacy reader is preferred reader.
+            if ($readerpluginname == 'logstore_legacy') {
+                $uselegacyreader = true;
+                break;
+            }
+
+            // If sql_internal_reader is preferred reader.
+            if ($reader instanceof \core\log\sql_internal_reader) {
+                $useinternalreader = true;
+                $logtable = $reader->get_internal_log_table_name();
+                $minloginternalreader = $DB->get_field_sql('SELECT min(timecreated) FROM {' . $logtable . '}');
+                break;
+            }
+        }
+    }
+
+    return array($uselegacyreader, $useinternalreader, $minloginternalreader, $logtable);
+}
+
+/**
+ * Return the most commonly used user outline information.
+ *
+ * @param int $userid the id of the user
+ * @param int $cmid the course module id
+ * @param string $module the name of the module (eg. 'book')
+ * @param int $instanceid (eg. the 'id' in the 'book' table)
+ * @return stdClass|null if any information is found then a stdClass containing
+ *  this info is returned, else null is.
+ */
+function report_outline_user_outline($userid, $cmid, $module, $instanceid) {
+    global $DB;
+
+    list($uselegacyreader, $useinternalreader, $minloginternalreader, $logtable) = report_outline_get_common_log_variables();
+
+    // If using legacy log then get users from old table.
+    if ($uselegacyreader) {
+        // Create the params for the query.
+        $params = array('userid' => $userid, 'module' => $module, 'action' => 'view', 'info' => $instanceid);
+        // If we are going to use the internal (not legacy) log table, we should only get records
+        // from the legacy table that exist before we started adding logs to the new table.
+        $limittime = '';
+        if (!empty($minloginternalreader)) {
+            $limittime = ' AND time < :timeto ';
+            $params['timeto'] = $minloginternalreader;
+        }
+        $select = "SELECT COUNT(id) ";
+        $from = "FROM {log} ";
+        $where = "WHERE userid = :userid
+                    AND module = :module
+                    AND action = :action
+                    AND info = :info ";
+        if ($legacylogcount = $DB->count_records_sql($select . $from . $where . $limittime, $params)) {
+            $numviews = $legacylogcount;
+
+            // Get the time for the last log.
+            $select = "SELECT MAX(time) ";
+            $lastlogtime = $DB->get_field_sql($select . $from . $where, $params);
+
+            $result = new stdClass();
+            $result->info = get_string('numviews', '', $numviews);
+            $result->time = $lastlogtime;
+        }
+    }
+
+    // Get record from sql_internal_reader and combine with the number of views from the legacy log table (if needed).
+    if ($useinternalreader) {
+        $params = array('userid' => $userid, 'contextlevel' => CONTEXT_MODULE, 'contextinstanceid' => $cmid, 'crud' => 'r',
+            'edulevel1' => core\event\base::LEVEL_PARTICIPATING, 'edulevel2' => core\event\base::LEVEL_TEACHING,
+            'edulevel3' => core\event\base::LEVEL_OTHER, 'anonymous' => 0);
+        $select = "SELECT COUNT(*) as count ";
+        $from = "FROM {" . $logtable . "} ";
+        $where = "WHERE userid = :userid
+                    AND contextlevel = :contextlevel
+                    AND contextinstanceid = :contextinstanceid
+                    AND crud = :crud
+                    AND edulevel IN (:edulevel1, :edulevel2, :edulevel3)
+                    AND anonymous = :anonymous";
+        if ($internalreadercount = $DB->count_records_sql($select . $from . $where, $params)) {
+            if (!empty($numviews)) {
+                $numviews = $numviews + $internalreadercount;
+            } else {
+                $numviews = $internalreadercount;
+            }
+
+            // Get the time for the last log.
+            $select = "SELECT MAX(timecreated) ";
+            $lastlogtime = $DB->get_field_sql($select . $from . $where, $params);
+
+            $result = new stdClass();
+            $result->info = get_string('numviews', '', $numviews);
+            $result->time = $lastlogtime;
+        }
+    }
+
+    if (!empty($result)) {
+        return $result;
+    }
+
+    return null;
+}
+
+/**
+ * Display the most commonly used user complete information.
+ *
+ * @param int $userid the id of the user
+ * @param int $cmid the course module id
+ * @param string $module the name of the module (eg. 'book')
+ * @param int $instanceid (eg. the 'id' in the 'book' table)
+ * @return string
+ */
+function report_outline_user_complete($userid, $cmid, $module, $instanceid) {
+    global $DB;
+
+    list($uselegacyreader, $useinternalreader, $minloginternalreader, $logtable) = report_outline_get_common_log_variables();
+
+    // If using legacy log then get users from old table.
+    if ($uselegacyreader) {
+        // Create the params for the query.
+        $params = array('userid' => $userid, 'module' => $module, 'action' => 'view', 'info' => $instanceid);
+        // If we are going to use the internal (not legacy) log table, we should only get records
+        // from the legacy table that exist before we started adding logs to the new table.
+        $limittime = '';
+        if (!empty($minloginternalreader)) {
+            $limittime = ' AND time < :timeto ';
+            $params['timeto'] = $minloginternalreader;
+        }
+        $select = "SELECT COUNT(id) ";
+        $from = "FROM {log} ";
+        $where = "WHERE userid = :userid
+                    AND module = :module
+                    AND action = :action
+                    AND info = :info ";
+        if ($legacylogcount = $DB->count_records_sql($select . $from . $where . $limittime, $params)) {
+            $numviews = $legacylogcount;
+
+            // Get the time for the last log.
+            $select = "SELECT MAX(time) ";
+            $lastlogtime = $DB->get_field_sql($select . $from . $where, $params);
+
+            $strnumviews = get_string('numviews', '', $numviews);
+        }
+    }
+
+    // Get record from sql_internal_reader and combine with the number of views from the legacy log table (if needed).
+    if ($useinternalreader) {
+        $params = array('userid' => $userid, 'contextlevel' => CONTEXT_MODULE, 'contextinstanceid' => $cmid, 'crud' => 'r',
+            'edulevel1' => core\event\base::LEVEL_PARTICIPATING, 'edulevel2' => core\event\base::LEVEL_TEACHING,
+            'edulevel3' => core\event\base::LEVEL_OTHER, 'anonymous' => 0);
+        $select = "SELECT COUNT(*) as count ";
+        $from = "FROM {" . $logtable . "} ";
+        $where = "WHERE userid = :userid
+                    AND contextlevel = :contextlevel
+                    AND contextinstanceid = :contextinstanceid
+                    AND crud = :crud
+                    AND edulevel IN (:edulevel1, :edulevel2, :edulevel3)
+                    AND anonymous = :anonymous";
+        if ($internalreadercount = $DB->count_records_sql($select . $from . $where, $params)) {
+            if (!empty($numviews)) {
+                $numviews = $numviews + $internalreadercount;
+            } else {
+                $numviews = $internalreadercount;
+            }
+
+            // Get the time for the last log.
+            $select = "SELECT MAX(timecreated) ";
+            $lastlogtime = $DB->get_field_sql($select . $from . $where, $params);
+
+            $strnumviews = get_string('numviews', '', $numviews);
+        }
+    }
+
+    if (!empty($strnumviews) && (!empty($lastlogtime))) {
+        return $strnumviews . ' - ' . get_string('mostrecently') . ' ' . userdate($lastlogtime);
+    } else {
+        return get_string('neverseen', 'report_outline');
+    }
+}
diff --git a/report/outline/tests/behat/outline.feature b/report/outline/tests/behat/outline.feature
new file mode 100644 (file)
index 0000000..ffcea0f
--- /dev/null
@@ -0,0 +1,109 @@
+@report @report_outline
+Feature: View an outline report
+  In order to ensure the outline report works as expected
+  As a teacher
+  I need to log in as a teacher and view the outline report
+
+  Background:
+    Given the following "courses" exist:
+      | fullname | shortname | format |
+      | Course 1 | C1 | topics |
+    And the following "users" exist:
+      | username | firstname | lastname | email |
+      | teacher1 | Teacher | 1 | teacher1@asd.com
+      | student1 | Student | 1 | student1@asd.com |
+      | student2 | Student | 2 | student2@asd.com |
+    And the following "course enrolments" exist:
+      | user | course | role |
+      | teacher1 | C1 | editingteacher |
+      | student1 | C1 | student |
+      | student2 | C1 | student |
+    When I log in as "admin"
+    And I follow "Course 1"
+    And I turn editing mode on
+    And I add a "Forum" to section "1" and I fill the form with:
+      | Forum name | Forum name |
+      | Description | Forum description |
+    And I add a "Book" to section "1" and I fill the form with:
+      | Name | Book name |
+      | Description | Book description |
+
+  @javascript
+  Scenario: View the outline report when only the legacy log reader is enabled
+    Given I navigate to "Manage log stores" node in "Site administration > Plugins > Logging"
+    And I click on "Enable" "link" in the "Legacy log" "table_row"
+    And I click on "Disable" "link" in the "Standard log" "table_row"
+    And I set the following administration settings values:
+      | Log legacy data | 1 |
+    And I log out
+    And I log in as "student1"
+    And I follow "Course 1"
+    And I follow "Forum name"
+    And I follow "Course 1"
+    And I follow "Book name"
+    And I log out
+    And I log in as "student2"
+    And I follow "Course 1"
+    And I follow "Book name"
+    And I log out
+    And I log in as "teacher1"
+    And I follow "Course 1"
+    When I navigate to "Activity report" node in "Course administration > Reports"
+    Then I should see "2" in the "//tr[contains(concat(' ', normalize-space(@class),' '),' r0 ')]/child::td[contains(concat(' ', normalize-space(@class),' '),' numviews ')]" "xpath_element"
+    And I should see "1" in the "//tr[contains(concat(' ', normalize-space(@class),' '),' r1 ')]/child::td[contains(concat(' ', normalize-space(@class),' '),' numviews ')]" "xpath_element"
+
+  @javascript
+  Scenario: View the outline report when only the standard log reader is enabled
+    Given I navigate to "Manage log stores" node in "Site administration > Plugins > Logging"
+    And "Enable" "link" should exist in the "Legacy log" "table_row"
+    And "Disable" "link" should exist in the "Standard log" "table_row"
+    And I log out
+    And I log in as "student1"
+    And I follow "Course 1"
+    And I follow "Forum name"
+    And I follow "Course 1"
+    And I follow "Book name"
+    And I log out
+    And I log in as "student2"
+    And I follow "Course 1"
+    And I follow "Book name"
+    And I log out
+    And I log in as "admin"
+    And I follow "Course 1"
+    When I navigate to "Activity report" node in "Course administration > Reports"
+    Then I should see "2" in the "//tr[contains(concat(' ', normalize-space(@class),' '),' r0 ')]/child::td[contains(concat(' ', normalize-space(@class),' '),' numviews ')]" "xpath_element"
+    And I should see "1" in the "//tr[contains(concat(' ', normalize-space(@class),' '),' r1 ')]/child::td[contains(concat(' ', normalize-space(@class),' '),' numviews ')]" "xpath_element"
+
+  @javascript
+  Scenario: View the outline report when both the standard and legacy log readers are enabled
+    Given I navigate to "Manage log stores" node in "Site administration > Plugins > Logging"
+    And I click on "Enable" "link" in the "Legacy log" "table_row"
+    And "Disable" "link" should exist in the "Standard log" "table_row"
+    And I set the following administration settings values:
+      | Log legacy data | 1 |
+    And I log out
+    And I log in as "student1"
+    And I follow "Course 1"
+    And I follow "Forum name"
+    And I follow "Course 1"
+    And I follow "Book name"
+    And I log out
+    And I log in as "student2"
+    And I follow "Course 1"
+    And I follow "Book name"
+    And I log out
+    And I log in as "teacher1"
+    And I follow "Course 1"
+    When I navigate to "Activity report" node in "Course administration > Reports"
+    Then I should see "2" in the "//tr[contains(concat(' ', normalize-space(@class),' '),' r0 ')]/child::td[contains(concat(' ', normalize-space(@class),' '),' numviews ')]" "xpath_element"
+    And I should see "1" in the "//tr[contains(concat(' ', normalize-space(@class),' '),' r1 ')]/child::td[contains(concat(' ', normalize-space(@class),' '),' numviews ')]" "xpath_element"
+
+  @javascript
+  Scenario: View the outline report when no log reader is enabled
+    Given I navigate to "Manage log stores" node in "Site administration > Plugins > Logging"
+    And "Enable" "link" should exist in the "Legacy log" "table_row"
+    And I click on "Disable" "link" in the "Standard log" "table_row"
+    And I follow "Home"
+    And I follow "Course 1"
+    When I navigate to "Activity report" node in "Course administration > Reports"
+    Then I should see "No log reader enabled"
diff --git a/report/outline/tests/behat/user.feature b/report/outline/tests/behat/user.feature
new file mode 100644 (file)
index 0000000..8a181dc
--- /dev/null
@@ -0,0 +1,107 @@
+@report @report_outline
+Feature: View the user page for the outline report
+  In order to ensure the user page for the outline report works as expected
+  As a student
+  I need to log in as a student and view the user page for the outline report
+
+  Background:
+    Given the following "courses" exist:
+      | fullname | shortname | format | showreports |
+      | Course 1 | C1 | topics | 1 |
+    And the following "users" exist:
+      | username | firstname | lastname | email |
+      | teacher1 | Teacher | 1 | teacher1@asd.com
+      | student1 | Student | 1 | student1@asd.com |
+    And the following "course enrolments" exist:
+      | user | course | role |
+      | teacher1 | C1 | editingteacher |
+      | student1 | C1 | student |
+    When I log in as "admin"
+    And I follow "Course 1"
+    And I turn editing mode on
+    And I add a "Folder" to section "1" and I fill the form with:
+      | Name | Folder name |
+      | Description | Folder description |
+    And I add a "URL" to section "1" and I fill the form with:
+      | Name | URL name |
+      | Description | URL description |
+      | External URL | http://www.google.com |
+
+  @javascript
+  Scenario: View the user page when only the legacy log reader is enabled
+    Given I navigate to "Manage log stores" node in "Site administration > Plugins > Logging"
+    And I click on "Enable" "link" in the "Legacy log" "table_row"
+    And I click on "Disable" "link" in the "Standard log" "table_row"
+    And I set the following administration settings values:
+      | Log legacy data | 1 |
+    And I log out
+    And I log in as "student1"
+    And I follow "Course 1"
+    # We want to view this multiple times, to make sure the count is working.
+    And I follow "Folder name"
+    And I follow "Folder name"
+    And I follow "Folder name"
+    And I follow "Folder name"
+    And I follow "Course 1"
+    # We want to view this multiple times, to make sure the count is working.
+    And I follow "URL name"
+    And I follow "URL name"
+    And I follow "URL name"
+    When I navigate to "Outline report" node in "My profile settings > Activity reports"
+    Then I should see "4 views" in the "Folder name" "table_row"
+    And I should see "3 views" in the "URL name" "table_row"
+    And I navigate to "Complete report" node in "My profile settings > Activity reports"
+    And I should see "4 views"
+    And I should see "3 views"
+
+  @javascript
+  Scenario: View the user page when only the standard log reader is enabled
+    Given I navigate to "Manage log stores" node in "Site administration > Plugins > Logging"
+    And "Enable" "link" should exist in the "Legacy log" "table_row"
+    And "Disable" "link" should exist in the "Standard log" "table_row"
+    And I log out
+    And I log in as "student1"
+    And I follow "Course 1"
+    # We want to view this multiple times, to make sure the count is working.
+    And I follow "Folder name"
+    And I follow "Folder name"
+    And I follow "Folder name"
+    And I follow "Folder name"
+    And I follow "Course 1"
+    # We want to view this multiple times, to make sure the count is working.
+    And I follow "URL name"
+    And I follow "URL name"
+    And I follow "URL name"
+    When I navigate to "Outline report" node in "My profile settings > Activity reports"
+    Then I should see "4 views" in the "Folder name" "table_row"
+    And I should see "3 views" in the "URL name" "table_row"
+    And I navigate to "Complete report" node in "My profile settings > Activity reports"
+    And I should see "4 views"
+    And I should see "3 views"
+
+  @javascript
+  Scenario: View the user page when both the standard and legacy log readers are enabled
+    Given I navigate to "Manage log stores" node in "Site administration > Plugins > Logging"
+    And I click on "Enable" "link" in the "Legacy log" "table_row"
+    And "Disable" "link" should exist in the "Standard log" "table_row"
+    And I set the following administration settings values:
+      | Log legacy data | 1 |
+    And I log out
+    And I log in as "student1"
+    And I follow "Course 1"
+    # We want to view this multiple times, to make sure the count is working.
+    And I follow "Folder name"
+    And I follow "Folder name"
+    And I follow "Folder name"
+    And I follow "Folder name"
+    And I follow "Course 1"
+    # We want to view this multiple times, to make sure the count is working.
+    And I follow "URL name"
+    And I follow "URL name"
+    And I follow "URL name"
+    When I navigate to "Outline report" node in "My profile settings > Activity reports"
+    Then I should see "4 views" in the "Folder name" "table_row"
+    And I should see "3 views" in the "URL name" "table_row"
+    And I navigate to "Complete report" node in "My profile settings > Activity reports"
+    And I should see "4 views"
+    And I should see "3 views"
index a4cbd50..03dd659 100644 (file)
@@ -25,6 +25,7 @@
 
 require('../../config.php');
 require_once($CFG->dirroot.'/report/outline/locallib.php');
+require_once($CFG->dirroot.'/report/outline/lib.php');
 
 $userid   = required_param('id', PARAM_INT);
 $courseid = required_param('course', PARAM_INT);
@@ -109,29 +110,33 @@ foreach ($sections as $i => $section) {
                                 $user_outline = $mod->modname."_user_outline";
                                 if (function_exists($user_outline)) {
                                     $output = $user_outline($course, $user, $mod, $instance);
-                                    report_outline_print_row($mod, $instance, $output);
+                                } else {
+                                    $output = report_outline_user_outline($user->id, $cmid, $mod->modname, $instance->id);
                                 }
+                                report_outline_print_row($mod, $instance, $output);
                                 break;
                             case "complete":
                                 $user_complete = $mod->modname."_user_complete";
-                                if (function_exists($user_complete)) {
-                                    $image = $OUTPUT->pix_icon('icon', $mod->modfullname, 'mod_'.$mod->modname, array('class'=>'icon'));
-                                    echo "<h4>$image $mod->modfullname: ".
-                                         "<a href=\"$CFG->wwwroot/mod/$mod->modname/view.php?id=$mod->id\">".
-                                         format_string($instance->name,true)."</a></h4>";
+                                $image = $OUTPUT->pix_icon('icon', $mod->modfullname, 'mod_'.$mod->modname, array('class'=>'icon'));
+                                echo "<h4>$image $mod->modfullname: ".
+                                     "<a href=\"$CFG->wwwroot/mod/$mod->modname/view.php?id=$mod->id\">".
+                                     format_string($instance->name,true)."</a></h4>";
 
-                                    ob_start();
+                                ob_start();
 
-                                    echo "<ul>";
+                                echo "<ul>";
+                                if (function_exists($user_complete)) {
                                     $user_complete($course, $user, $mod, $instance);
-                                    echo "</ul>";
+                                } else {
+                                    echo report_outline_user_complete($user->id, $cmid, $mod->modname, $instance->id);
+                                }
+                                echo "</ul>";
 
-                                    $output = ob_get_contents();
-                                    ob_end_clean();
+                                $output = ob_get_contents();
+                                ob_end_clean();
 
-                                    if (str_replace(' ', '', $output) != '<ul></ul>') {
-                                        echo $output;
-                                    }
+                                if (str_replace(' ', '', $output) != '<ul></ul>') {
+                                    echo $output;
                                 }
                                 break;
                             }