summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2012-05-05 12:45:23 -0400
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2012-05-05 12:45:23 -0400
commit39b0781a42655bc1d59970ffc780997dc944c0d9 (patch)
tree243e5ab1e0cb06829eeb73f6e71727724fe3a327
parent08cf300da0e745b4f8b7dee72253f4d21eb51fe4 (diff)
downloadaskbot-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__.py4
-rw-r--r--askbot/models/user.py6
-rw-r--r--askbot/skins/common/media/js/post.js30
-rw-r--r--askbot/skins/common/media/js/utils.js110
-rw-r--r--askbot/skins/default/media/style/style.less8
-rw-r--r--askbot/skins/default/templates/users.html56
-rw-r--r--askbot/skins/default/templates/widgets/group_info.html59
-rw-r--r--askbot/urls.py5
-rw-r--r--askbot/views/commands.py38
-rw-r--r--askbot/views/users.py2
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',