4 * User-related classes.
6 * @author Alexander Ebert
7 * @copyright 2001-2014 WoltLab GmbH
8 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
14 * @param boolean isQuickLogin
16 WCF
.User
.Login
= Class
.extend({
21 _loginSubmitButton
: null,
30 * password input container
33 _passwordContainer
: null,
42 * cookie input container
45 _useCookiesContainer
: null,
48 * Initializes the user login
50 * @param boolean isQuickLogin
52 init: function(isQuickLogin
) {
53 this._loginSubmitButton
= $('#loginSubmitButton');
54 this._password
= $('#password'),
55 this._passwordContainer
= this._password
.parents('dl');
56 this._useCookies
= $('#useCookies');
57 this._useCookiesContainer
= this._useCookies
.parents('dl');
59 var $loginForm
= $('#loginForm');
60 $loginForm
.find('input[name=action]').change($.proxy(this._change
, this));
63 WCF
.User
.QuickLogin
.init();
68 * Handle toggle between login and register.
72 _change: function(event
) {
73 if ($(event
.currentTarget
).val() === 'register') {
74 this._setState(false, WCF
.Language
.get('wcf.user.button.register'));
77 this._setState(true, WCF
.Language
.get('wcf.user.button.login'));
84 * @param boolean enable
85 * @param string buttonTitle
87 _setState: function(enable
, buttonTitle
) {
89 this._password
.enable();
90 this._passwordContainer
.removeClass('disabled');
91 this._useCookies
.enable();
92 this._useCookiesContainer
.removeClass('disabled');
95 this._password
.disable();
96 this._passwordContainer
.addClass('disabled');
97 this._useCookies
.disable();
98 this._useCookiesContainer
.addClass('disabled');
101 this._loginSubmitButton
.val(buttonTitle
);
106 * Namespace for User Panel classes.
108 WCF
.User
.Panel
= { };
111 * Abstract implementation for user panel items providing an interactive dropdown.
113 * @param jQuery triggerElement
114 * @param string identifier
115 * @param object options
117 WCF
.User
.Panel
.Abstract
= Class
.extend({
125 * interactive dropdown instance
126 * @var WCF.Dropdown.Interactive.Instance
131 * dropdown identifier
137 * true if data should be loaded using an AJAX request
143 * header icon to mark all items belonging to this user panel item as read
146 _markAllAsReadLink
: null,
149 * list of options required to dropdown initialization and UI features
156 * @var WCF.Action.Proxy
164 _triggerElement
: null,
167 * Initializes the WCF.User.Panel.Abstract class.
169 * @param jQuery triggerElement
170 * @param string identifier
171 * @param object options
173 init: function(triggerElement
, identifier
, options
) {
174 this._dropdown
= null;
175 this._identifier
= identifier
;
176 this._triggerElement
= triggerElement
;
177 this._options
= options
;
179 this._proxy
= new WCF
.Action
.Proxy({
180 showLoadingOverlay
: false,
181 success
: $.proxy(this._success
, this)
184 this._triggerElement
.click($.proxy(this.toggle
, this));
185 if (this._options
.showAllLink
) {
186 this._triggerElement
.dblclick($.proxy(this._dblClick
, this));
189 var $badge
= this._triggerElement
.find('span.badge');
191 this._badge
= $badge
;
196 * Toggles the interactive dropdown.
198 * @param object event
201 toggle: function(event
) {
202 event
.preventDefault();
204 if (this._dropdown
=== null) {
205 this._dropdown
= this._initDropdown();
208 if (this._dropdown
.toggle()) {
209 if (!this._loadData
) {
210 // check if there are outstanding items but there are no outstanding ones in the current list
211 if (this._badge
!== null) {
212 var $count
= parseInt(this._badge
.text()) || 0;
213 if ($count
&& !this._dropdown
.getItemList().children('.interactiveDropdownItemOutstanding').length
) {
214 this._loadData
= true;
219 if (this._loadData
) {
220 this._loadData
= false;
229 * Forward to original URL by double clicking the trigger element.
231 * @param object event
234 _dblClick: function(event
) {
235 event
.preventDefault();
237 window
.location
= this._options
.showAllLink
;
243 * Initializes the dropdown on first usage.
245 * @return WCF.Dropdown.Interactive.Instance
247 _initDropdown: function() {
248 var $dropdown
= WCF
.Dropdown
.Interactive
.Handler
.create(this._triggerElement
, this._identifier
, this._options
);
249 $('<li class="loading"><span class="icon icon24 fa-spinner" /> <span>' + WCF
.Language
.get('wcf.global.loading') + '</span></li>').appendTo($dropdown
.getItemList());
255 * Loads item list data via AJAX.
258 // override this in your own implementation to fetch display data
262 * Handles successful AJAX requests.
266 _success: function(data
) {
267 if (data
.returnValues
.template
!== undefined) {
268 var $itemList
= this._dropdown
.getItemList().empty();
269 $(data
.returnValues
.template
).appendTo($itemList
);
271 if (!$itemList
.children().length
) {
272 $('<li class="noItems">' + this._options
.noItems
+ '</li>').appendTo($itemList
);
275 if (this._options
.enableMarkAsRead
) {
276 var $outstandingItems
= this._dropdown
.getItemList().children('.interactiveDropdownItemOutstanding');
277 if (this._markAllAsReadLink
=== null && $outstandingItems
.length
&& this._options
.markAllAsReadConfirmMessage
) {
278 var $button
= this._markAllAsReadLink
= $('<li class="interactiveDropdownItemMarkAllAsRead"><a href="#" title="' + WCF
.Language
.get('wcf.user.panel.markAllAsRead') + '" class="jsTooltip"><span class="icon icon16 fa-check" /></a></li>').appendTo(this._dropdown
.getLinkList());
279 $button
.click((function(event
) {
280 this._dropdown
.close();
282 WCF
.System
.Confirmation
.show(this._options
.markAllAsReadConfirmMessage
, (function(action
) {
283 if (action
=== 'confirm') {
284 this._markAllAsRead();
292 $outstandingItems
.each((function(index
, item
) {
293 var $item
= $(item
).addClass('interactiveDropdownItemOutstandingIcon');
294 var $objectID
= $item
.data('objectID');
296 var $button
= $('<div class="interactiveDropdownItemMarkAsRead"><a href="#" title="' + WCF
.Language
.get('wcf.user.panel.markAsRead') + '" class="jsTooltip"><span class="icon icon16 fa-check" /></a></div>').appendTo($item
);
297 $button
.click((function(event
) {
298 this._markAsRead(event
, $objectID
);
305 this._dropdown
.getItemList().children().each(function(index
, item
) {
307 var $link
= $item
.data('link');
310 if ($.browser
.msie
) {
311 $item
.click(function(event
) {
312 if (event
.target
.tagName
!== 'A') {
313 window
.location
= $link
;
320 $item
.addClass('interactiveDropdownItemShadow');
321 $('<a href="' + $link
+ '" class="interactiveDropdownItemShadowLink" />').appendTo($item
);
324 if ($item
.data('linkReplaceAll')) {
325 $item
.find('> .box32 a:not(.userLink)').prop('href', $link
);
330 this._dropdown
.rebuildScrollbar();
333 if (data
.returnValues
.totalCount
!== undefined) {
334 this.updateBadge(data
.returnValues
.totalCount
);
337 if (this._options
.enableMarkAsRead
) {
338 if (data
.returnValues
.markAsRead
) {
339 var $item
= this._dropdown
.getItemList().children('li[data-object-id=' + data
.returnValues
.markAsRead
+ ']');
341 $item
.removeClass('interactiveDropdownItemOutstanding').data('isRead', true);
342 $item
.children('.interactiveDropdownItemMarkAsRead').remove();
345 else if (data
.returnValues
.markAllAsRead
) {
353 * Marks an item as read.
355 * @param object event
356 * @param integer objectID
358 _markAsRead: function(event
, objectID
) {
359 // override this in your own implementation to mark an item as read
363 * Marks all items as read.
365 _markAllAsRead: function() {
366 // override this in your own implementation to mark all items as read
370 * Updates the badge's count or removes it if count reaches zero. Passing a negative number is undefined.
372 * @param integer count
374 updateBadge: function(count
) {
375 count
= parseInt(count
) || 0;
378 if (this._badge
=== null) {
379 this._badge
= $('<span class="badge badgeInverse" />').appendTo(this._triggerElement
.children('a'));
380 this._badge
.before(' ');
383 this._badge
.text(count
);
385 else if (this._badge
!== null) {
386 this._badge
.remove();
389 if (this._options
.enableMarkAsRead
) {
390 if (!count
&& this._markAllAsReadLink
!== null) {
391 this._markAllAsReadLink
.remove();
392 this._markAllAsReadLink
= null;
398 * Resets the dropdown's inner item list.
400 resetItems: function() {
401 this._dropdown
.resetItems();
406 * User Panel implementation for user notifications.
408 * @see WCF.User.Panel.Abstract
410 WCF
.User
.Panel
.Notification
= WCF
.User
.Panel
.Abstract
.extend({
418 * @see WCF.User.Panel.Abstract.init()
420 init: function(options
) {
421 options
.enableMarkAsRead
= true;
423 this._super($('#userNotifications'), 'userNotifications', options
);
426 this._favico
= new Favico({
431 if (this._badge
!== null) {
432 var $count
= parseInt(this._badge
.text()) || 0;
433 this._favico
.badge($count
);
437 console
.debug("[WCF.User.Panel.Notification] Failed to initialized Favico: " + e
.message
);
440 WCF
.System
.PushNotification
.addCallback('userNotificationCount', $.proxy(this.updateUserNotificationCount
, this));
444 * @see WCF.User.Panel.Abstract._initDropdown()
446 _initDropdown: function() {
447 var $dropdown
= this._super();
449 $('<li><a href="' + this._options
.settingsLink
+ '" title="' + WCF
.Language
.get('wcf.user.panel.settings') + '" class="jsTooltip"><span class="icon icon16 fa-cog" /></a></li>').appendTo($dropdown
.getLinkList());
455 * @see WCF.User.Panel.Abstract._load()
458 this._proxy
.setOption('data', {
459 actionName
: 'getOutstandingNotifications',
460 className
: 'wcf\\data\\user\\notification\\UserNotificationAction'
462 this._proxy
.sendRequest();
466 * @see WCF.User.Panel.Abstract._markAsRead()
468 _markAsRead: function(event
, objectID
) {
469 this._proxy
.setOption('data', {
470 actionName
: 'markAsConfirmed',
471 className
: 'wcf\\data\\user\\notification\\UserNotificationAction',
472 objectIDs
: [ objectID
]
474 this._proxy
.sendRequest();
478 * @see WCF.User.Panel.Abstract._markAllAsRead()
480 _markAllAsRead: function(event
) {
481 this._proxy
.setOption('data', {
482 actionName
: 'markAllAsConfirmed',
483 className
: 'wcf\\data\\user\\notification\\UserNotificationAction'
485 this._proxy
.sendRequest();
489 * @see WCF.User.Panel.Abstract.resetItems()
491 resetItems: function() {
494 if (this._markAllAsReadLink
) {
495 this._markAllAsReadLink
.remove();
496 this._markAllAsReadLink
= null;
501 * @see WCF.User.Panel.Abstract.updateBadge()
503 updateBadge: function(count
) {
504 count
= parseInt(count
) || 0;
506 if (this._favico
!== null) {
507 this._favico
.badge(count
);
514 * Updates the badge counter and resets the dropdown's item list.
516 * @param integer count
518 updateUserNotificationCount: function(count
) {
519 if (this._dropdown
!== null) {
520 this._dropdown
.resetItems();
523 this.updateBadge(count
);
530 WCF
.User
.QuickLogin
= {
538 * login message container
544 * Initializes the quick login box
547 $('.loginLink').click($.proxy(this._render
, this));
549 // prepend protocol and hostname
550 $('#loginForm input[name=url]').val(function(index
, value
) {
551 return window
.location
.protocol
+ '//' + window
.location
.host
+ value
;
556 * Displays the quick login box with a info message
558 * @param string message
560 show: function(message
) {
562 if (this._loginMessage
=== null) {
563 this._loginMessage
= $('<p class="info" />').hide().prependTo($('#loginForm > form'));
566 this._loginMessage
.show().text(message
);
568 else if (this._loginMessage
!== null) {
569 this._loginMessage
.hide();
578 * @param jQuery.Event event
580 _render: function(event
) {
581 if (event
!== undefined) {
582 event
.preventDefault();
585 if (this._dialog
=== null) {
586 this._dialog
= $('#loginForm').wcfDialog({
587 title
: WCF
.Language
.get('wcf.user.login')
589 this._dialog
.find('#username').focus();
592 this._dialog
.wcfDialog('open');
598 * UserProfile namespace
600 WCF
.User
.Profile
= {};
603 * Shows the activity point list for users.
605 WCF
.User
.Profile
.ActivityPointList
= {
607 * list of cached templates
619 * initialization state
626 * @var WCF.Action.Proxy
631 * Initializes the WCF.User.Profile.ActivityPointList class.
640 this._proxy
= new WCF
.Action
.Proxy({
641 success
: $.proxy(this._success
, this)
646 WCF
.DOMNodeInsertedHandler
.addCallback('WCF.User.Profile.ActivityPointList', $.proxy(this._init
, this));
648 this._didInit
= true;
652 * Initializes display for activity points.
655 $('.activityPointsDisplay').removeClass('activityPointsDisplay').click($.proxy(this._click
, this));
659 * Shows or loads the activity point for selected user.
661 * @param object event
663 _click: function(event
) {
664 event
.preventDefault();
665 var $userID
= $(event
.currentTarget
).data('userID');
667 if (this._cache
[$userID
] === undefined) {
668 this._proxy
.setOption('data', {
669 actionName
: 'getDetailedActivityPointList',
670 className
: 'wcf\\data\\user\\UserProfileAction',
671 objectIDs
: [ $userID
]
673 this._proxy
.sendRequest();
681 * Displays activity points for given user.
683 * @param integer userID
685 _show: function(userID
) {
686 if (this._dialog
=== null) {
687 this._dialog
= $('<div>' + this._cache
[userID
] + '</div>').hide().appendTo(document
.body
);
688 this._dialog
.wcfDialog({
689 title
: WCF
.Language
.get('wcf.user.activityPoint')
693 this._dialog
.html(this._cache
[userID
]);
694 this._dialog
.wcfDialog('open');
699 * Handles successful AJAX requests.
702 * @param string textStatus
703 * @param jQuery jqXHR
705 _success: function(data
, textStatus
, jqXHR
) {
706 this._cache
[data
.returnValues
.userID
] = data
.returnValues
.template
;
707 this._show(data
.returnValues
.userID
);
712 * Provides methods to follow an user.
714 * @param integer userID
715 * @param boolean following
717 WCF
.User
.Profile
.Follow
= Class
.extend({
725 * true if following current user
731 * action proxy object
732 * @var WCF.Action.Proxy
743 * Creates a new follow object.
745 * @param integer userID
746 * @param boolean following
748 init: function (userID
, following
) {
749 this._following
= following
;
750 this._userID
= userID
;
751 this._proxy
= new WCF
.Action
.Proxy({
752 success
: $.proxy(this._success
, this)
755 this._createButton();
760 * Creates the (un-)follow button
762 _createButton: function () {
763 this._button
= $('<li id="followUser"><a href="#" class="button jsTooltip" title="'+WCF
.Language
.get('wcf.user.button.'+(this._following
? 'un' : '')+'follow')+'"><span class="icon icon16 icon-plus"></span> <span class="invisible">'+WCF
.Language
.get('wcf.user.button.'+(this._following
? 'un' : '')+'follow')+'</span></a></li>').prependTo($('#profileButtonContainer'));
764 this._button
.click($.proxy(this._execute
, this));
768 * Follows or unfollows an user.
770 _execute: function (event
) {
771 event
.preventDefault();
772 var $actionName
= (this._following
) ? 'unfollow' : 'follow';
773 this._proxy
.setOption('data', {
774 'actionName': $actionName
,
775 'className': 'wcf\\data\\user\\follow\\UserFollowAction',
782 this._proxy
.sendRequest();
786 * Displays current follow state.
788 _showButton: function () {
789 if (this._following
) {
790 this._button
.find('.button').data('tooltip', WCF
.Language
.get('wcf.user.button.unfollow')).addClass('active').children('.icon').removeClass('icon-plus').addClass('icon-minus');
793 this._button
.find('.button').data('tooltip', WCF
.Language
.get('wcf.user.button.follow')).removeClass('active').children('.icon').removeClass('icon-minus').addClass('icon-plus');
798 * Update object state on success.
801 * @param string textStatus
802 * @param jQuery jqXHR
804 _success: function (data
, textStatus
, jqXHR
) {
805 this._following
= data
.returnValues
.following
;
808 var $notification
= new WCF
.System
.Notification();
809 $notification
.show();
814 * Provides methods to manage ignored users.
816 * @param integer userID
817 * @param boolean isIgnoredUser
819 WCF
.User
.Profile
.IgnoreUser
= Class
.extend({
830 _isIgnoredUser
: false,
834 * @var WCF.Action.Proxy
845 * Initializes methods to manage an ignored user.
847 * @param integer userID
848 * @param boolean isIgnoredUser
850 init: function(userID
, isIgnoredUser
) {
851 this._userID
= userID
;
852 this._isIgnoredUser
= isIgnoredUser
;
855 this._proxy
= new WCF
.Action
.Proxy({
856 success
: $.proxy(this._success
, this)
860 this._updateButton();
861 this._button
.click($.proxy(this._click
, this));
865 * Handle clicks, might cause 'ignore' or 'unignore' to be triggered.
867 _click: function(event
) {
868 event
.preventDefault();
869 var $action
= (this._isIgnoredUser
) ? 'unignore' : 'ignore';
871 this._proxy
.setOption('data', {
873 className
: 'wcf\\data\\user\\ignore\\UserIgnoreAction',
876 ignoreUserID
: this._userID
881 this._proxy
.sendRequest();
885 * Updates button label and function upon successful request.
888 * @param string textStatus
889 * @param jQuery jqXHR
891 _success: function(data
, textStatus
, jqXHR
) {
892 this._isIgnoredUser
= data
.returnValues
.isIgnoredUser
;
893 this._updateButton();
895 var $notification
= new WCF
.System
.Notification();
896 $notification
.show();
900 * Updates button label and inserts it if not exists.
902 _updateButton: function() {
903 if (this._button
=== null) {
904 this._button
= $('<li id="ignoreUser"><a href="#" class="button jsTooltip" title="'+WCF
.Language
.get('wcf.user.button.'+(this._isIgnoredUser
? 'un' : '')+'ignore')+'"><span class="icon icon16 icon-ban-circle"></span> <span class="invisible">'+WCF
.Language
.get('wcf.user.button.'+(this._isIgnoredUser
? 'un' : '')+'ignore')+'</span></a></li>').prependTo($('#profileButtonContainer'));
907 this._button
.find('.button').data('tooltip', WCF
.Language
.get('wcf.user.button.' + (this._isIgnoredUser
? 'un' : '') + 'ignore'));
908 if (this._isIgnoredUser
) this._button
.find('.button').addClass('active').children('.icon').removeClass('icon-ban-circle').addClass('icon-circle-blank');
909 else this._button
.find('.button').removeClass('active').children('.icon').removeClass('icon-circle-blank').addClass('icon-ban-circle');
914 * Provides methods to load tab menu content upon request.
916 WCF
.User
.Profile
.TabMenu
= Class
.extend({
927 _profileContent
: null,
931 * @var WCF.Action.Proxy
942 * Initializes the tab menu loader.
944 * @param integer userID
946 init: function(userID
) {
947 this._profileContent
= $('#profileContent');
948 this._userID
= userID
;
950 var $activeMenuItem
= this._profileContent
.data('active');
951 var $enableProxy
= false;
953 // fetch content state
954 this._profileContent
.find('div.tabMenuContent').each($.proxy(function(index
, container
) {
955 var $containerID
= $(container
).wcfIdentify();
957 if ($activeMenuItem
=== $containerID
) {
958 this._hasContent
[$containerID
] = true;
961 this._hasContent
[$containerID
] = false;
966 // enable loader if at least one container is empty
968 this._proxy
= new WCF
.Action
.Proxy({
969 success
: $.proxy(this._success
, this)
972 this._profileContent
.on('wcftabsbeforeactivate', $.proxy(this._loadContent
, this));
974 // check which tab is selected
975 this._profileContent
.find('> nav.tabMenu > ul > li').each($.proxy(function(index
, listItem
) {
976 var $listItem
= $(listItem
);
977 if ($listItem
.hasClass('ui-state-active')) {
979 this._loadContent(null, {
980 newPanel
: $('#' + $listItem
.attr('aria-controls'))
991 * Prepares to load content once tabs are being switched.
993 * @param object event
996 _loadContent: function(event
, ui
) {
997 var $panel
= $(ui
.newPanel
);
998 var $containerID
= $panel
.attr('id');
1000 if (!this._hasContent
[$containerID
]) {
1001 this._proxy
.setOption('data', {
1002 actionName
: 'getContent',
1003 className
: 'wcf\\data\\user\\profile\\menu\\item\\UserProfileMenuItemAction',
1006 containerID
: $containerID
,
1007 menuItem
: $panel
.data('menuItem'),
1008 userID
: this._userID
1012 this._proxy
.sendRequest();
1017 * Shows previously requested content.
1019 * @param object data
1020 * @param string textStatus
1021 * @param jQuery jqXHR
1023 _success: function(data
, textStatus
, jqXHR
) {
1024 var $containerID
= data
.returnValues
.containerID
;
1025 this._hasContent
[$containerID
] = true;
1028 var $content
= this._profileContent
.find('#' + $containerID
);
1029 $('<div>' + data
.returnValues
.template
+ '</div>').hide().appendTo($content
);
1032 $content
.children('div').wcfBlindIn();
1037 * User profile inline editor.
1039 * @param integer userID
1040 * @param boolean editOnInit
1042 WCF
.User
.Profile
.Editor
= Class
.extend({
1050 * list of interface buttons
1056 * cached tab content
1059 _cachedTemplate
: '',
1063 * @var WCF.Action.Proxy
1080 * Initializes the WCF.User.Profile.Editor object.
1082 * @param integer userID
1083 * @param boolean editOnInit
1085 init: function(userID
, editOnInit
) {
1086 this._actionName
= '';
1087 this._cachedTemplate
= '';
1088 this._tab
= $('#about');
1089 this._userID
= userID
;
1090 this._proxy
= new WCF
.Action
.Proxy({
1091 success
: $.proxy(this._success
, this)
1094 this._initButtons();
1096 // begin editing on page load
1103 * Initializes interface buttons.
1105 _initButtons: function() {
1106 var $buttonContainer
= $('#profileButtonContainer');
1110 beginEdit
: $('<li><a class="button"><span class="icon icon16 icon-pencil" /> <span>' + WCF
.Language
.get('wcf.user.editProfile') + '</span></a></li>').click($.proxy(this._beginEdit
, this)).appendTo($buttonContainer
)
1117 _beginEdit: function() {
1118 this._actionName
= 'beginEdit';
1119 this._buttons
.beginEdit
.hide();
1120 $('#profileContent').wcfTabs('select', 'about');
1123 this._proxy
.setOption('data', {
1124 actionName
: 'beginEdit',
1125 className
: 'wcf\\data\\user\\UserProfileAction',
1126 objectIDs
: [ this._userID
]
1128 this._proxy
.sendRequest();
1132 * Saves input values.
1135 this._actionName
= 'save';
1138 var $regExp
= /values\[([a-zA-Z0-9._-]+)\]/;
1140 this._tab
.find('input, textarea, select').each(function(index
, element
) {
1141 var $element
= $(element
);
1144 switch ($element
.getTagName()) {
1146 var $type
= $element
.attr('type');
1148 if (($type
=== 'radio' || $type
=== 'checkbox') && !$element
.prop('checked')) {
1154 if ($element
.data('redactor')) {
1155 $value
= $element
.redactor('wutil.getText');
1160 var $name
= $element
.attr('name');
1161 if ($regExp
.test($name
)) {
1162 $values
[RegExp
.$1] = ($value
=== null) ? $element
.val() : $value
;
1166 this._proxy
.setOption('data', {
1168 className
: 'wcf\\data\\user\\UserProfileAction',
1169 objectIDs
: [ this._userID
],
1174 this._proxy
.sendRequest();
1178 * Restores back to default view.
1180 _restore: function() {
1181 this._actionName
= 'restore';
1182 this._buttons
.beginEdit
.show();
1184 this._destroyEditor();
1186 this._tab
.html(this._cachedTemplate
).children().css({ height
: 'auto' });
1190 * Handles successful AJAX requests.
1192 * @param object data
1193 * @param string textStatus
1194 * @param jQuery jqXHR
1196 _success: function(data
, textStatus
, jqXHR
) {
1197 switch (this._actionName
) {
1199 this._prepareEdit(data
);
1203 // save was successful, show parsed template
1204 if (data
.returnValues
.success
) {
1205 this._cachedTemplate
= data
.returnValues
.template
;
1209 this._prepareEdit(data
, true);
1216 * Prepares editing mode.
1218 * @param object data
1219 * @param boolean disableCache
1221 _prepareEdit: function(data
, disableCache
) {
1222 this._destroyEditor();
1226 this._tab
.html(function(index
, oldHTML
) {
1227 if (disableCache
!== true) {
1228 self
._cachedTemplate
= oldHTML
;
1231 return data
.returnValues
.template
;
1234 // block autocomplete
1235 this._tab
.find('input[type=text]').attr('autocomplete', 'off');
1237 // bind event listener
1238 this._tab
.find('.formSubmit > button[data-type=save]').click($.proxy(this._save
, this));
1239 this._tab
.find('.formSubmit > button[data-type=restore]').click($.proxy(this._restore
, this));
1240 this._tab
.find('input').keyup(function(event
) {
1241 if (event
.which
=== $.ui
.keyCode
.ENTER
) {
1244 event
.preventDefault();
1251 * Destroys all editor instances within current tab.
1253 _destroyEditor: function() {
1254 // destroy all editor instances
1255 this._tab
.find('textarea').each(function(index
, container
) {
1256 var $container
= $(container
);
1257 if ($container
.data('redactor')) {
1258 $container
.redactor('core.destroy');
1265 * Namespace for registration functions.
1267 WCF
.User
.Registration
= {};
1270 * Validates the password.
1272 * @param jQuery element
1273 * @param jQuery confirmElement
1274 * @param object options
1276 WCF
.User
.Registration
.Validation
= Class
.extend({
1290 * confirmation input element
1293 _confirmElement
: null,
1302 * list of error messages
1305 _errorMessages
: { },
1308 * list of additional options
1315 * @var WCF.Action.Proxy
1320 * Initializes the validation.
1322 * @param jQuery element
1323 * @param jQuery confirmElement
1324 * @param object options
1326 init: function(element
, confirmElement
, options
) {
1327 this._element
= element
;
1328 this._element
.blur($.proxy(this._blur
, this));
1329 this._confirmElement
= confirmElement
|| null;
1331 if (this._confirmElement
!== null) {
1332 this._confirmElement
.blur($.proxy(this._blurConfirm
, this));
1335 options
= options
|| { };
1336 this._setOptions(options
);
1338 this._proxy
= new WCF
.Action
.Proxy({
1339 success
: $.proxy(this._success
, this),
1340 showLoadingOverlay
: false
1343 this._setErrorMessages();
1347 * Sets additional options
1349 _setOptions: function(options
) { },
1352 * Sets error messages.
1354 _setErrorMessages: function() {
1355 this._errorMessages
= {
1362 * Validates once focus on input is lost.
1364 * @param object event
1366 _blur: function(event
) {
1367 var $value
= this._element
.val();
1369 return this._showError(this._element
, WCF
.Language
.get('wcf.global.form.error.empty'));
1372 if (this._confirmElement
!== null) {
1373 var $confirmValue
= this._confirmElement
.val();
1374 if ($confirmValue
!= '' && $value
!= $confirmValue
) {
1375 return this._showError(this._confirmElement
, this._errorMessages
.notEqual
);
1379 if (!this._validateOptions()) {
1383 this._proxy
.setOption('data', {
1384 actionName
: this._actionName
,
1385 className
: this._className
,
1386 parameters
: this._getParameters()
1388 this._proxy
.sendRequest();
1392 * Returns a list of parameters.
1396 _getParameters: function() {
1401 * Validates input by options.
1405 _validateOptions: function() {
1410 * Validates value once confirmation input focus is lost.
1412 * @param object event
1414 _blurConfirm: function(event
) {
1415 var $value
= this._confirmElement
.val();
1417 return this._showError(this._confirmElement
, WCF
.Language
.get('wcf.global.form.error.empty'));
1424 * Handles AJAX responses.
1426 * @param object data
1427 * @param string textStatus
1428 * @param jQuery jqXHR
1430 _success: function(data
, textStatus
, jqXHR
) {
1431 if (data
.returnValues
.isValid
) {
1432 this._showSuccess(this._element
);
1433 if (this._confirmElement
!== null && this._confirmElement
.val()) {
1434 this._showSuccess(this._confirmElement
);
1438 this._showError(this._element
, WCF
.Language
.get(this._errorMessages
.ajaxError
+ data
.returnValues
.error
));
1443 * Shows an error message.
1445 * @param jQuery element
1446 * @param string message
1448 _showError: function(element
, message
) {
1449 element
.parent().parent().addClass('formError').removeClass('formSuccess');
1451 var $innerError
= element
.parent().find('small.innerError');
1452 if (!$innerError
.length
) {
1453 $innerError
= $('<small />').addClass('innerError').insertAfter(element
);
1456 $innerError
.text(message
);
1460 * Displays a success message.
1462 * @param jQuery element
1464 _showSuccess: function(element
) {
1465 element
.parent().parent().addClass('formSuccess').removeClass('formError');
1466 element
.next('small.innerError').remove();
1471 * Username validation for registration.
1473 * @see WCF.User.Registration.Validation
1475 WCF
.User
.Registration
.Validation
.Username
= WCF
.User
.Registration
.Validation
.extend({
1477 * @see WCF.User.Registration.Validation._actionName
1479 _actionName
: 'validateUsername',
1482 * @see WCF.User.Registration.Validation._className
1484 _className
: 'wcf\\data\\user\\UserRegistrationAction',
1487 * @see WCF.User.Registration.Validation._setOptions()
1489 _setOptions: function(options
) {
1490 this._options
= $.extend(true, {
1497 * @see WCF.User.Registration.Validation._setErrorMessages()
1499 _setErrorMessages: function() {
1500 this._errorMessages
= {
1501 ajaxError
: 'wcf.user.username.error.'
1506 * @see WCF.User.Registration.Validation._validateOptions()
1508 _validateOptions: function() {
1509 var $value
= this._element
.val();
1510 if ($value
.length
< this._options
.minlength
|| $value
.length
> this._options
.maxlength
) {
1511 this._showError(this._element
, WCF
.Language
.get('wcf.user.username.error.notValid'));
1519 * @see WCF.User.Registration.Validation._getParameters()
1521 _getParameters: function() {
1523 username
: this._element
.val()
1529 * Email validation for registration.
1531 * @see WCF.User.Registration.Validation
1533 WCF
.User
.Registration
.Validation
.EmailAddress
= WCF
.User
.Registration
.Validation
.extend({
1535 * @see WCF.User.Registration.Validation._actionName
1537 _actionName
: 'validateEmailAddress',
1540 * @see WCF.User.Registration.Validation._className
1542 _className
: 'wcf\\data\\user\\UserRegistrationAction',
1545 * @see WCF.User.Registration.Validation._getParameters()
1547 _getParameters: function() {
1549 email
: this._element
.val()
1554 * @see WCF.User.Registration.Validation._setErrorMessages()
1556 _setErrorMessages: function() {
1557 this._errorMessages
= {
1558 ajaxError
: 'wcf.user.email.error.',
1559 notEqual
: WCF
.Language
.get('wcf.user.confirmEmail.error.notEqual')
1565 * Password validation for registration.
1567 * @see WCF.User.Registration.Validation
1569 WCF
.User
.Registration
.Validation
.Password
= WCF
.User
.Registration
.Validation
.extend({
1571 * @see WCF.User.Registration.Validation._actionName
1573 _actionName
: 'validatePassword',
1576 * @see WCF.User.Registration.Validation._className
1578 _className
: 'wcf\\data\\user\\UserRegistrationAction',
1581 * @see WCF.User.Registration.Validation._getParameters()
1583 _getParameters: function() {
1585 password
: this._element
.val()
1590 * @see WCF.User.Registration.Validation._setErrorMessages()
1592 _setErrorMessages: function() {
1593 this._errorMessages
= {
1594 ajaxError
: 'wcf.user.password.error.',
1595 notEqual
: WCF
.Language
.get('wcf.user.confirmPassword.error.notEqual')
1601 * Toggles input fields for lost password form.
1603 WCF
.User
.Registration
.LostPassword
= Class
.extend({
1617 * Initializes LostPassword-form class.
1620 // bind input fields
1621 this._email
= $('#emailInput');
1622 this._username
= $('#usernameInput');
1624 // bind event listener
1625 this._email
.keyup($.proxy(this._checkEmail
, this));
1626 this._username
.keyup($.proxy(this._checkUsername
, this));
1628 if ($.browser
.mozilla
&& $.browser
.touch
) {
1629 this._email
.on('input', $.proxy(this._checkEmail
, this));
1630 this._username
.on('input', $.proxy(this._checkUsername
, this));
1633 // toggle fields on init
1635 this._checkUsername();
1639 * Checks for content in email field and toggles username.
1641 _checkEmail: function() {
1642 if (this._email
.val() == '') {
1643 this._username
.enable();
1644 this._username
.parents('dl:eq(0)').removeClass('disabled');
1647 this._username
.disable();
1648 this._username
.parents('dl:eq(0)').addClass('disabled');
1653 * Checks for content in username field and toggles email.
1655 _checkUsername: function() {
1656 if (this._username
.val() == '') {
1657 this._email
.enable();
1658 this._email
.parents('dl:eq(0)').removeClass('disabled');
1661 this._email
.disable();
1662 this._email
.parents('dl:eq(0)').addClass('disabled');
1668 * Notification system for WCF.
1670 * @author Alexander Ebert
1671 * @copyright 2001-2014 WoltLab GmbH
1672 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
1674 WCF
.Notification
= { };
1677 * Handles the notification list.
1679 WCF
.Notification
.List
= Class
.extend({
1682 * @var WCF.Action.Proxy
1687 * Initializes the WCF.Notification.List object.
1690 this._proxy
= new WCF
.Action
.Proxy({
1691 success
: $.proxy(this._success
, this)
1694 // handle 'mark all as confirmed' buttons
1695 $('.contentNavigation .jsMarkAllAsConfirmed').click(function() {
1696 WCF
.System
.Confirmation
.show(WCF
.Language
.get('wcf.user.notification.markAllAsConfirmed.confirmMessage'), function(action
) {
1697 if (action
=== 'confirm') {
1698 new WCF
.Action
.Proxy({
1701 actionName
: 'markAllAsConfirmed',
1702 className
: 'wcf\\data\\user\\notification\\UserNotificationAction'
1704 success: function() { window
.location
.reload(); }
1710 // handle regular items
1711 this._convertList();
1715 * Converts the notification item list to be in sync with the notification dropdown.
1717 _convertList: function() {
1718 $('.userNotificationItemList > .notificationItem').each((function(index
, item
) {
1719 var $item
= $(item
);
1721 if (!$item
.data('isRead')) {
1722 $item
.find('a:not(.userLink)').prop('href', $item
.data('link'));
1724 var $markAsConfirmed
= $('<a href="#" class="icon icon24 fa-check notificationItemMarkAsConfirmed jsTooltip" title="' + WCF
.Language
.get('wcf.user.notification.markAsConfirmed') + '" />').appendTo($item
);
1725 $markAsConfirmed
.click($.proxy(this._markAsConfirmed
, this));
1729 WCF
.DOMNodeInsertedHandler
.execute();
1733 * Marks a single notification as confirmed.
1735 * @param object event
1737 _markAsConfirmed: function(event
) {
1738 event
.preventDefault();
1740 var $notificationID
= $(event
.currentTarget
).parents('.notificationItem:eq(0)').data('objectID');
1742 this._proxy
.setOption('data', {
1743 actionName
: 'markAsConfirmed',
1744 className
: 'wcf\\data\\user\\notification\\UserNotificationAction',
1745 objectIDs
: [ $notificationID
]
1747 this._proxy
.sendRequest();
1753 * Handles successful AJAX requests.
1755 * @param object data
1756 * @param string textStatus
1757 * @param jQuery jqXHR
1759 _success: function(data
, textStatus
, jqXHR
) {
1760 var $item
= $('.userNotificationItemList > .notificationItem[data-object-id=' + data
.returnValues
.markAsRead
+ ']');
1762 $item
.data('isRead', true);
1763 $item
.find('.newContentBadge').remove();
1764 $item
.find('.notificationItemMarkAsConfirmed').remove();
1765 $item
.removeClass('notificationUnconfirmed');
1770 * Signature preview.
1772 * @see WCF.Message.Preview
1774 WCF
.User
.SignaturePreview
= WCF
.Message
.Preview
.extend({
1776 * @see WCF.Message.Preview._handleResponse()
1778 _handleResponse: function(data
) {
1779 // get preview container
1780 var $preview
= $('#previewContainer');
1781 if (!$preview
.length
) {
1782 $preview
= $('<fieldset id="previewContainer"><legend>' + WCF
.Language
.get('wcf.global.preview') + '</legend><div></div></fieldset>').insertBefore($('#signatureContainer')).wcfFadeIn();
1785 $preview
.children('div').first().html(data
.returnValues
.message
);
1790 * Loads recent activity events once the user scrolls to the very bottom.
1792 * @param integer userID
1794 WCF
.User
.RecentActivityLoader
= Class
.extend({
1802 * true if list should be filtered by followed users
1805 _filteredByFollowedUsers
: false,
1808 * button to load next events
1815 * @var WCF.Action.Proxy
1826 * Initializes a new RecentActivityLoader object.
1828 * @param integer userID
1829 * @param boolean filteredByFollowedUsers
1831 init: function(userID
, filteredByFollowedUsers
) {
1832 this._container
= $('#recentActivities');
1833 this._filteredByFollowedUsers
= (filteredByFollowedUsers
=== true);
1834 this._userID
= userID
;
1836 if (this._userID
!== null && !this._userID
) {
1837 console
.debug("[WCF.User.RecentActivityLoader] Invalid parameter 'userID' given.");
1841 this._proxy
= new WCF
.Action
.Proxy({
1842 success
: $.proxy(this._success
, this)
1845 if (this._container
.children('li').length
) {
1846 this._loadButton
= $('<li class="recentActivitiesMore"><button class="small">' + WCF
.Language
.get('wcf.user.recentActivity.more') + '</button></li>').appendTo(this._container
);
1847 this._loadButton
= this._loadButton
.children('button').click($.proxy(this._click
, this));
1850 $('<li class="recentActivitiesMore"><small>' + WCF
.Language
.get('wcf.user.recentActivity.noMoreEntries') + '</small></li>').appendTo(this._container
);
1853 if (WCF
.User
.userID
) {
1854 $('.jsRecentActivitySwitchContext .button').click($.proxy(this._switchContext
, this));
1859 * Loads next activity events.
1861 _click: function() {
1862 this._loadButton
.enable();
1865 lastEventID
: this._container
.data('lastEventID'),
1866 lastEventTime
: this._container
.data('lastEventTime')
1869 $parameters
.userID
= this._userID
;
1871 else if (this._filteredByFollowedUsers
) {
1872 $parameters
.filteredByFollowedUsers
= 1;
1875 this._proxy
.setOption('data', {
1877 className
: 'wcf\\data\\user\\activity\\event\\UserActivityEventAction',
1878 parameters
: $parameters
1880 this._proxy
.sendRequest();
1884 * Switches recent activity context.
1886 _switchContext: function(event
) {
1887 event
.preventDefault();
1889 if (!$(event
.currentTarget
).hasClass('active')) {
1890 new WCF
.Action
.Proxy({
1893 actionName
: 'switchContext',
1894 className
: 'wcf\\data\\user\\activity\\event\\UserActivityEventAction'
1896 success: function() {
1897 window
.location
.hash
= '#dashboardBoxRecentActivity';
1898 window
.location
.reload();
1905 * Handles successful AJAX requests.
1907 * @param object data
1908 * @param string textStatus
1909 * @param jQuery jqXHR
1911 _success: function(data
, textStatus
, jqXHR
) {
1912 if (data
.returnValues
.template
) {
1913 $(data
.returnValues
.template
).insertBefore(this._loadButton
.parent());
1915 this._container
.data('lastEventTime', data
.returnValues
.lastEventTime
);
1916 this._container
.data('lastEventID', data
.returnValues
.lastEventID
);
1917 this._loadButton
.enable();
1920 $('<small>' + WCF
.Language
.get('wcf.user.recentActivity.noMoreEntries') + '</small>').appendTo(this._loadButton
.parent());
1921 this._loadButton
.remove();
1927 * Loads likes once the user scrolls to the very bottom.
1929 * @param integer userID
1931 WCF
.User
.LikeLoader
= Class
.extend({
1942 _likeType
: 'received',
1951 * button to load next events
1957 * 'no more entries' element
1960 _noMoreEntries
: null,
1964 * @var WCF.Action.Proxy
1975 * Initializes a new RecentActivityLoader object.
1977 * @param integer userID
1978 * @param boolean filteredByFollowedUsers
1980 init: function(userID
) {
1981 this._container
= $('#likeList');
1982 this._userID
= userID
;
1984 if (!this._userID
) {
1985 console
.debug("[WCF.User.RecentActivityLoader] Invalid parameter 'userID' given.");
1989 this._proxy
= new WCF
.Action
.Proxy({
1990 success
: $.proxy(this._success
, this)
1993 var $container
= $('<li class="likeListMore recentActivitiesMore"><button class="small">' + WCF
.Language
.get('wcf.like.likes.more') + '</button><small>' + WCF
.Language
.get('wcf.like.likes.noMoreEntries') + '</small></li>').appendTo(this._container
);
1994 this._loadButton
= $container
.children('button').click($.proxy(this._click
, this));
1995 this._noMoreEntries
= $container
.children('small').hide();
1997 if (this._container
.find('> li').length
== 2) {
1998 this._loadButton
.hide();
1999 this._noMoreEntries
.show();
2002 $('#likeType .button').click($.proxy(this._clickLikeType
, this));
2003 $('#likeValue .button').click($.proxy(this._clickLikeValue
, this));
2007 * Handles like type change.
2009 _clickLikeType: function(event
) {
2010 var $button
= $(event
.currentTarget
);
2011 if (this._likeType
!= $button
.data('likeType')) {
2012 this._likeType
= $button
.data('likeType');
2013 $('#likeType .button').removeClass('active');
2014 $button
.addClass('active');
2020 * Handles like value change.
2022 _clickLikeValue: function(event
) {
2023 var $button
= $(event
.currentTarget
);
2024 if (this._likeValue
!= $button
.data('likeValue')) {
2025 this._likeValue
= $button
.data('likeValue');
2026 $('#likeValue .button').removeClass('active');
2027 $button
.addClass('active');
2029 // change button labels
2030 $('#likeType > li:first-child > .button').text(WCF
.Language
.get('wcf.like.' + (this._likeValue
== -1 ? 'dis' : '') + 'likesReceived'));
2031 $('#likeType > li:last-child > .button').text(WCF
.Language
.get('wcf.like.' + (this._likeValue
== -1 ? 'dis' : '') + 'likesGiven'));
2040 _reload: function() {
2041 this._container
.find('> li:not(:first-child):not(:last-child)').remove();
2042 this._container
.data('lastLikeTime', 0);
2049 _click: function() {
2050 this._loadButton
.enable();
2053 lastLikeTime
: this._container
.data('lastLikeTime'),
2054 userID
: this._userID
,
2055 likeType
: this._likeType
,
2056 likeValue
: this._likeValue
2059 this._proxy
.setOption('data', {
2061 className
: 'wcf\\data\\like\\LikeAction',
2062 parameters
: $parameters
2064 this._proxy
.sendRequest();
2068 * Handles successful AJAX requests.
2070 * @param object data
2071 * @param string textStatus
2072 * @param jQuery jqXHR
2074 _success: function(data
, textStatus
, jqXHR
) {
2075 if (data
.returnValues
.template
) {
2076 $(data
.returnValues
.template
).insertBefore(this._loadButton
.parent());
2078 this._container
.data('lastLikeTime', data
.returnValues
.lastLikeTime
);
2079 this._noMoreEntries
.hide();
2080 this._loadButton
.show().enable();
2083 this._noMoreEntries
.show();
2084 this._loadButton
.hide();
2090 * Loads user profile previews.
2094 WCF
.User
.ProfilePreview
= WCF
.Popover
.extend({
2097 * @var WCF.Action.Proxy
2102 * list of user profiles
2108 * @see WCF.Popover.init()
2111 this._super('.userLink');
2113 this._proxy
= new WCF
.Action
.Proxy({
2114 showLoadingOverlay
: false
2117 // register instance
2118 WCF
.System
.ObjectStore
.add('WCF.User.ProfilePreview', this);
2122 * @see WCF.Popover._loadContent()
2124 _loadContent: function() {
2125 var $element
= $('#' + this._activeElementID
);
2126 var $userID
= $element
.data('userID');
2128 if (this._userProfiles
[$userID
]) {
2129 // use cached user profile
2130 this._insertContent(this._activeElementID
, this._userProfiles
[$userID
], true);
2133 this._proxy
.setOption('data', {
2134 actionName
: 'getUserProfile',
2135 className
: 'wcf\\data\\user\\UserProfileAction',
2136 objectIDs
: [ $userID
]
2139 var $elementID
= this._activeElementID
;
2141 this._proxy
.setOption('success', function(data
, textStatus
, jqXHR
) {
2142 // cache user profile
2143 self
._userProfiles
[$userID
] = data
.returnValues
.template
;
2145 // show user profile
2146 self
._insertContent($elementID
, data
.returnValues
.template
, true);
2148 this._proxy
.setOption('failure', function(data
, jqXHR
, textStatus
, errorThrown
) {
2149 // cache user profile
2150 self
._userProfiles
[$userID
] = data
.message
;
2152 // show user profile
2153 self
._insertContent($elementID
, data
.message
, true);
2157 this._proxy
.sendRequest();
2162 * Purages a cached user profile.
2164 * @param integer userID
2166 purge: function(userID
) {
2167 delete this._userProfiles
[userID
];
2169 // purge content cache
2175 * Initalizes WCF.User.Action namespace.
2177 WCF
.User
.Action
= {};
2180 * Handles user follow and unfollow links.
2182 WCF
.User
.Action
.Follow
= Class
.extend({
2184 * list with elements containing follow and unfollow buttons
2187 _containerList
: null,
2190 * CSS selector for follow buttons
2193 _followButtonSelector
: '.jsFollowButton',
2196 * id of the user that is currently being followed/unfollowed
2202 * Initializes new WCF.User.Action.Follow object.
2204 * @param array containerList
2205 * @param string followButtonSelector
2207 init: function(containerList
, followButtonSelector
) {
2208 if (!containerList
.length
) {
2211 this._containerList
= containerList
;
2213 if (followButtonSelector
) {
2214 this._followButtonSelector
= followButtonSelector
;
2218 this._proxy
= new WCF
.Action
.Proxy({
2219 success
: $.proxy(this._success
, this)
2222 // bind event listeners
2223 this._containerList
.each($.proxy(function(index
, container
) {
2224 $(container
).find(this._followButtonSelector
).click($.proxy(this._click
, this));
2229 * Handles a click on a follow or unfollow button.
2231 * @param object event
2233 _click: function(event
) {
2234 event
.preventDefault();
2235 var link
= $(event
.target
);
2236 if (!link
.is('a')) {
2237 link
= link
.closest('a');
2239 this._userID
= link
.data('objectID');
2241 this._proxy
.setOption('data', {
2242 'actionName': link
.data('following') ? 'unfollow' : 'follow',
2243 'className': 'wcf\\data\\user\\follow\\UserFollowAction',
2246 userID
: this._userID
2250 this._proxy
.sendRequest();
2254 * Handles the successful (un)following of a user.
2256 * @param object data
2257 * @param string textStatus
2258 * @param jQuery jqXHR
2260 _success: function(data
, textStatus
, jqXHR
) {
2261 this._containerList
.each($.proxy(function(index
, container
) {
2262 var button
= $(container
).find(this._followButtonSelector
).get(0);
2264 if (button
&& $(button
).data('objectID') == this._userID
) {
2267 // toogle icon title
2268 if (data
.returnValues
.following
) {
2269 button
.data('tooltip', WCF
.Language
.get('wcf.user.button.unfollow')).children('.icon').removeClass('icon-plus').addClass('icon-minus');
2272 button
.data('tooltip', WCF
.Language
.get('wcf.user.button.follow')).children('.icon').removeClass('icon-minus').addClass('icon-plus');
2275 button
.data('following', data
.returnValues
.following
);
2281 var $notification
= new WCF
.System
.Notification();
2282 $notification
.show();
2284 // force rebuilding of popover cache
2286 WCF
.System
.ObjectStore
.invoke('WCF.User.ProfilePreview', function(profilePreview
) {
2287 profilePreview
.purge(self
._userID
);
2293 * Handles user ignore and unignore links.
2295 WCF
.User
.Action
.Ignore
= Class
.extend({
2297 * list with elements containing ignore and unignore buttons
2300 _containerList
: null,
2303 * CSS selector for ignore buttons
2306 _ignoreButtonSelector
: '.jsIgnoreButton',
2309 * id of the user that is currently being ignored/unignored
2315 * Initializes new WCF.User.Action.Ignore object.
2317 * @param array containerList
2318 * @param string ignoreButtonSelector
2320 init: function(containerList
, ignoreButtonSelector
) {
2321 if (!containerList
.length
) {
2324 this._containerList
= containerList
;
2326 if (ignoreButtonSelector
) {
2327 this._ignoreButtonSelector
= ignoreButtonSelector
;
2331 this._proxy
= new WCF
.Action
.Proxy({
2332 success
: $.proxy(this._success
, this)
2335 // bind event listeners
2336 this._containerList
.each($.proxy(function(index
, container
) {
2337 $(container
).find(this._ignoreButtonSelector
).click($.proxy(this._click
, this));
2342 * Handles a click on a ignore or unignore button.
2344 * @param object event
2346 _click: function(event
) {
2347 event
.preventDefault();
2348 var link
= $(event
.target
);
2349 if (!link
.is('a')) {
2350 link
= link
.closest('a');
2352 this._userID
= link
.data('objectID');
2354 this._proxy
.setOption('data', {
2355 'actionName': link
.data('ignored') ? 'unignore' : 'ignore',
2356 'className': 'wcf\\data\\user\\ignore\\UserIgnoreAction',
2359 ignoreUserID
: this._userID
2363 this._proxy
.sendRequest();
2367 * Handles the successful (un)ignoring of a user.
2369 * @param object data
2370 * @param string textStatus
2371 * @param jQuery jqXHR
2373 _success: function(data
, textStatus
, jqXHR
) {
2374 this._containerList
.each($.proxy(function(index
, container
) {
2375 var button
= $(container
).find(this._ignoreButtonSelector
).get(0);
2377 if (button
&& $(button
).data('objectID') == this._userID
) {
2380 // toogle icon title
2381 if (data
.returnValues
.isIgnoredUser
) {
2382 button
.data('tooltip', WCF
.Language
.get('wcf.user.button.unignore')).children('.icon').removeClass('icon-ban-circle').addClass('icon-circle-blank');
2385 button
.data('tooltip', WCF
.Language
.get('wcf.user.button.ignore')).children('.icon').removeClass('icon-circle-blank').addClass('icon-ban-circle');
2388 button
.data('ignored', data
.returnValues
.isIgnoredUser
);
2394 var $notification
= new WCF
.System
.Notification();
2395 $notification
.show();
2397 // force rebuilding of popover cache
2399 WCF
.System
.ObjectStore
.invoke('WCF.User.ProfilePreview', function(profilePreview
) {
2400 profilePreview
.purge(self
._userID
);
2406 * Namespace for avatar functions.
2408 WCF
.User
.Avatar
= {};
2411 * Handles cropping an avatar.
2413 WCF
.User
.Avatar
.Crop
= Class
.extend({
2415 * current crop setting in x-direction
2421 * current crop setting in y-direction
2427 * avatar crop dialog
2433 * action proxy to send the crop AJAX requests
2434 * @var WCF.Action.Proxy
2439 * maximum size of thumbnails
2442 MAX_THUMBNAIL_SIZE
: 128,
2445 * Creates a new instance of WCF.User.Avatar.Crop.
2447 * @param integer avatarID
2449 init: function(avatarID
) {
2450 this._avatarID
= avatarID
;
2455 this._dialog
= null;
2457 // check if object already had been initialized
2459 this._proxy
= new WCF
.Action
.Proxy({
2460 success
: $.proxy(this._success
, this)
2464 $('.userAvatarCrop').click($.proxy(this._showCropDialog
, this));
2468 * Destroys the avatar crop interface.
2470 destroy: function() {
2471 this._dialog
.remove();
2475 * Sends AJAX request to crop avatar.
2477 * @param object event
2479 _crop: function(event
) {
2480 this._proxy
.setOption('data', {
2481 actionName
: 'cropAvatar',
2482 className
: 'wcf\\data\\user\\avatar\\UserAvatarAction',
2483 objectIDs
: [ this._avatarID
],
2489 this._proxy
.sendRequest();
2493 * Initializes the dialog after a successful 'getCropDialog' request.
2495 * @param object data
2497 _getCropDialog: function(data
) {
2498 if (!this._dialog
) {
2499 this._dialog
= $('<div />').hide().appendTo(document
.body
);
2500 this._dialog
.wcfDialog({
2501 title
: WCF
.Language
.get('wcf.user.avatar.type.custom.crop')
2505 this._dialog
.html(data
.returnValues
.template
);
2506 this._dialog
.find('button[data-type="save"]').click($.proxy(this._crop
, this));
2508 this._cropX
= data
.returnValues
.cropX
;
2509 this._cropY
= data
.returnValues
.cropY
;
2511 var $image
= $('#userAvatarCropSelection > img');
2512 $('#userAvatarCropSelection').css({
2513 height
: $image
.height() + 'px',
2514 width
: $image
.width() + 'px'
2516 $('#userAvatarCropOverlaySelection').css({
2517 'background-image': 'url(' + $image
.attr('src') + ')',
2518 'background-position': -this._cropX
+ 'px ' + -this._cropY
+ 'px',
2519 'left': this._cropX
+ 'px',
2520 'top': this._cropY
+ 'px'
2522 containment
: 'parent',
2523 drag
: $.proxy(this._updateSelection
, this),
2524 stop
: $.proxy(this._updateSelection
, this)
2527 this._dialog
.find('button[data-type="save"]').click($.proxy(this._save
, this));
2529 this._dialog
.wcfDialog('render');
2533 * Shows the cropping dialog.
2535 _showCropDialog: function() {
2536 if (!this._dialog
) {
2537 this._proxy
.setOption('data', {
2538 actionName
: 'getCropDialog',
2539 className
: 'wcf\\data\\user\\avatar\\UserAvatarAction',
2540 objectIDs
: [ this._avatarID
]
2542 this._proxy
.sendRequest();
2545 this._dialog
.wcfDialog('open');
2550 * Handles successful AJAX request.
2552 * @param object data
2553 * @param string textStatus
2554 * @param jQuery jqXHR
2556 _success: function(data
, textStatus
, jqXHR
) {
2557 switch (data
.actionName
) {
2558 case 'getCropDialog':
2559 this._getCropDialog(data
);
2563 $('#avatarUpload > dt > img').replaceWith($('<img src="' + data
.returnValues
.url
+ '" alt="" class="userAvatarCrop jsTooltip" title="' + WCF
.Language
.get('wcf.user.avatar.type.custom.crop') + '" />').css({
2566 }).click($.proxy(this._showCropDialog
, this)));
2568 WCF
.DOMNodeInsertedHandler
.execute();
2570 this._dialog
.wcfDialog('close');
2572 var $notification
= new WCF
.System
.Notification();
2573 $notification
.show();
2579 * Updates the current crop selection if the selection overlay is dragged.
2581 * @param object event
2584 _updateSelection: function(event
, ui
) {
2585 this._cropX
= ui
.position
.left
;
2586 this._cropY
= ui
.position
.top
;
2588 $('#userAvatarCropOverlaySelection').css({
2589 'background-position': -ui
.position
.left
+ 'px ' + -ui
.position
.top
+ 'px'
2595 * Avatar upload function
2599 WCF
.User
.Avatar
.Upload
= WCF
.Upload
.extend({
2601 * handles cropping the avatar
2602 * @var WCF.User.Avatar.Crop
2607 * user id of avatar owner
2613 * Initalizes a new WCF.User.Avatar.Upload object.
2615 * @param integer userID
2616 * @param WCF.User.Avatar.Crop avatarCrop
2618 init: function(userID
, avatarCrop
) {
2619 this._super($('#avatarUpload > dd > div'), undefined, 'wcf\\data\\user\\avatar\\UserAvatarAction');
2620 this._userID
= userID
|| 0;
2621 this._avatarCrop
= avatarCrop
;
2623 $('#avatarForm input[type=radio]').change(function() {
2624 if ($(this).val() == 'custom') {
2625 $('#avatarUpload > dd > div').show();
2628 $('#avatarUpload > dd > div').hide();
2631 if (!$('#avatarForm input[type=radio][value=custom]:checked').length
) {
2632 $('#avatarUpload > dd > div').hide();
2637 * @see WCF.Upload._initFile()
2639 _initFile: function(file
) {
2640 return $('#avatarUpload > dt > img');
2644 * @see WCF.Upload._success()
2646 _success: function(uploadID
, data
) {
2647 if (data
.returnValues
.url
) {
2648 this._updateImage(data
.returnValues
.url
, data
.returnValues
.canCrop
);
2650 if (data
.returnValues
.canCrop
) {
2651 if (!this._avatarCrop
) {
2652 this._avatarCrop
= new WCF
.User
.Avatar
.Crop(data
.returnValues
.avatarID
);
2655 this._avatarCrop
.init(data
.returnValues
.avatarID
);
2658 else if (this._avatarCrop
) {
2659 this._avatarCrop
.destroy();
2660 this._avatarCrop
= null;
2664 $('#avatarUpload > dd > .innerError').remove();
2666 // show success message
2667 var $notification
= new WCF
.System
.Notification(WCF
.Language
.get('wcf.user.avatar.upload.success'));
2668 $notification
.show();
2670 else if (data
.returnValues
.errorType
) {
2672 this._getInnerErrorElement().text(WCF
.Language
.get('wcf.user.avatar.upload.error.' + data
.returnValues
.errorType
));
2677 * Updates the displayed avatar image.
2680 * @param boolean canCrop
2682 _updateImage: function(url
, canCrop
) {
2683 $('#avatarUpload > dt > img').remove();
2684 var $image
= $('<img src="' + url
+ '" alt="" />').css({
2686 'max-height': '96px',
2687 'max-width': '96px',
2691 $image
.addClass('userAvatarCrop').addClass('jsTooltip');
2692 $image
.attr('title', WCF
.Language
.get('wcf.user.avatar.type.custom.crop'));
2695 $('#avatarUpload > dt').prepend($image
);
2697 WCF
.DOMNodeInsertedHandler
.execute();
2701 * Returns the inner error element.
2705 _getInnerErrorElement: function() {
2706 var $span
= $('#avatarUpload > dd > .innerError');
2707 if (!$span
.length
) {
2708 $span
= $('<small class="innerError"></span>');
2709 $('#avatarUpload > dd').append($span
);
2716 * @see WCF.Upload._getParameters()
2718 _getParameters: function() {
2720 userID
: this._userID
2726 * Generic implementation for grouped user lists.
2728 * @param string className
2729 * @param string dialogTitle
2730 * @param object additionalParameters
2732 WCF
.User
.List
= Class
.extend({
2734 * list of additional parameters
2737 _additionalParameters
: { },
2740 * list of cached pages
2777 * @var WCF.Action.Proxy
2782 * Initializes a new grouped user list.
2784 * @param string className
2785 * @param string dialogTitle
2786 * @param object additionalParameters
2788 init: function(className
, dialogTitle
, additionalParameters
) {
2789 this._additionalParameters
= additionalParameters
|| { };
2791 this._className
= className
;
2792 this._dialog
= null;
2793 this._dialogTitle
= dialogTitle
;
2794 this._pageCount
= 0;
2797 this._proxy
= new WCF
.Action
.Proxy({
2798 success
: $.proxy(this._success
, this)
2803 * Opens the dialog overlay.
2811 * Displays the specified page.
2813 * @param object event
2814 * @param object data
2816 _showPage: function(event
, data
) {
2817 if (data
&& data
.activePage
) {
2818 this._pageNo
= data
.activePage
;
2821 if (this._pageCount
!= 0 && (this._pageNo
< 1 || this._pageNo
> this._pageCount
)) {
2822 console
.debug("[WCF.User.List] Cannot access page " + this._pageNo
+ " of " + this._pageCount
);
2826 if (this._cache
[this._pageNo
]) {
2827 var $dialogCreated
= false;
2828 if (this._dialog
=== null) {
2829 this._dialog
= $('<div id="userList' + this._className
.hashCode() + '" />').hide().appendTo(document
.body
);
2830 $dialogCreated
= true;
2833 // remove current view
2834 this._dialog
.empty();
2837 this._dialog
.html(this._cache
[this._pageNo
]);
2840 if (this._pageCount
> 1) {
2841 this._dialog
.find('.jsPagination').wcfPages({
2842 activePage
: this._pageNo
,
2843 maxPage
: this._pageCount
2844 }).on('wcfpagesswitched', $.proxy(this._showPage
, this));
2848 if ($dialogCreated
) {
2849 this._dialog
.wcfDialog({
2850 title
: this._dialogTitle
2854 this._dialog
.wcfDialog('open').wcfDialog('render');
2858 this._additionalParameters
.pageNo
= this._pageNo
;
2860 // load template via AJAX
2861 this._proxy
.setOption('data', {
2862 actionName
: 'getGroupedUserList',
2863 className
: this._className
,
2864 interfaceName
: 'wcf\\data\\IGroupedUserListAction',
2865 parameters
: this._additionalParameters
2867 this._proxy
.sendRequest();
2872 * Handles successful AJAX requests.
2874 * @param object data
2875 * @param string textStatus
2876 * @param jQuery jqXHR
2878 _success: function(data
, textStatus
, jqXHR
) {
2879 if (data
.returnValues
.pageCount
) {
2880 this._pageCount
= data
.returnValues
.pageCount
;
2883 this._cache
[this._pageNo
] = data
.returnValues
.template
;
2889 * Namespace for object watch functions.
2891 WCF
.User
.ObjectWatch
= {};
2894 * Handles subscribe/unsubscribe links.
2896 WCF
.User
.ObjectWatch
.Subscribe
= Class
.extend({
2898 * CSS selector for subscribe buttons
2901 _buttonSelector
: '.jsSubscribeButton',
2916 * system notification
2917 * @var WCF.System.Notification
2919 _notification
: null,
2922 * reload page on unsubscribe
2925 _reloadOnUnsubscribe
: false,
2928 * WCF.User.ObjectWatch.Subscribe object.
2930 * @param boolean reloadOnUnsubscribe
2932 init: function(reloadOnUnsubscribe
) {
2933 this._buttons
= { };
2934 this._notification
= null;
2935 this._reloadOnUnsubscribe
= (reloadOnUnsubscribe
=== true);
2938 this._proxy
= new WCF
.Action
.Proxy({
2939 success
: $.proxy(this._success
, this)
2942 // bind event listeners
2943 $(this._buttonSelector
).each($.proxy(function(index
, button
) {
2944 var $button
= $(button
);
2945 var $objectID
= $button
.data('objectID');
2946 this._buttons
[$objectID
] = $button
.click($.proxy(this._click
, this));
2949 WCF
.System
.Event
.addListener('com.woltlab.wcf.objectWatch', 'update', $.proxy(this._updateSubscriptionStatus
, this));
2953 * Handles a click on a subscribe button.
2955 * @param object event
2957 _click: function(event
) {
2958 event
.preventDefault();
2959 var $button
= $(event
.currentTarget
);
2961 this._proxy
.setOption('data', {
2962 actionName
: 'manageSubscription',
2963 className
: 'wcf\\data\\user\\object\\watch\\UserObjectWatchAction',
2965 objectID
: $button
.data('objectID'),
2966 objectType
: $button
.data('objectType')
2969 this._proxy
.sendRequest();
2973 * Handles successful AJAX requests.
2975 * @param object data
2976 * @param string textStatus
2977 * @param jQuery jqXHR
2979 _success: function(data
, textStatus
, jqXHR
) {
2980 if (data
.actionName
=== 'manageSubscription') {
2981 if (this._dialog
=== null) {
2982 this._dialog
= $('<div>' + data
.returnValues
.template
+ '</div>').hide().appendTo(document
.body
);
2983 this._dialog
.wcfDialog({
2984 title
: WCF
.Language
.get('wcf.user.objectWatch.manageSubscription')
2988 this._dialog
.html(data
.returnValues
.template
);
2989 this._dialog
.wcfDialog('open');
2992 // bind event listener
2993 this._dialog
.find('.formSubmit > .jsButtonSave').data('objectID', data
.returnValues
.objectID
).click($.proxy(this._save
, this));
2994 var $enableNotification
= this._dialog
.find('input[name=enableNotification]').disable();
2996 // toggle subscription
2997 this._dialog
.find('input[name=subscribe]').change(function(event
) {
2998 var $input
= $(event
.currentTarget
);
2999 if ($input
.val() == 1) {
3000 $enableNotification
.enable();
3003 $enableNotification
.disable();
3008 var $selectedOption
= this._dialog
.find('input[name=subscribe]:checked');
3009 if ($selectedOption
.length
&& $selectedOption
.val() == 1) {
3010 $enableNotification
.enable();
3013 else if (data
.actionName
=== 'saveSubscription' && this._dialog
.is(':visible')) {
3014 this._dialog
.wcfDialog('close');
3016 this._updateSubscriptionStatus({
3017 isSubscribed
: data
.returnValues
.subscribe
,
3018 objectID
: data
.returnValues
.objectID
3022 // show notification
3023 if (this._notification
=== null) {
3024 this._notification
= new WCF
.System
.Notification(WCF
.Language
.get('wcf.global.success.edit'));
3027 this._notification
.show();
3032 * Saves the subscription.
3034 * @param object event
3036 _save: function(event
) {
3037 var $button
= this._buttons
[$(event
.currentTarget
).data('objectID')];
3038 var $subscribe
= this._dialog
.find('input[name=subscribe]:checked').val();
3039 var $enableNotification
= (this._dialog
.find('input[name=enableNotification]').is(':checked')) ? 1 : 0;
3041 this._proxy
.setOption('data', {
3042 actionName
: 'saveSubscription',
3043 className
: 'wcf\\data\\user\\object\\watch\\UserObjectWatchAction',
3045 enableNotification
: $enableNotification
,
3046 objectID
: $button
.data('objectID'),
3047 objectType
: $button
.data('objectType'),
3048 subscribe
: $subscribe
3051 this._proxy
.sendRequest();
3055 * Updates subscription status and icon.
3057 * @param object data
3059 _updateSubscriptionStatus: function(data
) {
3060 var $button
= $(this._buttonSelector
+ '[data-object-id=' + data
.objectID
+ ']');
3061 var $icon
= $button
.children('.icon');
3062 if (data
.isSubscribed
) {
3063 $icon
.removeClass('icon-bookmark-empty').addClass('icon-bookmark');
3064 $button
.data('isSubscribed', true);
3067 $icon
.removeClass('icon-bookmark').addClass('icon-bookmark-empty');
3068 $button
.data('isSubscribed', false);
3070 if (this._reloadOnUnsubscribe
) {
3071 window
.location
.reload();
3076 WCF
.System
.Event
.fireEvent('com.woltlab.wcf.objectWatch', 'updatedSubscription', data
);
3081 * Handles inline editing of users.
3083 WCF
.User
.InlineEditor
= WCF
.InlineEditor
.extend({
3085 * data of additional options
3088 _additionalOptions
: { },
3097 * list of permissions
3103 * @see WCF.InlineEditor.init()
3105 init: function(elementSelector
) {
3106 this._super(elementSelector
);
3108 WCF
.System
.ObjectStore
.add('WCF.User.InlineEditor', this);
3112 * @see WCF.InlineEditor._execute()
3114 _execute: function(elementID
, optionName
) {
3115 if (!this._validate(elementID
, optionName
)) {
3120 var $element
= $('#' + elementID
);
3121 if (this._additionalOptions
[optionName
] !== undefined) {
3122 this._additionalOptions
[optionName
].callback(elementID
);
3125 switch (optionName
) {
3127 case 'enableAvatar':
3128 case 'enableSignature':
3129 switch (optionName
) {
3134 case 'enableAvatar':
3135 $data
.disableAvatar
= 0;
3138 case 'enableSignature':
3139 $data
.disableSignature
= 0;
3143 this._proxy
.setOption('data', {
3144 actionName
: optionName
,
3145 className
: 'wcf\\data\\user\\UserAction',
3146 objectIDs
: [ $element
.data('objectID') ]
3148 this._proxy
.sendRequest();
3152 case 'disableAvatar':
3153 case 'disableSignature':
3154 if (optionName
== 'ban') {
3158 $data
[optionName
] = 1;
3161 this._showReasonDialog($element
.data('objectID'), optionName
);
3165 window
.location
= this._getTriggerElement($element
).attr('href');
3170 if ($.getLength($data
)) {
3171 this._updateData
.push({
3173 elementID
: elementID
,
3179 * Executes an action with a reason.
3181 * @param integer userID
3182 * @param string optionName
3183 * @param string reason
3185 _executeReasonAction: function(userID
, optionName
, reason
) {
3186 var $optionName
= this._dialog
.data('optionName');
3188 this._dialog
.find('.innerError').remove();
3190 var $banExpires
= '';
3191 if (!$('#' + $optionName
+ 'NeverExpires').is(':checked')) {
3192 $banExpires
= $('#' + $optionName
+ 'ExpiresDatePicker').val();
3194 this._dialog
.find('#' + $optionName
+ 'ExpiresSettings > dd > small').prepend($('<small class="innerError" />').text(WCF
.Language
.get('wcf.global.form.error.empty')));
3199 var $parameters
= { };
3200 $parameters
[$optionName
+ 'Reason'] = $('#' + $optionName
+ 'Reason').val();
3201 $parameters
[$optionName
+ 'Expires'] = $banExpires
;
3203 this._proxy
.setOption('data', {
3204 actionName
: $optionName
,
3205 className
: 'wcf\\data\\user\\UserAction',
3206 objectIDs
: [ this._dialog
.data('userID') ],
3207 parameters
: $parameters
3209 this._proxy
.sendRequest();
3213 * Returns a specific permission.
3215 * @param string permission
3218 _getPermission: function(permission
) {
3219 if (this._permissions
[permission
]) {
3220 return this._permissions
[permission
];
3227 * @see WCF.InlineEditor._getTriggerElement()
3229 _getTriggerElement: function(element
) {
3230 return element
.find('.jsUserInlineEditor');
3234 * @see WCF.InlineEditor._setOptions()
3236 _setOptions: function() {
3239 { label
: WCF
.Language
.get('wcf.user.ban'), optionName
: 'ban' },
3240 { label
: WCF
.Language
.get('wcf.user.unban'), optionName
: 'unban' },
3243 { label
: WCF
.Language
.get('wcf.user.disableAvatar'), optionName
: 'disableAvatar' },
3244 { label
: WCF
.Language
.get('wcf.user.enableAvatar'), optionName
: 'enableAvatar' },
3246 // disabling signature
3247 { label
: WCF
.Language
.get('wcf.user.disableSignature'), optionName
: 'disableSignature' },
3248 { label
: WCF
.Language
.get('wcf.user.enableSignature'), optionName
: 'enableSignature' }
3251 for (var $optionName
in this._additionalOptions
) {
3252 this._options
.push({ label
: this._additionalOptions
[$optionName
].label
, optionName
: $optionName
});
3256 this._options
.push({ optionName
: 'divider' });
3259 this._options
.push({ label
: WCF
.Language
.get('wcf.user.edit'), optionName
: 'advanced' });
3263 * @see WCF.InlineEditor._show()
3265 _show: function(event
) {
3266 var $element
= $(event
.currentTarget
);
3267 var $elementID
= $element
.data('elementID');
3269 if (!this._dropdowns
[$elementID
]) {
3270 var $dropdownMenu
= $element
.next('.dropdownMenu');
3272 if ($dropdownMenu
) {
3273 this._dropdowns
[$elementID
] = $dropdownMenu
;
3274 WCF
.Dropdown
.initDropdown(this._getTriggerElement(this._elements
[$elementID
]), true);
3278 return this._super(event
);
3282 * Shows the dialog to enter a reason for executing the option with the
3285 * @param string optionName
3287 _showReasonDialog: function(userID
, optionName
) {
3289 this._dialog
.remove();
3293 this._dialog
= $('<div />').hide().appendTo(document
.body
);
3294 this._dialog
.append($('<fieldset><dl><dt><label for="' + optionName
+ 'Reason">' + WCF
.Language
.get('wcf.global.reason') + '</label></dt><dd><textarea id="' + optionName
+ 'Reason" cols="40" rows="3" />' + (WCF
.Language
.get('wcf.user.' + optionName
+ '.reason.description') != 'wcf.user.' + optionName
+ '.reason.description' ? '<small>' + WCF
.Language
.get('wcf.user.' + optionName
+ '.reason.description') + '</small>' : '') + '</dd></dl><dl><dt></dt><dd><label for="' + optionName
+ 'NeverExpires"><input type="checkbox" name="' + optionName
+ 'NeverExpires" id="' + optionName
+ 'NeverExpires" checked="checked" /> ' + WCF
.Language
.get('wcf.user.' + optionName
+ '.neverExpires') + '</label></dd></dl><dl id="' + optionName
+ 'ExpiresSettings" style="display: none;"><dt><label for="' + optionName
+ 'Expires">' + WCF
.Language
.get('wcf.user.' + optionName
+ '.expires') + '</label></dt><dd><input type="date" name="' + optionName
+ 'Expires" id="' + optionName
+ 'Expires" class="medium" min="' + new Date(TIME_NOW
* 1000).toISOString() + '" data-ignore-timezone="true" /><small>' + WCF
.Language
.get('wcf.user.' + optionName
+ '.expires.description') + '</small></dd></dl></fieldset>'));
3295 this._dialog
.append($('<div class="formSubmit"><button class="buttonPrimary" accesskey="s">' + WCF
.Language
.get('wcf.global.button.submit') + '</button></div>'));
3297 this._dialog
.data('optionName', optionName
).data('userID', userID
);
3299 this._dialog
.find('#' + optionName
+ 'NeverExpires').change(function() {
3300 $('#' + optionName
+ 'ExpiresSettings').toggle();
3303 this._dialog
.find('button').click($.proxy(this._executeReasonAction
, this));
3305 this._dialog
.wcfDialog({
3306 title
: WCF
.Language
.get('wcf.user.' + optionName
+ '.confirmMessage')
3311 * @see WCF.InlineEditor._updateState()
3313 _updateState: function(data
) {
3314 this._notification
.show();
3316 for (var $i
= 0, $length
= this._updateData
.length
; $i
< $length
; $i
++) {
3317 var $data
= this._updateData
[$i
];
3318 var $element
= $('#' + $data
.elementID
);
3320 for (var $property
in $data
.data
) {
3321 $element
.data($property
, $data
.data
[$property
]);
3325 if (data
.actionName
== 'ban' || data
.actionName
== 'disableAvatar' || data
.actionName
== 'disableSignature') {
3326 this._dialog
.wcfDialog('close');
3329 if (data
.actionName
== 'unban') {
3330 $('.userHeadline .jsUserBanned').remove();
3335 * @see WCF.InlineEditor._validate()
3337 _validate: function(elementID
, optionName
) {
3338 var $user
= $('#' + elementID
);
3340 if (this._additionalOptions
[optionName
] !== undefined) {
3344 switch (optionName
) {
3347 if (!this._getPermission('canBanUser')) {
3351 if (optionName
== 'ban') {
3352 return !$user
.data('banned');
3355 return $user
.data('banned');
3359 case 'disableAvatar':
3360 case 'enableAvatar':
3361 if (!this._getPermission('canDisableAvatar')) {
3365 if (optionName
== 'disableAvatar') {
3366 return !$user
.data('disableAvatar');
3369 return $user
.data('disableAvatar');
3373 case 'disableSignature':
3374 case 'enableSignature':
3375 if (!this._getPermission('canDisableSignature')) {
3379 if (optionName
== 'disableSignature') {
3380 return !$user
.data('disableSignature');
3383 return $user
.data('disableSignature');
3388 return this._getPermission('canEditUser');
3396 * Adds an additional option.
3398 * @param string label
3399 * @param string optionName
3400 * @param function callback
3402 addOption: function(label
, optionName
, callback
) {
3403 if (!$.isFunction(callback
)) {
3404 console
.debug('[WCF.User.InlineEditor] Missing callback');
3406 if (this._additionalOptions
[optionName
] !== undefined) {
3407 console
.debug('[WCF.User.InlineEditor] Additional option with name "' + optionName
+ "' already exists");
3410 this._additionalOptions
[optionName
] = {
3419 * Sets a permission.
3421 * @param string permission
3422 * @param integer value
3424 setPermission: function(permission
, value
) {
3425 this._permissions
[permission
] = value
;
3431 * @param object permissions
3433 setPermissions: function(permissions
) {
3434 for (var $permission
in permissions
) {
3435 this.setPermission($permission
, permissions
[$permission
]);