@crowdin/app-project-module 0.80.0 → 0.81.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.
@@ -499,9 +499,9 @@ function addDefaultApiEndpoints(app, config) {
499
499
  checkSubscriptionExpiration: true,
500
500
  moduleKey: config.projectIntegration.key,
501
501
  }), (req, res) => {
502
- var _a;
502
+ var _a, _b;
503
503
  let fields = [];
504
- if ((_a = config.projectIntegration) === null || _a === void 0 ? void 0 : _a.loginForm.fields) {
504
+ if ((_b = (_a = config.projectIntegration) === null || _a === void 0 ? void 0 : _a.loginForm) === null || _b === void 0 ? void 0 : _b.fields) {
505
505
  fields = getFormFields(config === null || config === void 0 ? void 0 : config.projectIntegration.loginForm.fields);
506
506
  }
507
507
  res.send({ fields });
@@ -47,19 +47,25 @@ function handle() {
47
47
  if (!hasManagerAccess) {
48
48
  return res.status(403).send({ error: 'Access denied' });
49
49
  }
50
+ const inviteRestricted = !isAdmin &&
51
+ 'invite_restrict_enabled' in req.crowdinContext.jwtPayload.context &&
52
+ !!req.crowdinContext.jwtPayload.context.invite_restrict_enabled;
50
53
  const usersWhoWillBeInvitedToOrganization = filterOrganizationUsers(usersToInvite);
51
54
  const usersWhoWillBeInvitedToProject = filterProjectUsers({
55
+ inviteRestricted,
52
56
  usersToInvite,
53
57
  projectMembers,
54
58
  organizationMembers,
55
59
  });
56
60
  const usersWhoNotIssetInApplicationInstallation = filterNotIssetApplicationUsers({
61
+ inviteRestricted,
57
62
  usersToInvite,
58
63
  applicationInstallation,
59
64
  projectMembers,
60
65
  organizationMembers,
61
66
  });
62
67
  const usersWhoNotIssetInIntegration = filterNotIssetIntegrationUsers({
68
+ inviteRestricted,
63
69
  usersToInvite,
64
70
  integrationCredentials,
65
71
  projectMembers,
@@ -69,6 +75,7 @@ function handle() {
69
75
  return res.send({
70
76
  data: {
71
77
  isAdmin,
78
+ inviteRestricted,
72
79
  editApplicationAvailable: applicationInstallation !== null,
73
80
  usersWhoWillBeInvitedToOrganization,
74
81
  usersWhoWillBeInvitedToProject,
@@ -81,6 +88,7 @@ function handle() {
81
88
  req,
82
89
  projectId,
83
90
  isAdmin,
91
+ inviteRestricted,
84
92
  usersToInvite,
85
93
  applicationInstallation,
86
94
  usersWhoWillBeInvitedToOrganization,
@@ -94,10 +102,10 @@ exports.default = handle;
94
102
  function filterOrganizationUsers(usersToInvite) {
95
103
  return usersToInvite.filter((identifier) => (0, util_1.validateEmail)(identifier)).map((name) => ({ name: `${name}` }));
96
104
  }
97
- function filterProjectUsers({ usersToInvite, projectMembers, organizationMembers, }) {
105
+ function filterProjectUsers({ inviteRestricted, usersToInvite, projectMembers, organizationMembers, }) {
98
106
  return usersToInvite
99
107
  .map((identifier) => {
100
- if ((0, util_1.validateEmail)(identifier)) {
108
+ if ((0, util_1.validateEmail)(identifier) && !inviteRestricted) {
101
109
  return { name: `${identifier}` };
102
110
  }
103
111
  const user = projectMembers.find((member) => member.id === +identifier);
@@ -113,7 +121,7 @@ function filterProjectUsers({ usersToInvite, projectMembers, organizationMembers
113
121
  })
114
122
  .filter(Boolean);
115
123
  }
116
- function filterNotIssetApplicationUsers({ usersToInvite, applicationInstallation, projectMembers, organizationMembers, }) {
124
+ function filterNotIssetApplicationUsers({ inviteRestricted, usersToInvite, applicationInstallation, projectMembers, organizationMembers, }) {
117
125
  let userIdentifiers = [];
118
126
  if (!applicationInstallation) {
119
127
  return [];
@@ -130,7 +138,7 @@ function filterNotIssetApplicationUsers({ usersToInvite, applicationInstallation
130
138
  }
131
139
  return userIdentifiers
132
140
  .map((identifier) => {
133
- if ((0, util_1.validateEmail)(identifier)) {
141
+ if ((0, util_1.validateEmail)(identifier) && !inviteRestricted) {
134
142
  return { name: `${identifier}` };
135
143
  }
136
144
  let user;
@@ -142,7 +150,7 @@ function filterNotIssetApplicationUsers({ usersToInvite, applicationInstallation
142
150
  })
143
151
  .filter(Boolean);
144
152
  }
145
- function filterNotIssetIntegrationUsers({ usersToInvite, integrationCredentials, projectMembers, organizationMembers, }) {
153
+ function filterNotIssetIntegrationUsers({ inviteRestricted, usersToInvite, integrationCredentials, projectMembers, organizationMembers, }) {
146
154
  let integrationManagers = [];
147
155
  if (integrationCredentials === null || integrationCredentials === void 0 ? void 0 : integrationCredentials.managers) {
148
156
  integrationManagers = JSON.parse(integrationCredentials.managers);
@@ -152,7 +160,7 @@ function filterNotIssetIntegrationUsers({ usersToInvite, integrationCredentials,
152
160
  if (integrationManagers.includes(`${identifier}`)) {
153
161
  return null;
154
162
  }
155
- if ((0, util_1.validateEmail)(identifier)) {
163
+ if ((0, util_1.validateEmail)(identifier) && !inviteRestricted) {
156
164
  return { name: `${identifier}` };
157
165
  }
158
166
  let user;
@@ -164,9 +172,12 @@ function filterNotIssetIntegrationUsers({ usersToInvite, integrationCredentials,
164
172
  })
165
173
  .filter(Boolean);
166
174
  }
167
- function inviteUsers({ req, projectId, isAdmin, usersToInvite, applicationInstallation, usersWhoWillBeInvitedToOrganization, usersWhoWillBeInvitedToProject, usersWhoNotIssetInApplicationInstallation, }) {
175
+ function inviteUsers({ req, projectId, isAdmin, inviteRestricted, usersToInvite, applicationInstallation, usersWhoWillBeInvitedToOrganization, usersWhoWillBeInvitedToProject, usersWhoNotIssetInApplicationInstallation, }) {
168
176
  return __awaiter(this, void 0, void 0, function* () {
169
177
  const client = req.crowdinApiClient;
178
+ if (inviteRestricted) {
179
+ usersWhoWillBeInvitedToOrganization = [];
180
+ }
170
181
  const alreadyAddedUserIds = yield inviteUsersToProject({
171
182
  client,
172
183
  projectId,
@@ -197,8 +197,10 @@ function register({ config, app }) {
197
197
  }
198
198
  // remove user errors
199
199
  cron.schedule('0 0 * * *', () => __awaiter(this, void 0, void 0, function* () {
200
- const date = (0, util_1.getPreviousDate)(integrationLogic.userErrorLifetimeDays);
201
- yield (0, storage_1.getStorage)().deleteAllUsersErrorsOlderThan(`${date.getTime()}`);
200
+ if (integrationLogic.userErrorLifetimeDays) {
201
+ const date = (0, util_1.getPreviousDate)(integrationLogic.userErrorLifetimeDays);
202
+ yield (0, storage_1.getStorage)().deleteAllUsersErrorsOlderThan(`${date.getTime()}`);
203
+ }
202
204
  }));
203
205
  if (integrationLogic.webhooks) {
204
206
  app.post(`${integrationLogic.webhooks.crowdinWebhookUrl
@@ -6,7 +6,7 @@ export interface IntegrationLogic extends ModuleKey {
6
6
  /**
7
7
  * Customize your app login form
8
8
  */
9
- loginForm: LoginForm;
9
+ loginForm?: LoginForm;
10
10
  /**
11
11
  * Define login process via OAuth2 protocol
12
12
  */
@@ -162,7 +162,7 @@ export interface IntegrationLogic extends ModuleKey {
162
162
  /**
163
163
  * The duration for storing user errors, default is 30 days.
164
164
  */
165
- userErrorLifetimeDays: number;
165
+ userErrorLifetimeDays?: number;
166
166
  /**
167
167
  * When true, folder filtering during automatic translation sync is bypassed, and the file tree is returned unchanged.
168
168
  */
@@ -71,7 +71,7 @@
71
71
  integration-one-level-fetching="true"
72
72
  {{/if}}
73
73
  {{#if integrationSearchListener}}
74
- allow-filter-change-listener="true"
74
+ allow-integration-filter-change-listener="true"
75
75
  {{/if}}
76
76
  {{#if integrationPagination}}
77
77
  integration-load-more-files="true"
@@ -166,6 +166,7 @@
166
166
  <tr class="organization-invite">
167
167
  <td>
168
168
  <crowdin-p>Registration in Organization</crowdin-p>
169
+ <div class="permission-description"></div>
169
170
  </td>
170
171
  <td class="affected-users"></td>
171
172
  <td class="status"></td>
@@ -1009,7 +1010,7 @@
1009
1010
  if (organizationInvite) {
1010
1011
  const organizationWillGrantElement = `<span class="badge badge-will-be-granted">Will Be Registered</span>`;
1011
1012
 
1012
- processUsersWhoWillBeInvited(organizationInvite, usersData.usersWhoWillBeInvitedToOrganization, organizationWillGrantElement, grantedElement);
1013
+ processUsersWhoWillBeInvitedToOrganization(organizationInvite, usersData, organizationWillGrantElement, grantedElement, notAvailableElement);
1013
1014
  }
1014
1015
 
1015
1016
  processUsersWhoWillBeInvited(projectInvite, usersData.usersWhoWillBeInvitedToProject, willGrantedElement, grantedElement);
@@ -1018,6 +1019,38 @@
1018
1019
  processUsersWhoWillBeInvitedApplicationSettings(usersData, willGrantedElement, grantedElement, notAvailableElement);
1019
1020
  }
1020
1021
 
1022
+ function processUsersWhoWillBeInvitedToOrganization(element, usersData, willGrantedElement, alreadyGrantedMessage, notAvailableElement) {
1023
+ const tooltip = element.querySelector('.status');
1024
+ const description = element.querySelector('.permission-description');
1025
+ const userList = element.querySelector('.affected-users');
1026
+
1027
+ description.classList.remove('text-warning');
1028
+ description.innerText = '';
1029
+
1030
+ const users = usersData.usersWhoWillBeInvitedToOrganization;
1031
+
1032
+ if (users.length) {
1033
+ if (usersData.inviteRestricted) {
1034
+ description.classList.add('text-warning');
1035
+ description.innerText = 'New user invitations are restricted to administrators.';
1036
+
1037
+ tooltip.innerHTML = notAvailableElement;
1038
+ } else {
1039
+ tooltip.innerHTML = willGrantedElement;
1040
+ }
1041
+
1042
+ let affectedUsers = '<ul>';
1043
+ for (const user of users) {
1044
+ affectedUsers += `<li><crowdin-p>${sanitizeHTML(user.name)}</crowdin-p></li>`;
1045
+ }
1046
+ affectedUsers += '</ul>';
1047
+ userList.innerHTML = affectedUsers;
1048
+ } else {
1049
+ tooltip.innerHTML = alreadyGrantedMessage;
1050
+ userList.innerHTML = alreadyGrantedMessage;
1051
+ }
1052
+ }
1053
+
1021
1054
  function processUsersWhoWillBeInvited(element, users, grantMessage, alreadyGrantedMessage) {
1022
1055
  const tooltip = element.querySelector('.status');
1023
1056
  const userList = element.querySelector('.affected-users');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdin/app-project-module",
3
- "version": "0.80.0",
3
+ "version": "0.81.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",