2 * Class and function collection for WCF ACP
4 * @author Alexander Ebert, Matthias Schmidt
5 * @copyright 2001-2014 WoltLab GmbH
6 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
10 * Initialize WCF.ACP namespace
15 * Namespace for ACP application management.
17 WCF
.ACP
.Application
= { };
20 * Provides the ability to set an application as primary.
22 * @param integer packageID
24 WCF
.ACP
.Application
.SetAsPrimary
= Class
.extend({
26 * application package id
32 * Initializes the WCF.ACP.Application.SetAsPrimary class.
34 * @param integer packageID
36 init: function(packageID
) {
37 this._packageID
= packageID
;
39 $('#setAsPrimary').click($.proxy(this._click
, this));
43 * Shows a confirmation dialog to set current application as primary.
46 WCF
.System
.Confirmation
.show(WCF
.Language
.get('wcf.acp.application.setAsPrimary.confirmMessage'), $.proxy(function(action
) {
47 if (action
=== 'confirm') {
54 * Sets an application as primary.
56 _setAsPrimary: function() {
57 new WCF
.Action
.Proxy({
60 actionName
: 'setAsPrimary',
61 className
: 'wcf\\data\\application\\ApplicationAction',
62 objectIDs
: [ this._packageID
]
64 success
: $.proxy(function(data
, textStatus
, jqXHR
) {
65 var $notification
= new WCF
.System
.Notification(WCF
.Language
.get('wcf.global.success'));
69 $('#setAsPrimary').parent().remove();
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
);
76 WCF
.DOMNodeInsertedHandler
.execute();
83 * Namespace for ACP cronjob management.
85 WCF
.ACP
.Cronjob
= { };
88 * Handles the manual execution of cronjobs.
90 WCF
.ACP
.Cronjob
.ExecutionHandler
= Class
.extend({
93 * @var WCF.System.Notification
99 * @var WCF.Action.Proxy
104 * Initializes WCF.ACP.Cronjob.ExecutionHandler object.
107 this._proxy
= new WCF
.Action
.Proxy({
108 success
: $.proxy(this._success
, this)
111 $('.jsCronjobRow .jsExecuteButton').click($.proxy(this._click
, this));
113 this._notification
= new WCF
.System
.Notification(WCF
.Language
.get('wcf.global.success'), 'success');
117 * Handles a click on an execute button.
119 * @param object event
121 _click: function(event
) {
122 this._proxy
.setOption('data', {
123 actionName
: 'execute',
124 className
: 'wcf\\data\\cronjob\\CronjobAction',
125 objectIDs
: [ $(event
.target
).data('objectID') ]
128 this._proxy
.sendRequest();
132 * Handles successful cronjob execution.
135 * @param string textStatus
136 * @param jQuery jqXHR
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');
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();
150 this._notification
.show();
159 * Handles the cronjob log list.
161 WCF
.ACP
.Cronjob
.LogList
= Class
.extend({
163 * error message dialog
169 * Initializes WCF.ACP.Cronjob.LogList object.
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({
179 actionName
: 'clearAll',
180 className
: 'wcf\\data\\cronjob\\log\\CronjobLogAction'
182 success: function() {
183 window
.location
.reload();
190 // bind event listeners to error badges
191 $('.jsCronjobError').click($.proxy(this._showError
, this));
195 * Shows certain error message
197 * @param object event
199 _showError: function(event
) {
200 var $errorBadge
= $(event
.currentTarget
);
202 if (this._dialog
=== null) {
203 this._dialog
= $('<div style="overflow: auto"><pre>' + $errorBadge
.next().html() + '</pre></div>').hide().appendTo(document
.body
);
204 this._dialog
.wcfDialog({
205 title
: WCF
.Language
.get('wcf.acp.cronjob.log.error.details')
209 this._dialog
.html('<pre>' + $errorBadge
.next().html() + '</pre>');
210 this._dialog
.wcfDialog('open');
218 * @param array<string> activeMenuItems
220 WCF
.ACP
.Menu
= Class
.extend({
222 * Initializes ACPMenu.
224 * @param array activeMenuItems
226 init: function(activeMenuItems
) {
227 this._headerNavigation
= $('nav#mainMenu');
228 this._sidebarNavigation
= $('aside.collapsibleMenu > div');
230 this._prepareElements(activeMenuItems
);
234 * Resets all elements and binds event listeners.
236 _prepareElements: function(activeMenuItems
) {
237 this._headerNavigation
.find('li').removeClass('active');
239 this._sidebarNavigation
.find('legend').each($.proxy(function(index
, menuHeader
) {
240 $(menuHeader
).click($.proxy(this._toggleItem
, this));
243 // close all navigation groups
244 this._sidebarNavigation
.find('nav ul').each(function() {
248 this._headerNavigation
.find('li').click($.proxy(this._toggleSidebar
, this));
250 if (activeMenuItems
.length
=== 0) {
251 this._renderSidebar(this._headerNavigation
.find('li:first').data('menuItem'), []);
254 this._renderSidebar('', activeMenuItems
);
259 * Toggles a navigation group entry.
261 _toggleItem: function(event
) {
262 var $menuItem
= $(event
.currentTarget
);
264 $menuItem
.parent().find('nav ul').stop(true, true).toggle('blind', { }, 200).end();
265 $menuItem
.toggleClass('active');
269 * Handles clicks on main menu.
271 * @param object event
273 _toggleSidebar: function(event
) {
274 var $target
= $(event
.currentTarget
);
276 if ($target
.hasClass('active')) {
280 this._renderSidebar($target
.data('menuItem'), []);
284 * Renders sidebar including highlighting of currently active menu items.
286 * @param string menuItem
287 * @param array activeMenuItems
289 _renderSidebar: function(menuItem
, activeMenuItems
) {
290 // reset visible and active items
291 this._headerNavigation
.find('li').removeClass('active');
292 this._sidebarNavigation
.find('> div').hide();
294 if (activeMenuItems
.length
=== 0) {
296 this._headerNavigation
.find('li[data-menu-item="' + menuItem
+ '"]').addClass('active');
297 this._sidebarNavigation
.find('div[data-parent-menu-item="' + menuItem
+ '"]').show();
300 // open menu by active menu items, first element is always a head navigation item
301 menuItem
= activeMenuItems
.shift();
303 this._headerNavigation
.find('li[data-menu-item="' + menuItem
+ '"]').addClass('active');
304 this._sidebarNavigation
.find('div[data-parent-menu-item="' + menuItem
+ '"]').show();
306 for (var $i
= 0, $size
= activeMenuItems
.length
; $i
< $size
; $i
++) {
307 var $item
= activeMenuItems
[$i
];
309 if ($.wcfIsset($item
)) {
310 var $menuItem
= $('#' + $.wcfEscapeID($item
));
312 if ($menuItem
.getTagName() === 'ul') {
313 $menuItem
.show().parents('fieldset').children('legend').addClass('active');
316 $menuItem
.addClass('active');
325 * Namespace for ACP package management.
327 WCF
.ACP
.Package
= { };
330 * Provides the package installation.
332 * @param integer queueID
333 * @param string actionName
335 WCF
.ACP
.Package
.Installation
= Class
.extend({
337 * package installation type
340 _actionName
: 'InstallPackage',
343 * true, if rollbacks are supported
346 _allowRollback
: false,
355 * name of the language item with the title of the dialog
362 * @var WCF.Action.Proxy
367 * package installation queue id
373 * true, if dialog should be rendered again
376 _shouldRender
: false,
379 * Initializes the WCF.ACP.Package.Installation class.
381 * @param integer queueID
382 * @param string actionName
383 * @param boolean allowRollback
384 * @param boolean isUpdate
386 init: function(queueID
, actionName
, allowRollback
, isUpdate
) {
387 this._actionName
= (actionName
) ? actionName
: 'InstallPackage';
388 this._allowRollback
= (allowRollback
=== true) ? true : false;
389 this._queueID
= queueID
;
391 switch (this._actionName
) {
392 case 'InstallPackage':
393 this._dialogTitle
= 'wcf.acp.package.' + (isUpdate
? 'update' : 'install') + '.title';
396 case 'UninstallPackage':
397 this._dialogTitle
= 'wcf.acp.package.uninstallation.title';
406 * Initializes the WCF.Action.Proxy object.
408 _initProxy: function() {
409 this._proxy
= new WCF
.Action
.Proxy({
410 failure
: $.proxy(this._failure
, this),
411 showLoadingOverlay
: false,
412 success
: $.proxy(this._success
, this),
413 url
: 'index.php/' + this._actionName
+ '/?t=' + SECURITY_TOKEN
+ SID_ARG_2ND
418 * Initializes the package installation.
421 $('#submitButton').click($.proxy(this.prepareInstallation
, this));
425 * Handles erroneous AJAX requests.
427 _failure: function() {
428 if (this._dialog
!== null) {
429 $('#packageInstallationProgress').removeAttr('value');
430 this._setIcon('remove');
433 if (!this._allowRollback
) {
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));
442 $('#packageInstallationInnerContentContainer').show();
444 this._dialog
.wcfDialog('render');
450 * Performs a rollback.
452 * @param object event
454 _rollback: function(event
) {
455 this._setIcon('spinner');
458 $(event
.currentTarget
).disable();
461 this._executeStep('rollback');
465 * Prepares installation dialog.
467 prepareInstallation: function() {
468 this._proxy
.setOption('data', this._getParameters());
469 this._proxy
.sendRequest();
473 * Returns parameters to prepare installation.
477 _getParameters: function() {
479 queueID
: this._queueID
,
485 * Handles successful AJAX requests.
488 * @param string textStatus
489 * @param jQuery jqXHR
491 _success: function(data
, textStatus
, jqXHR
) {
492 this._shouldRender
= false;
494 if (this._dialog
=== null) {
495 this._dialog
= $('<div id="packageInstallationDialog" />').hide().appendTo(document
.body
);
496 this._dialog
.wcfDialog({
498 title
: WCF
.Language
.get(this._dialogTitle
)
502 this._setIcon('spinner');
504 if (data
.step
== 'rollback') {
505 this._dialog
.wcfDialog('close');
506 this._dialog
.remove();
508 new WCF
.PeriodicalExecuter(function(pe
) {
511 var $uninstallation
= new WCF
.ACP
.Package
.Uninstallation();
512 $uninstallation
.start(data
.packageID
);
518 // receive new queue id
520 this._queueID
= data
.queueID
;
524 if (data
.template
&& !data
.ignoreTemplate
) {
525 this._dialog
.html(data
.template
);
526 this._shouldRender
= true;
531 $('#packageInstallationProgress').attr('value', data
.progress
).text(data
.progress
+ '%');
532 $('#packageInstallationProgressLabel').text(data
.progress
+ '%');
536 if (data
.currentAction
) {
537 $('#packageInstallationAction').html(data
.currentAction
);
541 if (data
.step
=== 'success') {
544 this._purgeTemplateContent($.proxy(function() {
545 var $form
= $('<div class="formSubmit" />').appendTo($('#packageInstallationInnerContent'));
546 var $button
= $('<button class="buttonPrimary">' + WCF
.Language
.get('wcf.global.button.next') + '</button>').appendTo($form
).click(function() {
548 window
.location
= data
.redirectLocation
;
551 $('#packageInstallationInnerContentContainer').show();
553 $(document
).keydown(function(event
) {
554 if (event
.which
=== $.ui
.keyCode
.ENTER
) {
555 $button
.trigger('click');
559 this._dialog
.wcfDialog('render');
565 // handle inner template
566 if (data
.innerTemplate
) {
568 $('#packageInstallationInnerContent').html(data
.innerTemplate
).find('input').keyup(function(event
) {
569 if (event
.keyCode
=== $.ui
.keyCode
.ENTER
) {
574 // create button to handle next step
575 if (data
.step
&& data
.node
) {
576 $('#packageInstallationProgress').removeAttr('value');
577 this._setIcon('question');
579 var $form
= $('<div class="formSubmit" />').appendTo($('#packageInstallationInnerContent'));
580 $('<button class="buttonPrimary">' + WCF
.Language
.get('wcf.global.button.next') + '</button>').appendTo($form
).click($.proxy(function(event
) {
581 $(event
.currentTarget
).disable();
587 $('#packageInstallationInnerContentContainer').show();
589 this._dialog
.wcfDialog('render');
594 this._purgeTemplateContent($.proxy(function() {
596 if (this._shouldRender
) {
597 this._dialog
.wcfDialog('render');
601 if (data
.step
&& data
.node
) {
602 this._executeStep(data
.step
, data
.node
);
608 * Submits the dialog content.
612 _submit: function(data
) {
613 this._setIcon('spinner');
615 // collect form values
616 var $additionalData
= { };
617 $('#packageInstallationInnerContent input').each(function(index
, inputElement
) {
618 var $inputElement
= $(inputElement
);
619 var $type
= $inputElement
.attr('type');
621 if (($type
!= 'checkbox' && $type
!= 'radio') || $inputElement
.prop('checked')) {
622 var $name
= $inputElement
.attr('name');
623 if ($name
.match(/(.*)\[([^[]*)\]$/)) {
627 if ($additionalData
[$name
] === undefined) {
629 $additionalData
[$name
] = { };
632 $additionalData
[$name
] = [ ];
637 $additionalData
[$name
][$key
] = $inputElement
.val();
640 $additionalData
[$name
].push($inputElement
.val());
644 $additionalData
[$name
] = $inputElement
.val();
649 this._executeStep(data
.step
, data
.node
, $additionalData
);
653 * Purges template content.
655 * @param function callback
657 _purgeTemplateContent: function(callback
) {
658 if ($('#packageInstallationInnerContent').children().length
> 1) {
659 $('#packageInstallationInnerContentContainer').wcfBlindOut('vertical', $.proxy(function() {
660 $('#packageInstallationInnerContent').empty();
661 this._shouldRender
= true;
673 * Executes the next installation step.
677 * @param object additionalData
679 _executeStep: function(step
, node
, additionalData
) {
680 if (!additionalData
) additionalData
= { };
682 var $data
= $.extend({
684 queueID
: this._queueID
,
688 this._proxy
.setOption('data', $data
);
689 this._proxy
.sendRequest();
693 * Sets the icon with the given name as the current installation status icon.
695 * @param string iconName
697 _setIcon: function(iconName
) {
698 this._dialog
.find('.jsPackageInstallationStatus').removeClass('icon-ok icon-question icon-remove icon-spinner').addClass('icon-' + iconName
);
703 * Handles canceling the package installation at the package installation
706 WCF
.ACP
.Package
.Installation
.Cancel
= Class
.extend({
708 * Creates a new instance of WCF.ACP.Package.Installation.Cancel.
710 * @param integer queueID
712 init: function(queueID
) {
713 $('#backButton').click(function() {
714 new WCF
.Action
.Proxy({
717 actionName
: 'cancelInstallation',
718 className
: 'wcf\\data\\package\\installation\\queue\\PackageInstallationQueueAction',
719 objectIDs
: [ queueID
]
721 success: function(data
) {
722 window
.location
= data
.returnValues
.url
;
730 * Provides the package uninstallation.
732 * @param jQuery elements
733 * @param string wcfPackageListURL
735 WCF
.ACP
.Package
.Uninstallation
= WCF
.ACP
.Package
.Installation
.extend({
737 * list of uninstallation buttons
749 * URL of WCF package list
752 _wcfPackageListURL
: '',
755 * Initializes the WCF.ACP.Package.Uninstallation class.
757 * @param jQuery elements
758 * @param string wcfPackageListURL
760 init: function(elements
, wcfPackageListURL
) {
761 this._elements
= elements
;
763 this._wcfPackageListURL
= wcfPackageListURL
;
765 if (this._elements
!== undefined && this._elements
.length
) {
766 this._super(0, 'UninstallPackage');
771 * Begins a package uninstallation without user action.
773 * @param integer packageID
775 start: function(packageID
) {
776 this._actionName
= 'UninstallPackage';
777 this._packageID
= packageID
;
779 this._dialogTitle
= 'wcf.acp.package.uninstallation.title';
782 this.prepareInstallation();
786 * @see WCF.ACP.Package.Installation.init()
789 this._elements
.click($.proxy(this._showConfirmationDialog
, this));
793 * Displays a confirmation dialog prior to package uninstallation.
795 * @param object event
797 _showConfirmationDialog: function(event
) {
798 var $element
= $(event
.currentTarget
);
800 if ($element
.data('isApplication') && this._wcfPackageListURL
) {
801 window
.location
= WCF
.String
.unescapeHTML(this._wcfPackageListURL
.replace(/{packageID}/, $element
.data('objectID')));
806 WCF
.System
.Confirmation
.show($element
.data('confirmMessage'), function(action
) {
807 if (action
=== 'confirm') {
808 self
._packageID
= $element
.data('objectID');
809 self
.prepareInstallation();
815 * @see WCF.ACP.Package.Installation._getParameters()
817 _getParameters: function() {
819 packageID
: this._packageID
,
826 * Manages package search.
828 WCF
.ACP
.Package
.Search
= Class
.extend({
836 * list of cached pages
854 * package input field
860 * package description input field
863 _packageDescription
: null,
866 * package name input field
872 * package search result container
875 _packageSearchResultContainer
: null,
878 * package search result list
881 _packageSearchResultList
: null,
897 * @var WCF.Action:proxy
908 * currently selected package
911 _selectedPackage
: '',
914 * currently selected package's version
916 _selectedPackageVersion
: '',
919 * Initializes the WCF.ACP.Package.Seach class.
924 this._container
= $('#packageSearch');
926 this._package
= null;
927 this._packageName
= null;
928 this._packageSearchResultContainer
= $('#packageSearchResultContainer');
929 this._packageSearchResultList
= $('#packageSearchResultList');
932 this._searchDescription
= null;
934 this._selectedPackage
= '';
935 this._selectedPackageVersion
= '';
937 this._proxy
= new WCF
.Action
.Proxy({
938 success
: $.proxy(this._success
, this)
941 this._initElements();
945 * Initializes search elements.
947 _initElements: function() {
948 this._button
= this._container
.find('.formSubmit > button.jsButtonPackageSearch').disable().click($.proxy(this._search
, this));
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));
956 * Handles the 'keyup' event.
958 _keyUp: function(event
) {
959 if (this._package
.val() === '' && this._packageDescription
.val() === '' && this._packageName
.val() === '') {
960 this._button
.disable();
963 this._button
.enable();
966 if (event
.which
=== 13) {
967 this._button
.trigger('click');
973 * Performs a new search.
975 _search: function() {
976 var $values
= this._getSearchValues();
977 if (!this._validate($values
)) {
981 $values
.pageNo
= this._pageNo
;
982 this._proxy
.setOption('data', {
983 actionName
: 'search',
984 className
: 'wcf\\data\\package\\update\\PackageUpdateAction',
987 this._proxy
.sendRequest();
991 * Returns search values.
995 _getSearchValues: function() {
997 'package': $.trim(this._package
.val()),
998 packageDescription
: $.trim(this._packageDescription
.val()),
999 packageName
: $.trim(this._packageName
.val())
1004 * Validates search values.
1006 * @param object values
1009 _validate: function(values
) {
1010 if (values
['package'] === '' && values
['packageDescription'] === '' && values
['packageName'] === '') {
1018 * Handles successful AJAX requests.
1020 * @param object data
1021 * @param string textStatus
1022 * @param jQuery jqXHR
1024 _success: function(data
, textStatus
, jqXHR
) {
1025 switch (data
.actionName
) {
1026 case 'getResultList':
1027 this._insertTemplate(data
.returnValues
.template
);
1030 case 'prepareInstallation':
1031 if (data
.returnValues
.queueID
) {
1032 if (this._dialog
!== null) {
1033 this._dialog
.wcfDialog('close');
1036 var $installation
= new WCF
.ACP
.Package
.Installation(data
.returnValues
.queueID
, undefined, false);
1037 $installation
.prepareInstallation();
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')
1047 this._dialog
.html(data
.returnValues
.template
).wcfDialog('open');
1050 this._dialog
.find('.formSubmit > button').click($.proxy(this._submitAuthentication
, this));
1055 this._pageCount
= data
.returnValues
.pageCount
;
1056 this._searchID
= data
.returnValues
.searchID
;
1058 this._insertTemplate(data
.returnValues
.template
, (data
.returnValues
.count
=== undefined ? undefined : data
.returnValues
.count
));
1059 this._setupPagination();
1065 * Submits authentication data for current update server.
1067 * @param object event
1069 _submitAuthentication: function(event
) {
1070 var $usernameField
= $('#packageUpdateServerUsername');
1071 var $passwordField
= $('#packageUpdateServerPassword');
1073 // remove error messages if any
1074 $usernameField
.next('small.innerError').remove();
1075 $passwordField
.next('small.innerError').remove();
1077 var $continue = true;
1078 if ($.trim($usernameField
.val()) === '') {
1079 $('<small class="innerError">' + WCF
.Language
.get('wcf.global.form.error.empty') + '</small>').insertAfter($usernameField
);
1083 if ($.trim($passwordField
.val()) === '') {
1084 $('<small class="innerError">' + WCF
.Language
.get('wcf.global.form.error.empty') + '</small>').insertAfter($passwordField
);
1089 this._prepareInstallation($(event
.currentTarget
).data('packageUpdateServerID'));
1094 * Inserts search result list template.
1096 * @param string template
1097 * @param integer count
1099 _insertTemplate: function(template
, count
) {
1100 this._packageSearchResultContainer
.show();
1102 this._packageSearchResultList
.html(template
);
1103 if (count
=== undefined) {
1104 this._content
[this._pageNo
] = template
;
1107 // update badge count
1108 if (count
!== undefined) {
1109 this._content
= { 1: template
};
1110 this._packageSearchResultContainer
.find('> header > h2 > .badge').html(count
);
1114 this._packageSearchResultList
.find('.jsInstallPackage').click($.proxy(this._click
, this));
1118 * Prepares a package installation.
1120 * @param object event
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();
1134 * Prepares package installation.
1136 * @param integer packageUpdateServerID
1138 _prepareInstallation: function(packageUpdateServerID
) {
1142 $parameters
['packages'][this._selectedPackage
] = this._selectedPackageVersion
;
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())
1153 this._proxy
.setOption('data', {
1154 actionName
: 'prepareInstallation',
1155 className
: 'wcf\\data\\package\\update\\PackageUpdateAction',
1156 parameters
: $parameters
1158 this._proxy
.sendRequest();
1162 * Setups pagination for current search.
1164 _setupPagination: function() {
1165 // remove previous instances
1166 this._content
= { 1: this._packageSearchResultList
.html() };
1167 this._packageSearchResultContainer
.find('.pageNavigation').wcfPages('destroy').remove();
1169 if (this._pageCount
> 1) {
1170 // TODO: Fix ui.wcfPages to properly synchronize multiple instances without triggering events
1171 /*$('<div class="contentNavigation" />').insertBefore(this._packageSearchResultList).wcfPages({
1172 activePage: this._pageNo,
1173 maxPage: this._pageCount
1174 }).on('wcfpagesswitched', $.proxy(this._showPage, this));*/
1176 $('<div class="contentNavigation" />').insertAfter(this._packageSearchResultList
).wcfPages({
1177 activePage
: this._pageNo
,
1178 maxPage
: this._pageCount
1179 }).on('wcfpagesswitched', $.proxy(this._showPage
, this));
1184 * Displays requested pages or loads it.
1186 * @param object event
1187 * @param object data
1189 _showPage: function(event
, data
) {
1190 if (data
&& data
.activePage
) {
1191 this._pageNo
= data
.activePage
;
1195 if (this._pageNo
< 1 || this._pageNo
> this._pageCount
) {
1196 console
.debug("[WCF.ACP.Package.Search] Cannot access page " + this._pageNo
+ " of " + this._pageCount
);
1201 if (this._content
[this._pageNo
] === undefined) {
1202 this._proxy
.setOption('data', {
1203 actionName
: 'getResultList',
1204 className
: 'wcf\\data\\package\\update\\PackageUpdateAction',
1206 pageNo
: this._pageNo
,
1207 searchID
: this._searchID
1210 this._proxy
.sendRequest();
1213 // show cached content
1214 this._packageSearchResultList
.html(this._content
[this._pageNo
]);
1216 WCF
.DOMNodeInsertedHandler
.execute();
1221 WCF
.ACP
.Package
.Server
= { };
1223 WCF
.ACP
.Package
.Server
.Installation
= Class
.extend({
1225 _selectedPackage
: '',
1228 this._dialog
= null;
1229 this._selectedPackage
= null;
1231 this._proxy
= new WCF
.Action
.Proxy({
1232 success
: $.proxy(this._success
, this)
1237 $('.jsButtonPackageInstall').removeClass('jsButtonPackageInstall').click($.proxy(this._click
, this));
1241 * Prepares a package installation.
1243 * @param object event
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();
1257 * Handles successful AJAX requests.
1259 * @param object data
1261 _success: function(data
) {
1262 if (data
.returnValues
.queueID
) {
1263 if (this._dialog
!== null) {
1264 this._dialog
.wcfDialog('close');
1267 var $installation
= new WCF
.ACP
.Package
.Installation(data
.returnValues
.queueID
, undefined, false);
1268 $installation
.prepareInstallation();
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')
1278 this._dialog
.html(data
.returnValues
.template
).wcfDialog('open');
1281 this._dialog
.find('.formSubmit > button').click($.proxy(this._submitAuthentication
, this));
1286 * Submits authentication data for current update server.
1288 * @param object event
1290 _submitAuthentication: function(event
) {
1291 var $usernameField
= $('#packageUpdateServerUsername');
1292 var $passwordField
= $('#packageUpdateServerPassword');
1294 // remove error messages if any
1295 $usernameField
.next('small.innerError').remove();
1296 $passwordField
.next('small.innerError').remove();
1298 var $continue = true;
1299 if ($.trim($usernameField
.val()) === '') {
1300 $('<small class="innerError">' + WCF
.Language
.get('wcf.global.form.error.empty') + '</small>').insertAfter($usernameField
);
1304 if ($.trim($passwordField
.val()) === '') {
1305 $('<small class="innerError">' + WCF
.Language
.get('wcf.global.form.error.empty') + '</small>').insertAfter($passwordField
);
1310 this._prepareInstallation($(event
.currentTarget
).data('packageUpdateServerID'));
1315 * Prepares package installation.
1317 * @param integer packageUpdateServerID
1319 _prepareInstallation: function(packageUpdateServerID
) {
1323 $parameters
['packages'][this._selectedPackage
] = this._selectedPackageVersion
;
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())
1334 this._proxy
.setOption('data', {
1335 actionName
: 'prepareInstallation',
1336 className
: 'wcf\\data\\package\\update\\PackageUpdateAction',
1337 parameters
: $parameters
1339 this._proxy
.sendRequest();
1344 * Namespace for package update related classes.
1346 WCF
.ACP
.Package
.Update
= { };
1349 * Handles the package update process.
1351 WCF
.ACP
.Package
.Update
.Manager
= Class
.extend({
1360 * @var WCF.Action.Proxy
1368 _submitButton
: null,
1371 * Initializes the WCF.ACP.Package.Update.Manager class.
1374 this._dialog
= null;
1375 this._submitButton
= $('.formSubmit > button').click($.proxy(this._click
, this));
1377 this._proxy
= new WCF
.Action
.Proxy({
1378 success
: $.proxy(this._success
, this)
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));
1388 * Handles toggles for a specific update.
1390 _change: function(event
) {
1391 var $checkbox
= $(event
.currentTarget
);
1393 if ($checkbox
.is(':checked')) {
1394 $checkbox
.data('packageUpdate').find('select').enable();
1395 $checkbox
.data('packageUpdate').find('dl').removeClass('disabled');
1397 this._submitButton
.enable();
1400 $checkbox
.data('packageUpdate').find('select').disable();
1401 $checkbox
.data('packageUpdate').find('dl').addClass('disabled');
1403 // disable submit button
1404 if (!$('input[type=checkbox]:checked').length
) {
1405 this._submitButton
.disable();
1411 * Handles clicks on the submit button.
1413 * @param object event
1414 * @param integer packageUpdateServerID
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();
1425 if ($.getLength($packages
)) {
1426 this._submitButton
.disable();
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())
1440 this._proxy
.setOption('data', {
1441 actionName
: 'prepareUpdate',
1442 className
: 'wcf\\data\\package\\update\\PackageUpdateAction',
1443 parameters
: $parameters
,
1445 this._proxy
.sendRequest();
1450 * Handles successful AJAX requests.
1452 * @param object data
1453 * @param string textStatus
1454 * @param jQuery jqXHR
1456 _success: function(data
, textStatus
, jqXHR
) {
1457 if (data
.returnValues
.queueID
) {
1458 if (this._dialog
!== null) {
1459 this._dialog
.wcfDialog('close');
1462 var $installation
= new WCF
.ACP
.Package
.Installation(data
.returnValues
.queueID
, undefined, false, true);
1463 $installation
.prepareInstallation();
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')
1473 this._dialog
.html(data
.returnValues
.template
).wcfDialog('open');
1476 this._dialog
.find('.formSubmit > button').click($.proxy(this._submitAuthentication
, this));
1481 * Submits authentication data for current update server.
1483 * @param object event
1485 _submitAuthentication: function(event
) {
1486 var $usernameField
= $('#packageUpdateServerUsername');
1487 var $passwordField
= $('#packageUpdateServerPassword');
1489 // remove error messages if any
1490 $usernameField
.next('small.innerError').remove();
1491 $passwordField
.next('small.innerError').remove();
1493 var $continue = true;
1494 if ($.trim($usernameField
.val()) === '') {
1495 $('<small class="innerError">' + WCF
.Language
.get('wcf.global.form.error.empty') + '</small>').insertAfter($usernameField
);
1499 if ($.trim($passwordField
.val()) === '') {
1500 $('<small class="innerError">' + WCF
.Language
.get('wcf.global.form.error.empty') + '</small>').insertAfter($passwordField
);
1505 this._click(undefined, $(event
.currentTarget
).data('packageUpdateServerID'));
1511 * Searches for available updates.
1513 * @param boolean bindOnExistingButtons
1515 WCF
.ACP
.Package
.Update
.Search
= Class
.extend({
1523 * Initializes the WCF.ACP.Package.SearchForUpdates class.
1525 * @param boolean bindOnExistingButtons
1527 init: function(bindOnExistingButtons
) {
1528 this._dialog
= null;
1530 if (bindOnExistingButtons
=== true) {
1531 $('.jsButtonPackageUpdate').click($.proxy(this._click
, this));
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'));
1540 * Handles clicks on the search button.
1542 _click: function() {
1543 if (this._dialog
=== null) {
1544 new WCF
.Action
.Proxy({
1547 actionName
: 'searchForUpdates',
1548 className
: 'wcf\\data\\package\\update\\PackageUpdateAction',
1553 success
: $.proxy(this._success
, this)
1557 this._dialog
.wcfDialog('open');
1562 * Handles successful AJAX requests.
1564 * @param object data
1565 * @param string textStatus
1566 * @param jQuery jqXHR
1568 _success: function(data
, textStatus
, jqXHR
) {
1569 if (data
.returnValues
.url
) {
1570 window
.location
= data
.returnValues
.url
;
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')
1582 * Namespace for classes related to the WoltLab Plugin-Store.
1584 WCF
.ACP
.PluginStore
= { };
1587 * Namespace for classes handling items purchased in the WoltLab Plugin-Store.
1589 WCF
.ACP
.PluginStore
.PurchasedItems
= { };
1592 * Searches for purchased items available for install but not yet installed.
1594 WCF
.ACP
.PluginStore
.PurchasedItems
.Search
= Class
.extend({
1603 * @var WCF.Action.Proxy
1608 * Initializes the WCF.ACP.PluginStore.PurchasedItems.Search class.
1611 this._dialog
= null;
1612 this._proxy
= new WCF
.Action
.Proxy({
1613 success
: $.proxy(this._success
, this)
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>');
1617 $button
.prependTo($('.contentNavigation:eq(0) > nav > ul')).click($.proxy(this._click
, this));
1621 * Handles clicks on the search button.
1623 _click: function() {
1624 this._proxy
.setOption('data', {
1625 actionName
: 'searchForPurchasedItems',
1626 className
: 'wcf\\data\\package\\PackageAction'
1628 this._proxy
.sendRequest();
1632 * Handles successful AJAX requests.
1634 * @param object data
1635 * @param string textStatus
1636 * @param jQuery jqXHR
1638 _success: function(data
, textStatus
, jqXHR
) {
1639 // prompt for credentials
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({
1644 title
: WCF
.Language
.get('wcf.acp.pluginStore.authorization')
1648 this._dialog
.html(data
.returnValues
.template
);
1649 this._dialog
.wcfDialog('open');
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');
1660 else if (data
.returnValues
.noResults
) {
1661 // there are no purchased products yet
1662 this._dialog
.wcfDialog('option', 'title', WCF
.Language
.get('wcf.acp.pluginStore.purchasedItems'));
1663 this._dialog
.html(data
.returnValues
.noResults
);
1664 this._dialog
.wcfDialog('open');
1666 else if (data
.returnValues
.redirectURL
) {
1667 // redirect to list of purchased products
1668 window
.location
= data
.returnValues
.redirectURL
;
1673 * Submits the user credentials.
1675 _submit: function() {
1676 this._dialog
.wcfDialog('close');
1678 this._proxy
.setOption('data', {
1679 actionName
: 'searchForPurchasedItems',
1680 className
: 'wcf\\data\\package\\PackageAction',
1682 password
: $('#pluginStorePassword').val(),
1683 username
: $('#pluginStoreUsername').val()
1686 this._proxy
.sendRequest();
1691 * Handles option selection.
1693 WCF
.ACP
.Options
= Class
.extend({
1695 * Initializes options.
1698 $('.jsEnablesOptions').each($.proxy(this._initOption
, this));
1702 * Initializes an option.
1704 * @param integer index
1705 * @param object option
1707 _initOption: function(index
, option
) {
1708 // execute action on init
1709 this._change(option
);
1711 // bind event listener
1712 $(option
).change($.proxy(this._handleChange
, this));
1716 * Applies whenever an option is changed.
1718 * @param object event
1720 _handleChange: function(event
) {
1721 this._change($(event
.target
));
1725 * Enables or disables options on option value change.
1727 * @param object option
1729 _change: function(option
) {
1732 var $disableOptions
= eval(option
.data('disableOptions'));
1733 var $enableOptions
= eval(option
.data('enableOptions'));
1735 // determine action by type
1736 switch(option
.getTagName()) {
1738 switch(option
.attr('type')) {
1740 this._execute(option
.prop('checked'), $disableOptions
, $enableOptions
);
1744 if (option
.prop('checked')) {
1745 this._execute(true, $disableOptions
, $enableOptions
);
1752 var $value
= option
.val();
1753 var $disableOptions
= $enableOptions
= [];
1755 if (option
.data('disableOptions').length
> 0) {
1756 for (var $index
in option
.data('disableOptions')) {
1757 var $item
= option
.data('disableOptions')[$index
];
1759 if ($item
.value
== $value
) {
1760 $disableOptions
.push($item
.option
);
1765 if (option
.data('enableOptions').length
> 0) {
1766 for (var $index
in option
.data('enableOptions')) {
1767 var $item
= option
.data('enableOptions')[$index
];
1769 if ($item
.value
== $value
) {
1770 $enableOptions
.push($item
.option
);
1775 this._execute(true, $disableOptions
, $enableOptions
);
1781 * Enables or disables options.
1783 * @param boolean isActive
1784 * @param array disableOptions
1785 * @param array enableOptions
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
)) {
1792 this._enableOption($target
, !isActive
);
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
)) {
1801 this._enableOption($target
, isActive
);
1808 * Enables an option.
1810 * @param string target
1811 * @param boolean enable
1813 _enableOption: function(target
, enable
) {
1814 var $targetElement
= $('#' + $.wcfEscapeID(target
));
1815 var $tagName
= $targetElement
.getTagName();
1817 if ($tagName
== 'select' || ($tagName
== 'input' && ($targetElement
.attr('type') == 'checkbox' || $targetElement
.attr('type') == 'radio'))) {
1818 if (enable
) $targetElement
.enable();
1819 else $targetElement
.disable();
1822 if (enable
) $targetElement
.removeAttr('readonly');
1823 else $targetElement
.attr('readonly', true);
1827 $targetElement
.closest('dl').removeClass('disabled');
1830 $targetElement
.closest('dl').addClass('disabled');
1836 * Worker support for ACP.
1838 * @param string dialogID
1839 * @param string className
1840 * @param string title
1841 * @param object parameters
1842 * @param object callback
1844 WCF
.ACP
.Worker
= Class
.extend({
1852 * callback invoked after worker completed
1871 * @var WCF.Action.Proxy
1882 * Initializes a new worker instance.
1884 * @param string dialogID
1885 * @param string className
1886 * @param string title
1887 * @param object parameters
1888 * @param object callback
1889 * @param object confirmMessage
1891 init: function(dialogID
, className
, title
, parameters
, callback
) {
1892 this._aborted
= false;
1893 this._callback
= callback
|| null;
1894 this._dialogID
= dialogID
+ 'Worker';
1895 this._dialog
= null;
1896 this._proxy
= new WCF
.Action
.Proxy({
1899 className
: className
,
1900 parameters
: parameters
|| { }
1902 showLoadingOverlay
: false,
1903 success
: $.proxy(this._success
, this),
1904 url
: 'index.php/WorkerProxy/?t=' + SECURITY_TOKEN
+ SID_ARG_2ND
1906 this._title
= title
;
1910 * Handles response from server.
1912 * @param object data
1914 _success: function(data
) {
1916 if (this._dialog
=== null) {
1917 this._dialog
= $('<div id="' + this._dialogID
+ '" />').hide().appendTo(document
.body
);
1918 this._dialog
.wcfDialog({
1919 closeConfirmMessage
: WCF
.Language
.get('wcf.acp.worker.abort.confirmMessage'),
1920 closeViaModal
: false,
1921 onClose
: $.proxy(function() {
1922 this._aborted
= true;
1923 this._proxy
.abortPrevious();
1925 window
.location
.reload();
1931 if (this._aborted
) {
1935 if (data
.template
) {
1936 this._dialog
.html(data
.template
);
1940 this._dialog
.find('progress').attr('value', data
.progress
).text(data
.progress
+ '%').next('span').text(data
.progress
+ '%');
1942 // worker is still busy with its business, carry on
1943 if (data
.progress
< 100) {
1944 // send request for next loop
1945 this._proxy
.setOption('data', {
1946 className
: data
.className
,
1947 loopCount
: data
.loopCount
,
1948 parameters
: data
.parameters
1950 this._proxy
.sendRequest();
1952 else if (this._callback
!== null) {
1953 this._callback(this, data
);
1956 // display continue button
1957 var $formSubmit
= $('<div class="formSubmit" />').appendTo(this._dialog
);
1958 $('<button class="buttonPrimary">' + WCF
.Language
.get('wcf.global.button.next') + '</button>').appendTo($formSubmit
).focus().click(function() { window
.location
= data
.proceedURL
; });
1960 this._dialog
.wcfDialog('render');
1966 * Namespace for category-related functions.
1968 WCF
.ACP
.Category
= { };
1971 * Handles collapsing categories.
1973 * @param string className
1974 * @param integer objectTypeID
1976 WCF
.ACP
.Category
.Collapsible
= WCF
.Collapsible
.SimpleRemote
.extend({
1978 * @see WCF.Collapsible.Remote.init()
1980 init: function(className
) {
1981 var sortButton
= $('.formSubmit > button[data-type="submit"]');
1983 sortButton
.click($.proxy(this._sort
, this));
1986 this._super(className
);
1990 * @see WCF.Collapsible.Remote._getButtonContainer()
1992 _getButtonContainer: function(containerID
) {
1993 return $('#' + containerID
+ ' > .buttons');
1997 * @see WCF.Collapsible.Remote._getContainers()
1999 _getContainers: function() {
2000 return $('.jsCategory').has('ol').has('li');
2004 * @see WCF.Collapsible.Remote._getTarget()
2006 _getTarget: function(containerID
) {
2007 return $('#' + containerID
+ ' > ol');
2011 * Handles a click on the sort button.
2014 // remove existing collapsible buttons
2015 $('.collapsibleButton').remove();
2017 // reinit containers
2018 this._containers
= { };
2019 this._containerData
= { };
2021 var $containers
= this._getContainers();
2022 if ($containers
.length
== 0) {
2023 console
.debug('[WCF.ACP.Category.Collapsible] Empty container set given, aborting.');
2025 $containers
.each($.proxy(function(index
, container
) {
2026 var $container
= $(container
);
2027 var $containerID
= $container
.wcfIdentify();
2028 this._containers
[$containerID
] = $container
;
2030 this._initContainer($containerID
);
2036 * Provides the search dropdown for ACP
2038 * @see WCF.Search.Base
2040 WCF
.ACP
.Search
= WCF
.Search
.Base
.extend({
2042 * @see WCF.Search.Base.init()
2045 this._className
= 'wcf\\data\\acp\\search\\provider\\ACPSearchProviderAction';
2046 this._super('#search input[name=q]');
2048 // disable form submitting
2049 $('#search > form').on('submit', function(event
) {
2050 event
.preventDefault();
2055 * @see WCF.Search.Base._createListItem()
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
);
2064 $('<li class="dropdownText">' + resultList
.title
+ '</li>').appendTo(this._list
);
2067 for (var $i
in resultList
.items
) {
2068 var $item
= resultList
.items
[$i
];
2070 $('<li><a href="' + $item
.link
+ '">' + WCF
.String
.escapeHTML($item
.title
) + '</a></li>').appendTo(this._list
);
2077 * @see WCF.Search.Base._handleEmptyResult()
2079 _handleEmptyResult: function() {
2080 $('<li class="dropdownText">' + WCF
.Language
.get('wcf.acp.search.noResults') + '</li>').appendTo(this._list
);
2086 * @see WCF.Search.Base._highlightSelectedElement()
2088 _highlightSelectedElement: function() {
2089 this._list
.find('li').removeClass('dropdownNavigationItem');
2090 this._list
.find('li:not(.dropdownDivider):not(.dropdownText)').eq(this._itemIndex
).addClass('dropdownNavigationItem');
2094 * @see WCF.Search.Base._selectElement()
2096 _selectElement: function(event
) {
2097 if (this._itemIndex
=== -1) {
2101 window
.location
= this._list
.find('li.dropdownNavigationItem > a').attr('href');
2106 * Namespace for user management.
2111 * Generic implementation to ban users.
2113 WCF
.ACP
.User
.BanHandler
= {
2128 * @var WCF.Action.Proxy
2133 * Initializes WCF.ACP.User.BanHandler on first use.
2136 this._dialog
= $('<div />').hide().appendTo(document
.body
);
2137 this._proxy
= new WCF
.Action
.Proxy({
2138 success
: $.proxy(this._success
, this)
2141 $('.jsBanButton').click($.proxy(function(event
) {
2142 var $button
= $(event
.currentTarget
);
2143 if ($button
.data('banned')) {
2144 this.unban([ $button
.data('objectID') ]);
2147 this.ban([ $button
.data('objectID') ]);
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));
2163 * Handles clipboard actions.
2165 * @param object event
2166 * @param string type
2167 * @param string actionName
2168 * @param object parameters
2170 _execute: function(event
, type
, actionName
, parameters
) {
2171 if (actionName
== 'com.woltlab.wcf.user.ban') {
2172 this.ban(parameters
.objectIDs
);
2179 * @param array<integer> userIDs
2181 unban: function(userIDs
) {
2182 this._proxy
.setOption('data', {
2183 actionName
: 'unban',
2184 className
: 'wcf\\data\\user\\UserAction',
2187 this._proxy
.sendRequest();
2193 * @param array<integer> userIDs
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', {
2200 className
: 'wcf\\data\\user\\UserAction',
2203 banReason
: $('#userBanReason').val()
2206 this._proxy
.sendRequest();
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>'));
2212 * Handles successful AJAX calls.
2214 * @param object data
2215 * @param string textStatus
2216 * @param jQuery jqXHR
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');
2226 $button
.data('banned', true).data('tooltip', $button
.data('unbanMessage')).removeClass('icon-unlock').addClass('icon-lock');
2231 var $notification
= new WCF
.System
.Notification();
2232 $notification
.show();
2234 WCF
.Clipboard
.reload();
2239 * Generic implementation to enable users.
2241 WCF
.ACP
.User
.EnableHandler
= {
2244 * @var WCF.Action.Proxy
2249 * Initializes WCF.ACP.User.EnableHandler on first use.
2252 this._proxy
= new WCF
.Action
.Proxy({
2253 success
: $.proxy(this._success
, this)
2256 $('.jsEnableButton').click($.proxy(function(event
) {
2257 var $button
= $(event
.currentTarget
);
2258 if ($button
.data('enabled')) {
2259 this.disable([ $button
.data('objectID') ]);
2262 this.enable([ $button
.data('objectID') ]);
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));
2278 * Handles clipboard actions.
2280 * @param object event
2281 * @param string type
2282 * @param string actionName
2283 * @param object parameters
2285 _execute: function(event
, type
, actionName
, parameters
) {
2286 if (actionName
== 'com.woltlab.wcf.user.enable') {
2287 this.enable(parameters
.objectIDs
);
2294 * @param array<integer> userIDs
2296 disable: function(userIDs
) {
2297 this._proxy
.setOption('data', {
2298 actionName
: 'disable',
2299 className
: 'wcf\\data\\user\\UserAction',
2302 this._proxy
.sendRequest();
2308 * @param array<integer> userIDs
2310 enable: function(userIDs
) {
2311 this._proxy
.setOption('data', {
2312 actionName
: 'enable',
2313 className
: 'wcf\\data\\user\\UserAction',
2316 this._proxy
.sendRequest();
2320 * Handles successful AJAX calls.
2322 * @param object data
2323 * @param string textStatus
2324 * @param jQuery jqXHR
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') {
2331 $button
.data('enabled', false).data('tooltip', $button
.data('enableMessage')).removeClass('icon-check').addClass('icon-check-empty');
2334 $button
.data('enabled', true).data('tooltip', $button
.data('disableMessage')).removeClass('icon-check-empty').addClass('icon-check');
2339 var $notification
= new WCF
.System
.Notification();
2340 $notification
.show(function() { window
.location
.reload(); });
2345 * Handles the send new password clipboard action.
2347 WCF
.ACP
.User
.SendNewPasswordHandler
= {
2350 * @var WCF.Action.Proxy
2355 * Initializes WCF.ACP.User.SendNewPasswordHandler on first use.
2358 this._proxy
= new WCF
.Action
.Proxy({
2359 success
: $.proxy(this._success
, this)
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));
2374 * Handles clipboard actions.
2376 * @param object event
2377 * @param string type
2378 * @param string actionName
2379 * @param object parameters
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
2395 * Namespace for import-related classes.
2397 WCF
.ACP
.Import
= { };
2402 * @param array<string> objectTypes
2404 WCF
.ACP
.Import
.Manager
= Class
.extend({
2418 * current object type index
2424 * list of object types
2425 * @var array<string>
2431 * @var WCF.Action.Proxy
2442 * Initializes the WCF.ACP.Importer object.
2444 * @param array<string> objectTypes
2445 * @param string redirectURL
2447 init: function(objectTypes
, redirectURL
) {
2448 this._currentAction
= '';
2450 this._objectTypes
= objectTypes
;
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
2456 this._redirectURL
= redirectURL
;
2462 * Invokes importing of an object type.
2464 _invoke: function() {
2466 if (this._index
>= this._objectTypes
.length
) {
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'));
2470 var $form
= $('<div class="formSubmit" />').appendTo(this._dialog
.find('#workerContainer'));
2471 $('<button>' + WCF
.Language
.get('wcf.global.button.next') + '</button>').click($.proxy(function() {
2472 new WCF
.Action
.Proxy({
2478 success
: $.proxy(function() {
2479 window
.location
= this._redirectURL
;
2481 url
: 'index.php/CacheClear/?t=' + SECURITY_TOKEN
+ SID_ARG_2ND
2483 }, this)).appendTo($form
);
2485 this._dialog
.wcfDialog('render');
2489 WCF
.Language
.get('wcf.acp.dataImport.data.' + this._objectTypes
[this._index
]),
2490 this._objectTypes
[this._index
]
2496 * Executes import of given object type.
2498 * @param string currentAction
2499 * @param string objectType
2501 _run: function(currentAction
, objectType
) {
2502 this._currentAction
= currentAction
;
2503 this._proxy
.setOption('data', {
2504 className
: 'wcf\\system\\worker\\ImportWorker',
2506 objectType
: objectType
2509 this._proxy
.sendRequest();
2513 * Handles response from server.
2515 * @param object data
2517 _success: function(data
) {
2519 if (this._dialog
=== null) {
2520 this._dialog
= $('<div />').hide().appendTo(document
.body
);
2521 this._dialog
.wcfDialog({
2523 title
: WCF
.Language
.get('wcf.acp.dataImport')
2527 if (data
.template
) {
2528 this._dialog
.html(data
.template
);
2531 if (this._currentAction
) {
2532 this._dialog
.find('h1').text(this._currentAction
);
2536 this._dialog
.find('progress').attr('value', data
.progress
).text(data
.progress
+ '%').next('span').text(data
.progress
+ '%');
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
2546 this._proxy
.sendRequest();
2555 * Namespace for stat-related classes.
2560 * Shows the daily stat chart.
2562 WCF
.ACP
.Stat
.Chart
= Class
.extend({
2564 this._proxy
= new WCF
.Action
.Proxy({
2565 success
: $.proxy(this._success
, this)
2568 $('#statRefreshButton').click($.proxy(this._refresh
, this));
2573 _refresh: function() {
2574 var $objectTypeIDs
= [ ];
2575 $('input[name=objectTypeID]:checked').each(function() {
2576 $objectTypeIDs
.push($(this).val());
2579 if (!$objectTypeIDs
.length
) return;
2581 this._proxy
.setOption('data', {
2582 className
: 'wcf\\data\\stat\\daily\\StatDailyAction',
2583 actionName
: 'getData',
2585 startDate
: $('#startDateDatePicker').val(),
2586 endDate
: $('#endDateDatePicker').val(),
2587 value
: $('input[name=value]:checked').val(),
2588 dateGrouping
: $('input[name=dateGrouping]:checked').val(),
2589 objectTypeIDs
: $objectTypeIDs
2592 this._proxy
.sendRequest();
2595 _success: function(data
) {
2596 switch ($('input[name=dateGrouping]:checked').val()) {
2598 var $minTickSize
= [1, "year"];
2599 var $timeFormat
= WCF
.Language
.get('wcf.acp.stat.timeFormat.yearly');
2602 var $minTickSize
= [1, "month"];
2603 var $timeFormat
= WCF
.Language
.get('wcf.acp.stat.timeFormat.monthly');
2606 var $minTickSize
= [7, "day"];
2607 var $timeFormat
= WCF
.Language
.get('wcf.acp.stat.timeFormat.weekly');
2610 var $minTickSize
= [1, "day"];
2611 var $timeFormat
= WCF
.Language
.get('wcf.acp.stat.timeFormat.daily');
2628 minTickSize
: $minTickSize
,
2629 timeformat
: $timeFormat
,
2630 monthNames
: WCF
.Language
.get('__monthsShort')
2635 tickFormatter: function(val
) {
2636 return WCF
.String
.addThousandsSeparator(val
);
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;
2651 $.plot("#chart", $data
, options
);
2653 $("#chart").on("plothover", function(event
, pos
, item
) {
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();
2658 $("#chartTooltip").hide();