Commit f30ba8b8 authored by Hugh Davenport's avatar Hugh Davenport Committed by Gerrit Code Review
Browse files

Merge changes I0351b21a,Ic116c836

* changes:
  Add a page to update group members via CSV file
  Add page for Group CSV Uploads
parents 2deaf8db 350ef921
<?php
/**
* Mahara: Electronic portfolio, weblog, resume builder and social networking
* Copyright (C) 2011 Catalyst IT Ltd and others; see:
* http://wiki.mahara.org/Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @package mahara
* @subpackage admin
* @author Catalyst IT Ltd
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL
* @copyright (C) 2011 Catalyst IT Ltd http://catalyst.net.nz
*
*/
define('INTERNAL', 1);
define('INSTITUTIONALADMIN', 1);
define('MENUITEM', 'managegroups/uploadcsv');
require(dirname(dirname(dirname(__FILE__))) . '/init.php');
define('TITLE', get_string('uploadgroupcsv', 'admin'));
require_once('pieforms/pieform.php');
require_once(get_config('libroot') . 'group.php');
require_once(get_config('libroot') . 'institution.php');
safe_require('artefact', 'internal');
raise_memory_limit("512M");
// Turn on autodetecting of line endings, so mac newlines (\r) will work
ini_set('auto_detect_line_endings', 1);
$FORMAT = array();
$ALLOWEDKEYS = array(
'shortname',
'displayname',
'description',
'grouptype',
'public',
);
if ($USER->get('admin')) {
$ALLOWEDKEYS[] = 'usersautoadded';
$ALLOWEDKEYS[] = 'quota';
}
$MANDATORYFIELDS = array(
'shortname',
'displayname',
'grouptype',
);
$UPDATES = array(); // During validation, remember which group already exist
$JOINTYPES = array();
foreach (group_get_grouptypes() as $type) {
safe_require('grouptype', $type);
$JOINTYPES[$type] = call_static_method('GroupType' . $type, 'allowed_join_types');
}
$form = array(
'name' => 'uploadcsv',
'elements' => array(
'institution' => get_institution_selector(),
'file' => array(
'type' => 'file',
'title' => get_string('csvfile', 'admin'),
'description' => get_string('groupcsvfiledescription', 'admin'),
'rules' => array(
'required' => true
)
),
'updategroups' => array(
'type' => 'checkbox',
'title' => get_string('updategroups', 'admin'),
'description' => get_string('updategroupsdescription', 'admin'),
'defaultvalue' => false,
),
'submit' => array(
'type' => 'submit',
'value' => get_string('uploadgroupcsv', 'admin')
)
)
);
/**
* The CSV file is parsed here so validation errors can be returned to the
* user. The data from a successful parsing is stored in the <var>$CVSDATA</var>
* array so it can be accessed by the submit function
*
* @param Pieform $form The form to validate
* @param array $values The values submitted
*/
function uploadcsv_validate(Pieform $form, $values) {
global $CSVDATA, $ALLOWEDKEYS, $MANDATORYFIELDS, $JOINTYPES, $FORMAT, $USER, $UPDATES;
// Don't even start attempting to parse if there are previous errors
if ($form->has_errors()) {
return;
}
if ($values['file']['size'] == 0) {
$form->set_error('file', $form->i18n('rule', 'required', 'required', array()));
return;
}
$institution = $values['institution'];
if (!$USER->can_edit_institution($institution)) {
$form->set_error('institution', get_string('notadminforinstitution', 'admin'));
return;
}
require_once('csvfile.php');
$csvgroups = new CsvFile($values['file']['tmp_name']);
$csvgroups->set('allowedkeys', $ALLOWEDKEYS);
$csvgroups->set('mandatoryfields', $MANDATORYFIELDS);
$csvdata = $csvgroups->get_data();
if (!empty($csvdata->errors['file'])) {
$form->set_error('file', $csvdata->errors['file']);
return;
}
$csverrors = new CSVErrors();
$formatkeylookup = array_flip($csvdata->format);
$shortnames = array();
$displaynames = array();
foreach ($csvdata->data as $key => $line) {
// If headers exists, increment i = key + 2 for actual line number
$i = ($csvgroups->get('headerExists')) ? ($key + 2) : ($key + 1);
// Trim non-breaking spaces -- they get left in place by File_CSV
foreach ($line as &$field) {
$field = preg_replace('/^(\s|\xc2\xa0)*(.*?)(\s|\xc2\xa0)*$/', '$2', $field);
}
if (count($line) != count($csvdata->format)) {
$csverrors->add($i, get_string('uploadcsverrorwrongnumberoffields', 'admin', $i));
continue;
}
$shortname = $line[$formatkeylookup['shortname']];
$displayname = $line[$formatkeylookup['displayname']];
$grouptype = $line[$formatkeylookup['grouptype']];
if (!preg_match('/^[a-zA-Z0-9_.-]{2,255}$/', $shortname)) {
$csverrors->add($i, get_string('uploadgroupcsverrorinvalidshortname', 'admin', $i, $shortname));
}
if (isset($shortnames[$shortname])) {
// Duplicate shortname within this file.
$csverrors->add($i, get_string('uploadgroupcsverrorshortnamealreadytaken', 'admin', $i, $shortname));
}
else if (!$values['updategroups']) {
// The groupname must be new
if (record_exists('group', 'shortname', $shortname, 'institution', $institution)) {
$csverrors->add($i, get_string('uploadgroupcsverrorshortnamealreadytaken', 'admin', $i, $shortname));
}
}
$shortnames[$shortname] = array(
'shortname' => $shortname,
'displayname' => $displayname,
'grouptype' => $grouptype,
'lineno' => $i,
'raw' => $line,
);
if (isset($displaynames[strtolower($displayname)])) {
// Duplicate displayname within this file
$csverrors->add($i, get_string('uploadgroupcsverrorsgroupnamealreadyexists', 'admin', $i, $displayname));
}
else if (!$values['updategroups']) {
// The displayname must be new
if (get_records_sql_array('SELECT id FROM {group} WHERE LOWER(TRIM(name)) = ?', array(strtolower(trim($displayname))))) {
$csverrors->add($i, get_string('uploadgroupcsverrorgroupnamealreadyexists', 'admin', $i, $displayname));
}
}
else {
// This displayname must be new if not our shortname/institution
if (get_records_sql_array('
SELECT id FROM {group}
WHERE LOWER(TRIM(name)) = ?
AND NOT (shortname = ? AND institution = ?)',
array(
strtolower(trim($displayname)),
$shortname,
$institution
))) {
$csverrors->add($i, get_string('uploadgroupcsverrorgroupnamealreadyexists', 'admin', $i, $displayname));
}
}
$displaynames[strtolower($displayname)] = 1;
$groupjointype = split('/', $grouptype);
if (count($groupjointype) != 2) {
$csverrors->add($i, get_string('uploadgroupcsverrorinvalidgrouptype', 'admin', $i, $grouptype));
}
else {
if (!isset($JOINTYPES[$groupjointype[0]]) || !in_array($groupjointype[1], $JOINTYPES[$groupjointype[0]])) {
$csverrors->add($i, get_string('uploadgroupcsverrorinvalidgrouptype', 'admin', $i, $grouptype));
}
}
if ($values['updategroups']) {
foreach ($shortnames as $shortname => $data) {
// TODO: Any other checks we have to do for updated groups
$UPDATES[$shortname] = 1;
}
}
}
if ($errors = $csverrors->process()) {
$form->set_error('file', clean_html($errors));
return;
}
$FORMAT = $csvdata->format;
$CSVDATA = $csvdata->data;
}
/**
* Add the users to the system. Make sure that they have to change their
* password on next login also.
*/
function uploadcsv_submit(Pieform $form, $values) {
global $SESSION, $CSVDATA, $FORMAT, $UPDATES, $USER;
$formatkeylookup = array_flip($FORMAT);
$institution = $values['institution'];
if ($values['updategroups']) {
log_info('Updating groups from the CSV file');
}
else {
log_info('Inserting groups from the CSV file');
}
db_begin();
$addedgroups = array();
foreach ($CSVDATA as $record) {
$groupjointype = split('/', $record[$formatkeylookup['grouptype']]);
$group = new StdClass;
$group->name = $record[$formatkeylookup['displayname']];
$group->shortname = $record[$formatkeylookup['shortname']];
$group->institution = $institution;
$group->grouptype = $groupjointype[0];
$group->jointype = $groupjointype[1];
foreach ($FORMAT as $field) {
if ($field == 'displayname' || $field == 'shortname' || $field == 'grouptype') {
continue;
}
$group->{$field} = $record[$formatkeylookup[$field]];
}
if (!$values['updategroups'] || !isset($UPDATES[$group->shortname])) {
$group->members = array($USER->id => 'admin');
$group->id = group_create((array)$group);
$addedgroups[] = $group;
log_debug('added group ' . $group->name);
}
else if (isset($UPDATES[$group->shortname])) {
$shortname = $group->shortname;
$updates = group_update($group);
if (empty($updates)) {
unset($UPDATES[$shortname]);
}
else {
if (isset($updates['name'])) {
$updates['displayname'] = $updates['name'];
unset($updates['name']);
}
$UPDATES[$shortname] = $updates;
log_debug('updated group ' . $group->name . ' (' . implode(', ', array_keys((array)$updates)) . ')');
}
}
}
db_commit();
$SESSION->add_ok_msg(get_string('csvfileprocessedsuccessfully', 'admin'));
if ($UPDATES) {
$updatemsg = smarty_core();
$updatemsg->assign('added', count($addedgroups));
$updatemsg->assign('updates', $UPDATES);
$SESSION->add_info_msg($updatemsg->fetch('admin/groups/csvupdatemessage.tpl'), false);
}
else {
$SESSION->add_ok_msg(get_string('numbernewgroupsadded', 'admin', count($addedgroups)));
}
redirect('/admin/groups/uploadcsv.php');
}
$grouptypes = "<ul class=fieldslist>\n";
foreach ($JOINTYPES as $grouptype => $jointypes) {
foreach ($jointypes as $type) {
$grouptypes .= '<li>' . hsc($grouptype) . '/' . hsc($type) . "</li>\n";
}
}
$grouptypes .= "<div class=cl></div></ul>\n";
$fields = "<ul class=fieldslist>\n";
foreach ($ALLOWEDKEYS as $type) {
$helplink = '';
if ($type == 'grouptype' || $type == 'public' || $type == 'usersautoadded') {
$helplink = get_help_icon('core', 'groups', 'creategroup', $type);
}
$fields .= '<li>' . hsc($type) . $helplink . "</li>\n";
}
$fields .= "<div class=cl></div></ul>\n";
$uploadcsvpagedescription = get_string('uploadgroupcsvpagedescription2', 'admin', get_help_icon('core', 'groups', 'creategroup', 'grouptype'), $grouptypes, $fields);
$form = pieform($form);
$smarty = smarty(array('adminuploadcsv'));
$smarty->assign('uploadcsvpagedescription', $uploadcsvpagedescription);
$smarty->assign('uploadcsvform', $form);
$smarty->assign('PAGEHEADING', TITLE);
$smarty->display('admin/groups/uploadcsv.tpl');
<?php
/**
* Mahara: Electronic portfolio, weblog, resume builder and social networking
* Copyright (C) 2011 Catalyst IT Ltd and others; see:
* http://wiki.mahara.org/Contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @package mahara
* @subpackage admin
* @author Catalyst IT Ltd
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL
* @copyright (C) 2011 Catalyst IT Ltd http://catalyst.net.nz
*
*/
define('INTERNAL', 1);
define('INSTITUTIONALADMIN', 1);
define('MENUITEM', 'managegroups/uploadmemberscsv');
require(dirname(dirname(dirname(__FILE__))) . '/init.php');
define('TITLE', get_string('uploadgroupmemberscsv', 'admin'));
require_once('pieforms/pieform.php');
require_once(get_config('libroot') . 'group.php');
require_once(get_config('libroot') . 'institution.php');
safe_require('artefact', 'internal');
raise_memory_limit("512M");
// Turn on autodetecting of line endings, so mac newlines (\r) will work
ini_set('auto_detect_line_endings', 1);
$FORMAT = array();
$ALLOWEDKEYS = array(
'shortname',
'username',
'role',
);
$MANDATORYFIELDS = array(
'shortname',
'username',
'role',
);
$MEMBERS = array(); // Store the members
$GROUPS = array(); // Map gid to group shortnames
$form = array(
'name' => 'uploadcsv',
'elements' => array(
'institution' => get_institution_selector(),
'file' => array(
'type' => 'file',
'title' => get_string('csvfile', 'admin'),
'description' => get_string('groupmemberscsvfiledescription', 'admin'),
'rules' => array(
'required' => true
)
),
'submit' => array(
'type' => 'submit',
'value' => get_string('uploadgroupmemberscsv', 'admin')
)
)
);
/**
* The CSV file is parsed here so validation errors can be returned to the
* user. The data from a successful parsing is stored in the <var>$CVSDATA</var>
* array so it can be accessed by the submit function
*
* @param Pieform $form The form to validate
* @param array $values The values submitted
*/
function uploadcsv_validate(Pieform $form, $values) {
global $CSVDATA, $ALLOWEDKEYS, $MANDATORYFIELDS, $FORMAT, $USER, $UPDATES, $MEMBERS, $GROUPS;
// Don't even start attempting to parse if there are previous errors
if ($form->has_errors()) {
return;
}
if ($values['file']['size'] == 0) {
$form->set_error('file', $form->i18n('rule', 'required', 'required', array()));
return;
}
$institution = $values['institution'];
if (!$USER->can_edit_institution($institution)) {
$form->set_error('institution', get_string('notadminforinstitution', 'admin'));
return;
}
require_once('csvfile.php');
$csvgroups = new CsvFile($values['file']['tmp_name']);
$csvgroups->set('allowedkeys', $ALLOWEDKEYS);
$csvgroups->set('mandatoryfields', $MANDATORYFIELDS);
$csvdata = $csvgroups->get_data();
if (!empty($csvdata->errors['file'])) {
$form->set_error('file', $csvdata->errors['file']);
return;
}
$csverrors = new CSVErrors();
$formatkeylookup = array_flip($csvdata->format);
$shortnames = array();
$hadadmin = array();
foreach ($csvdata->data as $key => $line) {
// If headers exists, increment i = key + 2 for actual line number
$i = ($csvgroups->get('headerExists')) ? ($key + 2) : ($key + 1);
// Trim non-breaking spaces -- they get left in place by File_CSV
foreach ($line as &$field) {
$field = preg_replace('/^(\s|\xc2\xa0)*(.*?)(\s|\xc2\xa0)*$/', '$2', $field);
}
$shortname = $line[$formatkeylookup['shortname']];
$username = $line[$formatkeylookup['username']];
$role = $line[$formatkeylookup['role']];
$gid = get_field('group', 'id', 'shortname', $shortname, 'institution', $institution);
if (!$gid) {
$csverrors->add($i, get_string('uploadgroupmemberscsverrornosuchshortname', 'admin', $i, $shortname, $institution));
continue;
}
$uid = get_field_sql('SELECT id FROM {usr} WHERE LOWER(username) = ?', array(strtolower($username)));
if (!$uid) {
$csverrors->add($i, get_string('uploadgroupmemberscsverrornosuchusername', 'admin', $i, $username));
continue;
}
if ($institution != 'mahara' && !record_exists('usr_institution', 'usr', $uid, 'institution', $institution)) {
$csverrors->add($i, get_string('uploadgroupmemberscsverrorusernotininstitution', 'admin', $i, $username, $institution));
continue;
}
if (!in_array($role, array_keys(group_get_role_info($gid)))) {
$csverrors->add($i, get_string('uploadgroupmemberscsverrorinvalidrole', 'admin', $i, $role));
continue;
}
if (!isset($MEMBERS[$gid])) {
$MEMBERS[$gid] = array();
}
if (isset($MEMBERS[$gid][$uid])) {
$csverrors->add($i, get_string('uploadgroupmemberscsverrorduplicateusername', 'admin', $i, $shortname, $username));
continue;
}
$MEMBERS[$gid][$uid] = $role;
$GROUPS[$gid] = $shortname;
if ($role == 'admin') {
$hasadmin[$shortname] = 1;
}
}
foreach ($GROUPS as $shortname) {
if (!isset($hasadmin[$shortname])) {
$csverrors->add($i, get_string('uploadgroupmemberscsverrornoadminlisted', 'admin', $i, $shortname));
}
}
if ($errors = $csverrors->process()) {
$form->set_error('file', clean_html($errors));
return;
}
$FORMAT = $csvdata->format;
$CSVDATA = $csvdata->data;
}
/**
* Add the users to the system. Make sure that they have to change their
* password on next login also.
*/
function uploadcsv_submit(Pieform $form, $values) {
global $SESSION, $CSVDATA, $FORMAT, $UPDATES, $USER, $MEMBERS, $GROUPS;
$formatkeylookup = array_flip($FORMAT);
$institution = $values['institution'];
db_begin();
foreach ($MEMBERS as $gid => $members) {
$updates = group_update_members($gid, $members);
if (empty($updates)) {
unset($UPDATES[$GROUPS[$gid]]);
}
else {
$UPDATES[$GROUPS[$gid]] = $updates;
log_debug('updated group members ' . $GROUPS[$gid] . ' (' . implode(', ', array_keys((array)$updates)) . ')');
}
}
db_commit();
// TODO: Fix this to show correct info
$SESSION->add_ok_msg(get_string('csvfileprocessedsuccessfully', 'admin'));
if ($UPDATES) {
$updatemsg = smarty_core();
$updatemsg->assign('updates', $UPDATES);
$SESSION->add_info_msg($updatemsg->fetch('admin/groups/memberscsvupdatemessage.tpl'), false);
}
else {
$SESSION->add_ok_msg(get_string('numbergroupsupdated', 'admin', 0));
}
redirect('/admin/groups/uploadmemberscsv.php');
}
$uploadcsvpagedescription = get_string('uploadgroupmemberscsvpagedescription2', 'admin',
get_config('wwwroot') . 'admin/groups/uploadcsv.php',
get_string('uploadgroupcsv', 'admin'));
$form = pieform($form);
$smarty = smarty(array('adminuploadcsv'));
$smarty->assign('uploadcsvpagedescription', $uploadcsvpagedescription);
$smarty->assign('uploadcsvform', $form);
$smarty->assign('PAGEHEADING', TITLE);
$smarty->display('admin/groups/uploadcsv.tpl');
</
......@@ -100,6 +100,8 @@ $string['adminnotifications'] = 'Admin Notifications';
$string['adminnotificationsdescription'] = 'Overview of how administrators receive system notifications';
$string['uploadcsv'] = 'Add Users by CSV';
$string['uploadcsvdescription'] = 'Upload a CSV file containing new users';
$string['uploadgroupcsv'] = 'Add Groups by CSV';
$string['uploadgroupmemberscsv'] = 'Update Group Members by CSV';
$string['usersearch'] = 'User Search';
$string['usersearchdescription'] = 'Search all users and perform administrative actions on them';
$string['usersearchinstructions'] = 'You can search for users by clicking on the initials of their first and last names, or by entering a name in the search box. You can also enter an email address in the search box if you would like to search email addresses.';
......@@ -107,6 +109,8 @@ $string['usersearchinstructions'] = 'You can search for users by clicking on the
$string['administergroups'] = 'Administer Groups';
$string['administergroupsdescription'] = 'Appoint group administrators and delete groups';
$string['groupcategoriesdescription'] = 'Add and edit group categories';
$string['uploadgroupcsvdescription'] = 'Upload a CSV file containing new groups';
$string['uploadgroupmemberscsvdescription'] = 'Upload a CSV file containing members for groups';
$string['institutionmembersdescription'] = 'Associate users with institutions';
$string['institutionstaffdescription'] = 'Assign users Staff permissions';