Merge branch '2.0'
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / acp / js / WCF.ACP.js
CommitLineData
158bd3ca
TD
1/**
2 * Class and function collection for WCF ACP
3 *
13d8b49b 4 * @author Alexander Ebert, Matthias Schmidt
ca4ba303 5 * @copyright 2001-2014 WoltLab GmbH
158bd3ca
TD
6 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
7 */
8
9/**
10 * Initialize WCF.ACP namespace
11 */
cb6e5946 12WCF.ACP = { };
158bd3ca 13
1a8d655f
AE
14/**
15 * Namespace for ACP application management.
16 */
17WCF.ACP.Application = { };
18
19/**
81662a51 20 * Provides the ability to set an application as primary.
861edae8 21 *
81662a51 22 * @param integer packageID
1a8d655f 23 */
81662a51 24WCF.ACP.Application.SetAsPrimary = Class.extend({
861edae8 25 /**
81662a51
AE
26 * application package id
27 * @var integer
861edae8 28 */
81662a51 29 _packageID: 0,
861edae8 30
1a8d655f 31 /**
81662a51 32 * Initializes the WCF.ACP.Application.SetAsPrimary class.
861edae8 33 *
81662a51 34 * @param integer packageID
1a8d655f 35 */
81662a51
AE
36 init: function(packageID) {
37 this._packageID = packageID;
861edae8 38
81662a51 39 $('#setAsPrimary').click($.proxy(this._click, this));
1a8d655f
AE
40 },
41
42 /**
81662a51 43 * Shows a confirmation dialog to set current application as primary.
1a8d655f 44 */
81662a51
AE
45 _click: function() {
46 WCF.System.Confirmation.show(WCF.Language.get('wcf.acp.application.setAsPrimary.confirmMessage'), $.proxy(function(action) {
1a8d655f 47 if (action === 'confirm') {
81662a51 48 this._setAsPrimary();
1a8d655f
AE
49 }
50 }, this));
51 },
52
53 /**
81662a51 54 * Sets an application as primary.
1a8d655f 55 */
6974308c 56 _setAsPrimary: function() {
1a8d655f
AE
57 new WCF.Action.Proxy({
58 autoSend: true,
59 data: {
81662a51
AE
60 actionName: 'setAsPrimary',
61 className: 'wcf\\data\\application\\ApplicationAction',
62 objectIDs: [ this._packageID ]
1a8d655f
AE
63 },
64 success: $.proxy(function(data, textStatus, jqXHR) {
9b566f66 65 var $notification = new WCF.System.Notification(WCF.Language.get('wcf.global.success'));
81662a51
AE
66 $notification.show();
67
68 // remove button
69 $('#setAsPrimary').parent().remove();
70
71 // insert icon
70fec3c6
MS
72 $headline = $('.boxHeadline > h1');
73 $headline.html($headline.html() + ' ');
74 $('<span class="icon icon16 icon-ok-sign jsTooltip" title="' + WCF.Language.get('wcf.acp.application.primaryApplication') + '" />').appendTo($headline);
42d7d2cc
AE
75
76 WCF.DOMNodeInsertedHandler.execute();
1a8d655f
AE
77 }, this)
78 });
79 }
80});
81
29873759
MS
82/**
83 * Namespace for ACP cronjob management.
84 */
85WCF.ACP.Cronjob = { };
86
02b148ad
MS
87/**
88 * Handles the manual execution of cronjobs.
89 */
90WCF.ACP.Cronjob.ExecutionHandler = Class.extend({
91 /**
92 * notification object
93 * @var WCF.System.Notification
94 */
95 _notification: null,
96
97 /**
98 * action proxy
99 * @var WCF.Action.Proxy
100 */
101 _proxy: null,
102
103 /**
104 * Initializes WCF.ACP.Cronjob.ExecutionHandler object.
105 */
106 init: function() {
107 this._proxy = new WCF.Action.Proxy({
108 success: $.proxy(this._success, this)
109 });
110
111 $('.jsCronjobRow .jsExecuteButton').click($.proxy(this._click, this));
112
113 this._notification = new WCF.System.Notification(WCF.Language.get('wcf.global.success'), 'success');
114 },
115
116 /**
117 * Handles a click on an execute button.
118 *
119 * @param object event
120 */
121 _click: function(event) {
122 this._proxy.setOption('data', {
123 actionName: 'execute',
124 className: 'wcf\\data\\cronjob\\CronjobAction',
125 objectIDs: [ $(event.target).data('objectID') ]
126 });
127
128 this._proxy.sendRequest();
129 },
130
131 /**
132 * Handles successful cronjob execution.
133 *
134 * @param object data
135 * @param string textStatus
136 * @param jQuery jqXHR
137 */
138 _success: function(data, textStatus, jqXHR) {
139 $('.jsCronjobRow').each($.proxy(function(index, row) {
140 var $button = $(row).find('.jsExecuteButton');
141 var $objectID = ($button).data('objectID');
142
143 if (WCF.inArray($objectID, data.objectIDs)) {
144 if (data.returnValues[$objectID]) {
145 // insert feedback here
146 $(row).find('td.columnNextExec').html(data.returnValues[$objectID].formatted);
147 $(row).wcfHighlight();
148 }
149
150 this._notification.show();
151
152 return false;
153 }
154 }, this));
155 }
156});
157
29873759
MS
158/**
159 * Handles the cronjob log list.
160 */
161WCF.ACP.Cronjob.LogList = Class.extend({
162 /**
163 * error message dialog
164 * @var jQuery
165 */
166 _dialog: null,
167
168 /**
169 * Initializes WCF.ACP.Cronjob.LogList object.
170 */
171 init: function() {
172 // bind event listener to delete cronjob log button
173 $('.jsCronjobLogDelete').click(function() {
174 WCF.System.Confirmation.show(WCF.Language.get('wcf.acp.cronjob.log.clear.confirm'), function(action) {
175 if (action == 'confirm') {
176 new WCF.Action.Proxy({
177 autoSend: true,
178 data: {
179 actionName: 'clearAll',
180 className: 'wcf\\data\\cronjob\\log\\CronjobLogAction'
181 },
182 success: function() {
183 window.location.reload();
184 }
185 });
186 }
187 });
188 });
189
190 // bind event listeners to error badges
191 $('.jsCronjobError').click($.proxy(this._showError, this));
192 },
193
194 /**
195 * Shows certain error message
196 *
197 * @param object event
198 */
199 _showError: function(event) {
200 var $errorBadge = $(event.currentTarget);
201
202 if (this._dialog === null) {
cafc0b69 203 this._dialog = $('<div style="overflow: auto"><pre>' + $errorBadge.next().html() + '</pre></div>').hide().appendTo(document.body);
29873759
MS
204 this._dialog.wcfDialog({
205 title: WCF.Language.get('wcf.acp.cronjob.log.error.details')
206 });
207 }
208 else {
cafc0b69 209 this._dialog.html('<pre>' + $errorBadge.next().html() + '</pre>');
29873759
MS
210 this._dialog.wcfDialog('open');
211 }
212 }
213});
214
158bd3ca
TD
215/**
216 * Handles ACPMenu.
9f959ced 217 *
7ba38c25 218 * @param array<string> activeMenuItems
158bd3ca 219 */
7ba38c25 220WCF.ACP.Menu = Class.extend({
158bd3ca
TD
221 /**
222 * Initializes ACPMenu.
9f959ced 223 *
158bd3ca
TD
224 * @param array activeMenuItems
225 */
226 init: function(activeMenuItems) {
227 this._headerNavigation = $('nav#mainMenu');
72d68cbd 228 this._sidebarNavigation = $('aside.collapsibleMenu > div');
158bd3ca
TD
229
230 this._prepareElements(activeMenuItems);
231 },
232
233 /**
234 * Resets all elements and binds event listeners.
235 */
236 _prepareElements: function(activeMenuItems) {
184a8d6d 237 this._headerNavigation.find('li').removeClass('active');
158bd3ca 238
ecbd8a0b 239 this._sidebarNavigation.find('legend').each($.proxy(function(index, menuHeader) {
158bd3ca
TD
240 $(menuHeader).click($.proxy(this._toggleItem, this));
241 }, this));
242
243 // close all navigation groups
ecbd8a0b 244 this._sidebarNavigation.find('nav ul').each(function() {
158bd3ca
TD
245 $(this).hide();
246 });
247
248 this._headerNavigation.find('li').click($.proxy(this._toggleSidebar, this));
249
250 if (activeMenuItems.length === 0) {
251 this._renderSidebar(this._headerNavigation.find('li:first').data('menuItem'), []);
252 }
253 else {
254 this._renderSidebar('', activeMenuItems);
255 }
256 },
257
258 /**
259 * Toggles a navigation group entry.
260 */
261 _toggleItem: function(event) {
7ba38c25 262 var $menuItem = $(event.currentTarget);
158bd3ca 263
ecbd8a0b
MW
264 $menuItem.parent().find('nav ul').stop(true, true).toggle('blind', { }, 200).end();
265 $menuItem.toggleClass('active');
158bd3ca
TD
266 },
267
268 /**
269 * Handles clicks on main menu.
9f959ced 270 *
158bd3ca
TD
271 * @param object event
272 */
273 _toggleSidebar: function(event) {
7ba38c25 274 var $target = $(event.currentTarget);
6b2eb388 275
184a8d6d 276 if ($target.hasClass('active')) {
158bd3ca
TD
277 return;
278 }
28bb0b82 279
6b2eb388 280 this._renderSidebar($target.data('menuItem'), []);
158bd3ca
TD
281 },
282
283 /**
284 * Renders sidebar including highlighting of currently active menu items.
9f959ced 285 *
158bd3ca
TD
286 * @param string menuItem
287 * @param array activeMenuItems
288 */
289 _renderSidebar: function(menuItem, activeMenuItems) {
290 // reset visible and active items
184a8d6d 291 this._headerNavigation.find('li').removeClass('active');
ecbd8a0b 292 this._sidebarNavigation.find('> div').hide();
158bd3ca
TD
293
294 if (activeMenuItems.length === 0) {
295 // show active menu
184a8d6d 296 this._headerNavigation.find('li[data-menu-item="' + menuItem + '"]').addClass('active');
975cd22f 297 this._sidebarNavigation.find('div[data-parent-menu-item="' + menuItem + '"]').show();
158bd3ca
TD
298 }
299 else {
300 // open menu by active menu items, first element is always a head navigation item
301 menuItem = activeMenuItems.shift();
302
184a8d6d 303 this._headerNavigation.find('li[data-menu-item="' + menuItem + '"]').addClass('active');
975cd22f 304 this._sidebarNavigation.find('div[data-parent-menu-item="' + menuItem + '"]').show();
158bd3ca
TD
305
306 for (var $i = 0, $size = activeMenuItems.length; $i < $size; $i++) {
307 var $item = activeMenuItems[$i];
308
309 if ($.wcfIsset($item)) {
310 var $menuItem = $('#' + $.wcfEscapeID($item));
311
312 if ($menuItem.getTagName() === 'ul') {
ecbd8a0b 313 $menuItem.show().parents('fieldset').children('legend').addClass('active');
158bd3ca
TD
314 }
315 else {
184a8d6d 316 $menuItem.addClass('active');
158bd3ca
TD
317 }
318 }
319 }
320 }
321 }
7ba38c25 322});
158bd3ca 323
e5b88a7f
AE
324/**
325 * Namespace for ACP package management.
326 */
3536d2fe 327WCF.ACP.Package = { };
d71e5a29 328
158bd3ca 329/**
cb9e9c12 330 * Provides the package installation.
158bd3ca 331 *
158bd3ca 332 * @param integer queueID
2ef79140 333 * @param string actionName
158bd3ca 334 */
cb9e9c12 335WCF.ACP.Package.Installation = Class.extend({
158bd3ca
TD
336 /**
337 * package installation type
158bd3ca
TD
338 * @var string
339 */
cb9e9c12 340 _actionName: 'InstallPackage',
9f959ced 341
0acbb6be
AE
342 /**
343 * true, if rollbacks are supported
344 * @var boolean
345 */
346 _allowRollback: false,
347
e5b88a7f 348 /**
cb9e9c12
AE
349 * dialog object
350 * @var jQuery
158bd3ca
TD
351 */
352 _dialog: null,
353
6207cc21
MS
354 /**
355 * name of the language item with the title of the dialog
356 * @var string
357 */
358 _dialogTitle: '',
359
e5b88a7f
AE
360 /**
361 * action proxy
362 * @var WCF.Action.Proxy
363 */
364 _proxy: null,
9f959ced 365
158bd3ca 366 /**
cb9e9c12 367 * package installation queue id
158bd3ca
TD
368 * @var integer
369 */
370 _queueID: 0,
9f959ced 371
ba133309
AE
372 /**
373 * true, if dialog should be rendered again
374 * @var boolean
375 */
376 _shouldRender: false,
377
0a3949d4 378 /**
cb9e9c12 379 * Initializes the WCF.ACP.Package.Installation class.
158bd3ca 380 *
158bd3ca 381 * @param integer queueID
2ef79140 382 * @param string actionName
0acbb6be 383 * @param boolean allowRollback
adf9791d 384 * @param boolean isUpdate
158bd3ca 385 */
adf9791d 386 init: function(queueID, actionName, allowRollback, isUpdate) {
2ef79140 387 this._actionName = (actionName) ? actionName : 'InstallPackage';
0acbb6be 388 this._allowRollback = (allowRollback === true) ? true : false;
158bd3ca 389 this._queueID = queueID;
cb9e9c12 390
3484b0ce 391 switch (this._actionName) {
824d9e90 392 case 'InstallPackage':
328111b8 393 this._dialogTitle = 'wcf.acp.package.' + (isUpdate ? 'update' : 'install') + '.title';
824d9e90
AE
394 break;
395
396 case 'UninstallPackage':
397 this._dialogTitle = 'wcf.acp.package.uninstallation.title';
398 break;
6207cc21
MS
399 }
400
0acbb6be
AE
401 this._initProxy();
402 this._init();
403 },
404
405 /**
406 * Initializes the WCF.Action.Proxy object.
407 */
408 _initProxy: function() {
e5b88a7f 409 this._proxy = new WCF.Action.Proxy({
cb9e9c12 410 failure: $.proxy(this._failure, this),
324e8301 411 showLoadingOverlay: false,
cb9e9c12 412 success: $.proxy(this._success, this),
e5b88a7f
AE
413 url: 'index.php/' + this._actionName + '/?t=' + SECURITY_TOKEN + SID_ARG_2ND
414 });
cb9e9c12
AE
415 },
416
417 /**
418 * Initializes the package installation.
419 */
420 _init: function() {
b32a906e 421 $('#submitButton').click($.proxy(this.prepareInstallation, this));
cb9e9c12
AE
422 },
423
424 /**
425 * Handles erroneous AJAX requests.
cb9e9c12 426 */
014819c4 427 _failure: function() {
e0a0fe54 428 if (this._dialog !== null) {
5df9b2ef 429 $('#packageInstallationProgress').removeAttr('value');
e0a0fe54
MS
430 this._setIcon('remove');
431 }
e098d212 432
0acbb6be
AE
433 if (!this._allowRollback) {
434 return;
435 }
436
7289c8d3
MS
437 if (this._dialog !== null) {
438 this._purgeTemplateContent($.proxy(function() {
439 var $form = $('<div class="formSubmit" />').appendTo($('#packageInstallationInnerContent'));
440 $('<button class="buttonPrimary">' + WCF.Language.get('wcf.acp.package.installation.rollback') + '</button>').appendTo($form).click($.proxy(this._rollback, this));
441
442 $('#packageInstallationInnerContentContainer').show();
443
444 this._dialog.wcfDialog('render');
445 }, this));
446 }
0acbb6be
AE
447 },
448
449 /**
450 * Performs a rollback.
7fcd3ad5
AE
451 *
452 * @param object event
0acbb6be 453 */
7fcd3ad5 454 _rollback: function(event) {
e098d212
AE
455 this._setIcon('spinner');
456
7fcd3ad5
AE
457 if (event) {
458 $(event.currentTarget).disable();
459 }
460
0acbb6be 461 this._executeStep('rollback');
158bd3ca
TD
462 },
463
464 /**
465 * Prepares installation dialog.
466 */
b32a906e 467 prepareInstallation: function() {
4da866ad
AE
468 this._proxy.setOption('data', this._getParameters());
469 this._proxy.sendRequest();
158bd3ca
TD
470 },
471
472 /**
cb9e9c12
AE
473 * Returns parameters to prepare installation.
474 *
475 * @return object
476 */
477 _getParameters: function() {
478 return {
479 queueID: this._queueID,
480 step: 'prepare'
481 };
482 },
483
484 /**
485 * Handles successful AJAX requests.
e5b88a7f
AE
486 *
487 * @param object data
488 * @param string textStatus
489 * @param jQuery jqXHR
158bd3ca 490 */
cb9e9c12 491 _success: function(data, textStatus, jqXHR) {
ba133309 492 this._shouldRender = false;
cb9e9c12 493
4da866ad
AE
494 if (this._dialog === null) {
495 this._dialog = $('<div id="packageInstallationDialog" />').hide().appendTo(document.body);
496 this._dialog.wcfDialog({
497 closable: false,
6207cc21 498 title: WCF.Language.get(this._dialogTitle)
4da866ad
AE
499 });
500 }
501
e098d212
AE
502 this._setIcon('spinner');
503
0acbb6be
AE
504 if (data.step == 'rollback') {
505 this._dialog.wcfDialog('close');
e9add3b2 506 this._dialog.remove();
0acbb6be
AE
507
508 new WCF.PeriodicalExecuter(function(pe) {
509 pe.stop();
510
511 var $uninstallation = new WCF.ACP.Package.Uninstallation();
512 $uninstallation.start(data.packageID);
513 }, 200);
514
515 return;
516 }
517
158bd3ca 518 // receive new queue id
e5b88a7f
AE
519 if (data.queueID) {
520 this._queueID = data.queueID;
158bd3ca
TD
521 }
522
47d85da7
AE
523 // update template
524 if (data.template && !data.ignoreTemplate) {
525 this._dialog.html(data.template);
526 this._shouldRender = true;
527 }
528
158bd3ca 529 // update progress
e5b88a7f
AE
530 if (data.progress) {
531 $('#packageInstallationProgress').attr('value', data.progress).text(data.progress + '%');
532 $('#packageInstallationProgressLabel').text(data.progress + '%');
e70175d6 533 }
9f959ced 534
e70175d6 535 // update action
e5b88a7f
AE
536 if (data.currentAction) {
537 $('#packageInstallationAction').html(data.currentAction);
158bd3ca
TD
538 }
539
540 // handle success
cb9e9c12 541 if (data.step === 'success') {
e098d212
AE
542 this._setIcon('ok');
543
0a3949d4 544 this._purgeTemplateContent($.proxy(function() {
cb9e9c12 545 var $form = $('<div class="formSubmit" />').appendTo($('#packageInstallationInnerContent'));
45f1e6f9 546 var $button = $('<button class="buttonPrimary">' + WCF.Language.get('wcf.global.button.next') + '</button>').appendTo($form).click(function() {
7fcd3ad5
AE
547 $(this).disable();
548 window.location = data.redirectLocation;
549 });
3c87a1b1 550
0a3949d4 551 $('#packageInstallationInnerContentContainer').show();
cb9e9c12 552
45f1e6f9
AE
553 $(document).keydown(function(event) {
554 if (event.which === $.ui.keyCode.ENTER) {
555 $button.trigger('click');
556 }
557 });
558
cb9e9c12 559 this._dialog.wcfDialog('render');
0a3949d4 560 }, this));
158bd3ca
TD
561
562 return;
563 }
564
158bd3ca 565 // handle inner template
e5b88a7f 566 if (data.innerTemplate) {
599a39ba 567 var self = this;
599a39ba 568 $('#packageInstallationInnerContent').html(data.innerTemplate).find('input').keyup(function(event) {
45f1e6f9 569 if (event.keyCode === $.ui.keyCode.ENTER) {
b32a906e 570 self._submit(data);
599a39ba
AE
571 }
572 });
158bd3ca
TD
573
574 // create button to handle next step
e5b88a7f 575 if (data.step && data.node) {
5df9b2ef 576 $('#packageInstallationProgress').removeAttr('value');
e098d212
AE
577 this._setIcon('question');
578
cb9e9c12 579 var $form = $('<div class="formSubmit" />').appendTo($('#packageInstallationInnerContent'));
45f1e6f9 580 $('<button class="buttonPrimary">' + WCF.Language.get('wcf.global.button.next') + '</button>').appendTo($form).click($.proxy(function(event) {
7fcd3ad5
AE
581 $(event.currentTarget).disable();
582
583 this._submit(data);
9db0c330 584 }, this));
158bd3ca
TD
585 }
586
0a3949d4 587 $('#packageInstallationInnerContentContainer').show();
158bd3ca 588
cb9e9c12 589 this._dialog.wcfDialog('render');
158bd3ca
TD
590 return;
591 }
592
593 // purge content
3c87a1b1 594 this._purgeTemplateContent($.proxy(function() {
e5b88a7f 595 // render container
ba133309 596 if (this._shouldRender) {
cb9e9c12 597 this._dialog.wcfDialog('render');
0a3949d4 598 }
5e6179f1 599
3c87a1b1 600 // execute next step
e5b88a7f
AE
601 if (data.step && data.node) {
602 this._executeStep(data.step, data.node);
3c87a1b1
AE
603 }
604 }, this));
605 },
606
599a39ba
AE
607 /**
608 * Submits the dialog content.
609 *
610 * @param object data
611 */
b32a906e 612 _submit: function(data) {
e098d212
AE
613 this._setIcon('spinner');
614
599a39ba 615 // collect form values
cb6e5946 616 var $additionalData = { };
599a39ba
AE
617 $('#packageInstallationInnerContent input').each(function(index, inputElement) {
618 var $inputElement = $(inputElement);
619 var $type = $inputElement.attr('type');
620
3bbe83f0
MS
621 if (($type != 'checkbox' && $type != 'radio') || $inputElement.prop('checked')) {
622 var $name = $inputElement.attr('name');
623 if ($name.match(/(.*)\[([^[]*)\]$/)) {
624 $name = RegExp.$1;
625 $key = RegExp.$2;
626
627 if ($additionalData[$name] === undefined) {
628 if ($key) {
629 $additionalData[$name] = { };
630 }
631 else {
632 $additionalData[$name] = [ ];
633 }
634 }
635
c001b457 636 if ($key) {
3bbe83f0 637 $additionalData[$name][$key] = $inputElement.val();
c001b457
AE
638 }
639 else {
3bbe83f0 640 $additionalData[$name].push($inputElement.val());
c001b457
AE
641 }
642 }
c001b457 643 else {
3bbe83f0 644 $additionalData[$name] = $inputElement.val();
c001b457
AE
645 }
646 }
599a39ba
AE
647 });
648
649 this._executeStep(data.step, data.node, $additionalData);
650 },
651
3c87a1b1
AE
652 /**
653 * Purges template content.
654 *
655 * @param function callback
656 */
657 _purgeTemplateContent: function(callback) {
158bd3ca 658 if ($('#packageInstallationInnerContent').children().length > 1) {
5e6179f1 659 $('#packageInstallationInnerContentContainer').wcfBlindOut('vertical', $.proxy(function() {
158bd3ca 660 $('#packageInstallationInnerContent').empty();
0a3949d4 661 this._shouldRender = true;
158bd3ca 662
3c87a1b1
AE
663 // execute callback
664 callback();
158bd3ca
TD
665 }, this));
666 }
667 else {
3c87a1b1 668 callback();
158bd3ca
TD
669 }
670 },
671
672 /**
673 * Executes the next installation step.
674 *
675 * @param string step
676 * @param string node
677 * @param object additionalData
678 */
679 _executeStep: function(step, node, additionalData) {
cb6e5946 680 if (!additionalData) additionalData = { };
158bd3ca
TD
681
682 var $data = $.extend({
a698c064
AE
683 node: node,
684 queueID: this._queueID,
685 step: step
158bd3ca 686 }, additionalData);
9f959ced 687
e5b88a7f
AE
688 this._proxy.setOption('data', $data);
689 this._proxy.sendRequest();
e098d212
AE
690 },
691
e0a0fe54
MS
692 /**
693 * Sets the icon with the given name as the current installation status icon.
694 *
695 * @param string iconName
696 */
e098d212
AE
697 _setIcon: function(iconName) {
698 this._dialog.find('.jsPackageInstallationStatus').removeClass('icon-ok icon-question icon-remove icon-spinner').addClass('icon-' + iconName);
158bd3ca 699 }
cb9e9c12 700});
158bd3ca 701
a45fa84d
MS
702/**
703 * Handles canceling the package installation at the package installation
704 * confirm page.
705 */
706WCF.ACP.Package.Installation.Cancel = Class.extend({
707 /**
708 * Creates a new instance of WCF.ACP.Package.Installation.Cancel.
709 *
710 * @param integer queueID
711 */
712 init: function(queueID) {
713 $('#backButton').click(function() {
714 new WCF.Action.Proxy({
715 autoSend: true,
716 data: {
717 actionName: 'cancelInstallation',
718 className: 'wcf\\data\\package\\installation\\queue\\PackageInstallationQueueAction',
719 objectIDs: [ queueID ]
720 },
721 success: function(data) {
722 window.location = data.returnValues.url;
723 }
724 });
725 });
726 }
727});
728
158bd3ca 729/**
cb9e9c12 730 * Provides the package uninstallation.
158bd3ca
TD
731 *
732 * @param jQuery elements
74622428 733 * @param string wcfPackageListURL
158bd3ca 734 */
cb9e9c12 735WCF.ACP.Package.Uninstallation = WCF.ACP.Package.Installation.extend({
158bd3ca 736 /**
cb9e9c12
AE
737 * list of uninstallation buttons
738 * @var jQuery
158bd3ca 739 */
cb9e9c12 740 _elements: null,
158bd3ca
TD
741
742 /**
cb9e9c12
AE
743 * current package id
744 * @var integer
745 */
746 _packageID: 0,
747
74622428
AE
748 /**
749 * URL of WCF package list
750 * @var string
751 */
752 _wcfPackageListURL: '',
753
cb9e9c12
AE
754 /**
755 * Initializes the WCF.ACP.Package.Uninstallation class.
158bd3ca
TD
756 *
757 * @param jQuery elements
74622428 758 * @param string wcfPackageListURL
158bd3ca 759 */
74622428 760 init: function(elements, wcfPackageListURL) {
cb9e9c12
AE
761 this._elements = elements;
762 this._packageID = 0;
74622428 763 this._wcfPackageListURL = wcfPackageListURL;
158bd3ca 764
0acbb6be 765 if (this._elements !== undefined && this._elements.length) {
2ef79140 766 this._super(0, 'UninstallPackage');
cb9e9c12 767 }
158bd3ca
TD
768 },
769
0acbb6be
AE
770 /**
771 * Begins a package uninstallation without user action.
772 *
773 * @param integer packageID
774 */
775 start: function(packageID) {
776 this._actionName = 'UninstallPackage';
777 this._packageID = packageID;
778 this._queueID = 0;
6207cc21 779 this._dialogTitle = 'wcf.acp.package.uninstallation.title';
0acbb6be
AE
780
781 this._initProxy();
782 this.prepareInstallation();
783 },
784
158bd3ca 785 /**
cb9e9c12
AE
786 * @see WCF.ACP.Package.Installation.init()
787 */
788 _init: function() {
e3be0700 789 this._elements.click($.proxy(this._showConfirmationDialog, this));
e852aa82
AE
790 },
791
792 /**
793 * Displays a confirmation dialog prior to package uninstallation.
794 *
cec91f01 795 * @param object event
e852aa82 796 */
cec91f01 797 _showConfirmationDialog: function(event) {
e3be0700
AE
798 var $element = $(event.currentTarget);
799
800 if ($element.data('isApplication') && this._wcfPackageListURL) {
801 window.location = WCF.String.unescapeHTML(this._wcfPackageListURL.replace(/{packageID}/, $element.data('objectID')));
74622428
AE
802 return;
803 }
804
cb9e9c12 805 var self = this;
e3be0700 806 WCF.System.Confirmation.show($element.data('confirmMessage'), function(action) {
cb9e9c12 807 if (action === 'confirm') {
e3be0700 808 self._packageID = $element.data('objectID');
b32a906e 809 self.prepareInstallation();
cb9e9c12
AE
810 }
811 });
29b533c8
AE
812 },
813
814 /**
cb9e9c12 815 * @see WCF.ACP.Package.Installation._getParameters()
29b533c8 816 */
cb9e9c12
AE
817 _getParameters: function() {
818 return {
819 packageID: this._packageID,
820 step: 'prepare'
821 };
158bd3ca 822 }
cb9e9c12 823});
158bd3ca 824
25c2c382
AE
825/**
826 * Manages package search.
827 */
828WCF.ACP.Package.Search = Class.extend({
829 /**
830 * search button
831 * @var jQuery
832 */
833 _button: null,
834
835 /**
836 * list of cached pages
837 * @var object
838 */
839 _cache: { },
840
841 /**
842 * search container
843 * @var jQuery
844 */
845 _container: null,
846
996eb650
AE
847 /**
848 * dialog overlay
849 * @var jQuery
850 */
851 _dialog: null,
852
25c2c382
AE
853 /**
854 * package input field
855 * @var jQuery
856 */
857 _package: null,
858
859 /**
860 * package description input field
861 * @var jQuery
862 */
863 _packageDescription: null,
864
865 /**
866 * package name input field
867 * @var jQuery
868 */
869 _packageName: null,
870
871 /**
872 * package search result container
873 * @var jQuery
874 */
875 _packageSearchResultContainer: null,
876
877 /**
878 * package search result list
879 * @var jQuery
880 */
881 _packageSearchResultList: null,
882
883 /**
884 * number of pages
885 * @var integer
886 */
887 _pageCount: 0,
888
889 /**
890 * current page
891 * @var integer
892 */
893 _pageNo: 1,
894
895 /**
896 * action proxy
897 * @var WCF.Action:proxy
898 */
899 _proxy: null,
900
901 /**
902 * search id
903 * @var integer
904 */
905 _searchID: 0,
906
996eb650
AE
907 /**
908 * currently selected package
909 * @var string
910 */
911 _selectedPackage: '',
912
913 /**
914 * currently selected package's version
915 */
916 _selectedPackageVersion: '',
917
25c2c382
AE
918 /**
919 * Initializes the WCF.ACP.Package.Seach class.
920 */
921 init: function() {
922 this._button = null;
923 this._cache = { };
924 this._container = $('#packageSearch');
996eb650 925 this._dialog = null;
25c2c382
AE
926 this._package = null;
927 this._packageName = null;
928 this._packageSearchResultContainer = $('#packageSearchResultContainer');
929 this._packageSearchResultList = $('#packageSearchResultList');
930 this._pageCount = 0;
931 this._pageNo = 1;
932 this._searchDescription = null;
933 this._searchID = 0;
996eb650
AE
934 this._selectedPackage = '';
935 this._selectedPackageVersion = '';
25c2c382
AE
936
937 this._proxy = new WCF.Action.Proxy({
938 success: $.proxy(this._success, this)
939 });
940
941 this._initElements();
942 },
943
944 /**
945 * Initializes search elements.
946 */
947 _initElements: function() {
948 this._button = this._container.find('.formSubmit > button.jsButtonPackageSearch').disable().click($.proxy(this._search, this));
949
950 this._package = $('#package').keyup($.proxy(this._keyUp, this));
951 this._packageDescription = $('#packageDescription').keyup($.proxy(this._keyUp, this));
952 this._packageName = $('#packageName').keyup($.proxy(this._keyUp, this));
953 },
954
955 /**
956 * Handles the 'keyup' event.
957 */
958 _keyUp: function(event) {
959 if (this._package.val() === '' && this._packageDescription.val() === '' && this._packageName.val() === '') {
960 this._button.disable();
961 }
962 else {
963 this._button.enable();
964
965 // submit on [Enter]
966 if (event.which === 13) {
967 this._button.trigger('click');
968 }
969 }
970 },
971
972 /**
973 * Performs a new search.
974 */
975 _search: function() {
976 var $values = this._getSearchValues();
977 if (!this._validate($values)) {
978 return false;
979 }
980
981 $values.pageNo = this._pageNo;
982 this._proxy.setOption('data', {
983 actionName: 'search',
984 className: 'wcf\\data\\package\\update\\PackageUpdateAction',
985 parameters: $values
986 });
987 this._proxy.sendRequest();
988 },
989
990 /**
991 * Returns search values.
992 *
993 * @return object
994 */
995 _getSearchValues: function() {
996 return {
997 'package': $.trim(this._package.val()),
998 packageDescription: $.trim(this._packageDescription.val()),
999 packageName: $.trim(this._packageName.val())
1000 };
1001 },
1002
1003 /**
1004 * Validates search values.
1005 *
1006 * @param object values
1007 * @return boolean
1008 */
1009 _validate: function(values) {
1010 if (values['package'] === '' && values['packageDescription'] === '' && values['packageName'] === '') {
1011 return false;
1012 }
1013
1014 return true;
1015 },
1016
1017 /**
1018 * Handles successful AJAX requests.
1019 *
1020 * @param object data
1021 * @param string textStatus
1022 * @param jQuery jqXHR
1023 */
1024 _success: function(data, textStatus, jqXHR) {
1025 switch (data.actionName) {
1026 case 'getResultList':
1027 this._insertTemplate(data.returnValues.template);
1028 break;
1029
996eb650
AE
1030 case 'prepareInstallation':
1031 if (data.returnValues.queueID) {
1032 if (this._dialog !== null) {
1033 this._dialog.wcfDialog('close');
1034 }
1035
1036 var $installation = new WCF.ACP.Package.Installation(data.returnValues.queueID, undefined, false);
1037 $installation.prepareInstallation();
1038 }
1039 else if (data.returnValues.template) {
1040 if (this._dialog === null) {
1041 this._dialog = $('<div>' + data.returnValues.template + '</div>').hide().appendTo(document.body);
1042 this._dialog.wcfDialog({
1043 title: WCF.Language.get('wcf.acp.package.update.unauthorized')
1044 });
1045 }
1046 else {
1047 this._dialog.html(data.returnValues.template).wcfDialog('open');
1048 }
1049
1050 this._dialog.find('.formSubmit > button').click($.proxy(this._submitAuthentication, this));
1051 }
1052 break;
1053
25c2c382
AE
1054 case 'search':
1055 this._pageCount = data.returnValues.pageCount;
1056 this._searchID = data.returnValues.searchID;
1057
1058 this._insertTemplate(data.returnValues.template, (data.returnValues.count === undefined ? undefined : data.returnValues.count));
1059 this._setupPagination();
1060 break;
1061 }
1062 },
1063
996eb650
AE
1064 /**
1065 * Submits authentication data for current update server.
1066 *
1067 * @param object event
1068 */
1069 _submitAuthentication: function(event) {
1070 var $usernameField = $('#packageUpdateServerUsername');
1071 var $passwordField = $('#packageUpdateServerPassword');
1072
1073 // remove error messages if any
1074 $usernameField.next('small.innerError').remove();
1075 $passwordField.next('small.innerError').remove();
1076
1077 var $continue = true;
1078 if ($.trim($usernameField.val()) === '') {
1079 $('<small class="innerError">' + WCF.Language.get('wcf.global.form.error.empty') + '</small>').insertAfter($usernameField);
1080 $continue = false;
1081 }
1082
1083 if ($.trim($passwordField.val()) === '') {
1084 $('<small class="innerError">' + WCF.Language.get('wcf.global.form.error.empty') + '</small>').insertAfter($passwordField);
1085 $continue = false;
1086 }
1087
1088 if ($continue) {
1089 this._prepareInstallation($(event.currentTarget).data('packageUpdateServerID'));
1090 }
1091 },
1092
25c2c382
AE
1093 /**
1094 * Inserts search result list template.
1095 *
1096 * @param string template
1097 * @param integer count
1098 */
1099 _insertTemplate: function(template, count) {
1100 this._packageSearchResultContainer.show();
1101
1102 this._packageSearchResultList.html(template);
1103 if (count === undefined) {
1104 this._content[this._pageNo] = template;
1105 }
1106
1107 // update badge count
1108 if (count !== undefined) {
1109 this._content = { 1: template };
996eb650
AE
1110 this._packageSearchResultContainer.find('> header > h2 > .badge').html(count);
1111 }
1112
1113 // bind listener
1114 this._packageSearchResultList.find('.jsInstallPackage').click($.proxy(this._click, this));
1115 },
1116
1117 /**
1118 * Prepares a package installation.
1119 *
1120 * @param object event
1121 */
1122 _click: function(event) {
1123 var $button = $(event.currentTarget);
1124 WCF.System.Confirmation.show($button.data('confirmMessage'), $.proxy(function(action) {
1125 if (action === 'confirm') {
1126 this._selectedPackage = $button.data('package');
1127 this._selectedPackageVersion = $button.data('packageVersion');
1128 this._prepareInstallation();
1129 }
1130 }, this));
1131 },
1132
1133 /**
1134 * Prepares package installation.
1135 *
1136 * @param integer packageUpdateServerID
1137 */
1138 _prepareInstallation: function(packageUpdateServerID) {
1139 var $parameters = {
02662193 1140 'packages': { }
996eb650 1141 };
02662193 1142 $parameters['packages'][this._selectedPackage] = this._selectedPackageVersion;
996eb650
AE
1143
1144 if (packageUpdateServerID) {
1145 $parameters.authData = {
1146 packageUpdateServerID: packageUpdateServerID,
1147 password: $.trim($('#packageUpdateServerPassword').val()),
1148 saveCredentials: ($('#packageUpdateServerSaveCredentials:checked').length ? true : false),
1149 username: $.trim($('#packageUpdateServerUsername').val())
1150 };
25c2c382 1151 }
996eb650
AE
1152
1153 this._proxy.setOption('data', {
1154 actionName: 'prepareInstallation',
1155 className: 'wcf\\data\\package\\update\\PackageUpdateAction',
1156 parameters: $parameters
1157 });
1158 this._proxy.sendRequest();
25c2c382
AE
1159 },
1160
1161 /**
1162 * Setups pagination for current search.
1163 */
1164 _setupPagination: function() {
1165 // remove previous instances
1166 this._content = { 1: this._packageSearchResultList.html() };
1167 this._packageSearchResultContainer.find('.pageNavigation').wcfPages('destroy').remove();
1168
1169 if (this._pageCount > 1) {
d62d0d94
AE
1170 // TODO: Fix ui.wcfPages to properly synchronize multiple instances without triggering events
1171 /*$('<div class="contentNavigation" />').insertBefore(this._packageSearchResultList).wcfPages({
25c2c382
AE
1172 activePage: this._pageNo,
1173 maxPage: this._pageCount
d62d0d94 1174 }).on('wcfpagesswitched', $.proxy(this._showPage, this));*/
25c2c382 1175
996eb650 1176 $('<div class="contentNavigation" />').insertAfter(this._packageSearchResultList).wcfPages({
25c2c382
AE
1177 activePage: this._pageNo,
1178 maxPage: this._pageCount
d62d0d94 1179 }).on('wcfpagesswitched', $.proxy(this._showPage, this));
25c2c382
AE
1180 }
1181 },
1182
1183 /**
1184 * Displays requested pages or loads it.
1185 *
1186 * @param object event
1187 * @param object data
1188 */
1189 _showPage: function(event, data) {
1190 if (data && data.activePage) {
1191 this._pageNo = data.activePage;
1192 }
1193
1194 // validate page no
1195 if (this._pageNo < 1 || this._pageNo > this._pageCount) {
1196 console.debug("[WCF.ACP.Package.Search] Cannot access page " + this._pageNo + " of " + this._pageCount);
1197 return;
1198 }
1199
1200 // load content
1201 if (this._content[this._pageNo] === undefined) {
1202 this._proxy.setOption('data', {
1203 actionName: 'getResultList',
1204 className: 'wcf\\data\\package\\update\\PackageUpdateAction',
1205 parameters: {
1206 pageNo: this._pageNo,
1207 searchID: this._searchID
1208 }
1209 });
1210 this._proxy.sendRequest();
1211 }
1212 else {
25c2c382
AE
1213 // show cached content
1214 this._packageSearchResultList.html(this._content[this._pageNo]);
1215
42d7d2cc 1216 WCF.DOMNodeInsertedHandler.execute();
25c2c382
AE
1217 }
1218 }
1219});
1220
66ed9925 1221WCF.ACP.Package.Server = { };
298a7cd8
AE
1222
1223WCF.ACP.Package.Server.Installation = Class.extend({
1224 _proxy: null,
1225 _selectedPackage: '',
1226
1227 init: function() {
1228 this._dialog = null;
1229 this._selectedPackage = null;
1230
1231 this._proxy = new WCF.Action.Proxy({
1232 success: $.proxy(this._success, this)
1233 });
1234 },
1235
1236 bind: function() {
1237 $('.jsButtonPackageInstall').removeClass('jsButtonPackageInstall').click($.proxy(this._click, this));
1238 },
1239
1240 /**
1241 * Prepares a package installation.
1242 *
1243 * @param object event
1244 */
1245 _click: function(event) {
1246 var $button = $(event.currentTarget);
1247 WCF.System.Confirmation.show($button.data('confirmMessage'), $.proxy(function(action) {
1248 if (action === 'confirm') {
1249 this._selectedPackage = $button.data('package');
1250 this._selectedPackageVersion = $button.data('packageVersion');
1251 this._prepareInstallation();
1252 }
1253 }, this));
1254 },
1255
1256 /**
1257 * Handles successful AJAX requests.
1258 *
1259 * @param object data
1260 */
1261 _success: function(data) {
1262 if (data.returnValues.queueID) {
1263 if (this._dialog !== null) {
1264 this._dialog.wcfDialog('close');
1265 }
1266
1267 var $installation = new WCF.ACP.Package.Installation(data.returnValues.queueID, undefined, false);
1268 $installation.prepareInstallation();
1269 }
1270 else if (data.returnValues.template) {
1271 if (this._dialog === null) {
1272 this._dialog = $('<div>' + data.returnValues.template + '</div>').hide().appendTo(document.body);
1273 this._dialog.wcfDialog({
1274 title: WCF.Language.get('wcf.acp.package.update.unauthorized')
1275 });
1276 }
1277 else {
1278 this._dialog.html(data.returnValues.template).wcfDialog('open');
1279 }
1280
1281 this._dialog.find('.formSubmit > button').click($.proxy(this._submitAuthentication, this));
1282 }
1283 },
1284
1285 /**
1286 * Submits authentication data for current update server.
1287 *
1288 * @param object event
1289 */
1290 _submitAuthentication: function(event) {
1291 var $usernameField = $('#packageUpdateServerUsername');
1292 var $passwordField = $('#packageUpdateServerPassword');
1293
1294 // remove error messages if any
1295 $usernameField.next('small.innerError').remove();
1296 $passwordField.next('small.innerError').remove();
1297
1298 var $continue = true;
1299 if ($.trim($usernameField.val()) === '') {
1300 $('<small class="innerError">' + WCF.Language.get('wcf.global.form.error.empty') + '</small>').insertAfter($usernameField);
1301 $continue = false;
1302 }
1303
1304 if ($.trim($passwordField.val()) === '') {
1305 $('<small class="innerError">' + WCF.Language.get('wcf.global.form.error.empty') + '</small>').insertAfter($passwordField);
1306 $continue = false;
1307 }
1308
1309 if ($continue) {
1310 this._prepareInstallation($(event.currentTarget).data('packageUpdateServerID'));
1311 }
1312 },
1313
1314 /**
1315 * Prepares package installation.
1316 *
1317 * @param integer packageUpdateServerID
1318 */
1319 _prepareInstallation: function(packageUpdateServerID) {
1320 var $parameters = {
1321 'packages': { }
1322 };
1323 $parameters['packages'][this._selectedPackage] = this._selectedPackageVersion;
1324
1325 if (packageUpdateServerID) {
1326 $parameters.authData = {
1327 packageUpdateServerID: packageUpdateServerID,
1328 password: $.trim($('#packageUpdateServerPassword').val()),
1329 saveCredentials: ($('#packageUpdateServerSaveCredentials:checked').length ? true : false),
1330 username: $.trim($('#packageUpdateServerUsername').val())
1331 };
1332 }
1333
1334 this._proxy.setOption('data', {
1335 actionName: 'prepareInstallation',
1336 className: 'wcf\\data\\package\\update\\PackageUpdateAction',
1337 parameters: $parameters
1338 });
1339 this._proxy.sendRequest();
1340 },
1341})
1342
3536d2fe
AE
1343/**
1344 * Namespace for package update related classes.
1345 */
1346WCF.ACP.Package.Update = { };
1347
1348/**
1349 * Handles the package update process.
1350 */
1351WCF.ACP.Package.Update.Manager = Class.extend({
1352 /**
1353 * dialog overlay
1354 * @var jQuery
1355 */
1356 _dialog: null,
1357
1358 /**
1359 * action proxy
1360 * @var WCF.Action.Proxy
1361 */
1362 _proxy: null,
1363
1364 /**
1365 * submit button
1366 * @var jQuery
1367 */
1368 _submitButton: null,
1369
1370 /**
1371 * Initializes the WCF.ACP.Package.Update.Manager class.
1372 */
1373 init: function() {
1374 this._dialog = null;
1375 this._submitButton = $('.formSubmit > button').click($.proxy(this._click, this));
1376
1377 this._proxy = new WCF.Action.Proxy({
1378 success: $.proxy(this._success, this)
1379 });
1380
1381 $('.jsPackageUpdate').each($.proxy(function(index, packageUpdate) {
1382 var $packageUpdate = $(packageUpdate);
1383 $packageUpdate.find('input[type=checkbox]').data('packageUpdate', $packageUpdate).change($.proxy(this._change, this));
1384 }, this));
1385 },
1386
1387 /**
1388 * Handles toggles for a specific update.
1389 */
1390 _change: function(event) {
1391 var $checkbox = $(event.currentTarget);
1392
1393 if ($checkbox.is(':checked')) {
1394 $checkbox.data('packageUpdate').find('select').enable();
1395 $checkbox.data('packageUpdate').find('dl').removeClass('disabled');
1396
1397 this._submitButton.enable();
1398 }
1399 else {
1400 $checkbox.data('packageUpdate').find('select').disable();
1401 $checkbox.data('packageUpdate').find('dl').addClass('disabled');
1402
1403 // disable submit button
1404 if (!$('input[type=checkbox]:checked').length) {
1405 this._submitButton.disable();
1406 }
1407 }
1408 },
1409
1410 /**
1411 * Handles clicks on the submit button.
1412 *
1413 * @param object event
1414 * @param integer packageUpdateServerID
1415 */
1416 _click: function(event, packageUpdateServerID) {
1417 var $packages = { };
1418 $('.jsPackageUpdate').each($.proxy(function(index, packageUpdate) {
1419 var $packageUpdate = $(packageUpdate);
1420 if ($packageUpdate.find('input[type=checkbox]:checked').length) {
1421 $packages[$packageUpdate.data('package')] = $packageUpdate.find('select').val();
1422 }
1423 }, this));
1424
1425 if ($.getLength($packages)) {
839984a4
MS
1426 this._submitButton.disable();
1427
3536d2fe
AE
1428 var $parameters = {
1429 packages: $packages
1430 };
1431 if (packageUpdateServerID) {
1432 $parameters.authData = {
1433 packageUpdateServerID: packageUpdateServerID,
1434 password: $.trim($('#packageUpdateServerPassword').val()),
1435 saveCredentials: ($('#packageUpdateServerSaveCredentials:checked').length ? true : false),
1436 username: $.trim($('#packageUpdateServerUsername').val())
996eb650 1437 };
3536d2fe
AE
1438 }
1439
1440 this._proxy.setOption('data', {
1441 actionName: 'prepareUpdate',
1442 className: 'wcf\\data\\package\\update\\PackageUpdateAction',
1443 parameters: $parameters,
1444 });
1445 this._proxy.sendRequest();
1446 }
1447 },
1448
1449 /**
1450 * Handles successful AJAX requests.
1451 *
1452 * @param object data
1453 * @param string textStatus
1454 * @param jQuery jqXHR
1455 */
1456 _success: function(data, textStatus, jqXHR) {
1457 if (data.returnValues.queueID) {
1458 if (this._dialog !== null) {
1459 this._dialog.wcfDialog('close');
1460 }
1461
adf9791d 1462 var $installation = new WCF.ACP.Package.Installation(data.returnValues.queueID, undefined, false, true);
3536d2fe
AE
1463 $installation.prepareInstallation();
1464 }
1465 else if (data.returnValues.template) {
1466 if (this._dialog === null) {
1467 this._dialog = $('<div>' + data.returnValues.template + '</div>').hide().appendTo(document.body);
1468 this._dialog.wcfDialog({
1469 title: WCF.Language.get('wcf.acp.package.update.unauthorized')
1470 });
1471 }
1472 else {
1473 this._dialog.html(data.returnValues.template).wcfDialog('open');
1474 }
1475
1476 this._dialog.find('.formSubmit > button').click($.proxy(this._submitAuthentication, this));
1477 }
1478 },
1479
1480 /**
1481 * Submits authentication data for current update server.
1482 *
1483 * @param object event
1484 */
1485 _submitAuthentication: function(event) {
1486 var $usernameField = $('#packageUpdateServerUsername');
1487 var $passwordField = $('#packageUpdateServerPassword');
1488
1489 // remove error messages if any
1490 $usernameField.next('small.innerError').remove();
1491 $passwordField.next('small.innerError').remove();
1492
1493 var $continue = true;
1494 if ($.trim($usernameField.val()) === '') {
1495 $('<small class="innerError">' + WCF.Language.get('wcf.global.form.error.empty') + '</small>').insertAfter($usernameField);
1496 $continue = false;
1497 }
1498
1499 if ($.trim($passwordField.val()) === '') {
1500 $('<small class="innerError">' + WCF.Language.get('wcf.global.form.error.empty') + '</small>').insertAfter($passwordField);
1501 $continue = false;
1502 }
1503
1504 if ($continue) {
22508797 1505 this._click(undefined, $(event.currentTarget).data('packageUpdateServerID'));
3536d2fe
AE
1506 }
1507 }
1508});
1509
1510/**
1511 * Searches for available updates.
60fe64f6
AE
1512 *
1513 * @param boolean bindOnExistingButtons
3536d2fe
AE
1514 */
1515WCF.ACP.Package.Update.Search = Class.extend({
1516 /**
1517 * dialog overlay
1518 * @var jQuery
1519 */
1520 _dialog: null,
1521
1522 /**
60fe64f6
AE
1523 * Initializes the WCF.ACP.Package.SearchForUpdates class.
1524 *
1525 * @param boolean bindOnExistingButtons
3536d2fe 1526 */
60fe64f6 1527 init: function(bindOnExistingButtons) {
3536d2fe
AE
1528 this._dialog = null;
1529
60fe64f6
AE
1530 if (bindOnExistingButtons === true) {
1531 $('.jsButtonPackageUpdate').click($.proxy(this._click, this));
1532 }
1533 else {
1534 var $button = $('<li><a class="button"><span class="icon icon16 icon-refresh"></span> <span>' + WCF.Language.get('wcf.acp.package.searchForUpdates') + '</span></a></li>');
1535 $button.click($.proxy(this._click, this)).prependTo($('.contentNavigation:eq(0) > nav:not(.pageNavigation) > ul'));
1536 }
3536d2fe
AE
1537 },
1538
1539 /**
1540 * Handles clicks on the search button.
1541 */
1542 _click: function() {
1543 if (this._dialog === null) {
1544 new WCF.Action.Proxy({
1545 autoSend: true,
1546 data: {
1547 actionName: 'searchForUpdates',
70e6f4e6
AE
1548 className: 'wcf\\data\\package\\update\\PackageUpdateAction',
1549 parameters: {
1550 ignoreCache: 1
1551 }
3536d2fe
AE
1552 },
1553 success: $.proxy(this._success, this)
1554 });
1555 }
1556 else {
1557 this._dialog.wcfDialog('open');
1558 }
1559 },
1560
1561 /**
1562 * Handles successful AJAX requests.
1563 *
1564 * @param object data
1565 * @param string textStatus
1566 * @param jQuery jqXHR
1567 */
1568 _success: function(data, textStatus, jqXHR) {
1569 if (data.returnValues.url) {
1570 window.location = data.returnValues.url;
1571 }
1572 else {
1573 this._dialog = $('<div>' + WCF.Language.get('wcf.acp.package.searchForUpdates.noResults') + '</div>').hide().appendTo(document.body);
1574 this._dialog.wcfDialog({
1575 title: WCF.Language.get('wcf.acp.package.searchForUpdates')
1576 });
1577 }
1578 }
1579});
1580
317c8af5
AE
1581/**
1582 * Namespace for classes related to the WoltLab Plugin-Store.
1583 */
1584WCF.ACP.PluginStore = { };
1585
1586/**
1587 * Namespace for classes handling items purchased in the WoltLab Plugin-Store.
1588 */
1589WCF.ACP.PluginStore.PurchasedItems = { };
1590
1591/**
1592 * Searches for purchased items available for install but not yet installed.
1593 */
1594WCF.ACP.PluginStore.PurchasedItems.Search = Class.extend({
4087dcf3
AE
1595 /**
1596 * dialog overlay
1597 * @var jQuery
1598 */
317c8af5 1599 _dialog: null,
4087dcf3
AE
1600
1601 /**
1602 * action proxy
1603 * @var WCF.Action.Proxy
1604 */
317c8af5 1605 _proxy: null,
317c8af5 1606
4087dcf3
AE
1607 /**
1608 * Initializes the WCF.ACP.PluginStore.PurchasedItems.Search class.
1609 */
d1813e77 1610 init: function() {
317c8af5
AE
1611 this._dialog = null;
1612 this._proxy = new WCF.Action.Proxy({
1613 success: $.proxy(this._success, this)
1614 });
317c8af5 1615
d528766d 1616 var $button = $('<li><a class="button"><span class="icon icon16 fa-shopping-cart" /> <span>' + WCF.Language.get('wcf.acp.pluginStore.purchasedItems.button.search') + '</span></a></li>');
317c8af5
AE
1617 $button.prependTo($('.contentNavigation:eq(0) > nav > ul')).click($.proxy(this._click, this));
1618 },
1619
4087dcf3
AE
1620 /**
1621 * Handles clicks on the search button.
1622 */
317c8af5
AE
1623 _click: function() {
1624 this._proxy.setOption('data', {
1625 actionName: 'searchForPurchasedItems',
d1813e77 1626 className: 'wcf\\data\\package\\PackageAction'
317c8af5
AE
1627 });
1628 this._proxy.sendRequest();
1629 },
1630
4087dcf3
AE
1631 /**
1632 * Handles successful AJAX requests.
1633 *
1634 * @param object data
1635 * @param string textStatus
1636 * @param jQuery jqXHR
1637 */
317c8af5 1638 _success: function(data, textStatus, jqXHR) {
4087dcf3 1639 // prompt for credentials
317c8af5
AE
1640 if (data.returnValues.template) {
1641 if (this._dialog === null) {
1642 this._dialog = $('<div />').hide().appendTo(document.body);
1643 this._dialog.html(data.returnValues.template).wcfDialog({
d528766d 1644 title: WCF.Language.get('wcf.acp.pluginStore.authorization')
317c8af5
AE
1645 });
1646 }
1647 else {
1648 this._dialog.html(data.returnValues.template);
1649 this._dialog.wcfDialog('open');
1650 }
1651
298a7cd8
AE
1652 var $button = this._dialog.find('button').click($.proxy(this._submit, this));
1653 this._dialog.find('input').keyup(function(event) {
1654 if (event.which == $.ui.keyCode.ENTER) {
1655 $button.trigger('click');
1656 return false;
1657 }
1658 });
317c8af5
AE
1659 }
1660 else if (data.returnValues.noResults) {
4087dcf3 1661 // there are no purchased products yet
d528766d 1662 this._dialog.wcfDialog('option', 'title', WCF.Language.get('wcf.acp.pluginStore.purchasedItems'));
317c8af5
AE
1663 this._dialog.html(data.returnValues.noResults);
1664 this._dialog.wcfDialog('open');
1665 }
298a7cd8 1666 else if (data.returnValues.redirectURL) {
4087dcf3 1667 // redirect to list of purchased products
298a7cd8
AE
1668 window.location = data.returnValues.redirectURL;
1669 }
317c8af5
AE
1670 },
1671
4087dcf3
AE
1672 /**
1673 * Submits the user credentials.
1674 */
317c8af5
AE
1675 _submit: function() {
1676 this._dialog.wcfDialog('close');
1677
1678 this._proxy.setOption('data', {
1679 actionName: 'searchForPurchasedItems',
1680 className: 'wcf\\data\\package\\PackageAction',
1681 parameters: {
1682 password: $('#pluginStorePassword').val(),
d1813e77 1683 username: $('#pluginStoreUsername').val()
317c8af5
AE
1684 }
1685 });
1686 this._proxy.sendRequest();
1687 }
1688});
1689
158bd3ca
TD
1690/**
1691 * Handles option selection.
1692 */
cb9e9c12 1693WCF.ACP.Options = Class.extend({
158bd3ca
TD
1694 /**
1695 * Initializes options.
1696 */
1697 init: function() {
b950dd22 1698 $('.jsEnablesOptions').each($.proxy(this._initOption, this));
158bd3ca
TD
1699 },
1700
1701 /**
1702 * Initializes an option.
1703 *
1704 * @param integer index
1705 * @param object option
1706 */
1707 _initOption: function(index, option) {
1708 // execute action on init
1709 this._change(option);
1710
1711 // bind event listener
1712 $(option).change($.proxy(this._handleChange, this));
1713 },
1714
1715 /**
1716 * Applies whenever an option is changed.
1717 *
1718 * @param object event
1719 */
1720 _handleChange: function(event) {
1721 this._change($(event.target));
1722 },
1723
1724 /**
1725 * Enables or disables options on option value change.
1726 *
1727 * @param object option
1728 */
1729 _change: function(option) {
1730 option = $(option);
1731
1732 var $disableOptions = eval(option.data('disableOptions'));
1733 var $enableOptions = eval(option.data('enableOptions'));
1734
1735 // determine action by type
1736 switch(option.getTagName()) {
1737 case 'input':
1738 switch(option.attr('type')) {
1739 case 'checkbox':
1a50aae0 1740 this._execute(option.prop('checked'), $disableOptions, $enableOptions);
158bd3ca
TD
1741 break;
1742
1743 case 'radio':
1a50aae0 1744 if (option.prop('checked')) {
158bd3ca
TD
1745 this._execute(true, $disableOptions, $enableOptions);
1746 }
1747 break;
1748 }
1749 break;
1750
1751 case 'select':
1752 var $value = option.val();
1753 var $disableOptions = $enableOptions = [];
1754
1755 if (option.data('disableOptions').length > 0) {
1756 for (var $index in option.data('disableOptions')) {
1757 var $item = option.data('disableOptions')[$index];
1758
1759 if ($item.value == $value) {
1760 $disableOptions.push($item.option);
1761 }
1762 }
1763 }
1764
1765 if (option.data('enableOptions').length > 0) {
1766 for (var $index in option.data('enableOptions')) {
1767 var $item = option.data('enableOptions')[$index];
1768
1769 if ($item.value == $value) {
1770 $enableOptions.push($item.option);
1771 }
1772 }
1773 }
1774
1775 this._execute(true, $disableOptions, $enableOptions);
1776 break;
1777 }
1778 },
1779
1780 /**
1781 * Enables or disables options.
1782 *
1783 * @param boolean isActive
1784 * @param array disableOptions
1785 * @param array enableOptions
1786 */
1787 _execute: function(isActive, disableOptions, enableOptions) {
1788 if (disableOptions.length > 0) {
1789 for (var $i = 0, $size = disableOptions.length; $i < $size; $i++) {
1790 var $target = disableOptions[$i];
1791 if ($.wcfIsset($target)) {
cb9caddf 1792 this._enableOption($target, !isActive);
158bd3ca
TD
1793 }
1794 }
1795 }
1796
1797 if (enableOptions.length > 0) {
1798 for (var $i = 0, $size = enableOptions.length; $i < $size; $i++) {
1799 var $target = enableOptions[$i];
1800 if ($.wcfIsset($target)) {
cb9caddf 1801 this._enableOption($target, isActive);
158bd3ca
TD
1802 }
1803 }
1804 }
cb9caddf
MW
1805 },
1806
1807 /**
1808 * Enables an option.
59dc0db6 1809 *
cb9caddf
MW
1810 * @param string target
1811 * @param boolean enable
1812 */
1813 _enableOption: function(target, enable) {
1814 var $targetElement = $('#' + $.wcfEscapeID(target));
1815 var $tagName = $targetElement.getTagName();
1816
1817 if ($tagName == 'select' || ($tagName == 'input' && ($targetElement.attr('type') == 'checkbox' || $targetElement.attr('type') == 'radio'))) {
1818 if (enable) $targetElement.enable();
1819 else $targetElement.disable();
1820 }
1821 else {
1822 if (enable) $targetElement.removeAttr('readonly');
1823 else $targetElement.attr('readonly', true);
1824 }
1825
1826 if (enable) {
1827 $targetElement.closest('dl').removeClass('disabled');
1828 }
1829 else {
1830 $targetElement.closest('dl').addClass('disabled');
1831 }
158bd3ca 1832 }
cb9e9c12 1833});
532f1173
AE
1834
1835/**
1836 * Worker support for ACP.
1837 *
1838 * @param string dialogID
1839 * @param string className
0cb0c522
AE
1840 * @param string title
1841 * @param object parameters
1842 * @param object callback
532f1173 1843 */
1f9100ba 1844WCF.ACP.Worker = Class.extend({
af7da802
AE
1845 /**
1846 * worker aborted
1847 * @var boolean
1848 */
1849 _aborted: false,
1850
0cb0c522
AE
1851 /**
1852 * callback invoked after worker completed
1853 * @var object
1854 */
1855 _callback: null,
1856
532f1173
AE
1857 /**
1858 * dialog id
1859 * @var string
1860 */
1861 _dialogID: null,
1862
1863 /**
1864 * dialog object
1865 * @var jQuery
1866 */
1867 _dialog: null,
1868
2cea2b5b
AE
1869 /**
1870 * action proxy
1871 * @var WCF.Action.Proxy
1872 */
1873 _proxy: null,
1874
4da866ad
AE
1875 /**
1876 * dialog title
1877 * @var string
1878 */
1879 _title: '',
1880
532f1173
AE
1881 /**
1882 * Initializes a new worker instance.
1883 *
1884 * @param string dialogID
1885 * @param string className
2cea2b5b
AE
1886 * @param string title
1887 * @param object parameters
0cb0c522 1888 * @param object callback
af7da802 1889 * @param object confirmMessage
532f1173 1890 */
0cb0c522 1891 init: function(dialogID, className, title, parameters, callback) {
af7da802 1892 this._aborted = false;
0cb0c522 1893 this._callback = callback || null;
532f1173 1894 this._dialogID = dialogID + 'Worker';
2cea2b5b
AE
1895 this._dialog = null;
1896 this._proxy = new WCF.Action.Proxy({
4da866ad 1897 autoSend: true,
c556ba48
TD
1898 data: {
1899 className: className,
2cea2b5b 1900 parameters: parameters || { }
532f1173 1901 },
4da866ad 1902 showLoadingOverlay: false,
2cea2b5b 1903 success: $.proxy(this._success, this),
4da866ad 1904 url: 'index.php/WorkerProxy/?t=' + SECURITY_TOKEN + SID_ARG_2ND
532f1173 1905 });
4da866ad 1906 this._title = title;
532f1173
AE
1907 },
1908
1909 /**
1910 * Handles response from server.
2cea2b5b
AE
1911 *
1912 * @param object data
532f1173 1913 */
2cea2b5b 1914 _success: function(data) {
532f1173
AE
1915 // init binding
1916 if (this._dialog === null) {
4da866ad
AE
1917 this._dialog = $('<div id="' + this._dialogID + '" />').hide().appendTo(document.body);
1918 this._dialog.wcfDialog({
af7da802
AE
1919 closeConfirmMessage: WCF.Language.get('wcf.acp.worker.abort.confirmMessage'),
1920 closeViaModal: false,
06355ec3 1921 onClose: $.proxy(function() {
af7da802 1922 this._aborted = true;
131a201b 1923 this._proxy.abortPrevious();
af7da802
AE
1924
1925 window.location.reload();
131a201b 1926 }, this),
4da866ad
AE
1927 title: this._title
1928 });
532f1173
AE
1929 }
1930
af7da802
AE
1931 if (this._aborted) {
1932 return;
1933 }
1934
196e0bd2
MS
1935 if (data.template) {
1936 this._dialog.html(data.template);
1937 }
1938
532f1173 1939 // update progress
2cea2b5b 1940 this._dialog.find('progress').attr('value', data.progress).text(data.progress + '%').next('span').text(data.progress + '%');
532f1173 1941
e72efaf9 1942 // worker is still busy with its business, carry on
2cea2b5b 1943 if (data.progress < 100) {
532f1173 1944 // send request for next loop
2cea2b5b
AE
1945 this._proxy.setOption('data', {
1946 className: data.className,
1947 loopCount: data.loopCount,
fb213f97 1948 parameters: data.parameters
532f1173 1949 });
2cea2b5b 1950 this._proxy.sendRequest();
532f1173 1951 }
0cb0c522
AE
1952 else if (this._callback !== null) {
1953 this._callback(this, data);
1954 }
891d1d67 1955 else {
2cea2b5b
AE
1956 // display continue button
1957 var $formSubmit = $('<div class="formSubmit" />').appendTo(this._dialog);
e72efaf9 1958 $('<button class="buttonPrimary">' + WCF.Language.get('wcf.global.button.next') + '</button>').appendTo($formSubmit).focus().click(function() { window.location = data.proceedURL; });
891d1d67 1959
e5b88a7f 1960 this._dialog.wcfDialog('render');
891d1d67 1961 }
532f1173 1962 }
1f9100ba 1963});
13d8b49b
MS
1964
1965/**
1966 * Namespace for category-related functions.
1967 */
cb6e5946 1968WCF.ACP.Category = { };
13d8b49b
MS
1969
1970/**
1971 * Handles collapsing categories.
1972 *
1973 * @param string className
1974 * @param integer objectTypeID
1975 */
1976WCF.ACP.Category.Collapsible = WCF.Collapsible.SimpleRemote.extend({
1977 /**
1978 * @see WCF.Collapsible.Remote.init()
1979 */
fc3d134b 1980 init: function(className) {
13d8b49b
MS
1981 var sortButton = $('.formSubmit > button[data-type="submit"]');
1982 if (sortButton) {
1983 sortButton.click($.proxy(this._sort, this));
1984 }
1985
1986 this._super(className);
1987 },
9f959ced 1988
13d8b49b
MS
1989 /**
1990 * @see WCF.Collapsible.Remote._getButtonContainer()
1991 */
1992 _getButtonContainer: function(containerID) {
1993 return $('#' + containerID + ' > .buttons');
1994 },
9f959ced 1995
13d8b49b
MS
1996 /**
1997 * @see WCF.Collapsible.Remote._getContainers()
1998 */
1999 _getContainers: function() {
2000 return $('.jsCategory').has('ol').has('li');
2001 },
9f959ced 2002
13d8b49b
MS
2003 /**
2004 * @see WCF.Collapsible.Remote._getTarget()
2005 */
2006 _getTarget: function(containerID) {
2007 return $('#' + containerID + ' > ol');
2008 },
2009
2010 /**
2011 * Handles a click on the sort button.
2012 */
2013 _sort: function() {
2014 // remove existing collapsible buttons
2015 $('.collapsibleButton').remove();
2016
2017 // reinit containers
cb6e5946
MS
2018 this._containers = { };
2019 this._containerData = { };
13d8b49b
MS
2020
2021 var $containers = this._getContainers();
2022 if ($containers.length == 0) {
a0b60d23 2023 console.debug('[WCF.ACP.Category.Collapsible] Empty container set given, aborting.');
13d8b49b
MS
2024 }
2025 $containers.each($.proxy(function(index, container) {
2026 var $container = $(container);
2027 var $containerID = $container.wcfIdentify();
2028 this._containers[$containerID] = $container;
2029
2030 this._initContainer($containerID);
2031 }, this));
2032 }
2033});
2034
71662ae8
AE
2035/**
2036 * Provides the search dropdown for ACP
2037 *
2038 * @see WCF.Search.Base
2039 */
2040WCF.ACP.Search = WCF.Search.Base.extend({
2041 /**
2042 * @see WCF.Search.Base.init()
2043 */
2044 init: function() {
2045 this._className = 'wcf\\data\\acp\\search\\provider\\ACPSearchProviderAction';
2046 this._super('#search input[name=q]');
d6760827
MS
2047
2048 // disable form submitting
2049 $('#search > form').on('submit', function(event) {
2050 event.preventDefault();
2051 });
71662ae8
AE
2052 },
2053
2054 /**
2055 * @see WCF.Search.Base._createListItem()
2056 */
2057 _createListItem: function(resultList) {
2058 // add a divider between result lists
2059 if (this._list.children('li').length > 0) {
2060 $('<li class="dropdownDivider" />').appendTo(this._list);
2061 }
2062
2063 // add caption
2064 $('<li class="dropdownText">' + resultList.title + '</li>').appendTo(this._list);
2065
2066 // add menu items
2067 for (var $i in resultList.items) {
2068 var $item = resultList.items[$i];
2069
a980041b 2070 $('<li><a href="' + $item.link + '">' + WCF.String.escapeHTML($item.title) + '</a></li>').appendTo(this._list);
9a130ab4
AE
2071
2072 this._itemCount++;
71662ae8 2073 }
9a130ab4
AE
2074 },
2075
c138555b
AE
2076 /**
2077 * @see WCF.Search.Base._handleEmptyResult()
2078 */
2079 _handleEmptyResult: function() {
2080 $('<li class="dropdownText">' + WCF.Language.get('wcf.acp.search.noResults') + '</li>').appendTo(this._list);
2081
2082 return true;
2083 },
2084
9a130ab4
AE
2085 /**
2086 * @see WCF.Search.Base._highlightSelectedElement()
2087 */
2088 _highlightSelectedElement: function() {
2089 this._list.find('li').removeClass('dropdownNavigationItem');
2090 this._list.find('li:not(.dropdownDivider):not(.dropdownText)').eq(this._itemIndex).addClass('dropdownNavigationItem');
2091 },
2092
2093 /**
2094 * @see WCF.Search.Base._selectElement()
2095 */
2096 _selectElement: function(event) {
c138555b
AE
2097 if (this._itemIndex === -1) {
2098 return false;
2099 }
2100
9a130ab4 2101 window.location = this._list.find('li.dropdownNavigationItem > a').attr('href');
71662ae8
AE
2102 }
2103});
11cf19be
MW
2104
2105/**
2106 * Namespace for user management.
2107 */
2108WCF.ACP.User = { };
2109
2110/**
2111 * Generic implementation to ban users.
2112 */
2113WCF.ACP.User.BanHandler = {
2114 /**
2115 * callback object
2116 * @var object
2117 */
2118 _callback: null,
2119
2120 /**
2121 * dialog overlay
2122 * @var jQuery
2123 */
2124 _dialog: null,
2125
2126 /**
2127 * action proxy
2128 * @var WCF.Action.Proxy
2129 */
2130 _proxy: null,
2131
2132 /**
2133 * Initializes WCF.ACP.User.BanHandler on first use.
2134 */
2135 init: function() {
2136 this._dialog = $('<div />').hide().appendTo(document.body);
2137 this._proxy = new WCF.Action.Proxy({
2138 success: $.proxy(this._success, this)
2139 });
2140
2141 $('.jsBanButton').click($.proxy(function(event) {
2142 var $button = $(event.currentTarget);
2143 if ($button.data('banned')) {
2144 this.unban([ $button.data('objectID') ]);
2145 }
2146 else {
2147 this.ban([ $button.data('objectID') ]);
2148 }
2149 }, this));
bbef7ed8
MW
2150
2151 // bind listener
2152 $('.jsClipboardEditor').each($.proxy(function(index, container) {
2153 var $container = $(container);
2154 var $types = eval($container.data('types'));
2155 if (WCF.inArray('com.woltlab.wcf.user', $types)) {
2156 $container.on('clipboardAction', $.proxy(this._execute, this));
2157 return false;
2158 }
2159 }, this));
2160 },
2161
2162 /**
2163 * Handles clipboard actions.
2164 *
2165 * @param object event
2166 * @param string type
2167 * @param string actionName
2168 * @param object parameters
2169 */
2170 _execute: function(event, type, actionName, parameters) {
2171 if (actionName == 'com.woltlab.wcf.user.ban') {
2172 this.ban(parameters.objectIDs);
2173 }
11cf19be
MW
2174 },
2175
2176 /**
2177 * Unbans users.
2178 *
2179 * @param array<integer> userIDs
2180 */
2181 unban: function(userIDs) {
2182 this._proxy.setOption('data', {
2183 actionName: 'unban',
2184 className: 'wcf\\data\\user\\UserAction',
2185 objectIDs: userIDs
2186 });
2187 this._proxy.sendRequest();
2188 },
2189
2190 /**
2191 * Bans users.
2192 *
2193 * @param array<integer> userIDs
2194 */
2195 ban: function(userIDs) {
2196 WCF.System.Confirmation.show(WCF.Language.get('wcf.acp.user.ban.sure'), $.proxy(function(action) {
2197 if (action === 'confirm') {
2198 this._proxy.setOption('data', {
2199 actionName: 'ban',
2200 className: 'wcf\\data\\user\\UserAction',
2201 objectIDs: userIDs,
2202 parameters: {
2203 banReason: $('#userBanReason').val()
2204 }
2205 });
2206 this._proxy.sendRequest();
2207 }
2208 }, this), '', $('<fieldset><dl><dt><label for="userBanReason">' + WCF.Language.get('wcf.acp.user.banReason') + '</label></dt><dd><textarea id="userBanReason" cols="40" rows="3" /><small>' + WCF.Language.get('wcf.acp.user.banReason.description') + '</small></dd></dl></fieldset>'));
2209 },
2210
2211 /**
2212 * Handles successful AJAX calls.
2213 *
2214 * @param object data
2215 * @param string textStatus
2216 * @param jQuery jqXHR
2217 */
2218 _success: function(data, textStatus, jqXHR) {
2219 $('.jsBanButton').each(function(index, button) {
2220 var $button = $(button);
2221 if (WCF.inArray($button.data('objectID'), data.objectIDs)) {
2222 if (data.actionName == 'unban') {
2223 $button.data('banned', false).data('tooltip', $button.data('banMessage')).removeClass('icon-lock').addClass('icon-unlock');
2224 }
2225 else {
2226 $button.data('banned', true).data('tooltip', $button.data('unbanMessage')).removeClass('icon-unlock').addClass('icon-lock');
2227 }
2228 }
2229 });
2230
2231 var $notification = new WCF.System.Notification();
2232 $notification.show();
bbef7ed8
MW
2233
2234 WCF.Clipboard.reload();
11cf19be 2235 }
bc3b71fd 2236};
ef012ee1
MW
2237
2238/**
2239 * Generic implementation to enable users.
2240 */
2241WCF.ACP.User.EnableHandler = {
2242 /**
2243 * action proxy
2244 * @var WCF.Action.Proxy
2245 */
2246 _proxy: null,
2247
2248 /**
2249 * Initializes WCF.ACP.User.EnableHandler on first use.
2250 */
2251 init: function() {
2252 this._proxy = new WCF.Action.Proxy({
2253 success: $.proxy(this._success, this)
2254 });
2255
2256 $('.jsEnableButton').click($.proxy(function(event) {
2257 var $button = $(event.currentTarget);
2258 if ($button.data('enabled')) {
2259 this.disable([ $button.data('objectID') ]);
2260 }
2261 else {
2262 this.enable([ $button.data('objectID') ]);
2263 }
2264 }, this));
2265
2266 // bind listener
2267 $('.jsClipboardEditor').each($.proxy(function(index, container) {
2268 var $container = $(container);
2269 var $types = eval($container.data('types'));
2270 if (WCF.inArray('com.woltlab.wcf.user', $types)) {
2271 $container.on('clipboardAction', $.proxy(this._execute, this));
2272 return false;
2273 }
2274 }, this));
2275 },
2276
2277 /**
2278 * Handles clipboard actions.
2279 *
2280 * @param object event
2281 * @param string type
2282 * @param string actionName
2283 * @param object parameters
2284 */
2285 _execute: function(event, type, actionName, parameters) {
2286 if (actionName == 'com.woltlab.wcf.user.enable') {
2287 this.enable(parameters.objectIDs);
2288 }
2289 },
2290
2291 /**
2292 * Disables users.
2293 *
2294 * @param array<integer> userIDs
2295 */
2296 disable: function(userIDs) {
2297 this._proxy.setOption('data', {
2298 actionName: 'disable',
2299 className: 'wcf\\data\\user\\UserAction',
2300 objectIDs: userIDs
2301 });
2302 this._proxy.sendRequest();
2303 },
2304
2305 /**
2306 * Enables users.
2307 *
2308 * @param array<integer> userIDs
2309 */
2310 enable: function(userIDs) {
2311 this._proxy.setOption('data', {
2312 actionName: 'enable',
2313 className: 'wcf\\data\\user\\UserAction',
2314 objectIDs: userIDs
2315 });
2316 this._proxy.sendRequest();
2317 },
2318
2319 /**
2320 * Handles successful AJAX calls.
2321 *
2322 * @param object data
2323 * @param string textStatus
2324 * @param jQuery jqXHR
2325 */
2326 _success: function(data, textStatus, jqXHR) {
2327 $('.jsEnableButton').each(function(index, button) {
2328 var $button = $(button);
2329 if (WCF.inArray($button.data('objectID'), data.objectIDs)) {
2330 if (data.actionName == 'disable') {
d3bd4037 2331 $button.data('enabled', false).data('tooltip', $button.data('enableMessage')).removeClass('icon-check').addClass('icon-check-empty');
ef012ee1
MW
2332 }
2333 else {
d3bd4037 2334 $button.data('enabled', true).data('tooltip', $button.data('disableMessage')).removeClass('icon-check-empty').addClass('icon-check');
ef012ee1
MW
2335 }
2336 }
2337 });
2338
2339 var $notification = new WCF.System.Notification();
00ce5cf8 2340 $notification.show(function() { window.location.reload(); });
ef012ee1
MW
2341 }
2342};
2343
cb6e5946
MS
2344/**
2345 * Handles the send new password clipboard action.
2346 */
2347WCF.ACP.User.SendNewPasswordHandler = {
2348 /**
2349 * action proxy
2350 * @var WCF.Action.Proxy
2351 */
2352 _proxy: null,
2353
2354 /**
2355 * Initializes WCF.ACP.User.SendNewPasswordHandler on first use.
2356 */
2357 init: function() {
2358 this._proxy = new WCF.Action.Proxy({
2359 success: $.proxy(this._success, this)
2360 });
2361
2362 // bind clipboard event listener
2363 $('.jsClipboardEditor').each($.proxy(function(index, container) {
2364 var $container = $(container);
2365 var $types = eval($container.data('types'));
2366 if (WCF.inArray('com.woltlab.wcf.user', $types)) {
2367 $container.on('clipboardAction', $.proxy(this._execute, this));
2368 return false;
2369 }
2370 }, this));
2371 },
2372
2373 /**
2374 * Handles clipboard actions.
2375 *
2376 * @param object event
2377 * @param string type
2378 * @param string actionName
2379 * @param object parameters
2380 */
2381 _execute: function(event, type, actionName, parameters) {
2382 if (actionName == 'com.woltlab.wcf.user.sendNewPassword') {
2383 WCF.System.Confirmation.show(parameters.confirmMessage, function(action) {
2384 if (action === 'confirm') {
2385 new WCF.ACP.Worker('sendingNewPasswords', 'wcf\\system\\worker\\SendNewPasswordWorker', WCF.Language.get('wcf.acp.user.sendNewPassword.workerTitle'), {
2386 userIDs: parameters.objectIDs
2387 });
2388 }
2389 });
2390 }
2391 }
2392};
2393
971fbab8
AE
2394/**
2395 * Namespace for import-related classes.
2396 */
2397WCF.ACP.Import = { };
2398
24d15c69
AE
2399/**
2400 * Importer for ACP.
2401 *
971fbab8 2402 * @param array<string> objectTypes
24d15c69 2403 */
971fbab8 2404WCF.ACP.Import.Manager = Class.extend({
24d15c69 2405 /**
971fbab8
AE
2406 * current action
2407 * @var string
24d15c69 2408 */
971fbab8 2409 _currentAction: '',
24d15c69
AE
2410
2411 /**
2412 * dialog overlay
2413 * @var jQuery
2414 */
2415 _dialog: null,
2416
971fbab8
AE
2417 /**
2418 * current object type index
2419 * @var integer
2420 */
2421 _index: -1,
2422
2423 /**
2424 * list of object types
2425 * @var array<string>
2426 */
2427 _objectTypes: [ ],
2428
24d15c69
AE
2429 /**
2430 * action proxy
2431 * @var WCF.Action.Proxy
2432 */
2433 _proxy: null,
2434
fed8bce0
AE
2435 /**
2436 * redirect URL
2437 * @var string
2438 */
2439 _redirectURL: '',
2440
24d15c69
AE
2441 /**
2442 * Initializes the WCF.ACP.Importer object.
2443 *
971fbab8 2444 * @param array<string> objectTypes
fed8bce0 2445 * @param string redirectURL
24d15c69 2446 */
fed8bce0 2447 init: function(objectTypes, redirectURL) {
971fbab8
AE
2448 this._currentAction = '';
2449 this._index = -1;
2450 this._objectTypes = objectTypes;
24d15c69
AE
2451 this._proxy = new WCF.Action.Proxy({
2452 showLoadingOverlay: false,
2453 success: $.proxy(this._success, this),
2454 url: 'index.php/WorkerProxy/?t=' + SECURITY_TOKEN + SID_ARG_2ND
2455 });
fed8bce0 2456 this._redirectURL = redirectURL;
971fbab8
AE
2457
2458 this._invoke();
24d15c69
AE
2459 },
2460
2461 /**
2462 * Invokes importing of an object type.
971fbab8
AE
2463 */
2464 _invoke: function() {
2465 this._index++;
2466 if (this._index >= this._objectTypes.length) {
3bbdfdb0
AE
2467 this._dialog.find('.icon-spinner').removeClass('icon-spinner').addClass('icon-ok');
2468 this._dialog.find('h1').text(WCF.Language.get('wcf.acp.dataImport.completed'));
2469
2470 var $form = $('<div class="formSubmit" />').appendTo(this._dialog.find('#workerContainer'));
2471 $('<button>' + WCF.Language.get('wcf.global.button.next') + '</button>').click($.proxy(function() {
127ee67e
AE
2472 new WCF.Action.Proxy({
2473 autoSend: true,
2474 data: {
2475 noRedirect: 1
2476 },
1d997070 2477 dataType: 'html',
127ee67e
AE
2478 success: $.proxy(function() {
2479 window.location = this._redirectURL;
2480 }, this),
2481 url: 'index.php/CacheClear/?t=' + SECURITY_TOKEN + SID_ARG_2ND
2482 });
3bbdfdb0
AE
2483 }, this)).appendTo($form);
2484
2485 this._dialog.wcfDialog('render');
971fbab8
AE
2486 }
2487 else {
2488 this._run(
607e3941 2489 WCF.Language.get('wcf.acp.dataImport.data.' + this._objectTypes[this._index]),
971fbab8
AE
2490 this._objectTypes[this._index]
2491 );
2492 }
2493 },
2494
2495 /**
2496 * Executes import of given object type.
24d15c69 2497 *
971fbab8 2498 * @param string currentAction
24d15c69
AE
2499 * @param string objectType
2500 */
971fbab8
AE
2501 _run: function(currentAction, objectType) {
2502 this._currentAction = currentAction;
24d15c69
AE
2503 this._proxy.setOption('data', {
2504 className: 'wcf\\system\\worker\\ImportWorker',
2505 parameters: {
2506 objectType: objectType
2507 }
2508 });
2509 this._proxy.sendRequest();
2510 },
2511
2512 /**
2513 * Handles response from server.
2514 *
2515 * @param object data
2516 */
2517 _success: function(data) {
2518 // init binding
2519 if (this._dialog === null) {
2520 this._dialog = $('<div />').hide().appendTo(document.body);
2521 this._dialog.wcfDialog({
2522 closable: false,
2523 title: WCF.Language.get('wcf.acp.dataImport')
2524 });
2525 }
2526
2527 if (data.template) {
2528 this._dialog.html(data.template);
2529 }
2530
971fbab8
AE
2531 if (this._currentAction) {
2532 this._dialog.find('h1').text(this._currentAction);
2533 }
2534
24d15c69
AE
2535 // update progress
2536 this._dialog.find('progress').attr('value', data.progress).text(data.progress + '%').next('span').text(data.progress + '%');
2537
2538 // worker is still busy with it's business, carry on
2539 if (data.progress < 100) {
2540 // send request for next loop
2541 this._proxy.setOption('data', {
2542 className: data.className,
2543 loopCount: data.loopCount,
2544 parameters: data.parameters
2545 });
2546 this._proxy.sendRequest();
2547 }
2548 else {
971fbab8 2549 this._invoke();
24d15c69
AE
2550 }
2551 }
2552});
e1fe9453
MW
2553
2554/**
2555 * Namespace for stat-related classes.
2556 */
2557WCF.ACP.Stat = { };
2558
2559/**
2560 * Shows the daily stat chart.
2561 */
2562WCF.ACP.Stat.Chart = Class.extend({
2563 init: function() {
2564 this._proxy = new WCF.Action.Proxy({
2565 success: $.proxy(this._success, this)
2566 });
2567
49ec07c5 2568 $('#statRefreshButton').click($.proxy(this._refresh, this));
e1fe9453 2569
49ec07c5 2570 this._refresh();
e1fe9453
MW
2571 },
2572
49ec07c5 2573 _refresh: function() {
e1fe9453
MW
2574 var $objectTypeIDs = [ ];
2575 $('input[name=objectTypeID]:checked').each(function() {
2576 $objectTypeIDs.push($(this).val());
2577 });
2578
49ec07c5
MW
2579 if (!$objectTypeIDs.length) return;
2580
e1fe9453
MW
2581 this._proxy.setOption('data', {
2582 className: 'wcf\\data\\stat\\daily\\StatDailyAction',
2583 actionName: 'getData',
2584 parameters: {
2585 startDate: $('#startDateDatePicker').val(),
2586 endDate: $('#endDateDatePicker').val(),
2587 value: $('input[name=value]:checked').val(),
49ec07c5 2588 dateGrouping: $('input[name=dateGrouping]:checked').val(),
e1fe9453
MW
2589 objectTypeIDs: $objectTypeIDs
2590 }
2591 });
2592 this._proxy.sendRequest();
2593 },
2594
2595 _success: function(data) {
49ec07c5
MW
2596 switch ($('input[name=dateGrouping]:checked').val()) {
2597 case 'yearly':
2598 var $minTickSize = [1, "year"];
2599 var $timeFormat = WCF.Language.get('wcf.acp.stat.timeFormat.yearly');
2600 break;
2601 case 'monthly':
2602 var $minTickSize = [1, "month"];
2603 var $timeFormat = WCF.Language.get('wcf.acp.stat.timeFormat.monthly');
2604 break;
2605 case 'weekly':
2606 var $minTickSize = [7, "day"];
2607 var $timeFormat = WCF.Language.get('wcf.acp.stat.timeFormat.weekly');
2608 break;
2609 default:
2610 var $minTickSize = [1, "day"];
2611 var $timeFormat = WCF.Language.get('wcf.acp.stat.timeFormat.daily');
2612 }
2613
e1fe9453
MW
2614 var options = {
2615 series: {
2616 lines: {
2617 show: true
2618 },
2619 points: {
2620 show: true
2621 }
2622 },
2623 grid: {
2624 hoverable: true
2625 },
2626 xaxis: {
2627 mode: "time",
49ec07c5
MW
2628 minTickSize: $minTickSize,
2629 timeformat: $timeFormat,
2630 monthNames: WCF.Language.get('__monthsShort')
e1fe9453
MW
2631 },
2632 yaxis: {
2633 min: 0,
2634 tickDecimals: 0,
2635 tickFormatter: function(val) {
2636 return WCF.String.addThousandsSeparator(val);
2637 }
2638 },
2639 };
2640
2641 var $data = [ ];
2642 for (var $key in data.returnValues) {
2643 var $row = data.returnValues[$key];
2644 for (var $i = 0; $i < $row.data.length; $i++) {
2645 $row.data[$i][0] *= 1000;
2646 }
2647
2648 $data.push($row);
2649 }
2650
2651 $.plot("#chart", $data, options);
2652
66990032 2653 $("#chart").on("plothover", function(event, pos, item) {
e1fe9453 2654 if (item) {
49ec07c5 2655 $("#chartTooltip").html(item.series.xaxis.tickFormatter(item.datapoint[0], item.series.xaxis) + ', ' + WCF.String.formatNumeric(item.datapoint[1]) + ' ' + item.series.label).css({top: item.pageY + 5, left: item.pageX + 5}).wcfFadeIn();
66990032
MW
2656 }
2657 else {
e1fe9453
MW
2658 $("#chartTooltip").hide();
2659 }
2660 });
2661 }
e72efaf9 2662});