Remove all checks for minimum versions previous to 5.31 (#5572)

* Remove all checks for minimum versions previous to 5.31

* Fix tests

* Fix bad merge

* Address feedback

* Patch flaky test
This commit is contained in:
Daniel Espino García 2021-09-06 12:57:24 +02:00 committed by GitHub
parent 45108bde02
commit 69a65067c3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 186 additions and 651 deletions

View file

@ -33,7 +33,6 @@ import {getTeamByName as selectTeamByName, getCurrentTeam, getTeamMemberships} f
import {getCurrentUserId} from '@mm-redux/selectors/entities/users';
import {getChannelByName as selectChannelByName, getChannelsIdForTeam} from '@mm-redux/utils/channel_utils';
import EventEmitter from '@mm-redux/utils/event_emitter';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {getChannelReachable} from '@selectors/channel';
import {getViewingGlobalThreads} from '@selectors/threads';
import telemetry, {PERF_MARKERS} from '@telemetry';
@ -637,11 +636,10 @@ function loadGroupData(isReconnect = false) {
const actions = [];
const team = getCurrentTeam(state);
const currentUserId = getCurrentUserId(state);
const serverVersion = state.entities.general.serverVersion;
const license = getLicense(state);
const hasLicense = license?.IsLicensed === 'true' && license?.LDAPGroups === 'true';
if (hasLicense && team && isMinimumServerVersion(serverVersion, 5, 24)) {
if (hasLicense && team) {
for (let i = 0; i <= MAX_RETRIES; i++) {
try {
if (team.group_constrained) {

View file

@ -3,11 +3,9 @@
import moment from 'moment-timezone';
import {Client4} from '@client/rest';
import PushNotifications from '@init/push_notifications';
import {getSessions} from '@mm-redux/actions/users';
import {getConfig} from '@mm-redux/selectors/entities/general';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
const sortByNewest = (a, b) => {
if (a.create_at > b.create_at) {
@ -23,7 +21,7 @@ export function scheduleExpiredNotification(intl) {
const {currentUserId} = state.entities.users;
const config = getConfig(state);
if (isMinimumServerVersion(Client4.serverVersion, 5, 24) && config.ExtendSessionLengthWithActivity === 'true') {
if (config.ExtendSessionLengthWithActivity === 'true') {
PushNotifications.cancelAllLocalNotifications();
return;
}

View file

@ -22,7 +22,6 @@ import {GlobalState} from '@mm-redux/types/store';
import {TeamMembership} from '@mm-redux/types/teams';
import {WebSocketMessage} from '@mm-redux/types/websocket';
import EventEmitter from '@mm-redux/utils/event_emitter';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {removeUserFromList} from '@mm-redux/utils/user_utils';
import {getChannelSinceValue} from '@utils/channels';
import websocketClient from '@websocket';
@ -99,7 +98,7 @@ export function doFirstConnect(now: number) {
const {lastDisconnectAt} = state.websocket;
const actions: Array<GenericAction> = [wsConnected(now)];
if (isMinimumServerVersion(Client4.getServerVersion(), 5, 14) && lastDisconnectAt) {
if (lastDisconnectAt) {
const currentUserId = getCurrentUserId(state);
const users = getUsers(state);
const userIds = Object.keys(users);
@ -220,7 +219,7 @@ export function doReconnect(now: number) {
});
}
if (isMinimumServerVersion(Client4.getServerVersion(), 5, 14) && lastDisconnectAt) {
if (lastDisconnectAt) {
const userIds = Object.keys(users);
const userUpdates = await Client4.getProfilesByIds(userIds, {since: lastDisconnectAt});

View file

@ -62,7 +62,7 @@ describe('Websocket Post Events', () => {
// Mock that post does not exist and check it is added
PostSelectors.getPost.mockReturnValueOnce(false);
mockServer.emit('message', message);
await TestHelper.wait(100);
await TestHelper.wait(300);
entities = store.getState().entities;
posts = entities.posts.posts;
const postId = Object.keys(posts)[0];

View file

@ -450,32 +450,6 @@ describe('Actions.Websocket notVisibleUsersActions', () => {
expect(actions.length).toEqual(3);
expect(actions).toEqual(expectedAction);
});
it('should do nothing if the server version is less than 5.23', async () => {
const profiles = {
[me.id]: me,
[user.id]: user,
[user2.id]: user2,
[user3.id]: user3,
[user4.id]: user4,
[user5.id]: user5,
};
Client4.serverVersion = '5.22.0';
const state = {
entities: {
users: {
currentUserId: me.id,
profiles,
},
},
};
mockGetKnownUsersRequest([user.id, user3.id]);
const actions = await notVisibleUsersActions(state);
expect(actions.length).toEqual(0);
});
});
describe('Actions.Websocket handleUserTypingEvent', () => {

View file

@ -4,7 +4,7 @@
import {analytics} from '@init/analytics';
import {General} from '@mm-redux/constants';
import {UserCustomStatus, UserProfile, UserStatus} from '@mm-redux/types/users';
import {buildQueryString, isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {buildQueryString} from '@mm-redux/utils/helpers';
import {PER_PAGE_DEFAULT} from './constants';
@ -255,13 +255,7 @@ const ClientUsers = (superclass: any) => class extends superclass {
getProfilesInChannel = async (channelId: string, page = 0, perPage = PER_PAGE_DEFAULT, sort = '') => {
analytics.trackAPI('api_profiles_get_in_channel', {channel_id: channelId});
const serverVersion = this.getServerVersion();
let queryStringObj;
if (isMinimumServerVersion(serverVersion, 4, 7)) {
queryStringObj = {in_channel: channelId, page, per_page: perPage, sort};
} else {
queryStringObj = {in_channel: channelId, page, per_page: perPage};
}
const queryStringObj = {in_channel: channelId, page, per_page: perPage, sort};
return this.doFetch(
`${this.getUsersRoute()}${buildQueryString(queryStringObj)}`,
{method: 'get'},

View file

@ -12,7 +12,6 @@ import {getAssociatedGroupsForReference, searchAssociatedGroupsForReferenceLocal
import {getTheme} from '@mm-redux/selectors/entities/preferences';
import {haveIChannelPermission} from '@mm-redux/selectors/entities/roles';
import {getCurrentTeamId} from '@mm-redux/selectors/entities/teams';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {
filterMembersInChannel,
filterMembersNotInChannel,
@ -28,18 +27,15 @@ function mapStateToProps(state, ownProps) {
const currentTeamId = getCurrentTeamId(state);
const license = getLicense(state);
const hasLicense = license?.IsLicensed === 'true' && license?.LDAPGroups === 'true';
let useChannelMentions = true;
if (isMinimumServerVersion(state.entities.general.serverVersion, 5, 22)) {
useChannelMentions = haveIChannelPermission(
state,
{
channel: currentChannelId,
team: currentTeamId,
permission: Permissions.USE_CHANNEL_MENTIONS,
default: true,
},
);
}
const useChannelMentions = haveIChannelPermission(
state,
{
channel: currentChannelId,
team: currentTeamId,
permission: Permissions.USE_CHANNEL_MENTIONS,
default: true,
},
);
const value = ownProps.value.substring(0, cursorPosition);
const matchTerm = getMatchTermForAtMention(value, isSearch);
@ -55,7 +51,7 @@ function mapStateToProps(state, ownProps) {
outChannel = filterMembersNotInChannel(state, matchTerm);
}
if (haveIChannelPermission(state, {channel: currentChannelId, team: currentTeamId, permission: Permissions.USE_GROUP_MENTIONS, default: true}) && hasLicense && isMinimumServerVersion(state.entities.general.serverVersion, 5, 24)) {
if (haveIChannelPermission(state, {channel: currentChannelId, team: currentTeamId, permission: Permissions.USE_GROUP_MENTIONS, default: true}) && hasLicense) {
if (matchTerm) {
groups = searchAssociatedGroupsForReferenceLocal(state, matchTerm, currentTeamId, currentChannelId);
} else {

View file

@ -8,11 +8,9 @@ import {
Platform,
} from 'react-native';
import {Client4} from '@client/rest';
import {analytics} from '@init/analytics';
import {Command, AutocompleteSuggestion, CommandArgs} from '@mm-redux/types/integrations';
import {Theme} from '@mm-redux/types/theme';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {makeStyleSheetFromTheme} from '@utils/theme';
import {AppCommandParser} from './app_command_parser/app_command_parser';
@ -110,25 +108,21 @@ export default class SlashSuggestion extends PureComponent<Props, State> {
}
this.showBaseCommands(nextValue, nextCommands, this.props.channelId, this.props.currentTeamId, this.props.rootId);
} else if (isMinimumServerVersion(Client4.getServerVersion(), 5, 24)) {
if (nextSuggestions === prevProps.suggestions) {
const args = {
channel_id: prevProps.channelId,
team_id: prevProps.currentTeamId,
...(prevProps.rootId && {root_id: prevProps.rootId}),
};
this.props.actions.getCommandAutocompleteSuggestions(nextValue, nextTeamId, args);
} else {
const matches: AutocompleteSuggestion[] = [];
nextSuggestions.forEach((suggestion: AutocompleteSuggestion) => {
if (!this.contains(matches, '/' + suggestion.Complete)) {
matches.push(suggestion);
}
});
this.updateSuggestions(matches);
}
} else if (nextSuggestions === prevProps.suggestions) {
const args = {
channel_id: prevProps.channelId,
team_id: prevProps.currentTeamId,
...(prevProps.rootId && {root_id: prevProps.rootId}),
};
this.props.actions.getCommandAutocompleteSuggestions(nextValue, nextTeamId, args);
} else {
this.setActive(false);
const matches: AutocompleteSuggestion[] = [];
nextSuggestions.forEach((suggestion: AutocompleteSuggestion) => {
if (!this.contains(matches, '/' + suggestion.Complete)) {
matches.push(suggestion);
}
});
this.updateSuggestions(matches);
}
}

View file

@ -5,10 +5,7 @@ import {connect} from 'react-redux';
import {Client4} from '@client/rest';
import {getCustomEmojisByName} from '@mm-redux/selectors/entities/emojis';
import {getConfig} from '@mm-redux/selectors/entities/general';
import {getCurrentUserId} from '@mm-redux/selectors/entities/users';
import {GlobalState} from '@mm-redux/types/store';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {EmojiIndicesByAlias, Emojis} from '@utils/emojis';
import Emoji from './emoji';
@ -18,7 +15,6 @@ type OwnProps = {
}
function mapStateToProps(state: GlobalState, ownProps: OwnProps) {
const config = getConfig(state);
const emojiName = ownProps.emojiName;
const customEmojis = getCustomEmojisByName(state);
const serverUrl = Client4.getUrl();
@ -41,11 +37,7 @@ function mapStateToProps(state: GlobalState, ownProps: OwnProps) {
imageUrl = Client4.getCustomEmojiImageUrl(emoji!.id);
isCustomEmoji = true;
} else {
displayTextOnly = state.entities.emojis.nonExistentEmoji.has(emojiName) ||
config.EnableCustomEmoji !== 'true' ||
config.ExperimentalEnablePostMetadata === 'true' ||
getCurrentUserId(state) === '' ||
isMinimumServerVersion(Client4.getServerVersion(), 5, 12);
displayTextOnly = true;
}
return {

View file

@ -18,7 +18,6 @@ import {getAssociatedGroupsForReferenceMap} from '@mm-redux/selectors/entities/g
import {getTheme} from '@mm-redux/selectors/entities/preferences';
import {haveIChannelPermission} from '@mm-redux/selectors/entities/roles';
import {getCurrentUserId, getStatusForUserId} from '@mm-redux/selectors/entities/users';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {isLandscape} from '@selectors/device';
import {getCurrentChannelDraft, getThreadDraft} from '@selectors/views';
@ -43,7 +42,7 @@ export function mapStateToProps(state, ownProps) {
const channelMemberCountsByGroup = selectChannelMemberCountsByGroup(state, channelId);
let groupsWithAllowReference = new Map();
if (channel && isMinimumServerVersion(state.entities.general.serverVersion, 5, 22)) {
if (channel) {
useChannelMentions = haveIChannelPermission(
state,
{
@ -55,7 +54,7 @@ export function mapStateToProps(state, ownProps) {
);
}
if (channel && isMinimumServerVersion(state.entities.general.serverVersion, 5, 24) && license && license.IsLicensed === 'true') {
if (channel && license?.IsLicensed === 'true') {
useGroupMentions = haveIChannelPermission(
state,
{

View file

@ -8,7 +8,6 @@ import {getCurrentChannel, getChannel, isChannelReadOnlyById} from '@mm-redux/se
import {getTheme} from '@mm-redux/selectors/entities/preferences';
import {haveIChannelPermission} from '@mm-redux/selectors/entities/roles';
import {getCurrentUserId} from '@mm-redux/selectors/entities/users';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {getChannelMembersForDm} from '@selectors/channel';
import PostDraft from './post_draft';
@ -27,7 +26,7 @@ export function mapStateToProps(state, ownProps) {
}
}
if (channel && isMinimumServerVersion(state.entities.general.serverVersion, 5, 22)) {
if (channel) {
canPost = haveIChannelPermission(
state,
{

View file

@ -60,27 +60,6 @@ describe('mapStateToProps', () => {
const baseOwnProps = {};
test('haveIChannelPermission is not called when isMinimumServerVersion is not 5.22v', () => {
const state = {...baseState};
state.entities.general.serverVersion = '5.21';
mapStateToProps(state, baseOwnProps);
expect(isMinimumServerVersion(state.entities.general.serverVersion, 5, 22)).toBe(false);
expect(roleSelectors.haveIChannelPermission).not.toHaveBeenCalledWith(state, {
channel: undefined,
team: undefined,
permission: Permissions.CREATE_POST,
default: true,
});
expect(roleSelectors.haveIChannelPermission).not.toHaveBeenCalledWith(state, {
channel: undefined,
permission: Permissions.USE_CHANNEL_MENTIONS,
default: true,
});
});
test('haveIChannelPermission is called when isMinimumServerVersion is 5.22v', () => {
const state = {...baseState};
state.entities.general.serverVersion = '5.22';

View file

@ -11,14 +11,12 @@ import {
getSortedUnreadChannelIds,
getOrderedChannelIds,
} from '@mm-redux/selectors/entities/channels';
import {getConfig, getLicense, hasNewPermissions} from '@mm-redux/selectors/entities/general';
import {getTheme, getFavoritesPreferences, getSidebarPreferences, isCollapsedThreadsEnabled} from '@mm-redux/selectors/entities/preferences';
import {haveITeamPermission} from '@mm-redux/selectors/entities/roles';
import {getCurrentTeamId} from '@mm-redux/selectors/entities/teams';
import {getCurrentUserId, getCurrentUserRoles} from '@mm-redux/selectors/entities/users';
import {getCurrentUserRoles} from '@mm-redux/selectors/entities/users';
import {showCreateOption} from '@mm-redux/utils/channel_utils';
import {memoizeResult} from '@mm-redux/utils/helpers';
import {isAdmin as checkIsAdmin, isSystemAdmin as checkIsSystemAdmin} from '@mm-redux/utils/user_utils';
import List from './list';
@ -32,13 +30,8 @@ const filterZeroUnreads = memoizeResult((sections) => {
});
function mapStateToProps(state) {
const config = getConfig(state);
const collapsedThreadsEnabled = isCollapsedThreadsEnabled(state);
const license = getLicense(state);
const roles = getCurrentUserId(state) ? getCurrentUserRoles(state) : '';
const currentTeamId = getCurrentTeamId(state);
const isAdmin = checkIsAdmin(roles);
const isSystemAdmin = checkIsSystemAdmin(roles);
const sidebarPrefs = getSidebarPreferences(state);
const lastUnreadChannel = DeviceTypes.IS_TABLET ? state.views.channel.keepChannelIdAsUnread : null;
const unreadChannelIds = getSortedUnreadChannelIds(state, lastUnreadChannel);
@ -52,15 +45,12 @@ function mapStateToProps(state) {
sidebarPrefs.favorite_at_top === 'true' && favoriteChannelIds.length,
));
let canJoinPublicChannels = true;
if (hasNewPermissions(state)) {
canJoinPublicChannels = haveITeamPermission(state, {
team: currentTeamId,
permission: Permissions.JOIN_PUBLIC_CHANNELS,
});
}
const canCreatePublicChannels = showCreateOption(state, config, license, currentTeamId, General.OPEN_CHANNEL, isAdmin, isSystemAdmin);
const canCreatePrivateChannels = showCreateOption(state, config, license, currentTeamId, General.PRIVATE_CHANNEL, isAdmin, isSystemAdmin);
const canJoinPublicChannels = haveITeamPermission(state, {
team: currentTeamId,
permission: Permissions.JOIN_PUBLIC_CHANNELS,
});
const canCreatePublicChannels = showCreateOption(state, currentTeamId, General.OPEN_CHANNEL);
const canCreatePrivateChannels = showCreateOption(state, currentTeamId, General.PRIVATE_CHANNEL);
return {
canJoinPublicChannels,

View file

@ -33,7 +33,6 @@ import {isCollapsedThreadsEnabled} from '@mm-redux/selectors/entities/preference
import {isTimezoneEnabled} from '@mm-redux/selectors/entities/timezone';
import {getCurrentUser, getUser} from '@mm-redux/selectors/entities/users';
import EventEmitter from '@mm-redux/utils/event_emitter';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import EphemeralStore from '@store/ephemeral_store';
import initialState from '@store/initial_state';
import Store from '@store/store';
@ -242,7 +241,7 @@ class GlobalEventHandler {
onServerConfigChanged = (config) => {
this.configureAnalytics(config);
if (isMinimumServerVersion(Client4.serverVersion, 5, 24) && config.ExtendSessionLengthWithActivity === 'true') {
if (config.ExtendSessionLengthWithActivity === 'true') {
PushNotifications.cancelAllLocalNotifications();
}
};

View file

@ -198,21 +198,6 @@ describe('Actions.General', () => {
});
describe('getRedirectLocation', () => {
it('old server', async () => {
store.dispatch({type: GeneralTypes.RECEIVED_SERVER_VERSION, data: '5.0.0'});
const mock = nock(Client4.getBaseRoute()).
get('/redirect_location').
reply(404);
// Should return the original link
const result = await store.dispatch(Actions.getRedirectLocation('http://examp.le'));
assert.deepEqual(result.data, {location: 'http://examp.le'});
// Should not call the API on an old server
assert.equal(mock.isDone(), false);
});
it('should save the correct location', async () => {
store.dispatch({type: GeneralTypes.RECEIVED_SERVER_VERSION, data: '5.3.0'});

View file

@ -190,12 +190,7 @@ export function setUrl(url: string) {
export function getRedirectLocation(url: string): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
let pendingData: Promise<any>;
if (isMinimumServerVersion(getServerVersion(getState()), 5, 3)) {
pendingData = Client4.getRedirectLocation(url);
} else {
pendingData = Promise.resolve({location: url});
}
const pendingData = Client4.getRedirectLocation(url);
let data;
try {

View file

@ -8,7 +8,6 @@ import {getCurrentUserId, getUsers} from '@mm-redux/selectors/entities/users';
import {batchActions, Action, ActionFunc, GenericAction, DispatchFunc, GetStateFunc} from '@mm-redux/types/actions';
import {Client4Error} from '@mm-redux/types/client4';
import {GlobalState} from '@mm-redux/types/store';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {logError} from './errors';
@ -138,9 +137,6 @@ export function debounce(func: (...args: any) => unknown, wait: number, immediat
}
export async function notVisibleUsersActions(state: GlobalState): Promise<Array<GenericAction>> {
if (!isMinimumServerVersion(Client4.getServerVersion(), 5, 23)) {
return [];
}
let knownUsers: Set<string>;
try {
const fetchResult = await Client4.getKnownUsers();

View file

@ -6,7 +6,6 @@ import assert from 'assert';
import nock from 'nock';
import {Client4} from '@client/rest';
import {GeneralTypes} from '@mm-redux/action_types';
import * as Actions from '@mm-redux/actions/teams';
import {login} from '@mm-redux/actions/users';
import TestHelper from '@test/test_helper';
@ -261,64 +260,6 @@ describe('Actions.Teams', () => {
assert.strictEqual(patched.description, description);
});
it('Join Open Team', async () => {
const client = TestHelper.createClient();
nock(Client4.getBaseRoute()).
post('/users').
query(true).
reply(201, TestHelper.fakeUserWithId());
const user = await client.createUser(
TestHelper.fakeUser(),
null,
null,
TestHelper.basicTeam.invite_id,
);
nock(Client4.getBaseRoute()).
post('/users/login').
reply(200, user);
await client.login(user.email, 'password1');
nock(Client4.getBaseRoute()).
post('/teams').
reply(201, {...TestHelper.fakeTeamWithId(), allow_open_invite: true});
const team = await client.createTeam({...TestHelper.fakeTeam(), allow_open_invite: true});
store.dispatch({type: GeneralTypes.RECEIVED_SERVER_VERSION, data: '4.0.0'});
nock(Client4.getBaseRoute()).
post('/teams/members/invite').
query(true).
reply(201, {user_id: TestHelper.basicUser.id, team_id: team.id});
nock(Client4.getBaseRoute()).
get(`/teams/${team.id}`).
reply(200, team);
nock(Client4.getUserRoute('me')).
get('/teams/members').
reply(200, [{user_id: TestHelper.basicUser.id, roles: 'team_user', team_id: team.id}]);
nock(Client4.getUserRoute('me')).
get('/teams/unread').
reply(200, [{team_id: team.id, msg_count: 0, mention_count: 0}]);
await Actions.joinTeam(team.invite_id, team.id)(store.dispatch, store.getState);
const state = store.getState();
const request = state.requests.teams.joinTeam;
if (request.status !== RequestStatus.SUCCESS) {
throw new Error(JSON.stringify(request.error));
}
const {teams, myMembers} = state.entities.teams;
assert.ok(teams[team.id]);
assert.ok(myMembers[team.id]);
});
it('getMyTeamMembers and getMyTeamUnreads', async () => {
nock(Client4.getUserRoute('me')).
get('/teams/members').

View file

@ -2,7 +2,6 @@
// See LICENSE.txt for license information.
import {Client4} from '@client/rest';
import {ChannelTypes, RoleTypes, TeamTypes, UserTypes} from '@mm-redux/action_types';
import {isCompatibleWithJoinViewTeamPermissions} from '@mm-redux/selectors/entities/general';
import {getRoles} from '@mm-redux/selectors/entities/roles_helpers';
import {getCurrentTeamId} from '@mm-redux/selectors/entities/teams';
import {getCurrentUserId} from '@mm-redux/selectors/entities/users';
@ -417,18 +416,14 @@ export function removeUserFromTeam(teamId: string, userId: string): ActionFunc {
};
}
export function joinTeam(inviteId: string, teamId: string): ActionFunc {
export function joinTeam(teamId: string): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
dispatch({type: TeamTypes.JOIN_TEAM_REQUEST, data: null});
const state = getState();
try {
if (isCompatibleWithJoinViewTeamPermissions(state)) {
const currentUserId = state.entities.users.currentUserId;
await Client4.addToTeam(teamId, currentUserId);
} else {
await Client4.joinTeam(inviteId);
}
const currentUserId = state.entities.users.currentUserId;
await Client4.addToTeam(teamId, currentUserId);
} catch (error) {
forceLogoutIfNecessary(error, dispatch, getState);
dispatch(batchActions([

View file

@ -3,19 +3,16 @@
import {Client4} from '@client/rest';
import {analytics} from '@init/analytics';
import {UserTypes, TeamTypes} from '@mm-redux/action_types';
import {getConfig, getServerVersion} from '@mm-redux/selectors/entities/general';
import {getCurrentUserId, getUsers} from '@mm-redux/selectors/entities/users';
import {Action, ActionFunc, ActionResult, batchActions, DispatchFunc, GetStateFunc} from '@mm-redux/types/actions';
import {TeamMembership} from '@mm-redux/types/teams';
import {UserProfile, UserStatus} from '@mm-redux/types/users';
import {Dictionary} from '@mm-redux/types/utilities';
import {getUserIdFromChannelName, isDirectChannel, isDirectChannelVisible, isGroupChannel, isGroupChannelVisible} from '@mm-redux/utils/channel_utils';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {removeUserFromList} from '@mm-redux/utils/user_utils';
import {General} from '../constants';
import {getAllCustomEmojis} from './emojis';
import {logError} from './errors';
import {getClientConfig, setServerVersion} from './general';
import {bindClientFunc, forceLogoutIfNecessary, debounce} from './helpers';
@ -112,7 +109,7 @@ export function loginById(id: string, password: string, mfaToken = ''): ActionFu
}
function completeLogin(data: UserProfile): ActionFunc {
return async (dispatch: DispatchFunc, getState: GetStateFunc) => {
return async (dispatch: DispatchFunc) => {
dispatch({
type: UserTypes.RECEIVED_ME,
data,
@ -153,9 +150,6 @@ function completeLogin(data: UserProfile): ActionFunc {
const serverVersion = Client4.getServerVersion();
dispatch(setServerVersion(serverVersion));
if (!isMinimumServerVersion(serverVersion, 4, 7) && getConfig(getState()).EnableCustomEmoji === 'true') {
dispatch(getAllCustomEmojis());
}
try {
await Promise.all(promises);
@ -915,11 +909,6 @@ export function checkForModifiedUsers() {
const state = getState();
const users = getUsers(state);
const lastDisconnectAt = state.websocket.lastDisconnectAt;
const serverVersion = getServerVersion(state);
if (!isMinimumServerVersion(serverVersion, 5, 14)) {
return {data: true};
}
await dispatch(getProfilesByIds(Object.keys(users), {since: lastDisconnectAt}));
return {data: true};

View file

@ -1399,20 +1399,6 @@ describe('Selectors.Channels.getRedirectChannelNameForTeam', () => {
},
});
it('getRedirectChannelNameForTeam without advanced permissions', () => {
const modifiedState = {
...testState,
entities: {
...testState.entities,
general: {
...testState.entities.general,
serverVersion: '4.8.0',
},
},
};
assert.equal(Selectors.getRedirectChannelNameForTeam(modifiedState, team1.id), General.DEFAULT_CHANNEL);
});
it('getRedirectChannelNameForTeam with advanced permissions but without JOIN_PUBLIC_CHANNELS permission', () => {
const modifiedState = {
...testState,
@ -1506,20 +1492,6 @@ describe('Selectors.Channels.getRedirectChannelNameForTeam', () => {
assert.equal(Selectors.getRedirectChannelNameForTeam(modifiedState, team1.id), General.DEFAULT_CHANNEL);
});
it('getRedirectChannelNameForTeam without advanced permissions in not current team', () => {
const modifiedState = {
...testState,
entities: {
...testState.entities,
general: {
...testState.entities.general,
serverVersion: '4.8.0',
},
},
};
assert.equal(Selectors.getRedirectChannelNameForTeam(modifiedState, team2.id), General.DEFAULT_CHANNEL);
});
it('getRedirectChannelNameForTeam with advanced permissions but without JOIN_PUBLIC_CHANNELS permission in not current team', () => {
const modifiedState = {
...testState,

View file

@ -3,11 +3,11 @@
import {createSelector} from 'reselect';
import {getCurrentChannelId, getCurrentUser, getUsers, getMyChannelMemberships, getMyCurrentChannelMembership} from '@mm-redux/selectors/entities/common';
import {getConfig, getLicense, hasNewPermissions} from '@mm-redux/selectors/entities/general';
import {getConfig} from '@mm-redux/selectors/entities/general';
import {getLastPostPerChannel, getAllPosts} from '@mm-redux/selectors/entities/posts';
import {getFavoritesPreferences, getMyPreferences, getTeammateNameDisplaySetting, getVisibleTeammate, getVisibleGroupIds, isCollapsedThreadsEnabled} from '@mm-redux/selectors/entities/preferences';
import {haveICurrentChannelPermission, haveIChannelPermission, haveITeamPermission} from '@mm-redux/selectors/entities/roles';
import {getCurrentTeamId, getCurrentTeamMembership, getMyTeams, getTeamMemberships} from '@mm-redux/selectors/entities/teams';
import {getCurrentTeamId, getMyTeams, getTeamMemberships} from '@mm-redux/selectors/entities/teams';
import {isCurrentUserSystemAdmin, getCurrentUserId} from '@mm-redux/selectors/entities/users';
import {Channel, ChannelStats, ChannelMembership, ChannelMemberCountsByGroup} from '@mm-redux/types/channels';
import {Config} from '@mm-redux/types/config';
@ -18,7 +18,7 @@ import {TeamMembership, Team} from '@mm-redux/types/teams';
import {ThreadsState} from '@mm-redux/types/threads';
import {UsersState, UserProfile} from '@mm-redux/types/users';
import {NameMappedObjects, UserIDMappedObjects, IDMappedObjects, RelationOneToOne, RelationOneToMany} from '@mm-redux/types/utilities';
import {buildDisplayableChannelListWithUnreadSection, canManageMembersOldPermissions, completeDirectChannelInfo, completeDirectChannelDisplayName, getUserIdFromChannelName, getChannelByName as getChannelByNameHelper, isChannelMuted, getDirectChannelName, isAutoClosed, isDirectChannelVisible, isGroupChannelVisible, isGroupOrDirectChannelVisible, sortChannelsByDisplayName, isFavoriteChannel, isDefault, sortChannelsByRecency, getMsgCountInChannel} from '@mm-redux/utils/channel_utils';
import {buildDisplayableChannelListWithUnreadSection, completeDirectChannelInfo, completeDirectChannelDisplayName, getUserIdFromChannelName, getChannelByName as getChannelByNameHelper, isChannelMuted, getDirectChannelName, isAutoClosed, isDirectChannelVisible, isGroupChannelVisible, isGroupOrDirectChannelVisible, sortChannelsByDisplayName, isFavoriteChannel, isDefault, sortChannelsByRecency, getMsgCountInChannel} from '@mm-redux/utils/channel_utils';
import {createIdsSelector} from '@mm-redux/utils/helpers';
import {General, Permissions} from '../../constants';
@ -471,24 +471,31 @@ export const getUnreadsInCurrentTeam: (a: GlobalState) => {
mentionCount,
};
});
export const canManageChannelMembers: (a: GlobalState) => boolean = createSelector(getCurrentChannel, getCurrentUser, getCurrentTeamMembership, getMyCurrentChannelMembership, getConfig, getLicense, hasNewPermissions, (state: GlobalState): boolean => haveICurrentChannelPermission(state, {
permission: Permissions.MANAGE_PRIVATE_CHANNEL_MEMBERS,
}), (state: GlobalState): boolean => haveICurrentChannelPermission(state, {
permission: Permissions.MANAGE_PUBLIC_CHANNEL_MEMBERS,
}), (channel: Channel, user: UserProfile, teamMembership: TeamMembership, channelMembership: ChannelMembership | undefined | null, config: Config, license: any, newPermissions: boolean, managePrivateMembers: boolean, managePublicMembers: boolean): boolean => {
if (!channel) {
return false;
}
export const canManageChannelMembers: (a: GlobalState) => boolean = createSelector(
getCurrentChannel,
(state: GlobalState): boolean => haveICurrentChannelPermission(state, {
permission: Permissions.MANAGE_PRIVATE_CHANNEL_MEMBERS,
}),
(state: GlobalState): boolean => haveICurrentChannelPermission(state, {
permission: Permissions.MANAGE_PUBLIC_CHANNEL_MEMBERS,
}),
(
channel: Channel,
managePrivateMembers: boolean,
managePublicMembers: boolean,
): boolean => {
if (!channel) {
return false;
}
if (channel.delete_at !== 0) {
return false;
}
if (channel.delete_at !== 0) {
return false;
}
if (channel.type === General.DM_CHANNEL || channel.type === General.GM_CHANNEL || channel.name === General.DEFAULT_CHANNEL) {
return false;
}
if (channel.type === General.DM_CHANNEL || channel.type === General.GM_CHANNEL || channel.name === General.DEFAULT_CHANNEL) {
return false;
}
if (newPermissions) {
if (channel.type === General.OPEN_CHANNEL) {
return managePublicMembers;
} else if (channel.type === General.PRIVATE_CHANNEL) {
@ -496,14 +503,7 @@ export const canManageChannelMembers: (a: GlobalState) => boolean = createSelect
}
return true;
}
if (!channelMembership) {
return false;
}
return canManageMembersOldPermissions(channel, user, teamMembership, channelMembership, config, license);
}); // Determine if the user has permissions to manage members in at least one channel of the current team
}); // Determine if the user has permissions to manage members in at least one channel of the current team
export const canManageAnyChannelMembersInCurrentTeam: (a: GlobalState) => boolean = createSelector(getMyChannelMemberships, getCurrentTeamId, (state: GlobalState): GlobalState => state, (members: RelationOneToOne<Channel, ChannelMembership>, currentTeamId: string, state: GlobalState): boolean => {
for (const channelId of Object.keys(members)) {
@ -950,7 +950,7 @@ export const getMyFirstChannelForTeams: (a: GlobalState) => RelationOneToOne<Tea
export const getRedirectChannelNameForTeam = (state: GlobalState, teamId: string): string => {
const defaultChannelForTeam = getDefaultChannelForTeams(state)[teamId];
const myFirstChannelForTeam = getMyFirstChannelForTeams(state)[teamId];
const canIJoinPublicChannelsInTeam = !hasNewPermissions(state) || haveITeamPermission(state, {
const canIJoinPublicChannelsInTeam = haveITeamPermission(state, {
team: teamId,
permission: Permissions.JOIN_PUBLIC_CHANNELS,
});

View file

@ -254,31 +254,6 @@ describe('Selectors.General', () => {
}), true);
});
it('hasNewPermissions', () => {
const state = {
entities: {
general: {
serverVersion: '4.8.0',
},
},
};
assert.equal(Selectors.hasNewPermissions(state), false);
state.entities.general.serverVersion = '4.8.0.dev.123123';
assert.equal(Selectors.hasNewPermissions(state), true);
state.entities.general.serverVersion = '4.8.0.4.8.1.ffffff';
assert.equal(Selectors.hasNewPermissions(state), false);
state.entities.general.serverVersion = '4.8.0.3607.2f31498e967dc08ed38d7a2d7a306825.true';
assert.equal(Selectors.hasNewPermissions(state), true);
state.entities.general.serverVersion = '4.8.1.3607.2f31498e967dc08ed38d7a2d7a306825.true';
assert.equal(Selectors.hasNewPermissions(state), true);
state.entities.general.serverVersion = '4.9.0';
assert.equal(Selectors.hasNewPermissions(state), true);
state.entities.general.serverVersion = '4.10.0';
assert.equal(Selectors.hasNewPermissions(state), true);
state.entities.general.serverVersion = '5.10.0.dev';
assert.equal(Selectors.hasNewPermissions(state), true);
});
describe('getAutolinkedUrlSchemes', () => {
it('setting doesn\'t exist', () => {
const state = {

View file

@ -4,7 +4,6 @@ import {createSelector} from 'reselect';
import {Config, FeatureFlags} from '@mm-redux/types/config';
import {GlobalState} from '@mm-redux/types/store';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {General} from '../../constants';
@ -31,22 +30,6 @@ 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;
// FIXME This must be changed to 4, 9, 0 before we generate the 4.9.0 release
return isMinimumServerVersion(version, 4, 9, 0) ||
(version.indexOf('dev') !== -1 && isMinimumServerVersion(version, 4, 8, 0)) ||
(version.match(/^4.8.\d.\d\d\d\d.*$/) !== null && isMinimumServerVersion(version, 4, 8, 0));
}
export const canUploadFilesOnMobile: (a: GlobalState) => boolean = createSelector(
getConfig,
getLicense,

View file

@ -91,34 +91,6 @@ describe('Selectors.Teams', () => {
assert.deepEqual(Selectors.getTeamMember(testState, team1.id, user2.id), membersInTeam[team1.id][user2.id]);
});
it('getJoinableTeams', () => {
const openTeams = [team3, team4];
const joinableTeams = Selectors.getJoinableTeams(testState);
assert.strictEqual(joinableTeams[0], openTeams[0]);
assert.strictEqual(joinableTeams[1], openTeams[1]);
});
it('getSortedJoinableTeams', () => {
const openTeams = [team4, team3];
const joinableTeams = Selectors.getSortedJoinableTeams(testState);
assert.strictEqual(joinableTeams[0], openTeams[0]);
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];

View file

@ -3,7 +3,7 @@
import * as reselect from 'reselect';
import {Permissions, Preferences} from '@mm-redux/constants';
import {getConfig, getCurrentUrl, isCompatibleWithJoinViewTeamPermissions} from '@mm-redux/selectors/entities/general';
import {getConfig, getCurrentUrl} from '@mm-redux/selectors/entities/general';
import {get as getPreference} from '@mm-redux/selectors/entities/preferences';
import {haveISystemPermission} from '@mm-redux/selectors/entities/roles_helpers';
import {GlobalState} from '@mm-redux/types/store';
@ -147,15 +147,11 @@ export const getListableTeamIds = createIdsSelector(
getTeamMemberships,
(state) => haveISystemPermission(state, {permission: Permissions.LIST_PUBLIC_TEAMS}),
(state) => haveISystemPermission(state, {permission: Permissions.LIST_PRIVATE_TEAMS}),
isCompatibleWithJoinViewTeamPermissions,
(teams, myMembers, canListPublicTeams, canListPrivateTeams, compatibleWithJoinViewTeamPermissions) => {
(teams, myMembers, canListPublicTeams, canListPrivateTeams) => {
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);
}
const canList = (canListPrivateTeams && !team.allow_open_invite) || (canListPublicTeams && team.allow_open_invite);
return team.delete_at === 0 && canList && !member;
});
},
@ -189,15 +185,11 @@ export const getJoinableTeamIds = createIdsSelector(
getTeamMemberships,
(state: GlobalState) => haveISystemPermission(state, {permission: Permissions.JOIN_PUBLIC_TEAMS}),
(state: GlobalState) => haveISystemPermission(state, {permission: Permissions.JOIN_PRIVATE_TEAMS}),
isCompatibleWithJoinViewTeamPermissions,
(teams, myMembers, canJoinPublicTeams, canJoinPrivateTeams, compatibleWithJoinViewTeamPermissions) => {
(teams, myMembers, canJoinPublicTeams, canJoinPrivateTeams) => {
return Object.keys(teams).filter((id) => {
const team = teams[id];
const member = myMembers[id];
let canJoin = team.allow_open_invite;
if (compatibleWithJoinViewTeamPermissions) {
canJoin = (canJoinPrivateTeams && !team.allow_open_invite) || (canJoinPublicTeams && team.allow_open_invite);
}
const canJoin = (canJoinPrivateTeams && !team.allow_open_invite) || (canJoinPublicTeams && team.allow_open_invite);
return team.delete_at === 0 && canJoin && !member;
});
},

View file

@ -1,7 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {hasNewPermissions} from '@mm-redux/selectors/entities/general';
import {haveITeamPermission, haveIChannelPermission} from '@mm-redux/selectors/entities/roles';
import {Channel, ChannelMembership, ChannelType, ChannelNotifyProps} from '@mm-redux/types/channels';
import {Post} from '@mm-redux/types/posts';
@ -285,115 +284,30 @@ export function isGroupOrDirectChannelVisible(
);
}
export function showCreateOption(state: GlobalState, config: any, license: any, teamId: string, channelType: ChannelType, isAdmin: boolean, isSystemAdmin: boolean): boolean {
if (hasNewPermissions(state)) {
if (channelType === General.OPEN_CHANNEL) {
return haveITeamPermission(state, {team: teamId, permission: Permissions.CREATE_PUBLIC_CHANNEL});
} else if (channelType === General.PRIVATE_CHANNEL) {
return haveITeamPermission(state, {team: teamId, permission: Permissions.CREATE_PRIVATE_CHANNEL});
}
return true;
}
if (license.IsLicensed !== 'true') {
return true;
}
// Backwards compatibility with pre-advanced permissions config settings.
export function showCreateOption(state: GlobalState, teamId: string, channelType: ChannelType): boolean {
if (channelType === General.OPEN_CHANNEL) {
if (config.RestrictPublicChannelCreation === General.SYSTEM_ADMIN_ROLE && !isSystemAdmin) {
return false;
} else if (config.RestrictPublicChannelCreation === General.TEAM_ADMIN_ROLE && !isAdmin) {
return false;
}
return haveITeamPermission(state, {team: teamId, permission: Permissions.CREATE_PUBLIC_CHANNEL});
} else if (channelType === General.PRIVATE_CHANNEL) {
if (config.RestrictPrivateChannelCreation === General.SYSTEM_ADMIN_ROLE && !isSystemAdmin) {
return false;
} else if (config.RestrictPrivateChannelCreation === General.TEAM_ADMIN_ROLE && !isAdmin) {
return false;
}
return haveITeamPermission(state, {team: teamId, permission: Permissions.CREATE_PRIVATE_CHANNEL});
}
return true;
}
export function showManagementOptions(state: GlobalState, config: any, license: any, channel: Channel, isAdmin: boolean, isSystemAdmin: boolean, isChannelAdmin: boolean): boolean {
if (hasNewPermissions(state)) {
if (channel.type === General.OPEN_CHANNEL) {
return haveIChannelPermission(state, {channel: channel.id, team: channel.team_id, permission: Permissions.MANAGE_PUBLIC_CHANNEL_PROPERTIES});
} else if (channel.type === General.PRIVATE_CHANNEL) {
return haveIChannelPermission(state, {channel: channel.id, team: channel.team_id, permission: Permissions.MANAGE_PRIVATE_CHANNEL_PROPERTIES});
}
return true;
}
if (license.IsLicensed !== 'true') {
return true;
}
// Backwards compatibility with pre-advanced permissions config settings.
export function showManagementOptions(state: GlobalState, channel: Channel): boolean {
if (channel.type === General.OPEN_CHANNEL) {
if (config.RestrictPublicChannelManagement === General.SYSTEM_ADMIN_ROLE && !isSystemAdmin) {
return false;
}
if (config.RestrictPublicChannelManagement === General.TEAM_ADMIN_ROLE && !isAdmin) {
return false;
}
if (config.RestrictPublicChannelManagement === General.CHANNEL_ADMIN_ROLE && !isChannelAdmin && !isAdmin) {
return false;
}
return haveIChannelPermission(state, {channel: channel.id, team: channel.team_id, permission: Permissions.MANAGE_PUBLIC_CHANNEL_PROPERTIES});
} else if (channel.type === General.PRIVATE_CHANNEL) {
if (config.RestrictPrivateChannelManagement === General.SYSTEM_ADMIN_ROLE && !isSystemAdmin) {
return false;
}
if (config.RestrictPrivateChannelManagement === General.TEAM_ADMIN_ROLE && !isAdmin) {
return false;
}
if (config.RestrictPrivateChannelManagement === General.CHANNEL_ADMIN_ROLE && !isChannelAdmin && !isAdmin) {
return false;
}
return haveIChannelPermission(state, {channel: channel.id, team: channel.team_id, permission: Permissions.MANAGE_PRIVATE_CHANNEL_PROPERTIES});
}
return true;
}
export function showDeleteOption(state: GlobalState, config: any, license: any, channel: Channel, isAdmin: boolean, isSystemAdmin: boolean, isChannelAdmin: boolean): boolean {
if (hasNewPermissions(state)) {
if (channel.type === General.OPEN_CHANNEL) {
return haveIChannelPermission(state, {channel: channel.id, team: channel.team_id, permission: Permissions.DELETE_PUBLIC_CHANNEL});
} else if (channel.type === General.PRIVATE_CHANNEL) {
return haveIChannelPermission(state, {channel: channel.id, team: channel.team_id, permission: Permissions.DELETE_PRIVATE_CHANNEL});
}
return true;
}
if (license.IsLicensed !== 'true') {
return true;
}
// Backwards compatibility with pre-advanced permissions config settings.
export function showDeleteOption(state: GlobalState, channel: Channel): boolean {
if (channel.type === General.OPEN_CHANNEL) {
if (config.RestrictPublicChannelDeletion === General.SYSTEM_ADMIN_ROLE && !isSystemAdmin) {
return false;
}
if (config.RestrictPublicChannelDeletion === General.TEAM_ADMIN_ROLE && !isAdmin) {
return false;
}
if (config.RestrictPublicChannelDeletion === General.CHANNEL_ADMIN_ROLE && !isChannelAdmin && !isAdmin) {
return false;
}
return haveIChannelPermission(state, {channel: channel.id, team: channel.team_id, permission: Permissions.DELETE_PUBLIC_CHANNEL});
} else if (channel.type === General.PRIVATE_CHANNEL) {
if (config.RestrictPrivateChannelDeletion === General.SYSTEM_ADMIN_ROLE && !isSystemAdmin) {
return false;
}
if (config.RestrictPrivateChannelDeletion === General.TEAM_ADMIN_ROLE && !isAdmin) {
return false;
}
if (config.RestrictPrivateChannelDeletion === General.CHANNEL_ADMIN_ROLE && !isChannelAdmin && !isAdmin) {
return false;
}
return haveIChannelPermission(state, {channel: channel.id, team: channel.team_id, permission: Permissions.DELETE_PRIVATE_CHANNEL});
}
return true;
}

View file

@ -100,35 +100,46 @@ describe('PostUtils', () => {
const channelId = 'channel-id';
const userId = 'user-id';
const state = {entities: {general: {serverVersion: ''}}};
it('should allow to edit my post without license', () => {
// Hasn't license
assert.ok(canEditPost(state, {PostEditTimeLimit: -1}, notLicensed, teamId, channelId, userId, {user_id: userId, type: 'normal'}));
assert.ok(!canEditPost(state, {PostEditTimeLimit: -1}, notLicensed, teamId, channelId, userId, {user_id: userId, type: 'system_test'}));
assert.ok(!canEditPost(state, {PostEditTimeLimit: -1}, notLicensed, teamId, channelId, userId, {user_id: 'other', type: 'normal'}));
assert.ok(!canEditPost(state, {PostEditTimeLimit: -1}, notLicensed, teamId, channelId, userId, {user_id: 'other', type: 'system_test'}));
assert.ok(!canEditPost(state, {PostEditTimeLimit: -1}, notLicensed, teamId, channelId, userId, null));
});
it('should work with old permissions version', () => {
const oldVersionState = {
const newVersionState = {
entities: {
general: {
serverVersion: '4.3.0',
serverVersion: '5.26.0',
},
users: {
currentUserId: userId,
profiles: {
'user-id': {roles: 'system_role'},
},
},
teams: {
currentTeamId: teamId,
myMembers: {
'team-id': {roles: 'team_role'},
},
},
channels: {
currentChannelId: channelId,
myMembers: {
'channel-id': {roles: 'channel_role'},
},
},
roles: {
roles: {
system_role: {permissions: [Permissions.EDIT_POST]},
team_role: {permissions: []},
channel_role: {permissions: []},
},
},
},
};
// With old permissions
assert.ok(!canEditPost(oldVersionState, {PostEditTimeLimit: null, AllowEditPost: 'never'}, licensed, teamId, channelId, userId, {user_id: userId}));
assert.ok(canEditPost(oldVersionState, {PostEditTimeLimit: null, AllowEditPost: 'always'}, licensed, teamId, channelId, userId, {user_id: userId}));
assert.ok(canEditPost(oldVersionState, {PostEditTimeLimit: 300, AllowEditPost: 'time_limit'}, licensed, teamId, channelId, userId, {user_id: userId, create_at: Date.now() - 100}));
assert.ok(!canEditPost(oldVersionState, {PostEditTimeLimit: 300, AllowEditPost: 'time_limit'}, licensed, teamId, channelId, userId, {user_id: userId, create_at: Date.now() - 600000}));
assert.ok(!canEditPost(oldVersionState, {PostEditTimeLimit: null, AllowEditPost: 'never'}, licensed, teamId, channelId, userId, {user_id: 'other'}));
assert.ok(!canEditPost(oldVersionState, {PostEditTimeLimit: null, AllowEditPost: 'always'}, licensed, teamId, channelId, userId, {user_id: 'other'}));
assert.ok(!canEditPost(oldVersionState, {PostEditTimeLimit: 300, AllowEditPost: 'time_limit'}, licensed, teamId, channelId, userId, {user_id: 'other', create_at: Date.now() - 100}));
assert.ok(!canEditPost(oldVersionState, {PostEditTimeLimit: 300, AllowEditPost: 'time_limit'}, licensed, teamId, channelId, userId, {user_id: 'other', create_at: Date.now() - 600000}));
// Hasn't license
assert.ok(canEditPost(newVersionState, {PostEditTimeLimit: -1}, notLicensed, teamId, channelId, userId, {user_id: userId, type: 'normal'}));
assert.ok(!canEditPost(newVersionState, {PostEditTimeLimit: -1}, notLicensed, teamId, channelId, userId, {user_id: userId, type: 'system_test'}));
assert.ok(!canEditPost(newVersionState, {PostEditTimeLimit: -1}, notLicensed, teamId, channelId, userId, {user_id: 'other', type: 'normal'}));
assert.ok(!canEditPost(newVersionState, {PostEditTimeLimit: -1}, notLicensed, teamId, channelId, userId, {user_id: 'other', type: 'system_test'}));
assert.ok(!canEditPost(newVersionState, {PostEditTimeLimit: -1}, notLicensed, teamId, channelId, userId, null));
});
it('should work with new permissions version', () => {

View file

@ -1,6 +1,5 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {hasNewPermissions} from '@mm-redux/selectors/entities/general';
import {haveIChannelPermission} from '@mm-redux/selectors/entities/roles';
import {Channel} from '@mm-redux/types/channels';
import {Post, PostType, PostMetadata, PostEmbed} from '@mm-redux/types/posts';
@ -9,9 +8,8 @@ import {GlobalState} from '@mm-redux/types/store';
import {Team} from '@mm-redux/types/teams';
import {UserProfile} from '@mm-redux/types/users';
import {$ID} from '@mm-redux/types/utilities';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {General, Posts, Preferences, Permissions} from '../constants';
import {Posts, Preferences, Permissions} from '../constants';
import {getPreferenceKey} from './preference_utils';
@ -54,72 +52,24 @@ export function isEdited(post: Post): boolean {
return post.edit_at > 0;
}
export function canDeletePost(state: GlobalState, config: any, license: any, teamId: $ID<Team>, channelId: $ID<Channel>, userId: $ID<UserProfile>, post: Post, isAdmin: boolean, isSystemAdmin: boolean): boolean {
if (!post) {
return false;
}
const isOwner = isPostOwner(userId, post);
if (hasNewPermissions(state)) {
let permissions = [];
if (isOwner) {
permissions = [Permissions.DELETE_POST];
} else {
const {serverVersion} = state.entities.general;
// prior to v5.27, the server used to require delete_own_posts and
// delete_others_posts permissions to be able to delete a post by a
// different author.
permissions = isMinimumServerVersion(serverVersion, 5, 27) ? [Permissions.DELETE_OTHERS_POSTS] : [Permissions.DELETE_POST, Permissions.DELETE_OTHERS_POSTS];
}
return permissions.every((permission) => haveIChannelPermission(state, {team: teamId, channel: channelId, permission, default: false}));
}
// Backwards compatibility with pre-advanced permissions config settings.
if (license.IsLicensed === 'true') {
return (config.RestrictPostDelete === General.PERMISSIONS_ALL && (isOwner || isAdmin)) ||
(config.RestrictPostDelete === General.PERMISSIONS_TEAM_ADMIN && isAdmin) ||
(config.RestrictPostDelete === General.PERMISSIONS_SYSTEM_ADMIN && isSystemAdmin);
}
return isOwner || isAdmin;
}
export function canEditPost(state: GlobalState, config: any, license: any, teamId: $ID<Team>, channelId: $ID<Channel>, userId: $ID<UserProfile>, post: Post): boolean {
if (!post || isSystemMessage(post)) {
return false;
}
const isOwner = isPostOwner(userId, post);
let canEdit = true;
if (hasNewPermissions(state)) {
let permissions = [];
if (isOwner) {
permissions = [Permissions.EDIT_POST];
} else {
const {serverVersion} = state.entities.general;
// prior to v5.26, the server used to require edit_own_posts and
// edit_others_posts permissions to be able to edit a post by a
// different author.
permissions = isMinimumServerVersion(serverVersion, 5, 26) ? [Permissions.EDIT_OTHERS_POSTS] : [Permissions.EDIT_POST, Permissions.EDIT_OTHERS_POSTS];
}
canEdit = permissions.every((permission) => haveIChannelPermission(state, {team: teamId, channel: channelId, permission, default: false}));
if (license.IsLicensed === 'true' && config.PostEditTimeLimit !== '-1' && config.PostEditTimeLimit !== -1) {
const timeLeft = (post.create_at + (config.PostEditTimeLimit * 1000)) - Date.now();
if (timeLeft <= 0) {
canEdit = false;
}
}
let permissions = [];
if (isOwner) {
permissions = [Permissions.EDIT_POST];
} else {
// Backwards compatibility with pre-advanced permissions config settings.
canEdit = isOwner && config.AllowEditPost !== 'never';
if (config.AllowEditPost === General.ALLOW_EDIT_POST_TIME_LIMIT) {
const timeLeft = (post.create_at + (config.PostEditTimeLimit * 1000)) - Date.now();
if (timeLeft <= 0) {
canEdit = false;
}
permissions = [Permissions.EDIT_OTHERS_POSTS];
}
let canEdit = permissions.every((permission) => haveIChannelPermission(state, {team: teamId, channel: channelId, permission, default: false}));
if (license.IsLicensed === 'true' && config.PostEditTimeLimit !== '-1' && config.PostEditTimeLimit !== -1) {
const timeLeft = (post.create_at + (config.PostEditTimeLimit * 1000)) - Date.now();
if (timeLeft <= 0) {
canEdit = false;
}
}

View file

@ -11,49 +11,38 @@ import {deleteChannel, getChannel, unarchiveChannel} from '@mm-redux/actions/cha
import {General} from '@mm-redux/constants';
import Permissions from '@mm-redux/constants/permissions';
import {getCurrentChannel, isCurrentChannelReadOnly} from '@mm-redux/selectors/entities/channels';
import {getConfig, getLicense, hasNewPermissions} from '@mm-redux/selectors/entities/general';
import {getConfig} from '@mm-redux/selectors/entities/general';
import {haveITeamPermission} from '@mm-redux/selectors/entities/roles';
import {getCurrentTeamId} from '@mm-redux/selectors/entities/teams';
import {getCurrentUser, getCurrentUserRoles} from '@mm-redux/selectors/entities/users';
import {getCurrentUser} from '@mm-redux/selectors/entities/users';
import {showDeleteOption} from '@mm-redux/utils/channel_utils';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {isAdmin as checkIsAdmin, isChannelAdmin as checkIsChannelAdmin, isSystemAdmin as checkIsSystemAdmin} from '@mm-redux/utils/user_utils';
import {isGuest as isUserGuest} from '@utils/users';
import Archive from './archive';
function mapStateToProps(state) {
const config = getConfig(state);
const license = getLicense(state);
const currentChannel = getCurrentChannel(state);
const currentUser = getCurrentUser(state);
const roles = getCurrentUserRoles(state) || '';
const isGuest = isUserGuest(currentUser);
const isDefaultChannel = currentChannel.name === General.DEFAULT_CHANNEL;
const isDirectMessage = currentChannel.type === General.DM_CHANNEL;
const isGroupMessage = currentChannel.type === General.GM_CHANNEL;
const isAdmin = checkIsAdmin(roles);
const isChannelAdmin = checkIsChannelAdmin(roles);
const isSystemAdmin = checkIsSystemAdmin(roles);
const canLeave = (!isDefaultChannel && !isDirectMessage && !isGroupMessage) || (isDefaultChannel && isGuest);
const canDelete = showDeleteOption(state, config, license, currentChannel, isAdmin, isSystemAdmin, isChannelAdmin);
const canDelete = showDeleteOption(state, currentChannel);
const isArchived = currentChannel?.delete_at > 0;
const canUnarchive = (isArchived && !isDirectMessage && !isGroupMessage);
const viewArchivedChannels = config.ExperimentalViewArchivedChannels === 'true';
const {serverVersion} = state.entities.general;
let isReadOnly = false;
if (currentUser?.id && currentChannel?.id) {
isReadOnly = isCurrentChannelReadOnly(state) || false;
}
let canUnarchiveChannel = false;
if (hasNewPermissions(state) && isMinimumServerVersion(serverVersion, 5, 20)) {
canUnarchiveChannel = haveITeamPermission(state, {
team: getCurrentTeamId(state),
permission: Permissions.MANAGE_TEAM,
});
}
const canUnarchiveChannel = haveITeamPermission(state, {
team: getCurrentTeamId(state),
permission: Permissions.MANAGE_TEAM,
});
return {
canArchive: (!isArchived && canLeave && canDelete && !isReadOnly),

View file

@ -6,11 +6,9 @@ import {connect} from 'react-redux';
import {convertChannelToPrivate} from '@mm-redux/actions/channels';
import {General, Permissions} from '@mm-redux/constants';
import {getCurrentChannel} from '@mm-redux/selectors/entities/channels';
import {getServerVersion} from '@mm-redux/selectors/entities/general';
import {haveIChannelPermission} from '@mm-redux/selectors/entities/roles';
import {getCurrentTeamId} from '@mm-redux/selectors/entities/teams';
import {getCurrentUserRoles} from '@mm-redux/selectors/entities/users';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {isAdmin as checkIsAdmin} from '@mm-redux/utils/user_utils';
import ConvertPrivate from './convert_private';
@ -24,18 +22,15 @@ function mapStateToProps(state) {
const isChannelConvertible = !isDefaultChannel && isPublicChannel;
const roles = getCurrentUserRoles(state) || '';
const isAdmin = checkIsAdmin(roles);
let canConvert = isChannelConvertible && isAdmin;
if (isMinimumServerVersion(getServerVersion(state), 5, 28)) {
canConvert = isChannelConvertible && haveIChannelPermission(
state,
{
channel: channelId,
team: currentTeamId,
permission: Permissions.CONVERT_PUBLIC_CHANNEL_TO_PRIVATE,
default: isAdmin,
},
);
}
const canConvert = isChannelConvertible && haveIChannelPermission(
state,
{
channel: channelId,
team: currentTeamId,
permission: Permissions.CONVERT_PUBLIC_CHANNEL_TO_PRIVATE,
default: isAdmin,
},
);
return {
canConvert,

View file

@ -4,29 +4,21 @@
import {connect} from 'react-redux';
import {getCurrentChannel, isCurrentChannelReadOnly} from '@mm-redux/selectors/entities/channels';
import {getConfig, getLicense} from '@mm-redux/selectors/entities/general';
import {getCurrentUserId, getCurrentUserRoles} from '@mm-redux/selectors/entities/users';
import {getCurrentUserId} from '@mm-redux/selectors/entities/users';
import {showManagementOptions} from '@mm-redux/utils/channel_utils';
import {isAdmin as checkIsAdmin, isChannelAdmin as checkIsChannelAdmin, isSystemAdmin as checkIsSystemAdmin} from '@mm-redux/utils/user_utils';
import EditChannel from './edit_channel';
function mapStateToProps(state) {
const config = getConfig(state);
const license = getLicense(state);
const currentChannel = getCurrentChannel(state);
const currentUserId = getCurrentUserId(state);
const roles = getCurrentUserRoles(state) || '';
const isAdmin = checkIsAdmin(roles);
const isChannelAdmin = checkIsChannelAdmin(roles);
const isSystemAdmin = checkIsSystemAdmin(roles);
let channelIsReadOnly = false;
if (currentUserId && currentChannel.id) {
channelIsReadOnly = isCurrentChannelReadOnly(state) || false;
}
const canEdit = !channelIsReadOnly && showManagementOptions(state, config, license, currentChannel, isAdmin, isSystemAdmin, isChannelAdmin);
const canEdit = !channelIsReadOnly && showManagementOptions(state, currentChannel);
return {
canEdit,

View file

@ -7,14 +7,12 @@ import {bindActionCreators} from 'redux';
import {setProfileImageUri, removeProfileImage, updateUser} from '@actions/views/edit_profile';
import {getConfig} from '@mm-redux/selectors/entities/general';
import {getTheme} from '@mm-redux/selectors/entities/preferences';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {isLandscape} from '@selectors/device';
import EditProfile from './edit_profile';
function mapStateToProps(state, ownProps) {
const config = getConfig(state);
const {serverVersion} = state.entities.general;
const {auth_service: service} = ownProps.currentUser;
const firstNameDisabled = (service === 'ldap' && config.LdapFirstNameAttributeSet === 'true') ||
@ -31,10 +29,7 @@ function mapStateToProps(state, ownProps) {
const positionDisabled = (service === 'ldap' && config.LdapPositionAttributeSet === 'true') ||
(service === 'saml' && config.SamlPositionAttributeSet === 'true');
let profilePictureDisabled = false;
if (isMinimumServerVersion(serverVersion, 5, 24)) {
profilePictureDisabled = (service === 'ldap' || service === 'saml') && config.LdapPictureAttributeSet === 'true';
}
const profilePictureDisabled = (service === 'ldap' || service === 'saml') && config.LdapPictureAttributeSet === 'true';
return {
firstNameDisabled,

View file

@ -7,13 +7,11 @@ import {bindActionCreators} from 'redux';
import {handleSelectChannel, setChannelDisplayName} from '@actions/views/channel';
import {getArchivedChannels, getChannels, getSharedChannels, joinChannel, searchChannels} from '@mm-redux/actions/channels';
import {General} from '@mm-redux/constants';
import {getConfig, getLicense} from '@mm-redux/selectors/entities/general';
import {getConfig} from '@mm-redux/selectors/entities/general';
import {getTheme} from '@mm-redux/selectors/entities/preferences';
import {getCurrentTeamId} from '@mm-redux/selectors/entities/teams';
import {getCurrentUserId, getCurrentUserRoles} from '@mm-redux/selectors/entities/users';
import {getCurrentUserId} from '@mm-redux/selectors/entities/users';
import {showCreateOption} from '@mm-redux/utils/channel_utils';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {isAdmin, isSystemAdmin} from '@mm-redux/utils/user_utils';
import {teamArchivedChannels, joinablePublicChannels, joinableSharedChannels} from '@selectors/channel';
import MoreChannels from './more_channels';
@ -22,18 +20,15 @@ const defaultSharedChannels = [];
function mapStateToProps(state) {
const config = getConfig(state);
const license = getLicense(state);
const sharedChannelsEnabled = config.ExperimentalSharedChannels === 'true';
const roles = getCurrentUserRoles(state);
const channels = joinablePublicChannels(state);
const sharedChannels = sharedChannelsEnabled ? joinableSharedChannels(state) : defaultSharedChannels;
const archivedChannels = teamArchivedChannels(state);
const currentTeamId = getCurrentTeamId(state);
const canShowArchivedChannels = config.ExperimentalViewArchivedChannels === 'true' &&
isMinimumServerVersion(state.entities.general.serverVersion, 5, 18);
const canShowArchivedChannels = config.ExperimentalViewArchivedChannels === 'true';
return {
canCreateChannels: showCreateOption(state, config, license, currentTeamId, General.OPEN_CHANNEL, isAdmin(roles), isSystemAdmin(roles)),
canCreateChannels: showCreateOption(state, currentTeamId, General.OPEN_CHANNEL),
currentUserId: getCurrentUserId(state),
currentTeamId,
channels,

View file

@ -12,7 +12,6 @@ import {getTheme} from '@mm-redux/selectors/entities/preferences';
import {getCurrentTeamId} from '@mm-redux/selectors/entities/teams';
import {isTimezoneEnabled} from '@mm-redux/selectors/entities/timezone';
import {getCurrentUser} from '@mm-redux/selectors/entities/users';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {getUserCurrentTimezone} from '@mm-redux/utils/timezone_utils';
import {makePreparePostIdsForSearchPosts} from '@selectors/post_list';
import {getDeviceUtcOffset, getUtcOffsetForTimeZone} from '@utils/timezone';
@ -45,9 +44,6 @@ function makeMapStateToProps() {
const userCurrentTimezone = enableTimezone ? getUserCurrentTimezone(currentUser.timezone) : '';
const timezoneOffsetInSeconds = (userCurrentTimezone.length > 0 ? getUtcOffsetForTimeZone(userCurrentTimezone) : getDeviceUtcOffset()) * 60;
const serverVersion = state.entities.general.serverVersion;
const enableDateSuggestion = isMinimumServerVersion(serverVersion, 5, 3);
const isSearchGettingMore = state.entities.search.isSearchGettingMore;
return {
@ -58,7 +54,6 @@ function makeMapStateToProps() {
recent: recent[currentTeamId],
isSearchGettingMore,
theme: getTheme(state),
enableDateSuggestion,
timezoneOffsetInSeconds,
viewArchivedChannels,
};

View file

@ -68,7 +68,6 @@ export default class Search extends PureComponent {
recent: PropTypes.array.isRequired,
isSearchGettingMore: PropTypes.bool.isRequired,
theme: PropTypes.object.isRequired,
enableDateSuggestion: PropTypes.bool,
timezoneOffsetInSeconds: PropTypes.number.isRequired,
viewArchivedChannels: PropTypes.bool,
};
@ -116,7 +115,6 @@ export default class Search extends PureComponent {
}
componentDidUpdate(prevProps, prevState) {
const {enableDateSuggestion} = this.props;
const {recent, didFail, isLoaded, status} = this.state;
const {status: prevStatus} = prevState;
const shouldScroll = prevStatus !== status &&
@ -127,7 +125,7 @@ export default class Search extends PureComponent {
setTimeout(() => {
const recentLabelsHeight = recent.length * RECENT_LABEL_HEIGHT;
const recentSeparatorsHeight = (recent.length - 1) * RECENT_SEPARATOR_HEIGHT;
const modifiersCount = enableDateSuggestion ? 5 : 2;
const modifiersCount = 5;
const modifiersHeight = modifiersCount * MODIFIER_LABEL_HEIGHT;
const modifiersSeparatorHeight = (modifiersCount - 1) * RECENT_SEPARATOR_HEIGHT;
const offset = modifiersHeight + modifiersSeparatorHeight + SECTION_HEIGHT + recentLabelsHeight + recentSeparatorsHeight;
@ -539,36 +537,33 @@ export default class Search extends PureComponent {
}),
}];
// if search by date filters supported
if (this.props.enableDateSuggestion) {
sectionsData.push({
value: 'on:',
testID: 'search.on_section',
modifier: 'YYYY-MM-DD',
description: intl.formatMessage({
id: 'mobile.search.on_modifier_description',
defaultMessage: 'to find posts on a specific date',
}),
});
sectionsData.push({
value: 'after:',
testID: 'search.after_section',
modifier: 'YYYY-MM-DD',
description: intl.formatMessage({
id: 'mobile.search.after_modifier_description',
defaultMessage: 'to find posts after a specific date',
}),
});
sectionsData.push({
value: 'before:',
testID: 'search.before_section',
modifier: 'YYYY-MM-DD',
description: intl.formatMessage({
id: 'mobile.search.before_modifier_description',
defaultMessage: 'to find posts before a specific date',
}),
});
}
sectionsData.push({
value: 'on:',
testID: 'search.on_section',
modifier: 'YYYY-MM-DD',
description: intl.formatMessage({
id: 'mobile.search.on_modifier_description',
defaultMessage: 'to find posts on a specific date',
}),
});
sectionsData.push({
value: 'after:',
testID: 'search.after_section',
modifier: 'YYYY-MM-DD',
description: intl.formatMessage({
id: 'mobile.search.after_modifier_description',
defaultMessage: 'to find posts after a specific date',
}),
});
sectionsData.push({
value: 'before:',
testID: 'search.before_section',
modifier: 'YYYY-MM-DD',
description: intl.formatMessage({
id: 'mobile.search.before_modifier_description',
defaultMessage: 'to find posts before a specific date',
}),
});
const sections = [{
data: sectionsData,
@ -714,7 +709,7 @@ export default class Search extends PureComponent {
onChangeText={this.handleTextChanged}
isSearch={true}
value={value}
enableDateSuggestion={this.props.enableDateSuggestion}
enableDateSuggestion={true}
/>
</KeyboardLayout>
);

View file

@ -8,7 +8,6 @@ import {updateMe} from '@mm-redux/actions/users';
import {getConfig} from '@mm-redux/selectors/entities/general';
import {getMyPreferences, getTheme, isCollapsedThreadsEnabled} from '@mm-redux/selectors/entities/preferences';
import {getCurrentUser, getStatusForUserId} from '@mm-redux/selectors/entities/users';
import {isMinimumServerVersion} from '@mm-redux/utils/helpers';
import {isLandscape} from '@selectors/device';
import NotificationSettings from './notification_settings';
@ -17,8 +16,7 @@ function mapStateToProps(state) {
const config = getConfig(state);
const currentUser = getCurrentUser(state) || {};
const currentUserStatus = getStatusForUserId(state, currentUser.id);
const serverVersion = state.entities.general.serverVersion;
const enableAutoResponder = isMinimumServerVersion(serverVersion, 4, 9) && config.ExperimentalEnableAutomaticReplies === 'true';
const enableAutoResponder = config.ExperimentalEnableAutomaticReplies === 'true';
return {
config,