Commit 5117c733 authored by Richard Mansfield's avatar Richard Mansfield
Browse files

Allow per-group page editing permissions



The roles which have edit permissions on group views is set for each
grouptype.  This changes the setting to be per-group.

Partially addresses bug #547362, bug #631189

Change-Id: I3f51f0ed44b7f479a094a2c5b2e2ee4807722e34
Signed-off-by: default avatarRichard Mansfield <richard.mansfield@catalyst.net.nz>
parent 3b58a258
......@@ -50,6 +50,7 @@ $ALLOWEDKEYS = array(
'roles',
'public',
'submitpages',
'editroles',
);
if ($USER->get('admin')) {
$ALLOWEDKEYS[] = 'usersautoadded';
......@@ -62,6 +63,7 @@ $MANDATORYFIELDS = array(
);
$UPDATES = array(); // During validation, remember which group already exist
$GROUPTYPES = group_get_grouptype_options();
$EDITROLES = group_get_editroles_options();
$form = array(
'name' => 'uploadcsv',
......@@ -98,7 +100,7 @@ $form = array(
* @param array $values The values submitted
*/
function uploadcsv_validate(Pieform $form, $values) {
global $CSVDATA, $ALLOWEDKEYS, $MANDATORYFIELDS, $GROUPTYPES, $FORMAT, $USER, $UPDATES;
global $CSVDATA, $ALLOWEDKEYS, $MANDATORYFIELDS, $GROUPTYPES, $FORMAT, $USER, $UPDATES, $EDITROLES;
// Don't even start attempting to parse if there are previous errors
if ($form->has_errors()) {
......@@ -157,6 +159,9 @@ function uploadcsv_validate(Pieform $form, $values) {
$controlled = isset($formatkeylookup['controlled']) && !empty($line[$formatkeylookup['controlled']]);
$request = isset($formatkeylookup['request']) && !empty($line[$formatkeylookup['request']]);
$submitpages = isset($formatkeylookup['submitpages']) && !empty($line[$formatkeylookup['submitpages']]);
if (isset($formatkeylookup['editroles'])) {
$editroles = $line[$formatkeylookup['editroles']];
}
if (!preg_match('/^[a-zA-Z0-9_.-]{2,255}$/', $shortname)) {
$csverrors->add($i, get_string('uploadgroupcsverrorinvalidshortname', 'admin', $i, $shortname));
......@@ -210,6 +215,10 @@ function uploadcsv_validate(Pieform $form, $values) {
$csverrors->add($i, get_string('uploadgroupcsverrorinvalidgrouptype', 'admin', $i, $grouptype));
}
if (isset($editroles) && !isset($EDITROLES[$editroles])) {
$csverrors->add($i, get_string('uploadgroupcsverrorinvalideditroles', 'admin', $i, $editroles));
}
if ($open && $controlled) {
$csverrors->add($i, get_string('uploadgroupcsverroropencontrolled', 'admin', $i));
}
......@@ -326,6 +335,14 @@ foreach (array_keys($GROUPTYPES) as $grouptype) {
}
$grouptypes .= "<div class=cl></div></ul>\n";
$editroles = "<ul class=fieldslist>\n";
foreach (array_keys($EDITROLES) as $editrole) {
$editroles .= '<li>' . hsc($editrole) . "</li>\n";
}
$editroles .= "<div class=cl></div></ul>\n";
$grouptypes .= get_string('uploadgroupcsveditrolesdescription', 'admin', get_help_icon('core', 'groups', 'editgroup', 'editroles'), $editroles);
$fields = "<ul class=fieldslist>\n";
foreach ($ALLOWEDKEYS as $type) {
$helplink = '';
......
......@@ -958,12 +958,14 @@ class User {
}
$group = $v->get('group');
if ($group) {
$editroles = $v->get('editingroles');
$this->reset_grouproles();
if ($v->get('type') == 'grouphomepage' && $this->grouproles[$group] != 'admin') {
return false;
}
return isset($this->grouproles[$group]) && in_array($this->grouproles[$group], $editroles);
if (!isset($this->grouproles[$group])) {
return false;
}
return group_role_can_edit_views($group, $this->grouproles[$group]);
}
return false;
}
......
......@@ -65,6 +65,7 @@ else {
'usersautoadded' => 0,
'viewnotify' => 1,
'submittableto' => 0,
'editroles' => 'all',
);
}
......@@ -176,12 +177,22 @@ if (!empty($forcegrouptype) || count($grouptypeoptions) < 2) {
);
}
$elements['pages'] = array(
'type' => 'html',
'title' => get_string('views'),
'value' => '',
);
$elements['editroles'] = array(
'type' => 'select',
'options' => group_get_editroles_options(),
'title' => get_string('editroles', 'group'),
'description' => get_string('editrolesdescription', 'group'),
'defaultvalue' => $group_data->editroles,
'help' => true,
);
if ($cancreatecontrolled) {
$elements['pages'] = array(
'type' => 'html',
'title' => get_string('views'),
'value' => '',
);
$elements['submittableto'] = array(
'type' => 'checkbox',
'title' => get_string('allowssubmissions', 'group'),
......@@ -296,6 +307,7 @@ function editgroup_submit(Pieform $form, $values) {
'public' => intval($values['public']),
'viewnotify' => intval($values['viewnotify']),
'submittableto' => intval($values['submittableto']),
'editroles' => $values['editroles'],
);
db_begin();
......
......@@ -34,7 +34,7 @@ define('MENUITEM', 'groups/share');
define('GROUP', param_integer('group'));
$group = group_current_group();
if (!group_user_can_edit_views($group->id)) {
if (!group_user_can_edit_views($group)) {
throw new AccessDeniedException();
}
......
......@@ -49,10 +49,6 @@ class GroupTypeCourse extends GroupType {
return array('member', 'tutor', 'admin');
}
public static function get_view_editing_roles() {
return array('tutor', 'admin');
}
public static function get_view_moderating_roles() {
return array('tutor', 'admin');
}
......@@ -64,12 +60,4 @@ class GroupTypeCourse extends GroupType {
public static function default_role() {
return 'member';
}
public static function default_artefact_rolepermissions() {
return array(
'member' => (object) array('view' => true, 'edit' => false, 'republish' => false),
'tutor' => (object) array('view' => true, 'edit' => true, 'republish' => true),
'admin' => (object) array('view' => true, 'edit' => true, 'republish' => true),
);
}
}
......@@ -61,13 +61,11 @@ abstract class GroupType {
if (!in_array('admin', $roles)) {
$roles[] = 'admin';
}
$editingroles = $this->get_view_editing_roles();
$assessingroles = $this->get_view_assessing_roles();
foreach ($roles as $r) {
insert_record('grouptype_roles', (object) array(
'grouptype' => $type,
'role' => $r,
'edit_views' => (int)in_array($r, $editingroles),
'see_submitted_views' => (int)in_array($r, $assessingroles),
));
}
......@@ -86,8 +84,6 @@ abstract class GroupType {
*/
public static abstract function get_roles();
public static abstract function get_view_editing_roles();
public static abstract function get_view_moderating_roles();
public static abstract function get_view_assessing_roles();
......@@ -95,6 +91,4 @@ abstract class GroupType {
public static function get_group_artefact_plugins() {
return array('file');
}
public static abstract function default_artefact_rolepermissions();
}
......@@ -47,10 +47,6 @@ class GroupTypeStandard extends GroupType {
return array('member', 'admin');
}
public static function get_view_editing_roles() {
return array('member', 'admin');
}
public static function get_view_moderating_roles() {
return array('admin');
}
......@@ -62,12 +58,4 @@ class GroupTypeStandard extends GroupType {
public static function default_role() {
return 'member';
}
public static function default_artefact_rolepermissions() {
return array(
'member' => (object) array('view' => true, 'edit' => true, 'republish' => true),
'admin' => (object) array('view' => true, 'edit' => true, 'republish' => true),
);
}
}
......@@ -464,6 +464,7 @@ $string['uploadcsvpagedescription2institutionaladmin'] = '<p>You may use this fa
$string['uploadgroupcsverrorgroupnamealreadyexists'] = 'Error on line %s of your file: The groupname "%s" already exists';
$string['uploadgroupcsverrorinvalidshortname'] = 'Error on line %s of your file: The shortname "%s" is invalid';
$string['uploadgroupcsverrorinvalidgrouptype'] = 'Error on line %s of your file: The grouptype "%s" is invalid';
$string['uploadgroupcsverrorinvalideditroles'] = 'Error on line %s of your file: The value for editroles "%s" is invalid';
$string['uploadgroupcsverrorshortnamealreadytaken'] = 'Error on line %s of your file: The shortname "%s" is already taken';
$string['uploadgroupcsverrorusernamesnotlastfield'] = 'The "usernames" field must be the last field in the header';
$string['uploadgroupcsverroropencontrolled'] = 'Line %s: Groups cannot have both open and controlled membership';
......@@ -482,6 +483,8 @@ $string['uploadgroupcsvpagedescription2'] = '<p>You may use this facility to upl
<p>Your CSV file may include any other fields as you require. The full list of fields is:</p>
%s';
$string['uploadgroupcsveditrolesdescription'] = '<p>The editroles field can have any of the following: %s</p>
%s';
$string['uploadgroupmemberscsverrorduplicateusername'] = 'Error on line %s of your file: The shortname "%s" and username "%s" have already been specified in this file';
$string['uploadgroupmemberscsverrorinvalidrole'] = 'Error on line %s of your file: The role "%s" is invalid for the specified group';
......
......@@ -73,6 +73,9 @@ $string['invalidshortname'] = 'Invalid group short name';
$string['shortnameformat'] = 'Group short names can be from 2 to 255 characters in length and contain only alphanumeric characters, ".", "-", and "_"';
$string['Created'] = 'Created';
$string['groupadmins'] = 'Group admins';
$string['editroles'] = 'Who can edit pages';
$string['editrolesdescription'] = 'Roles with permission to create and edit group pages.';
$string['allexceptmember'] = 'Everyone except ordinary members';
$string['Admin'] = 'Admin';
$string['grouptype'] = 'Group type';
$string['publiclyviewablegroup'] = 'Publicly Viewable Group?';
......
<h3>Editing Pages</h3>
<p>You can choose which roles will be allowed to create and edit pages owned by the group. If your group contains only Members and Admins, then "Group Admins" and "Everyone except Members" are identical.</p>
<p>These edit permissions will also be used by default for new group files. Permissions can be changed on individual files in the group
files area.</p>
......@@ -306,7 +306,6 @@
<FIELDS>
<FIELD NAME="grouptype" TYPE="char" LENGTH="20" NOTNULL="true" />
<FIELD NAME="role" TYPE="char" LENGTH="255" NOTNULL="true" />
<FIELD NAME="edit_views" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1" />
<FIELD NAME="see_submitted_views" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" />
</FIELDS>
<KEYS>
......@@ -344,6 +343,7 @@
<FIELD NAME="shortname" TYPE="char" LENGTH="255" NOTNULL="false" />
<FIELD NAME="request" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" />
<FIELD NAME="submittableto" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" />
<FIELD NAME="editroles" TYPE="char" LENGTH="20" NOTNULL="true" ENUM="true" ENUMVALUES="'all', 'notmember', 'admin'" DEFAULT="all" />
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" />
......
......@@ -2675,6 +2675,24 @@ function xmldb_core_upgrade($oldversion=0) {
// Any group can potentially take submissions, so make sure someone can assess them
set_field('grouptype_roles', 'see_submitted_views', 1, 'role', 'admin');
// Move group view editing permission from grouptype_roles to the group table
$table = new XMLDBTable('group');
$field = new XMLDBField('editroles');
$field->setAttributes(
XMLDB_TYPE_CHAR, 20, null, XMLDB_NOTNULL, null, XMLDB_ENUM,
array('all', 'notmember', 'admin'), 'all'
);
add_field($table, $field);
execute_sql("
UPDATE {group} SET editroles = 'notmember' WHERE grouptype IN (
SELECT grouptype FROM {grouptype_roles} WHERE role = 'member' AND edit_views = 0
)"
);
$table = new XMLDBTable('grouptype_roles');
$field = new XMLDBField('edit_views');
drop_field($table, $field);
}
return $status;
......
......@@ -131,11 +131,11 @@ function group_change_role($groupid, $userid, $role) {
/**
* Returns whether a user is allowed to edit views in a given group
*
* @param int $groupid The ID of the group
* @param mixed $group The ID of the group
* @param int $userid The ID of the user
* @returns boolean
*/
function group_user_can_edit_views($groupid, $userid=null) {
function group_user_can_edit_views($group, $userid=null) {
// root user can always do whatever it wants
$sysuser = get_record('usr', 'username', 'root');
if ($sysuser->id == $userid) {
......@@ -146,19 +146,36 @@ function group_user_can_edit_views($groupid, $userid=null) {
return false;
}
$groupid = group_param_groupid($groupid);
$groupid = is_numeric($group) ? group_param_groupid($group) : intval($group->id);
$userid = group_param_userid($userid);
return get_field_sql('
SELECT
r.edit_views
FROM
{group_member} m
INNER JOIN {group} g ON (m.group = g.id AND g.deleted = 0)
INNER JOIN {grouptype_roles} r ON (g.grouptype = r.grouptype AND m.role = r.role)
WHERE
m.group = ?
AND m.member = ?', array($groupid, $userid));
if ($role = group_user_access($groupid, $userid)) {
return group_role_can_edit_views($group, $role);
}
return false;
}
function group_role_can_edit_views($group, $role) {
if ($role == 'admin') {
return true;
}
if (is_numeric($group)) {
$editroles = get_field('group', 'editroles', 'id', $group);
}
else if (!isset($group->editroles)) {
$editroles = get_field('group', 'editroles', 'id', $group->id);
}
else {
$editroles = $group->editroles;
}
if ($role == 'member') {
return $editroles == 'all';
}
return $editroles != 'admin';
}
/**
......@@ -304,6 +321,13 @@ function group_create($data) {
$data['submittableto'] = $data['grouptype'] != 'standard';
}
if (!isset($data['editroles'])) {
$data['editroles'] = $data['grouptype'] == 'standard' ? 'all' : 'notmember';
}
else if (!in_array($data['editroles'], array_keys(group_get_editroles_options()))) {
throw new InvalidArgumentException("group_create: invalid option for page editroles setting");
}
db_begin();
$id = insert_record(
......@@ -323,6 +347,7 @@ function group_create($data) {
'shortname' => $data['shortname'],
'request' => isset($data['request']) ? intval($data['request']) : 0,
'submittableto' => intval($data['submittableto']),
'editroles' => $data['editroles'],
),
'id',
true
......@@ -429,7 +454,7 @@ function group_update($new, $create=false) {
unset($new->institution);
unset($new->shortname);
foreach (array('id', 'grouptype', 'public', 'request', 'submittableto') as $f) {
foreach (array('id', 'grouptype', 'public', 'request', 'submittableto', 'editroles') as $f) {
if (!isset($new->$f)) {
$new->$f = $old->$f;
}
......@@ -1100,7 +1125,7 @@ function group_get_admin_ids($groupid) {
* @return array
*/
function group_get_role_info($groupid) {
$roles = get_records_sql_assoc('SELECT "role", edit_views, see_submitted_views, gr.grouptype FROM {grouptype_roles} gr
$roles = get_records_sql_assoc('SELECT "role", see_submitted_views, gr.grouptype FROM {grouptype_roles} gr
INNER JOIN {group} g ON g.grouptype = gr.grouptype
WHERE g.id = ?', array($groupid));
foreach ($roles as $role) {
......@@ -1111,9 +1136,22 @@ function group_get_role_info($groupid) {
}
function group_get_default_artefact_permissions($groupid) {
$type = get_field('group', 'grouptype', 'id', $groupid);
safe_require('grouptype', $type);
return call_static_method('GroupType' . $type, 'default_artefact_rolepermissions');
$permissions = array();
$records = get_records_sql_array('
SELECT g.editroles, r.role
FROM {grouptype_roles} r, {group} g
WHERE g.grouptype = r.grouptype AND g.id = ?',
array($groupid)
);
foreach ($records as $r) {
$canedit = $r->role == 'admin' || $r->editroles == 'all' || ($r->role != 'member' && $r->editroles != 'admin');
$permissions[$r->role] = (object) array(
'view' => true,
'edit' => $canedit,
'republish' => $canedit,
);
}
return $permissions;
}
/**
......@@ -1339,6 +1377,14 @@ function group_get_grouptype_options($currentgrouptype=null) {
return $groupoptions;
}
function group_get_editroles_options() {
return array(
'all' => get_string('allgroupmembers', 'group'),
'notmember' => get_string('allexceptmember', 'group'),
'admin' => get_string('groupadmins', 'group'),
);
}
/**
* Returns a datastructure describing the tabs that appear on a group page
*
......@@ -1381,7 +1427,7 @@ function group_get_menu_tabs() {
'weight' => 50,
);
if (group_user_can_edit_views($group->id)) {
if (group_user_can_edit_views($group)) {
$menu['share'] = array(
'path' => 'groups/share',
'url' => 'group/shareviews.php?group='.$group->id,
......
......@@ -59,7 +59,6 @@ class View {
private $dirtycolumns; // for when we change stuff
private $tags;
private $categorydata;
private $editingroles;
private $moderatingroles;
private $template;
private $retainview;
......@@ -152,7 +151,6 @@ class View {
throw new ViewNotFoundException(get_string('viewnotfound', 'error', $id));
}
safe_require('grouptype', $group->grouptype);
$this->editingroles = call_static_method('GroupType' . ucfirst($group->grouptype), 'get_view_editing_roles');
$this->moderatingroles = call_static_method('GroupType' . ucfirst($group->grouptype), 'get_view_moderating_roles');
}
}
......@@ -2777,17 +2775,17 @@ class View {
AND (va.stopdate IS NULL OR va.stopdate > current_timestamp)";
}
else {
$from = '
$from = "
FROM {view} v
LEFT OUTER JOIN {collection_view} cv ON cv.view = v.id
LEFT OUTER JOIN {collection} c ON cv.collection = c.id
LEFT OUTER JOIN {group} gd ON v.group = gd.id
LEFT OUTER JOIN (
SELECT
gtr.edit_views, gm.group AS groupid
1 AS edit_views, gm.group AS groupid
FROM {group} g
INNER JOIN {group_member} gm ON (g.id = gm.group AND gm.member = ?)
INNER JOIN {grouptype_roles} gtr ON (g.grouptype = gtr.grouptype AND gtr.role = gm.role)
WHERE gm.role = 'admin' OR g.editroles = 'all' OR (g.editroles != 'admin' AND gm.role != 'member')
) AS vg ON (vg.groupid = v.group)
LEFT OUTER JOIN {view_access} va ON (
va.view = v.id
......@@ -2820,7 +2818,7 @@ class View {
INNER JOIN {usr_institution} ui ON (vai.institution = ui.institution AND ui.usr = ?)
) AS vaui ON (
vaui.view = v.id
)';
)";
$where .= "
AND (
v.owner = ?
......
......@@ -46,7 +46,7 @@ if (!is_logged_in() && !$group->public) {
define('TITLE', $group->name . ' - ' . get_string('groupviews', 'view'));
$can_edit = group_user_can_edit_views($group->id);
$can_edit = group_user_can_edit_views($group);
// If the user can edit group views, show a page similar to the my views
// page, otherwise just show a list of the views owned by this group that
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment