@crowdin/app-project-module 0.98.0-cf-3 → 0.99.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.
Files changed (142) hide show
  1. package/out/index.js +12 -28
  2. package/out/middlewares/crowdin-client.d.ts +1 -1
  3. package/out/middlewares/integration-credentials.d.ts +1 -1
  4. package/out/middlewares/integration-credentials.js +1 -4
  5. package/out/middlewares/render-ui-module.d.ts +3 -3
  6. package/out/middlewares/render-ui-module.js +13 -9
  7. package/out/middlewares/ui-module.d.ts +1 -1
  8. package/out/middlewares/ui-module.js +1 -4
  9. package/out/modules/about.d.ts +1 -1
  10. package/out/modules/about.js +2 -8
  11. package/out/modules/ai-prompt-provider/handlers/compile.d.ts +1 -1
  12. package/out/modules/ai-prompt-provider/index.js +2 -4
  13. package/out/modules/ai-provider/handlers/chat-completions.d.ts +1 -1
  14. package/out/modules/ai-provider/handlers/get-model-list.d.ts +1 -1
  15. package/out/modules/ai-provider/index.js +2 -4
  16. package/out/modules/ai-request-processors/handler.d.ts +1 -1
  17. package/out/modules/ai-tools/handlers/tool-calls.d.ts +1 -1
  18. package/out/modules/ai-tools/index.js +1 -1
  19. package/out/modules/api/api.js +87 -2
  20. package/out/modules/context-menu/index.js +2 -2
  21. package/out/modules/custom-mt/handlers/translate.d.ts +1 -1
  22. package/out/modules/custom-mt/index.js +1 -3
  23. package/out/modules/custom-spell-check/handlers/get-languages-list.d.ts +1 -1
  24. package/out/modules/custom-spell-check/handlers/spell-check.d.ts +1 -1
  25. package/out/modules/custom-spell-check/index.js +4 -4
  26. package/out/modules/editor-right-panel/index.js +1 -1
  27. package/out/modules/external-qa-check/handlers/validate.d.ts +1 -1
  28. package/out/modules/external-qa-check/index.js +2 -2
  29. package/out/modules/file-processing/handlers/custom-file-format.d.ts +1 -1
  30. package/out/modules/file-processing/handlers/file-download.d.ts +1 -1
  31. package/out/modules/file-processing/handlers/pre-post-process.d.ts +1 -1
  32. package/out/modules/form-data-display.d.ts +1 -1
  33. package/out/modules/form-data-save.d.ts +1 -1
  34. package/out/modules/install.d.ts +1 -1
  35. package/out/modules/integration/handlers/crowdin-file-progress.d.ts +1 -1
  36. package/out/modules/integration/handlers/crowdin-files.d.ts +1 -1
  37. package/out/modules/integration/handlers/crowdin-files.js +6 -4
  38. package/out/modules/integration/handlers/crowdin-project.d.ts +1 -1
  39. package/out/modules/integration/handlers/crowdin-update.d.ts +1 -1
  40. package/out/modules/integration/handlers/crowdin-update.js +18 -4
  41. package/out/modules/integration/handlers/crowdin-webhook.d.ts +1 -1
  42. package/out/modules/integration/handlers/integration-data.d.ts +1 -1
  43. package/out/modules/integration/handlers/integration-data.js +17 -13
  44. package/out/modules/integration/handlers/integration-login.d.ts +1 -1
  45. package/out/modules/integration/handlers/integration-logout.d.ts +1 -1
  46. package/out/modules/integration/handlers/integration-update.d.ts +1 -1
  47. package/out/modules/integration/handlers/integration-webhook.d.ts +1 -1
  48. package/out/modules/integration/handlers/invite-users.d.ts +1 -1
  49. package/out/modules/integration/handlers/job-cancel.d.ts +1 -1
  50. package/out/modules/integration/handlers/job-info-deprecated.d.ts +4 -0
  51. package/out/modules/integration/handlers/job-info-deprecated.js +138 -0
  52. package/out/modules/integration/handlers/job-info.d.ts +1 -2
  53. package/out/modules/integration/handlers/job-info.js +20 -75
  54. package/out/modules/integration/handlers/job-list.d.ts +3 -0
  55. package/out/modules/integration/handlers/job-list.js +46 -0
  56. package/out/modules/integration/handlers/main.d.ts +1 -1
  57. package/out/modules/integration/handlers/main.js +3 -13
  58. package/out/modules/integration/handlers/oauth-login.d.ts +1 -1
  59. package/out/modules/integration/handlers/oauth-login.js +2 -10
  60. package/out/modules/integration/handlers/oauth-polling.d.ts +1 -1
  61. package/out/modules/integration/handlers/oauth-url.d.ts +1 -1
  62. package/out/modules/integration/handlers/settings-save.d.ts +1 -1
  63. package/out/modules/integration/handlers/settings.d.ts +1 -1
  64. package/out/modules/integration/handlers/sync-settings-save.d.ts +1 -1
  65. package/out/modules/integration/handlers/sync-settings.d.ts +1 -1
  66. package/out/modules/integration/handlers/user-errors.d.ts +1 -1
  67. package/out/modules/integration/handlers/users.d.ts +1 -1
  68. package/out/modules/integration/index.d.ts +1 -3
  69. package/out/modules/integration/index.js +37 -38
  70. package/out/modules/integration/types.d.ts +11 -1
  71. package/out/modules/integration/util/defaults.js +51 -45
  72. package/out/modules/integration/util/files.d.ts +1 -0
  73. package/out/modules/integration/util/files.js +52 -4
  74. package/out/modules/integration/util/job.d.ts +1 -1
  75. package/out/modules/integration/util/job.js +3 -2
  76. package/out/modules/integration/util/types.d.ts +8 -0
  77. package/out/modules/modal/index.js +2 -2
  78. package/out/modules/organization-menu/index.js +4 -6
  79. package/out/modules/organization-settings-menu/index.js +4 -6
  80. package/out/modules/profile-resources-menu/index.js +4 -6
  81. package/out/modules/profile-settings-menu/index.js +4 -6
  82. package/out/modules/project-menu/index.js +1 -1
  83. package/out/modules/project-menu-crowdsource/index.js +1 -1
  84. package/out/modules/project-reports/index.js +2 -4
  85. package/out/modules/project-tools/index.js +2 -4
  86. package/out/modules/status.d.ts +1 -1
  87. package/out/modules/subscription-paid.d.ts +1 -1
  88. package/out/modules/uninstall.d.ts +1 -1
  89. package/out/modules/webhooks/handlers/webhook-handler.d.ts +1 -1
  90. package/out/modules/workflow-step-type/handlers/delete-step.d.ts +1 -1
  91. package/out/modules/workflow-step-type/handlers/step-settings-save.d.ts +1 -1
  92. package/out/modules/workflow-step-type/index.js +2 -3
  93. package/out/storage/index.d.ts +2 -1
  94. package/out/storage/index.js +1 -8
  95. package/out/storage/mysql.d.ts +3 -2
  96. package/out/storage/mysql.js +42 -1
  97. package/out/storage/postgre.d.ts +3 -2
  98. package/out/storage/postgre.js +43 -4
  99. package/out/storage/sqlite.d.ts +3 -2
  100. package/out/storage/sqlite.js +38 -4
  101. package/out/types.d.ts +1 -18
  102. package/out/util/credentials-masker.d.ts +1 -1
  103. package/out/util/handlebars.d.ts +1 -0
  104. package/out/util/handlebars.js +46 -0
  105. package/out/util/index.d.ts +0 -2
  106. package/out/util/index.js +2 -14
  107. package/out/views/about.handlebars +102 -0
  108. package/out/views/error.handlebars +54 -0
  109. package/out/views/form.handlebars +30 -0
  110. package/out/views/install.handlebars +16 -0
  111. package/out/views/login.handlebars +331 -0
  112. package/out/views/main.handlebars +1901 -0
  113. package/out/views/oauth.handlebars +11 -0
  114. package/out/views/partials/head.handlebars +53 -0
  115. package/out/views/subscription.handlebars +26 -0
  116. package/package.json +11 -12
  117. package/out/storage/d1.d.ts +0 -99
  118. package/out/storage/d1.js +0 -769
  119. package/out/util/jsx-renderer.d.ts +0 -6
  120. package/out/util/jsx-renderer.js +0 -13
  121. package/out/util/static-files.d.ts +0 -19
  122. package/out/util/static-files.js +0 -82
  123. package/out/views/AboutPage.d.ts +0 -9
  124. package/out/views/AboutPage.js +0 -79
  125. package/out/views/ErrorPage.d.ts +0 -18
  126. package/out/views/ErrorPage.js +0 -56
  127. package/out/views/FormPage.d.ts +0 -13
  128. package/out/views/FormPage.js +0 -27
  129. package/out/views/InstallPage.d.ts +0 -10
  130. package/out/views/InstallPage.js +0 -22
  131. package/out/views/LoginPage.d.ts +0 -33
  132. package/out/views/LoginPage.js +0 -199
  133. package/out/views/MainPage.d.ts +0 -79
  134. package/out/views/MainPage.js +0 -1613
  135. package/out/views/OAuthPage.d.ts +0 -7
  136. package/out/views/OAuthPage.js +0 -17
  137. package/out/views/SubscriptionPage.d.ts +0 -7
  138. package/out/views/SubscriptionPage.js +0 -26
  139. package/out/views/index.d.ts +0 -13
  140. package/out/views/index.js +0 -25
  141. package/out/views/layout/Head.d.ts +0 -9
  142. package/out/views/layout/Head.js +0 -54
