diff options
author | Evgeny Fadeev <evgeny.fadeev@gmail.com> | 2012-05-05 12:45:23 -0400 |
---|---|---|
committer | Evgeny Fadeev <evgeny.fadeev@gmail.com> | 2012-05-05 12:45:23 -0400 |
commit | 39b0781a42655bc1d59970ffc780997dc944c0d9 (patch) | |
tree | 243e5ab1e0cb06829eeb73f6e71727724fe3a327 | |
parent | 08cf300da0e745b4f8b7dee72253f4d21eb51fe4 (diff) | |
download | askbot-39b0781a42655bc1d59970ffc780997dc944c0d9.tar.gz askbot-39b0781a42655bc1d59970ffc780997dc944c0d9.tar.bz2 askbot-39b0781a42655bc1d59970ffc780997dc944c0d9.zip |
moved group wiki to the sidebar and made join/leave group button work
-rw-r--r-- | askbot/models/__init__.py | 4 | ||||
-rw-r--r-- | askbot/models/user.py | 6 | ||||
-rw-r--r-- | askbot/skins/common/media/js/post.js | 30 | ||||
-rw-r--r-- | askbot/skins/common/media/js/utils.js | 110 | ||||
-rw-r--r-- | askbot/skins/default/media/style/style.less | 8 | ||||
-rw-r--r-- | askbot/skins/default/templates/users.html | 56 | ||||
-rw-r--r-- | askbot/skins/default/templates/widgets/group_info.html | 59 | ||||
-rw-r--r-- | askbot/urls.py | 5 | ||||
-rw-r--r-- | askbot/views/commands.py | 38 | ||||
-rw-r--r-- | askbot/views/users.py | 2 |
10 files changed, 263 insertions, 55 deletions
diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py index 9167974e..12e2c97b 100644 --- a/askbot/models/__init__.py +++ b/askbot/models/__init__.py @@ -2335,6 +2335,9 @@ def user_edit_group_membership(self, user = None, group = None, action = None): else: raise ValueError('invalid action') +def user_is_group_member(self, group = None): + return self.group_memberships.filter(group = group).count() == 1 + User.add_to_class( 'add_missing_askbot_subscriptions', user_add_missing_askbot_subscriptions @@ -2402,6 +2405,7 @@ User.add_to_class('is_administrator', user_is_administrator) User.add_to_class('is_administrator_or_moderator', user_is_administrator_or_moderator) User.add_to_class('set_admin_status', user_set_admin_status) User.add_to_class('edit_group_membership', user_edit_group_membership) +User.add_to_class('is_group_member', user_is_group_member) User.add_to_class('remove_admin_status', user_remove_admin_status) User.add_to_class('is_moderator', user_is_moderator) User.add_to_class('is_approved', user_is_approved) diff --git a/askbot/models/user.py b/askbot/models/user.py index 6f8931a9..e6bd6e1d 100644 --- a/askbot/models/user.py +++ b/askbot/models/user.py @@ -345,6 +345,7 @@ class GroupMembership(models.Model): class Meta: app_label = 'askbot' + unique_together = ('group', 'user') class GroupProfile(models.Model): """stores group profile data""" @@ -367,10 +368,13 @@ class GroupProfile(models.Model): def can_accept_user(self, user): """True if user is preapproved to join the group""" + if user.is_anonymous(): + return False + if self.is_open: return True - if user.is_moderator_or_administrator(): + if user.is_administrator_or_moderator(): return True #relying on a specific method of storage diff --git a/askbot/skins/common/media/js/post.js b/askbot/skins/common/media/js/post.js index e858413d..c597e84c 100644 --- a/askbot/skins/common/media/js/post.js +++ b/askbot/skins/common/media/js/post.js @@ -2298,6 +2298,36 @@ UserGroupProfileEditor.prototype.decorate = function(element){ logo_changer.decorate(change_logo_btn); }; +var GroupJoinButton = function(group_id){ + FollowToggle.call(this); + this._group_id = group_id; +}; +inherits(GroupJoinButton, FollowToggle); + +GroupJoinButton.prototype.getPostData = function(){ + return { group_id: this._group_id }; +}; + +GroupJoinButton.prototype.getHandler = function(){ + var me = this; + return function(){ + $.ajax({ + type: 'POST', + dataType: 'json', + cache: false, + data: me.getPostData(), + url: askbot['urls']['join_or_leave_group'], + success: function(data){ + if (data['success']){ + me.setOn(data['is_member']); + } else { + showMessage(me.getElement(), data['message']); + } + } + }); + }; +}; + $(document).ready(function() { $('[id^="comments-for-"]').each(function(index, element){ var comments = new PostCommentsWidget(); diff --git a/askbot/skins/common/media/js/utils.js b/askbot/skins/common/media/js/utils.js index a466efa5..0c908b5d 100644 --- a/askbot/skins/common/media/js/utils.js +++ b/askbot/skins/common/media/js/utils.js @@ -410,6 +410,10 @@ SimpleControl.prototype.setHandler = function(handler){ } }; +SimpleControl.prototype.getHandler = function(){ + return this._handler; +}; + SimpleControl.prototype.setHandlerInternal = function(){ //default internal setHandler behavior setupButtonEventHandlers(this._element, this._handler); @@ -474,6 +478,112 @@ DeleteIcon.prototype.setContent = function(content){ } } +/** + * A button on which user can click + * and become added to some group (followers, group members, etc.) + * The button has four states on-prompt, off-prompt, on-state and off-state + * on-prompt is activated on mouseover, when user is not part of group + * off-prompt - on mouseover, when user is part of group + * on-state - when user is part of group and mouse is not over the button + * off-state - same as above, but when user is not part of the group + */ +var FollowToggle = function(){ + SimpleControl.call(this); + this._state = null; + this._state_messages = {}; + this._states = [ + 'on-state', + 'off-state', + 'on-prompt', + 'off-prompt' + ]; +}; +inherits(FollowToggle, SimpleControl); + +FollowToggle.prototype.resetStyles = function(){ + var element = this._element; + var states = this._states; + $.each(states, function(idx, state){ + element.removeClass(state); + }); + this._element.html(''); +}; + +FollowToggle.prototype.isOn = function(){ + return this._element.hasClass('on'); +}; + +FollowToggle.prototype.setOn = function(is_on){ + if (is_on){ + this._element.addClass('on'); + this._element.html(this._state_messages['on-state']); + } else { + this._element.removeClass('on'); + this._element.html(this._state_messages['off-state']); + } +}; + +FollowToggle.prototype.setState = function(state){ + this._state = state; + if (this._element){ + this.resetStyles(); + this._element.addClass(state); + this._element.html(this._state_messages[state]); + } +}; + +FollowToggle.prototype.decorate = function(element){ + this._element = element; + //read messages for all states + var messages = {}; + $.each(this._states, function(idx, state){ + if (state === 'off-state'){ + return; + } + messages[state] = element.attr('data-' + state + '-text'); + }); + messages['off-state'] = messages['on-prompt'] + this._state_messages = messages; + + //detect state and save it + var text = $.trim(element.html()); + for (var i = 0; i < this._states.length; i++){ + var state = this._states[i]; + if (text === messages[state]){ + this._state = state; + break; + } + } + + //set mouseover handler + var me = this; + element.mouseover(function(){ + var is_on = me.isOn(); + if (is_on){ + me.setState('off-prompt'); + } else { + me.setState('on-prompt'); + } + element.css('background-color', 'red'); + return false; + }); + element.mouseout(function(){ + var is_on = me.isOn(); + if (is_on){ + me.setState('on-state'); + } else { + me.setState('off-state'); + } + element.css('background-color', 'white'); + return false; + }); + + setupButtonEventHandlers(element, this.getHandler()); +}; + +/** + * A list of items from where one can be selected + */ var SelectBox = function(){ WrappedElement.call(this); this._items = []; diff --git a/askbot/skins/default/media/style/style.less b/askbot/skins/default/media/style/style.less index 88b12ebd..d97de8ab 100644 --- a/askbot/skins/default/media/style/style.less +++ b/askbot/skins/default/media/style/style.less @@ -3419,7 +3419,6 @@ body.anon.lang-es { .group-wiki { .content { - float: left; p:last-child { margin-bottom: 5px; } @@ -3428,13 +3427,18 @@ body.anon.lang-es { float: left; margin: 0 5px 3px 0; } + .follow-toggle.group-join-btn { + width: 150px; + margin: 4px auto 10px auto; + display: block; + } .controls { margin: 0 0 10px 0; } } img.group-logo { - height: 64px; + height: 60px;/* important to align with the line spacing */ } #groups-list { diff --git a/askbot/skins/default/templates/users.html b/askbot/skins/default/templates/users.html index 351a6df9..595bbd72 100644 --- a/askbot/skins/default/templates/users.html +++ b/askbot/skins/default/templates/users.html @@ -51,50 +51,6 @@ {% if not users.object_list %} <p><span>{% trans %}Nothing found.{% endtrans %}</span></p> {% endif %} -{% if group %} - <div id="group-wiki-{{group.id}}" class="group-wiki"> - <img class="group-logo" - {% if group.group_profile.logo_url %} - src="{{ group.group_profile.logo_url }}" - {% else %} - style="display:none" - {% endif %} - /> - <div class="content"> - {% if group.tag_wiki %} - {{ group.tag_wiki.html }} - {% endif %} - </div> - <div class="clearfix"></div> - {% if request.user.is_authenticated() and request.user.is_administrator_or_moderator() %} - <div class="controls"> - <a class="edit-description" - >{% trans %}edit description{% endtrans %}</a> - {% if group.group_profile.logo_url %} - <span>|</span> - <a class="change-logo" - >{% trans %}change logo{% endtrans %}</a> - <span>|</span> - <a class="delete-logo">{% trans %}delete logo{% endtrans %}</a> - {% else %} - <span>|</span> - <a class="change-logo" - >{% trans %}add logo{% endtrans %}</a> - {% endif %} - {% if group_email_moderation_enabled %} - <span>|</span> - {% if group.group_profile.moderate_email %} - <a class="moderate-email" - >{% trans %}disable moderation of emailed questions{% endtrans %}</a> - {% else %} - <a class="moderate-email" - >{% trans %}moderate emailed questions{% endtrans %}</a> - {% endif %} - {% endif %} - </div> - {% endif %} - </div> -{% endif %} {{ macros.user_list( users.object_list, karma_mode = settings.KARMA_MODE, badges_mode = settings.BADGES_MODE @@ -104,6 +60,12 @@ {{ macros.paginator(paginator_context) }} </div> {% endblock %} +{% block sidebar %} + {% if group %} + {# this widget takes variables: group, user_can_join_group, user_is_group_member #} + {% include "widgets/group_info.html" %} + {% endif %} +{% endblock %} {% block endjs %} <script type='text/javascript'> var Attacklab = Attacklab || {}; @@ -114,6 +76,7 @@ askbot['urls']['save_group_logo_url'] = '{% url save_group_logo_url %}'; askbot['urls']['delete_group_logo_url'] = '{% url delete_group_logo %}'; askbot['urls']['toggle_group_email_moderation'] = '{% url toggle_group_email_moderation %}'; + askbot['urls']['join_or_leave_group'] = '{% url join_or_leave_group %}'; </script> <script type='text/javascript' src='{{"/js/editor.js"|media}}'></script> <script type='text/javascript' src='{{"/js/wmd/showdown.js"|media}}'></script> @@ -128,6 +91,11 @@ var codeFriendlyMarkdown = false; {% endif %} $().ready(function(){ + {% if group and request.user.is_authenticated() %} + var group_join_btn = new GroupJoinButton({{ group.id }}); + group_join_btn.decorate($('.group-join-btn')); + {% endif %} + //setup WMD editor if (askbot['data']['userIsAdminOrMod'] === true){ //todo: this is kind of Attacklab.init ... should not be here Attacklab.wmd = function(){ diff --git a/askbot/skins/default/templates/widgets/group_info.html b/askbot/skins/default/templates/widgets/group_info.html new file mode 100644 index 00000000..b819db79 --- /dev/null +++ b/askbot/skins/default/templates/widgets/group_info.html @@ -0,0 +1,59 @@ +<div id="group-wiki-{{group.id}}" class="box group-wiki"> + <h2>{% trans %}Group info{% endtrans %}</h2> + <img class="group-logo" + {% if group.group_profile.logo_url %} + src="{{ group.group_profile.logo_url }}" + {% else %} + style="display:none" + {% endif %} + /> + <div class="content"> + {% if group.tag_wiki %} + {{ group.tag_wiki.html }} + {% endif %} + </div> + <div class="clearfix"></div> + {% if user_can_join_group or user_is_group_member %} + <button + class="group-join-btn follow-toggle {% if user_is_group_member %}on on-state{% endif %}" + data-off-prompt-text="{% trans %}Leave this group{% endtrans %}" + data-on-prompt-text="{% trans %}Join this group{% endtrans %}" + data-on-state-text="{% trans %}You are a member{% endtrans %}" + > + {% if user_is_group_member %} + {% trans %}You are a member{% endtrans %} + {% else %} + {% if user_can_join_group %} + {% trans %}Join this group{% endtrans %} + {% endif %} + {% endif %} + </button> + {% endif %} + {% if request.user.is_authenticated() and request.user.is_administrator_or_moderator() %} + <div class="controls"> + <a class="edit-description" + >{% trans %}edit description{% endtrans %}</a> + {% if group.group_profile.logo_url %} + <span>|</span> + <a class="change-logo" + >{% trans %}change logo{% endtrans %}</a> + <span>|</span> + <a class="delete-logo">{% trans %}delete logo{% endtrans %}</a> + {% else %} + <span>|</span> + <a class="change-logo" + >{% trans %}add logo{% endtrans %}</a> + {% endif %} + {% if group_email_moderation_enabled %} + <span>|</span> + {% if group.group_profile.moderate_email %} + <a class="moderate-email" + >{% trans %}disable moderation of emailed questions{% endtrans %}</a> + {% else %} + <a class="moderate-email" + >{% trans %}moderate emailed questions{% endtrans %}</a> + {% endif %} + {% endif %} + </div> + {% endif %} +</div> diff --git a/askbot/urls.py b/askbot/urls.py index a3f694d9..7a3eb248 100644 --- a/askbot/urls.py +++ b/askbot/urls.py @@ -308,6 +308,11 @@ urlpatterns = patterns('', views.commands.edit_group_membership, name='edit_group_membership' ), + url(#ajax only + r'^join-or-leave-group/$', + views.commands.join_or_leave_group, + name='join_or_leave_group' + ), url( r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', diff --git a/askbot/views/commands.py b/askbot/views/commands.py index 7eeccabe..6825461e 100644 --- a/askbot/views/commands.py +++ b/askbot/views/commands.py @@ -10,7 +10,7 @@ from django.core import exceptions from django.core.urlresolvers import reverse from django.contrib.auth.decorators import login_required from django.http import HttpResponse, HttpResponseRedirect, Http404, HttpResponseBadRequest -from django.forms import ValidationError +from django.forms import ValidationError, IntegerField from django.shortcuts import get_object_or_404 from django.views.decorators import csrf from django.utils import simplejson @@ -486,7 +486,7 @@ def get_tag_list(request): def load_tag_wiki_text(request): """returns text of the tag wiki in markdown format""" tag = get_object_or_404(models.Tag, id = request.GET['tag_id']) - tag_wiki_text = getattr(tag.tag_wiki, 'text', '') + tag_wiki_text = getattr(tag.tag_wiki, 'text', '').strip() return HttpResponse(tag_wiki_text, mimetype = 'text/plain') @csrf.csrf_exempt @@ -731,6 +731,32 @@ def read_message(request):#marks message a read @csrf.csrf_exempt @decorators.ajax_only @decorators.post_only +def join_or_leave_group(request): + """only current user can join/leave group""" + if request.user.is_anonymous(): + raise exceptions.PermissionDenied() + + group_id = IntegerField().clean(request.POST['group_id']) + group = models.Tag.objects.get(id = group_id) + + if request.user.is_group_member(group): + action = 'remove' + is_member = False + else: + action = 'add' + is_member = True + request.user.edit_group_membership( + user = request.user, + group = group, + action = action + ) + return {'is_member': is_member} + + + +@csrf.csrf_exempt +@decorators.ajax_only +@decorators.post_only @decorators.admins_only def edit_group_membership(request): form = forms.EditGroupMembershipForm(request.POST) @@ -745,6 +771,7 @@ def edit_group_membership(request): ) action = form.cleaned_data['action'] + #warning: possible race condition if action == 'add': group_params = {'group_name': group_name, 'user': user} group = models.Tag.group_tags.get_or_create(**group_params) @@ -789,7 +816,6 @@ def save_group_logo_url(request): @decorators.post_only @decorators.admins_only def delete_group_logo(request): - from django.forms import IntegerField group_id = IntegerField().clean(int(request.POST['group_id'])) group = models.Tag.group_tags.get(id = group_id) group.group_profile.logo_url = None @@ -800,7 +826,6 @@ def delete_group_logo(request): @decorators.post_only @decorators.admins_only def delete_post_reject_reason(request): - from django.forms import IntegerField reason_id = IntegerField().clean(int(request.POST['reason_id'])) reason = models.PostFlagReason.objects.get(id = reason_id) reason.delete() @@ -810,15 +835,14 @@ def delete_post_reject_reason(request): @decorators.post_only @decorators.admins_only def toggle_group_email_moderation(request): - from django.forms import IntegerField group_id = IntegerField().clean(int(request.POST['group_id'])) group = models.Tag.group_tags.get(id = group_id) group.group_profile.moderate_email = not group.group_profile.moderate_email group.group_profile.save() if group.group_profile.moderate_email: - new_button_text = _('moderate emailed questions') - else: new_button_text = _('disable moderation of emailed questions') + else: + new_button_text = _('moderate emailed questions') return {'new_button_text': new_button_text} diff --git a/askbot/views/users.py b/askbot/views/users.py index ebd01001..ef0aea57 100644 --- a/askbot/views/users.py +++ b/askbot/views/users.py @@ -84,7 +84,7 @@ def users(request, by_group = False, group_id = None, group_slug = None): group_memberships__group__id = group_id ) if request.user.is_authenticated(): - user_is_group_member = users.filter(id = request.user.id) + user_is_group_member = bool(users.filter(id = request.user.id).count()) else: group_page_url = reverse( 'users_by_group', |