Commit beae0685 authored by Robert Lyon's avatar Robert Lyon Committed by Gerrit Code Review
Browse files

Merge changes from topic 'Analytics'

* changes:
  Bug 1697248: Lang string updates
  Bug 1714100: Check the status of current elasticsearch index
  Bug 1692385: Adjusting the handle event for 'updateviewaccess'
  Bug 1711958: Allow update_record to return id of record updated
  Bug 1697248: More updates for report navigation and reports
  Bug 1697248: Update report navigation and reports
  Bug 1692385: Adjusting the event_log table
  Bug 1710710: Placement of public search box vs language selector
parents 523d98ee 9a4834d0
......@@ -164,15 +164,15 @@ $siteoptionform = array(
),
'staffreports' => array(
'type' => 'switchbox',
'title' => get_string('staffuserreports', 'admin'),
'description' => get_string('staffuserreportsdescription1', 'admin'),
'title' => get_string('staffaccessreports', 'admin'),
'description' => get_string('staffaccessreportsdescription', 'admin'),
'defaultvalue' => get_config('staffreports'),
'disabled' => in_array('staffreports', $OVERRIDDEN),
),
'staffstats' => array(
'type' => 'switchbox',
'title' => get_string('staffuserstats', 'admin'),
'description' => get_string('staffuserstatsdescription1', 'admin'),
'title' => get_string('institutionstaffallreports', 'admin'),
'description' => get_string('institutionstaffallreportsdescription', 'admin'),
'defaultvalue' => get_config('staffstats'),
'disabled' => in_array('staffstats', $OVERRIDDEN),
),
......@@ -751,6 +751,14 @@ $siteoptionform = array(
'disabled' => in_array('eventlogexpiry', $OVERRIDDEN),
'class' => 'double'
),
'eventlogenhancedsearch' => array(
'type' => 'switchbox',
'title' => get_string('eventlogenhancedsearch', 'admin'),
'description' => get_string('eventlogenhancedsearchdescription', 'admin'),
'defaultvalue' => get_config('eventlogenhancedsearch'),
'help' => true,
'disabled' => (get_config('searchplugin') != 'elasticsearch' || in_array('eventlogenhancedsearch', $OVERRIDDEN)),
),
),
),
)
......@@ -797,7 +805,7 @@ function siteoptions_submit(Pieform $form, $values) {
'registerterms', 'licensemetadata', 'licenseallowcustom', 'allowmobileuploads', 'creategroups', 'createpublicgroups', 'allowgroupcategories', 'wysiwyg',
'staffreports', 'staffstats', 'userscandisabledevicedetection', 'watchlistnotification_delay',
'masqueradingreasonrequired', 'masqueradingnotified', 'searchuserspublic',
'eventloglevel', 'eventlogexpiry', 'sitefilesaccess', 'exporttoqueue', 'defaultmultipleblogs',
'eventloglevel', 'eventlogexpiry', 'eventlogenhancedsearch', 'sitefilesaccess', 'exporttoqueue', 'defaultmultipleblogs',
);
if (get_config('dropdownmenuenabled')) {
$fields = array_merge($fields, array('dropdownmenu'));
......@@ -846,7 +854,10 @@ function siteoptions_submit(Pieform $form, $values) {
// Ensure allowpublicprofiles is set as well
$values['allowpublicprofiles'] = 1;
}
// Can only set advanced event log searching if search plugin is elasticsearch
if (!empty($values['eventlogenhancedsearch']) && $values['searchplugin'] != 'elasticsearch') {
$values['eventlogenhancedsearch'] = false;
}
$oldsearchplugin = get_config('searchplugin');
$oldlanguage = get_config('lang');
$oldtheme = get_config('theme');
......
......@@ -20,6 +20,7 @@ define('SECTION_PAGE', 'usersearch');
define('IGNORE_FETCH_REMOTE_AVATAR', 1);
require_once('searchlib.php');
$SESSION->set('usersforstats', null);
$search = (object) array(
'query' => trim(param_variable('query', '')),
'f' => param_alpha('f', null), // first initial
......
......@@ -29,18 +29,39 @@ if (empty($institution)) {
$institution = 'mahara';
}
}
$start = param_variable('start', null);
$end = param_variable('end', null);
$start = $start ? format_date(strtotime($start), 'strftimew3cdate') : null;
$end = $end ? format_date(strtotime($end), 'strftimew3cdate') : null;
if (empty($extradata->start) && !empty($start)) {
$extradata->start = $start;
}
if (empty($extradata->end) && !empty($end)) {
$extradata->end = $end;
}
$activecolumns = $SESSION->get('columnsforstats');
$activecolumns = !empty($activecolumns) ? $activecolumns : array();
$extradata->columns = $activecolumns;
$type = param_alpha('type', 'users');
$subtype = param_alpha('subtype', $type);
$extraparams = new stdClass();
$extraparams->type = $type;
$extraparams->subtype = $subtype;
$extraparams->offset = param_integer('offset', 0);
$extraparams->limit = param_integer('limit', 10);
$extraparams->sort = isset($extradata->sort) ? $extradata->sort : 'displayname';
$extraparams->sortdesc = isset($extradata->sortdesc) ? true : false;
$extraparams->start = param_alphanumext('start', null);
$extraparams->end = param_alphanumext('end', null);
$extraparams->start = $start;
$extraparams->end = $end;
$extraparams->field = isset($extradata->field) ? $extradata->field : (($institution == 'all') ? 'count_usr' : 'count_members');
$extraparams->extra = (array)$extradata;
list($subpages, $institutiondata, $subpagedata) = display_statistics($institution, $type, $extraparams);
list($subpages, $subpagedata) = display_statistics($institution, $type, $extraparams);
json_reply(false, (object) array('message' => false, 'data' => $subpagedata['table']));
if (!empty($extradata) && !empty($extradata->csvdownload)) {
json_reply(false, (object) array('message' => false, 'data' => 'downloadready'));
}
else {
json_reply(false, (object) array('message' => false, 'data' => $subpagedata['table'], 'tableheadings' => $subpagedata['tableheadings'], 'extraparams' => $extraparams));
}
......@@ -11,7 +11,7 @@
define('INTERNAL', 1);
define('INSTITUTIONALSTAFF', 1);
define('MENUITEM', 'manageinstitutions/statistics');
define('MENUITEM', 'reports');
require(dirname(dirname(dirname(__FILE__))).'/init.php');
require(get_config('libroot') . 'statistics.php');
......@@ -23,15 +23,19 @@ if (!is_logged_in()) {
$institution = param_alphanum('institution', null);
$notallowed = false;
if (!empty($institution)) {
$staffstats = get_config('staffstats');
if (!$USER->get('admin') && !$USER->is_institutional_admin($institution) && (!$USER->is_institutional_staff($institution) || ($USER->is_institutional_staff($institution) && empty($staffstats)))) {
$notallowed = true;
}
$allstaffstats = get_config('staffstats');
$userstaffstats = get_config('staffreports'); // The old 'Users/access list/masquerading' reports from users section
if (!$USER->get('admin') && !$USER->is_institutional_admin($institution) &&
(!$USER->is_institutional_staff($institution) ||
($USER->is_institutional_staff($institution) && empty($allstaffstats) && empty($userstaffstats)))) {
$notallowed = true;
}
if (!$notallowed) {
$institutionelement = get_institution_selector(true, false, true, get_config('staffstats'), ($USER->get('admin') || $USER->get('staff')));
// Get the institution selector to worl out what institutions they are allowed to see
$institutionelement = get_institution_selector(true, false, true, ($allstaffstats || $userstaffstats), ($USER->get('admin') || $USER->get('staff')));
}
if (empty($institutionelement) || $notallowed) {
......@@ -48,58 +52,243 @@ else if (!empty($institution)) {
$institutionelement['defaultvalue'] = $institution;
}
$institutionselector = pieform(array(
'name' => 'usertypeselect',
'class' => 'form-inline',
'elements' => array(
'institution' => $institutionelement,
)
));
if ($usersparam = param_variable('users', null)) {
$newuserids = is_array($usersparam) ? array_map('intval', $usersparam) : null;
$SESSION->set('usersforstats', $newuserids);
}
define('PAGEHEADINGARROW', get_string('reports', 'statistics'));
$type = param_alpha('type', 'users');
$subtype = param_alpha('subtype', '');
if (isset($institution)) {
if (!$USER->get('admin') && !$USER->is_institutional_admin($institution) &&
$USER->is_institutional_staff($institution) && empty(get_config('staffstats')) && !empty(get_config('staffreports'))) {
// we need to give them the correct default report
$subtype = !empty($subtype) ? $subtype : 'userdetails';
}
}
// Work out the title for the report
$reporttype = get_string('peoplereports', 'statistics');
if ($subtype && $subtype !== $type) {
if (string_exists($type . $subtype . 'reports', 'statistics')) {
$reporttype = get_string($type . $subtype . 'reports', 'statistics');
}
}
else {
if (string_exists($type . 'reports', 'statistics')) {
$reporttype = get_string($type . 'reports', 'statistics');
}
}
define('SUBSECTIONHEADING', $reporttype);
$subtype = !empty($subtype) ? $subtype : $type;
$showall = ($institution == 'all') ? true : false;
if ($showall) {
define('TITLE', get_string('statisticsforallinstitutions', 'admin'));
$icon = 'icon-area-chart';
define('TITLE', get_string('Allinstitutions', 'mahara'));
}
else {
define('TITLE', get_string('institutionstatisticsfor', 'admin', get_field('institution', 'displayname', 'name', $institution)));
$icon = 'icon-university';
define('TITLE', get_field('institution', 'displayname', 'name', $institution));
}
$type = param_alpha('type', 'users');
$start = param_variable('start', null);
$end = param_variable('end', null);
$start = $start ? format_date(strtotime($start), 'strftimew3cdate') : null;
$end = $end ? format_date(strtotime($end), 'strftimew3cdate') : null;
$activecolumns = $SESSION->get('columnsforstats');
$activecolumns = !empty($activecolumns) ? $activecolumns : array();
$extraparams = new stdClass();
$extraparams->type = $type;
$extraparams->subtype = $subtype;
$extraparams->institution = $institution;
$extraparams->offset = param_integer('offset', 0);
$extraparams->limit = param_integer('limit', 10);
$extraparams->sort = param_alphanumext('sort', 'displayname');
$extraparams->sortdesc = param_boolean('sortdesc');
$extraparams->start = param_alphanumext('start', null);
$extraparams->end = param_alphanumext('end', null);
list($subpages, $institutiondata, $subpagedata) = display_statistics($institution, $type, $extraparams);
$extraparams->extra = array('sort' => param_alphanumext('sort', ''),
'sortdesc' => param_boolean('sortdesc'),
'start' => $start,
'end' => $end,
'columns' => $activecolumns,
);
$jsondatestart = !empty($start) ? "'" . $start ."'" : 'null';
$jsondateend = !empty($end) ? "'" . $end . "'" : 'null';
$extrajson = json_encode($extraparams->extra);
$wwwroot = get_config('wwwroot');
$js = <<< EOF
// Need to handle the pieform submission for the 'configure report' here
// This also populates the needed 'calendar' element headdata
// TODO - look to see if we need the ajax fetching of the config form or can we prepopulate the page with it?
$pieform = pieform_instance(report_config_form($extraparams, $institutionelement));
$js = <<<JS
var opts = {'id':'statistics_table_container',
'type':'{$type}',
'subtype':'{$subtype}',
'extradata':'{$extrajson}',
'institution':'{$institution}',
'offset':{$extraparams->offset},
'limit':{$extraparams->limit},
'start':{$jsondatestart},
'end':{$jsondateend},
};
function show_stats_config() {
sendjsonrequest(config['wwwroot'] + 'admin/users/statsconfig.json.php', opts, 'POST', function(data) {
$('#modal-configs .modal-body').empty();
$('#modal-configs .modal-body').append(data.data.html);
$("#cancel_reportconfigform_submit").on('click', function(e) {
e.preventDefault();
$("#modal-configs").modal("hide");
});
// The institution selector can be a hidden field if only 1 choice
// So we need to make sure the field is a select field and not a hidden one
var instselect = $('#reportconfigform_institution');
if (instselect.is('select')) {
instselect.select2({
dropdownParent: $("#modal-configs"),
width: '100%'
});
}
});
}
function update_table_headers(data) {
var headers = (data) ? data.tableheadings : null;
var activeheaders = (data) ? data.data.activeheadings : null;
var limit = (data) ? data.extraparams.limit : null;
if (headers) {
var newhtml = '';
$.each(headers, function(i, heading) {
if (heading.selected) {
newhtml += heading.html;
}
});
$('#statistics_table thead tr').html(newhtml);
}
$('#statistics_table thead tr').find('a.col_head_link').each(function (i, a) {
$(a).off('click');
$(a).on('click', function(e) {
e.preventDefault();
var loc = a.href.indexOf('?');
var queryData = [];
var extraData = {};
if (loc != -1) {
queryData = parseQueryString(a.href.substring(loc + 1, a.href.length));
queryData.limit = limit;
queryData.offset = 0;
// move the ones we need in extradata to there
extraData.sort = queryData.sort;
extraData.sortdesc = queryData.sortdesc || false;
extraData.columns = [];
if (activeheaders) {
for (x in activeheaders) {
extraData.columns.push(x);
}
}
queryData.extradata = JSON.stringify(extraData);
}
p.sendQuery(queryData, true);
});
});
}
$(document).on('pageupdated', function(e, data) {
// Update the table header links
$('#statistics_table thead tr').find('a').off('click');
update_table_headers(data);
});
jQuery(function ($) {
{$subpagedata['table']['pagination_js']}
// JS Code to deal with the report configuration modal
// This fetches the form for choosing the results with filters, eg time frame
// We need to show/hide modal explicitly so the on 'show.bs.modal' fires allowing
// us to do ajax call for form as modal opens
$('#configbtn').click(function() {
$("#modal-configs").modal("show");
});
$("#modal-configs").on('show.bs.modal', function () {
show_stats_config();
});
$("#modal-configs .close").on('click', function () {
$("#modal-configs").modal("hide");
});
$('.btn.filter').on('click', function() {
var filteropt = $(this);
var filteroptid = filteropt.prop('id');
sendjsonrequest(config['wwwroot'] + 'json/stats_setting.php', {'setting':filteroptid}, 'POST', function(data) {
filteropt.parent().hide();
$('#statistics_table th a:first').trigger('click');
});
});
$('#messages .alert-success').delay(1000).hide("slow");
update_table_headers(null);
if ($('.statinfoblock').length > 0) {
var maxHeight = Math.max.apply(null, $(".statinfoblock").map(function () {
return $(this).height();
}).get());
$('.statinfoblock').css('height', maxHeight + 'px');
}
});
JS;
function reloadStats() {
window.location.href = '{$wwwroot}admin/users/statistics.php?institution='+$('#usertypeselect_institution').val() +'&type={$type}';
if ($type == 'information' && (empty($subtype) || $subtype == 'information')) {
if ($institution == 'all') {
$institutiondata = site_statistics(true);
}
else {
$institutiondata = institution_statistics($institution, true);
}
$subpagedata = false;
$subpages = false;
$subpagination = '';
}
else {
list($subpages, $subpagedata) = display_statistics($institution, $type, $extraparams);
$subpagination = !empty($subpagedata['table']) ? $subpagedata['table']['pagination_js'] : false;
$institutiondata = false;
if ($subpagination) {
$js .= <<<JS
jQuery(function ($) {
// JS Code to deal with the download CSV button
// We want the CSV to return all results for time period rather than the current paginated page
// So we want to do this asynchronistically
$('#csvdownload').on('click', function(e) {
e.preventDefault();
var obj = JSON.parse(opts.extradata);
obj['csvdownload'] = true;
opts.extradata = JSON.stringify(obj);
sendjsonrequest(config.wwwroot + 'admin/users/statistics.json.php', opts, 'POST', function (data) {
window.location = config.wwwroot + 'download.php';
});
});
$('#usertypeselect_institution').on('change', reloadStats);
p = {$subpagination}
p.extraData = $extrajson;
});
EOF;
JS;
}
}
$smarty = smarty(array('paginator','js/chartjs/Chart.min.js'));
setpageicon($smarty, $icon);
$smarty->assign('INLINEJAVASCRIPT', $js);
$smarty->assign('institutiondata', $institutiondata);
$smarty->assign('type', $type);
$smarty->assign('subpages', $subpages);
$smarty->assign('showall', ($showall ? '_all' : ''));
$smarty->assign('subpagedata', $subpagedata);
$smarty->assign('institutionselector', $institutionselector);
if (isset($subpagedata['table']) && isset($subpagedata['table']['settings'])) {
$smarty->assign('reportsettings', get_report_settings($subpagedata['table']['settings']));
}
$smarty->display('admin/users/statistics.tpl');
<?php
/**
*
* @package mahara
* @subpackage core
* @author Catalyst IT Ltd
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
* @copyright For copyright information on Mahara, please see the README file distributed with this software.
*
*/
define('INTERNAL', 1);
define('INSTITUTIONALSTAFF', 1);
define('JSON', 1);
require(dirname(dirname(dirname(__FILE__))) . '/init.php');
require(get_config('libroot') . 'statistics.php');
require_once('institution.php');
$extradata = json_decode(param_variable('extradata'));
$institution = param_alphanum('institution', null);
if (empty($institution)) {
if (isset($extradata->institution)) {
$institution = $extradata->institution;
}
else if ($USER->get('admin') || $USER->get('staff')) {
$institution = 'all';
}
else {
$institution = 'mahara';
}
}
$notallowed = false;
$allstaffstats = get_config('staffstats');
$userstaffstats = get_config('staffreports'); // The old 'Users/access list/masquerading' reports from users section
if (!$USER->get('admin') && !$USER->is_institutional_admin($institution) &&
(!$USER->is_institutional_staff($institution) ||
($USER->is_institutional_staff($institution) && empty($allstaffstats) && empty($userstaffstats)))) {
$notallowed = true;
}
if (!$notallowed) {
// Get the institution selector to worl out what institutions they are allowed to see
$institutionelement = get_institution_selector(true, false, true, ($allstaffstats || $userstaffstats), ($USER->get('admin') || $USER->get('staff')));
}
if (empty($institutionelement) || $notallowed) {
json_reply(true, get_string('statistics', 'noaccessreport'));
exit;
}
$type = param_alpha('type', 'users');
$subtype = param_alpha('subtype', $type);
$extraparams = new stdClass();
$extraparams->type = $type;
$extraparams->subtype = $subtype;
$extraparams->institution = $institution;
$extraparams->offset = param_integer('offset', 0);
$extraparams->limit = param_integer('limit', 10);
$extraparams->sort = isset($extradata->sort) ? $extradata->sort : 'displayname';
$extraparams->sortdesc = isset($extradata->sortdesc) ? true : false;
$extraparams->start = param_alphanumext('start', null);
$extraparams->end = param_alphanumext('end', null);
$extraparams->field = isset($extradata->field) ? $extradata->field : (($institution == 'all') ? 'count_usr' : 'count_members');
$extraparams->extra = (array)$extradata;
$formarray = report_config_form($extraparams, $institutionelement);
$form = $formarray ? pieform($formarray) : '';
$reportinfo = get_string('reportdesc' . $subtype, 'statistics');
$tableheaders = '';
$function = $subtype . '_statistics_headers';
if (function_exists($function)) {
$tableheaders = $function($extraparams->extra, null);
}
$smarty = smarty_core();
$smarty->assign('form', $form);
// $smarty->assign('tableheadings', $tableheaders);
$smarty->assign('reportinformation', $reportinfo);
$html = $smarty->fetch('admin/users/statsconfig.tpl');
$data['html'] = $html;
json_reply(false, (object) array('message' => false, 'data' => $data));
......@@ -138,7 +138,7 @@ class PluginArtefactBlog extends PluginArtefact {
$name = display_name($user, null, true);
$blog = new ArtefactTypeBlog(0, (object) array(
'title' => get_string('defaultblogtitle', 'artefact.blog', $name),
'owner' => $user['id'],
'owner' => is_object($user) ? $user->id : $user['id'],
));
$blog->commit();
}
......
......@@ -146,6 +146,7 @@ class PluginArtefactFile extends PluginArtefact {
}
public static function newuser($event, $user) {
$user = is_object($user) ? (array)$user : $user;
if (empty($user['quota'])) {
update_record('usr', array('quota' => get_config_plugin('artefact', 'file', 'defaultquota')), array('id' => $user['id']));
}
......
......@@ -711,7 +711,12 @@ abstract class ArtefactType implements IArtefactType {
$this->log('deleted');
}
handle_event('deleteartefact', $this);
$ignorefields = array(
'dirty', 'deleted', 'mtime', 'atime',
'tags', 'allowcomments', 'approvecomments', 'path'
);
handle_event('deleteartefact', $this, $ignorefields);
// Set flags.
$this->dirty = false;
......@@ -772,8 +777,8 @@ abstract class ArtefactType implements IArtefactType {
}
call_static_method($classname, 'bulk_delete', $ids);
}
handle_event('deleteartefacts', $artefactids);
$logdata = array_merge($containers, $leaves);
handle_event('deleteartefacts', $logdata);
db_commit();
}
......
......@@ -1746,6 +1746,14 @@ class LiveUser extends User {
// Record the successful login in the usr_login_data table
insert_record('usr_login_data', (object) array('usr' => $user->id, 'ctime' => db_format_timestamp($time)));
if (get_config('eventloglevel') == 'all') {
// if we are doing fill event logging also record it there as the records
// in usr_login_data is deleted when usr is deleted but event_log data stays
insert_record('event_log', (object) array('usr' => $user->id,
'realusr' => $user->id,
'event' => 'login',
'ctime' => db_format_timestamp($time)));
}
// If user has chosen a language while logged out, save it as their lang pref.
$sessionlang = $this->SESSION->get('lang');
......
......@@ -865,6 +865,10 @@ class BlockInstance {
return '';
}
public function to_stdclass() {
return (object)get_object_vars($this);
}
/**
* Builds the HTML for the block, inserting the blocktype content at the
* appropriate place
......@@ -1397,9 +1401,15 @@ class BlockInstance {
$this->dirty = false;
return;
}
$ignorefields = array('order', 'dirty',
'ignoreconfigdata' => array('retractable',
'removeoncancel',
'sure',
'retractedonload'
)
);
//Propagate the deletion of the block
handle_event('deleteblockinstance', $this);
handle_event('deleteblockinstance', $this, $ignorefields);
db_begin();
safe_require('blocktype', $this->get('blocktype'), 'lib.php', 'require_once', true);
......
<?php
/**
*
* @package mahara