@@ -99,6 +99,7 @@ class SQLiteStorage {
99
99
  data varchar null,
100
100
  attempt varchar DEFAULT 0,
101
101
  errors varchar null,
102
+ processed_entities varchar null,
102
103
  created_at varchar not null,
103
104
  updated_at varchar null,
104
105
  finished_at varchar null
@@ -236,6 +237,7 @@ class SQLiteStorage {
236
237
  yield this.addColumns(['crowdin_id'], 'app_metadata');
237
238
  yield this.addColumn('job', 'attempt', 'DEFAULT 0');
238
239
  yield this.addColumn('job', 'errors', 'null');
240
+ yield this.addColumn('job', 'processed_entities', 'null');
239
241
  yield this.addColumn('integration_credentials', 'managers', 'null');
240
242
  });
241
243
  }
@@ -357,6 +359,7 @@ class SQLiteStorage {
357
359
  yield this.run('DELETE FROM job WHERE crowdin_id = ?', [id]);
358
360
  yield this.run('DELETE FROM translation_file_cache WHERE crowdin_id = ?', [id]);
359
361
  yield this.run('DELETE FROM unsynced_files WHERE crowdin_id = ?', [id]);
362
+ yield this.run('DELETE FROM synced_data WHERE crowdin_id = ?', [id]);
360
363
  });
