Изменения документа Licensed Extensions

Редактировал(а) Dmitriy Rybakov 20.06.2025, 16:46

От версии 3.1
отредактировано Andrey Burlaka
на 04.10.2024, 00:09
Изменить комментарий: Migrated property [scope] from class [XWiki.ConfigurableClass]
К версии 5.1
отредактировано Dmitriy Rybakov
на 20.06.2025, 16:46
Изменить комментарий: Install extension [com.xwiki.licensing:application-licensing-licensor-ui/1.29]

Сводка

Подробности

Свойства страницы
Автор документа
... ... @@ -1,1 +1,1 @@
1 -XWiki.burlakaas
1 +XWiki.rybakovds
Содержимое
... ... @@ -6,11 +6,14 @@
6 6   <fieldset class="header">
7 7   <legend>$services.localization.render('licensor.licenseManager.heading')</legend>
8 8   $services.localization.render('licensor.licenseManager.hint')
9 + <div class="required-by-hint">
10 + $services.icon.renderHTML('warning') : $services.localization.render('licensor.licenseManager.installedAsDependency.hint')
11 + </div
9 9   </fieldset>
10 10   {{/html}}
11 11   #set ($columns = ['name', 'version', 'status', 'support', 'userLimit', 'wiki', 'actions'])
12 12   #set ($columnsProperties = {
13 - 'name': {'filterable': false, 'sortable': false, 'link': 'auto'},
16 + 'name': {'filterable': false, 'sortable': false, 'link': 'auto', 'html': true},
14 14   'version': {'filterable': false, 'sortable': false},
15 15   'status': {'filterable': false, 'sortable': false, 'html': true},
16 16   'support': {'filterable': false, 'sortable': false},
... ... @@ -21,7 +21,7 @@
21 21   #set ($options = {
22 22   'resultPage':'Licenses.Code.LicenseJSON',
23 23   'translationPrefix' : 'licensor.',
24 - 'extraParams': "&showAllPaidExtensions=$!escapetool.url($request.showAllPaidExtensions)"
27 + 'extraParams': "&showTopLevelExtensions=$!escapetool.url($request.showTopLevelExtensions)"
25 25   })
26 26   #livetable('licenseManager' $columns $columnsProperties $options)
27 27   {{html clean="false"}}
... ... @@ -70,5 +70,7 @@
70 70   #displayOwnerDetailsForm
71 71   #displayLicensesLiveTable
72 72   #displayAddLicenseForm
76 + #feedbackForm
77 + #installedAsDependency
73 73  #end
74 74  {{/velocity}}
XWiki.ConfigurableClass[0]
Область видимости
... ... @@ -1,0 +1,1 @@
1 +WIKI
XWiki.JavaScriptExtension[0]
Код
... ... @@ -1,4 +1,11 @@
1 -define('licensor', ['jquery', 'xwiki-meta'], function($, xwikiMeta) {
1 +define('auto-update-button', {
2 + prefix: 'licensor.moreActions.autoUpgrade.',
3 + keys: [
4 + 'allow',
5 + 'prevent'
6 + ]
7 +});
8 +define('licensor', ['jquery', 'xwiki-meta', 'xwiki-l10n!auto-update-button'], function($, xwikiMeta, l10n) {
2 2   return {
3 3   addLicense: function(data) {
4 4   return $.post(
... ... @@ -48,11 +48,17 @@
48 48   new XWiki.Document('UpdateOwnerDetails', 'Licenses.Code').getURL('get', 'outputSyntax=plain'),
49 49   data
50 50   );
58 + },
59 + modifyAutoUpgradesAllowList: function(data) {
60 + return $.post(
61 + new XWiki.Document('UpdateAutomaticUpgradesAllowList', 'Licenses.Code').getURL('get', 'outputSyntax=plain'),
62 + data
63 + );
51 51   }
52 52   };
53 53  });
54 54  
55 -require(['jquery', 'licensor', 'xwiki-meta', 'xwiki-events-bridge', 'bootstrap'], function ($, licensor, xwikiMeta) {
68 +require(['jquery', 'licensor', 'xwiki-meta', 'xwiki-l10n!auto-update-button', 'xwiki-events-bridge', 'bootstrap'], function ($, licensor, xwikiMeta, l10n) {
56 56   //
57 57   // Owner Details Form
58 58   //
... ... @@ -118,7 +118,7 @@
118 118   // Remove "Extend Trial" buttons from livetable, if the trial licenses limits were reached.
119 119   var updateLivetableButtons = function() {
120 120   var instanceId = $('#instanceId').val();
121 - $('.licenseActions .btn-primary').each(function() {
134 + $('.licenseActions .licenseButton-extendTrial').each(function() {
122 122   // Compute the key to check in the localStorage.
123 123   var buttonData = JSON.parse($(this).attr('data-button'));
124 124   var featureId = buttonData.featureId;
... ... @@ -131,10 +131,22 @@
131 131   });
132 132   };
133 133  
147 + // An extensionId could have multiple rows in the livetable if is installed on multiple subwikis, but the allowlist
148 + // is applied to all namespaces.
149 + var updateExtensionOfOtherWikis = function(extensionId, isAutoUpgrade) {
150 + var similarExtensions = $("input[name='extensionId'][value='" + extensionId + "']");
151 + similarExtensions.each(function() {
152 + var autoUpgradeButton = $(this).siblings('.licenseButton-autoUpgrade');
153 + autoUpgradeButton.find('.action-icon').toggleClass('isAutoUpgrade', isAutoUpgrade);
154 + var translationKey = isAutoUpgrade ? 'prevent' : 'allow';
155 + autoUpgradeButton.prop('title', l10n.get(translationKey));
156 + });
157 + };
158 +
134 134   // xwiki:livetable:displayComplete might be triggered before the code from this jsx is executed. In this case, the
135 135   // livetable is also loaded before, because it is using Prototype.js which is loading before the page loads.
136 136   // Make sure that the livetable is loaded by checking for a row with some data.
137 - if ($('#licenseManager-display td.type').size() > 0) {
162 + if ($('#licenseManager-display td.type').length > 0) {
138 138   updateLivetableButtons();
139 139   }
140 140   $(document).on('xwiki:livetable:displayComplete', updateLivetableButtons);
... ... @@ -183,18 +183,10 @@
183 183   });
184 184   };
185 185  
186 - // Manages the license buttons from the licenses livetable. If a trial is requested, it is automatically generated
187 - // and installed, else the user is redirected to a page to buy a license.
188 - $('#licenseManager').on('click', '.licenseButton', function() {
211 + // Trigger Get Trial / Extend trial or Buy license actions.
212 + var triggerLicenseAction = function(buttonData) {
189 189   var ownerDetailsForm = $('#ownerDetails');
190 - if (!validateOwnerDetails(ownerDetailsForm)) {
191 - return;
192 - }
193 - // Update the owner details.
194 - var ownerDetails = ownerDetailsForm.serializeArray();
195 - licensor.updateOwnerDetails(ownerDetails);
196 - // Get the data from the license button.
197 - var buttonData = JSON.parse($(this).attr('data-button'));
214 + let ownerDetails = ownerDetailsForm.serializeArray();
198 198   if (buttonData.licenseType === 'TRIAL') {
199 199   ownerDetails.push.apply(ownerDetails, $.map(buttonData, function(value, name) {
200 200   return {name: name, value: value};
... ... @@ -207,6 +207,35 @@
207 207   ownerDetailsForm.append(extraFields).submit();
208 208   extraFields.remove();
209 209   };
227 + };
228 +
229 + // Manages the license buttons from the licenses livetable. If a trial is requested, it is automatically generated
230 + // and installed, else the user is redirected to a page to buy a license.
231 + $('#licenseManager').on('click', '.licenseButton', function(e) {
232 + e.preventDefault();
233 + var ownerDetailsForm = $('#ownerDetails');
234 + if (!validateOwnerDetails(ownerDetailsForm)) {
235 + return;
236 + }
237 + // Update the owner details.
238 + var ownerDetails = ownerDetailsForm.serializeArray();
239 + licensor.updateOwnerDetails(ownerDetails);
240 + // Get data from the license button.
241 + var buttonData = JSON.parse($(this).attr('data-button'));
242 + // If this extension was installed as dependency, ask for a confirmation before continuing this action, since it
243 + // would be better for the user to get a license for the top level licensed extension.
244 + const requiredByInfo = $($($(this).parents('td.actions')[0]).siblings('td.name')[0]).find('.required-by-info');
245 + if (requiredByInfo.size() > 0) {
246 + const parentExtensionNode = $('#installed-as-dependency').find('.parent-extensions');
247 + parentExtensionNode.empty();
248 + requiredByInfo.data('parent-extensions').split(',').forEach((extension) => {
249 + parentExtensionNode.append("<li>"+ extension.trim() + "</li>");
250 + });
251 + $('#installed-as-dependency').data('buttonData', buttonData);
252 + $('#installed-as-dependency').modal('show');
253 + } else {
254 + triggerLicenseAction(buttonData);
255 + }
210 210   });
211 211  
212 212   // Show a popover on the "Check for Updates" button to let the user know what to do after he pays for the license.
... ... @@ -227,7 +227,7 @@
227 227   updateLicensesButton.focus().popover('show');
228 228   });
229 229  
230 - $('#updateLicenses').click(function() {
276 + $('#updateLicenses').on('click', function() {
231 231   var updateButton = $(this);
232 232   updateButton.prop('disabled', true);
233 233   // Show a notification message.
... ... @@ -240,8 +240,70 @@
240 240   });
241 241   });
242 242  
289 + $(document).on('click', '.licenseButton-autoUpgrade', function(e) {
290 + e.preventDefault();
291 + var autoUpgradeButton = $(this);
292 + var autoUpgradeForm = autoUpgradeButton.closest('form').serializeArray();
293 + licensor.modifyAutoUpgradesAllowList(autoUpgradeForm).success(function(data) {
294 + autoUpgradeButton.find('.action-icon').toggleClass('isAutoUpgrade', data.isAutoUpgrade);
295 + if(data.isAutoUpgrade) {
296 + autoUpgradeButton.prop('title', l10n.get('prevent'));
297 + new XWiki.widgets.Notification(
298 + $jsontool.serialize($services.localization.render('licensor.moreActions.autoUpgrade.added')), 'done');
299 + } else {
300 + autoUpgradeButton.prop('title', l10n.get('allow'));
301 + new XWiki.widgets.Notification(
302 + $jsontool.serialize($services.localization.render('licensor.moreActions.autoUpgrade.removed')), 'done');
303 + }
304 + updateExtensionOfOtherWikis(data.extensionId, data.isAutoUpgrade);
305 + });
306 + });
307 +
308 + $(document).on('click', '.licenseButton-feedback', function(e) {
309 + // Get the data needed in the form.
310 + const buttonData = JSON.parse($(this).attr('data-button'));
311 + const ownerDetails = {};
312 + $('#ownerDetails').serializeArray().map(x => ownerDetails[x.name] = x.value);
313 + if (typeof MauticSDK !== 'undefined') {
314 + // Make sure no messages or errors remained from the previous submit.
315 + const formName = $('#feedbackFormModal').find('.webmecanikForm form').attr('data-mautic-form');
316 + MauticSDK.getValidator(formName).clearErrors();
317 + MauticSDK.getValidator(formName).setMessage('', 'message');
318 + // Prefill values.
319 + const fieldPrefix = '#mauticform_input_' + formName + '_';
320 + $(fieldPrefix + 'email').val(ownerDetails['email']);
321 + const instanceId = $(fieldPrefix + 'instance_id');
322 + instanceId.val(ownerDetails['instanceId']);
323 + const featureId = $(fieldPrefix + 'name_of_the_app');
324 + featureId.val(buttonData['featureId']);
325 +
326 + // Show the feedback form modal.
327 + $('#feedbackFormModal').modal('show');
328 + } else {
329 + const params = $.param({
330 + 'email': ownerDetails['email'],
331 + 'instanceId': ownerDetails['instanceId'],
332 + 'featureId': buttonData['featureId']
333 + });
334 +
335 + const feedbackURL = buttonData['storeFeedbackURL'] + '?' + params;
336 + window.open(feedbackURL, '_blank').focus();
337 + }
338 + });
339 +
340 + // Continue license action after user confirmation.
341 + $(document).on('click', '#installed-as-dependency .btn-primary', function(e) {
342 + e.preventDefault();
343 + triggerLicenseAction($('#installed-as-dependency').data('buttonData'));
344 + });
345 +
346 + $(document).on('hide.bs.modal', '#installed-as-dependency', function() {
347 + $('#installed-as-dependency').removeData('buttonData');
348 + $('#installed-as-dependency').find('.parent-extensions').empty();
349 + });
350 +
243 243   // Set the documentation links to open in new tab.
244 244   $('#licenseManager-display td.name a').each(function() {
245 - $(this).attr('target', '_blank')
353 + $(this).attr('target', '_blank');
246 246   });
247 247  });
XWiki.StyleSheetExtension[0]
Код
... ... @@ -6,11 +6,31 @@
6 6   margin-top: .7em;
7 7  }
8 8  
9 -/* Duplicate some rules from style.css for input[type='email'].
10 - Should be removed when https://jira.xwiki.org/browse/XWIKI-13803 is implemented.*/
11 -.xform input[type="email"] {
9 +/* .dropleft to be used after moving to Bootstrap 4 */
10 +.dropdown-left {
11 + right: 0;
12 + left: auto;
13 +}
14 +
15 +#licenseManager-display .required-by-info:hover {
16 + color: @gray;
17 +}
18 +
19 +.required-by-hint {
20 + padding-top: 2%;
21 +}
22 +
23 +#installed-as-dependency .parent-extensions li {
24 + padding-top: 1%;
25 + font-weight: bold;
26 +}
27 +
28 +.licenseButton-paid .action-icon, .licenseButton-extendPaid .action-icon, .isAutoUpgrade {
29 + color: @brand-success;
30 +}
31 +.licenseButton-trial .action-icon, .licenseButton-extendTrial .action-icon {
32 + color: @link-color;
33 +}
34 +.webmecanikForm input[type=text], .webmecanikForm input[type=email] {
12 12   width: 100%;
13 - padding: 6px 12px;
14 - border: 1px solid #ccc;
15 - border-radius: 4px;
16 16  }
Content Type
... ... @@ -1,1 +1,1 @@
1 -CSS
1 +LESS
Разбор содержимого
... ... @@ -1,1 +1,1 @@
1 -Нет
1 +Да