2649c669277b1d66465e90044230e9c0dd8e2af4
[Packages/TYPO3.CMS.git] / typo3 / sysext / extensionmanager / Resources / Public / JavaScript / Main.js
1 /*
2 * This file is part of the TYPO3 CMS project.
3 *
4 * It is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License, either version 2
6 * of the License, or any later version.
7 *
8 * For the full copyright and license information, please read the
9 * LICENSE.txt file that was distributed with this source code.
10 *
11 * The TYPO3 project - inspiring people to share!
12 */
13 /**
14 * main logic holding everything together, consists of multiple parts
15 * ExtensionManager => Various functions for displaying the extension list / sorting
16 * Repository => Various AJAX functions for TER downloads
17 * ExtensionManager.Update => Various AJAX functions to display updates
18 * ExtensionManager.uploadForm => helper to show the upload form
19 */
20 define(['jquery', 'datatables', 'jquery/jquery.clearable'], function($) {
21 var ExtensionManager = {
22 identifier: {
23 extensionlist: '#typo3-extension-list',
24 searchField: '#Tx_Extensionmanager_extensionkey',
25 extensionManager: '.typo3-extension-manager'
26 }
27 };
28
29 ExtensionManager.manageExtensionListing = function() {
30 var $searchField = $(this.identifier.searchField),
31 dataTable = $(this.identifier.extensionlist).DataTable({
32 paging: false,
33 jQueryUI: true,
34 dom: 'lrtip',
35 lengthChange: false,
36 pageLength: 15,
37 stateSave: true,
38 drawCallback: this.bindExtensionListActions,
39 columns: [
40 null,
41 null,
42 null,
43 null,
44 {
45 type: 'version'
46 }, {
47 orderable: false
48 },
49 null
50 ]
51 });
52
53 $searchField.parents('form').on('submit', function() {
54 return false;
55 });
56
57 var getVars = ExtensionManager.getUrlVars();
58
59 // restore filter
60 var currentSearch = (getVars['search'] ? getVars['search'] : dataTable.search());
61 $searchField.val(currentSearch);
62
63 $searchField.on('input', function(e) {
64 dataTable.search($(this).val()).draw();
65 });
66
67 return dataTable;
68 };
69
70 ExtensionManager.bindExtensionListActions = function() {
71 $('.removeExtension').not('.transformed').each(function() {
72 var $me = $(this);
73 $me.data('href', $me.attr('href'));
74 $me.attr('href', '#');
75 $me.addClass('transformed');
76 $me.click(function() {
77 var $extManager = $(ExtensionManager.identifier.extensionManager);
78 TYPO3.Dialog.QuestionDialog({
79 title: TYPO3.l10n.localize('extensionList.removalConfirmation.title'),
80 msg: TYPO3.l10n.localize('extensionList.removalConfirmation.question'),
81 url: $me.data('href'),
82 fn: function(button, dummy, dialog) {
83 if (button == 'yes') {
84 $extManager.mask();
85 $.ajax({
86 url: dialog.url,
87 success: function() {
88 location.reload();
89 },
90 error: function() {
91 $extManager.unmask();
92 }
93 });
94 }
95 }
96 });
97 });
98 });
99
100 $('.t3-icon-system-extension-update').parent().each(function() {
101 var $me = $(this);
102 $me.data('href', $me.attr('href'));
103 $me.attr('href', '#');
104 $me.addClass('transformed');
105 $me.click(function() {
106 $(ExtensionManager.identifier.extensionManager).mask();
107 $.ajax({
108 url: $(this).data('href'),
109 dataType: 'json',
110 success: ExtensionManager.updateExtension
111 });
112 });
113 });
114 };
115
116 ExtensionManager.getUrlVars = function() {
117 var vars = [], hash;
118 var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
119 for (var i = 0; i < hashes.length; i++) {
120 hash = hashes[i].split('=');
121 vars.push(hash[0]);
122 vars[hash[0]] = hash[1];
123 }
124 return vars;
125 };
126
127 $.fn.dataTableExt.oSort['version-asc'] = function(a, b) {
128 var result = ExtensionManager.compare(a, b);
129 return result * -1;
130 };
131
132 $.fn.dataTableExt.oSort['version-desc'] = function(a, b) {
133 return ExtensionManager.compare(a, b);
134 };
135
136 ExtensionManager.compare = function(a, b) {
137 if (a === b) {
138 return 0;
139 }
140
141 var a_components = a.split(".");
142 var b_components = b.split(".");
143
144 var len = Math.min(a_components.length, b_components.length);
145
146 // loop while the components are equal
147 for (var i = 0; i < len; i++) {
148 // A bigger than B
149 if (parseInt(a_components[i]) > parseInt(b_components[i])) {
150 return 1;
151 }
152
153 // B bigger than A
154 if (parseInt(a_components[i]) < parseInt(b_components[i])) {
155 return -1;
156 }
157 }
158
159 // If one's a prefix of the other, the longer one is greaRepository.
160 if (a_components.length > b_components.length) {
161 return 1;
162 }
163
164 if (a_components.length < b_components.length) {
165 return -1;
166 }
167 // Otherwise they are the same.
168 return 0;
169 };
170
171 ExtensionManager.updateExtension = function(data) {
172 var message = '<h1>' + TYPO3.l10n.localize('extensionList.updateConfirmation.title') + '</h1>';
173 message += '<h2>' + TYPO3.l10n.localize('extensionList.updateConfirmation.message') + '</h2>';
174 $.each(data.updateComments, function(version, comment) {
175 message += '<h3>' + version + '</h3>';
176 message += '<div>' + comment + '</div>';
177 });
178
179 TYPO3.Dialog.QuestionDialog({
180 title: TYPO3.l10n.localize('extensionList.updateConfirmation.questionVersionComments'),
181 msg: message,
182 width: 600,
183 url: data.url,
184 fn: function(button, dummy, dialog) {
185 var $extManager = $(ExtensionManager.identifier.extensionManager);
186 if (button == 'yes') {
187 $.ajax({
188 url: dialog.url,
189 dataType: 'json',
190 success: function(data) {
191 if (data.hasErrors) {
192 top.TYPO3.Flashmessage.display(
193 top.TYPO3.Severity.error,
194 TYPO3.l10n.localize('downloadExtension.updateExtension.error'),
195 data.errorMessage,
196 15
197 );
198 } else {
199 top.TYPO3.Flashmessage.display(
200 top.TYPO3.Severity.info,
201 TYPO3.l10n.localize('extensionList.updateFlashMessage.title'),
202 TYPO3.l10n.localize('extensionList.updateFlashMessage.message').replace(/\{0\}/g, data.extension),
203 15
204 );
205 }
206 $extManager.unmask();
207 },
208 error: function(jqXHR, textStatus, errorThrown) {
209 // Create an error message with diagnosis info.
210 var errorMessage = textStatus + '(' + errorThrown + '): ' + jqXHR.responseText;
211
212 top.TYPO3.Flashmessage.display(
213 top.TYPO3.Severity.error,
214 TYPO3.l10n.localize('downloadExtension.updateExtension.error'),
215 errorMessage,
216 15
217 );
218 $extManager.unmask();
219 }
220 });
221 } else {
222 $extManager.unmask();
223 }
224 }
225 });
226 };
227
228 /**
229 * configuration properties
230 */
231 ExtensionManager.configurationFieldSupport = function() {
232 $('.offset').each(function() {
233 var $me = $(this),
234 $parent = $me.parent();
235 $me.hide();
236
237 var val = $me.attr('value');
238 var valArr = val.split(',');
239
240 $me.wrap('<div class="offsetSelector"></div>');
241 $parent.append('x: <input value="' + $.trim(valArr[0]) + '" class="tempOffset1 tempOffset">');
242 $parent.append('<span>, </span>');
243 $parent.append('y: <input value="' + $.trim(valArr[1]) + '" class="tempOffset2 tempOffset">');
244
245 $me.siblings('.tempOffset').keyup(function() {
246 $me.siblings('.offset').attr(
247 'value',
248 $parent.children('.tempOffset1').attr('value') + ',' + $parent.children('.tempOffset2').attr('value'));
249 });
250 });
251
252 $('.wrap').each(function() {
253 var $me = $(this),
254 $parent = $me.parent();
255 $me.hide();
256
257 var val = $me.attr('value');
258 var valArr = val.split('|');
259
260 $me.wrap('<div class="wrapSelector"></div>');
261 $parent.append('<input value="' + $.trim(valArr[0]) + '" class="tempWrap1 tempWrap">');
262 $parent.append('<span>|</span>');
263 $parent.append('<input value="' + $.trim(valArr[1]) + '" class="tempWrap2 tempWrap">');
264
265 $me.siblings('.tempWrap').keyup(function() {
266 $me.siblings('.wrap').attr(
267 'value',
268 $parent.children('.tempWrap1').attr('value') + '|' + $parent.children('.tempWrap2').attr('value'));
269 });
270 });
271 };
272
273 var Repository = {
274 downloadPath: '',
275 identifier: {
276 extensionManager: '.typo3-extension-manager'
277 }
278 };
279
280 Repository.initDom = function() {
281 $('#terTable').DataTable({
282 jQueryUI: true,
283 lengthChange: false,
284 pageLength: 15,
285 stateSave: false,
286 info: false,
287 paging: false,
288 searching: false,
289 ordering: false,
290 drawCallback: Repository.bindDownload
291 });
292
293 $('#terVersionTable').DataTable({
294 jQueryUI: true,
295 lengthChange: false,
296 pageLength: 15,
297 stateSave: false,
298 info: false,
299 paging: false,
300 searching: false,
301 drawCallback: Repository.bindDownload,
302 order: [
303 [2, 'asc']
304 ],
305 columns: [
306 {orderable: false},
307 null,
308 {type: 'version'},
309 null,
310 null,
311 null
312 ]
313 });
314
315 $('#terSearchTable').DataTable({
316 paging: false,
317 jQueryUI: true,
318 lengthChange: false,
319 stateSave: false,
320 searching: false,
321 language: {
322 search: 'Filter results:'
323 },
324 ordering: false,
325 drawCallback: Repository.bindDownload
326 });
327
328 Repository.bindDownload();
329 Repository.bindSearchFieldResetter();
330 };
331
332 Repository.bindDownload = function() {
333 var installButtons = $('.downloadFromTer form.download button[type=submit]');
334 installButtons.off('click');
335 installButtons.on('click', function(event) {
336 event.preventDefault();
337 $(Repository.identifier.extensionManager).mask();
338 var url = $(event.currentTarget.form).attr('data-href');
339 Repository.downloadPath = $(event.currentTarget.form).find('input.downloadPath:checked').val();
340 $.ajax({
341 url: url,
342 dataType: 'json',
343 success: Repository.getDependencies
344 });
345 });
346 };
347
348 Repository.getDependencies = function(data) {
349 if (data.hasDependencies) {
350 TYPO3.Dialog.QuestionDialog({
351 title: data.title,
352 msg: data.message,
353 url: data.url + '&tx_extensionmanager_tools_extensionmanagerextensionmanager[downloadPath]=' + Repository.downloadPath,
354 fn: Repository.getResolveDependenciesAndInstallResult
355 });
356 } else {
357 if(data.hasErrors) {
358 $(Repository.identifier.extensionManager).unmask();
359 top.TYPO3.Flashmessage.display(top.TYPO3.Severity.error, data.title, data.message, 15);
360 } else {
361 var button = 'yes';
362 var dialog = [];
363 var dummy = '';
364 dialog['url'] = data.url + '&tx_extensionmanager_tools_extensionmanagerextensionmanager[downloadPath]=' + Repository.downloadPath;
365 Repository.getResolveDependenciesAndInstallResult(button, dummy, dialog);
366 }
367 }
368 return false;
369 };
370
371 Repository.getResolveDependenciesAndInstallResult = function(button, dummy, dialog) {
372 var $emViewport = $(Repository.identifier.extensionManager);
373 if (button === 'yes') {
374 var newUrl = dialog.url;
375 $.ajax({
376 url: newUrl,
377 dataType: 'json',
378 success: function (data) {
379 $emViewport.unmask();
380 if (data.errorCount > 0) {
381 TYPO3.Dialog.QuestionDialog({
382 title: data.errorTitle,
383 msg: data.errorMessage,
384 url: data.skipDependencyUri,
385 fn: function (button, dummy, dialog) {
386 if (button == 'yes') {
387 $emViewport.mask();
388 Repository.getResolveDependenciesAndInstallResult('yes', dummy, dialog);
389 }
390 }
391 });
392 } else {
393 var successMessage = TYPO3.l10n.localize('extensionList.dependenciesResolveDownloadSuccess.message').replace(/\{0\}/g, data.extension) + ' <br />';
394 successMessage += '<br /><h3>' + TYPO3.l10n.localize('extensionList.dependenciesResolveDownloadSuccess.header') + ':</h3>';
395 $.each(data.result, function(index, value) {
396 successMessage += TYPO3.l10n.localize('extensionList.dependenciesResolveDownloadSuccess.item') + ' ' + index + ':<br /><ul>';
397 $.each(value, function(extkey, extdata) {
398 successMessage += '<li>' + extkey + '</li>';
399 });
400 successMessage += '</ul>';
401 });
402 top.TYPO3.Flashmessage.display(top.TYPO3.Severity.info, TYPO3.l10n.localize('extensionList.dependenciesResolveFlashMessage.title').replace(/\{0\}/g, data.extension), successMessage, 15);
403 }
404 }
405 });
406 } else {
407 $emViewport.unmask();
408 }
409 };
410
411 Repository.bindSearchFieldResetter = function() {
412 var $searchFields = $('.typo3-extensionmanager-searchTerForm input[type="text"]');
413 var searchResultShown = ('' !== $searchFields.first().val());
414
415 $searchFields.clearable(
416 {
417 onClear: function() {
418 if (searchResultShown) {
419 $(this).parents('form').first().submit();
420 }
421 }
422 }
423 );
424 };
425
426 ExtensionManager.Update = {
427 identifier: {
428 terUpdateAction: '.update-from-ter',
429 pagination: '.pagination-wrap',
430 splashscreen: '.splash-receivedata',
431 terTableWrapper: '#terTableWrapper',
432 terTableDataTableWrapper: '#terTableWrapper .dataTables_wrapper'
433 }
434 };
435
436 // Register "update from ter" action
437 ExtensionManager.Update.initializeEvents = function() {
438 $(ExtensionManager.Update.identifier.terUpdateAction).each(function() {
439
440 // "this" is the form which updates the extension list from
441 // TER on submit
442 var $me = $(this),
443 updateURL = $(this).attr('action');
444
445 $me.attr('action', '#');
446 $me.submit(function() {
447 // Force update on click.
448 ExtensionManager.Update.updateFromTer(updateURL, 1);
449
450 // Prevent normal submit action.
451 return false;
452 });
453
454 // This might give problems when there are more "update"-buttons,
455 // each one would trigger a TER-ExtensionManager.Update.
456 ExtensionManager.Update.updateFromTer(updateURL, 0);
457 });
458 };
459
460 ExtensionManager.Update.updateFromTer = function(url, forceUpdate) {
461 if (forceUpdate == 1) {
462 url = url + '&tx_extensionmanager_tools_extensionmanagerextensionmanager%5BforceUpdateCheck%5D=1';
463 }
464
465 // Hide triggers for TER update
466 $(ExtensionManager.Update.identifier.terUpdateAction).addClass('is-hidden');
467
468 // Show loaders
469 $(ExtensionManager.Update.identifier.splashscreen).addClass('is-shown');
470 $(ExtensionManager.Update.identifier.terTableDataTableWrapper).addClass('is-loading');
471 $(ExtensionManager.Update.identifier.pagination).addClass('is-loading');
472
473 $.ajax({
474 url: url,
475 dataType: 'json',
476 cache: false,
477 success: function(data) {
478 // Something went wrong, show message
479 if (data.errorMessage.length) {
480 top.TYPO3.Flashmessage.display(top.TYPO3.Severity.warning, TYPO3.l10n.localize('extensionList.updateFromTerFlashMessage.title'), data.errorMessage, 10);
481 }
482
483 // Message with latest updates
484 var $lastUpdate = $(ExtensionManager.Update.identifier.terUpdateAction + ' .time-since-last-update');
485 $lastUpdate.text(data.timeSinceLastUpdate);
486 $lastUpdate.attr(
487 'title',
488 TYPO3.l10n.localize('extensionList.updateFromTer.lastUpdate.timeOfLastUpdate') + data.lastUpdateTime
489 );
490
491 if (data.updated) {
492 $.ajax({
493 url: window.location.href + '&tx_extensionmanager_tools_extensionmanagerextensionmanager%5Bformat%5D=json',
494 dataType: 'json',
495 success: function(data) {
496 $(ExtensionManager.Update.identifier.terTableWrapper).html(data);
497 ExtensionManager.Update.transformPaginatorToAjax();
498 }
499 });
500 }
501 },
502 error: function(jqXHR, textStatus, errorThrown) {
503 // Create an error message with diagnosis info.
504 var errorMessage = textStatus + '(' + errorThrown + '): ' + jqXHR.responseText;
505
506 top.TYPO3.Flashmessage.display(
507 top.TYPO3.Severity.warning,
508 TYPO3.l10n.localize('extensionList.updateFromTerFlashMessage.title'),
509 errorMessage,
510 10
511 );
512 },
513 complete: function() {
514 // Hide loaders
515 $(ExtensionManager.Update.identifier.splashscreen).removeClass('is-shown');
516 $(ExtensionManager.Update.identifier.terTableDataTableWrapper).removeClass('is-loading');
517 $(ExtensionManager.Update.identifier.pagination).removeClass('is-loading');
518
519 // Show triggers for TER-update
520 $(ExtensionManager.Update.identifier.terUpdateAction).removeClass('is-hidden');
521 }
522 });
523 };
524
525 ExtensionManager.Update.transformPaginatorToAjax = function () {
526 $(ExtensionManager.Update.identifier.pagination + ' a').each(function() {
527 var $me = $(this);
528 $me.data('href', $(this).attr('href'));
529 $me.attr('href', '#');
530 $me.click(function() {
531 var $terTableWrapper = $(ExtensionManager.Update.identifier.terTableWrapper);
532 $terTableWrapper.mask();
533 $.ajax({
534 url: $(this).data('href'),
535 dataType: 'json',
536 success: function(data) {
537 $terTableWrapper.html(data);
538 $terTableWrapper.unmask();
539 ExtensionManager.Update.transformPaginatorToAjax();
540 }
541 });
542 });
543 });
544 };
545
546 /**
547 * show the uploading form
548 */
549 ExtensionManager.UploadForm = {
550 expandedUploadFormClass: 'transformed'
551 };
552
553 ExtensionManager.UploadForm.initializeEvents = function() {
554 // Show upload form
555 $(document).on('click', '#upload-button-wrap > a', function(event) {
556 var $me = $(this),
557 $uploadForm = $('.uploadForm');
558
559 event.preventDefault();
560 if($me.hasClass(ExtensionManager.UploadForm.expandedUploadFormClass)) {
561 $uploadForm.stop().slideUp();
562 $me.removeClass(ExtensionManager.UploadForm.expandedUploadFormClass);
563 } else {
564 $me.addClass(ExtensionManager.UploadForm.expandedUploadFormClass);
565 $uploadForm.stop().slideDown();
566
567 $.ajax({
568 url: $me.attr('href'),
569 dataType: 'html',
570 success: function (data) {
571 $uploadForm.html(data);
572 }
573 });
574 }
575 });
576 };
577
578 return function() {
579 $(document).ready(function() {
580 var dataTable = ExtensionManager.manageExtensionListing();
581
582 $('#typo3-extension-configuration-forms .tabs').tabs();
583
584 $(document).on('click', '.onClickMaskExtensionManager', function() {
585 $(ExtensionManager.identifier.extensionManager).mask();
586 });
587
588 $(ExtensionManager.identifier.searchField).clearable({
589 onClear: function() {
590 dataTable.search('').draw();
591 }
592 });
593
594 $('.expandable').expander({
595 expandEffect: 'slideDown',
596 collapseEffect: 'slideUp',
597 beforeExpand: function() {
598 $(this).parent().css('z-index', 199);
599 },
600 afterCollapse: function() {
601 $(this).parent().css('z-index', 1);
602 }
603 });
604
605 $(document).on('click', '.t3-button-action-installdistribution', function() {
606 $(ExtensionManager.identifier.extensionManager).mask();
607 });
608
609 ExtensionManager.configurationFieldSupport();
610 var $validate = $('.validate');
611 $validate.validate();
612 $(document).on('click', '.t3-icon-document-save-close', function() {
613 $validate.append($('<input />', {type: 'hidden', name: 'tx_extensionmanager_tools_extensionmanagerextensionmanager[action]', value: 'saveAndClose'})).submit();
614 });
615
616 // initialize the repository
617 Repository.initDom();
618
619 ExtensionManager.Update.initializeEvents();
620 ExtensionManager.UploadForm.initializeEvents();
621 });
622
623 if (typeof TYPO3.ExtensionManager === 'undefined') {
624 TYPO3.ExtensionManager = ExtensionManager;
625 }
626 }();
627 });