361
364
  }
362
365
  saveIntegrationCredentials(id, credentials, crowdinId) {
@@ -391,6 +394,7 @@ class SQLiteStorage {
391
394
  yield this.run('DELETE FROM webhooks where integration_id = ?', [id]);
392
395
  yield this.run('DELETE FROM job where integration_id = ?', [id]);
393
396
  yield this.run('DELETE FROM unsynced_files where integration_id = ?', [id]);
397
+ yield this.run('DELETE FROM synced_data where integration_id = ?', [id]);
394
398
  });
395
399
  }
396
400
  deleteAllIntegrationCredentials(crowdinId) {
@@ -402,6 +406,7 @@ class SQLiteStorage {
402
406
  yield this.run('DELETE FROM user_errors where crowdin_id = ?', [crowdinId]);
403
407
  yield this.run('DELETE FROM job where crowdin_id = ?', [crowdinId]);
404
408
  yield this.run('DELETE FROM unsynced_files where crowdin_id = ?', [crowdinId]);
409
+ yield this.run('DELETE FROM synced_data where crowdin_id = ?', [crowdinId]);
405
410
  });
406
411
  }
407
412
  saveMetadata(id, metadata, crowdinId) {
@@ -577,7 +582,7 @@ class SQLiteStorage {
577
582
  return id;
578
583
  });
