@crowdin/app-project-module 0.25.0 → 0.26.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/README.md +5 -2
- package/out/handlers/crowdin-update.js +1 -1
- package/out/handlers/integration-data.js +6 -4
- package/out/handlers/integration-update.js +1 -1
- package/out/handlers/main.js +1 -0
- package/out/index.js +7 -7
- package/out/middlewares/ui-module.d.ts +1 -1
- package/out/middlewares/ui-module.js +5 -1
- package/out/models/index.d.ts +13 -4
- package/out/util/index.d.ts +2 -2
- package/out/util/index.js +4 -4
- package/out/views/main.handlebars +20 -2
- package/out/views/partials/head.handlebars +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -89,9 +89,10 @@ const configuration = {
|
|
|
89
89
|
crowdin: true,
|
|
90
90
|
integration: true,
|
|
91
91
|
},
|
|
92
|
+
integrationPagination: true, //enable pagination on integration files list
|
|
92
93
|
integrationOneLevelFetching: true, //turn on request when opening a directory and pass its id
|
|
93
94
|
integrationSearchListener: true, //turn on search listener and pass search string
|
|
94
|
-
getIntegrationFiles: async (credentials, appSettings, parentId, search) => {
|
|
95
|
+
getIntegrationFiles: async (credentials, appSettings, parentId, search, page) => {
|
|
95
96
|
//here you need to fetch files/objects from integration
|
|
96
97
|
const res = [
|
|
97
98
|
{
|
|
@@ -125,6 +126,7 @@ const configuration = {
|
|
|
125
126
|
return {
|
|
126
127
|
data: res,
|
|
127
128
|
message: 'Test',
|
|
129
|
+
stopPagination: true, //send if no next files page
|
|
128
130
|
};
|
|
129
131
|
},
|
|
130
132
|
updateCrowdin: async (projectId, client, credentials, request, rootFolder, appSettings) => {
|
|
@@ -816,7 +818,8 @@ const configuration = {
|
|
|
816
818
|
projectReports: { //can be editorRightPanel, projectMenu, projectTools, projectMenuCrowdsource
|
|
817
819
|
imagePath: __dirname + '/' + 'reports.png',
|
|
818
820
|
fileName: 'reports.html', //optional, only needed if file is not index.html
|
|
819
|
-
uiPath: __dirname + '/' + 'public' // folder where UI of the module is located (js, html, css files)
|
|
821
|
+
uiPath: __dirname + '/' + 'public', // folder where UI of the module is located (js, html, css files)
|
|
822
|
+
allowUnauthorized: true //make module publicly available without crowdin context
|
|
820
823
|
},
|
|
821
824
|
};
|
|
822
825
|
|
|
@@ -21,7 +21,7 @@ function handle(config, integration) {
|
|
|
21
21
|
}
|
|
22
22
|
const result = yield integration.updateCrowdin(projectId, req.crowdinApiClient, req.integrationCredentials, req.body, rootFolder, req.integrationSettings);
|
|
23
23
|
let message;
|
|
24
|
-
if ((0, util_1.
|
|
24
|
+
if ((0, util_1.isExtendedResultType)(result)) {
|
|
25
25
|
message = result.message;
|
|
26
26
|
}
|
|
27
27
|
res.send({ message });
|
|
@@ -12,20 +12,22 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
const util_1 = require("../util");
|
|
13
13
|
function handle(config, integration) {
|
|
14
14
|
return (0, util_1.runAsyncWrapper)((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
15
|
-
const { parent_id: parentId, search } = req.query;
|
|
15
|
+
const { parent_id: parentId, search, page } = req.query;
|
|
16
16
|
(0, util_1.log)('Recieved request to get integration data', config.logger);
|
|
17
|
-
const result = yield integration.getIntegrationFiles(req.integrationCredentials, req.integrationSettings, parentId, search);
|
|
17
|
+
const result = yield integration.getIntegrationFiles(req.integrationCredentials, req.integrationSettings, parentId, search, page);
|
|
18
18
|
let message;
|
|
19
|
+
let stopPagination;
|
|
19
20
|
let files;
|
|
20
|
-
if ((0, util_1.
|
|
21
|
+
if ((0, util_1.isExtendedResultType)(result)) {
|
|
21
22
|
files = result.data;
|
|
22
23
|
message = result.message;
|
|
24
|
+
stopPagination = result.stopPagination;
|
|
23
25
|
}
|
|
24
26
|
else {
|
|
25
27
|
files = result;
|
|
26
28
|
}
|
|
27
29
|
(0, util_1.log)(`Integration data response ${JSON.stringify(files, null, 2)}`, config.logger);
|
|
28
|
-
res.send({ data: files, message });
|
|
30
|
+
res.send({ data: files, message, stopPagination });
|
|
29
31
|
}), config.onError);
|
|
30
32
|
}
|
|
31
33
|
exports.default = handle;
|
|
@@ -20,7 +20,7 @@ function handle(config, integration) {
|
|
|
20
20
|
}
|
|
21
21
|
const result = yield integration.updateIntegration(req.crowdinContext.jwtPayload.context.project_id, req.crowdinApiClient, req.integrationCredentials, req.body, rootFolder, req.integrationSettings);
|
|
22
22
|
let message;
|
|
23
|
-
if ((0, util_1.
|
|
23
|
+
if ((0, util_1.isExtendedResultType)(result)) {
|
|
24
24
|
message = result.message;
|
|
25
25
|
}
|
|
26
26
|
res.send({ message });
|
package/out/handlers/main.js
CHANGED
|
@@ -40,6 +40,7 @@ function handle(config, integration) {
|
|
|
40
40
|
options.configurationFields = configurationFields;
|
|
41
41
|
options.config = JSON.stringify(req.integrationSettings || {});
|
|
42
42
|
options.reloadOnConfigSave = !!integration.reloadOnConfigSave;
|
|
43
|
+
options.integrationPagination = integration.integrationPagination;
|
|
43
44
|
(0, util_1.log)(`Adding configuration fields ${JSON.stringify(configurationFields, null, 2)}`, config.logger);
|
|
44
45
|
}
|
|
45
46
|
options.infoModal = integration.infoModal;
|
package/out/index.js
CHANGED
|
@@ -143,28 +143,28 @@ function addCrowdinEndpoints(app, config) {
|
|
|
143
143
|
}
|
|
144
144
|
if (config.profileResourcesMenu) {
|
|
145
145
|
app.get('/logo/resources/logo.png', (req, res) => { var _a; return res.sendFile(((_a = config.profileResourcesMenu) === null || _a === void 0 ? void 0 : _a.imagePath) || config.imagePath || (0, path_1.join)(__dirname, 'logo.png')); });
|
|
146
|
-
app.use('/resources', (0, ui_module_1.default)(config), express_1.default.static(config.profileResourcesMenu.uiPath));
|
|
146
|
+
app.use('/resources', (0, ui_module_1.default)(config, config.profileResourcesMenu.allowUnauthorized), express_1.default.static(config.profileResourcesMenu.uiPath));
|
|
147
147
|
}
|
|
148
148
|
if (config.organizationMenu) {
|
|
149
149
|
app.get('/logo/resources/logo.png', (req, res) => { var _a; return res.sendFile(((_a = config.organizationMenu) === null || _a === void 0 ? void 0 : _a.imagePath) || config.imagePath || (0, path_1.join)(__dirname, 'logo.png')); });
|
|
150
|
-
app.use('/resources', (0, ui_module_1.default)(config), express_1.default.static(config.organizationMenu.uiPath));
|
|
150
|
+
app.use('/resources', (0, ui_module_1.default)(config, config.organizationMenu.allowUnauthorized), express_1.default.static(config.organizationMenu.uiPath));
|
|
151
151
|
}
|
|
152
152
|
if (config.editorRightPanel) {
|
|
153
|
-
app.use('/editor-panels', (0, ui_module_1.default)(config), express_1.default.static(config.editorRightPanel.uiPath));
|
|
153
|
+
app.use('/editor-panels', (0, ui_module_1.default)(config, config.editorRightPanel.allowUnauthorized), express_1.default.static(config.editorRightPanel.uiPath));
|
|
154
154
|
}
|
|
155
155
|
if (config.projectMenu) {
|
|
156
|
-
app.use('/project-menu', (0, ui_module_1.default)(config), express_1.default.static(config.projectMenu.uiPath));
|
|
156
|
+
app.use('/project-menu', (0, ui_module_1.default)(config, config.projectMenu.allowUnauthorized), express_1.default.static(config.projectMenu.uiPath));
|
|
157
157
|
}
|
|
158
158
|
if (config.projectMenuCrowdsource) {
|
|
159
|
-
app.use('/project-menu-crowdsource', (0, ui_module_1.default)(config), express_1.default.static(config.projectMenuCrowdsource.uiPath));
|
|
159
|
+
app.use('/project-menu-crowdsource', (0, ui_module_1.default)(config, config.projectMenuCrowdsource.allowUnauthorized), express_1.default.static(config.projectMenuCrowdsource.uiPath));
|
|
160
160
|
}
|
|
161
161
|
if (config.projectTools) {
|
|
162
162
|
app.get('/logo/tools/logo.png', (req, res) => { var _a; return res.sendFile(((_a = config.projectTools) === null || _a === void 0 ? void 0 : _a.imagePath) || config.imagePath || (0, path_1.join)(__dirname, 'logo.png')); });
|
|
163
|
-
app.use('/tools', (0, ui_module_1.default)(config), express_1.default.static(config.projectTools.uiPath));
|
|
163
|
+
app.use('/tools', (0, ui_module_1.default)(config, config.projectTools.allowUnauthorized), express_1.default.static(config.projectTools.uiPath));
|
|
164
164
|
}
|
|
165
165
|
if (config.projectReports) {
|
|
166
166
|
app.get('/logo/reports/logo.png', (req, res) => { var _a; return res.sendFile(((_a = config.projectReports) === null || _a === void 0 ? void 0 : _a.imagePath) || config.imagePath || (0, path_1.join)(__dirname, 'logo.png')); });
|
|
167
|
-
app.use('/reports', (0, ui_module_1.default)(config), express_1.default.static(config.projectReports.uiPath));
|
|
167
|
+
app.use('/reports', (0, ui_module_1.default)(config, config.projectReports.allowUnauthorized), express_1.default.static(config.projectReports.uiPath));
|
|
168
168
|
}
|
|
169
169
|
return {
|
|
170
170
|
getMetadata: storage.getStorage().getMetadata.bind(storage.getStorage()),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/// <reference types="qs" />
|
|
2
2
|
import { Request, Response } from 'express';
|
|
3
3
|
import { Config } from '../models';
|
|
4
|
-
export default function handle(config: Config): (req: Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
|
|
4
|
+
export default function handle(config: Config, allowUnauthorized?: boolean): (req: Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
|
|
@@ -13,8 +13,12 @@ const crowdin_apps_functions_1 = require("@crowdin/crowdin-apps-functions");
|
|
|
13
13
|
const storage_1 = require("../storage");
|
|
14
14
|
const util_1 = require("../util");
|
|
15
15
|
const connection_1 = require("../util/connection");
|
|
16
|
-
function handle(config) {
|
|
16
|
+
function handle(config, allowUnauthorized = false) {
|
|
17
17
|
return (0, util_1.runAsyncWrapper)((req, res, next) => __awaiter(this, void 0, void 0, function* () {
|
|
18
|
+
if (allowUnauthorized) {
|
|
19
|
+
next();
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
18
22
|
const tokenJwt = req.query.tokenJwt;
|
|
19
23
|
if (!tokenJwt) {
|
|
20
24
|
return res.status(403).send({ error: 'Access denied' });
|
package/out/models/index.d.ts
CHANGED
|
@@ -179,15 +179,15 @@ export interface IntegrationLogic {
|
|
|
179
179
|
/**
|
|
180
180
|
* function to get data from integration
|
|
181
181
|
*/
|
|
182
|
-
getIntegrationFiles: (apiCredentials: any, config?: any, parentId?: any, search?: any) => Promise<TreeItem[] |
|
|
182
|
+
getIntegrationFiles: (apiCredentials: any, config?: any, parentId?: any, search?: any, page?: any) => Promise<TreeItem[] | ExtendedResult<TreeItem[]>>;
|
|
183
183
|
/**
|
|
184
184
|
* function to update crowdin files (e.g. pull integration data to crowdin source files)
|
|
185
185
|
*/
|
|
186
|
-
updateCrowdin: (projectId: number, client: Crowdin, apiCredentials: any, request: IntegrationFile[], appRootFolder?: SourceFilesModel.Directory, config?: any) => Promise<void |
|
|
186
|
+
updateCrowdin: (projectId: number, client: Crowdin, apiCredentials: any, request: IntegrationFile[], appRootFolder?: SourceFilesModel.Directory, config?: any) => Promise<void | ExtendedResult<void>>;
|
|
187
187
|
/**
|
|
188
188
|
* function to update integration content (e.g. load crowdin translations and push them to integration service)
|
|
189
189
|
*/
|
|
190
|
-
updateIntegration: (projectId: number, client: Crowdin, apiCredentials: any, request: UpdateIntegrationRequest, appRootFolder?: SourceFilesModel.Directory, config?: any) => Promise<void |
|
|
190
|
+
updateIntegration: (projectId: number, client: Crowdin, apiCredentials: any, request: UpdateIntegrationRequest, appRootFolder?: SourceFilesModel.Directory, config?: any) => Promise<void | ExtendedResult<void>>;
|
|
191
191
|
/**
|
|
192
192
|
* function to define configuration(settings) modal for you app (by default app will not have any custom settings)
|
|
193
193
|
*/
|
|
@@ -227,6 +227,10 @@ export interface IntegrationLogic {
|
|
|
227
227
|
* Enable integration search event
|
|
228
228
|
*/
|
|
229
229
|
integrationSearchListener?: boolean;
|
|
230
|
+
/**
|
|
231
|
+
* Enable integration next page event
|
|
232
|
+
*/
|
|
233
|
+
integrationPagination?: boolean;
|
|
230
234
|
}
|
|
231
235
|
export declare type ConfigurationModalEntity = FormField | ConfigurationDelimeter;
|
|
232
236
|
export interface ConfigurationDelimeter {
|
|
@@ -365,9 +369,10 @@ export interface FormField {
|
|
|
365
369
|
value: string;
|
|
366
370
|
}[];
|
|
367
371
|
}
|
|
368
|
-
export interface
|
|
372
|
+
export interface ExtendedResult<T> {
|
|
369
373
|
data?: T;
|
|
370
374
|
message?: string;
|
|
375
|
+
stopPagination?: boolean;
|
|
371
376
|
}
|
|
372
377
|
export declare type TreeItem = File | Folder;
|
|
373
378
|
/**
|
|
@@ -566,6 +571,10 @@ export interface UiModule {
|
|
|
566
571
|
* page name (default index.html)
|
|
567
572
|
*/
|
|
568
573
|
fileName?: string;
|
|
574
|
+
/**
|
|
575
|
+
* make module publicly available without crowdin context
|
|
576
|
+
*/
|
|
577
|
+
allowUnauthorized?: boolean;
|
|
569
578
|
}
|
|
570
579
|
export interface EditorPanels extends UiModule {
|
|
571
580
|
/**
|
package/out/util/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Request, Response } from 'express';
|
|
2
|
-
import { Config, Logger,
|
|
2
|
+
import { Config, Logger, ExtendedResult } from '../models';
|
|
3
3
|
export declare class CodeError extends Error {
|
|
4
4
|
code: number | undefined;
|
|
5
5
|
constructor(message: string, code?: number);
|
|
@@ -10,4 +10,4 @@ export declare function runAsyncWrapper(callback: Function, onError?: (e: any) =
|
|
|
10
10
|
export declare function encryptData(config: Config, data: string): string;
|
|
11
11
|
export declare function decryptData(config: Config, data: string): string;
|
|
12
12
|
export declare function executeWithRetry<T>(func: () => Promise<T>, numOfRetries?: number): Promise<T>;
|
|
13
|
-
export declare function
|
|
13
|
+
export declare function isExtendedResultType<T>(data?: T | ExtendedResult<T>): data is ExtendedResult<T>;
|
package/out/util/index.js
CHANGED
|
@@ -28,7 +28,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
28
28
|
});
|
|
29
29
|
};
|
|
30
30
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
-
exports.
|
|
31
|
+
exports.isExtendedResultType = exports.executeWithRetry = exports.decryptData = exports.encryptData = exports.runAsyncWrapper = exports.getMessage = exports.log = exports.CodeError = void 0;
|
|
32
32
|
const crypto = __importStar(require("crypto-js"));
|
|
33
33
|
const storage_1 = require("../storage");
|
|
34
34
|
class CodeError extends Error {
|
|
@@ -120,8 +120,8 @@ function executeWithRetry(func, numOfRetries = 2) {
|
|
|
120
120
|
});
|
|
121
121
|
}
|
|
122
122
|
exports.executeWithRetry = executeWithRetry;
|
|
123
|
-
function
|
|
123
|
+
function isExtendedResultType(data) {
|
|
124
124
|
const dataTyped = data;
|
|
125
|
-
return
|
|
125
|
+
return !Array.isArray(dataTyped);
|
|
126
126
|
}
|
|
127
|
-
exports.
|
|
127
|
+
exports.isExtendedResultType = isExtendedResultType;
|
|
@@ -40,6 +40,9 @@
|
|
|
40
40
|
{{#if integrationSearchListener}}
|
|
41
41
|
allow-filter-change-listener="true"
|
|
42
42
|
{{/if}}
|
|
43
|
+
{{#if integrationPagination}}
|
|
44
|
+
integration-load-more-files="true"
|
|
45
|
+
{{/if}}
|
|
43
46
|
integration-name="{{name}}"
|
|
44
47
|
integration-logo="logo.png"
|
|
45
48
|
>
|
|
@@ -229,13 +232,14 @@
|
|
|
229
232
|
.finally(() => (appComponent.setAttribute('is-crowdin-loading', false)));
|
|
230
233
|
}
|
|
231
234
|
|
|
232
|
-
function getIntegrationData(hardReload = false, parentId = '', search = '',) {
|
|
235
|
+
function getIntegrationData(hardReload = false, parentId = '', search = '', page = 0) {
|
|
233
236
|
appComponent.setAttribute('is-integration-loading', true);
|
|
234
237
|
checkOrigin()
|
|
235
|
-
.then(restParams => fetch(`api/integration/data${restParams}&parent_id=${parentId}&search=${search}`))
|
|
238
|
+
.then(restParams => fetch(`api/integration/data${restParams}&parent_id=${parentId}&search=${search}&page=${page}`))
|
|
236
239
|
.then(checkResponse)
|
|
237
240
|
.then((res) => {
|
|
238
241
|
const files = res.data;
|
|
242
|
+
const stopPagination = res.stopPagination;
|
|
239
243
|
const tree = files.map(e => {
|
|
240
244
|
const item = {
|
|
241
245
|
parent_id: e.parentId ? e.parentId : '0',
|
|
@@ -256,6 +260,13 @@
|
|
|
256
260
|
} else {
|
|
257
261
|
appComponent.pushIntegrationFilesData(tree);
|
|
258
262
|
}
|
|
263
|
+
{{#if integrationPagination}}
|
|
264
|
+
if (stopPagination) {
|
|
265
|
+
appComponent.querySelector('#integration-files').setAttribute('load-more-disabled', true);
|
|
266
|
+
} else {
|
|
267
|
+
appComponent.querySelector('#integration-files').setAttribute('load-more-disabled', false);
|
|
268
|
+
}
|
|
269
|
+
{{/if}}
|
|
259
270
|
if (search) {
|
|
260
271
|
const openIds = files.filter(e => !e.type).map(e => e.id);
|
|
261
272
|
appComponent.setIntegrationOpenedFolders(openIds);
|
|
@@ -422,6 +433,7 @@
|
|
|
422
433
|
|
|
423
434
|
document.addEventListener('keydown', (event) => {
|
|
424
435
|
if (event.keyCode == 27) {
|
|
436
|
+
|
|
425
437
|
if (infoModal) {
|
|
426
438
|
infoModal.close();
|
|
427
439
|
}
|
|
@@ -431,6 +443,12 @@
|
|
|
431
443
|
}
|
|
432
444
|
});
|
|
433
445
|
|
|
446
|
+
{{#if integrationPagination}}
|
|
447
|
+
document.body.addEventListener('fileListEnd', (e) => {
|
|
448
|
+
getIntegrationData(false, 0, '', e.detail.page)
|
|
449
|
+
})
|
|
450
|
+
{{/if}}
|
|
451
|
+
|
|
434
452
|
{{#if withCronSync}}
|
|
435
453
|
document.body.addEventListener('integrationScheduleSync', setIntegrationScheduleSync);
|
|
436
454
|
document.body.addEventListener('crowdinScheduleSync', setCrowdinScheduleSync);
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
<title></title>
|
|
6
6
|
<link rel="stylesheet" href="assets/css/styles.css">
|
|
7
7
|
<script type="module"
|
|
8
|
-
|
|
8
|
+
src="https://crowdin-web-components.s3.amazonaws.com/crowdin-web-components/crowdin-web-components.esm.js"></script>
|
|
9
9
|
<script nomodule=""
|
|
10
|
-
|
|
10
|
+
src="https://crowdin-web-components.s3.amazonaws.com/crowdin-web-components/crowdin-web-components.js"></script>
|
|
11
11
|
<script type="text/javascript" src="https://cdn.crowdin.com/apps/dist/iframe.js"></script>
|
|
12
12
|
<script type="text/javascript" src="assets/js/polyfills/promise.js"></script>
|
|
13
13
|
<script type="text/javascript" src="assets/js/polyfills/fetch.js"></script>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crowdin/app-project-module",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.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",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"test": "jest --config jestconfig.json"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@crowdin/crowdin-apps-functions": "0.3.
|
|
16
|
+
"@crowdin/crowdin-apps-functions": "0.3.1",
|
|
17
17
|
"@types/pg": "^8.6.5",
|
|
18
18
|
"crypto-js": "^4.0.0",
|
|
19
19
|
"express": "4.17.1",
|