Merge pull request #5987 from WoltLab/acp-dahsboard-box-hight
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / js / WCF.User.js
CommitLineData
e368b357
AE
1"use strict";
2
320f4a6d
MW
3/**
4 * User-related classes.
5 *
6 * @author Alexander Ebert
7b7b9764 7 * @copyright 2001-2019 WoltLab GmbH
320f4a6d
MW
8 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
9 */
10
320f4a6d
MW
11/**
12 * UserProfile namespace
13 */
14WCF.User.Profile = {};
15
16/**
17 * Shows the activity point list for users.
18 */
19WCF.User.Profile.ActivityPointList = {
20 /**
21 * list of cached templates
22 * @var object
23 */
24 _cache: { },
25
26 /**
27 * dialog overlay
28 * @var jQuery
29 */
30 _dialog: null,
31
32 /**
33 * initialization state
34 * @var boolean
35 */
36 _didInit: false,
37
38 /**
39 * action proxy
40 * @var WCF.Action.Proxy
41 */
42 _proxy: null,
43
44 /**
45 * Initializes the WCF.User.Profile.ActivityPointList class.
46 */
47 init: function() {
48 if (this._didInit) {
49 return;
50 }
51
52 this._cache = { };
53 this._dialog = null;
54 this._proxy = new WCF.Action.Proxy({
55 success: $.proxy(this._success, this)
56 });
57
58 this._init();
59
60 WCF.DOMNodeInsertedHandler.addCallback('WCF.User.Profile.ActivityPointList', $.proxy(this._init, this));
61
62 this._didInit = true;
63 },
64
65 /**
66 * Initializes display for activity points.
67 */
68 _init: function() {
69 $('.activityPointsDisplay').removeClass('activityPointsDisplay').click($.proxy(this._click, this));
70 },
71
72 /**
73 * Shows or loads the activity point for selected user.
74 *
75 * @param object event
76 */
77 _click: function(event) {
44accb08 78 event.preventDefault();
320f4a6d
MW
79 var $userID = $(event.currentTarget).data('userID');
80
81 if (this._cache[$userID] === undefined) {
82 this._proxy.setOption('data', {
83 actionName: 'getDetailedActivityPointList',
84 className: 'wcf\\data\\user\\UserProfileAction',
85 objectIDs: [ $userID ]
86 });
87 this._proxy.sendRequest();
88 }
89 else {
90 this._show($userID);
91 }
92 },
93
94 /**
95 * Displays activity points for given user.
96 *
97 * @param integer userID
98 */
99 _show: function(userID) {
100 if (this._dialog === null) {
101 this._dialog = $('<div>' + this._cache[userID] + '</div>').hide().appendTo(document.body);
102 this._dialog.wcfDialog({
103 title: WCF.Language.get('wcf.user.activityPoint')
104 });
105 }
106 else {
107 this._dialog.html(this._cache[userID]);
108 this._dialog.wcfDialog('open');
109 }
110 },
111
112 /**
113 * Handles successful AJAX requests.
114 *
115 * @param object data
116 * @param string textStatus
117 * @param jQuery jqXHR
118 */
119 _success: function(data, textStatus, jqXHR) {
120 this._cache[data.returnValues.userID] = data.returnValues.template;
121 this._show(data.returnValues.userID);
122 }
123};
124
320f4a6d
MW
125/**
126 * Provides methods to load tab menu content upon request.
127 */
128WCF.User.Profile.TabMenu = Class.extend({
129 /**
130 * list of containers
131 * @var object
132 */
133 _hasContent: { },
134
135 /**
136 * profile content
137 * @var jQuery
138 */
139 _profileContent: null,
140
141 /**
142 * action proxy
143 * @var WCF.Action.Proxy
144 */
145 _proxy: null,
146
147 /**
148 * target user id
149 * @var integer
150 */
151 _userID: 0,
152
153 /**
154 * Initializes the tab menu loader.
155 *
156 * @param integer userID
157 */
158 init: function(userID) {
159 this._profileContent = $('#profileContent');
160 this._userID = userID;
161
162 var $activeMenuItem = this._profileContent.data('active');
163 var $enableProxy = false;
164
165 // fetch content state
166 this._profileContent.find('div.tabMenuContent').each($.proxy(function(index, container) {
167 var $containerID = $(container).wcfIdentify();
168
169 if ($activeMenuItem === $containerID) {
170 this._hasContent[$containerID] = true;
171 }
172 else {
173 this._hasContent[$containerID] = false;
174 $enableProxy = true;
175 }
176 }, this));
177
178 // enable loader if at least one container is empty
179 if ($enableProxy) {
180 this._proxy = new WCF.Action.Proxy({
181 success: $.proxy(this._success, this)
182 });
183
8bb37fef
AE
184 this._profileContent.on('wcftabsbeforeactivate', $.proxy(this._loadContent, this));
185
186 // check which tab is selected
187 this._profileContent.find('> nav.tabMenu > ul > li').each($.proxy(function(index, listItem) {
188 var $listItem = $(listItem);
189 if ($listItem.hasClass('ui-state-active')) {
190 if (index) {
191 this._loadContent(null, {
192 newPanel: $('#' + $listItem.attr('aria-controls'))
193 });
194 }
195
196 return false;
197 }
198 }, this));
320f4a6d 199 }
75988196 200
08784699 201 $('.userProfileUser .contentDescription a[href$="#likes"]').click((function (event) {
75988196
AE
202 event.preventDefault();
203
204 require(['Ui/TabMenu'], function (UiTabMenu) {
205 UiTabMenu.getTabMenu('profileContent').select('likes');
206 })
207 }).bind(this))
320f4a6d
MW
208 },
209
210 /**
211 * Prepares to load content once tabs are being switched.
212 *
213 * @param object event
214 * @param object ui
215 */
216 _loadContent: function(event, ui) {
217 var $panel = $(ui.newPanel);
218 var $containerID = $panel.attr('id');
219
220 if (!this._hasContent[$containerID]) {
221 this._proxy.setOption('data', {
222 actionName: 'getContent',
223 className: 'wcf\\data\\user\\profile\\menu\\item\\UserProfileMenuItemAction',
224 parameters: {
225 data: {
226 containerID: $containerID,
227 menuItem: $panel.data('menuItem'),
228 userID: this._userID
229 }
230 }
231 });
232 this._proxy.sendRequest();
233 }
234 },
235
236 /**
237 * Shows previously requested content.
238 *
239 * @param object data
240 * @param string textStatus
241 * @param jQuery jqXHR
242 */
243 _success: function(data, textStatus, jqXHR) {
244 var $containerID = data.returnValues.containerID;
245 this._hasContent[$containerID] = true;
246
99c920fc
AE
247 // insert content, uses non jQuery because DomUtil.insertHtml() moves <script> elements
248 // to the bottom of the element by default which is exactly what is required here
cac7556d 249 require(['Dom/ChangeListener', 'Dom/Util'], function(DomChangeListener, DomUtil) {
99c920fc 250 DomUtil.insertHtml(data.returnValues.template, elById($containerID), 'append');
cac7556d
MS
251
252 DomChangeListener.trigger();
99c920fc 253 });
320f4a6d
MW
254 }
255});
256
f5e3a61b
AE
257if (COMPILER_TARGET_DEFAULT) {
258 /**
259 * User profile inline editor.
260 *
261 * @param integer userID
262 * @param boolean editOnInit
263 */
264 WCF.User.Profile.Editor = Class.extend({
265 /**
266 * current action
267 * @var string
268 */
269 _actionName: '',
270
271 _active: false,
272
273 /**
274 * list of interface buttons
275 * @var object
276 */
277 _buttons: {},
278
279 /**
280 * cached tab content
281 * @var string
282 */
283 _cachedTemplate: '',
284
285 /**
286 * action proxy
287 * @var WCF.Action.Proxy
288 */
289 _proxy: null,
290
291 /**
292 * tab object
293 * @var jQuery
294 */
295 _tab: null,
296
297 /**
298 * target user id
299 * @var integer
300 */
301 _userID: 0,
302
303 /**
304 * Initializes the WCF.User.Profile.Editor object.
305 *
306 * @param integer userID
307 * @param boolean editOnInit
308 */
309 init: function (userID, editOnInit) {
310 this._actionName = '';
311 this._active = false;
312 this._cachedTemplate = '';
313 this._tab = $('#about');
314 this._userID = userID;
315 this._proxy = new WCF.Action.Proxy({
316 success: $.proxy(this._success, this)
317 });
320f4a6d 318
f5e3a61b
AE
319 this._initButtons();
320
321 // begin editing on page load
322 if (editOnInit) {
323 this._beginEdit();
320f4a6d 324 }
f5e3a61b
AE
325 },
326
327 /**
328 * Initializes interface buttons.
329 */
330 _initButtons: function () {
331 // create buttons
332 this._buttons = {
333 beginEdit: $('.jsButtonEditProfile:eq(0)').click(this._beginEdit.bind(this))
334 };
335 },
336
337 /**
338 * Begins editing.
339 *
340 * @param {Event?} event event object
341 */
342 _beginEdit: function (event) {
343 if (event) event.preventDefault();
344
345 if (this._active) return;
346 this._active = true;
347
348 this._actionName = 'beginEdit';
349 this._buttons.beginEdit.parent().addClass('active');
350 $('#profileContent').wcfTabs('select', 'about');
351
352 // load form
353 this._proxy.setOption('data', {
354 actionName: 'beginEdit',
355 className: 'wcf\\data\\user\\UserProfileAction',
356 objectIDs: [this._userID]
357 });
358 this._proxy.sendRequest();
359 },
360
361 /**
362 * Saves input values.
363 */
364 _save: function () {
ac1c2cb8
AE
365 require(["WoltLabSuite/Core/Component/Ckeditor"], ({ getCkeditor }) => {
366 const textareas = Array.from(this._tab[0].querySelectorAll("textarea"));
367 const scrollToTextarea = textareas.find((textarea) => {
368 const editor = getCkeditor(textarea);
369 if (editor === undefined) {
370 return false;
371 }
372
373 const data = {
374 api: {
375 throwError: elInnerError
376 },
377 valid: true
378 };
379 WCF.System.Event.fireEvent('com.woltlab.wcf.ckeditor5', `validate_${textarea.id}`, data);
380
381 return data.valid === false;
382 });
a6f6deee 383
ac1c2cb8
AE
384 if (scrollToTextarea) {
385 scrollToTextarea.parentElement.scrollIntoView({ behavior: 'smooth' });
386 return;
a6f6deee 387 }
3555965d 388
ac1c2cb8
AE
389 this._actionName = 'save';
390
391 // collect values
392 var $regExp = /values\[([a-zA-Z0-9._-]+)\]/;
393 var $values = {};
394 this._tab.find('input, textarea, select').each(function (index, element) {
395 var $element = $(element);
396 var $value = null;
397
398 switch ($element.getTagName()) {
399 case 'input':
400 var $type = $element.attr('type');
401
402 if (($type === 'radio' || $type === 'checkbox') && !$element.prop('checked')) {
403 return;
404 }
405 break;
f5e3a61b 406
ac1c2cb8
AE
407 case 'textarea':
408 let editor = getCkeditor(element);
409 if (editor !== undefined) {
410 $value = editor.getHtml();
411 }
412 break;
413 }
3555965d 414
ac1c2cb8
AE
415 var $name = $element.attr('name');
416 if ($regExp.test($name)) {
417 var $fieldName = RegExp.$1;
418 if ($value === null) $value = $element.val();
419
420 // check for checkboxes
421 if ($element.attr('type') === 'checkbox' && /\[\]$/.test($name)) {
422 if (!Array.isArray($values[$fieldName])) {
423 $values[$fieldName] = [];
424 }
425
426 $values[$fieldName].push($value);
f5e3a61b 427 }
ac1c2cb8
AE
428 else {
429 $values[$fieldName] = $value;
f5e3a61b 430 }
f5e3a61b 431 }
ac1c2cb8
AE
432 });
433
434 this._proxy.setOption('data', {
435 actionName: 'save',
436 className: 'wcf\\data\\user\\UserProfileAction',
437 objectIDs: [this._userID],
438 parameters: {
439 values: $values
f5e3a61b 440 }
ac1c2cb8
AE
441 });
442 this._proxy.sendRequest();
f5e3a61b 443 });
f5e3a61b
AE
444 },
445
446 /**
447 * Restores back to default view.
448 */
449 _restore: function () {
450 this._actionName = 'restore';
451 this._active = false;
452 this._buttons.beginEdit.parent().removeClass('active');
453
454 this._destroyEditor();
455
456 this._tab.html(this._cachedTemplate).children().css({height: 'auto'});
457 },
458
459 /**
460 * Handles successful AJAX requests.
461 *
462 * @param object data
463 * @param string textStatus
464 * @param jQuery jqXHR
465 */
466 _success: function (data, textStatus, jqXHR) {
467 switch (this._actionName) {
468 case 'beginEdit':
469 this._prepareEdit(data);
470 break;
320f4a6d 471
f5e3a61b
AE
472 case 'save':
473 // save was successful, show parsed template
474 if (data.returnValues.success) {
475 this._cachedTemplate = data.returnValues.template;
476 this._restore();
477 }
478 else {
479 this._prepareEdit(data, true);
480 }
481 break;
320f4a6d 482 }
f5e3a61b
AE
483 },
484
485 /**
486 * Prepares editing mode.
487 *
488 * @param object data
489 * @param boolean disableCache
490 */
491 _prepareEdit: function (data, disableCache) {
492 this._destroyEditor();
493
494 // update template
495 var self = this;
496 this._tab.html(function (index, oldHTML) {
497 if (disableCache !== true) {
498 self._cachedTemplate = oldHTML;
499 }
500
501 return data.returnValues.template;
502 });
503
504 // block autocomplete
505 this._tab.find('input[type=text]').attr('autocomplete', 'off');
506
507 // bind event listener
508 this._tab.find('.formSubmit > button[data-type=save]').click($.proxy(this._save, this));
509 this._tab.find('.formSubmit > button[data-type=restore]').click($.proxy(this._restore, this));
510 this._tab.find('input').keyup(function (event) {
511 if (event.which === $.ui.keyCode.ENTER) {
512 self._save();
513
514 event.preventDefault();
515 return false;
516 }
517 });
518 },
519
520 /**
521 * Destroys all editor instances within current tab.
522 */
523 _destroyEditor: function () {
ac1c2cb8
AE
524 require(["WoltLabSuite/Core/Component/Ckeditor"], ({ getCkeditor }) => {
525 this._tab[0].querySelectorAll("textarea").forEach((textarea) => {
526 const editor = getCkeditor(textarea);
527 editor?.destroy();
528 });
f5e3a61b
AE
529 });
530 }
531 });
532}
533else {
534 WCF.User.Profile.Editor = Class.extend({
535 _actionName: "",
536 _active: false,
537 _buttons: {},
538 _cachedTemplate: "",
539 _proxy: {},
540 _tab: {},
541 _userID: 0,
542 init: function() {},
543 _initButtons: function() {},
544 _beginEdit: function() {},
545 _save: function() {},
546 _restore: function() {},
547 _success: function() {},
548 _prepareEdit: function() {},
549 _destroyEditor: function() {}
550 });
551}
320f4a6d 552
320f4a6d
MW
553/**
554 * Namespace for registration functions.
555 */
556WCF.User.Registration = {};
557
558/**
559 * Validates the password.
560 *
561 * @param jQuery element
562 * @param jQuery confirmElement
563 * @param object options
6b6aa915 564 * @deprecated 6.1 use `WoltLabSuite/Core/Controller/User/Registration` instead
320f4a6d
MW
565 */
566WCF.User.Registration.Validation = Class.extend({
567 /**
568 * action name
569 * @var string
570 */
571 _actionName: '',
572
573 /**
574 * class name
575 * @var string
576 */
577 _className: '',
578
579 /**
580 * confirmation input element
581 * @var jQuery
582 */
583 _confirmElement: null,
584
585 /**
586 * input element
587 * @var jQuery
588 */
589 _element: null,
590
591 /**
592 * list of error messages
593 * @var object
594 */
595 _errorMessages: { },
596
597 /**
598 * list of additional options
599 * @var object
600 */
601 _options: { },
602
603 /**
604 * AJAX proxy
605 * @var WCF.Action.Proxy
606 */
607 _proxy: null,
608
609 /**
610 * Initializes the validation.
611 *
612 * @param jQuery element
613 * @param jQuery confirmElement
614 * @param object options
615 */
616 init: function(element, confirmElement, options) {
617 this._element = element;
618 this._element.blur($.proxy(this._blur, this));
619 this._confirmElement = confirmElement || null;
620
621 if (this._confirmElement !== null) {
622 this._confirmElement.blur($.proxy(this._blurConfirm, this));
623 }
624
625 options = options || { };
626 this._setOptions(options);
627
628 this._proxy = new WCF.Action.Proxy({
629 success: $.proxy(this._success, this),
630 showLoadingOverlay: false
631 });
632
633 this._setErrorMessages();
634 },
635
636 /**
637 * Sets additional options
638 */
639 _setOptions: function(options) { },
640
641 /**
642 * Sets error messages.
643 */
644 _setErrorMessages: function() {
645 this._errorMessages = {
646 ajaxError: '',
647 notEqual: ''
648 };
649 },
650
651 /**
652 * Validates once focus on input is lost.
653 *
654 * @param object event
655 */
656 _blur: function(event) {
657 var $value = this._element.val();
658 if (!$value) {
659 return this._showError(this._element, WCF.Language.get('wcf.global.form.error.empty'));
660 }
661
662 if (this._confirmElement !== null) {
663 var $confirmValue = this._confirmElement.val();
664 if ($confirmValue != '' && $value != $confirmValue) {
665 return this._showError(this._confirmElement, this._errorMessages.notEqual);
666 }
667 }
668
669 if (!this._validateOptions()) {
670 return;
671 }
672
673 this._proxy.setOption('data', {
674 actionName: this._actionName,
675 className: this._className,
676 parameters: this._getParameters()
677 });
678 this._proxy.sendRequest();
679 },
680
681 /**
682 * Returns a list of parameters.
683 *
684 * @return object
685 */
686 _getParameters: function() {
687 return { };
688 },
689
690 /**
691 * Validates input by options.
692 *
693 * @return boolean
694 */
695 _validateOptions: function() {
696 return true;
697 },
698
699 /**
700 * Validates value once confirmation input focus is lost.
701 *
702 * @param object event
703 */
704 _blurConfirm: function(event) {
705 var $value = this._confirmElement.val();
706 if (!$value) {
707 return this._showError(this._confirmElement, WCF.Language.get('wcf.global.form.error.empty'));
708 }
709
710 this._blur(event);
711 },
712
713 /**
714 * Handles AJAX responses.
715 *
716 * @param object data
717 * @param string textStatus
718 * @param jQuery jqXHR
719 */
720 _success: function(data, textStatus, jqXHR) {
721 if (data.returnValues.isValid) {
722 this._showSuccess(this._element);
723 if (this._confirmElement !== null && this._confirmElement.val()) {
724 this._showSuccess(this._confirmElement);
725 }
726 }
727 else {
728 this._showError(this._element, WCF.Language.get(this._errorMessages.ajaxError + data.returnValues.error));
729 }
730 },
731
732 /**
733 * Shows an error message.
734 *
735 * @param jQuery element
736 * @param string message
737 */
738 _showError: function(element, message) {
739 element.parent().parent().addClass('formError').removeClass('formSuccess');
740
741 var $innerError = element.parent().find('small.innerError');
742 if (!$innerError.length) {
743 $innerError = $('<small />').addClass('innerError').insertAfter(element);
744 }
745
746 $innerError.text(message);
747 },
748
749 /**
750 * Displays a success message.
751 *
752 * @param jQuery element
753 */
754 _showSuccess: function(element) {
755 element.parent().parent().addClass('formSuccess').removeClass('formError');
756 element.next('small.innerError').remove();
757 }
758});
759
760/**
761 * Username validation for registration.
e3369fd2 762 *
320f4a6d 763 * @see WCF.User.Registration.Validation
6b6aa915 764 * @deprecated 6.1 use `WoltLabSuite/Core/Controller/User/Registration` instead
320f4a6d
MW
765 */
766WCF.User.Registration.Validation.Username = WCF.User.Registration.Validation.extend({
767 /**
768 * @see WCF.User.Registration.Validation._actionName
769 */
770 _actionName: 'validateUsername',
771
772 /**
773 * @see WCF.User.Registration.Validation._className
774 */
775 _className: 'wcf\\data\\user\\UserRegistrationAction',
776
777 /**
778 * @see WCF.User.Registration.Validation._setOptions()
779 */
780 _setOptions: function(options) {
781 this._options = $.extend(true, {
782 minlength: 3,
783 maxlength: 25
784 }, options);
785 },
786
787 /**
788 * @see WCF.User.Registration.Validation._setErrorMessages()
789 */
790 _setErrorMessages: function() {
791 this._errorMessages = {
792 ajaxError: 'wcf.user.username.error.'
793 };
794 },
795
796 /**
797 * @see WCF.User.Registration.Validation._validateOptions()
798 */
799 _validateOptions: function() {
800 var $value = this._element.val();
801 if ($value.length < this._options.minlength || $value.length > this._options.maxlength) {
063bbf46 802 this._showError(this._element, WCF.Language.get('wcf.user.username.error.invalid'));
320f4a6d
MW
803 return false;
804 }
805
806 return true;
807 },
808
809 /**
810 * @see WCF.User.Registration.Validation._getParameters()
811 */
812 _getParameters: function() {
813 return {
814 username: this._element.val()
815 };
816 }
817});
818
819/**
820 * Email validation for registration.
821 *
822 * @see WCF.User.Registration.Validation
6b6aa915 823 * @deprecated 6.1 use `WoltLabSuite/Core/Controller/User/Registration` instead
320f4a6d
MW
824 */
825WCF.User.Registration.Validation.EmailAddress = WCF.User.Registration.Validation.extend({
826 /**
827 * @see WCF.User.Registration.Validation._actionName
828 */
829 _actionName: 'validateEmailAddress',
830
831 /**
832 * @see WCF.User.Registration.Validation._className
833 */
834 _className: 'wcf\\data\\user\\UserRegistrationAction',
835
836 /**
837 * @see WCF.User.Registration.Validation._getParameters()
838 */
839 _getParameters: function() {
840 return {
841 email: this._element.val()
842 };
843 },
844
845 /**
846 * @see WCF.User.Registration.Validation._setErrorMessages()
847 */
848 _setErrorMessages: function() {
849 this._errorMessages = {
850 ajaxError: 'wcf.user.email.error.',
851 notEqual: WCF.Language.get('wcf.user.confirmEmail.error.notEqual')
852 };
853 }
854});
855
320f4a6d
MW
856/**
857 * Notification system for WCF.
858 *
859 * @author Alexander Ebert
7b7b9764 860 * @copyright 2001-2019 WoltLab GmbH
320f4a6d
MW
861 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
862 */
078256b5
AE
863WCF.Notification = { };
864
f5e3a61b 865if (COMPILER_TARGET_DEFAULT) {
078256b5 866 /**
f5e3a61b 867 * Handles the notification list.
078256b5 868 */
f5e3a61b
AE
869 WCF.Notification.List = Class.extend({
870 /**
871 * action proxy
872 * @var WCF.Action.Proxy
873 */
874 _proxy: null,
078256b5 875
f5e3a61b
AE
876 /**
877 * Initializes the WCF.Notification.List object.
878 */
879 init: function () {
880 this._proxy = new WCF.Action.Proxy({
881 success: $.proxy(this._success, this)
882 });
883
884 // handle 'mark all as confirmed' buttons
af59d819 885 $('.jsMarkAllAsConfirmed').click(function () {
f5e3a61b
AE
886 WCF.System.Confirmation.show(WCF.Language.get('wcf.user.notification.markAllAsConfirmed.confirmMessage'), function (action) {
887 if (action === 'confirm') {
888 new WCF.Action.Proxy({
889 autoSend: true,
890 data: {
891 actionName: 'markAllAsConfirmed',
892 className: 'wcf\\data\\user\\notification\\UserNotificationAction'
893 },
894 success: function () {
895 window.location.reload();
896 }
897 });
898 }
899 });
900 });
901
902 // handle regular items
903 this._convertList();
904 },
905
906 /**
907 * Converts the notification item list to be in sync with the notification dropdown.
908 */
909 _convertList: function () {
910 $('.userNotificationItemList > .notificationItem').each((function (index, item) {
911 var $item = $(item);
912
913 if (!$item.data('isRead')) {
914 $item.find('a:not(.userLink)').prop('href', $item.data('link'));
915
58d5a1b5 916 var $markAsConfirmed = $(`<button type="button" class="notificationItemMarkAsConfirmed jsTooltip" title="${WCF.Language.get('wcf.global.button.markAsRead')}">
925dad9f
AE
917 <fa-icon size="24" name="check"></fa-icon>
918 </button>`).appendTo($item);
f5e3a61b
AE
919 $markAsConfirmed.click($.proxy(this._markAsConfirmed, this));
920 }
f5e3a61b
AE
921 }).bind(this));
922
923 WCF.DOMNodeInsertedHandler.execute();
924 },
925
926 /**
927 * Marks a single notification as confirmed.
928 *
929 * @param object event
930 */
931 _markAsConfirmed: function (event) {
932 event.preventDefault();
933
934 var $notificationID = $(event.currentTarget).parents('.notificationItem:eq(0)').data('objectID');
935
936 this._proxy.setOption('data', {
937 actionName: 'markAsConfirmed',
938 className: 'wcf\\data\\user\\notification\\UserNotificationAction',
939 objectIDs: [$notificationID]
078256b5 940 });
f5e3a61b 941 this._proxy.sendRequest();
078256b5 942
f5e3a61b
AE
943 return false;
944 },
945
946 /**
947 * Handles successful AJAX requests.
948 *
949 * @param object data
950 * @param string textStatus
951 * @param jQuery jqXHR
952 */
953 _success: function (data, textStatus, jqXHR) {
954 var $item = $('.userNotificationItemList > .notificationItem[data-object-id=' + data.returnValues.markAsRead + ']');
75b89dff 955
f5e3a61b
AE
956 $item.data('isRead', true);
957 $item.find('.newContentBadge').remove();
958 $item.find('.notificationItemMarkAsConfirmed').remove();
959 $item.removeClass('notificationUnconfirmed');
960 }
961 });
962
963 /**
964 * Signature preview.
965 *
966 * @see WCF.Message.Preview
967 */
968 WCF.User.SignaturePreview = WCF.Message.Preview.extend({
969 /**
970 * @see WCF.Message.Preview._handleResponse()
971 */
972 _handleResponse: function (data) {
973 // get preview container
974 var $preview = $('#previewContainer');
975 if (!$preview.length) {
e3ac253a 976 $preview = $('<section class="section" id="previewContainer"><h2 class="sectionTitle">' + WCF.Language.get('wcf.global.preview') + '</h2><div class="htmlContent messageSignatureConstraints"></div></section>').insertBefore($('#signatureContainer')).wcfFadeIn();
75b89dff 977 }
f5e3a61b
AE
978
979 $preview.children('div').first().html(data.returnValues.message);
980 }
981 });
982}
983else {
984 WCF.Notification.List = Class.extend({
985 _proxy: {},
986 init: function() {},
987 _convertList: function() {},
988 _markAsConfirmed: function() {},
989 _success: function() {}
990 });
991
992 WCF.User.SignaturePreview = WCF.Message.Preview.extend({
993 _handleResponse: function() {},
994 _className: "",
995 _messageFieldID: "",
996 _messageField: {},
997 _proxy: {},
998 _previewButton: {},
999 _previewButtonLabel: "",
1000 init: function() {},
1001 _click: function() {},
1002 _getParameters: function() {},
1003 _getMessage: function() {},
1004 _success: function() {},
1005 _failure: function() {}
1006 });
1007}
320f4a6d
MW
1008
1009/**
1010 * Loads recent activity events once the user scrolls to the very bottom.
1011 *
1012 * @param integer userID
e83f88f7 1013 * @deprecated 6.1 use `WoltLabSuite/Core/Components/User/RecentActivity/Loader` instead
320f4a6d
MW
1014 */
1015WCF.User.RecentActivityLoader = Class.extend({
1016 /**
1017 * container object
1018 * @var jQuery
1019 */
1020 _container: null,
1021
1022 /**
1023 * true if list should be filtered by followed users
1024 * @var boolean
1025 */
1026 _filteredByFollowedUsers: false,
1027
1028 /**
1029 * button to load next events
1030 * @var jQuery
1031 */
1032 _loadButton: null,
1033
1034 /**
1035 * action proxy
1036 * @var WCF.Action.Proxy
1037 */
1038 _proxy: null,
1039
1040 /**
1041 * user id
1042 * @var integer
1043 */
1044 _userID: 0,
1045
1046 /**
1047 * Initializes a new RecentActivityLoader object.
1048 *
1049 * @param integer userID
1050 * @param boolean filteredByFollowedUsers
1051 */
1052 init: function(userID, filteredByFollowedUsers) {
1053 this._container = $('#recentActivities');
1054 this._filteredByFollowedUsers = (filteredByFollowedUsers === true);
1055 this._userID = userID;
1056
1057 if (this._userID !== null && !this._userID) {
1058 console.debug("[WCF.User.RecentActivityLoader] Invalid parameter 'userID' given.");
1059 return;
1060 }
1061
1062 this._proxy = new WCF.Action.Proxy({
1063 success: $.proxy(this._success, this)
1064 });
1065
b22b7eae 1066 if (this._container.children('li').length) {
58d5a1b5 1067 this._loadButton = $('<li class="showMore"><button type="button" class="button small">' + WCF.Language.get('wcf.user.recentActivity.more') + '</button></li>').appendTo(this._container);
b22b7eae
AE
1068 this._loadButton = this._loadButton.children('button').click($.proxy(this._click, this));
1069 }
1070 else {
9e90dd74 1071 $('<li class="showMore"><small>' + WCF.Language.get('wcf.user.recentActivity.noMoreEntries') + '</small></li>').appendTo(this._container);
b22b7eae
AE
1072 }
1073
1074 if (WCF.User.userID) {
c673b4f1 1075 $('.jsRecentActivitySwitchContext .button').click($.proxy(this._switchContext, this));
b22b7eae 1076 }
320f4a6d
MW
1077 },
1078
1079 /**
1080 * Loads next activity events.
1081 */
1082 _click: function() {
1083 this._loadButton.enable();
1084
1085 var $parameters = {
06f95270 1086 lastEventID: this._container.data('lastEventID'),
320f4a6d
MW
1087 lastEventTime: this._container.data('lastEventTime')
1088 };
1089 if (this._userID) {
1090 $parameters.userID = this._userID;
1091 }
1092 else if (this._filteredByFollowedUsers) {
1093 $parameters.filteredByFollowedUsers = 1;
1094 }
1095
1096 this._proxy.setOption('data', {
1097 actionName: 'load',
1098 className: 'wcf\\data\\user\\activity\\event\\UserActivityEventAction',
1099 parameters: $parameters
1100 });
1101 this._proxy.sendRequest();
1102 },
1103
b22b7eae
AE
1104 /**
1105 * Switches recent activity context.
1106 */
48af22b1
MW
1107 _switchContext: function(event) {
1108 event.preventDefault();
1109
1110 if (!$(event.currentTarget).hasClass('active')) {
1111 new WCF.Action.Proxy({
1112 autoSend: true,
1113 data: {
1114 actionName: 'switchContext',
1115 className: 'wcf\\data\\user\\activity\\event\\UserActivityEventAction'
1116 },
1117 success: function() {
1118 window.location.hash = '#dashboardBoxRecentActivity';
1119 window.location.reload();
1120 }
1121 });
1122 }
b22b7eae
AE
1123 },
1124
320f4a6d
MW
1125 /**
1126 * Handles successful AJAX requests.
1127 *
1128 * @param object data
1129 * @param string textStatus
1130 * @param jQuery jqXHR
1131 */
1132 _success: function(data, textStatus, jqXHR) {
1133 if (data.returnValues.template) {
1134 $(data.returnValues.template).insertBefore(this._loadButton.parent());
1135
1136 this._container.data('lastEventTime', data.returnValues.lastEventTime);
06f95270 1137 this._container.data('lastEventID', data.returnValues.lastEventID);
320f4a6d
MW
1138 this._loadButton.enable();
1139 }
1140 else {
1141 $('<small>' + WCF.Language.get('wcf.user.recentActivity.noMoreEntries') + '</small>').appendTo(this._loadButton.parent());
1142 this._loadButton.remove();
1143 }
1144 }
1145});
1146
320f4a6d 1147/**
1615fc2e 1148 * Initializes WCF.User.Action namespace.
320f4a6d
MW
1149 */
1150WCF.User.Action = {};
1151
f5e3a61b
AE
1152if (COMPILER_TARGET_DEFAULT) {
1153 /**
1154 * Handles user follow and unfollow links.
4a90b12e
MW
1155 *
1156 * @deprecated 6.1 use `WoltLabSuite/Core/Component/User/Follow` instead
f5e3a61b
AE
1157 */
1158 WCF.User.Action.Follow = Class.extend({
1159 /**
1160 * list with elements containing follow and unfollow buttons
1161 * @var array
1162 */
1163 _containerList: null,
1164
1165 /**
1166 * CSS selector for follow buttons
1167 * @var string
1168 */
1169 _followButtonSelector: '.jsFollowButton',
1170
1171 /**
1172 * id of the user that is currently being followed/unfollowed
1173 * @var integer
1174 */
1175 _userID: 0,
1176
1177 /**
1178 * Initializes new WCF.User.Action.Follow object.
1179 *
1180 * @param array containerList
1181 * @param string followButtonSelector
1182 */
1183 init: function (containerList, followButtonSelector) {
1184 if (!containerList.length) {
1185 return;
320f4a6d 1186 }
f5e3a61b 1187 this._containerList = containerList;
320f4a6d 1188
f5e3a61b
AE
1189 if (followButtonSelector) {
1190 this._followButtonSelector = followButtonSelector;
1191 }
1192
1193 // initialize proxy
1194 this._proxy = new WCF.Action.Proxy({
1195 success: $.proxy(this._success, this)
1196 });
1197
1198 // bind event listeners
1199 this._containerList.each($.proxy(function (index, container) {
1200 $(container).find(this._followButtonSelector).click($.proxy(this._click, this));
1201 }, this));
1202 },
1203
1204 /**
1205 * Handles a click on a follow or unfollow button.
1206 *
1207 * @param object event
1208 */
1209 _click: function (event) {
1210 event.preventDefault();
1211 var link = $(event.target);
1212 if (!link.is('a')) {
1213 link = link.closest('a');
1214 }
1215 this._userID = link.data('objectID');
1216
1217 this._proxy.setOption('data', {
1218 'actionName': link.data('following') ? 'unfollow' : 'follow',
1219 'className': 'wcf\\data\\user\\follow\\UserFollowAction',
1220 'parameters': {
1221 data: {
1222 userID: this._userID
1223 }
320f4a6d 1224 }
f5e3a61b
AE
1225 });
1226 this._proxy.sendRequest();
1227 },
1228
1229 /**
1230 * Handles the successful (un)following of a user.
1231 *
1232 * @param object data
1233 * @param string textStatus
1234 * @param jQuery jqXHR
1235 */
1236 _success: function (data, textStatus, jqXHR) {
1237 this._containerList.each($.proxy(function (index, container) {
1238 var button = $(container).find(this._followButtonSelector).get(0);
320f4a6d 1239
f5e3a61b
AE
1240 if (button && $(button).data('objectID') == this._userID) {
1241 button = $(button);
1242
1243 // toogle icon title
1244 if (data.returnValues.following) {
0caf2e88 1245 button.attr('data-tooltip', WCF.Language.get('wcf.user.button.unfollow'));
7c42ba45 1246 button[0].querySelector("fa-icon").setIcon("circle-minus");
f5e3a61b
AE
1247 button.children('.invisible').text(WCF.Language.get('wcf.user.button.unfollow'));
1248 }
1249 else {
0caf2e88 1250 button.attr('data-tooltip', WCF.Language.get('wcf.user.button.follow'));
7c42ba45 1251 button[0].querySelector("fa-icon").setIcon("circle-plus");
f5e3a61b
AE
1252 button.children('.invisible').text(WCF.Language.get('wcf.user.button.follow'));
1253 }
1254
1255 button.data('following', data.returnValues.following);
1256
1257 return false;
320f4a6d 1258 }
f5e3a61b
AE
1259 }, this));
1260
1261 var $notification = new WCF.System.Notification();
1262 $notification.show();
1263 }
1264 });
1265
1266 /**
1267 * Handles user ignore and unignore links.
30cc7e32
TD
1268 *
1269 * @deprecated 5.4 Use a FormBuilderDialog for wcf\data\user\ignore\UserIgnoreAction::getDialog()
f5e3a61b
AE
1270 */
1271 WCF.User.Action.Ignore = Class.extend({
1272 /**
1273 * list with elements containing ignore and unignore buttons
1274 * @var array
1275 */
1276 _containerList: null,
1277
1278 /**
1279 * CSS selector for ignore buttons
1280 * @var string
1281 */
1282 _ignoreButtonSelector: '.jsIgnoreButton',
1283
1284 /**
1285 * id of the user that is currently being ignored/unignored
1286 * @var integer
1287 */
1288 _userID: 0,
1289
1290 /**
1291 * Initializes new WCF.User.Action.Ignore object.
1292 *
1293 * @param array containerList
1294 * @param string ignoreButtonSelector
1295 */
1296 init: function (containerList, ignoreButtonSelector) {
1297 if (!containerList.length) {
1298 return;
1299 }
1300 this._containerList = containerList;
1301
1302 if (ignoreButtonSelector) {
1303 this._ignoreButtonSelector = ignoreButtonSelector;
320f4a6d 1304 }
320f4a6d 1305
f5e3a61b
AE
1306 // initialize proxy
1307 this._proxy = new WCF.Action.Proxy({
1308 success: $.proxy(this._success, this)
1309 });
1310
1311 // bind event listeners
1312 this._containerList.each($.proxy(function (index, container) {
1313 $(container).find(this._ignoreButtonSelector).click($.proxy(this._click, this));
1314 }, this));
1315 },
1316
1317 /**
1318 * Handles a click on a ignore or unignore button.
1319 *
1320 * @param object event
1321 */
1322 _click: function (event) {
1323 event.preventDefault();
1324 var link = $(event.target);
1325 if (!link.is('a')) {
1326 link = link.closest('a');
1327 }
1328 this._userID = link.data('objectID');
1329
1330 this._proxy.setOption('data', {
1331 'actionName': link.data('ignored') ? 'unignore' : 'ignore',
1332 'className': 'wcf\\data\\user\\ignore\\UserIgnoreAction',
1333 'parameters': {
1334 data: {
1335 userID: this._userID
1336 }
320f4a6d 1337 }
f5e3a61b
AE
1338 });
1339 this._proxy.sendRequest();
1340 },
1341
1342 /**
1343 * Handles the successful (un)ignoring of a user.
1344 *
1345 * @param object data
1346 * @param string textStatus
1347 * @param jQuery jqXHR
1348 */
1349 _success: function (data, textStatus, jqXHR) {
1350 this._containerList.each($.proxy(function (index, container) {
1351 var button = $(container).find(this._ignoreButtonSelector).get(0);
320f4a6d 1352
f5e3a61b
AE
1353 if (button && $(button).data('objectID') == this._userID) {
1354 button = $(button);
1355
1356 // toogle icon title
1357 if (data.returnValues.isIgnoredUser) {
0caf2e88
AE
1358 button.attr('data-tooltip', WCF.Language.get('wcf.user.button.unignore'));
1359 button[0].querySelector("fa-icon").setIcon("circle");
f5e3a61b
AE
1360 button.children('.invisible').text(WCF.Language.get('wcf.user.button.unignore'));
1361 }
1362 else {
0caf2e88
AE
1363 button.attr('data-tooltip', WCF.Language.get('wcf.user.button.ignore'));
1364 button[0].querySelector("fa-icon").setIcon("ban");
f5e3a61b
AE
1365 button.children('.invisible').text(WCF.Language.get('wcf.user.button.ignore'));
1366 }
1367
1368 button.data('ignored', data.returnValues.isIgnoredUser);
1369
1370 return false;
1371 }
1372 }, this));
1373
1374 var $notification = new WCF.System.Notification();
1375 $notification.show();
f5e3a61b
AE
1376 }
1377 });
1378}
1379else {
1380 WCF.User.Action.Follow = Class.extend({
1381 _containerList: {},
1382 _followButtonSelector: "",
1383 _userID: 0,
1384 init: function() {},
1385 _click: function() {},
1386 _success: function() {}
1387 });
1388
30cc7e32
TD
1389 /**
1390 * @deprecated
1391 */
f5e3a61b
AE
1392 WCF.User.Action.Ignore = Class.extend({
1393 _containerList: {},
1394 _ignoreButtonSelector: "",
1395 _userID: 0,
1396 init: function() {},
1397 _click: function() {},
1398 _success: function() {}
1399 });
1400}
320f4a6d
MW
1401
1402/**
1403 * Namespace for avatar functions.
1404 */
1405WCF.User.Avatar = {};
1406
f5e3a61b
AE
1407if (COMPILER_TARGET_DEFAULT) {
1408 /**
1409 * Avatar upload function
1410 *
1411 * @see WCF.Upload
1412 */
1413 WCF.User.Avatar.Upload = WCF.Upload.extend({
1414 /**
1415 * user id of avatar owner
1416 * @var integer
1417 */
1418 _userID: 0,
1419
1420 /**
1615fc2e 1421 * Initializes a new WCF.User.Avatar.Upload object.
f5e3a61b
AE
1422 *
1423 * @param integer userID
1424 */
1425 init: function (userID) {
1426 this._super($('#avatarUpload > dd > div'), undefined, 'wcf\\data\\user\\avatar\\UserAvatarAction');
1427 this._userID = userID || 0;
1428
1429 $('#avatarForm input[type=radio]').change(function () {
1430 if ($(this).val() == 'custom') {
1431 $('#avatarUpload > dd > div').show();
1432 }
1433 else {
1434 $('#avatarUpload > dd > div').hide();
1435 }
1436 });
1437 if (!$('#avatarForm input[type=radio][value=custom]:checked').length) {
320f4a6d
MW
1438 $('#avatarUpload > dd > div').hide();
1439 }
f5e3a61b
AE
1440 },
1441
1442 /**
1443 * @see WCF.Upload._initFile()
1444 */
1445 _initFile: function (file) {
1446 return $('#avatarUpload > dt > img');
1447 },
1448
1449 /**
1450 * @see WCF.Upload._success()
1451 */
1452 _success: function (uploadID, data) {
1453 if (data.returnValues.url) {
1454 this._updateImage(data.returnValues.url);
1455
1456 // hide error
1457 $('#avatarUpload > dd > .innerError').remove();
1458
1459 // show success message
1460 var $notification = new WCF.System.Notification(WCF.Language.get('wcf.user.avatar.upload.success'));
1461 $notification.show();
1462 }
1463 else if (data.returnValues.errorType) {
1464 // show error
1465 this._getInnerErrorElement().text(WCF.Language.get('wcf.user.avatar.upload.error.' + data.returnValues.errorType));
1466 }
1467 },
1468
1469 /**
1470 * Updates the displayed avatar image.
1471 *
1472 * @param string url
1473 */
1474 _updateImage: function (url) {
1475 $('#avatarUpload > dt > img').remove();
1476 var $image = $('<img src="' + url + '" class="userAvatarImage" alt="" />').css({
1477 'height': 'auto',
1478 'max-height': '96px',
1479 'max-width': '96px',
1480 'width': 'auto'
1481 });
320f4a6d 1482
f5e3a61b 1483 $('#avatarUpload > dt').prepend($image);
320f4a6d 1484
f5e3a61b
AE
1485 WCF.DOMNodeInsertedHandler.execute();
1486 },
1487
1488 /**
1489 * Returns the inner error element.
1490 *
1491 * @return jQuery
1492 */
1493 _getInnerErrorElement: function () {
1494 var $span = $('#avatarUpload > dd > .innerError');
1495 if (!$span.length) {
1496 $span = $('<small class="innerError"></span>');
1497 $('#avatarUpload > dd').append($span);
1498 }
1499
1500 return $span;
1501 },
1502
1503 /**
1504 * @see WCF.Upload._getParameters()
1505 */
1506 _getParameters: function () {
1507 return {
1508 userID: this._userID
1509 };
320f4a6d 1510 }
f5e3a61b
AE
1511 });
1512}
1513else {
1514 WCF.User.Avatar.Upload = WCF.Upload.extend({
1515 _userID: 0,
1516 init: function() {},
1517 _initFile: function() {},
1518 _success: function() {},
1519 _updateImage: function() {},
1520 _getInnerErrorElement: function() {},
1521 _getParameters: function() {},
1522 _name: "",
1523 _buttonSelector: {},
1524 _fileListSelector: {},
1525 _fileUpload: {},
1526 _className: "",
1527 _iframe: {},
1528 _internalFileID: 0,
1529 _options: {},
1530 _uploadMatrix: {},
1531 _supportsAJAXUpload: true,
1532 _overlay: {},
1533 _createButton: function() {},
1534 _insertButton: function() {},
1535 _removeButton: function() {},
1536 _upload: function() {},
1537 _createUploadMatrix: function() {},
1538 _error: function() {},
1539 _progress: function() {},
1540 _showOverlay: function() {},
1541 _evaluateResponse: function() {},
1542 _getFilename: function() {}
1543 });
1544}
320f4a6d
MW
1545
1546/**
1547 * Generic implementation for grouped user lists.
1548 *
1549 * @param string className
1550 * @param string dialogTitle
1551 * @param object additionalParameters
25c0e379 1552 * @deprecated 6.0 use `WoltLabSuite/Core/Component/User/List` instead
320f4a6d
MW
1553 */
1554WCF.User.List = Class.extend({
1555 /**
1556 * list of additional parameters
1557 * @var object
1558 */
1559 _additionalParameters: { },
1560
1561 /**
1562 * list of cached pages
1563 * @var object
1564 */
1565 _cache: { },
1566
1567 /**
1568 * action class name
1569 * @var string
1570 */
1571 _className: '',
1572
1573 /**
1574 * dialog overlay
1575 * @var jQuery
1576 */
1577 _dialog: null,
1578
1579 /**
1580 * dialog title
1581 * @var string
1582 */
1583 _dialogTitle: '',
1584
1585 /**
1586 * page count
1587 * @var integer
1588 */
1589 _pageCount: 0,
1590
1591 /**
1592 * current page no
1593 * @var integer
1594 */
1595 _pageNo: 1,
1596
1597 /**
1598 * action proxy
1599 * @var WCF.Action.Proxy
1600 */
1601 _proxy: null,
1602
1603 /**
1604 * Initializes a new grouped user list.
1605 *
1606 * @param string className
1607 * @param string dialogTitle
1608 * @param object additionalParameters
1609 */
1610 init: function(className, dialogTitle, additionalParameters) {
1611 this._additionalParameters = additionalParameters || { };
1612 this._cache = { };
1613 this._className = className;
1614 this._dialog = null;
1615 this._dialogTitle = dialogTitle;
1616 this._pageCount = 0;
1617 this._pageNo = 1;
1618
1619 this._proxy = new WCF.Action.Proxy({
1620 success: $.proxy(this._success, this)
1621 });
1622 },
1623
1624 /**
1625 * Opens the dialog overlay.
1626 */
1627 open: function() {
1628 this._pageNo = 1;
1629 this._showPage();
1630 },
1631
1632 /**
1633 * Displays the specified page.
1634 *
1635 * @param object event
1636 * @param object data
1637 */
1638 _showPage: function(event, data) {
1639 if (data && data.activePage) {
1640 this._pageNo = data.activePage;
1641 }
1642
1643 if (this._pageCount != 0 && (this._pageNo < 1 || this._pageNo > this._pageCount)) {
1644 console.debug("[WCF.User.List] Cannot access page " + this._pageNo + " of " + this._pageCount);
1645 return;
1646 }
1647
1648 if (this._cache[this._pageNo]) {
1649 var $dialogCreated = false;
1650 if (this._dialog === null) {
5a8a01c9
AE
1651 this._dialog = $('#userList' + this._className.hashCode());
1652 if (this._dialog.length === 0) {
1653 this._dialog = $('<div id="userList' + this._className.hashCode() + '" />').hide().appendTo(document.body);
1654 $dialogCreated = true;
1655 }
320f4a6d
MW
1656 }
1657
1658 // remove current view
1659 this._dialog.empty();
1660
1661 // insert HTML
1662 this._dialog.html(this._cache[this._pageNo]);
1663
1664 // add pagination
1665 if (this._pageCount > 1) {
1666 this._dialog.find('.jsPagination').wcfPages({
1667 activePage: this._pageNo,
1668 maxPage: this._pageCount
8bb37fef 1669 }).on('wcfpagesswitched', $.proxy(this._showPage, this));
320f4a6d 1670 }
b992269f
AE
1671 else {
1672 this._dialog.find('.jsPagination').hide();
1673 }
320f4a6d
MW
1674
1675 // show dialog
1676 if ($dialogCreated) {
1677 this._dialog.wcfDialog({
1678 title: this._dialogTitle
1679 });
1680 }
1681 else {
0d073c60 1682 this._dialog.wcfDialog('option', 'title', this._dialogTitle);
320f4a6d
MW
1683 this._dialog.wcfDialog('open').wcfDialog('render');
1684 }
49576e5e
AE
1685
1686 WCF.DOMNodeInsertedHandler.execute();
320f4a6d
MW
1687 }
1688 else {
1689 this._additionalParameters.pageNo = this._pageNo;
1690
1691 // load template via AJAX
1692 this._proxy.setOption('data', {
1693 actionName: 'getGroupedUserList',
1694 className: this._className,
1695 interfaceName: 'wcf\\data\\IGroupedUserListAction',
1696 parameters: this._additionalParameters
1697 });
1698 this._proxy.sendRequest();
1699 }
1700 },
1701
1702 /**
1703 * Handles successful AJAX requests.
1704 *
1705 * @param object data
1706 * @param string textStatus
1707 * @param jQuery jqXHR
1708 */
1709 _success: function(data, textStatus, jqXHR) {
1710 if (data.returnValues.pageCount) {
1711 this._pageCount = data.returnValues.pageCount;
1712 }
1713
1714 this._cache[this._pageNo] = data.returnValues.template;
1715 this._showPage();
1716 }
1717});
1718
1719/**
1720 * Namespace for object watch functions.
1721 */
1722WCF.User.ObjectWatch = {};
1723
f5e3a61b
AE
1724if (COMPILER_TARGET_DEFAULT) {
1725 /**
1726 * Handles subscribe/unsubscribe links.
7e04e684
MW
1727 *
1728 * @deprecated since 6.0, use `WoltLabSuite/Core/Ui/User/ObjectWatch` instead.
f5e3a61b
AE
1729 */
1730 WCF.User.ObjectWatch.Subscribe = Class.extend({
1731 /**
1732 * CSS selector for subscribe buttons
1733 * @var string
1734 */
1735 _buttonSelector: '.jsSubscribeButton',
1736
1737 /**
1738 * list of buttons
1739 * @var object
1740 */
1741 _buttons: {},
1742
1743 /**
1744 * dialog overlay
1745 * @var object
1746 */
1747 _dialog: null,
1748
1749 /**
1750 * system notification
1751 * @var WCF.System.Notification
1752 */
1753 _notification: null,
1754
1755 /**
1756 * reload page on unsubscribe
1757 * @var boolean
1758 */
1759 _reloadOnUnsubscribe: false,
1760
1761 /**
1762 * WCF.User.ObjectWatch.Subscribe object.
1763 *
1764 * @param boolean reloadOnUnsubscribe
1765 */
1766 init: function (reloadOnUnsubscribe) {
1767 this._buttons = {};
1768 this._notification = null;
1769 this._reloadOnUnsubscribe = (reloadOnUnsubscribe === true);
0f7229ca 1770
f5e3a61b
AE
1771 // initialize proxy
1772 this._proxy = new WCF.Action.Proxy({
1773 success: $.proxy(this._success, this)
1774 });
0f7229ca 1775
f5e3a61b
AE
1776 // bind event listeners
1777 $(this._buttonSelector).each($.proxy(function (index, button) {
1778 var $button = $(button);
1779 $button.addClass('pointer');
1780 var $objectType = $button.data('objectType');
1781 var $objectID = $button.data('objectID');
1782
1783 if (this._buttons[$objectType] === undefined) {
1784 this._buttons[$objectType] = {};
1785 }
1786
1787 this._buttons[$objectType][$objectID] = $button.click($.proxy(this._click, this));
1788 }, this));
320f4a6d 1789
f5e3a61b
AE
1790 WCF.System.Event.addListener('com.woltlab.wcf.objectWatch', 'update', $.proxy(this._updateSubscriptionStatus, this));
1791 },
1792
1793 /**
1794 * Handles a click on a subscribe button.
1795 *
1796 * @param object event
1797 */
1798 _click: function (event) {
1799 event.preventDefault();
1800 var $button = $(event.currentTarget);
320f4a6d 1801
f5e3a61b
AE
1802 this._proxy.setOption('data', {
1803 actionName: 'manageSubscription',
1804 className: 'wcf\\data\\user\\object\\watch\\UserObjectWatchAction',
1805 parameters: {
1806 objectID: $button.data('objectID'),
1807 objectType: $button.data('objectType')
1808 }
1809 });
1810 this._proxy.sendRequest();
1811 },
1812
1813 /**
1814 * Handles successful AJAX requests.
1815 *
1816 * @param object data
1817 * @param string textStatus
1818 * @param jQuery jqXHR
1819 */
1820 _success: function (data, textStatus, jqXHR) {
1821 if (data.actionName === 'manageSubscription') {
1822 if (this._dialog === null) {
1823 this._dialog = $('<div>' + data.returnValues.template + '</div>').hide().appendTo(document.body);
1824 this._dialog.wcfDialog({
1825 title: WCF.Language.get('wcf.user.objectWatch.manageSubscription')
1826 });
320f4a6d
MW
1827 }
1828 else {
f5e3a61b
AE
1829 this._dialog.html(data.returnValues.template);
1830 this._dialog.wcfDialog('open');
1831 }
1832
1833 // bind event listener
1834 this._dialog.find('.formSubmit > .jsButtonSave').data('objectID', data.returnValues.objectID).data('objectType', data.returnValues.objectType).click($.proxy(this._save, this));
1835 var $enableNotification = this._dialog.find('input[name=enableNotification]').disable();
1836
1837 // toggle subscription
1838 this._dialog.find('input[name=subscribe]').change(function (event) {
1839 var $input = $(event.currentTarget);
1840 if ($input.val() == 1) {
1841 $enableNotification.enable();
1842 }
1843 else {
1844 $enableNotification.disable();
1845 }
1846 });
1847
1848 // setup
1849 var $selectedOption = this._dialog.find('input[name=subscribe]:checked');
1850 if ($selectedOption.length && $selectedOption.val() == 1) {
1851 $enableNotification.enable();
320f4a6d 1852 }
320f4a6d 1853 }
f5e3a61b
AE
1854 else if (data.actionName === 'saveSubscription' && this._dialog.is(':visible')) {
1855 this._dialog.wcfDialog('close');
1856
1857 this._updateSubscriptionStatus({
1858 isSubscribed: data.returnValues.subscribe,
1859 objectID: data.returnValues.objectID
1860 });
1861
1862
1863 // show notification
1864 if (this._notification === null) {
1865 this._notification = new WCF.System.Notification(WCF.Language.get('wcf.global.success.edit'));
1866 }
1867
1868 this._notification.show();
320f4a6d 1869 }
f5e3a61b
AE
1870 },
1871
1872 /**
1873 * Saves the subscription.
1874 *
1875 * @param object event
1876 */
1877 _save: function (event) {
1878 var $button = this._buttons[$(event.currentTarget).data('objectType')][$(event.currentTarget).data('objectID')];
1879 var $subscribe = this._dialog.find('input[name=subscribe]:checked').val();
1880 var $enableNotification = (this._dialog.find('input[name=enableNotification]').is(':checked')) ? 1 : 0;
320f4a6d 1881
f5e3a61b
AE
1882 this._proxy.setOption('data', {
1883 actionName: 'saveSubscription',
1884 className: 'wcf\\data\\user\\object\\watch\\UserObjectWatchAction',
1885 parameters: {
1886 enableNotification: $enableNotification,
1887 objectID: $button.data('objectID'),
1888 objectType: $button.data('objectType'),
1889 subscribe: $subscribe
1890 }
1891 });
1892 this._proxy.sendRequest();
1893 },
1894
1895 /**
1896 * Updates subscription status and icon.
1897 *
1898 * @param object data
1899 */
1900 _updateSubscriptionStatus: function (data) {
1901 var $button = $(this._buttonSelector + '[data-object-id=' + data.objectID + ']');
1902 var $icon = $button.children('.icon');
1903 if (data.isSubscribed) {
0caf2e88 1904 $icon[0].querySelector("fa-icon").setIcon("bookmark", true);
f5e3a61b 1905 $button.data('isSubscribed', true);
93667537 1906 $button.addClass('active');
74e11e6d
MW
1907 }
1908 else {
f5e3a61b
AE
1909 if ($button.data('removeOnUnsubscribe')) {
1910 $button.parent().remove();
1911 }
1912 else {
0caf2e88 1913 $icon[0].querySelector("fa-icon").setIcon("bookmark");
f5e3a61b 1914 $button.data('isSubscribed', false);
93667537 1915 $button.removeClass('active');
f5e3a61b
AE
1916 }
1917
1918 if (this._reloadOnUnsubscribe) {
1919 window.location.reload();
1920 return;
1921 }
74e11e6d 1922 }
ff7bcda0 1923
f5e3a61b
AE
1924 WCF.System.Event.fireEvent('com.woltlab.wcf.objectWatch', 'updatedSubscription', data);
1925 }
1926 });
1927}
1928else {
1929 WCF.User.ObjectWatch.Subscribe = Class.extend({
1930 _buttonSelector: "",
1931 _buttons: {},
1932 _dialog: {},
1933 _notification: {},
1934 _reloadOnUnsubscribe: false,
1935 init: function() {},
1936 _click: function() {},
1937 _success: function() {},
1938 _save: function() {},
1939 _updateSubscriptionStatus: function() {}
1940 });
1941}