diff --git a/locallib.php b/locallib.php new file mode 100644 index 0000000..867e04c --- /dev/null +++ b/locallib.php @@ -0,0 +1,545 @@ +libdir . '/gradelib.php'); + +class attforblock_permissions { + private $canview = null; + private $canviewreports = null; + private $cantake = null; + private $canchange = null; + private $canmanage = null; + private $canchangepreferences = null; + private $canexport = null; + private $canbelisted = null; + + private $context; + + public function __construct($context) { + $this->context = $context; + } + + public function can_view() { + if (is_null($this->canview)) + $this->canview = has_capability ('mod/attforblock:view', $this->context); + + return $this->canview; + } + + public function can_viewreports() { + if (is_null($this->canviewreports)) + $this->canviewreports = has_capability ('mod/attforblock:viewreports', $this->context); + + return $this->canviewreports; + } + + public function can_take() { + if (is_null($this->cantake)) + $this->cantake = has_capability ('mod/attforblock:takeattendances', $this->context); + + return $this->cantake; + } + + public function can_change() { + if (is_null($this->canchange)) + $this->canchange = has_capability ('mod/attforblock:changeattendances', $this->context); + + return $this->canchange; + } + + public function can_manage() { + if (is_null($this->canmanage)) + $this->canmanage = has_capability ('mod/attforblock:manageattendances', $this->context); + + return $this->canmanage; + } + + public function can_change_preferences() { + if (is_null($this->canchangepreferences)) + $this->canchangepreferences = has_capability ('mod/attforblock:changepreferences', $this->context); + + return $this->canchangepreferences; + } + + public function can_export() { + if (is_null($this->canexport)) + $this->canexport = has_capability ('mod/attforblock:export', $this->context); + + return $this->canexport; + } + + public function can_be_listed() { + if (is_null($this->canbelisted)) + $this->canbelisted = has_capability ('mod/attforblock:canbelisted', $this->context); + + return $this->canbelisted; + } +} + +class attforblock { + const SESSION_COMMON = 1; + const SESSION_GROUP = 2; + + const VIEW_DAYS = 1; + const VIEW_WEEKS = 2; + const VIEW_MONTHS = 3; + const VIEW_ALLTAKEN = 4; + const VIEW_ALL = 5; + + const SELECTOR_NONE = 1; + const SELECTOR_GROUP = 2; + const SELECTOR_SESS_TYPE = 3; + + const SORTED_LIST = 1; + const SORTED_GRID = 2; + + const DEFAULT_VIEW = attforblock::VIEW_WEEKS; + const DEFAULT_CURDATE = 0; + const DEFAULT_VIEW_TAKE = attforblock::SORTED_LIST; + const DEFAULT_SHOWENDTIME = 0; + + /** @var stdclass course module record */ + public $cm; + + /** @var stdclass course record */ + public $course; + + /** @var stdclass context object */ + public $context; + + /** @var int attendance instance identifier */ + public $id; + + /** @var string attendance activity name */ + public $name; + + /** @var float number (10, 5) unsigned, the maximum grade for attendance */ + public $grade; + + /** @var int current view mode */ + public $view = self::DEFAULT_VIEW; + + /** @var int $view and $curdate specify displaed date range */ + public $curdate = self::DEFAULT_CURDATE; + + /** @var int start date of displayed date range */ + public $startdate; + + /** @var int end date of displayed date range */ + public $enddate; + + /** @var int view mode of taking attendance page*/ + public $view_take = self::DEFAULT_VIEW_TAKE; + + /** @var int whether sessions end time will be displayed on manage.php */ + public $showendtime = self::DEFAULT_SHOWENDTIME; + + /** @var attforblock_permissions permission of current user for attendance instance*/ + public $perm; + /** + * Initializes the attendance API instance using the data from DB + * + * Makes deep copy of all passed records properties. Replaces integer $course attribute + * with a full database record (course should not be stored in instances table anyway). + * + * @param stdClass $dbrecord Attandance instance data from {attforblock} table + * @param stdClass $cm Course module record as returned by {@link get_coursemodule_from_id()} + * @param stdClass $course Course record from {course} table + * @param stdClass $context The context of the workshop instance + */ + public function __construct(stdclass $dbrecord, stdclass $cm, stdclass $course, stdclass $context=null) { + foreach ($dbrecord as $field => $value) { + if (property_exists('attforblock', $field)) { + $this->{$field} = $value; + } + else { + throw new coding_exception('The attendance table has field for which there is no property in the attforblock class'); + } + } + $this->cm = $cm; + $this->course = $course; + if (is_null($context)) { + $this->context = get_context_instance(CONTEXT_MODULE, $this->cm->id); + } else { + $this->context = $context; + } + + $this->perm = new attforblock_permissions($this->context); + } + + public function init_view_params($view=NULL, $curdate=NULL, $view_take=NULL, $showendtime=NULL) { + global $SESSION; + + if (isset($view)) { + $SESSION->attcurrentattview[$this->course->id] = $view; + $this->view = $view; + } + elseif (isset($SESSION->attcurrentattview[$this->course->id])) { + $this->view = $SESSION->attcurrentattview[$this->course->id]; + } + + if ($curdate) { + $SESSION->attcurrentattdate[$this->course->id] = $curdate; + $this->curdate = $curdate; + } + elseif (isset($SESSION->attcurrentattdate[$this->course->id])) { + $this->curdate = $SESSION->attcurrentattdate[$this->course->id]; + } + else { + $this->curdate = time(); + } + + if (isset($view_take)) { + set_user_preference("attforblock_view_take", $view_take); + $this->view_take = $view_take; + } + else { + $this->view_take = get_user_preferences("attforblock_view_take", $this->view_take); + } + + if (isset($showendtime)) { + set_user_preference("attforblock_showendtime", $showendtime); + $this->showendtime = $showendtime; + } + else { + $this->showendtime = get_user_preferences("attforblock_showendtime", $this->showendtime); + } + + $this->init_start_end_date(); + } + + private function init_start_end_date() { + $date = usergetdate($this->curdate); + $mday = $date['mday']; + $wday = $date['wday']; + $mon = $date['mon']; + $year = $date['year']; + + switch ($this->view) { + case self::VIEW_DAYS: + $this->startdate = make_timestamp($year, $mon, $mday); + $this->enddate = make_timestamp($year, $mon, $mday + 1); + break; + case self::VIEW_WEEKS: + $this->startdate = make_timestamp($year, $mon, $mday - $wday + 1); + $this->enddate = make_timestamp($year, $mon, $mday + 7 - $wday + 1) - 1; + break; + case self::VIEW_MONTHS: + $this->startdate = make_timestamp($year, $mon); + $this->enddate = make_timestamp($year, $mon + 1); + break; + case self::VIEW_ALLTAKEN: + $this->startdate = 1; + $this->enddate = time(); + break; + case self::VIEW_ALL: + $this->startdate = 0; + $this->enddate = 0; + break; + } + } + + /** + * Returns today sessions for this attendance + * + * Fetches data from {attendance_sessions} + * + * @return array of records or an empty array + */ + public function get_today_sessions() { + global $DB; + + $today = time(); // because we compare with database, we don't need to use usertime() + + $sql = "SELECT id, groupid, lasttaken + FROM {attendance_sessions} + WHERE :time BETWEEN sessdate AND (sessdate + duration) + AND courseid = :cid AND attendanceid = :aid"; + $params = array( + 'time' => $today, + 'cid' => $this->course->id, + 'aid' => $this->id); + + return $DB->get_records_sql($sql, $params); + } + + /** + * Returns count of hidden sessions for this attendance + * + * Fetches data from {attendance_sessions} + * + * @return count of hidden sessions + */ + public function get_hidden_sessions_count() { + global $DB; + + $where = "courseid = :cid AND attendanceid = :aid AND sessdate < :csdate"; + $params = array( + 'cid' => $this->course->id, + 'aid' => $this->id, + 'csdate'=> $this->course->startdate); + + return $DB->count_records_select('attendance_sessions', $where, $params); + } + + /** + * @return moodle_url of manage.php for attendance instance + */ + public function url_manage() { + $params = array('id' => $this->cm->id); + return new moodle_url('/mod/attforblock/manage.php', $params); + } + + /** + * @return moodle_url of sessions.php for attendance instance + */ + public function url_sessions() { + $params = array('id' => $this->cm->id); + return new moodle_url('/mod/attforblock/sessions.php', $params); + } + + /** + * @return moodle_url of report.php for attendance instance + */ + public function url_report() { + $params = array('id' => $this->cm->id); + return new moodle_url('/mod/attforblock/report.php', $params); + } + + /** + * @return moodle_url of export.php for attendance instance + */ + public function url_export() { + $params = array('id' => $this->cm->id); + return new moodle_url('/mod/attforblock/export.php', $params); + } + + /** + * @return moodle_url of attsettings.php for attendance instance + */ + public function url_settings() { + $params = array('id' => $this->cm->id); + return new moodle_url('/mod/attforblock/attsettings.php', $params); + } + + /** + * @return moodle_url of attendances.php for attendance instance + */ + public function url_take() { + $params = array('id' => $this->cm->id); + return new moodle_url('/mod/attforblock/attendances.php', $params); + } +} + + +/** + * Represents info about attendance tabs. + * + * Proxy class for security reasons (renderers must not have access to all attforblock methods) + * + */ +class attforblock_tabs implements renderable { + const TAB_SESSIONS = 'sessions'; + const TAB_ADD = 'add'; + const TAB_REPORT = 'report'; + const TAB_EXPORT = 'export'; + const TAB_SETTINGS = 'settings'; + + public $currenttab; + + /** @var attforblock */ + private $att; + + /** + * Prepare info about sessions for attendance taking into account view parameters. + * + * @param attforblock $att instance + * @param $currenttab - one of attforblock_tabs constants + */ + public function __construct(attforblock $att, $currenttab=self::TAB_SESSIONS) { + $this->att = $att; + $this->currenttab = $currenttab; + } + + /** + * Return array of rows where each row is an array of tab objects + * taking into account permissions of current user + */ + public function get_tabs() { + $toprow = array(); + if ($this->att->perm->can_manage() or + $this->att->perm->can_take() or + $this->att->perm->can_change()) { + $toprow[] = new tabobject(self::TAB_SESSIONS, $this->att->url_sessions()->out(), + get_string('sessions','attforblock')); + } + + if ($this->att->perm->can_manage()) { + $toprow[] = new tabobject(self::TAB_ADD, $this->att->url_sessions()->out(true, array('action' => 'add')), + get_string('add','attforblock')); + } + + if ($this->att->perm->can_viewreports()) { + $toprow[] = new tabobject(self::TAB_REPORT, $this->att->url_report()->out(), + get_string('report','attforblock')); + } + + if ($this->att->perm->can_export()) { + $toprow[] = new tabobject(self::TAB_EXPORT, $this->att->url_export()->out(), + get_string('export','quiz')); + } + + if ($this->att->perm->can_change_preferences()) { + $toprow[] = new tabobject(self::TAB_SETTINGS, $this->att->url_settings()->out(), + get_string('settings','attforblock')); + } + + return array($toprow); + } +} + + +class attforblock_filter_controls implements renderable { + /** @var int current view mode */ + public $view; + + public $prevcur; + public $nextcur; + public $curdatetxt; + + private $att; + + public function __construct(attforblock $att) { + $this->view = $att->view; + + $date = usergetdate($att->curdate); + $mday = $date['mday']; + $wday = $date['wday']; + $mon = $date['mon']; + $year = $date['year']; + + switch ($this->view) { + case attforblock::VIEW_DAYS: + $format = get_string('strftimedm', 'attforblock'); + $this->prevcur = make_timestamp($year, $mon, $mday - 1); + $this->nextcur = make_timestamp($year, $mon, $mday + 1); + $this->curdatetxt = userdate($att->startdate, $format); + break; + case attforblock::VIEW_WEEKS: + $format = get_string('strftimedm', 'attforblock'); + $this->prevcur = $att->startdate - WEEKSECS; + $this->nextcur = $att->startdate + WEEKSECS; + $this->curdatetxt = userdate($att->startdate, $format)." - ".userdate($att->enddate, $format); + break; + case attforblock::VIEW_MONTHS: + $format = '%B'; + $this->prevcur = make_timestamp($year, $mon - 1); + $this->nextcur = make_timestamp($year, $mon + 1); + $this->curdatetxt = userdate($att->startdate, $format); + break; + } + + $this->att = $att; + } + + public function url($params=NULL) { + global $PAGE; + + return new moodle_url($PAGE->url, $params); + } +} + +/** + * Represents info about attendance sessions taking into account view parameters. + * + */ +class attforblock_sessions_manage_data implements renderable { + /** @var int start date of displayed date range */ + public $startdate; + + /** @var int end date of displayed date range */ + public $enddate; + + /** @var array of sessions*/ + public $sessions; + + /** @var int number of hidden sessions (sessions before $course->startdate)*/ + public $hiddensessionscount; + + /** @var attforblock_permissions permission of current user for attendance instance*/ + public $perm; + + /** @var int whether sessions end time will be displayed */ + public $showendtime; + + public $groups; + + public $hiddensesscount; + + /** @var attforblock */ + private $att; + /** + * Prepare info about attendance sessions taking into account view parameters. + * + * @param attforblock $att instance + */ + public function __construct(attforblock $att) { + global $DB; + + $this->perm = $att->perm; + + $this->showendtime = $att->showendtime; + + $this->startdate = $att->startdate; + $this->enddate = $att->enddate; + + if ($this->startdate && $this->enddate) { + $where = "courseid=:cid AND attendanceid = :aid AND sessdate >= :csdate AND sessdate >= :sdate AND sessdate < :edate"; + } else { + $where = "courseid=:cid AND attendanceid = :aid AND sessdate >= :csdate"; + } + $params = array( + 'cid' => $att->course->id, + 'aid' => $att->id, + 'csdate' => $att->course->startdate, + 'sdate' => $this->startdate, + 'edate' => $this->enddate/*, + 'cgroup' => $currentgroup*/); + $this->sessions = $DB->get_records_select('attendance_sessions', $where, $params, 'sessdate asc'); + + $where = "courseid = :cid AND attendanceid = :aid AND sessdate < :csdate"; + $params = array( + 'cid' => $att->course->id, + 'aid' => $att->id, + 'csdate' => $att->course->startdate); + $this->hiddensessionscount = $DB->count_records_select('attendance_sessions', $where, $params); + + $this->groups = groups_get_all_groups($att->course->id); + + $this->hiddensessionscount = $att->get_hidden_sessions_count(); + + $this->att = $att; + } + + public function url_take($sessionid, $grouptype=NULL) { + $params = array('sessionid' => $sessionid); + $url = new moodle_url($this->att->url_take(), $params); + if (isset($grouptype)) + $url->param('grouptype', $grouptype); + + return $url; + } + + /** + * Must be called without or with both parameters + */ + public function url_sessions($sessionid=NULL, $action=NULL) { + $url = new moodle_url($this->att->url_sessions()); + if (isset($sessionid) && isset($action)) + $url->params(array('sessionid' => $sessionid, 'action' => $action)); + + return $url; + } +} + +?> diff --git a/manage.php b/manage.php new file mode 100644 index 0000000..88a7284 --- /dev/null +++ b/manage.php @@ -0,0 +1,77 @@ +get_record('course', array('id' => $cm->course), '*', MUST_EXIST); +$att = $DB->get_record('attforblock', array('id' => $cm->instance), '*', MUST_EXIST); + +require_login($course, true, $cm); + +$canmanage = !has_capability('mod/attforblock:manageattendances', $PAGE->context); +$cantake = !has_capability('mod/attforblock:takeattendances', $PAGE->context); +$canchange = !has_capability('mod/attforblock:changeattendances', $PAGE->context); +if ($canmanage && $cantake && $canchange) + redirect("view.php?id=$cm->id"); + +$att = new attforblock($att, $cm, $course, $PAGE->context); +$att->init_view_params($view, $curdate, NULL, $showendtime); + +// if teacher is coming from block, then check for a session exists for today +if ($from === 'block') { + $atts = $att->get_today_sessions(); + $size = count($atts); + if ($size == 1) { + $att = reset($atts); + $nottaken = !$att->lasttaken && has_capability('mod/attforblock:takeattendances', $context); + $canchange = $att->lasttaken && has_capability('mod/attforblock:changeattendances', $context); + if ($nottaken || $canchange) + redirect('attendances.php?id='.$id.'&sessionid='.$att->id.'&grouptype='.$att->groupid); + } elseif ($size > 1) { + $att->curdate = $today; + //temporally set $view for single access to page from block + $att->$view = attforblock::VIEW_DAYS; + } +} + +$PAGE->set_url($att->url_manage()); +$PAGE->set_title($course->shortname. ": ".$att->name); +$PAGE->set_heading($course->fullname); +$PAGE->set_focuscontrol(''); +$PAGE->set_cacheable(true); +$PAGE->set_button($OUTPUT->update_module_button($cm->id, 'attforblock')); +$PAGE->navbar->add($att->name); + +$output = $PAGE->get_renderer('mod_attforblock'); +$tabs = new attforblock_tabs($att); +$filtercontrols = new attforblock_filter_controls($att); +$sesstable = new attforblock_sessions_manage_data($att); + +/// Output starts here + +echo $output->header(); +echo $output->heading(get_string('attendanceforthecourse','attforblock').' :: ' .$course->fullname); +echo $output->render($tabs); +echo $output->render($filtercontrols); +echo $output->render($sesstable); + +echo $output->footer(); + + +?> diff --git a/renderer.php b/renderer.php new file mode 100644 index 0000000..9a8794f --- /dev/null +++ b/renderer.php @@ -0,0 +1,185 @@ +get_tabs(), $atttabs->currenttab, NULL, NULL, true); + } + + /** + * Renders filter controls for attendance + * + * @param fcontrols - filter controls data to display + * @return string html code + */ + protected function render_attforblock_filter_controls(attforblock_filter_controls $fcontrols) { + $filtertable = new html_table(); + $filtertable->attributes['class'] = ' '; + $filtertable->width = '100%'; + $filtertable->align = array('left', 'center', 'right'); + + $filtertable->data[0][] = ''; + + $curdatecontrols = ''; + if ($fcontrols->curdatetxt) { + $curdatecontrols = html_writer::link($fcontrols->url(array('curdate' => $fcontrols->prevcur)), $this->output->larrow()); + $curdatecontrols .= $fcontrols->curdatetxt; + $curdatecontrols .= html_writer::link($fcontrols->url(array('curdate' => $fcontrols->nextcur)), $this->output->rarrow()); + //plug_yui_calendar($current); + } + $filtertable->data[0][] = $curdatecontrols; + + $views[attforblock::VIEW_ALL] = get_string('all', 'attforblock'); + $views[attforblock::VIEW_ALLTAKEN] = get_string('alltaken', 'attforblock'); + $views[attforblock::VIEW_MONTHS] = get_string('months', 'attforblock'); + $views[attforblock::VIEW_WEEKS] = get_string('weeks', 'attforblock'); + $views[attforblock::VIEW_DAYS] = get_string('days', 'attforblock'); + $viewcontrols = ''; + foreach ($views as $key => $sview) { + if ($key != $fcontrols->view) { + $link = html_writer::link($fcontrols->url(array('view' => $key)), $sview); + $viewcontrols .= html_writer::tag('span', $link, array('class' => 'attbtn')); + } + else + $viewcontrols .= html_writer::tag('span', $sview, array('class' => 'attcurbtn')); + } + $filtertable->data[0][] = html_writer::tag('nobr', $viewcontrols); + + $o = html_writer::table($filtertable); + $o = $this->output->container($o, 'attfiltercontrols attwidth'); + + return $o; + } + + /** + * Renders attendance sessions managing table + * + * @param attforblock_sessions_manage_data $sessdata to display + * @return string html code + */ + protected function render_attforblock_sessions_manage_data(attforblock_sessions_manage_data $sessdata) { + $sesstable = new html_table(); + $sesstable->width = '100%'; + $sesstable->head = array( + '#', + get_string('sessiontypeshort', 'attforblock'), + get_string('date'), get_string('from'), + ($sessdata->showendtime == '0') ? get_string('duration', 'attforblock') : get_string('to'), + get_string('description','attforblock'), + get_string('actions'), + get_string('select') + ); + $sesstable->align = array('', '', '', 'right', 'left', 'center', 'center'); + $sesstable->size = array('1px', '', '1px', '1px', '1px', '*', '1px', '1px'); + + $i = 0; + foreach ($sessdata->sessions as $key => $sess) { + $i++; + $actions = ''; + $desctext = empty($sess->description) ? get_string('nodescription', 'attforblock') : $sess->description; + if($sess->lasttaken > 0) //attendance has taken + { + if ($sessdata->perm->can_change()) { + $url = $sessdata->url_take($sess->id, $sess->groupid); + $title = get_string('changeattendance','attforblock'); + + $desc = html_writer::link($url, $desctext, array('title' => $title)); + } else { + $desc = '' . $desctext . ''; + } + } else { + $desc = $desctext; + if ($sessdata->perm->can_take()) { + $url = $sessdata->url_take($sess->id, $sess->groupid); + $title = get_string('takeattendance','attforblock'); + $actions = $this->output->action_icon($url, new pix_icon('t/go', $title)); + } + } + if($sessdata->perm->can_manage()) { + $url = $sessdata->url_sessions($sess->id, 'update'); + $title = get_string('editsession','attforblock'); + $actions .= $this->output->action_icon($url, new pix_icon('t/edit', $title)); + + $url = $sessdata->url_sessions($sess->id, 'delete'); + $title = get_string('deletesession','attforblock'); + $actions .= $this->output->action_icon($url, new pix_icon('t/delete', $title)); + } + + $sesstable->data[$sess->id][] = $i; + $sesstable->data[$sess->id][] = $sess->groupid ? $sessdata->groups[$sess->groupid]->name : get_string('commonsession', 'attforblock'); + $sesstable->data[$sess->id][] = userdate($sess->sessdate, get_string('strftimedmyw', 'attforblock')); + $sesstable->data[$sess->id][] = userdate($sess->sessdate, get_string('strftimehm', 'attforblock')); + $hours = floor($sess->duration / HOURSECS); + $mins = floor(($sess->duration - $hours * HOURSECS) / MINSECS); + $mins = $mins < 10 ? "0$mins" : "$mins"; + $duration = "{$mins} " . get_string('min'); + if ($hours) + $duration = "{$hours} " . get_string('hours') . " " . $duration; + $endtime = userdate($sess->sessdate+$sess->duration, get_string('strftimehm', 'attforblock')); + $sesstable->data[$sess->id][] = ($sessdata->showendtime == 0) ? $duration : $endtime; + $sesstable->data[$sess->id][] = $desc; + $sesstable->data[$sess->id][] = $actions; + $sesstable->data[$sess->id][] = html_writer::checkbox('sessid', $sess->id, false); + } + + $controltable = new html_table(); + $controltable->attributes['class'] = ' '; + $controltable->width = '100%'; + $controltable->align = array('left', 'right'); + + $controltable->data[0][] = $this->output->help_icon('hiddensessions', 'attforblock', + get_string('hiddensessions', 'attforblock').': '.$sessdata->hiddensessionscount); + + $controls = html_writer::link('javascript:checkall();', get_string('selectall')).' / '. + html_writer::link('javascript:checknone();', get_string('deselectall')). + html_writer::empty_tag('br'); + if ($sessdata->perm->can_manage()) { + $options = array('deleteselected' => get_string('delete'), + 'changeduration' => get_string('changeduration', 'attforblock')); + $controls .= html_writer::select($options, 'action'); + $attributes = array( + 'type' => 'submit', + 'name' => 'ok', + 'value' => get_string('ok')); + $controls .= html_writer::empty_tag('input', $attributes); + } else { + $controls .= get_string('youcantdo', 'attforblock'); //You can't do anything + } + $controltable->data[0][] = $controls; + + $o = html_writer::table($sesstable) . html_writer::table($controltable); + $o = html_writer::tag('form', $o, array('method' => 'post', 'action' => $sessdata->url_sessions()->out())); + $o = $this->output->container($o, 'generalbox attwidth'); + $o = $this->output->container($o, 'attsessions_manage_table'); + + return $o; + } +} +?> diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..bd21cd9 --- /dev/null +++ b/styles.css @@ -0,0 +1,69 @@ +.path-mod-attforblock .attbtn { + border:1px solid #AAAAAA; + margin-left: 2px; + margin-right: 2px; + padding: 5px; + border-radius: 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + -opera-border-radius: 5px; + -khtml-border-radius: 5px; +} + +.path-mod-attforblock .attcurbtn { + margin-left: 2px; + margin-right: 2px; + padding: 5px; +} + +.path-mod-attforblock .attfiltercontrols { + margin-bottom: 10px; + margin-right:auto; + margin-left:auto; +} + +.path-mod-attforblock .attwidth { + width:90%; + margin: auto; +} + +.path-mod-attforblock #mod-attforblock-attendances table.controls { + width: 100%; + text-align: center; +} + +.path-mod-attforblock #mod-attforblock-attendances table.controls tr { + vertical-align: top; +} + +.path-mod-attforblock #mod-attforblock-attendances table.controls td.right, +.path-mod-attforblock #mod-attforblock-attendances table.controls td.left { + padding-bottom: 4px; + padding-left: 4px; + padding-right: 4px; + padding-top: 4px; +} + +.path-mod-attforblock #mod-attforblock-attendances table.controls .right { + text-align: right; +} +/* for IE7*/ +.path-mod-attforblock .filtercontrols td { + padding:6px; +} + +/*wwwroot}/lib/yui" +?> +/* +Copyright (c) 2010, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 2.8.1 +*/ +/*.yuidialog body{font:13px/1.231 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;} .yuidialog select,.yuidialog input,.yuidialog button,.yuidialog textarea,.yuidialog button{font:99% arial,helvetica,clean,sans-serif;} .yuidialog table{font-size:inherit;font:100%;} .yuidialog pre,.yuidialog code,.yuidialog kbd,.yuidialog samp,.yuidialog tt{font-family:monospace;*font-size:108%;line-height:100%;} +.yui-button{display:-moz-inline-box;display:inline-block;vertical-align:text-bottom;}.yui-button .first-child{display:block;*display:inline-block;}.yui-button button,.yui-button a{display:block;*display:inline-block;border:none;margin:0;}.yui-button button{background-color:transparent;*overflow:visible;cursor:pointer;}.yui-button a{text-decoration:none;}.yui-skin-sam .yui-button{border-width:1px 0;border-style:solid;border-color:#808080;background:url(http://yui.yahooapis.com/2.8.1/build/assets/skins/sam/sprite.png) repeat-x 0 0;margin:auto .25em;}.yui-skin-sam .yui-button .first-child{border-width:0 1px;border-style:solid;border-color:#808080;margin:0 -1px;_margin:0;}.yui-skin-sam .yui-button button,.yui-skin-sam .yui-button a,.yui-skin-sam .yui-button a:visited{padding:0 10px;font-size:93%;line-height:2;*line-height:1.7;min-height:2em;*min-height:auto;color:#000;}.yui-skin-sam .yui-button a{*line-height:1.875;*padding-bottom:1px;}.yui-skin-sam .yui-split-button button,.yui-skin-sam .yui-menu-button button{padding-right:20px;background-position:right center;background-repeat:no-repeat;}.yui-skin-sam .yui-menu-button button{background-image:url(/assets/skins/sam/menu-button-arrow.png);}.yui-skin-sam .yui-split-button button{background-image:url(/assets/skins/sam/split-button-arrow.png);}.yui-skin-sam .yui-button-focus{border-color:#7D98B8;background-position:0 -1300px;}.yui-skin-sam .yui-button-focus .first-child{border-color:#7D98B8;}.yui-skin-sam .yui-split-button-focus button{background-image:url(/assets/skins/sam/split-button-arrow-focus.png);}.yui-skin-sam .yui-button-hover{border-color:#7D98B8;background-position:0 -1300px;}.yui-skin-sam .yui-button-hover .first-child{border-color:#7D98B8;}.yui-skin-sam .yui-split-button-hover button{background-image:url(/assets/skins/sam/split-button-arrow-hover.png);}.yui-skin-sam .yui-button-active{border-color:#7D98B8;background-position:0 -1700px;}.yui-skin-sam .yui-button-active .first-child{border-color:#7D98B8;}.yui-skin-sam .yui-split-button-activeoption{border-color:#808080;background-position:0 0;}.yui-skin-sam .yui-split-button-activeoption .first-child{border-color:#808080;}.yui-skin-sam .yui-split-button-activeoption button{background-image:url(/assets/skins/sam/split-button-arrow-active.png);}.yui-skin-sam .yui-radio-button-checked,.yui-skin-sam .yui-checkbox-button-checked{border-color:#304369;background-position:0 -1400px;}.yui-skin-sam .yui-radio-button-checked .first-child,.yui-skin-sam .yui-checkbox-button-checked .first-child{border-color:#304369;}.yui-skin-sam .yui-radio-button-checked button,.yui-skin-sam .yui-checkbox-button-checked button{color:#fff;}.yui-skin-sam .yui-button-disabled{border-color:#ccc;background-position:0 -1500px;}.yui-skin-sam .yui-button-disabled .first-child{border-color:#ccc;}.yui-skin-sam .yui-button-disabled button,.yui-skin-sam .yui-button-disabled a,.yui-skin-sam .yui-button-disabled a:visited{color:#A6A6A6;cursor:default;}.yui-skin-sam .yui-menu-button-disabled button{background-image:url(/assets/skins/sam/menu-button-arrow-disabled.png);}.yui-skin-sam .yui-split-button-disabled button{background-image:url(/assets/skins/sam/split-button-arrow-disabled.png);} +.yui-overlay,.yui-panel-container{visibility:hidden;position:absolute;z-index:2;}.yui-panel{position:relative;}.yui-panel-container form{margin:0;}.mask{z-index:1;display:none;position:absolute;top:0;left:0;right:0;bottom:0;}.mask.block-scrollbars{overflow:auto;}.masked select,.drag select,.hide-select select{_visibility:hidden;}.yui-panel-container select{_visibility:inherit;}.hide-scrollbars,.hide-scrollbars *{overflow:hidden;}.hide-scrollbars select{display:none;}.show-scrollbars{overflow:auto;}.yui-panel-container.show-scrollbars,.yui-tt.show-scrollbars{overflow:visible;}.yui-panel-container.show-scrollbars .underlay,.yui-tt.show-scrollbars .yui-tt-shadow{overflow:auto;}.yui-panel-container.shadow .underlay.yui-force-redraw{padding-bottom:1px;}.yui-effect-fade .underlay,.yui-effect-fade .yui-tt-shadow{display:none;}.yui-tt-shadow{position:absolute;}.yui-override-padding{padding:0!important;}.yui-panel-container .container-close{overflow:hidden;text-indent:-10000em;text-decoration:none;}.yui-overlay.yui-force-redraw,.yui-panel-container.yui-force-redraw{margin-bottom:1px;}.yui-skin-sam .mask{background-color:#000;opacity:.25;filter:alpha(opacity=25);}.yui-skin-sam .yui-panel-container{padding:0 1px;*padding:2px;}.yui-skin-sam .yui-panel{position:relative;left:0;top:0;border-style:solid;border-width:1px 0;border-color:#808080;z-index:1;*border-width:1px;*zoom:1;_zoom:normal;}.yui-skin-sam .yui-panel .hd,.yui-skin-sam .yui-panel .bd,.yui-skin-sam .yui-panel .ft{border-style:solid;border-width:0 1px;border-color:#808080;margin:0 -1px;*margin:0;*border:0;}.yui-skin-sam .yui-panel .hd{border-bottom:solid 1px #ccc;}.yui-skin-sam .yui-panel .bd,.yui-skin-sam .yui-panel .ft{background-color:#F2F2F2;}.yui-skin-sam .yui-panel .hd{padding:0 10px;font-size:93%;line-height:2;*line-height:1.9;font-weight:bold;color:#000;background:url(http://yui.yahooapis.com/2.8.1/build/assets/skins/sam/sprite.png) repeat-x 0 -200px;}.yui-skin-sam .yui-panel .bd{padding:10px;}.yui-skin-sam .yui-panel .ft{border-top:solid 1px #808080;padding:5px 10px;font-size:77%;}.yui-skin-sam .container-close{position:absolute;top:5px;right:6px;width:25px;height:15px;background:url(http://yui.yahooapis.com/2.8.1/build/assets/skins/sam/sprite.png) no-repeat 0 -300px;cursor:pointer;}.yui-skin-sam .yui-panel-container .underlay{right:-1px;left:-1px;}.yui-skin-sam .yui-panel-container.matte{padding:9px 10px;background-color:#fff;}.yui-skin-sam .yui-panel-container.shadow{_padding:2px 4px 0 2px;}.yui-skin-sam .yui-panel-container.shadow .underlay{position:absolute;top:2px;left:-3px;right:-3px;bottom:-3px;*top:4px;*left:-1px;*right:-1px;*bottom:-1px;_top:0;_left:0;_right:0;_bottom:0;_margin-top:3px;_margin-left:-1px;background-color:#000;opacity:.12;filter:alpha(opacity=12);}.yui-skin-sam .yui-dialog .ft{border-top:none;padding:0 10px 10px 10px;font-size:100%;}.yui-skin-sam .yui-dialog .ft .button-group{display:block;text-align:right;}.yui-skin-sam .yui-dialog .ft button.default{font-weight:bold;}.yui-skin-sam .yui-dialog .ft span.default{border-color:#304369;background-position:0 -1400px;}.yui-skin-sam .yui-dialog .ft span.default .first-child{border-color:#304369;}.yui-skin-sam .yui-dialog .ft span.default button{color:#fff;}.yui-skin-sam .yui-dialog .ft span.yui-button-disabled{background-position:0 -1500px;border-color:#ccc;}.yui-skin-sam .yui-dialog .ft span.yui-button-disabled .first-child{border-color:#ccc;}.yui-skin-sam .yui-dialog .ft span.yui-button-disabled button{color:#a6a6a6;}.yui-skin-sam .yui-simple-dialog .bd .yui-icon{background:url(http://yui.yahooapis.com/2.8.1/build/assets/skins/sam/sprite.png) no-repeat 0 0;width:16px;height:16px;margin-right:10px;float:left;}.yui-skin-sam .yui-simple-dialog .bd span.blckicon{background-position:0 -1100px;}.yui-skin-sam .yui-simple-dialog .bd span.alrticon{background-position:0 -1050px;}.yui-skin-sam .yui-simple-dialog .bd span.hlpicon{background-position:0 -1150px;}.yui-skin-sam .yui-simple-dialog .bd span.infoicon{background-position:0 -1200px;}.yui-skin-sam .yui-simple-dialog .bd span.warnicon{background-position:0 -1900px;}.yui-skin-sam .yui-simple-dialog .bd span.tipicon{background-position:0 -1250px;}.yui-skin-sam .yui-tt .bd{position:relative;top:0;left:0;z-index:1;color:#000;padding:2px 5px;border-color:#D4C237 #A6982B #A6982B #A6982B;border-width:1px;border-style:solid;background-color:#FFEE69;}.yui-skin-sam .yui-tt.show-scrollbars .bd{overflow:auto;}.yui-skin-sam .yui-tt-shadow{top:2px;right:-3px;left:-3px;bottom:-3px;background-color:#000;}.yui-skin-sam .yui-tt-shadow-visible{opacity:.12;filter:alpha(opacity=12);} +.yui-calcontainer{position:relative;float:left;_overflow:hidden;}.yui-calcontainer iframe{position:absolute;border:none;margin:0;padding:0;z-index:0;width:100%;height:100%;left:0;top:0;}.yui-calcontainer iframe.fixedsize{width:50em;height:50em;top:-1px;left:-1px;}.yui-calcontainer.multi .groupcal{z-index:1;float:left;position:relative;}.yui-calcontainer .title{position:relative;z-index:1;}.yui-calcontainer .close-icon{position:absolute;z-index:1;text-indent:-10000em;overflow:hidden;}.yui-calendar{position:relative;}.yui-calendar .calnavleft{position:absolute;z-index:1;text-indent:-10000em;overflow:hidden;}.yui-calendar .calnavright{position:absolute;z-index:1;text-indent:-10000em;overflow:hidden;}.yui-calendar .calheader{position:relative;width:100%;text-align:center;}.yui-calcontainer .yui-cal-nav-mask{position:absolute;z-index:2;margin:0;padding:0;width:100%;height:100%;_width:0;_height:0;left:0;top:0;display:none;}.yui-calcontainer .yui-cal-nav{position:absolute;z-index:3;top:0;display:none;}.yui-calcontainer .yui-cal-nav .yui-cal-nav-btn{display:-moz-inline-box;display:inline-block;}.yui-calcontainer .yui-cal-nav .yui-cal-nav-btn button{display:block;*display:inline-block;*overflow:visible;border:none;background-color:transparent;cursor:pointer;}.yui-calendar .calbody a:hover{background:inherit;}p#clear{clear:left;padding-top:10px;}.yui-skin-sam .yui-calcontainer{background-color:#f2f2f2;border:1px solid #808080;padding:10px;}.yui-skin-sam .yui-calcontainer.multi{padding:0 5px 0 5px;}.yui-skin-sam .yui-calcontainer.multi .groupcal{background-color:transparent;border:none;padding:10px 5px 10px 5px;margin:0;}.yui-skin-sam .yui-calcontainer .title{background:url(http://yui.yahooapis.com/2.8.1/build/assets/skins/sam/sprite.png) repeat-x 0 0;border-bottom:1px solid #ccc;font:100% sans-serif;color:#000;font-weight:bold;height:auto;padding:.4em;margin:0 -10px 10px -10px;top:0;left:0;text-align:left;}.yui-skin-sam .yui-calcontainer.multi .title{margin:0 -5px 0 -5px;}.yui-skin-sam .yui-calcontainer.withtitle{padding-top:0;}.yui-skin-sam .yui-calcontainer .calclose{background:url(http://yui.yahooapis.com/2.8.1/build/assets/skins/sam/sprite.png) no-repeat 0 -300px;width:25px;height:15px;top:.4em;right:.4em;cursor:pointer;}.yui-skin-sam .yui-calendar{border-spacing:0;border-collapse:collapse;font:100% sans-serif;text-align:center;margin:0;}.yui-skin-sam .yui-calendar .calhead{background:transparent;border:none;vertical-align:middle;padding:0;}.yui-skin-sam .yui-calendar .calheader{background:transparent;font-weight:bold;padding:0 0 .6em 0;text-align:center;}.yui-skin-sam .yui-calendar .calheader img{border:none;}.yui-skin-sam .yui-calendar .calnavleft{background:url(http://yui.yahooapis.com/2.8.1/build/assets/skins/sam/sprite.png) no-repeat 0 -450px;width:25px;height:15px;top:0;bottom:0;left:-10px;margin-left:.4em;cursor:pointer;}.yui-skin-sam .yui-calendar .calnavright{background:url(http://yui.yahooapis.com/2.8.1/build/assets/skins/sam/sprite.png) no-repeat 0 -500px;width:25px;height:15px;top:0;bottom:0;right:-10px;margin-right:.4em;cursor:pointer;}.yui-skin-sam .yui-calendar .calweekdayrow{height:2em;}.yui-skin-sam .yui-calendar .calweekdayrow th{padding:0;border:none;}.yui-skin-sam .yui-calendar .calweekdaycell{color:#000;font-weight:bold;text-align:center;width:2em;}.yui-skin-sam .yui-calendar .calfoot{background-color:#f2f2f2;}.yui-skin-sam .yui-calendar .calrowhead,.yui-skin-sam .yui-calendar .calrowfoot{color:#a6a6a6;font-size:85%;font-style:normal;font-weight:normal;border:none;}.yui-skin-sam .yui-calendar .calrowhead{text-align:right;padding:0 2px 0 0;}.yui-skin-sam .yui-calendar .calrowfoot{text-align:left;padding:0 0 0 2px;}.yui-skin-sam .yui-calendar td.calcell{border:1px solid #ccc;background:#fff;padding:1px;height:1.6em;line-height:1.6em;text-align:center;white-space:nowrap;}.yui-skin-sam .yui-calendar td.calcell a{color:#06c;display:block;height:100%;text-decoration:none;}.yui-skin-sam .yui-calendar td.calcell.today{background-color:#000;}.yui-skin-sam .yui-calendar td.calcell.today a{background-color:#fff;}.yui-skin-sam .yui-calendar td.calcell.oom{background-color:#ccc;color:#a6a6a6;cursor:default;}.yui-skin-sam .yui-calendar td.calcell.selected{background-color:#fff;color:#000;}.yui-skin-sam .yui-calendar td.calcell.selected a{background-color:#b3d4ff;color:#000;}.yui-skin-sam .yui-calendar td.calcell.calcellhover{background-color:#426fd9;color:#fff;cursor:pointer;}.yui-skin-sam .yui-calendar td.calcell.calcellhover a{background-color:#426fd9;color:#fff;}.yui-skin-sam .yui-calendar td.calcell.previous{color:#e0e0e0;}.yui-skin-sam .yui-calendar td.calcell.restricted{text-decoration:line-through;}.yui-skin-sam .yui-calendar td.calcell.highlight1{background-color:#cf9;}.yui-skin-sam .yui-calendar td.calcell.highlight2{background-color:#9cf;}.yui-skin-sam .yui-calendar td.calcell.highlight3{background-color:#fcc;}.yui-skin-sam .yui-calendar td.calcell.highlight4{background-color:#cf9;}.yui-skin-sam .yui-calendar a.calnav{border:1px solid #f2f2f2;padding:0 4px;text-decoration:none;color:#000;zoom:1;}.yui-skin-sam .yui-calendar a.calnav:hover{background:url(http://yui.yahooapis.com/2.8.1/build/assets/skins/sam/sprite.png) repeat-x 0 0;border-color:#A0A0A0;cursor:pointer;}.yui-skin-sam .yui-calcontainer .yui-cal-nav-mask{background-color:#000;opacity:.25;filter:alpha(opacity=25);}.yui-skin-sam .yui-calcontainer .yui-cal-nav{font-family:arial,helvetica,clean,sans-serif;font-size:93%;border:1px solid #808080;left:50%;margin-left:-7em;width:14em;padding:0;top:2.5em;background-color:#f2f2f2;}.yui-skin-sam .yui-calcontainer.withtitle .yui-cal-nav{top:4.5em;}.yui-skin-sam .yui-calcontainer.multi .yui-cal-nav{width:16em;margin-left:-8em;}.yui-skin-sam .yui-calcontainer .yui-cal-nav-y,.yui-skin-sam .yui-calcontainer .yui-cal-nav-m,.yui-skin-sam .yui-calcontainer .yui-cal-nav-b{padding:5px 10px 5px 10px;}.yui-skin-sam .yui-calcontainer .yui-cal-nav-b{text-align:center;}.yui-skin-sam .yui-calcontainer .yui-cal-nav-e{margin-top:5px;padding:5px;background-color:#EDF5FF;border-top:1px solid black;display:none;}.yui-skin-sam .yui-calcontainer .yui-cal-nav label{display:block;font-weight:bold;} +.yui-skin-sam .yui-calcontainer .yui-cal-nav-mc{width:100%;_width:auto;}.yui-skin-sam .yui-calcontainer .yui-cal-nav-y input.yui-invalid{background-color:#FFEE69;border:1px solid #000;}.yui-skin-sam .yui-calcontainer .yui-cal-nav-yc{width:4em;}.yui-skin-sam .yui-calcontainer .yui-cal-nav .yui-cal-nav-btn{border:1px solid #808080;background:url(http://yui.yahooapis.com/2.8.1/build/assets/skins/sam/sprite.png) repeat-x 0 0;background-color:#ccc;margin:auto .15em;}.yui-skin-sam .yui-calcontainer .yui-cal-nav .yui-cal-nav-btn button{padding:0 8px;font-size:93%;line-height:2;*line-height:1.7;min-height:2em;*min-height:auto;color:#000;}.yui-skin-sam .yui-calcontainer .yui-cal-nav .yui-cal-nav-btn.yui-default{border:1px solid #304369;background-color:#426fd9;background:url(http://yui.yahooapis.com/2.8.1/build/assets/skins/sam/sprite.png) repeat-x 0 -1400px;}.yui-skin-sam .yui-calcontainer .yui-cal-nav .yui-cal-nav-btn.yui-default button{color:#fff;} +*/ \ No newline at end of file diff --git a/version.php b/version.php index 6b50468..7a2e199 100644 --- a/version.php +++ b/version.php @@ -1,4 +1,4 @@ -version = 2011020901; // The current module version (Date: YYYYMMDDXX) -$module->release = '2.2.5'; +$module->release = '2.3.0'; $module->cron = 0; // Period for cron to check this module (secs) ?>