Skip to content

Fixes #4525: Adds "gated" option to groups, separating membership policy... #233

Closed
wants to merge 9 commits into from
+312 −60
View
28 engine/classes/ElggGroup.php
@@ -267,16 +267,36 @@ public function getMembers($limit = 10, $offset = 0, $count = false) {
}
/**
- * Returns whether the current group is public membership or not.
+ * Returns whether the current group has open membership or not.
*
* @return bool
*/
public function isPublicMembership() {
- if ($this->membership == ACCESS_PUBLIC) {
- return true;
+ return ($this->membership == ACCESS_PUBLIC);
+ }
+
+ /**
+ * Returns whether the group content is restricted to members
+ *
+ * @return bool
+ */
+ public function isWalled() {
+ $walled = $this->walled;
+ if (! is_string($walled)) {
+ // fallback to 1.8 default behavior
+ $walled = $this->isPublicMembership() ? 'no' : 'yes';
+ $this->walled = $walled;
}
+ return ($walled === 'yes');
+ }
- return false;
+ /**
+ * Sets the walled status (whether group content is restricted to members)
+ *
+ * @param bool $walled
+ */
+ public function setWalled($walled) {
+ $this->walled = $walled ? 'yes' : 'no';
}
/**
View
76 engine/lib/group.php
@@ -247,45 +247,51 @@ function get_users_membership($user_guid) {
}
/**
- * Checks access to a group.
+ * Checks if user can access content within a group.
*
* @param boolean $forward If set to true (default), will forward the page;
* if set to false, will return true or false.
*
- * @return true|false If $forward is set to false.
+ * @param ElggGroup $group If not given, will use page owner
+ *
+ * @param ElggUser $user If not given, will use logged in user
+ *
+ * @return bool If $forward is set to false.
*/
-function group_gatekeeper($forward = true) {
+function group_gatekeeper($forward = true, ElggGroup $group = null, ElggUser $user = null) {
$allowed = true;
$url = '';
+ if (!$user) {
+ $user = elgg_get_logged_in_user_entity();
+ }
+ if (! $group) {
+ $group = elgg_get_page_owner_entity();
+ }
- if ($group = elgg_get_page_owner_entity()) {
- if ($group instanceof ElggGroup) {
- $url = $group->getURL();
- if (!$group->isPublicMembership()) {
- // closed group so must be member or an admin
-
- if (!elgg_is_logged_in()) {
- $allowed = false;
- if ($forward == true) {
- $_SESSION['last_forward_from'] = current_page_url();
- register_error(elgg_echo('loggedinrequired'));
- forward('', 'login');
- }
- } else if (!$group->isMember(elgg_get_logged_in_user_entity())) {
- $allowed = false;
- }
+ if ($group && ($group instanceof ElggGroup)) {
+ /* @var ElggGroup $group */
+ $url = $group->getURL();
- // Admin override
- if (elgg_is_admin_logged_in()) {
- $allowed = true;
- }
+ if ($group->isWalled()) {
+ if (!$user || !$group->isMember($user)) {
+ $allowed = false;
}
}
+ // Admin override
+ if (!$allowed && $user && $user->isAdmin()) {
+ $allowed = true;
+ }
}
- if ($forward && $allowed == false) {
- register_error(elgg_echo('membershiprequired'));
- forward($url, 'member');
+ if ($forward && !$allowed) {
+ if ($user) {
+ register_error(elgg_echo('membershiprequired'));
+ forward($url, 'member');
+ } else {
+ $_SESSION['last_forward_from'] = current_page_url();
+ register_error(elgg_echo('loggedinrequired'));
+ forward('', 'login');
+ }
}
return $allowed;
@@ -342,3 +348,21 @@ function remove_group_tool_option($name) {
}
}
}
+
+/**
+ * Runs unit tests for the group entities.
+ *
+ * @param string $hook
+ * @param string $type
+ * @param array $value
+ * @param array $params
+ *
+ * @return array
+ */
+function _elgg_groups_test($hook, $type, $value, $params) {
+ global $CONFIG;
+ $value[] = $CONFIG->path . 'engine/tests/objects/groups.php';
+ return $value;
+}
+
+elgg_register_plugin_hook_handler('unit_test', 'system', '_elgg_groups_test');
View
73 engine/tests/objects/groups.php
@@ -0,0 +1,73 @@
+<?php
+/**
+* Elgg Test ElggGroup
+*
+* @package Elgg
+* @subpackage Test
+*/
+class ElggCoreGroupTest extends ElggCoreUnitTest {
+ /**
+ * @var ElggGroup
+ */
+ protected $group;
+
+ /**
+ * @var ElggUser
+ */
+ protected $user;
+
+ /**
+ * Called before each test method.
+ */
+ public function setUp() {
+ $this->group = new ElggGroup();
+ $this->group->membership = ACCESS_PUBLIC;
+ $this->group->save();
+ $this->user = new ElggUser();
+ $this->user->username = 'test_user_' . rand();
+ $this->user->save();
+ }
+
+ public function testWalled() {
+ // if walled not set, open groups are not walled
+ $this->assertFalse($this->group->isWalled());
+
+ // after first check, walled is set
+ $this->assertEqual('no', $this->group->walled);
+
+ // if walled not set, closed groups are walled
+ $this->group->deleteMetadata('walled');
+ $this->group->membership = ACCESS_PRIVATE;
+ $this->assertTrue($this->group->isWalled());
+
+ $this->group->setWalled(false);
+ $this->assertFalse($this->group->isWalled());
+ $this->group->setWalled(true);
+ $this->assertTrue($this->group->isWalled());
+ }
+
+ public function testGroupGatekeeper() {
+ // unwalled groups are open
+ $this->group->setWalled(false);
+ $this->assertTrue(group_gatekeeper(false, $this->group, $this->user));
+
+ // walled group: non-members fail
+ $this->group->setWalled(true);
+ $this->assertFalse(group_gatekeeper(false, $this->group, $this->user));
+
+ // admins succeed
+ $this->assertTrue(group_gatekeeper(false, $this->group, elgg_get_logged_in_user_entity()));
+
+ // members succeed
+ $this->group->join($this->user);
+ $this->assertTrue(group_gatekeeper(false, $this->group, $this->user));
+ }
+
+ /**
+ * Called after each test method.
+ */
+ public function tearDown() {
+ $this->group->delete();
+ $this->user->delete();
+ }
+}
View
31 js/lib/ui.js
@@ -21,6 +21,8 @@ elgg.ui.init = function () {
$('.elgg-requires-confirmation').live('click', elgg.ui.requiresConfirmation);
$('.elgg-autofocus').focus();
+
+ elgg.ui.initAccessInputs();
};
/**
@@ -276,6 +278,35 @@ elgg.ui.initDatePicker = function() {
}
};
+/**
+ * Initialize input/access for dynamic display of walled notifications
+ *
+ * If a select.elgg-input-access is accompanied by a note (.elgg-input-access-walled),
+ * then hide the note when the select value is PRIVATE or group members.
+ *
+ * @return void
+ */
+elgg.ui.initAccessInputs = function () {
+ $('.elgg-input-access').each(function () {
+ function updateWalledNote() {
+ var val = $select.val();
+ if (val != acl && val != 0) {
+ // .show() failed in Chrome. Maybe a float/jQuery bug
+ $note.css('visibility', 'visible');
+ } else {
+ $note.css('visibility', 'hidden');
+ }
+ }
+ var $select = $(this),
+ acl = $select.data('group-acl'),
+ $note = $('.elgg-input-access-walled', this.parentNode);
+ if ($note) {
+ updateWalledNote();
+ $select.change(updateWalledNote);
+ }
+ });
+};
+
elgg.register_hook_handler('init', 'system', elgg.ui.init);
elgg.register_hook_handler('init', 'system', elgg.ui.initDatePicker);
elgg.register_hook_handler('getOptions', 'ui.popup', elgg.ui.loginHandler);
View
1 languages/en.php
@@ -268,6 +268,7 @@
'PUBLIC' => "Public",
'access:friends:label' => "Friends",
'access' => "Access",
+ 'access:wallednotice' => "Note: Due to group policy, this content will be accessible only by group members.",
'access:limited:label' => "Limited",
'access:help' => "The access level",
View
14 mod/groups/actions/groups/edit.php
@@ -74,14 +74,12 @@ function profile_array_decoder(&$v) {
}
}
-// Group membership - should these be treated with same constants as access permissions?
-switch (get_input('membership')) {
- case ACCESS_PUBLIC:
- $group->membership = ACCESS_PUBLIC;
- break;
- default:
- $group->membership = ACCESS_PRIVATE;
-}
+// Group membership
+$group->membership = (get_input('membership') == ACCESS_PUBLIC)
+ ? ACCESS_PUBLIC
+ : ACCESS_PRIVATE;
+
+$group->setWalled(get_input('walled') === 'yes');
if ($new_group_flag) {
$group->access_id = ACCESS_PUBLIC;
View
10 mod/groups/languages/en.php
@@ -32,6 +32,9 @@
'groups:members:title' => 'Members of %s',
'groups:members:more' => "View all members",
'groups:membership' => "Group membership permissions",
+ 'groups:walled' => "Accessibility of group content",
+ 'groups:walled:no' => "Unrestricted - Access depends on content-level settings",
+ 'groups:walled:yes' => "Members Only - Non-members can never access group content",
'groups:access' => "Access permissions",
'groups:owner' => "Owner",
'groups:widget:num_display' => 'Number of groups to display',
@@ -105,8 +108,11 @@
'groups:access:private' => 'Closed - Users must be invited',
'groups:access:public' => 'Open - Any user may join',
'groups:access:group' => 'Group members only',
- 'groups:closedgroup' => 'This group has a closed membership.',
- 'groups:closedgroup:request' => 'To ask to be added, click the "request membership" menu link.',
+ 'groups:closedgroup' => "This group's membership is closed.",
+ 'groups:closedgroup:request' => 'To ask to be added, click the "Request membership" menu link.',
+ 'groups:closedgroup:walled' => "This group's membership is closed and its content is accessible only by members.",
+ 'groups:opengroup:walled' => "This group's content is accessible only by members.",
+ 'groups:opengroup:walled:join' => 'To be a member, click the "Join group" menu link.',
'groups:visibility' => 'Who can see this group?',
/*
View
4 mod/groups/start.php
@@ -319,6 +319,7 @@ function groups_entity_menu_setup($hook, $type, $return, $params) {
return $return;
}
+ /* @var ElggGroup $entity */
$entity = $params['entity'];
$handler = elgg_extract('handler', $params, false);
if ($handler != 'groups') {
@@ -332,8 +333,7 @@ function groups_entity_menu_setup($hook, $type, $return, $params) {
}
// membership type
- $membership = $entity->membership;
- if ($membership == ACCESS_PUBLIC) {
+ if ($entity->isPublicMembership()) {
$mem = elgg_echo("groups:open");
} else {
$mem = elgg_echo("groups:closed");
View
52 mod/groups/views/default/forms/groups/edit.php
@@ -5,17 +5,25 @@
* @package ElggGroups
*/
-// new groups default to open membership
-if (isset($vars['entity'])) {
- $membership = $vars['entity']->membership;
- $access = $vars['entity']->access_id;
+/* @var ElggGroup $group */
+$group = elgg_extract('entity', $vars);
+
+// context needed for input/access view
+elgg_push_context('group-edit');
+
+// new groups default to open membership, unwalled
+if ($group) {
+ $membership = $group->membership;
+ $access = $group->access_id;
if ($access != ACCESS_PUBLIC && $access != ACCESS_LOGGED_IN) {
// group only - this is done to handle access not created when group is created
$access = ACCESS_PRIVATE;
}
+ $walled = $group->isWalled() ? 'yes' : 'no';
} else {
$membership = ACCESS_PUBLIC;
$access = ACCESS_PUBLIC;
+ $walled = 'no';
}
?>
@@ -27,7 +35,7 @@
<label><?php echo elgg_echo("groups:name"); ?></label><br />
<?php echo elgg_view("input/text", array(
'name' => 'name',
- 'value' => $vars['entity']->name,
+ 'value' => $group->name,
));
?>
</div>
@@ -45,7 +53,7 @@
echo "</label>$line_break";
echo elgg_view("input/{$valtype}", array(
'name' => $shortname,
- 'value' => $vars['entity']->$shortname,
+ 'value' => $group->$shortname,
));
echo '</div>';
}
@@ -66,11 +74,26 @@
?>
</label>
</div>
+
+<div>
+ <label>
+ <?php echo elgg_echo('groups:walled'); ?><br />
+ <?php echo elgg_view('input/dropdown', array(
+ 'name' => 'walled',
+ 'value' => $walled,
+ 'options_values' => array(
+ 'no' => elgg_echo('groups:walled:no'),
+ 'yes' => elgg_echo('groups:walled:yes'),
+ )
+ ));
+ ?>
+ </label>
+</div>
<?php
if (elgg_get_plugin_setting('hidden_groups', 'groups') == 'yes') {
- $this_owner = $vars['entity']->owner_guid;
+ $this_owner = $group->owner_guid;
if (!$this_owner) {
$this_owner = elgg_get_logged_in_user_guid();
}
@@ -106,7 +129,10 @@
} else {
$group_option_default_value = 'no';
}
- $value = $vars['entity']->$group_option_toggle_name ? $vars['entity']->$group_option_toggle_name : $group_option_default_value;
+ $value = $group->$group_option_toggle_name;
+ if (! $value) {
+ $value = $group_option_default_value;
+ }
?>
<div>
<label>
@@ -129,23 +155,25 @@
<div class="elgg-foot">
<?php
-if (isset($vars['entity'])) {
+if ($group) {
echo elgg_view('input/hidden', array(
'name' => 'group_guid',
- 'value' => $vars['entity']->getGUID(),
+ 'value' => $group->getGUID(),
));
}
echo elgg_view('input/submit', array('value' => elgg_echo('save')));
-if (isset($vars['entity'])) {
- $delete_url = 'action/groups/delete?guid=' . $vars['entity']->getGUID();
+if ($group) {
+ $delete_url = 'action/groups/delete?guid=' . $group->getGUID();
echo elgg_view('output/confirmlink', array(
'text' => elgg_echo('groups:delete'),
'href' => $delete_url,
'confirm' => elgg_echo('groups:deletewarning'),
'class' => 'elgg-button elgg-button-delete float-alt',
));
}
+
+elgg_pop_context();
?>
</div>
View
4 mod/groups/views/default/groups/profile/closed_membership.php
@@ -1,13 +1,13 @@
<?php
/**
- * Display message about closed membership
+ * Message for non-members on closed membership group profile pages.
*
* @package ElggGroups
*/
?>
<p class="mtm">
-<?php
+<?php
echo elgg_echo('groups:closedgroup');
if (elgg_is_logged_in()) {
echo ' ' . elgg_echo('groups:closedgroup:request');
View
14 mod/groups/views/default/groups/profile/layout.php
@@ -5,9 +5,21 @@
* @uses $vars['entity']
*/
+/* @var ElggGroup $group */
+$group = elgg_extract('entity', $vars);
+
echo elgg_view('groups/profile/summary', $vars);
+
if (group_gatekeeper(false)) {
+ if (!$group->isPublicMembership() && !$group->isMember()) {
+ echo elgg_view('groups/profile/closed_membership');
+ }
+
echo elgg_view('groups/profile/widgets', $vars);
} else {
- echo elgg_view('groups/profile/closed_membership');
+ if ($group->isPublicMembership()) {
+ echo elgg_view('groups/profile/walled_open');
+ } else {
+ echo elgg_view('groups/profile/walled_closed');
+ }
}
View
17 mod/groups/views/default/groups/profile/walled_closed.php
@@ -0,0 +1,17 @@
+<?php
+/**
+ * Message on walled, closed membership group profile pages when user
+ * cannot access group content.
+ *
+ * @package ElggGroups
+ */
+
+?>
+<p class="mtm">
+<?php
+echo elgg_echo('groups:closedgroup:walled');
+if (elgg_is_logged_in()) {
+ echo ' ' . elgg_echo('groups:closedgroup:request');
+}
+?>
+</p>
View
17 mod/groups/views/default/groups/profile/walled_open.php
@@ -0,0 +1,17 @@
+<?php
+/**
+ * Message on walled, open membership group profile pages when user
+ * cannot access group content.
+ *
+ * @package ElggGroups
+ */
+
+?>
+<p class="mtm">
+<?php
+echo elgg_echo('groups:opengroup:walled');
+if (elgg_is_logged_in()) {
+ echo ' ' . elgg_echo('groups:opengroup:walled:join');
+}
+?>
+</p>
View
5 views/default/css/elements/forms.php
@@ -63,6 +63,11 @@
.elgg-input-access {
margin:5px 0 0 0;
}
+.elgg-input-access-walled {
+ float:right;
+ width:50%;
+ margin:0;
+}
input[type="checkbox"],
input[type="radio"] {
View
26 views/default/input/access.php
@@ -22,9 +22,25 @@
'options_values' => get_write_access_array(),
);
-if (isset($vars['entity'])) {
- $defaults['value'] = $vars['entity']->access_id;
- unset($vars['entity']);
+/* @var ElggEntity $entity */
+$entity = elgg_extract('entity', $vars);
+unset($vars['entity']);
+
+// should we tell users that public/logged-in access levels will be ignored?
+$container = elgg_get_page_owner_entity();
+if ($container
+ && ($container instanceof ElggGroup)
+ && $container->isWalled()
+ && !elgg_in_context('group-edit')
+ && !($entity && $entity instanceof ElggGroup)
+) {
+ $show_walled_notice = true;
+} else {
+ $show_walled_notice = false;
+}
+
+if ($entity) {
+ $defaults['value'] = $entity->access_id;
}
$vars = array_merge($defaults, $vars);
@@ -34,5 +50,9 @@
}
if (is_array($vars['options_values']) && sizeof($vars['options_values']) > 0) {
+ if ($show_walled_notice) {
+ $vars['data-group-acl'] = $container->group_acl;
+ echo "<p class='elgg-input-access-walled'>" . elgg_echo('access:wallednotice') . "</p>";
+ }
echo elgg_view('input/dropdown', $vars);
}
Something went wrong with that request. Please try again.