@crowdin/app-project-module 0.72.0 → 0.73.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/middlewares/integration-credentials.js +6 -9
- package/out/modules/integration/handlers/invite-users.d.ts +3 -0
- package/out/modules/integration/handlers/invite-users.js +268 -0
- package/out/modules/integration/handlers/main.js +11 -5
- package/out/modules/integration/handlers/users.d.ts +13 -0
- package/out/modules/integration/handlers/users.js +102 -0
- package/out/modules/integration/index.js +14 -0
- package/out/modules/integration/types.d.ts +1 -0
- package/out/modules/integration/util/defaults.js +0 -48
- package/out/modules/manifest.js +4 -1
- package/out/static/css/styles.css +77 -0
- package/out/storage/index.d.ts +1 -0
- package/out/storage/mysql.d.ts +1 -0
- package/out/storage/mysql.js +10 -3
- package/out/storage/postgre.d.ts +1 -0
- package/out/storage/postgre.js +10 -3
- package/out/storage/sqlite.d.ts +2 -0
- package/out/storage/sqlite.js +28 -3
- package/out/types.d.ts +4 -2
- package/out/types.js +2 -0
- package/out/util/index.d.ts +1 -0
- package/out/util/index.js +12 -1
- package/out/views/main.handlebars +274 -14
- package/package.json +1 -1
|
@@ -53,7 +53,7 @@ function handle(config, integration, optional = false) {
|
|
|
53
53
|
if (projectIntegrationCredentials.length) {
|
|
54
54
|
for (const credentials of projectIntegrationCredentials) {
|
|
55
55
|
ownerIds.push(crowdinAppFunctions.parseCrowdinId(credentials.id).userId);
|
|
56
|
-
if (
|
|
56
|
+
if (checkUserAccessToIntegration(credentials, `${userId}`)) {
|
|
57
57
|
integrationCredentials = credentials;
|
|
58
58
|
clientId = credentials.id;
|
|
59
59
|
req.crowdinContext.clientId = clientId;
|
|
@@ -99,14 +99,11 @@ function handle(config, integration, optional = false) {
|
|
|
99
99
|
}
|
|
100
100
|
exports.default = handle;
|
|
101
101
|
function checkUserAccessToIntegration(integrationCredentials, userId) {
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
return false;
|
|
109
|
-
});
|
|
102
|
+
if (integrationCredentials === null || integrationCredentials === void 0 ? void 0 : integrationCredentials.managers) {
|
|
103
|
+
const managers = JSON.parse(integrationCredentials.managers);
|
|
104
|
+
return (managers || []).includes(userId);
|
|
105
|
+
}
|
|
106
|
+
return false;
|
|
110
107
|
}
|
|
111
108
|
function getIntegrationManagedBy(ownerIds, req) {
|
|
112
109
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/// <reference types="qs" />
|
|
2
|
+
import { Response } from 'express';
|
|
3
|
+
export default function handle(): (req: import("../../../types").CrowdinClientRequest | import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const util_1 = require("../../../util");
|
|
13
|
+
const types_1 = require("../../../types");
|
|
14
|
+
const storage_1 = require("../../../storage");
|
|
15
|
+
const users_1 = require("./users");
|
|
16
|
+
const CONSTANTS = {
|
|
17
|
+
MAX_EMAIL_LENGTH: 76,
|
|
18
|
+
MANAGER_ROLES: ['owner', 'manager'],
|
|
19
|
+
PROJECT_INTEGRATIONS_MODULE_TYPE: 'project-integrations',
|
|
20
|
+
};
|
|
21
|
+
function handle() {
|
|
22
|
+
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
const onlyCheck = req.body.onlyCheck;
|
|
24
|
+
const usersToInvite = req.body.users;
|
|
25
|
+
if (!Array.isArray(usersToInvite)) {
|
|
26
|
+
return res.status(400).send('Invalid request');
|
|
27
|
+
}
|
|
28
|
+
const client = req.crowdinApiClient;
|
|
29
|
+
const projectId = req.crowdinContext.jwtPayload.context.project_id;
|
|
30
|
+
const userId = req.crowdinContext.jwtPayload.context.user_id;
|
|
31
|
+
const { projectMembers, organizationMembers } = yield (0, users_1.getUsers)({ client, projectId });
|
|
32
|
+
const integrationCredentials = yield (0, storage_1.getStorage)().getIntegrationCredentials(req.crowdinContext.clientId);
|
|
33
|
+
let applicationInstallation = null;
|
|
34
|
+
try {
|
|
35
|
+
applicationInstallation = yield client.applicationsApi.getApplicationInstallation(req.crowdinContext.appIdentifier);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (error.code !== 403) {
|
|
39
|
+
console.error('Failed to get application installation', error);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const isAdmin = organizationMembers.some((member) => member.id === +userId && member.isAdmin) ||
|
|
43
|
+
projectMembers.some((member) => member.id === +userId && 'role' in member && member.role === 'owner');
|
|
44
|
+
const hasManagerAccess = isAdmin ||
|
|
45
|
+
projectMembers.some((member) => member.id === +userId &&
|
|
46
|
+
('role' in member ? CONSTANTS.MANAGER_ROLES.includes(member.role) : member.isManager));
|
|
47
|
+
if (!hasManagerAccess) {
|
|
48
|
+
return res.status(403).send({ error: 'Access denied' });
|
|
49
|
+
}
|
|
50
|
+
const usersWhoWillBeInvitedToOrganization = filterOrganizationUsers(usersToInvite);
|
|
51
|
+
const usersWhoWillBeInvitedToProject = filterProjectUsers({
|
|
52
|
+
usersToInvite,
|
|
53
|
+
projectMembers,
|
|
54
|
+
organizationMembers,
|
|
55
|
+
});
|
|
56
|
+
const usersWhoNotIssetInApplicationInstallation = filterNotIssetApplicationUsers({
|
|
57
|
+
usersToInvite,
|
|
58
|
+
applicationInstallation,
|
|
59
|
+
projectMembers,
|
|
60
|
+
organizationMembers,
|
|
61
|
+
});
|
|
62
|
+
const usersWhoNotIssetInIntegration = filterNotIssetIntegrationUsers({
|
|
63
|
+
usersToInvite,
|
|
64
|
+
integrationCredentials,
|
|
65
|
+
projectMembers,
|
|
66
|
+
organizationMembers,
|
|
67
|
+
});
|
|
68
|
+
if (onlyCheck) {
|
|
69
|
+
return res.send({
|
|
70
|
+
data: {
|
|
71
|
+
isAdmin,
|
|
72
|
+
editApplicationAvailable: applicationInstallation !== null,
|
|
73
|
+
usersWhoWillBeInvitedToOrganization,
|
|
74
|
+
usersWhoWillBeInvitedToProject,
|
|
75
|
+
usersWhoNotIssetInApplicationInstallation,
|
|
76
|
+
usersWhoNotIssetInIntegration,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const response = yield inviteUsers({
|
|
81
|
+
req,
|
|
82
|
+
projectId,
|
|
83
|
+
isAdmin,
|
|
84
|
+
usersToInvite,
|
|
85
|
+
applicationInstallation,
|
|
86
|
+
usersWhoWillBeInvitedToOrganization,
|
|
87
|
+
usersWhoWillBeInvitedToProject,
|
|
88
|
+
usersWhoNotIssetInApplicationInstallation,
|
|
89
|
+
});
|
|
90
|
+
return res.send(response);
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
exports.default = handle;
|
|
94
|
+
function filterOrganizationUsers(usersToInvite) {
|
|
95
|
+
return usersToInvite.filter((identifier) => (0, util_1.validateEmail)(identifier)).map((name) => ({ name: `${name}` }));
|
|
96
|
+
}
|
|
97
|
+
function filterProjectUsers({ usersToInvite, projectMembers, organizationMembers, }) {
|
|
98
|
+
return usersToInvite
|
|
99
|
+
.map((identifier) => {
|
|
100
|
+
if ((0, util_1.validateEmail)(identifier)) {
|
|
101
|
+
return { name: `${identifier}` };
|
|
102
|
+
}
|
|
103
|
+
const user = projectMembers.find((member) => member.id === +identifier);
|
|
104
|
+
if (!user) {
|
|
105
|
+
const organizationUser = organizationMembers.find((member) => member.id === +identifier);
|
|
106
|
+
return organizationUser && !organizationUser.isAdmin
|
|
107
|
+
? { id: organizationUser.id, name: (0, users_1.getUserFullName)(organizationUser) }
|
|
108
|
+
: null;
|
|
109
|
+
}
|
|
110
|
+
return ('role' in user ? !CONSTANTS.MANAGER_ROLES.includes(user.role) : !user.isManager)
|
|
111
|
+
? { id: user.id, name: (0, users_1.getUserFullName)(user) }
|
|
112
|
+
: null;
|
|
113
|
+
})
|
|
114
|
+
.filter(Boolean);
|
|
115
|
+
}
|
|
116
|
+
function filterNotIssetApplicationUsers({ usersToInvite, applicationInstallation, projectMembers, organizationMembers, }) {
|
|
117
|
+
let userIdentifiers = [];
|
|
118
|
+
if (!applicationInstallation) {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
const projectIntegrationModules = applicationInstallation.data.modules.filter((module) => module.type === CONSTANTS.PROJECT_INTEGRATIONS_MODULE_TYPE);
|
|
122
|
+
if (!projectIntegrationModules.length) {
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
if (projectIntegrationModules.some((module) => module.permissions.user.value === types_1.UserPermissions.ALL_MEMBERS)) {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
userIdentifiers = usersToInvite.filter((userId) => projectIntegrationModules.every((module) => !module.permissions.user.ids.includes(+userId)));
|
|
130
|
+
}
|
|
131
|
+
return userIdentifiers
|
|
132
|
+
.map((identifier) => {
|
|
133
|
+
if ((0, util_1.validateEmail)(identifier)) {
|
|
134
|
+
return { name: `${identifier}` };
|
|
135
|
+
}
|
|
136
|
+
let user;
|
|
137
|
+
user = projectMembers.find((member) => member.id === +identifier);
|
|
138
|
+
if (!user) {
|
|
139
|
+
user = organizationMembers.find((member) => member.id === +identifier);
|
|
140
|
+
}
|
|
141
|
+
return user ? { id: user.id, name: (0, users_1.getUserFullName)(user) } : null;
|
|
142
|
+
})
|
|
143
|
+
.filter(Boolean);
|
|
144
|
+
}
|
|
145
|
+
function filterNotIssetIntegrationUsers({ usersToInvite, integrationCredentials, projectMembers, organizationMembers, }) {
|
|
146
|
+
let integrationManagers = [];
|
|
147
|
+
if (integrationCredentials === null || integrationCredentials === void 0 ? void 0 : integrationCredentials.managers) {
|
|
148
|
+
integrationManagers = JSON.parse(integrationCredentials.managers);
|
|
149
|
+
}
|
|
150
|
+
return usersToInvite
|
|
151
|
+
.map((identifier) => {
|
|
152
|
+
if (integrationManagers.includes(`${identifier}`)) {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
if ((0, util_1.validateEmail)(identifier)) {
|
|
156
|
+
return { name: `${identifier}` };
|
|
157
|
+
}
|
|
158
|
+
let user;
|
|
159
|
+
user = projectMembers.find((member) => member.id === +identifier);
|
|
160
|
+
if (!user) {
|
|
161
|
+
user = organizationMembers.find((member) => member.id === +identifier);
|
|
162
|
+
}
|
|
163
|
+
return user ? { id: user.id, name: (0, users_1.getUserFullName)(user) } : null;
|
|
164
|
+
})
|
|
165
|
+
.filter(Boolean);
|
|
166
|
+
}
|
|
167
|
+
function inviteUsers({ req, projectId, isAdmin, usersToInvite, applicationInstallation, usersWhoWillBeInvitedToOrganization, usersWhoWillBeInvitedToProject, usersWhoNotIssetInApplicationInstallation, }) {
|
|
168
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
169
|
+
const client = req.crowdinApiClient;
|
|
170
|
+
const alreadyAddedUserIds = yield inviteUsersToProject({
|
|
171
|
+
client,
|
|
172
|
+
projectId,
|
|
173
|
+
usersWhoWillBeInvitedToOrganization,
|
|
174
|
+
usersWhoWillBeInvitedToProject,
|
|
175
|
+
});
|
|
176
|
+
if (isAdmin) {
|
|
177
|
+
yield addUsersToApplicationInstallation({
|
|
178
|
+
client,
|
|
179
|
+
ownerId: req.integrationCredentials.ownerId,
|
|
180
|
+
applicationInstallation,
|
|
181
|
+
alreadyAddedUserIds,
|
|
182
|
+
usersWhoNotIssetInApplicationInstallation,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
yield addUsersToIntegration({
|
|
186
|
+
clientId: req.crowdinContext.clientId,
|
|
187
|
+
alreadyAddedUserIds,
|
|
188
|
+
usersToInvite,
|
|
189
|
+
});
|
|
190
|
+
return { success: true };
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
function inviteUsersToProject({ client, projectId, usersWhoWillBeInvitedToOrganization, usersWhoWillBeInvitedToProject, }) {
|
|
194
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
195
|
+
let addedIds = [];
|
|
196
|
+
const emailInvites = usersWhoWillBeInvitedToOrganization.map((user) => user.name);
|
|
197
|
+
const userIdInvites = usersWhoWillBeInvitedToProject.map((user) => user.id).filter(Boolean);
|
|
198
|
+
try {
|
|
199
|
+
if (emailInvites.length) {
|
|
200
|
+
const inviteResponse = (yield client.usersApi.addProjectMember(projectId, {
|
|
201
|
+
managerAccess: true,
|
|
202
|
+
emails: emailInvites,
|
|
203
|
+
})); // TODO: fix typings in the @crowdin/crowdin-api-client
|
|
204
|
+
addedIds = inviteResponse.data.added.map((member) => member.id);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
console.error('Failed to invite users', error);
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
if (userIdInvites.length) {
|
|
212
|
+
yield client.usersApi.addProjectMember(projectId, {
|
|
213
|
+
managerAccess: true,
|
|
214
|
+
userIds: userIdInvites,
|
|
215
|
+
}); // TODO: fix typings in the @crowdin/crowdin-api-client
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
console.error('Failed to grant project manager access', error);
|
|
220
|
+
}
|
|
221
|
+
return addedIds;
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
function addUsersToApplicationInstallation({ client, ownerId, applicationInstallation, alreadyAddedUserIds, usersWhoNotIssetInApplicationInstallation, }) {
|
|
225
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
226
|
+
if (!applicationInstallation || !usersWhoNotIssetInApplicationInstallation.length) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const applicationInvites = [
|
|
230
|
+
ownerId,
|
|
231
|
+
...alreadyAddedUserIds,
|
|
232
|
+
...usersWhoNotIssetInApplicationInstallation.map((user) => user.id).filter(Boolean),
|
|
233
|
+
];
|
|
234
|
+
const permissions = applicationInstallation.data.modules.filter((module) => module.type === CONSTANTS.PROJECT_INTEGRATIONS_MODULE_TYPE);
|
|
235
|
+
if (!permissions.length) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
for (const module of permissions) {
|
|
240
|
+
if (module.permissions.user.value === types_1.UserPermissions.ALL_MEMBERS) {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
const userIds = [...new Set([...module.permissions.user.ids, ...applicationInvites])];
|
|
244
|
+
client.applicationsApi.editApplicationInstallation(applicationInstallation.data.identifier, [
|
|
245
|
+
{
|
|
246
|
+
op: 'replace',
|
|
247
|
+
path: `/modules/${module.key}/permissions`,
|
|
248
|
+
value: {
|
|
249
|
+
user: {
|
|
250
|
+
value: types_1.UserPermissions.RESTRICTED,
|
|
251
|
+
ids: userIds,
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
]);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
console.error('Failed to update application permissions', error);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
function addUsersToIntegration({ clientId, alreadyAddedUserIds, usersToInvite, }) {
|
|
264
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
265
|
+
const integrationInvites = [...alreadyAddedUserIds, ...usersToInvite.filter((identifier) => +identifier)];
|
|
266
|
+
yield (0, storage_1.getStorage)().updateIntegrationManagers(clientId, JSON.stringify(integrationInvites.map((id) => `${id}`)));
|
|
267
|
+
});
|
|
268
|
+
}
|
|
@@ -39,7 +39,7 @@ const defaults_1 = require("../util/defaults");
|
|
|
39
39
|
const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions"));
|
|
40
40
|
function handle(config, integration) {
|
|
41
41
|
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
42
|
-
var _a, _b;
|
|
42
|
+
var _a, _b, _c;
|
|
43
43
|
const logger = req.logInfo || logger_1.log;
|
|
44
44
|
const installed = !!req.crowdinApiClient;
|
|
45
45
|
const loggedIn = !!req.integrationCredentials;
|
|
@@ -66,12 +66,18 @@ function handle(config, integration) {
|
|
|
66
66
|
}
|
|
67
67
|
else if (integration.getConfiguration) {
|
|
68
68
|
const { userId } = crowdinAppFunctions.parseCrowdinId(req.crowdinContext.clientId);
|
|
69
|
-
options.
|
|
70
|
-
|
|
71
|
-
options.configurationFields = configurationFields;
|
|
69
|
+
options.hasOrganization = !!req.crowdinContext.jwtPayload.domain;
|
|
70
|
+
options.isOwner = req.integrationCredentials.ownerId === userId;
|
|
72
71
|
options.config = JSON.stringify(req.integrationSettings || {});
|
|
73
72
|
options.reloadOnConfigSave = !!integration.reloadOnConfigSave;
|
|
74
73
|
options.integrationPagination = integration.integrationPagination;
|
|
74
|
+
if ((_b = req.query) === null || _b === void 0 ? void 0 : _b.parentUrl) {
|
|
75
|
+
const parentUrl = new URL(req.query.parentUrl);
|
|
76
|
+
parentUrl.searchParams.set('zen-mode', 'true');
|
|
77
|
+
options.zenModeUrl = parentUrl.toString();
|
|
78
|
+
}
|
|
79
|
+
const configurationFields = yield integration.getConfiguration(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, req.integrationCredentials);
|
|
80
|
+
options.configurationFields = configurationFields;
|
|
75
81
|
logger(`Adding configuration fields ${JSON.stringify(configurationFields, null, 2)}`);
|
|
76
82
|
}
|
|
77
83
|
options.infoModal = integration.infoModal;
|
|
@@ -96,7 +102,7 @@ function handle(config, integration) {
|
|
|
96
102
|
: null;
|
|
97
103
|
options.notice = integration.notice;
|
|
98
104
|
options.asyncProgress = {
|
|
99
|
-
checkInterval: ((
|
|
105
|
+
checkInterval: ((_c = integration.asyncProgress) === null || _c === void 0 ? void 0 : _c.checkInterval) || 1000,
|
|
100
106
|
};
|
|
101
107
|
logger(`Routing user to ${view} view`);
|
|
102
108
|
return res.render(view, options);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/// <reference types="qs" />
|
|
2
|
+
import Crowdin, { UsersModel } from '@crowdin/crowdin-api-client';
|
|
3
|
+
import { Response } from 'express';
|
|
4
|
+
export type ProjectMember = UsersModel.ProjectMember | UsersModel.EnterpriseProjectMember;
|
|
5
|
+
export default function handle(): (req: import("../../../types").CrowdinClientRequest | import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
|
|
6
|
+
export declare function getUsers({ client, projectId }: {
|
|
7
|
+
client: Crowdin;
|
|
8
|
+
projectId: number;
|
|
9
|
+
}): Promise<{
|
|
10
|
+
projectMembers: ProjectMember[];
|
|
11
|
+
organizationMembers: UsersModel.User[];
|
|
12
|
+
}>;
|
|
13
|
+
export declare function getUserFullName(user: UsersModel.User | ProjectMember): string;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
+
exports.getUserFullName = exports.getUsers = void 0;
|
|
36
|
+
const util_1 = require("../../../util");
|
|
37
|
+
const storage_1 = require("../../../storage");
|
|
38
|
+
const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions"));
|
|
39
|
+
function handle() {
|
|
40
|
+
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
41
|
+
const client = req.crowdinApiClient;
|
|
42
|
+
const ownerId = req.integrationCredentials.ownerId;
|
|
43
|
+
const { projectId } = crowdinAppFunctions.parseCrowdinId(req.crowdinContext.clientId);
|
|
44
|
+
const { projectMembers, organizationMembers } = yield getUsers({ client, projectId });
|
|
45
|
+
const uniqueUsers = new Map();
|
|
46
|
+
[...projectMembers, ...organizationMembers].forEach((manager) => {
|
|
47
|
+
if (manager.id !== ownerId && !uniqueUsers.has(manager.id)) {
|
|
48
|
+
uniqueUsers.set(manager.id, manager);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
const sortedUsers = Array.from(uniqueUsers.values())
|
|
52
|
+
.sort((a, b) => {
|
|
53
|
+
const aValue = a.firstName || a.username || '';
|
|
54
|
+
const bValue = b.firstName || b.username || '';
|
|
55
|
+
return aValue.localeCompare(bValue);
|
|
56
|
+
})
|
|
57
|
+
.map((user) => ({
|
|
58
|
+
id: user.id.toString(),
|
|
59
|
+
name: getUserFullName(user),
|
|
60
|
+
}));
|
|
61
|
+
let integrationManagers = [];
|
|
62
|
+
const integrationCredentials = yield (0, storage_1.getStorage)().getIntegrationCredentials(req.crowdinContext.clientId);
|
|
63
|
+
if (integrationCredentials === null || integrationCredentials === void 0 ? void 0 : integrationCredentials.managers) {
|
|
64
|
+
integrationManagers = JSON.parse(integrationCredentials.managers);
|
|
65
|
+
}
|
|
66
|
+
res.json({
|
|
67
|
+
data: {
|
|
68
|
+
users: sortedUsers,
|
|
69
|
+
managers: integrationManagers,
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
exports.default = handle;
|
|
75
|
+
function getUsers({ client, projectId }) {
|
|
76
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
77
|
+
const users = {
|
|
78
|
+
projectMembers: [],
|
|
79
|
+
organizationMembers: [],
|
|
80
|
+
};
|
|
81
|
+
const projectMembers = (yield client.usersApi.withFetchAll().listProjectMembers(projectId)).data;
|
|
82
|
+
users.projectMembers = projectMembers.map((members) => members.data);
|
|
83
|
+
if (client.organization) {
|
|
84
|
+
try {
|
|
85
|
+
const admins = (yield client.usersApi.withFetchAll().listUsers()).data;
|
|
86
|
+
users.organizationMembers = admins.map((admin) => admin.data);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
console.error('Failed to get organization users', error);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return users;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
exports.getUsers = getUsers;
|
|
96
|
+
function getUserFullName(user) {
|
|
97
|
+
const ownerFullName = 'fullName' in user ? user.fullName : ((user.firstName || '') + ' ' + (user.lastName || '')).trim();
|
|
98
|
+
return !!ownerFullName && (user === null || user === void 0 ? void 0 : user.username) !== ownerFullName
|
|
99
|
+
? `${ownerFullName} (${user === null || user === void 0 ? void 0 : user.username})`
|
|
100
|
+
: user === null || user === void 0 ? void 0 : user.username;
|
|
101
|
+
}
|
|
102
|
+
exports.getUserFullName = getUserFullName;
|
|
@@ -64,6 +64,8 @@ const subscription_info_1 = __importDefault(require("./handlers/subscription-inf
|
|
|
64
64
|
const sync_settings_1 = __importDefault(require("./handlers/sync-settings"));
|
|
65
65
|
const sync_settings_save_1 = __importDefault(require("./handlers/sync-settings-save"));
|
|
66
66
|
const user_errors_1 = __importDefault(require("./handlers/user-errors"));
|
|
67
|
+
const users_1 = __importDefault(require("./handlers/users"));
|
|
68
|
+
const invite_users_1 = __importDefault(require("./handlers/invite-users"));
|
|
67
69
|
const cron_1 = require("./util/cron");
|
|
68
70
|
const storage_1 = require("../../storage");
|
|
69
71
|
function register({ config, app }) {
|
|
@@ -222,6 +224,18 @@ function register({ config, app }) {
|
|
|
222
224
|
checkSubscriptionExpiration: true,
|
|
223
225
|
moduleKey: integrationLogic.key,
|
|
224
226
|
}), (0, integration_credentials_1.default)(config, integrationLogic), (0, user_errors_1.default)());
|
|
227
|
+
app.get('/api/users', json_response_1.default, (0, crowdin_client_1.default)({
|
|
228
|
+
config,
|
|
229
|
+
optional: false,
|
|
230
|
+
checkSubscriptionExpiration: true,
|
|
231
|
+
moduleKey: integrationLogic.key,
|
|
232
|
+
}), (0, integration_credentials_1.default)(config, integrationLogic), (0, users_1.default)());
|
|
233
|
+
app.post('/api/invite-users', json_response_1.default, (0, crowdin_client_1.default)({
|
|
234
|
+
config,
|
|
235
|
+
optional: false,
|
|
236
|
+
checkSubscriptionExpiration: true,
|
|
237
|
+
moduleKey: integrationLogic.key,
|
|
238
|
+
}), (0, integration_credentials_1.default)(config, integrationLogic), (0, invite_users_1.default)());
|
|
225
239
|
cron.schedule('0 0 1 * *', () => (0, cron_1.removeFinishedJobs)());
|
|
226
240
|
}
|
|
227
241
|
exports.register = register;
|
|
@@ -147,27 +147,6 @@ function applyIntegrationModuleDefaults(config, integration) {
|
|
|
147
147
|
fields = yield getUserSettings(projectId, crowdinClient, integrationCredentials);
|
|
148
148
|
}
|
|
149
149
|
const defaultSettings = [];
|
|
150
|
-
const mangers = yield getManagers(crowdinClient, projectId, integrationCredentials.ownerId);
|
|
151
|
-
if (mangers.length) {
|
|
152
|
-
defaultSettings.push({
|
|
153
|
-
key: 'managers',
|
|
154
|
-
label: 'Managers',
|
|
155
|
-
type: 'select',
|
|
156
|
-
isMulti: true,
|
|
157
|
-
isSearchable: true,
|
|
158
|
-
options: mangers.map((manager) => {
|
|
159
|
-
const ownerFullName = 'fullName' in manager
|
|
160
|
-
? manager.fullName
|
|
161
|
-
: ((manager.firstName || '') + ' ' + (manager.lastName || '')).trim();
|
|
162
|
-
return {
|
|
163
|
-
value: manager.id.toString(),
|
|
164
|
-
label: !!ownerFullName && (manager === null || manager === void 0 ? void 0 : manager.username) !== ownerFullName
|
|
165
|
-
? `${ownerFullName} (${manager === null || manager === void 0 ? void 0 : manager.username})`
|
|
166
|
-
: manager === null || manager === void 0 ? void 0 : manager.username,
|
|
167
|
-
};
|
|
168
|
-
}),
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
150
|
if (project.data.inContext) {
|
|
172
151
|
defaultSettings.push({
|
|
173
152
|
key: 'inContext',
|
|
@@ -320,30 +299,3 @@ function getOAuthLoginFormId(clientId) {
|
|
|
320
299
|
return `oauth_form_${clientId}`;
|
|
321
300
|
}
|
|
322
301
|
exports.getOAuthLoginFormId = getOAuthLoginFormId;
|
|
323
|
-
function getManagers(client, projectId, ownerId) {
|
|
324
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
325
|
-
const managers = [];
|
|
326
|
-
if (client.organization) {
|
|
327
|
-
try {
|
|
328
|
-
const admins = (yield client.usersApi.withFetchAll().listUsers()).data.filter((user) => user.data.isAdmin);
|
|
329
|
-
managers.push(...admins.map((admin) => admin.data));
|
|
330
|
-
}
|
|
331
|
-
catch (e) {
|
|
332
|
-
console.error('Failed to get organization users', e);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
const projectMembers = (yield client.usersApi.withFetchAll().listProjectMembers(projectId)).data.filter((user) => 'role' in user.data ? ['owner', 'manager'].includes(user.data.role) : user.data.isManager);
|
|
336
|
-
managers.push(...projectMembers.map((members) => members.data));
|
|
337
|
-
const uniqueManagers = new Map();
|
|
338
|
-
managers.forEach((manager) => {
|
|
339
|
-
if (manager.id !== ownerId && !uniqueManagers.has(manager.id)) {
|
|
340
|
-
uniqueManagers.set(manager.id, manager);
|
|
341
|
-
}
|
|
342
|
-
});
|
|
343
|
-
return Array.from(uniqueManagers.values()).sort((a, b) => {
|
|
344
|
-
const aValue = a.firstName || a.username || '';
|
|
345
|
-
const bValue = b.firstName || b.username || '';
|
|
346
|
-
return aValue.localeCompare(bValue);
|
|
347
|
-
});
|
|
348
|
-
});
|
|
349
|
-
}
|
package/out/modules/manifest.js
CHANGED
|
@@ -285,11 +285,14 @@ function handle(config) {
|
|
|
285
285
|
if (!(0, subscription_1.isAppFree)(config)) {
|
|
286
286
|
events['subscription_paid'] = '/subscription-paid';
|
|
287
287
|
}
|
|
288
|
+
const defaultScopes = config.projectIntegration
|
|
289
|
+
? [types_1.Scope.USERS, types_1.Scope.PROJECTS, types_1.Scope.APPLICATIONS]
|
|
290
|
+
: [types_1.Scope.PROJECTS];
|
|
288
291
|
return (_req, res) => {
|
|
289
292
|
const manifest = Object.assign(Object.assign(Object.assign(Object.assign({ identifier: config.identifier, name: config.name, logo: (0, util_1.getLogoUrl)(), baseUrl: config.baseUrl, authentication: {
|
|
290
293
|
type: config.authenticationType || types_1.AuthenticationType.APP,
|
|
291
294
|
clientId: config.clientId,
|
|
292
|
-
} }, (config.agent && { agent: config.agent })), { events, scopes: config.scopes ? config.scopes :
|
|
295
|
+
} }, (config.agent && { agent: config.agent })), { events, scopes: config.scopes ? config.scopes : defaultScopes }), (config.defaultPermissions && { default_permissions: config.defaultPermissions })), { modules });
|
|
293
296
|
res.send(manifest);
|
|
294
297
|
};
|
|
295
298
|
}
|
|
@@ -67,6 +67,10 @@
|
|
|
67
67
|
margin: 0;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
.m-2 {
|
|
71
|
+
margin: 16px;
|
|
72
|
+
}
|
|
73
|
+
|
|
70
74
|
.info-text {
|
|
71
75
|
max-width: 800px;
|
|
72
76
|
}
|
|
@@ -259,3 +263,76 @@
|
|
|
259
263
|
display: none;
|
|
260
264
|
}
|
|
261
265
|
}
|
|
266
|
+
|
|
267
|
+
.confirm-users-block .flex > div {
|
|
268
|
+
width: 40%;
|
|
269
|
+
margin-bottom: 8px
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.confirm-users-block crowdin-p {
|
|
273
|
+
line-height: 1;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
table {
|
|
277
|
+
width: 100%;
|
|
278
|
+
border-collapse: separate;
|
|
279
|
+
border-spacing: 0;
|
|
280
|
+
}
|
|
281
|
+
th, td {
|
|
282
|
+
padding: 16px;
|
|
283
|
+
text-align: left;
|
|
284
|
+
border-bottom: 1px solid var(--crowdin-border-color);
|
|
285
|
+
}
|
|
286
|
+
th {
|
|
287
|
+
font-size: 14px;
|
|
288
|
+
}
|
|
289
|
+
.permission-description {
|
|
290
|
+
font-size: 12px;
|
|
291
|
+
color: var(--crowdin-text-muted);
|
|
292
|
+
margin-top: 4px;
|
|
293
|
+
}
|
|
294
|
+
.affected-users {
|
|
295
|
+
ul {
|
|
296
|
+
list-style: none;
|
|
297
|
+
padding: 0;
|
|
298
|
+
margin: 0;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
li {
|
|
302
|
+
font-size: 14px;
|
|
303
|
+
|
|
304
|
+
&:not(:last-child) {
|
|
305
|
+
margin-bottom: 4px;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.badge {
|
|
311
|
+
display: inline-block;
|
|
312
|
+
padding: 4px 12px;
|
|
313
|
+
border-radius: 15px;
|
|
314
|
+
font-size: 12px;
|
|
315
|
+
font-weight: 500;
|
|
316
|
+
text-align: center;
|
|
317
|
+
position: relative;
|
|
318
|
+
cursor: default;
|
|
319
|
+
}
|
|
320
|
+
.badge-granted {
|
|
321
|
+
background-color: var(--crowdin-success);
|
|
322
|
+
color: var(--crowdin-white);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.badge-will-be-granted {
|
|
326
|
+
background-color: var(--crowdin-warning-bg);
|
|
327
|
+
color: var(--crowdin-warning);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.badge-not-available {
|
|
331
|
+
background-color: var(--crowdin-danger);
|
|
332
|
+
color: var(--crowdin-white);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.status {
|
|
336
|
+
position: relative;
|
|
337
|
+
text-align: right;
|
|
338
|
+
}
|
package/out/storage/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export interface Storage {
|
|
|
11
11
|
deleteCrowdinCredentials(id: string): Promise<void>;
|
|
12
12
|
saveIntegrationCredentials(id: string, credentials: any, crowdinId: string): Promise<void>;
|
|
13
13
|
updateIntegrationCredentials(id: string, credentials: any): Promise<void>;
|
|
14
|
+
updateIntegrationManagers(id: string, managers: any): Promise<void>;
|
|
14
15
|
getIntegrationCredentials(id: string): Promise<IntegrationCredentials | undefined>;
|
|
15
16
|
getAllIntegrationCredentials(crowdinId: string): Promise<IntegrationCredentials[]>;
|
|
16
17
|
deleteIntegrationCredentials(id: string): Promise<void>;
|
package/out/storage/mysql.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export declare class MySQLStorage implements Storage {
|
|
|
28
28
|
deleteCrowdinCredentials(id: string): Promise<void>;
|
|
29
29
|
saveIntegrationCredentials(id: string, credentials: any, crowdinId: string): Promise<void>;
|
|
30
30
|
updateIntegrationCredentials(id: string, credentials: any): Promise<void>;
|
|
31
|
+
updateIntegrationManagers(id: string, managers: any): Promise<void>;
|
|
31
32
|
getIntegrationCredentials(id: string): Promise<IntegrationCredentials | undefined>;
|
|
32
33
|
getAllIntegrationCredentials(crowdinId: string): Promise<IntegrationCredentials[]>;
|
|
33
34
|
deleteIntegrationCredentials(id: string): Promise<void>;
|
package/out/storage/mysql.js
CHANGED
|
@@ -84,7 +84,8 @@ class MySQLStorage {
|
|
|
84
84
|
(
|
|
85
85
|
id varchar(255) primary key,
|
|
86
86
|
credentials text,
|
|
87
|
-
crowdin_id varchar(255) not null
|
|
87
|
+
crowdin_id varchar(255) not null,
|
|
88
|
+
managers text
|
|
88
89
|
)
|
|
89
90
|
`);
|
|
90
91
|
yield connection.execute(`
|
|
@@ -266,11 +267,17 @@ class MySQLStorage {
|
|
|
266
267
|
yield this.executeQuery((connection) => connection.execute('UPDATE integration_credentials SET credentials = ? WHERE id = ?', [credentials, id]));
|
|
267
268
|
});
|
|
268
269
|
}
|
|
270
|
+
updateIntegrationManagers(id, managers) {
|
|
271
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
272
|
+
yield this.dbPromise;
|
|
273
|
+
yield this.executeQuery((connection) => connection.execute('UPDATE integration_credentials SET managers = ? WHERE id = ?', [managers, id]));
|
|
274
|
+
});
|
|
275
|
+
}
|
|
269
276
|
getIntegrationCredentials(id) {
|
|
270
277
|
return __awaiter(this, void 0, void 0, function* () {
|
|
271
278
|
yield this.dbPromise;
|
|
272
279
|
return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () {
|
|
273
|
-
const [rows] = yield connection.execute('SELECT id, credentials, crowdin_id as "crowdinId" FROM integration_credentials WHERE id = ?', [id]);
|
|
280
|
+
const [rows] = yield connection.execute('SELECT id, credentials, crowdin_id as "crowdinId", managers FROM integration_credentials WHERE id = ?', [id]);
|
|
274
281
|
return (rows || [])[0];
|
|
275
282
|
}));
|
|
276
283
|
});
|
|
@@ -279,7 +286,7 @@ class MySQLStorage {
|
|
|
279
286
|
return __awaiter(this, void 0, void 0, function* () {
|
|
280
287
|
yield this.dbPromise;
|
|
281
288
|
return this.executeQuery((connection) => __awaiter(this, void 0, void 0, function* () {
|
|
282
|
-
const [rows] = yield connection.execute('SELECT id, credentials, crowdin_id as "crowdinId" FROM integration_credentials WHERE crowdin_id = ?', [crowdinId]);
|
|
289
|
+
const [rows] = yield connection.execute('SELECT id, credentials, crowdin_id as "crowdinId", managers FROM integration_credentials WHERE crowdin_id = ?', [crowdinId]);
|
|
283
290
|
return rows || [];
|
|
284
291
|
}));
|
|
285
292
|
});
|
package/out/storage/postgre.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export declare class PostgreStorage implements Storage {
|
|
|
35
35
|
deleteCrowdinCredentials(id: string): Promise<void>;
|
|
36
36
|
saveIntegrationCredentials(id: string, credentials: any, crowdinId: string): Promise<void>;
|
|
37
37
|
updateIntegrationCredentials(id: string, credentials: any): Promise<void>;
|
|
38
|
+
updateIntegrationManagers(id: string, managers: any): Promise<void>;
|
|
38
39
|
getIntegrationCredentials(id: string): Promise<IntegrationCredentials | undefined>;
|
|
39
40
|
getAllIntegrationCredentials(crowdinId: string): Promise<IntegrationCredentials[]>;
|
|
40
41
|
deleteIntegrationCredentials(id: string): Promise<void>;
|
package/out/storage/postgre.js
CHANGED
|
@@ -112,7 +112,8 @@ class PostgreStorage {
|
|
|
112
112
|
(
|
|
113
113
|
id varchar primary key,
|
|
114
114
|
credentials varchar,
|
|
115
|
-
crowdin_id varchar not null
|
|
115
|
+
crowdin_id varchar not null,
|
|
116
|
+
managers varchar
|
|
116
117
|
)
|
|
117
118
|
`);
|
|
118
119
|
yield client.query(`
|
|
@@ -293,11 +294,17 @@ class PostgreStorage {
|
|
|
293
294
|
yield this.executeQuery((client) => client.query('UPDATE integration_credentials SET credentials = $1 WHERE id = $2', [credentials, id]));
|
|
294
295
|
});
|
|
295
296
|
}
|
|
297
|
+
updateIntegrationManagers(id, managers) {
|
|
298
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
299
|
+
yield this.dbPromise;
|
|
300
|
+
yield this.executeQuery((client) => client.query('UPDATE integration_credentials SET managers = $1 WHERE id = $2', [managers, id]));
|
|
301
|
+
});
|
|
302
|
+
}
|
|
296
303
|
getIntegrationCredentials(id) {
|
|
297
304
|
return __awaiter(this, void 0, void 0, function* () {
|
|
298
305
|
yield this.dbPromise;
|
|
299
306
|
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
|
|
300
|
-
const res = yield client.query('SELECT id, credentials, crowdin_id as "crowdinId" FROM integration_credentials WHERE id = $1', [id]);
|
|
307
|
+
const res = yield client.query('SELECT id, credentials, crowdin_id as "crowdinId", managers FROM integration_credentials WHERE id = $1', [id]);
|
|
301
308
|
return res === null || res === void 0 ? void 0 : res.rows[0];
|
|
302
309
|
}));
|
|
303
310
|
});
|
|
@@ -306,7 +313,7 @@ class PostgreStorage {
|
|
|
306
313
|
return __awaiter(this, void 0, void 0, function* () {
|
|
307
314
|
yield this.dbPromise;
|
|
308
315
|
return this.executeQuery((client) => __awaiter(this, void 0, void 0, function* () {
|
|
309
|
-
const res = yield client.query('SELECT id, credentials, crowdin_id as "crowdinId" FROM integration_credentials WHERE crowdin_id = $1', [crowdinId]);
|
|
316
|
+
const res = yield client.query('SELECT id, credentials, crowdin_id as "crowdinId", managers FROM integration_credentials WHERE crowdin_id = $1', [crowdinId]);
|
|
310
317
|
return (res === null || res === void 0 ? void 0 : res.rows) || [];
|
|
311
318
|
}));
|
|
312
319
|
});
|
package/out/storage/sqlite.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export declare class SQLiteStorage implements Storage {
|
|
|
22
22
|
private addColumn;
|
|
23
23
|
private updateTables;
|
|
24
24
|
private moveIntegrationSettings;
|
|
25
|
+
private migrateManagers;
|
|
25
26
|
migrate(): Promise<void>;
|
|
26
27
|
saveCrowdinCredentials(credentials: CrowdinCredentials): Promise<void>;
|
|
27
28
|
updateCrowdinCredentials(credentials: CrowdinCredentials): Promise<void>;
|
|
@@ -30,6 +31,7 @@ export declare class SQLiteStorage implements Storage {
|
|
|
30
31
|
deleteCrowdinCredentials(id: string): Promise<void>;
|
|
31
32
|
saveIntegrationCredentials(id: string, credentials: any, crowdinId: string): Promise<void>;
|
|
32
33
|
updateIntegrationCredentials(id: string, credentials: any): Promise<void>;
|
|
34
|
+
updateIntegrationManagers(id: string, managers: any): Promise<void>;
|
|
33
35
|
getIntegrationCredentials(id: string): Promise<IntegrationCredentials | undefined>;
|
|
34
36
|
getAllIntegrationCredentials(crowdinId: string): Promise<IntegrationCredentials[]>;
|
|
35
37
|
deleteIntegrationCredentials(id: string): Promise<void>;
|
package/out/storage/sqlite.js
CHANGED
|
@@ -145,6 +145,26 @@ class SQLiteStorage {
|
|
|
145
145
|
yield this.removeColumns('config', 'integration_credentials');
|
|
146
146
|
});
|
|
147
147
|
}
|
|
148
|
+
migrateManagers() {
|
|
149
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
150
|
+
const tableInfo = yield this.each('PRAGMA table_info(integration_credentials);', []);
|
|
151
|
+
const exists = tableInfo.some((columnInfo) => columnInfo.name === 'managers');
|
|
152
|
+
if (exists) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
yield this.addColumn('integration_credentials', 'managers', 'null');
|
|
156
|
+
const integrationSettings = yield this.each('SELECT integration_id, config FROM integration_settings', []);
|
|
157
|
+
for (const settings of integrationSettings) {
|
|
158
|
+
const config = JSON.parse(settings.config);
|
|
159
|
+
if (config.managers) {
|
|
160
|
+
yield this.run('UPDATE integration_credentials SET managers = ? WHERE id = ?', [
|
|
161
|
+
JSON.stringify(config.managers),
|
|
162
|
+
settings.integration_id,
|
|
163
|
+
]);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
148
168
|
migrate() {
|
|
149
169
|
return __awaiter(this, void 0, void 0, function* () {
|
|
150
170
|
let _connection_res;
|
|
@@ -186,7 +206,8 @@ class SQLiteStorage {
|
|
|
186
206
|
(
|
|
187
207
|
id varchar not null primary key,
|
|
188
208
|
credentials varchar not null,
|
|
189
|
-
crowdin_id varchar not null
|
|
209
|
+
crowdin_id varchar not null,
|
|
210
|
+
managers varchar null
|
|
190
211
|
);
|
|
191
212
|
`, []);
|
|
192
213
|
yield this._run(`
|
|
@@ -283,6 +304,7 @@ class SQLiteStorage {
|
|
|
283
304
|
// TODO: temporary code
|
|
284
305
|
yield this.updateTables();
|
|
285
306
|
yield this.moveIntegrationSettings();
|
|
307
|
+
yield this.migrateManagers();
|
|
286
308
|
}
|
|
287
309
|
catch (e) {
|
|
288
310
|
this._rej && this._rej(e);
|
|
@@ -353,16 +375,19 @@ class SQLiteStorage {
|
|
|
353
375
|
updateIntegrationCredentials(id, credentials) {
|
|
354
376
|
return this.run('UPDATE integration_credentials SET credentials = ? WHERE id = ?', [credentials, id]);
|
|
355
377
|
}
|
|
378
|
+
updateIntegrationManagers(id, managers) {
|
|
379
|
+
return this.run('UPDATE integration_credentials SET managers = ? WHERE id = ?', [managers, id]);
|
|
380
|
+
}
|
|
356
381
|
getIntegrationCredentials(id) {
|
|
357
382
|
return __awaiter(this, void 0, void 0, function* () {
|
|
358
|
-
const row = yield this.get('SELECT id, credentials, crowdin_id as crowdinId FROM integration_credentials WHERE id = ?', [id]);
|
|
383
|
+
const row = yield this.get('SELECT id, credentials, crowdin_id as crowdinId, managers FROM integration_credentials WHERE id = ?', [id]);
|
|
359
384
|
if (row) {
|
|
360
385
|
return row;
|
|
361
386
|
}
|
|
362
387
|
});
|
|
363
388
|
}
|
|
364
389
|
getAllIntegrationCredentials(crowdinId) {
|
|
365
|
-
return this.each('SELECT id, credentials, crowdin_id as crowdinId FROM integration_credentials WHERE crowdin_id = ?', [crowdinId]);
|
|
390
|
+
return this.each('SELECT id, credentials, crowdin_id as crowdinId, managers FROM integration_credentials WHERE crowdin_id = ?', [crowdinId]);
|
|
366
391
|
}
|
|
367
392
|
deleteIntegrationCredentials(id) {
|
|
368
393
|
return __awaiter(this, void 0, void 0, function* () {
|
package/out/types.d.ts
CHANGED
|
@@ -281,7 +281,8 @@ export declare enum Scope {
|
|
|
281
281
|
AI = "ai",
|
|
282
282
|
AI_PROVIDERS = "ai.provider",
|
|
283
283
|
AI_PROMPTS = "ai.prompt",
|
|
284
|
-
AI_PROXIES = "ai.proxy"
|
|
284
|
+
AI_PROXIES = "ai.proxy",
|
|
285
|
+
APPLICATIONS = "application"
|
|
285
286
|
}
|
|
286
287
|
export interface CrowdinClientRequest extends Request {
|
|
287
288
|
crowdinApiClient: Crowdin;
|
|
@@ -423,7 +424,8 @@ export declare enum UserPermissions {
|
|
|
423
424
|
OWNER = "owner",
|
|
424
425
|
MANAGERS = "managers",
|
|
425
426
|
ALL_MEMBERS = "all",
|
|
426
|
-
GUESTS = "guests"
|
|
427
|
+
GUESTS = "guests",
|
|
428
|
+
RESTRICTED = "restricted"
|
|
427
429
|
}
|
|
428
430
|
export declare enum ProjectPermissions {
|
|
429
431
|
OWN = "own",
|
package/out/types.js
CHANGED
|
@@ -34,6 +34,7 @@ var Scope;
|
|
|
34
34
|
Scope["AI_PROVIDERS"] = "ai.provider";
|
|
35
35
|
Scope["AI_PROMPTS"] = "ai.prompt";
|
|
36
36
|
Scope["AI_PROXIES"] = "ai.proxy";
|
|
37
|
+
Scope["APPLICATIONS"] = "application";
|
|
37
38
|
})(Scope = exports.Scope || (exports.Scope = {}));
|
|
38
39
|
var AccountType;
|
|
39
40
|
(function (AccountType) {
|
|
@@ -58,6 +59,7 @@ var UserPermissions;
|
|
|
58
59
|
UserPermissions["MANAGERS"] = "managers";
|
|
59
60
|
UserPermissions["ALL_MEMBERS"] = "all";
|
|
60
61
|
UserPermissions["GUESTS"] = "guests";
|
|
62
|
+
UserPermissions["RESTRICTED"] = "restricted";
|
|
61
63
|
})(UserPermissions = exports.UserPermissions || (exports.UserPermissions = {}));
|
|
62
64
|
var ProjectPermissions;
|
|
63
65
|
(function (ProjectPermissions) {
|
package/out/util/index.d.ts
CHANGED
|
@@ -13,3 +13,4 @@ export declare function isAuthorizedConfig(config: Config | UnauthorizedConfig):
|
|
|
13
13
|
export declare function isJson(string: string): boolean;
|
|
14
14
|
export declare function getPreviousDate(days: number): Date;
|
|
15
15
|
export declare function prepareFormDataMetadataId(req: CrowdinClientRequest, config: Config): Promise<string>;
|
|
16
|
+
export declare function validateEmail(email: string | number): boolean;
|
package/out/util/index.js
CHANGED
|
@@ -32,7 +32,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
32
32
|
});
|
|
33
33
|
};
|
|
34
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
-
exports.prepareFormDataMetadataId = exports.getPreviousDate = exports.isJson = exports.isAuthorizedConfig = exports.getLogoUrl = exports.executeWithRetry = exports.decryptData = exports.encryptData = exports.runAsyncWrapper = exports.CodeError = void 0;
|
|
35
|
+
exports.validateEmail = exports.prepareFormDataMetadataId = exports.getPreviousDate = exports.isJson = exports.isAuthorizedConfig = exports.getLogoUrl = exports.executeWithRetry = exports.decryptData = exports.encryptData = exports.runAsyncWrapper = exports.CodeError = void 0;
|
|
36
36
|
const crypto = __importStar(require("crypto-js"));
|
|
37
37
|
const storage_1 = require("../storage");
|
|
38
38
|
const types_1 = require("../types");
|
|
@@ -151,3 +151,14 @@ function prepareFormDataMetadataId(req, config) {
|
|
|
151
151
|
});
|
|
152
152
|
}
|
|
153
153
|
exports.prepareFormDataMetadataId = prepareFormDataMetadataId;
|
|
154
|
+
function validateEmail(email) {
|
|
155
|
+
if (!isNaN(+email)) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
if (`${email}`.trim().length > 76) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
const emailRegExp = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
162
|
+
return emailRegExp.test(String(email).toLowerCase());
|
|
163
|
+
}
|
|
164
|
+
exports.validateEmail = validateEmail;
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
<div id="buttons">
|
|
37
37
|
<crowdin-button id="show-integration-btn" class="hidden" icon-before="arrow_back" onclick="showIntegration();">Integration</crowdin-button>
|
|
38
38
|
<crowdin-button id="show-error-logs-btn" icon-before="list" onclick="showErrorLogs();">Error logs</crowdin-button>
|
|
39
|
+
<crowdin-button icon-before="manage_accounts" onclick="showPermissionsDialog()">Permissions</crowdin-button>
|
|
39
40
|
{{#if infoModal}}
|
|
40
41
|
<crowdin-button icon-before="info" onclick="openModal(infoModal);">{{infoModal.title}}</crowdin-button>
|
|
41
42
|
{{/if}}
|
|
@@ -115,19 +116,113 @@
|
|
|
115
116
|
<crowdin-button class="ml-10" secondary onclick="integrationLogout()">Log out</crowdin-button>
|
|
116
117
|
</div>
|
|
117
118
|
</crowdin-modal>
|
|
118
|
-
|
|
119
|
+
|
|
119
120
|
<crowdin-modal
|
|
120
121
|
style="display: none;"
|
|
121
|
-
id="
|
|
122
|
-
modal-
|
|
123
|
-
modal-title="{{infoModal.title}}"
|
|
122
|
+
id="permissions-modal"
|
|
123
|
+
modal-title="Permissions"
|
|
124
124
|
close-button-title="Close"
|
|
125
|
+
close-button
|
|
126
|
+
body-overflow-unset
|
|
125
127
|
>
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
128
|
+
<div class="loader hidden">
|
|
129
|
+
<crowdin-progress-indicator></crowdin-progress-indicator>
|
|
130
|
+
</div>
|
|
131
|
+
<div class="permissions-modal-content">
|
|
132
|
+
<div class="select-users-block">
|
|
133
|
+
<crowdin-users-select
|
|
134
|
+
allow-new-options
|
|
135
|
+
is-multi
|
|
136
|
+
is-searchable
|
|
137
|
+
id="users"
|
|
138
|
+
key="users"
|
|
139
|
+
label="Users"
|
|
140
|
+
help-text="Search for members by name, username, email, or invite new ones using their email."
|
|
141
|
+
is-position-fixed
|
|
142
|
+
onchange="inviteUsers()"
|
|
143
|
+
>
|
|
144
|
+
</crowdin-select>
|
|
145
|
+
</div>
|
|
146
|
+
<div class="select-users-info">
|
|
147
|
+
<div class="confirm-users-block mt-2 hidden">
|
|
148
|
+
<table>
|
|
149
|
+
<thead>
|
|
150
|
+
<tr>
|
|
151
|
+
<th style="width: 40%;">Permission Type (Upcoming changes)</th>
|
|
152
|
+
<th style="width: 35%;">Users</th>
|
|
153
|
+
<th class="status" style="width: 20%;">Status</th>
|
|
154
|
+
</tr>
|
|
155
|
+
</thead>
|
|
156
|
+
<tbody>
|
|
157
|
+
{{#if hasOrganization}}
|
|
158
|
+
<tr class="organization-invite">
|
|
159
|
+
<td>
|
|
160
|
+
<crowdin-p>Registration in Organization</crowdin-p>
|
|
161
|
+
</td>
|
|
162
|
+
<td class="affected-users"></td>
|
|
163
|
+
<td class="status"></td>
|
|
164
|
+
</tr>
|
|
165
|
+
{{/if}}
|
|
166
|
+
<tr class="project-invite">
|
|
167
|
+
<td>
|
|
168
|
+
<crowdin-p>Project Manager Access</crowdin-p>
|
|
169
|
+
</td>
|
|
170
|
+
<td class="affected-users"></td>
|
|
171
|
+
<td class="status"></td>
|
|
172
|
+
</tr>
|
|
173
|
+
<tr class="application-settings-invite">
|
|
174
|
+
<td>
|
|
175
|
+
<crowdin-p>Application Access</crowdin-p>
|
|
176
|
+
<div class="permission-description">{{name}} installation settings will be updated.</div>
|
|
177
|
+
</td>
|
|
178
|
+
<td class="affected-users"></td>
|
|
179
|
+
<td class="status"></td>
|
|
180
|
+
</tr>
|
|
181
|
+
<tr class="application-credentials-invite">
|
|
182
|
+
<td>
|
|
183
|
+
<crowdin-p>Access to the Integration</crowdin-p>
|
|
184
|
+
<div class="permission-description">{{name}} settings will be updated.</div>
|
|
185
|
+
</td>
|
|
186
|
+
<td class="affected-users"></td>
|
|
187
|
+
<td class="status"></td>
|
|
188
|
+
</tr>
|
|
189
|
+
</tbody>
|
|
190
|
+
</table>
|
|
191
|
+
</div>
|
|
192
|
+
{{#if zenModeUrl}}
|
|
193
|
+
<div class="mt-2">
|
|
194
|
+
<crowdin-input
|
|
195
|
+
with-fixed-height
|
|
196
|
+
label="Zen Mode link"
|
|
197
|
+
help-text="This focused view allows you to concentrate solely on the Integrations section, eliminating distractions and providing a streamlined experience for managing your project integrations."
|
|
198
|
+
value="{{zenModeUrl}}"
|
|
199
|
+
name="zenModeLink"
|
|
200
|
+
with-copy-button
|
|
201
|
+
></crowdin-input>
|
|
202
|
+
</div>
|
|
203
|
+
{{/if}}
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
<div slot="footer">
|
|
208
|
+
<crowdin-button id="confirm-users-btn" outlined onclick="inviteUsers(false)">Save</crowdin-button>
|
|
209
|
+
</div>
|
|
129
210
|
</crowdin-modal>
|
|
211
|
+
|
|
212
|
+
{{#if infoModal}}
|
|
213
|
+
<crowdin-modal
|
|
214
|
+
style="display: none;"
|
|
215
|
+
id="info-modal"
|
|
216
|
+
modal-width="50"
|
|
217
|
+
modal-title="{{infoModal.title}}"
|
|
218
|
+
close-button-title="Close"
|
|
219
|
+
>
|
|
220
|
+
<div>
|
|
221
|
+
{{{infoModal.content}}}
|
|
222
|
+
</div>
|
|
223
|
+
</crowdin-modal>
|
|
130
224
|
{{/if}}
|
|
225
|
+
|
|
131
226
|
{{#if configurationFields}}
|
|
132
227
|
<crowdin-modal
|
|
133
228
|
style="display: none;"
|
|
@@ -785,7 +880,7 @@
|
|
|
785
880
|
}
|
|
786
881
|
|
|
787
882
|
function saveSettings() {
|
|
788
|
-
setLoader();
|
|
883
|
+
setLoader('#settings-modal');
|
|
789
884
|
const settingsElements = Array.from(document.getElementById('modal-content').children);
|
|
790
885
|
const tags = ['crowdin-checkbox', 'crowdin-select', 'crowdin-input'];
|
|
791
886
|
const configReq = {};
|
|
@@ -820,7 +915,7 @@
|
|
|
820
915
|
})
|
|
821
916
|
.catch(e => catchRejection(e, 'Can\'t save settings'))
|
|
822
917
|
.finally(() => {
|
|
823
|
-
unsetLoader();
|
|
918
|
+
unsetLoader('#settings-modal');
|
|
824
919
|
settingsSaveBtn.removeAttribute('disabled');
|
|
825
920
|
closeModal(settingsModal);
|
|
826
921
|
{{#if reloadOnConfigSave}}
|
|
@@ -830,13 +925,174 @@
|
|
|
830
925
|
});
|
|
831
926
|
}
|
|
832
927
|
|
|
833
|
-
function
|
|
834
|
-
const
|
|
928
|
+
function showConfirmUsersBlock() {
|
|
929
|
+
const confirmUsersBlock = document.querySelector('.confirm-users-block');
|
|
930
|
+
confirmUsersBlock.classList.remove('hidden');
|
|
931
|
+
|
|
932
|
+
const permissionsModal = document.getElementById('permissions-modal');
|
|
933
|
+
permissionsModal.removeAttribute('body-overflow-unset')
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
function hideConfirmUsersBlock() {
|
|
937
|
+
const confirmUsersBlock = document.querySelector('.confirm-users-block');
|
|
938
|
+
confirmUsersBlock.classList.add('hidden');
|
|
939
|
+
|
|
940
|
+
const permissionsModal = document.getElementById('permissions-modal');
|
|
941
|
+
permissionsModal.setAttribute('body-overflow-unset', true)
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
function showPermissionsDialog() {
|
|
945
|
+
hideConfirmUsersBlock();
|
|
946
|
+
openModal(permissions)
|
|
947
|
+
setLoader('#permissions-modal');
|
|
948
|
+
const select = document.getElementById('users');
|
|
949
|
+
|
|
950
|
+
select.value = '[]';
|
|
951
|
+
|
|
952
|
+
checkOrigin()
|
|
953
|
+
.then(restParams => fetch('api/users' + restParams))
|
|
954
|
+
.then(checkResponse)
|
|
955
|
+
.then((res) => {
|
|
956
|
+
console.log(res)
|
|
957
|
+
let userOptions = res.data.users.map(user => `<option value="${user.id}">${user.name}</option>`).join('');
|
|
958
|
+
select.innerHTML = userOptions;
|
|
959
|
+
select.value = JSON.stringify(res.data.managers);
|
|
960
|
+
})
|
|
961
|
+
.catch(e => catchRejection(e, 'Can\'t fetch users'))
|
|
962
|
+
.finally(() => unsetLoader('#permissions-modal'));
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
async function inviteUsers(onlyCheck = true) {
|
|
966
|
+
setLoader('#permissions-modal');
|
|
967
|
+
|
|
968
|
+
const select = document.getElementById('users');
|
|
969
|
+
|
|
970
|
+
if (onlyCheck && select.value === '[]') {
|
|
971
|
+
hideConfirmUsersBlock();
|
|
972
|
+
unsetLoader('#permissions-modal');
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
const params = {
|
|
977
|
+
users: JSON.parse(select.value),
|
|
978
|
+
onlyCheck,
|
|
979
|
+
};
|
|
980
|
+
|
|
981
|
+
checkOrigin()
|
|
982
|
+
.then(restParams => fetch('api/invite-users' + restParams, {
|
|
983
|
+
method: 'POST',
|
|
984
|
+
headers: { 'Content-Type': 'application/json' },
|
|
985
|
+
body: JSON.stringify(params)
|
|
986
|
+
}))
|
|
987
|
+
.then(checkResponse)
|
|
988
|
+
.then((response) => {
|
|
989
|
+
if (!onlyCheck) {
|
|
990
|
+
showToast('Users successfully updated');
|
|
991
|
+
hideConfirmUsersBlock();
|
|
992
|
+
closeModal(permissions);
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
prepareUsersConfirmBlock(response.data);
|
|
997
|
+
})
|
|
998
|
+
.catch(e => {
|
|
999
|
+
catchRejection(e, e?.error || 'Can\'t invite users')
|
|
1000
|
+
})
|
|
1001
|
+
.finally(() => unsetLoader('#permissions-modal'));
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
function prepareUsersConfirmBlock(usersData) {
|
|
1005
|
+
showConfirmUsersBlock();
|
|
1006
|
+
|
|
1007
|
+
const organizationInvite = document.querySelector('.organization-invite');
|
|
1008
|
+
const projectInvite = document.querySelector('.project-invite');
|
|
1009
|
+
const applicationCredentialsInvite = document.querySelector('.application-credentials-invite');
|
|
1010
|
+
|
|
1011
|
+
const grantedElement = '<span class="badge badge-granted">Available</span>';
|
|
1012
|
+
const willGrantedElement = '<span class="badge badge-will-be-granted">Will Be Granted</span>';
|
|
1013
|
+
const notAvailableElement = '<span class="badge badge-not-available">Manual Action Required</span>';
|
|
1014
|
+
|
|
1015
|
+
// only in enterprise
|
|
1016
|
+
if (organizationInvite) {
|
|
1017
|
+
const organizationWillGrantElement = '<span class="badge badge-will-be-granted">Will be registered</span>';
|
|
1018
|
+
|
|
1019
|
+
processUsersWhoWillBeInvited(organizationInvite, usersData.usersWhoWillBeInvitedToOrganization, organizationWillGrantElement, grantedElement);
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
processUsersWhoWillBeInvited(projectInvite, usersData.usersWhoWillBeInvitedToProject, willGrantedElement, grantedElement);
|
|
1023
|
+
processUsersWhoWillBeInvited(applicationCredentialsInvite, usersData.usersWhoNotIssetInIntegration, willGrantedElement, grantedElement);
|
|
1024
|
+
|
|
1025
|
+
processUsersWhoWillBeInvitedApplicationSettings(usersData, willGrantedElement, grantedElement, notAvailableElement);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
function processUsersWhoWillBeInvited(element, users, grantMessage, alreadyGrantedMessage) {
|
|
1029
|
+
const tooltip = element.querySelector('.status');
|
|
1030
|
+
const userList = element.querySelector('.affected-users');
|
|
1031
|
+
|
|
1032
|
+
if (users.length) {
|
|
1033
|
+
tooltip.innerHTML = grantMessage;
|
|
1034
|
+
|
|
1035
|
+
let affectedUsers = '<ul>';
|
|
1036
|
+
for (const user of users) {
|
|
1037
|
+
affectedUsers += `<li><crowdin-p>${user.name}</crowdin-p></li>`;
|
|
1038
|
+
}
|
|
1039
|
+
affectedUsers += '</ul>';
|
|
1040
|
+
userList.innerHTML = affectedUsers;
|
|
1041
|
+
} else {
|
|
1042
|
+
tooltip.innerHTML = alreadyGrantedMessage;
|
|
1043
|
+
userList.innerHTML = '<crowdin-p>―</crowdin-p>';
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
function processUsersWhoWillBeInvitedApplicationSettings(usersData, willGrantedElement, grantedElement, notAvailableElement) {
|
|
1048
|
+
const applicationSettingsInvite = document.querySelector('.application-settings-invite');
|
|
1049
|
+
|
|
1050
|
+
const tooltip = applicationSettingsInvite.querySelector('.status');
|
|
1051
|
+
const userList = applicationSettingsInvite.querySelector('.affected-users');
|
|
1052
|
+
|
|
1053
|
+
let affectedUsers = '<ul>';
|
|
1054
|
+
|
|
1055
|
+
if (!usersData.editApplicationAvailable) {
|
|
1056
|
+
tooltip.innerHTML = `<crowdin-tooltip
|
|
1057
|
+
position="left"
|
|
1058
|
+
tooltip=">The application doesn't have permission to update this setting.">
|
|
1059
|
+
${notAvailableElement}
|
|
1060
|
+
</crowdin-tooltip>`;
|
|
1061
|
+
for (const user of usersData.usersWhoNotIssetInApplicationInstallation) {
|
|
1062
|
+
affectedUsers += `<li><crowdin-p>${user.name}</crowdin-p></li>`;
|
|
1063
|
+
}
|
|
1064
|
+
userList.innerHTML = affectedUsers + '</ul>';
|
|
1065
|
+
} else if (!usersData.usersWhoNotIssetInApplicationInstallation.length) {
|
|
1066
|
+
tooltip.innerHTML = grantedElement;
|
|
1067
|
+
userList.innerHTML = '<crowdin-p>―</crowdin-p>';
|
|
1068
|
+
} else {
|
|
1069
|
+
if (usersData.isAdmin) {
|
|
1070
|
+
tooltip.innerHTML = willGrantedElement;
|
|
1071
|
+
for (const user of usersData.usersWhoNotIssetInApplicationInstallation) {
|
|
1072
|
+
affectedUsers += `<li><crowdin-p>${user.name}</crowdin-p></li>`;
|
|
1073
|
+
}
|
|
1074
|
+
userList.innerHTML = affectedUsers + '</ul>';
|
|
1075
|
+
} else {
|
|
1076
|
+
tooltip.innerHTML = `<crowdin-tooltip
|
|
1077
|
+
position="left"
|
|
1078
|
+
tooltip="Only organization admins have permission to update this setting.">
|
|
1079
|
+
${notAvailableElement}
|
|
1080
|
+
</crowdin-tooltip>`;
|
|
1081
|
+
for (const user of usersData.usersWhoNotIssetInApplicationInstallation) {
|
|
1082
|
+
affectedUsers += `<li><crowdin-p>${user.name}</crowdin-p></li>`;
|
|
1083
|
+
}
|
|
1084
|
+
userList.innerHTML = affectedUsers + '</ul>';
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
function setLoader(id) {
|
|
1090
|
+
const loader = document.querySelector(`${id} .loader`);
|
|
835
1091
|
loader.classList.remove('hidden');
|
|
836
1092
|
}
|
|
837
1093
|
|
|
838
|
-
function unsetLoader() {
|
|
839
|
-
const loader = document.querySelector(
|
|
1094
|
+
function unsetLoader(id) {
|
|
1095
|
+
const loader = document.querySelector(`${id} .loader`);
|
|
840
1096
|
setTimeout(function() {
|
|
841
1097
|
loader.classList.add('hidden');
|
|
842
1098
|
}, 500)
|
|
@@ -845,6 +1101,8 @@
|
|
|
845
1101
|
const settingsModal = undefined;
|
|
846
1102
|
{{/if}}
|
|
847
1103
|
|
|
1104
|
+
const permissions = document.getElementById('permissions-modal');
|
|
1105
|
+
|
|
848
1106
|
{{#if infoModal}}
|
|
849
1107
|
const infoModal = document.getElementById('info-modal');
|
|
850
1108
|
{{else}}
|
|
@@ -853,7 +1111,9 @@
|
|
|
853
1111
|
|
|
854
1112
|
document.addEventListener('keydown', (event) => {
|
|
855
1113
|
if (event.keyCode == 27) {
|
|
856
|
-
|
|
1114
|
+
if (users) {
|
|
1115
|
+
closeModal(permissions);
|
|
1116
|
+
}
|
|
857
1117
|
if (infoModal) {
|
|
858
1118
|
closeModal(infoModal);
|
|
859
1119
|
}
|
package/package.json
CHANGED