579
584
  }
580
- updateJob({ id, progress, status, info, data, attempt, errors }) {
585
+ updateJob({ id, progress, status, info, data, attempt, errors, processedEntities, }) {
581
586
  const updateFields = ['updated_at = ?'];
582
587
  const updateParams = [Date.now().toString()];
583
588
  if (progress) {
@@ -612,6 +617,10 @@ class SQLiteStorage {
612
617
  updateFields.push('errors = ?');
613
618
  updateParams.push(JSON.stringify(errors));
614
619
  }
620
+ if (processedEntities) {
621
+ updateFields.push('processed_entities = ?');
622
+ updateParams.push(processedEntities);
623
+ }
615
624
  updateParams.push(id);
616
625
  const query = `
617
626
  UPDATE job
@@ -624,7 +633,7 @@ class SQLiteStorage {
624
633
  return __awaiter(this, void 0, void 0, function* () {
625
634
  const row = yield this.get(`
626
635
  SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status,
627
- title, info, data, attempt, errors, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt
636
+ title, info, data, attempt, errors, processed_entities as processedEntities, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt
628
637
  FROM job
629
638
  WHERE id = ?
630
639
  `, [id]);
@@ -636,7 +645,7 @@ class SQLiteStorage {
636
645
  getActiveJobs({ integrationId, crowdinId }) {
637
646
  return __awaiter(this, void 0, void 0, function* () {
638
647
  return this.each(`
639
- SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status, title, info, data, attempt, errors, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt
648
+ SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status, title, info, data, attempt, errors, processed_entities as processedEntities, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt
640
649
  FROM job
641
650
  WHERE integration_id = ? AND crowdin_id = ? AND finished_at is NULL
642
651
  `, [integrationId, crowdinId]);
@@ -650,12 +659,37 @@ class SQLiteStorage {
650
659
  getAllInProgressJobs() {
651
660
  return __awaiter(this, void 0, void 0, function* () {
652
661
  return this.each(`
653
- SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status, title, info, data, attempt, errors, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt
662
+ SELECT id, integration_id as integrationId, crowdin_id as crowdinId, type, payload, progress, status, title, info, data, attempt, errors, processed_entities as processedEntities, created_at as createdAt, updated_at as updatedAt, finished_at as finishedAt
654
663
  FROM job
655
664
  WHERE status IN (?,?) AND finished_at is NULL
656
665
  `, [types_2.JobStatus.IN_PROGRESS, types_2.JobStatus.CREATED]);
657
666
  });
658
667
  }
668
+ getAllJobs({ integrationId, crowdinId, limit, offset }) {
669
+ return __awaiter(this, void 0, void 0, function* () {
670
+ return this.each(`
671
+ SELECT
672
+ id,
673
+ integration_id as integrationId,
674
+ crowdin_id as crowdinId,
675
+ type,
676
+ payload,
677
+ progress,
678
+ status,
679
+ title,
680
+ info,
681
+ data,
682
+ attempt,
683
+ created_at as createdAt,
684
+ updated_at as updatedAt,
685
+ finished_at as finishedAt
686
+ FROM job
687
+ WHERE integration_id = ? AND crowdin_id = ?
688
+ ORDER BY created_at DESC
689
+ LIMIT ? OFFSET ?
690
+ `, [integrationId, crowdinId, limit, offset]);
691
+ });
692
+ }
659
693
  saveTranslationCache({ integrationId, crowdinId, fileId, languageId, etag }) {
660
694
  return this.run(`
661
695
  INSERT
package/out/types.d.ts CHANGED
@@ -10,8 +10,6 @@ import { IntegrationLogic } from './modules/integration/types';
10
10
  import { Storage } from './storage';
11
11
  import { MySQLStorageConfig } from './storage/mysql';
12
12
  import { PostgreStorageConfig } from './storage/postgre';
13
- import { D1StorageConfig } from './storage/d1';
14
- import type { Fetcher } from '@cloudflare/workers-types';
15
13
  import { ApiModule } from './modules/api/types';
16
14
  import { LogErrorFunction, LogFunction } from './util/logger';
17
15
  import { AiProviderModule } from './modules/ai-provider/types';
@@ -102,14 +100,6 @@ export interface ClientConfig extends ImagePath {
102
100
  * config to configure MySQL as a storage
103
101
  */
104
102
  mysqlConfig?: MySQLStorageConfig;
105
- /**
106
- * config to configure Cloudflare D1 as a storage
107
- */
108
- d1Config?: D1StorageConfig;
109
- /**
110
- * Cloudflare Workers Assets configuration
111
- */
112
- assetsConfig?: AssetsConfig;
113
103
  /**
114
104
  * integration module logic
115
105
  */
@@ -273,18 +263,12 @@ export interface ClientConfig extends ImagePath {
273
263
  */
274
264
  restrictAiToSameApp?: boolean;
275
265
  }
276
- export interface AssetsConfig {
277
- /**
278
- * Cloudflare Workers Assets Fetcher
279
- */
280
- fetcher: Fetcher;
281
- }
282
266
  export interface Environments {
283
267
  environments?: Environment | Environment[];
284
268
  }
285
269
  type Environment = 'crowdin' | 'crowdin-enterprise';
286
270
  export type Config = ClientConfig & {
287
- baseUrl?: string;
271
+ baseUrl: string;
288
272
  clientId: string;
289
273
  clientSecret: string;
290
274
  port: number;
@@ -408,7 +392,6 @@ export interface CrowdinAppUtilities extends CrowdinMetadataStore {
408
392
  extra: Record<string, any>;
409
393
  }>;
410
394
  storage: Storage;
411
- cronExecutions?: Record<string, () => Promise<void>>;
412
395
  }
413
396
  export interface CrowdinMetadataStore {
414
397
  saveMetadata: (id: string, metadata: any, crowdinId: string) => Promise<void>;
@@ -6,5 +6,5 @@ declare function getRequestCredentialsMasker({ moduleConfig, dataPath, }: {
6
6
  moduleConfig?: UiModule;
7
7
  dataPath?: string;
8
8
  }): (req: Request | CrowdinClientRequest, res: Response, next: Function) => any;
9
- declare function postRequestCredentialsMasker(moduleConfig?: UiModule, credentialsExtractor?: Function): (req: Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>> | CrowdinClientRequest, res: Response<any, Record<string, any>>, next: Function) => void;
9
+ declare function postRequestCredentialsMasker(moduleConfig?: UiModule, credentialsExtractor?: Function): (req: CrowdinClientRequest | Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>, next: Function) => void;
10
10
  export { getRequestCredentialsMasker, postRequestCredentialsMasker, maskKey };
@@ -0,0 +1 @@
1
+ export declare const engine: (path: string, options: object, callback: (e: any, rendered: string) => void) => void;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.engine = void 0;
7
+ const express_handlebars_1 = __importDefault(require("express-handlebars"));
8
+ exports.engine = (0, express_handlebars_1.default)({
9
+ layoutsDir: '',
10
+ defaultLayout: '',
11
+ helpers: {
12
+ ifeq: function (a, b, options) {
13
+ if (a === b) {
14
+ return options.fn(this);
15
+ }
16
+ return options.inverse(this);
17
+ },
18
+ checkLength: function (a, b, options) {
19
+ if (a.length > b) {
20
+ return options.fn(this);
21
+ }
22
+ return options.inverse(this);
23
+ },
24
+ in: function (a, b, options) {
25
+ if (a !== undefined && a !== null && b !== undefined && b !== null) {
26
+ let bArray;
27
+ if (Array.isArray(b)) {
28
+ bArray = b.map((item) => item.toString());
29
+ }
30
+ else {
31
+ bArray = b.toString().split(' ');
32
+ }
33
+ if (bArray.includes(a === null || a === void 0 ? void 0 : a.toString())) {
34
+ return options.fn(this);
35
+ }
36
+ }
37
+ return options.inverse(this);
38
+ },
39
+ or: function (a, b, options) {
40
+ return a || b ? options.fn(this) : options.inverse(this);
41
+ },
42
+ incrementedIndex: function (index) {
43
+ return index + 1;
44
+ },
45
+ },
46
+ });
@@ -1,11 +1,9 @@
1
1
  import { Request, Response } from 'express';
2
2
  import { Config, CrowdinClientRequest, ImagePath, UnauthorizedConfig } from '../types';
3
- export { renderJSX } from './jsx-renderer';
4
3
  export declare class CodeError extends Error {
5
4
  code: number | undefined;
6
5
  constructor(message: string, code?: number);
7
6
  }
8
- export declare function extractBaseUrlFromRequest(req: Request): string;
9
7
  export declare function runAsyncWrapper(callback: Function): (req: Request | CrowdinClientRequest, res: Response, next: Function) => void;
10
8
  export declare function encryptData(config: Config, data: string): string;
11
9
  export declare function decryptData(config: Config, data: string): string;
package/out/util/index.js CHANGED
@@ -32,16 +32,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
32
32
  });
33
33
  };
34
34
  Object.defineProperty(exports, "__esModule", { value: true });
35
- exports.getFormattedDate = exports.validateEmail = exports.prepareFormDataMetadataId = exports.getPreviousDate = exports.isJson = exports.isAuthorizedConfig = exports.getLogoUrl = exports.executeWithRetry = exports.decryptData = exports.encryptData = exports.runAsyncWrapper = exports.extractBaseUrlFromRequest = exports.CodeError = exports.renderJSX = void 0;
35
+ exports.getFormattedDate = 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");
39
39
  const logger_1 = require("./logger");
40
40
  const crowdin_apps_functions_1 = require("@crowdin/crowdin-apps-functions");
41
- const jsx_renderer_1 = require("./jsx-renderer");
42
- const views_1 = require("../views");
43
- var jsx_renderer_2 = require("./jsx-renderer");
44
- Object.defineProperty(exports, "renderJSX", { enumerable: true, get: function () { return jsx_renderer_2.renderJSX; } });
45
41
  class CodeError extends Error {
46
42
  constructor(message, code) {
47
43
  super(message);
@@ -49,12 +45,6 @@ class CodeError extends Error {
49
45
  }
50
46
  }
51
47
  exports.CodeError = CodeError;
52
- function extractBaseUrlFromRequest(req) {
53
- const protocol = req.protocol;
54
- const host = req.get('host');
55
- return `${protocol}://${host}`;
56
- }
57
- exports.extractBaseUrlFromRequest = extractBaseUrlFromRequest;
58
48
  function isCrowdinClientRequest(req) {
59
49
  return req.crowdinContext;
60
50
  }
@@ -71,9 +61,7 @@ function handleError(err, req, res) {
71
61
  if (!res.headersSent) {
72
62
  const errorMessage = { message: (0, logger_1.getErrorMessage)(err), code };
73
63
  if ('integrationCredentials' in req) {
74
- const html = (0, jsx_renderer_1.renderJSX)(views_1.ErrorPage, errorMessage);
75
- res.setHeader('Content-Type', 'text/html; charset=utf-8');
76
- res.send(html);
64
+ res.render('error', errorMessage);
77
65
  }
78
66
  else {
79
67
  res.status(code).send({ error: errorMessage });
@@ -0,0 +1,102 @@
1
+ <html>
2
+ <head>
3
+ <title>{{name}}</title>
4
+ <meta charset="utf-8" />
5
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+
8
+ {{#if storeLink}}
9
+ <link rel="canonical" href="{{storeLink}}">
10
+ {{/if}}
11
+
12
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
13
+ <link href="/assets/css/about.css" media="screen" rel="stylesheet" type="text/css">
14
+ </head>
15
+ <body>
16
+ <header id="header" class="navbar navbar-expand-lg fixed-top crowdin-navbar">
17
+ <a itemprop="url" class="navbar-brand crowdin-navbar__logo" href="https://crowdin.com" title="Crowdin — Localization Management Platform">
18
+ <svg class="d-none d-md-block d-lg-block default-logo" width="180" height="54" aria-labelledby="crowdinLogoTitle">
19
+ <title id="crowdinLogoTitle">Crowdin</title>
20
+ <image class="d-none d-md-block d-lg-block" href="/assets/img/crowdin.svg" width="180" height="54" alt="Crowdin" />
21
+ </svg>
22
+ <svg class="d-block d-md-none d-lg-none default-logo" width="173" height="32">
23
+ <image class="d-block d-md-none d-lg-none" href="/assets/img/crowdin_small.svg" width="173" height="32" alt="Crowdin"/>
24
+ </svg>
25
+ </a>
26
+ <div class="flex-nowrap justify-content-lg-end ml-auto my-lg-0">
27
+ <div class="pr-0 ml-2 my-2">
28
+ <span class="crowdin-navbar__nav-link contact ">
29
+ <a title="Contact" href="https://crowdin.com/contacts">Contact</a>
30
+ </span>
31
+ </div>
32
+ </div>
33
+ </header>
34
+ <div class="main">
35
+ <div class="container">
36
+ <div class="row crwd__product-template__header align-items-center">
37
+
38
+ <div class="offset-lg-1 col-lg-2 product-single__photo-wrapper crwd__product-single__photo-wrapper">
39
+ <div class="product-single__photo text-center mb-3 mb-lg-0">
40
+ <div class="image-holder">
41
+ <img width="200" src="{{logo}}" alt="{{name}}" id="FeaturedImage-product-template" class="img-fluid lazyloaded ls-is-cached product-featured-img" style="position: unset;">
42
+ </div>
43
+ </div>
44
+ </div>
45
+ <div class=" col-lg-8 crwd__product-template__header__title-container">
46
+ <div class="product-single__meta">
47
+ <div class="section-header">
48
+ <h1 class="product-single__title text-center text-lg-left">
49
+ <span>{{name}}</span>
50
+ </h1>
51
+ <div class="description">
52
+ {{name}} extends the default functionality available in Crowdin and Crowdin Enterprise by providing additional capabilities to enhance your localization workflows.
53
+ </div>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ <div class="row">
59
+ <div class="offset-lg-1 col-lg-10">
60
+ <p>To install this app, you can use either of the following methods: </p>
61
+ <ol>
62
+ <li>Install it directly from the <a href="https://store.crowdin.com/" target="_blank">Crowdin Store</a>, if available.</li>
63
+ <li>Install it manually using the manifest URL below.</li>
64
+ </ol>
65
+ <p>For detailed steps, follow the <a href="https://support.crowdin.com/developer/crowdin-apps-installation/" target="_blank">App Installation</a></p>
66
+
67
+ <div class="mb-3">
68
+ <label for="manifest-url" class="form-label">Paste the following manifest URL when prompted during manual installation:</label>
69
+ <div class="input-group">
70
+ <input
71
+ type="text"
72
+ class="form-control"
73
+ id="manifest-url"
74
+ value="{{manifest}}"
75
+ readonly
76
+ >
77
+ <button class="btn btn-outline-secondary" type="button" id="copy-button">
78
+ Copy
79
+ </button>
80
+ </div>
81
+ </div>
82
+
83
+
84
+
85
+ {{#if storeLink}}
86
+ <div>Read more about <a href="{{storeLink}}" target="_blank" title="{{name}} on Crowdin Store">{{name}}</a>.</div>
87
+ {{/if}}
88
+ </div>
89
+ </div>
90
+ </div>
91
+ </div>
92
+ </body>
93
+ <script>
94
+ document.getElementById('copy-button').addEventListener('click', async () => {
95
+ const text = document.getElementById('manifest-url').value;
96
+ await navigator.clipboard.writeText(text);
97
+ const btn = document.getElementById('copy-button');
98
+ btn.textContent = 'Copied!';
99
+ setTimeout(() => btn.textContent = 'Copy', 1500);
100
+ });
101
+ </script>
102
+ </html>
@@ -0,0 +1,54 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ {{> head}}
4
+
5
+ <body>
6
+ <div class="i_w">
7
+ <div class="error-page">
8
+ <div>
9
+ <div class="error-page-message">
10
+ <crowdin-h3>{{ message }}</crowdin-h3>
11
+ {{#if owners}}
12
+ <crowdin-p>This integration allows only one connection per project to ensure consistent configuration and prevent file import or synchronization issues.</crowdin-p>
13
+ {{#if owners.[1]}}
14
+ <crowdin-p class="mt-2">Managed by:</crowdin-p>
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-p class="mt-2">To request access, please contact one of the project members listed above.</crowdin-p>
21
+ {{else}}
22
+ <crowdin-p class="mt-2">Managed by: <crowdin-a href="#" onclick="contactUser({{owners.[0].id}})">{{owners.[0].name}}</crowdin-a></crowdin-p>
23
+ <crowdin-p class="mt-2">To request access, please contact the project member listed above.</crowdin-p>
24
+ {{/if}}
25
+ {{/if}}
26
+ </div>
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}}
33
+ </div>
34
+ </div>
35
+ </div>
36
+ </body>
37
+
38
+ </html>
39
+ <script>
40
+ function integrationLogout() {
41
+ checkOrigin()
42
+ .then(queryParams => fetch(`api/logout${queryParams}`, { method: 'POST' }))
43
+ .then(checkResponse)
44
+ .then(reloadLocation)
45
+ .then(localStorage.removeItem('revised_{{name}}'))
46
+ .catch(e => catchRejection(e, 'Looks like you are not logged in'));
47
+ }
48
+
49
+ function contactUser(userId) {
50
+ AP.getContext(function(context) {
51
+ AP.redirect(`${context.organization_id ? '/u' : ''}/messages/create/${userId}`);
52
+ });
53
+ }
54
+ </script>
@@ -0,0 +1,30 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ {{> head}}
4
+ <body>
5
+ <div
6
+ class="i_w"
7
+ style='max-width:680px; position: relative;'
8
+ >
9
+ <crowdin-card
10
+ id="card"
11
+ is-shadowed
12
+ >
13
+ <div id="form"></div>
14
+ <div id="form-loading">
15
+ <crowdin-progress-indicator />
16
+ </div>
17
+ </crowdin-card>
18
+ </div>
19
+ <crowdin-toasts></crowdin-toasts>
20
+ <script>
21
+ /*<!--*/
22
+ var formGetDataUrl = '{{formGetDataUrl}}';
23
+ var formPostDataUrl = '{{formPostDataUrl}}';
24
+ var formSchema = {{{formSchema}}};
25
+ var formUiSchema = {{{formUiSchema}}};
26
+ /*-->*/
27
+ </script>
28
+ <script src="/assets/js/form.js"></script>
29
+ </body>
30
+ </html>
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ {{> head}}
4
+
5
+ <body>
6
+ <div class="i_w center">
7
+ <div>
8
+ <h1>Looks like {{ name }} is not installed yet!</h1>
9
+ <p>Contact your organization administrator to install it. More info on <a
10
+ href='https://support.crowdin.com'>Link
11
+ to how to install</a></p>
12
+ </div>
13
+ </div>
14
+ </body>
15
+
16
+ </html>