@crowdin/app-project-module 0.70.2 → 0.71.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/index.js +3 -3
- package/out/middlewares/integration-credentials.js +92 -6
- package/out/modules/integration/handlers/integration-logout.js +28 -0
- package/out/modules/integration/handlers/main.js +26 -0
- package/out/modules/integration/util/defaults.js +35 -0
- package/out/modules/integration/util/webhooks.js +1 -7
- package/out/modules/uninstall.js +4 -0
- package/out/static/css/styles.css +5 -1
- package/out/util/connection.js +2 -0
- package/out/views/error.handlebars +28 -5
- package/out/views/main.handlebars +9 -2
- package/package.json +2 -2
package/out/index.js
CHANGED
|
@@ -105,9 +105,9 @@ exports.metadataStore = {
|
|
|
105
105
|
return storage.getStorage().deleteMetadata(id);
|
|
106
106
|
},
|
|
107
107
|
getUserSettings: (clientId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
108
|
-
const
|
|
109
|
-
if (
|
|
110
|
-
return JSON.parse(
|
|
108
|
+
const integrationConfig = yield storage.getStorage().getIntegrationConfig(clientId);
|
|
109
|
+
if (integrationConfig === null || integrationConfig === void 0 ? void 0 : integrationConfig.config) {
|
|
110
|
+
return JSON.parse(integrationConfig.config);
|
|
111
111
|
}
|
|
112
112
|
}),
|
|
113
113
|
};
|
|
@@ -1,4 +1,27 @@
|
|
|
1
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
|
+
};
|
|
2
25
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
26
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
27
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -13,19 +36,53 @@ const storage_1 = require("../storage");
|
|
|
13
36
|
const util_1 = require("../util");
|
|
14
37
|
const connection_1 = require("../util/connection");
|
|
15
38
|
const logger_1 = require("../util/logger");
|
|
39
|
+
const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions"));
|
|
16
40
|
function handle(config, integration, optional = false) {
|
|
17
41
|
return (0, util_1.runAsyncWrapper)((req, res, next) => __awaiter(this, void 0, void 0, function* () {
|
|
18
|
-
|
|
42
|
+
let clientId = req.crowdinContext.clientId;
|
|
43
|
+
const { organization, projectId, userId } = crowdinAppFunctions.parseCrowdinId(clientId);
|
|
19
44
|
req.logInfo(`Loading integration credentials for client ${clientId}`);
|
|
20
|
-
|
|
21
|
-
const
|
|
45
|
+
let integrationCredentials = yield (0, storage_1.getStorage)().getIntegrationCredentials(clientId);
|
|
46
|
+
const ownerIds = [];
|
|
47
|
+
// check if user has access to integration in settings(managers)
|
|
22
48
|
if (!integrationCredentials) {
|
|
23
|
-
|
|
49
|
+
const projectIntegrationCredentials = (yield (0, storage_1.getStorage)().getAllIntegrationCredentials(organization)).filter((item) => {
|
|
50
|
+
const { organization: itemOrganization, projectId: itemProjectId } = crowdinAppFunctions.parseCrowdinId(item.id);
|
|
51
|
+
return itemOrganization === organization && itemProjectId === projectId;
|
|
52
|
+
});
|
|
53
|
+
if (projectIntegrationCredentials.length) {
|
|
54
|
+
for (const credentials of projectIntegrationCredentials) {
|
|
55
|
+
ownerIds.push(crowdinAppFunctions.parseCrowdinId(credentials.id).userId);
|
|
56
|
+
if (yield checkUserAccessToIntegration(credentials, `${userId}`)) {
|
|
57
|
+
integrationCredentials = credentials;
|
|
58
|
+
clientId = credentials.id;
|
|
59
|
+
req.crowdinContext.clientId = clientId;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (!integrationCredentials) {
|
|
66
|
+
if (optional && !ownerIds.length) {
|
|
24
67
|
return next();
|
|
25
68
|
}
|
|
26
|
-
|
|
27
|
-
|
|
69
|
+
const errorOptions = {
|
|
70
|
+
code: 403,
|
|
71
|
+
message: 'Access denied',
|
|
72
|
+
owners: null,
|
|
73
|
+
hideActions: false,
|
|
74
|
+
};
|
|
75
|
+
if (ownerIds) {
|
|
76
|
+
errorOptions.message = 'Looks like you don’t have access';
|
|
77
|
+
errorOptions.hideActions = true;
|
|
78
|
+
errorOptions.owners = yield getIntegrationManagedBy(ownerIds, req);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
(0, logger_1.temporaryErrorDebug)('Access denied: integration-credentials', req);
|
|
82
|
+
}
|
|
83
|
+
return res.render('error', errorOptions);
|
|
28
84
|
}
|
|
85
|
+
const integrationConfig = yield (0, storage_1.getStorage)().getIntegrationConfig(clientId);
|
|
29
86
|
if (integrationConfig === null || integrationConfig === void 0 ? void 0 : integrationConfig.config) {
|
|
30
87
|
req.integrationSettings = JSON.parse(integrationConfig.config);
|
|
31
88
|
}
|
|
@@ -40,3 +97,32 @@ function handle(config, integration, optional = false) {
|
|
|
40
97
|
}));
|
|
41
98
|
}
|
|
42
99
|
exports.default = handle;
|
|
100
|
+
function checkUserAccessToIntegration(integrationCredentials, userId) {
|
|
101
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
102
|
+
const projectIntegrationConfig = yield (0, storage_1.getStorage)().getIntegrationConfig(integrationCredentials.id);
|
|
103
|
+
if (projectIntegrationConfig === null || projectIntegrationConfig === void 0 ? void 0 : projectIntegrationConfig.config) {
|
|
104
|
+
const appSettings = JSON.parse(projectIntegrationConfig.config);
|
|
105
|
+
return ((appSettings === null || appSettings === void 0 ? void 0 : appSettings.managers) || []).includes(userId);
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function getIntegrationManagedBy(ownerIds, req) {
|
|
111
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
112
|
+
const projectId = crowdinAppFunctions.getProjectId(req.crowdinContext.clientId);
|
|
113
|
+
const owners = ownerIds.length > 1
|
|
114
|
+
? (yield req.crowdinApiClient.usersApi.listProjectMembers(projectId)).data.filter((member) => ownerIds.includes(member.data.id))
|
|
115
|
+
: [yield req.crowdinApiClient.usersApi.getProjectMemberPermissions(projectId, ownerIds[0])];
|
|
116
|
+
return owners.map((owner) => {
|
|
117
|
+
const ownerFullName = 'fullName' in owner.data
|
|
118
|
+
? owner.data.fullName
|
|
119
|
+
: `${owner.data.firstName || ''} ${owner.data.lastName || ''}`.trim();
|
|
120
|
+
return {
|
|
121
|
+
id: owner.data.id,
|
|
122
|
+
name: !!ownerFullName && owner.data.username !== ownerFullName
|
|
123
|
+
? `${ownerFullName} (${owner.data.username})`
|
|
124
|
+
: owner.data.username,
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
}
|
|
@@ -1,4 +1,27 @@
|
|
|
1
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
|
+
};
|
|
2
25
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
26
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
27
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -14,8 +37,13 @@ const util_1 = require("../../../util");
|
|
|
14
37
|
const logger_1 = require("../../../util/logger");
|
|
15
38
|
const subscription_1 = require("../../../util/subscription");
|
|
16
39
|
const webhooks_1 = require("../util/webhooks");
|
|
40
|
+
const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions"));
|
|
17
41
|
function handle(config, integration) {
|
|
18
42
|
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
43
|
+
const { userId } = crowdinAppFunctions.parseCrowdinId(req.crowdinContext.clientId);
|
|
44
|
+
if (+userId !== +req.crowdinContext.jwtPayload.sub) {
|
|
45
|
+
return res.status(403).send({ error: 'Access denied' });
|
|
46
|
+
}
|
|
19
47
|
req.logInfo('Received integration logout request');
|
|
20
48
|
if (integration.onLogout) {
|
|
21
49
|
try {
|
|
@@ -1,4 +1,27 @@
|
|
|
1
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
|
+
};
|
|
2
25
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
26
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
27
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -13,6 +36,7 @@ const util_1 = require("../../../util");
|
|
|
13
36
|
const logger_1 = require("../../../util/logger");
|
|
14
37
|
const subscription_1 = require("../../../util/subscription");
|
|
15
38
|
const defaults_1 = require("../util/defaults");
|
|
39
|
+
const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions"));
|
|
16
40
|
function handle(config, integration) {
|
|
17
41
|
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
18
42
|
var _a, _b;
|
|
@@ -41,6 +65,8 @@ function handle(config, integration) {
|
|
|
41
65
|
}
|
|
42
66
|
}
|
|
43
67
|
else if (integration.getConfiguration) {
|
|
68
|
+
const { userId } = crowdinAppFunctions.parseCrowdinId(req.crowdinContext.clientId);
|
|
69
|
+
options.isOwner = +req.crowdinContext.jwtPayload.sub === userId;
|
|
44
70
|
const configurationFields = yield integration.getConfiguration(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, req.integrationCredentials);
|
|
45
71
|
options.configurationFields = configurationFields;
|
|
46
72
|
options.config = JSON.stringify(req.integrationSettings || {});
|
|
@@ -147,6 +147,27 @@ function applyIntegrationModuleDefaults(config, integration) {
|
|
|
147
147
|
fields = yield getUserSettings(projectId, crowdinClient, integrationCredentials);
|
|
148
148
|
}
|
|
149
149
|
const defaultSettings = [];
|
|
150
|
+
const mangers = yield getManagers(projectId, crowdinClient, integrationCredentials);
|
|
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
|
+
}
|
|
150
171
|
if (project.data.inContext) {
|
|
151
172
|
defaultSettings.push({
|
|
152
173
|
key: 'inContext',
|
|
@@ -299,3 +320,17 @@ function getOAuthLoginFormId(clientId) {
|
|
|
299
320
|
return `oauth_form_${clientId}`;
|
|
300
321
|
}
|
|
301
322
|
exports.getOAuthLoginFormId = getOAuthLoginFormId;
|
|
323
|
+
function getManagers(projectId, client, integrationCredentials) {
|
|
324
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
325
|
+
const managers = [];
|
|
326
|
+
if (client.organization) {
|
|
327
|
+
const admins = (yield client.usersApi.listUsers()).data.filter((user) => user.data.isAdmin);
|
|
328
|
+
managers.push(...admins.map((admin) => admin.data));
|
|
329
|
+
}
|
|
330
|
+
const projectMembers = (yield client.usersApi.listProjectMembers(projectId)).data.filter((user) => 'role' in user.data ? ['owner', 'manager'].includes(user.data.role) : user.data.isManager);
|
|
331
|
+
managers.push(...projectMembers.map((members) => members.data));
|
|
332
|
+
return managers
|
|
333
|
+
.filter((manager) => manager.id !== +integrationCredentials.ownerId)
|
|
334
|
+
.filter((manager, index, self) => self.findIndex((m) => m.id === manager.id) === index);
|
|
335
|
+
});
|
|
336
|
+
}
|
|
@@ -110,7 +110,6 @@ function registerWebhooks({ config, apiCredentials, appSettings, client, crowdin
|
|
|
110
110
|
projectId,
|
|
111
111
|
webhook,
|
|
112
112
|
events,
|
|
113
|
-
url: webhookUrl,
|
|
114
113
|
});
|
|
115
114
|
}
|
|
116
115
|
else if (isWebhookSync && !webhook) {
|
|
@@ -198,7 +197,7 @@ function registerCrowdinWebhook({ client, config, crowdinContext, events, url, }
|
|
|
198
197
|
});
|
|
199
198
|
});
|
|
200
199
|
}
|
|
201
|
-
function updateCrowdinWebhooks({ client, events, projectId,
|
|
200
|
+
function updateCrowdinWebhooks({ client, events, projectId, webhook, }) {
|
|
202
201
|
return __awaiter(this, void 0, void 0, function* () {
|
|
203
202
|
const payload = createPayload(events);
|
|
204
203
|
yield client.webhooksApi.editWebhook(projectId, webhook.id, [
|
|
@@ -212,11 +211,6 @@ function updateCrowdinWebhooks({ client, events, projectId, url, webhook, }) {
|
|
|
212
211
|
op: 'replace',
|
|
213
212
|
path: '/payload',
|
|
214
213
|
},
|
|
215
|
-
{
|
|
216
|
-
value: url,
|
|
217
|
-
op: 'replace',
|
|
218
|
-
path: '/url',
|
|
219
|
-
},
|
|
220
214
|
]);
|
|
221
215
|
});
|
|
222
216
|
}
|
package/out/modules/uninstall.js
CHANGED
|
@@ -20,6 +20,10 @@ function handle(config) {
|
|
|
20
20
|
(0, logger_1.log)(`Received uninstall request ${JSON.stringify(event, null, 2)}`);
|
|
21
21
|
const projectIntegration = config.projectIntegration;
|
|
22
22
|
const organization = (event.domain || event.organizationId).toString();
|
|
23
|
+
const crowdinCredential = yield (0, storage_1.getStorage)().getCrowdinCredentials(organization);
|
|
24
|
+
if ((crowdinCredential === null || crowdinCredential === void 0 ? void 0 : crowdinCredential.appSecret) !== event.appSecret) {
|
|
25
|
+
return res.status(403).send({ error: 'Access denied' });
|
|
26
|
+
}
|
|
23
27
|
if (config.onUninstall) {
|
|
24
28
|
let allCredentials = [];
|
|
25
29
|
if (projectIntegration) {
|
package/out/util/connection.js
CHANGED
|
@@ -46,6 +46,7 @@ const axios_2 = require("./axios");
|
|
|
46
46
|
const logger_1 = require("./logger");
|
|
47
47
|
const os = __importStar(require("os"));
|
|
48
48
|
const defaults_1 = require("../modules/integration/util/defaults");
|
|
49
|
+
const crowdinAppFunctions = __importStar(require("@crowdin/crowdin-apps-functions"));
|
|
49
50
|
const axiosCustom = new axios_2.AxiosProvider().axios;
|
|
50
51
|
function prepareCrowdinClient({ config, credentials, autoRenew = false, context, }) {
|
|
51
52
|
var _a, _b;
|
|
@@ -220,6 +221,7 @@ function prepareIntegrationCredentials(config, integration, integrationCredentia
|
|
|
220
221
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
221
222
|
return __awaiter(this, void 0, void 0, function* () {
|
|
222
223
|
const credentials = JSON.parse((0, _1.decryptData)(config, integrationCredentials.credentials));
|
|
224
|
+
credentials.ownerId = crowdinAppFunctions.parseCrowdinId(integrationCredentials.id).userId;
|
|
223
225
|
const oauthLogin = integration.oauthLogin;
|
|
224
226
|
const integrationLogin = integration.loginForm;
|
|
225
227
|
if ((oauthLogin === null || oauthLogin === void 0 ? void 0 : oauthLogin.refresh) || (integrationLogin === null || integrationLogin === void 0 ? void 0 : integrationLogin.refresh)) {
|
|
@@ -8,11 +8,28 @@
|
|
|
8
8
|
<div>
|
|
9
9
|
<div class="error-page-message">
|
|
10
10
|
<crowdin-h3>{{ message }}</crowdin-h3>
|
|
11
|
+
{{#if owners}}
|
|
12
|
+
<crowdin-h6>This integration allows only one connection per project to ensure consistent configuration and prevent file import or synchronization issues.</crowdin-h6>
|
|
13
|
+
{{#if owners.[1]}}
|
|
14
|
+
<crowdin-h5 class="mt-2">Managed by:</crowdin-h5>
|
|
15
|
+
<ul>
|
|
16
|
+
{{#each owners}}
|
|
17
|
+
<li><crowdin-a href="#" onclick="contactUser({{this.id}})">{{this.name}}</crowdin-a></li>
|
|
18
|
+
{{/each}}
|
|
19
|
+
</ul>
|
|
20
|
+
<crowdin-h6 class="mt-2">To request access, please contact one of the project members listed above.</crowdin-h6>
|
|
21
|
+
{{else}}
|
|
22
|
+
<crowdin-h5 class="mt-2">Managed by: <crowdin-a href="#" onclick="contactUser({{owners.[0].id}})">{{owners.[0].name}}</crowdin-a></crowdin-h5>
|
|
23
|
+
<crowdin-h6 class="mt-2">To request access, please contact the project member listed above.</crowdin-h6>
|
|
24
|
+
{{/if}}
|
|
25
|
+
{{/if}}
|
|
11
26
|
</div>
|
|
12
|
-
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
27
|
+
{{#unless hideActions}}
|
|
28
|
+
<div class="error-page-action">
|
|
29
|
+
<crowdin-button outlined onclick="integrationLogout()">Log out</crowdin-button>
|
|
30
|
+
<span> or <crowdin-a href="https://crowdin.com/contacts" target="_blank">contact us</crowdin-a> for help</span>
|
|
31
|
+
</div>
|
|
32
|
+
{{/unless}}
|
|
16
33
|
</div>
|
|
17
34
|
</div>
|
|
18
35
|
</div>
|
|
@@ -28,4 +45,10 @@
|
|
|
28
45
|
.then(localStorage.removeItem('revised_{{name}}'))
|
|
29
46
|
.catch(e => catchRejection(e, 'Looks like you are not logged in'));
|
|
30
47
|
}
|
|
31
|
-
|
|
48
|
+
|
|
49
|
+
function contactUser(userId) {
|
|
50
|
+
AP.getContext(function(context) {
|
|
51
|
+
AP.redirect(`${context.organization_id ? '/u' : ''}/messages/create/${userId}`);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
</script>
|
|
@@ -42,7 +42,14 @@
|
|
|
42
42
|
{{#if configurationFields}}
|
|
43
43
|
<crowdin-button icon-before="settings" onclick="openModal(settingsModal);fillSettingsForm();">Settings</crowdin-button>
|
|
44
44
|
{{/if}}
|
|
45
|
-
<crowdin-button
|
|
45
|
+
<crowdin-button
|
|
46
|
+
{{#unless isOwner}}
|
|
47
|
+
disabled
|
|
48
|
+
title="Only the owner can log out"
|
|
49
|
+
{{/unless}}
|
|
50
|
+
icon-before="account_circle"
|
|
51
|
+
onclick="integrationLogout()"
|
|
52
|
+
>Log out</crowdin-button>
|
|
46
53
|
</div>
|
|
47
54
|
</div>
|
|
48
55
|
<crowdin-simple-integration
|
|
@@ -388,7 +395,7 @@
|
|
|
388
395
|
.then(checkResponse)
|
|
389
396
|
.then(reloadLocation)
|
|
390
397
|
.then(localStorage.removeItem('revised_{{name}}'))
|
|
391
|
-
.catch(e => catchRejection(e, 'Looks like you are not logged in'));
|
|
398
|
+
.catch(e => catchRejection(e, e.error || 'Looks like you are not logged in'));
|
|
392
399
|
}
|
|
393
400
|
|
|
394
401
|
function getCrowdinData() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crowdin/app-project-module",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.71.0",
|
|
4
4
|
"description": "Module that generates for you all common endpoints for serving standalone Crowdin App",
|
|
5
5
|
"main": "out/index.js",
|
|
6
6
|
"types": "out/index.d.ts",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@aws-sdk/client-s3": "^3.606.0",
|
|
23
23
|
"@aws-sdk/s3-request-presigner": "^3.682.0",
|
|
24
|
-
"@crowdin/crowdin-apps-functions": "0.
|
|
24
|
+
"@crowdin/crowdin-apps-functions": "^0.10.0",
|
|
25
25
|
"@crowdin/logs-formatter": "^2.1.7",
|
|
26
26
|
"@godaddy/terminus": "^4.12.1",
|
|
27
27
|
"@monaco-editor/react": "^4.6.0",
|