Commit e49290f6 authored by Richard Mansfield's avatar Richard Mansfield Committed by Gerrit Code Review

Merge changes Iaa907484,I78df2ba1,I25e32623,I56c113a9,Ie10caa60,I3ca81dbc,I24408eb2

* changes:
  Add groups with hidden membership (bug #845287)
  Move role filtering out of group_get_user_groups query
  Let group admins hide their group in searches (bug #845285)
  Add group view setting to prevent editing by non-admins (bug #631189)
  Get data in one place for group homepage & "copy a page" (bug #845282)
  Get all user info in group admins query (bug #845279)
  Fix string on upload group csv page (bug #610690)
parents 51445524 15c5fdff
......@@ -51,6 +51,8 @@ $ALLOWEDKEYS = array(
'public',
'submitpages',
'editroles',
'hidden',
'hidemembers',
);
if ($USER->get('admin')) {
$ALLOWEDKEYS[] = 'usersautoadded';
......
......@@ -959,10 +959,10 @@ class User {
$group = $v->get('group');
if ($group) {
$this->reset_grouproles();
if ($v->get('type') == 'grouphomepage' && $this->grouproles[$group] != 'admin') {
if (!isset($this->grouproles[$group])) {
return false;
}
if (!isset($this->grouproles[$group])) {
if (($v->get('type') == 'grouphomepage' || $v->get('locked')) && $this->grouproles[$group] != 'admin') {
return false;
}
require_once('group.php');
......
......@@ -28,7 +28,6 @@
defined('INTERNAL') || die();
require_once('group.php');
safe_require('artefact', 'file');
class PluginBlocktypeGroupInfo extends SystemBlocktype {
public static function get_title() {
......@@ -97,18 +96,7 @@ class PluginBlocktypeGroupInfo extends SystemBlocktype {
throw new AccessDeniedException();
}
// find the group administrators
$group->admins = get_column_sql("SELECT \"member\"
FROM {group_member}
WHERE \"group\" = ?
AND \"role\" = 'admin'", array($group->id));
$group->settingsdescription = group_display_settings($group);
if (get_config('allowgroupcategories')) {
$group->categorytitle = ($group->category) ? get_field('group_category', 'title', 'id', $group->category) : '';
}
$filecounts = ArtefactTypeFileBase::count_user_files(null, $group->id, null);
list($group, $filecounts) = group_get_groupinfo_data($group);
return array('group'=>$group, 'filecounts'=>$filecounts);
}
......
......@@ -51,6 +51,10 @@ class PluginBlocktypeGroupMembers extends SystemBlocktype {
return array('grouphomepage');
}
public static function hide_title_on_empty_content() {
return true;
}
public static function render_instance (BlockInstance $instance, $editing = false) {
global $USER;
......@@ -61,6 +65,16 @@ class PluginBlocktypeGroupMembers extends SystemBlocktype {
$numtoshow = isset($configdata['numtoshow']) ? $configdata['numtoshow'] : $rows * $columns;
$groupid = $instance->get_view()->get('group');
// If the group has hidden membership, display nothing to non-members
$usergroups = $USER->get('grouproles');
if (!isset($usergroups[$groupid])) {
$group = defined('GROUP') && $groupid == GROUP ? group_current_group() : get_record('group', 'id', $groupid);
if ($group->hidemembers) {
return '';
}
}
require_once('searchlib.php');
$groupmembers = get_group_user_search_results($groupid, '', 0, $numtoshow, '', $order);
......
......@@ -58,12 +58,12 @@ class PluginBlocktypeMyGroups extends SystemBlocktype {
$smarty = smarty_core();
require_once('group.php');
// Group stuff
$results = group_get_associated_groups($userid, 'member');
$usergroups = group_get_user_groups($userid);
foreach ($results['groups'] as $group) {
foreach ($usergroups as $group) {
$group->roledisplay = get_string($group->role, 'grouptype.'.$group->grouptype);
}
$smarty->assign('USERGROUPS',$results['groups']);
$smarty->assign('USERGROUPS', $usergroups);
return $smarty->fetch('blocktype:mygroups:mygroups.tpl');
}
......
......@@ -68,6 +68,8 @@ else {
'viewnotify' => 1,
'submittableto' => 0,
'editroles' => 'all',
'hidden' => 0,
'hidemembers' => 0,
);
}
......@@ -209,12 +211,6 @@ else {
);
}
$elements['general'] = array(
'type' => 'html',
'title' => get_string('general'),
'value' => '',
);
$publicallowed = get_config('createpublicgroups') == 'all' || (get_config('createpublicgroups') == 'admins' && $USER->get('admin'));
if (!$id && !param_variable('editgroup_submit', null)) {
......@@ -222,13 +218,54 @@ if (!$id && !param_variable('editgroup_submit', null)) {
$publicparam = param_integer('public', null);
}
$ignorepublic = !$publicallowed || (isset($publicparam) && $publicparam === 0);
if ($cancreatecontrolled || !$ignorepublic) {
$elements['visibility'] = array(
'type' => 'html',
'title' => get_string('Visibility'),
'value' => '',
);
}
$elements['public'] = array(
'type' => 'checkbox',
'title' => get_string('publiclyviewablegroup', 'group'),
'description' => get_string('publiclyviewablegroupdescription', 'group'),
'defaultvalue' => $group_data->public,
'help' => true,
'ignore' => !$publicallowed || (isset($publicparam) && $publicparam === 0),
'ignore' => $ignorepublic,
);
if ($cancreatecontrolled) {
$elements['hidden'] = array(
'type' => 'checkbox',
'title' => get_string('hiddengroup', 'group'),
'description' => get_string('hiddengroupdescription', 'group'),
'defaultvalue' => $group_data->hidden,
);
$elements['hidemembers'] = array(
'type' => 'checkbox',
'title' => get_string('hidemembers', 'group'),
'description' => get_string('hidemembersdescription', 'group'),
'defaultvalue' => $group_data->hidemembers,
);
}
else {
$form['elements']['hidden'] = array(
'type' => 'hidden',
'value' => $group_data->hidden,
);
$form['elements']['hidemembers'] = array(
'type' => 'hidden',
'value' => $group_data->hidemembers,
);
}
$elements['general'] = array(
'type' => 'html',
'title' => get_string('general'),
'value' => '',
);
if (get_config('allowgroupcategories')
......@@ -310,6 +347,8 @@ function editgroup_submit(Pieform $form, $values) {
'viewnotify' => intval($values['viewnotify']),
'submittableto' => intval($values['submittableto']),
'editroles' => $values['editroles'],
'hidden' => intval($values['hidden']),
'hidemembers' => intval($values['hidemembers']),
);
db_begin();
......
......@@ -29,19 +29,11 @@ define('INTERNAL', 1);
define('JSON', 1);
require(dirname(dirname(__FILE__)) . '/init.php');
require_once(get_config('libroot') . 'group.php');
safe_require('artefact', 'file');
$id = param_integer('id');
$group = get_record('group', 'id', $id);
$group->admins = get_column_sql("SELECT member
FROM {group_member}
WHERE \"group\" = ?
AND \"role\" = 'admin'", array($group->id));
$filecounts = ArtefactTypeFileBase::count_user_files(null, $group->id, null);
$group->settingsdescription = group_display_settings($group);
list($group, $filecounts) = group_get_groupinfo_data($group);
$smarty = smarty_core();
$smarty->assign('group', $group);
......
......@@ -45,6 +45,10 @@ define('TITLE', $group->name . ' - ' . get_string('Members', 'group'));
$role = group_user_access($group->id);
if ($group->hidemembers && !$role && !$USER->get('admin') && !$USER->get('staff')) {
throw new AccessDeniedException();
}
if (!empty($membershiptype) && $role != 'admin') {
throw new AccessDeniedException();
}
......
......@@ -43,10 +43,16 @@ if (!is_logged_in() && !$group->public) {
throw new AccessDeniedException(get_string('accessdenied', 'error'));
}
$role = group_user_access($group->id);
if ($group->hidemembers && !$role && !$USER->get('admin') && !$USER->get('staff')) {
json_reply('local', get_string('accessdenied', 'error'));
}
$membershiptype = param_variable('membershiptype', '');
if (!empty($membershiptype)) {
if (group_user_access($id) != 'admin') {
if ($role != 'admin') {
json_reply('local', get_string('accessdenied', 'error'));
}
}
......
......@@ -477,7 +477,7 @@ $string['uploadgroupcsvpagedescription2'] = '<p>You may use this facility to upl
<pre>shortname,displayname,roles</pre>
<p>This row must include the <tt>shortname</tt>, <tt>displayname</tt>, and <tt>grouptype</tt> fields</p>
<p>This row must include the <tt>shortname</tt>, <tt>displayname</tt>, and <tt>roles</tt> fields</p>
<p>The roles field can have any of the following: %s</p>
......
......@@ -96,6 +96,10 @@ $string['groupinvitesfrom'] = 'Invited to join:';
$string['requestedmembershipin'] = 'Requested membership in:';
$string['viewnotify'] = 'Shared page notifications';
$string['viewnotifydescription'] = 'If checked, a notification will be sent to every group member whenever a member shares one of their pages with the group. Enabling this setting in very large groups can produce a lot of notifications.';
$string['hiddengroup'] = 'Hidden group';
$string['hiddengroupdescription'] = 'Do not list this group on the Find Groups page.';
$string['hidemembers'] = 'Hide membership';
$string['hidemembersdescription'] = 'Hide the group\'s membership listing from non-members.';
$string['editgroupmembership'] = 'Edit group membership';
$string['editmembershipforuser'] = 'Edit membership for %s';
......
......@@ -932,6 +932,7 @@ $string['Total'] = 'Total';
$string['Visits'] = 'Visits';
$string['after'] = 'after';
$string['before'] = 'before';
$string['Visibility'] = 'Visibility';
// import related strings (maybe separated later)
$string['importedfrom'] = 'Imported from %s';
......
......@@ -47,6 +47,8 @@ $string['unrecogniseddateformat'] = 'Unrecognised date format';
$string['allowcommentsonview'] = 'If checked, users will be allowed to leave comments.';
$string['ownerformat'] = 'Name display format';
$string['ownerformatdescription'] = 'How do you want people who look at your page to see your name?';
$string['Locked'] = 'Locked';
$string['lockedgroupviewdesc'] = 'If you lock this page, only group admins will be able to edit it.';
$string['profileviewtitle'] = 'Profile page';
$string['dashboardviewtitle'] = 'Dashboard page';
$string['grouphomepageviewtitle'] = 'Group Homepage';
......
......@@ -344,6 +344,8 @@
<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" />
<FIELD NAME="hidden" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" />
<FIELD NAME="hidemembers" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" />
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" />
......@@ -671,6 +673,7 @@
<FIELD NAME="allowcomments" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1" />
<FIELD NAME="approvecomments" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" />
<FIELD NAME="accessconf" TYPE="char" LENGTH="40" NOTNULL="false"/>
<FIELD NAME="locked" TYPE="int" LENGTH="1" DEFAULT="0" NOTNULL="true" />
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" />
......
......@@ -2702,5 +2702,25 @@ function xmldb_core_upgrade($oldversion=0) {
change_field_type($table, $field, true, true);
}
if ($oldversion < 2011091200) {
// Locked group views (only editable by group admins)
$table = new XMLDBTable('view');
$field = new XMLDBField('locked');
$field->setAttributes(XMLDB_TYPE_INTEGER, 1, null, XMLDB_NOTNULL, null, null, null, 0);
add_field($table, $field);
set_field('view', 'locked', 1, 'type', 'grouphomepage');
// Setting to hide groups from the "Find Groups" listing
$table = new XMLDBTable('group');
$field = new XMLDBField('hidden');
$field->setAttributes(XMLDB_TYPE_INTEGER, 1, null, XMLDB_NOTNULL, null, null, null, 0);
add_field($table, $field);
// Setting to hide group members
$field = new XMLDBField('hidemembers');
$field->setAttributes(XMLDB_TYPE_INTEGER, 1, null, XMLDB_NOTNULL, null, null, null, 0);
add_field($table, $field);
}
return $status;
}
......@@ -157,6 +157,10 @@ function group_user_can_edit_views($group, $userid=null) {
function group_role_can_edit_views($group, $role) {
if (empty($role)) {
return false;
}
if ($role == 'admin') {
return true;
}
......@@ -274,6 +278,8 @@ function group_create($data) {
$data['ctime'] = db_format_timestamp($data['ctime']);
$data['public'] = (isset($data['public'])) ? intval($data['public']) : 0;
$data['hidden'] = (isset($data['hidden'])) ? intval($data['hidden']) : 0;
$data['hidemembers'] = (isset($data['hidemembers'])) ? intval($data['hidemembers']) : 0;
$data['usersautoadded'] = (isset($data['usersautoadded'])) ? intval($data['usersautoadded']) : 0;
$data['quota'] = get_config_plugin('artefact', 'file', 'defaultgroupquota');
......@@ -348,6 +354,8 @@ function group_create($data) {
'request' => isset($data['request']) ? intval($data['request']) : 0,
'submittableto' => intval($data['submittableto']),
'editroles' => $data['editroles'],
'hidden' => $data['hidden'],
'hidemembers' => $data['hidemembers'],
),
'id',
true
......@@ -454,7 +462,7 @@ function group_update($new, $create=false) {
unset($new->institution);
unset($new->shortname);
foreach (array('id', 'grouptype', 'public', 'request', 'submittableto', 'editroles') as $f) {
foreach (array('id', 'grouptype', 'public', 'request', 'submittableto', 'editroles', 'hidden', 'hidemembers') as $f) {
if (!isset($new->$f)) {
$new->$f = $old->$f;
}
......@@ -1187,6 +1195,29 @@ function group_get_default_artefact_permissions($groupid) {
return $permissions;
}
// Retrieve a list of group admins
function group_get_admins($groupids) {
$groupids = array_map('intval', $groupids);
if (empty($groupids)) {
return array();
}
$groupadmins = get_records_sql_array('
SELECT m.group, m.member, u.id, u.username, u.firstname, u.lastname, u.preferredname, u.email, u.profileicon
FROM {group_member} m JOIN {usr} u ON u.id = m.member
WHERE m.group IN (' . implode(',', db_array_to_ph($groupids)) . ")
AND m.role = 'admin'",
$groupids
);
if (!$groupadmins) {
$groupadmins = array();
}
return $groupadmins;
}
/**
* Sets up groups for display in mygroups.php and find.php
*
......@@ -1200,25 +1231,15 @@ function group_prepare_usergroups_for_display($groups, $returnto='mygroups') {
return;
}
// Retrieve a list of all the group admins, for placing in each $group object
$groupadmins = array();
$groupids = array_map(create_function('$a', 'return $a->id;'), $groups);
if ($groupids) {
$groupadmins = get_records_sql_array('SELECT "group", "member"
FROM {group_member}
WHERE "group" IN (' . implode(',', db_array_to_ph($groupids)) . ")
AND \"role\" = 'admin'", $groupids);
if (!$groupadmins) {
$groupadmins = array();
}
}
$groupadmins = group_get_admins($groupids);
$i = 0;
foreach ($groups as $group) {
$group->admins = array();
foreach ($groupadmins as $admin) {
if ($admin->group == $group->id) {
$group->admins[] = $admin->member;
$group->admins[] = $admin;
}
}
if ($group->membershiptype == 'member') {
......@@ -1431,6 +1452,9 @@ function group_get_menu_tabs() {
if (!$group) {
return null;
}
$role = group_user_access($group->id);
$menu = array(
'info' => array(
'path' => 'groups/info',
......@@ -1438,14 +1462,18 @@ function group_get_menu_tabs() {
'title' => get_string('About', 'group'),
'weight' => 20
),
'members' => array(
);
if ($role || !$group->hidemembers) {
$menu['members'] = array(
'path' => 'groups/members',
'url' => 'group/members.php?id='.$group->id,
'title' => get_string('Members', 'group'),
'weight' => 30
),
);
if ($group->public || group_user_access($group->id)) {
);
}
if ($group->public || $role) {
$menu['forums'] = array( // @todo: get this from a function in the interaction plugin (or better, make forums an artefact plugin)
'path' => 'groups/forums',
'url' => 'interaction/forum/index.php?group='.$group->id,
......@@ -1460,7 +1488,7 @@ function group_get_menu_tabs() {
'weight' => 50,
);
if (group_user_can_edit_views($group)) {
if (group_role_can_edit_views($group, $role)) {
$menu['share'] = array(
'path' => 'groups/share',
'url' => 'group/shareviews.php?group='.$group->id,
......@@ -1469,7 +1497,7 @@ function group_get_menu_tabs() {
);
}
if (group_user_access($group->id)) {
if ($role) {
safe_require('grouptype', $group->grouptype);
$artefactplugins = call_static_method('GroupType' . $group->grouptype, 'get_group_artefact_plugins');
if ($plugins = get_records_array('artefact_installed', 'active', 1)) {
......@@ -1716,33 +1744,47 @@ function group_get_associated_groups($userid, $filter='all', $limit=20, $offset=
function group_get_user_groups($userid=null, $roles=null) {
global $USER;
static $usergroups = array();
$loggedinid = $USER->get('id');
if (is_null($userid)) {
global $USER;
$userid = $USER->get('id');
$userid = $loggedinid;
}
if (empty($roles) && isset($usergroups[$userid])) {
return $usergroups[$userid];
if (!isset($usergroups[$userid])) {
$groups = get_records_sql_array("
SELECT g.id, g.name, gm.role, g.jointype, g.request, g.grouptype, gtr.see_submitted_views, g.category,
g.hidemembers, gm1.role AS loggedinrole
FROM {group} g
JOIN {group_member} gm ON gm.group = g.id
JOIN {grouptype_roles} gtr ON g.grouptype = gtr.grouptype AND gm.role = gtr.role
LEFT OUTER JOIN {group_member} gm1 ON gm1.group = gm.group AND gm1.member = ?
WHERE gm.member = ?
AND g.deleted = 0
ORDER BY g.name, gm.role = 'admin' DESC, gm.role, g.id",
array($loggedinid, $userid)
);
$usergroups[$userid] = $groups ? $groups : array();
}
if (!$groups = get_records_sql_array(
"SELECT g.id, g.name, gm.role, g.jointype, g.request, g.grouptype, gtr.see_submitted_views, g.category
FROM {group} g
JOIN {group_member} gm ON (gm.group = g.id)
JOIN {grouptype_roles} gtr ON (g.grouptype = gtr.grouptype AND gm.role = gtr.role)
WHERE gm.member = ?
AND g.deleted = 0 " . (is_array($roles) ? (' AND gm.role IN (' . join(',', array_map('db_quote', $roles)) . ')') : '') . "
ORDER BY g.name, gm.role = 'admin' DESC, gm.role, g.id", array($userid))) {
$groups = array();
if (empty($roles) && $userid == $loggedinid) {
return $usergroups[$userid];
}
if (empty($roles)) {
$usergroups[$userid] = $groups;
$filtered = array();
foreach ($usergroups[$userid] as $g) {
$goodrole = empty($roles) || in_array($g->role, $roles);
$visible = !$g->hidemembers || $g->loggedinrole || $USER->get('admin') || $USER->get('staff');
if ($goodrole && $visible) {
$filtered[] = $g;
}
}
return $groups;
return $filtered;
}
function group_get_user_admintutor_groups() {
......@@ -1812,6 +1854,20 @@ function group_display_settings($group) {
return join(', ', $settings);
}
function group_get_groupinfo_data($group) {
safe_require('artefact', 'file');
$group->admins = group_get_admins(array($group->id));
$group->settingsdescription = group_display_settings($group);
if (get_config('allowgroupcategories')) {
$group->categorytitle = ($group->category) ? get_field('group_category', 'title', 'id', $group->category) : '';
}
$filecounts = ArtefactTypeFileBase::count_user_files(null, $group->id, null);
return array($group, $filecounts);
}
/**
* Return the view object for this group's homepage view
*
......
......@@ -28,7 +28,7 @@
defined('INTERNAL') || die();
$config = new StdClass;
$config->version = 2011090900;
$config->version = 2011091200;
$config->release = '1.5.0dev';
$config->minupgradefrom = 2008040200;
$config->minupgraderelease = '1.0.0 (release tag 1.0.0_RELEASE)';
......
......@@ -70,6 +70,7 @@ class View {
private $approvecomments;
private $collection;
private $accessconf;
private $locked;
/**
* Valid view layouts. These are read at install time and inserted into
......@@ -2450,7 +2451,7 @@ class View {
$userid = (!$groupid && !$institution) ? $USER->get('id') : null;
$select = '
SELECT v.id,v.title,v.description,v.type,v.mtime';
SELECT v.id,v.title,v.description,v.type,v.mtime,v.locked';
$from = '
FROM {view} v';
$where = '
......@@ -2500,6 +2501,7 @@ class View {
$data[$i]['type'] = $viewdata[$i]->type;
$data[$i]['title'] = $viewdata[$i]->title;
$data[$i]['mtime'] = $viewdata[$i]->mtime;
$data[$i]['locked'] = $viewdata[$i]->locked;
$data[$i]['removable'] = self::can_remove_viewtype($viewdata[$i]->type);
$data[$i]['description'] = $viewdata[$i]->description;
if (!empty($viewdata[$i]->submitgroupid)) {
......@@ -3719,6 +3721,7 @@ class View {
* @return array, array
*/
function get_views_and_collections($owner=null, $group=null, $institution=null, $matchconfig=null, $includeprofile=true) {
$excludelocked = $group && group_user_access($group) != 'admin';
$ownersql = self::owner_sql((object) array('owner' => $owner, 'group' => $group, 'institution' => $institution));
$sql = "
SELECT v.id AS vid, v.type AS vtype, v.title AS vname, v.accessconf,
......@@ -3729,6 +3732,7 @@ class View {
LEFT JOIN {collection} c ON cv.collection = c.id
WHERE v.$ownersql AND v.type IN ('portfolio'";
$sql .= $includeprofile ? ", 'profile') " : ') ';
$sql .= $excludelocked ? 'AND v.locked != 1 ' : '';
$sql .= 'ORDER BY c.name, v.title';
$records = get_records_sql_array($sql, array());
......
......@@ -300,7 +300,16 @@ class PluginSearchInternal extends PluginSearch {
private static function prepare_search_user_options($options) {
global $USER;
if (isset($options['group'])) {
$options['group'] = intval($options['group']);
// This option should only be used by group admins, so just ensure that the caller is
// using it correctly.
$roles = $USER->get('grouproles');
if ($USER->get('admin') || $USER->get('staff')
|| (isset($roles[$options['group']]) && $roles[$options['group']] == 'admin')) {
$options['group'] = intval($options['group']);
}
else {
unset($options['group']);
}
}
if (isset($options['includeadmins'])) {
$options['includeadmins'] = (bool)$options['includeadmins'];
......@@ -682,12 +691,21 @@ class PluginSearchInternal extends PluginSearch {
$grouproles = '-1';
}
$canseehidden = $USER->get('admin') || $USER->get('staff');
if ($type == 'member') {
$sql .= 'AND id IN (' . $grouproles . ')';