Compare commits

...

23 Commits

Author SHA1 Message Date
Dan Marsden 5b468f74af Fixes #223 allow student attendance to be disabled at site-level 8 years ago
Dan Marsden f2c3ea94fe fix php 7 statement 9 years ago
Dan Marsden 1843e854bc add travis support 9 years ago
Dan Marsden befaeaccd9 Merge pull request #180 from NeillM/MOODLE_28_STABLE 9 years ago
Neill Magill af5a743a2b Expired enrolments can prevent student self-attendance 9 years ago
Dan Marsden dfd65ea0e7 Merge pull request #167 from barrysspace/MOODLE_28_STABLE 9 years ago
Barry Oosthuizen 7302a019ce Fix #155 properly. Undo regression caused by moving $statuses definition. 9 years ago
Syxton d3abd673d3 Returns all users and their lowest status in a course. 9 years ago
Dan Marsden 68e9ef6276 Merge pull request #161 from barrysspace/hotfox/issue_159_delete_selected_sessions_M28 9 years ago
Dan Marsden 733e55f67d Merge pull request #158 from barrysspace/hotfix/issue_155_hiding_status_removes_it_MDL28 9 years ago
Barry Oosthuizen 9ebe730689 Fix issue #159 - Delete no selected sessions 9 years ago
Barry Oosthuizen 0c1346dd55 Fix #155: Hiding statuses removes them from Settings 9 years ago
Dan Marsden 61f61982eb Merge pull request #149 from barrysspace/hotfix/issue_145_hiding_status_M28 9 years ago
Barry Oosthuizen 1c5f2b75ab Fix #145 - hiding variable status items no longer working 9 years ago
Dan Marsden 26bae46805 update README 9 years ago
Barry Oosthuizen e7d3e90b59 Delete some whitespace introduced by b0638c67 10 years ago
Dan Marsden ded677cd45 Merge pull request #136 from barrysspace/MOODLE_28_STABLE 10 years ago
Barry Oosthuizen f088046122 Fix issue#134 - Add 'checkweekdays' language string 10 years ago
Dan Marsden da16ecdcaa Prevent this branch from installing on versions earlier than 2.8 10 years ago
Dan Marsden 2bd8b1462c Merge pull request #132 from barrysspace/hotfix/issue_129_empty_status_M28 10 years ago
Barry Oosthuizen 4995ecabe7 Behat test to notify error for empty acronym or description 10 years ago
Barry Oosthuizen f62a93bd86 Fix issue#129 - Notify error for empty acronym or description 10 years ago
Dan Marsden 8d314052ee bump release version to avoid confusion with 2.9/master branch 10 years ago
  1. 42
      .travis.yml
  2. 20
      README.md
  3. 5
      add_form.php
  4. 4
      attendance.php
  5. 4
      lang/en/attendance.php
  6. 17
      locallib.php
  7. 8
      preferences.php
  8. 5
      renderables.php
  9. 34
      renderer.php
  10. 10
      sessions.php
  11. 3
      settings.php
  12. 55
      tests/behat/preferences.feature
  13. 4
      version.php

42
.travis.yml

@ -0,0 +1,42 @@
language: php
sudo: false
cache:
directories:
- $HOME/.composer/cache
php:
# - 5.4
# - 5.5
- 5.6
- 7.0
matrix:
allow_failures:
- php: 7.0
env:
matrix:
- DB=pgsql MOODLE_BRANCH=MOODLE_28_STABLE
- DB=mysqli MOODLE_BRANCH=MOODLE_28_STABLE
before_install:
- cd ../..
- composer selfupdate
- composer create-project -n --no-dev moodlerooms/moodle-plugin-ci ci ^1
- export PATH="$(cd ci/bin; pwd):$(cd ci/vendor/bin; pwd):$PATH"
install:
- moodle-plugin-ci install
script:
- moodle-plugin-ci phplint
- moodle-plugin-ci phpcpd
- moodle-plugin-ci phpmd
- moodle-plugin-ci codechecker
- moodle-plugin-ci csslint
- moodle-plugin-ci shifter
- moodle-plugin-ci jshint
- moodle-plugin-ci phpunit
- moodle-plugin-ci behat

20
README.md

@ -1,21 +1,15 @@
ABOUT ABOUT
========== ==========
The "Attendance" module was developed by The Attendance module is supported and maintained by Dan Marsden http://danmarsden.com
The Attendance module was previously developed by
Dmitry Pupinin, Novosibirsk, Russia, Dmitry Pupinin, Novosibirsk, Russia,
Artem Andreev, Taganrog, Russia. Artem Andreev, Taganrog, Russia.
This block may be distributed under the terms of the General Public License
(see http://www.gnu.org/licenses/gpl.txt for details)
PURPOSE PURPOSE
========== ==========
The attendance module and block are designed to allow instructors of a course keep an attendance log of the students in their courses. The instructor will setup the frequency of his classes (# of days per week & length of course) and the attendance is ready for use. To take attendance, the instructor clicks on the "Update Attendance" button and is presented with a list of all the students in that course, along with 4 options: Present, Absent, Late & Excused, with a Remarks textbox. Instructors can download the attendance for their course in Excel format or text format. The Attendance module allows teachers to maintain a record of attendance, replacing or supplementing a paper-based attendance register.
Only the instructor can update the attendance data. However, a student gets to see his attendance record. It is primarily used in blended-learning environments where students are required to attend classes, lectures and tutorials and allows
the teacher to track and optionally provide a grade for the students attendance.
INSTALLATION
==========
The attendance module follows standard installation procedure.
1. Create folder <path to your moodle dir>/mod/attendance. Sessions can be configured to allow students to record their own attendance and a range of different reports are available.
2. Extract files from folder inside archive to created folder.
3. Visit page Home ► Site administration ► Notifications to complete installation.

5
add_form.php

@ -106,8 +106,13 @@ class mod_attendance_add_form extends moodleform {
$mform->addHelpButton('addmultiply', 'createmultiplesessions', 'attendance'); $mform->addHelpButton('addmultiply', 'createmultiplesessions', 'attendance');
// Students can mark own attendance. // Students can mark own attendance.
if (!empty(get_config('attendance', 'studentscanmark'))) {
$mform->addElement('checkbox', 'studentscanmark', '', get_string('studentscanmark', 'attendance')); $mform->addElement('checkbox', 'studentscanmark', '', get_string('studentscanmark', 'attendance'));
$mform->addHelpButton('studentscanmark', 'studentscanmark', 'attendance'); $mform->addHelpButton('studentscanmark', 'studentscanmark', 'attendance');
} else {
$mform->addElement('hidden', 'studentscanmark', '0');
$mform->settype('studentscanmark', PARAM_INT);
}
$mform->addElement('date_time_selector', 'sessiondate', get_string('sessiondate', 'attendance')); $mform->addElement('date_time_selector', 'sessiondate', get_string('sessiondate', 'attendance'));

4
attendance.php

@ -41,6 +41,10 @@ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST)
// Require the user is logged in. // Require the user is logged in.
require_login($course, true, $cm); require_login($course, true, $cm);
if (empty(get_config('attendance', 'studentscanmark')) || empty($attforsession->studentscanmark)) {
redirect(new moodle_url('/mod/attendance/view.php', array('id' => $cm->id)));
exit;
}
$pageparams->sessionid = $id; $pageparams->sessionid = $id;
$att = new attendance($attendance, $cm, $course, $PAGE->context, $pageparams); $att = new attendance($attendance, $cm, $course, $PAGE->context, $pageparams);

4
lang/en/attendance.php

@ -67,6 +67,7 @@ $string['cannottakeforgroup'] = 'You can\'t take attendance for group "{$a}"';
$string['changeattendance'] = 'Change attendance'; $string['changeattendance'] = 'Change attendance';
$string['changeduration'] = 'Change duration'; $string['changeduration'] = 'Change duration';
$string['changesession'] = 'Change session'; $string['changesession'] = 'Change session';
$string['checkweekdays'] = 'Select weekdays that fall within your selected session date range.';
$string['column'] = 'column'; $string['column'] = 'column';
$string['columns'] = 'columns'; $string['columns'] = 'columns';
$string['commonsession'] = 'Common'; $string['commonsession'] = 'Common';
@ -103,6 +104,8 @@ $string['downloadtext'] = 'Download in text format';
$string['donotusepaging'] = 'Do not use paging'; $string['donotusepaging'] = 'Do not use paging';
$string['duration'] = 'Duration'; $string['duration'] = 'Duration';
$string['editsession'] = 'Edit Session'; $string['editsession'] = 'Edit Session';
$string['emptyacronym'] = 'Empty acronyms are not allowed. Status record not updated.';
$string['emptydescription'] = 'Empty descriptions are not allowed. Status record not updated.';
$string['edituser'] = 'Edit user'; $string['edituser'] = 'Edit user';
$string['endtime'] = 'Session end time'; $string['endtime'] = 'Session end time';
$string['endofperiod'] = 'End of period'; $string['endofperiod'] = 'End of period';
@ -291,6 +294,7 @@ $string['eventstatusupdated'] = 'Status updated';
$string['eventstatusadded'] = 'Status added'; $string['eventstatusadded'] = 'Status added';
$string['studentscanmark'] = 'Allow students to record own attendance'; $string['studentscanmark'] = 'Allow students to record own attendance';
$string['studentscanmark_desc'] = 'If checked, teachers will be able to allow students to mark their own attendance.';
$string['studentscanmark_help'] = 'If checked students will be able to change their own attendance status for the session.'; $string['studentscanmark_help'] = 'If checked students will be able to change their own attendance status for the session.';
$string['set_by_student'] = 'Self-recorded'; $string['set_by_student'] = 'Self-recorded';
$string['attendance_already_submitted'] = 'You may not self register attendance that has already been set.'; $string['attendance_already_submitted'] = 'You may not self register attendance that has already been set.';

17
locallib.php

@ -1101,7 +1101,7 @@ class attendance {
$maxtime = 'CASE WHEN MIN(ue.timeend) = 0 THEN 0 ELSE MAX(ue.timeend) END'; $maxtime = 'CASE WHEN MIN(ue.timeend) = 0 THEN 0 ELSE MAX(ue.timeend) END';
// CONTRIB-3549 // CONTRIB-3549
$sql = "SELECT ue.userid, ue.status, $sql = "SELECT ue.userid, MIN(ue.status) as status,
$mintime AS mintime, $mintime AS mintime,
$maxtime AS maxtime $maxtime AS maxtime
FROM {user_enrolments} ue FROM {user_enrolments} ue
@ -1109,7 +1109,7 @@ class attendance {
WHERE ue.userid $sql WHERE ue.userid $sql
AND e.status = :estatus AND e.status = :estatus
AND e.courseid = :courseid AND e.courseid = :courseid
GROUP BY ue.userid, ue.status"; GROUP BY ue.userid";
$params += array('zerotime'=>0, 'estatus'=>ENROL_INSTANCE_ENABLED, 'courseid'=>$this->course->id); $params += array('zerotime'=>0, 'estatus'=>ENROL_INSTANCE_ENABLED, 'courseid'=>$this->course->id);
$enrolments = $DB->get_records_sql($sql, $params); $enrolments = $DB->get_records_sql($sql, $params);
@ -1166,7 +1166,7 @@ class attendance {
// CONTRIB-4868 // CONTRIB-4868
$mintime = 'MIN(CASE WHEN (ue.timestart > :zerotime) THEN ue.timestart ELSE ue.timecreated END)'; $mintime = 'MIN(CASE WHEN (ue.timestart > :zerotime) THEN ue.timestart ELSE ue.timecreated END)';
$maxtime = 'MAX(ue.timeend)'; $maxtime = 'CASE WHEN MIN(ue.timeend) = 0 THEN 0 ELSE MAX(ue.timeend) END';
$sql = "SELECT ue.userid, ue.status, $sql = "SELECT ue.userid, ue.status,
$mintime AS mintime, $mintime AS mintime,
@ -1596,6 +1596,13 @@ class attendance {
public function update_status($status, $acronym, $description, $grade, $visible) { public function update_status($status, $acronym, $description, $grade, $visible) {
global $DB; global $DB;
if (isset($visible)) {
$status->visible = $visible;
$updated[] = $visible ? get_string('show') : get_string('hide');
} else if (empty($acronym) || empty($description)) {
return array('acronym' => $acronym, 'description' => $description);
}
$updated = array(); $updated = array();
if ($acronym) { if ($acronym) {
@ -1610,10 +1617,6 @@ class attendance {
$status->grade = $grade; $status->grade = $grade;
$updated[] = $grade; $updated[] = $grade;
} }
if (isset($visible)) {
$status->visible = $visible;
$updated[] = $visible ? get_string('show') : get_string('hide');
}
$DB->update_record('attendance_statuses', $status); $DB->update_record('attendance_statuses', $status);
$event = \mod_attendance\event\status_updated::create(array( $event = \mod_attendance\event\status_updated::create(array(

8
preferences.php

@ -55,6 +55,8 @@ $PAGE->set_cacheable(true);
$PAGE->set_button($OUTPUT->update_module_button($cm->id, 'attendance')); $PAGE->set_button($OUTPUT->update_module_button($cm->id, 'attendance'));
$PAGE->navbar->add(get_string('settings', 'attendance')); $PAGE->navbar->add(get_string('settings', 'attendance'));
$errors = array();
switch ($att->pageparams->action) { switch ($att->pageparams->action) {
case att_preferences_page_params::ACTION_ADD: case att_preferences_page_params::ACTION_ADD:
$newacronym = optional_param('newacronym', null, PARAM_TEXT); $newacronym = optional_param('newacronym', null, PARAM_TEXT);
@ -92,7 +94,7 @@ switch ($att->pageparams->action) {
echo $OUTPUT->footer(); echo $OUTPUT->footer();
exit; exit;
case att_preferences_page_params::ACTION_HIDE: case att_preferences_page_params::ACTION_HIDE:
$statuses = $att->get_statuses(); $statuses = $att->get_statuses(false);
$status = $statuses[$att->pageparams->statusid]; $status = $statuses[$att->pageparams->statusid];
$att->update_status($status, null, null, null, 0); $att->update_status($status, null, null, null, 0);
break; break;
@ -112,7 +114,7 @@ switch ($att->pageparams->action) {
foreach ($acronym as $id => $v) { foreach ($acronym as $id => $v) {
$status = $statuses[$id]; $status = $statuses[$id];
$att->update_status($status, $acronym[$id], $description[$id], $grade[$id], null); $errors[$id] = $att->update_status($status, $acronym[$id], $description[$id], $grade[$id], null);
} }
if ($att->grade > 0) { if ($att->grade > 0) {
att_update_all_users_grades($att->id, $att->course, $att->context, $cm); att_update_all_users_grades($att->id, $att->course, $att->context, $cm);
@ -122,7 +124,7 @@ switch ($att->pageparams->action) {
$output = $PAGE->get_renderer('mod_attendance'); $output = $PAGE->get_renderer('mod_attendance');
$tabs = new attendance_tabs($att, attendance_tabs::TAB_PREFERENCES); $tabs = new attendance_tabs($att, attendance_tabs::TAB_PREFERENCES);
$prefdata = new attendance_preferences_data($att); $prefdata = new attendance_preferences_data($att, array_filter($errors));
$setselector = new attendance_set_selector($att, $maxstatusset); $setselector = new attendance_set_selector($att, $maxstatusset);
// Output starts here. // Output starts here.

5
renderables.php

@ -548,8 +548,11 @@ class attendance_preferences_data implements renderable {
private $att; private $att;
public function __construct(attendance $att) { public $errors;
public function __construct(attendance $att, $errors) {
$this->statuses = $att->get_statuses(false); $this->statuses = $att->get_statuses(false);
$this->errors = $errors;
foreach ($this->statuses as $st) { foreach ($this->statuses as $st) {
$st->haslogs = att_has_logs_for_status ($st->id); $st->haslogs = att_has_logs_for_status ($st->id);

34
renderer.php

@ -295,8 +295,6 @@ class mod_attendance_renderer extends plugin_renderer_base {
att_sessions_page_params::ACTION_DELETE_SELECTED => get_string('delete'), att_sessions_page_params::ACTION_DELETE_SELECTED => get_string('delete'),
att_sessions_page_params::ACTION_CHANGE_DURATION => get_string('changeduration', 'attendance')); att_sessions_page_params::ACTION_CHANGE_DURATION => get_string('changeduration', 'attendance'));
$controls = html_writer::select($options, 'action'); $controls = html_writer::select($options, 'action');
$attributes = array( $attributes = array(
'type' => 'submit', 'type' => 'submit',
@ -770,7 +768,8 @@ class mod_attendance_renderer extends plugin_renderer_base {
$cell->colspan = 2; $cell->colspan = 2;
$row->cells[] = $cell; $row->cells[] = $cell;
} else { } else {
if (!empty($sess->studentscanmark)) { // Student can mark their own attendance. if (!empty(get_config('attendance', 'studentscanmark')) && !empty($sess->studentscanmark)) {
// Student can mark their own attendance.
// URL to the page that lets the student modify their attendance. // URL to the page that lets the student modify their attendance.
$url = new moodle_url('/mod/attendance/attendance.php', $url = new moodle_url('/mod/attendance/attendance.php',
array('sessid' => $sess->id, 'sesskey' => sesskey())); array('sessid' => $sess->id, 'sesskey' => sesskey()));
@ -963,10 +962,21 @@ class mod_attendance_renderer extends plugin_renderer_base {
$i = 1; $i = 1;
foreach ($prefdata->statuses as $st) { foreach ($prefdata->statuses as $st) {
$emptyacronym = '';
$emptydescription = '';
if (array_key_exists($st->id, $prefdata->errors)) {
if (empty($prefdata->errors[$st->id]['acronym'])) {
$emptyacronym = $this->construct_notice(get_string('emptyacronym', 'mod_attendance'), 'notifyproblem');
}
if (empty($prefdata->errors[$st->id]['description'])) {
$emptydescription = $this->construct_notice(get_string('emptydescription', 'mod_attendance') , 'notifyproblem');
}
}
$table->data[$i][] = $i; $table->data[$i][] = $i;
$table->data[$i][] = $this->construct_text_input('acronym['.$st->id.']', 2, 2, $st->acronym); $table->data[$i][] = $this->construct_text_input('acronym['.$st->id.']', 2, 2, $st->acronym) . $emptyacronym;
$table->data[$i][] = $this->construct_text_input('description['.$st->id.']', 30, 30, $st->description); $table->data[$i][] = $this->construct_text_input('description['.$st->id.']', 30, 30, $st->description) . $emptydescription;
$table->data[$i][] = $this->construct_text_input('grade['.$st->id.']', 4, 4, format_float($st->grade)); $table->data[$i][] = $this->construct_text_input('grade['.$st->id.']', 4, 4, $st->grade);
$table->data[$i][] = $this->construct_preferences_actions_icons($st, $prefdata); $table->data[$i][] = $this->construct_preferences_actions_icons($st, $prefdata);
$i++; $i++;
@ -1040,6 +1050,18 @@ class mod_attendance_renderer extends plugin_renderer_base {
return html_writer::empty_tag('input', $attributes); return html_writer::empty_tag('input', $attributes);
} }
/**
* Construct a notice message
*
* @param string $text
* @param string $class
* @return string
*/
private function construct_notice($text, $class = 'notifymessage') {
$attributes = array('class' => $class);
return html_writer::tag('p', $text, $attributes);
}
// Show different picture if it is a temporary user. // Show different picture if it is a temporary user.
protected function user_picture($user, array $opts = null) { protected function user_picture($user, array $opts = null) {
if ($user->type == 'temporary') { if ($user->type == 'temporary') {

10
sessions.php

@ -126,8 +126,10 @@ switch ($att->pageparams->action) {
} }
redirect($att->url_manage(), get_string('sessiondeleted', 'attendance')); redirect($att->url_manage(), get_string('sessiondeleted', 'attendance'));
} }
$sessid = required_param_array('sessid', PARAM_SEQUENCE); $sessid = optional_param_array('sessid', '', PARAM_SEQUENCE);
if (empty($sessid)) {
print_error('nosessionsselected', 'attendance', $att->url_manage());
}
$sessionsinfo = $att->get_sessions_info($sessid); $sessionsinfo = $att->get_sessions_info($sessid);
$message = get_string('deletecheckfull', '', get_string('session', 'attendance')); $message = get_string('deletecheckfull', '', get_string('session', 'attendance'));
@ -192,6 +194,10 @@ function construct_sessions_data_for_add($formdata) {
$duration = $formdata->durtime['hours']*HOURSECS + $formdata->durtime['minutes']*MINSECS; $duration = $formdata->durtime['hours']*HOURSECS + $formdata->durtime['minutes']*MINSECS;
$now = time(); $now = time();
if (empty(get_config('attendance', 'studentscanmark'))) {
$formdata->studentscanmark = 0;
}
$sessions = array(); $sessions = array();
if (isset($formdata->addmultiply)) { if (isset($formdata->addmultiply)) {
$startdate = $formdata->sessiondate; $startdate = $formdata->sessiondate;

3
settings.php

@ -41,4 +41,7 @@ if ($ADMIN->fulltree) {
$settings->add(new admin_setting_configselect('attendance/resultsperpage', $settings->add(new admin_setting_configselect('attendance/resultsperpage',
get_string('resultsperpage', 'attendance'), get_string('resultsperpage_desc', 'attendance'), 25, $options)); get_string('resultsperpage', 'attendance'), get_string('resultsperpage_desc', 'attendance'), 25, $options));
$settings->add(new admin_setting_configcheckbox('attendance/studentscanmark',
get_string('studentscanmark', 'attendance'), get_string('studentscanmark_desc', 'attendance'), 1));
} }

55
tests/behat/preferences.feature

@ -0,0 +1,55 @@
@mod @uon @mod_attendance @mod_attendance_preferences
Feature: Teachers can't change status variables to have empty acronyms or descriptions
In order to update status variables
As a teacher
I need to see an error notice below each acronym / description that I try to set to be empty
Background:
Given the following "courses" exist:
| fullname | shortname | summary | category |
| Course 1 | C101 | Prove the attendance activity settings works | 0 |
And the following "users" exist:
| username | firstname | lastname |
| student1 | Sam | Student |
| teacher1 | Teacher | One |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C101 | student |
| teacher1 | C101 | editingteacher |
And I log in as "teacher1"
And I follow "Course 1"
And I turn editing mode on
And I add a "Attendance" to section "1"
And I press "Save and display"
And I follow "Settings"
@javascript
Scenario: Teachers can add status variables
# Set the second status acronym to be empty
Given I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[2]/td[2]/input" to ""
# Set the second status description to be empty
And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[2]/td[3]/input" to ""
# Set the second status grade to be empty
And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[2]/td[4]/input" to ""
When I click on "Update" "button" in the "#preferencesform" "css_element"
Then I should see "Empty acronyms are not allowed" in the "//*[@id='preferencesform']/table/tbody/tr[2]/td[2]/p" "xpath_element"
And I should see "Empty descriptions are not allowed" in the "//*[@id='preferencesform']/table/tbody/tr[2]/td[3]/p" "xpath_element"
And I click on "Update" "button" in the "#preferencesform" "css_element"
# Set the first status acronym to be empty
Given I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[1]/td[2]/input" to ""
# Set the first status description to be empty
And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[1]/td[3]/input" to ""
# Set the first status grade to be empty
And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[1]/td[4]/input" to ""
# Set the third status acronym to be empty
And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[3]/td[2]/input" to ""
# Set the third status description to be empty
And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[3]/td[3]/input" to ""
# Set the third status grade to be empty
And I set the field with xpath "//*[@id='preferencesform']/table/tbody/tr[3]/td[4]/input" to ""
When I click on "Update" "button" in the "#preferencesform" "css_element"
Then I should see "Empty acronyms are not allowed" in the "//*[@id='preferencesform']/table/tbody/tr[1]/td[2]/p" "xpath_element"
And I should see "Empty descriptions are not allowed" in the "//*[@id='preferencesform']/table/tbody/tr[1]/td[3]/p" "xpath_element"
And I should see "Empty acronyms are not allowed" in the "//*[@id='preferencesform']/table/tbody/tr[3]/td[2]/p" "xpath_element"
And I should see "Empty descriptions are not allowed" in the "//*[@id='preferencesform']/table/tbody/tr[3]/td[3]/p" "xpath_element"

4
version.php

@ -23,8 +23,8 @@
*/ */
$plugin->version = 2015040503; $plugin->version = 2015040503;
$plugin->requires = 2014042900; $plugin->requires = 2014111000;
$plugin->release = '2.9.1'; $plugin->release = '2.9.1.1';
$plugin->maturity = MATURITY_STABLE; $plugin->maturity = MATURITY_STABLE;
$plugin->cron = 0; $plugin->cron = 0;
$plugin->component = 'mod_attendance'; $plugin->component = 'mod_attendance';

Loading…
Cancel
Save