Unverified Commit c5764901 authored by Jesús Espino's avatar Jesús Espino Committed by GitHub
Browse files

Added join/list public/private teams permissions (#779)

* Added join/list public/private teams permissions

* Joining team in a different way depending on the server version

* Addressing PR review comments

* Addressing PR review comments
parent b0610c76
......@@ -15,6 +15,7 @@ import {loadRolesIfNeeded} from './roles';
import type {GetStateFunc, DispatchFunc, ActionFunc, ActionResult} from 'types/actions';
import type {Team} from 'types/teams';
import {isCompatibleWithJoinViewTeamPermissions} from 'selectors/entities/general';
async function getProfilesAndStatusesForMembers(userIds, dispatch, getState) {
const {currentUserId, profiles, statuses} = getState().entities.users;
......@@ -509,8 +510,14 @@ export function joinTeam(inviteId: string, teamId: string): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
dispatch({type: TeamTypes.JOIN_TEAM_REQUEST, data: null}, getState);
const state = getState();
try {
await Client4.joinTeam(inviteId);
if (isCompatibleWithJoinViewTeamPermissions(state)) {
const currentUserId = state.entities.users.currentUserId;
await Client4.addToTeam(teamId, currentUserId);
} else {
await Client4.joinTeam(inviteId);
}
} catch (error) {
forceLogoutIfNecessary(error, dispatch, getState);
dispatch(batchActions([
......
......@@ -21,6 +21,10 @@ export default {
CREATE_GROUP_CHANNEL: 'create_group_channel',
MANAGE_PUBLIC_CHANNEL_PROPERTIES: 'manage_public_channel_properties',
MANAGE_PRIVATE_CHANNEL_PROPERTIES: 'manage_private_channel_properties',
LIST_PUBLIC_TEAMS: 'list_public_teams',
JOIN_PUBLIC_TEAMS: 'join_public_teams',
LIST_PRIVATE_TEAMS: 'list_private_teams',
JOIN_PRIVATE_TEAMS: 'join_private_teams',
LIST_TEAM_CHANNELS: 'list_team_channels',
JOIN_PUBLIC_CHANNELS: 'join_public_channels',
DELETE_PUBLIC_CHANNEL: 'delete_public_channel',
......
......@@ -25,6 +25,13 @@ export function getCurrentUrl(state: GlobalState): string {
return state.entities.general.credentials.url;
}
export function isCompatibleWithJoinViewTeamPermissions(state: GlobalState): boolean {
const version = state.entities.general.serverVersion;
return isMinimumServerVersion(version, 5, 10, 0) ||
(version.indexOf('dev') !== -1 && isMinimumServerVersion(version, 5, 8, 0)) ||
(version.match(/^5.8.\d.\d\d\d\d.*$/) !== null && isMinimumServerVersion(version, 5, 8, 0));
}
export function hasNewPermissions(state: GlobalState): boolean {
const version = state.entities.general.serverVersion;
......
......@@ -3,12 +3,14 @@
import {createSelector} from 'reselect';
import {General} from 'constants';
import {Permissions} from 'constants';
import {getConfig, getCurrentUrl} from 'selectors/entities/general';
import {getConfig, getCurrentUrl, isCompatibleWithJoinViewTeamPermissions} from 'selectors/entities/general';
import {haveISystemPermission} from 'selectors/entities/roles';
import {createIdsSelector} from 'utils/helpers';
import {isTeamAdmin} from 'utils/user_utils';
import {sortTeamsWithLocale} from 'utils/team_utils';
export function getCurrentTeamId(state) {
return state.entities.teams.currentTeamId;
......@@ -137,14 +139,63 @@ export function getTeamMember(state, teamId, userId) {
return null;
}
export const getListableTeamIds = createIdsSelector(
getTeams,
getTeamMemberships,
(state) => haveISystemPermission(state, {permission: Permissions.LIST_PUBLIC_TEAMS}),
(state) => haveISystemPermission(state, {permission: Permissions.LIST_PRIVATE_TEAMS}),
isCompatibleWithJoinViewTeamPermissions,
(teams, myMembers, canListPublicTeams, canListPrivateTeams, compatibleWithJoinViewTeamPermissions) => {
return Object.keys(teams).filter((id) => {
const team = teams[id];
const member = myMembers[id];
let canList = team.allow_open_invite;
if (compatibleWithJoinViewTeamPermissions) {
canList = (canListPrivateTeams && !team.allow_open_invite) || (canListPublicTeams && team.allow_open_invite);
}
return team.delete_at === 0 && canList && !member;
});
}
);
export const getListableTeams = createSelector(
getTeams,
getListableTeamIds,
(teams, listableTeamIds) => {
return listableTeamIds.map((id) => teams[id]);
}
);
export const getSortedListableTeams = createSelector(
getTeams,
getListableTeamIds,
(state, locale) => locale,
(teams, listableTeamIds, locale) => {
const listableTeams = {};
for (const id of listableTeamIds) {
listableTeams[id] = teams[id];
}
return Object.values(listableTeams).sort(sortTeamsWithLocale(locale));
}
);
export const getJoinableTeamIds = createIdsSelector(
getTeams,
getTeamMemberships,
(teams, myMembers) => {
(state) => haveISystemPermission(state, {permission: Permissions.JOIN_PUBLIC_TEAMS}),
(state) => haveISystemPermission(state, {permission: Permissions.JOIN_PRIVATE_TEAMS}),
isCompatibleWithJoinViewTeamPermissions,
(teams, myMembers, canJoinPublicTeams, canJoinPrivateTeams, compatibleWithJoinViewTeamPermissions) => {
return Object.keys(teams).filter((id) => {
const team = teams[id];
const member = myMembers[id];
return team.delete_at === 0 && team.allow_open_invite && !member;
let canJoin = team.allow_open_invite;
if (compatibleWithJoinViewTeamPermissions) {
canJoin = (canJoinPrivateTeams && !team.allow_open_invite) || (canJoinPublicTeams && team.allow_open_invite);
}
return team.delete_at === 0 && canJoin && !member;
});
}
);
......@@ -162,21 +213,13 @@ export const getSortedJoinableTeams = createSelector(
getJoinableTeamIds,
(state, locale) => locale,
(teams, joinableTeamIds, locale) => {
const openTeams = {};
const joinableTeams = {};
for (const id of joinableTeamIds) {
openTeams[id] = teams[id];
joinableTeams[id] = teams[id];
}
function sortTeams(a, b) {
if (a.display_name !== b.display_name) {
return a.display_name.toLowerCase().localeCompare(b.display_name.toLowerCase(), locale || General.DEFAULT_LOCALE, {numeric: true});
}
return a.name.toLowerCase().localeCompare(b.name.toLowerCase(), locale || General.DEFAULT_LOCALE, {numeric: true});
}
return Object.values(openTeams).sort(sortTeams);
return Object.values(joinableTeams).sort(sortTeamsWithLocale(locale));
}
);
......@@ -185,13 +228,7 @@ export const getMySortedTeamIds = createIdsSelector(
getTeamMemberships,
(state, locale) => locale,
(teams, myMembers, locale) => {
return Object.values(teams).filter((t) => myMembers[t.id]).sort((a, b) => {
if (a.display_name !== b.display_name) {
return a.display_name.toLowerCase().localeCompare(b.display_name.toLowerCase(), locale, {numeric: true});
}
return a.name.toLowerCase().localeCompare(b.name.toLowerCase(), locale, {numeric: true});
}).map((t) => t.id);
return Object.values(teams).filter((t) => myMembers[t.id]).sort(sortTeamsWithLocale(locale)).map((t) => t.id);
}
);
......
......@@ -9,6 +9,7 @@ import * as Selectors from 'selectors/entities/teams';
import {General} from 'constants';
describe('Selectors.Teams', () => {
TestHelper.initMockEntities();
const team1 = TestHelper.fakeTeamWithId();
const team2 = TestHelper.fakeTeamWithId();
const team3 = TestHelper.fakeTeamWithId();
......@@ -60,6 +61,12 @@ describe('Selectors.Teams', () => {
myMembers,
membersInTeam,
},
roles: {
roles: TestHelper.basicRoles,
},
general: {
serverVersion: '5.8.0',
},
},
});
......@@ -93,6 +100,232 @@ describe('Selectors.Teams', () => {
assert.strictEqual(joinableTeams[1], openTeams[1]);
});
it('getListableTeams', () => {
const openTeams = [team3, team4];
const listableTeams = Selectors.getListableTeams(testState);
assert.strictEqual(listableTeams[0], openTeams[0]);
assert.strictEqual(listableTeams[1], openTeams[1]);
});
it('getListedJoinableTeams', () => {
const openTeams = [team4, team3];
const joinableTeams = Selectors.getSortedListableTeams(testState);
assert.strictEqual(joinableTeams[0], openTeams[0]);
assert.strictEqual(joinableTeams[1], openTeams[1]);
});
it('getJoinableTeamsUsingPermissions', () => {
const privateTeams = [team1, team2];
const openTeams = [team3, team4];
let modifiedState = {
entities: {
...testState.entities,
teams: {
...testState.entities.teams,
myMembers: {},
},
roles: {
roles: {
system_user: {
...testState.entities.roles.roles.system_user,
permissions: ['join_private_teams'],
},
},
},
general: {
serverVersion: '5.10.0',
},
},
};
let joinableTeams = Selectors.getJoinableTeams(modifiedState);
assert.strictEqual(joinableTeams[0], privateTeams[0]);
assert.strictEqual(joinableTeams[1], privateTeams[1]);
modifiedState = {
entities: {
...testState.entities,
teams: {
...testState.entities.teams,
myMembers: {},
},
roles: {
roles: {
system_user: {
permissions: ['join_public_teams'],
},
},
},
general: {
serverVersion: '5.10.0',
},
},
};
joinableTeams = Selectors.getJoinableTeams(modifiedState);
assert.strictEqual(joinableTeams[0], openTeams[0]);
assert.strictEqual(joinableTeams[1], openTeams[1]);
modifiedState = {
entities: {
...testState.entities,
teams: {
...testState.entities.teams,
myMembers: {},
},
roles: {
roles: {
system_user: {
permissions: ['join_public_teams', 'join_private_teams'],
},
},
},
general: {
serverVersion: '5.10.0',
},
},
};
joinableTeams = Selectors.getJoinableTeams(modifiedState);
assert.strictEqual(joinableTeams[0], privateTeams[0]);
assert.strictEqual(joinableTeams[1], privateTeams[1]);
assert.strictEqual(joinableTeams[2], openTeams[0]);
assert.strictEqual(joinableTeams[3], openTeams[1]);
});
it('getSortedJoinableTeamsUsingPermissions', () => {
const privateTeams = [team2, team1];
const openTeams = [team4, team3];
const modifiedState = {
entities: {
...testState.entities,
teams: {
...testState.entities.teams,
myMembers: {},
},
roles: {
roles: {
system_user: {
...testState.entities.roles.roles.system_user,
permissions: ['join_public_teams', 'join_private_teams'],
},
},
},
general: {
serverVersion: '5.10.0',
},
},
};
const joinableTeams = Selectors.getSortedJoinableTeams(modifiedState);
assert.strictEqual(joinableTeams[0], openTeams[0]);
assert.strictEqual(joinableTeams[1], privateTeams[0]);
assert.strictEqual(joinableTeams[2], privateTeams[1]);
assert.strictEqual(joinableTeams[3], openTeams[1]);
});
it('getListableTeamsUsingPermissions', () => {
const privateTeams = [team1, team2];
const openTeams = [team3, team4];
let modifiedState = {
entities: {
...testState.entities,
teams: {
...testState.entities.teams,
myMembers: {},
},
roles: {
roles: {
system_user: {
...testState.entities.roles.roles.system_user,
permissions: ['list_private_teams'],
},
},
},
general: {
serverVersion: '5.10.0',
},
},
};
let listableTeams = Selectors.getListableTeams(modifiedState);
assert.strictEqual(listableTeams[0], privateTeams[0]);
assert.strictEqual(listableTeams[1], privateTeams[1]);
modifiedState = {
entities: {
...testState.entities,
teams: {
...testState.entities.teams,
myMembers: {},
},
roles: {
roles: {
system_user: {
permissions: ['list_public_teams'],
},
},
},
general: {
serverVersion: '5.10.0',
},
},
};
listableTeams = Selectors.getListableTeams(modifiedState);
assert.strictEqual(listableTeams[0], openTeams[0]);
assert.strictEqual(listableTeams[1], openTeams[1]);
modifiedState = {
entities: {
...testState.entities,
teams: {
...testState.entities.teams,
myMembers: {},
},
roles: {
roles: {
system_user: {
permissions: ['list_public_teams', 'list_private_teams'],
},
},
},
general: {
serverVersion: '5.10.0',
},
},
};
listableTeams = Selectors.getListableTeams(modifiedState);
assert.strictEqual(listableTeams[0], privateTeams[0]);
assert.strictEqual(listableTeams[1], privateTeams[1]);
assert.strictEqual(listableTeams[2], openTeams[0]);
assert.strictEqual(listableTeams[3], openTeams[1]);
});
it('getSortedListableTeamsUsingPermissions', () => {
const privateTeams = [team2, team1];
const openTeams = [team4, team3];
const modifiedState = {
entities: {
...testState.entities,
teams: {
...testState.entities.teams,
myMembers: {},
},
roles: {
roles: {
system_user: {
...testState.entities.roles.roles.system_user,
permissions: ['list_public_teams', 'list_private_teams'],
},
},
},
general: {
serverVersion: '5.10.0',
},
},
};
const listableTeams = Selectors.getSortedListableTeams(modifiedState);
assert.strictEqual(listableTeams[0], openTeams[0]);
assert.strictEqual(listableTeams[1], privateTeams[0]);
assert.strictEqual(listableTeams[2], privateTeams[1]);
assert.strictEqual(listableTeams[3], openTeams[1]);
});
it('isCurrentUserCurrentTeamAdmin', () => {
assert.deepEqual(Selectors.isCurrentUserCurrentTeamAdmin(testState), false);
});
......
......@@ -4,6 +4,7 @@
import type {Team} from 'types/teams';
import type {IDMappedObjects} from 'types/utilities';
import {General} from 'constants';
export function teamListToMap(teamList: Array<Team>): IDMappedObjects<Team> {
const teams = {};
......@@ -12,3 +13,13 @@ export function teamListToMap(teamList: Array<Team>): IDMappedObjects<Team> {
}
return teams;
}
export function sortTeamsWithLocale(locale: string): (a: Team, b: Team) => number {
return (a: Team, b: Team): number => {
if (a.display_name !== b.display_name) {
return a.display_name.toLowerCase().localeCompare(b.display_name.toLowerCase(), locale || General.DEFAULT_LOCALE, {numeric: true});
}
return a.name.toLowerCase().localeCompare(b.name.toLowerCase(), locale || General.DEFAULT_LOCALE, {numeric: true});
};
}
......@@ -65,6 +65,7 @@ class TestHelper {
last_name: this.generateId(),
create_at: Date.now(),
delete_at: 0,
roles: 'system_user',
};
};
......
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