summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2011-05-24 02:19:28 -0400
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2011-05-24 02:19:28 -0400
commit1243d42844613a5e04d8f40b7d3cc525f9ff8979 (patch)
tree01fa326b3edfc2386f98e8dc83a1a4cb6339792b
parent67ed525e48eb7d917a2fd5f8a9155ccb287c353f (diff)
downloadaskbot-1243d42844613a5e04d8f40b7d3cc525f9ff8979.tar.gz
askbot-1243d42844613a5e04d8f40b7d3cc525f9ff8979.tar.bz2
askbot-1243d42844613a5e04d8f40b7d3cc525f9ff8979.zip
follow user feature works, still need to modify the subscription model
-rw-r--r--askbot/doc/source/optional-modules.rst13
-rw-r--r--askbot/models/__init__.py18
-rw-r--r--askbot/models/question.py7
-rwxr-xr-xaskbot/skins/default/media/style/style.css21
-rw-r--r--askbot/skins/default/templates/macros.html28
-rw-r--r--askbot/skins/default/templates/main_page/nothing_found.html4
-rw-r--r--askbot/skins/default/templates/main_page/tab_bar.html4
-rw-r--r--askbot/skins/default/templates/user_profile/user_info.html4
-rw-r--r--askbot/skins/default/templates/user_profile/user_network.html25
-rw-r--r--askbot/skins/default/templates/user_profile/user_tabs.html6
-rw-r--r--askbot/skins/default/templates/users.html23
-rw-r--r--askbot/tests/email_alert_tests.py2
-rw-r--r--askbot/tests/follow_tests.py8
-rw-r--r--askbot/views/users.py70
14 files changed, 167 insertions, 66 deletions
diff --git a/askbot/doc/source/optional-modules.rst b/askbot/doc/source/optional-modules.rst
index d7ca2f24..3689db19 100644
--- a/askbot/doc/source/optional-modules.rst
+++ b/askbot/doc/source/optional-modules.rst
@@ -5,12 +5,21 @@ Optional modules
Askbot supports a number of optional modules, enabling certain features, not available
in askbot by default.
+Follow users
+============
+
+Install ``django-followit`` app::
+
+ pip install -e git+git://github.com/ASKBOT/django-followit.git#egg=followit
+
+Then add ``followit`` to the ``INSTALLED_APPS`` and run ``syncdb`` management command.
+
Uploaded avatars
================
To enable uploadable avatars (in addition to :ref:`gravatars <gravatar>`),
please install development version of
-application ``django-avatar``, with the following command:
+application ``django-avatar``, with the following command::
pip install -e git+git://github.com/ericflo/django-avatar.git#egg=django-avatar
@@ -19,6 +28,8 @@ and run (to install database table used by the avatar app):
python manage.py syncdb
+Also, settings ``MEDIA_ROOT`` and ``MEDIA_URL`` will need to be added to your ``settings.py`` file.
+
.. note::
Version of the ``avatar`` application available at pypi may not
diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py
index 87e5fe0f..7c5f4ea7 100644
--- a/askbot/models/__init__.py
+++ b/askbot/models/__init__.py
@@ -1706,6 +1706,16 @@ def user_follow_question(self, question = None):
if self not in question.followed_by.all():
question.followed_by.add(self)
+def user_is_following_question(user, question):
+ """True if user is following a question"""
+ followers = question.followed_by.all()
+ try:
+ followers.get(id = user.id)
+ return True
+ except User.DoesNotExist:
+ return False
+
+
def upvote(self, post, timestamp=None, cancel=False):
return _process_vote(
self,post,
@@ -1870,6 +1880,7 @@ User.add_to_class('delete_messages', delete_messages)
User.add_to_class('toggle_favorite_question', toggle_favorite_question)
User.add_to_class('follow_question', user_follow_question)
User.add_to_class('unfollow_question', user_unfollow_question)
+User.add_to_class('is_following_question', user_is_following_question)
User.add_to_class('mark_tags', user_mark_tags)
User.add_to_class('decrement_response_count', user_decrement_response_count)
User.add_to_class('increment_response_count', user_increment_response_count)
@@ -2434,8 +2445,11 @@ signals.post_updated.connect(
signals.site_visited.connect(record_user_visit)
#set up a possibility for the users to follow others
-import followit
-followit.register(User)
+try:
+ import followit
+ followit.register(User)
+except ImportError:
+ pass
__all__ = [
'signals',
diff --git a/askbot/models/question.py b/askbot/models/question.py
index ccb587e7..5ad64a32 100644
--- a/askbot/models/question.py
+++ b/askbot/models/question.py
@@ -209,7 +209,12 @@ class QuestionQuerySet(models.query.QuerySet):
else:
raise Exception('UNANSWERED_QUESTION_MEANING setting is wrong')
elif scope_selector == 'favorite':
- qs = qs.filter(favorited_by = request_user)
+ favorite_filter = models.Q(favorited_by = request_user)
+ if 'followit' in settings.INSTALLED_APPS:
+ followed_users = request_user.get_followed_users()
+ favorite_filter |= models.Q(author__in = followed_users)
+ favorite_filter |= models.Q(answers__author__in = followed_users)
+ qs = qs.filter(favorite_filter)
#user contributed questions & answers
if author_selector:
diff --git a/askbot/skins/default/media/style/style.css b/askbot/skins/default/media/style/style.css
index 3d04ec7d..a4e7d581 100755
--- a/askbot/skins/default/media/style/style.css
+++ b/askbot/skins/default/media/style/style.css
@@ -771,11 +771,10 @@ a:hover.medal {
height: 24px;
line-height: 26px;
margin-top: 3px;
- padding: 0px 11px 0px 11px;
}
-.tabsA a.on, tabsA a.on:hover {
- padding: 0px 6px 0px 11px;
+.tabsA a.rev.on, tabsA a.rev.on:hover {
+ padding: 0px 2px 0px 7px;
}
.tabsA a, .tabsC a{
@@ -790,7 +789,7 @@ a:hover.medal {
height: 20px;
line-height: 22px;
margin: 5px 0 0 4px;
- padding: 0 11px 0 11px;
+ padding: 0 7px;
text-decoration: none;
}
@@ -2162,5 +2161,17 @@ pre.prettyprint { padding: 3px; border: 0px solid #888; }
}
.follow-toggle {
- color: #406A24;
+ border-radius: 3px;
+ -moz-border-radius: 3px;
+ background: #fff0e0;
+ color: #777;
+ font-weight: bolder;
+ border: 1px solid #aaa;
+ cursor: pointer;
+}
+
+.follow-toggle.unfollow:hover {
+ background: #a40000;
+ color: #fff;
+ border: 1px solid #d40000;
}
diff --git a/askbot/skins/default/templates/macros.html b/askbot/skins/default/templates/macros.html
index d1b2bc40..fb3e3898 100644
--- a/askbot/skins/default/templates/macros.html
+++ b/askbot/skins/default/templates/macros.html
@@ -102,6 +102,30 @@
{{ user_country_name_and_flag(user) }}
{%- endmacro -%}
+{%- macro user_list(users, profile_section = None) -%}
+<div class="userList">
+ <table class="list-table">
+ <tr>
+ <td class="list-td">
+ {% for user in users %}
+ <div class="user">
+ <ul>
+ <li class="thumb">{{ gravatar(user, 32) }}</li>
+ <li><a href="{% url user_profile user.id, user.username|slugify %}{% if profile_section %}?sort={{profile_section}}{% endif %}">{{user.username}}</a>{{ user_country_flag(user) }}</li>
+ <li>{{ user_score_and_badge_summary(user) }}</li>
+ </ul>
+ </div>
+ {% if loop.index is divisibleby 7 %}
+ </td>
+ <td>
+ {% endif %}
+ {% endfor %}
+ </td>
+ </tr>
+ </table>
+</div>
+{%- endmacro -%}
+
{%- macro paginator(p, position='left') -%}{# p is paginator context dictionary #}
{% spaceless %}
{% if p.is_paginated %}
@@ -538,12 +562,12 @@ poor design of the data or methods on data objects #}
{% if sort == key_name + "-asc" %}{# "worst" first #}
<a id="by_{{key_name}}"
href="?sort={{key_name}}-desc"
- class="on"
+ class="rev on"
title="{{desc_tooltip}}"><span>{{label}} &#9650;</span></a>
{% elif sort == key_name + "-desc" %}{# "best first" #}
<a id="by_{{key_name}}"
href="?sort={{key_name}}-asc"
- class="on"
+ class="rev on"
title="{{asc_tooltip}}"><span>{{label}} &#9660;</span></a>
{% else %}{# default, when other button is active #}
<a id="by_{{key_name}}"
diff --git a/askbot/skins/default/templates/main_page/nothing_found.html b/askbot/skins/default/templates/main_page/nothing_found.html
index bc58fc27..50f2f340 100644
--- a/askbot/skins/default/templates/main_page/nothing_found.html
+++ b/askbot/skins/default/templates/main_page/nothing_found.html
@@ -4,8 +4,8 @@
{% trans %}There are no unanswered questions here{% endtrans %}
{% endif %}
{% if scope == "favorite" %}
- {% trans %}No favorite questions here. {% endtrans %}
- {% trans %}Please start (bookmark) some questions when you visit them{% endtrans %}
+ {% trans %}No questions here. {% endtrans %}
+ {% trans %}Please star (bookmark) some questions or follow some users.{% endtrans %}
{% endif %}
</p>
{% if query or search_tags or author_name %}
diff --git a/askbot/skins/default/templates/main_page/tab_bar.html b/askbot/skins/default/templates/main_page/tab_bar.html
index 12096a3b..e398be87 100644
--- a/askbot/skins/default/templates/main_page/tab_bar.html
+++ b/askbot/skins/default/templates/main_page/tab_bar.html
@@ -17,8 +17,8 @@
<a id="favorite"
class="{% if scope == 'favorite' %}on{% else %}off{% endif %}"
href="?scope=favorite"
- title="{% trans %}see your favorite questions{% endtrans %}"
- ><span>{% trans %}favorite{% endtrans %}</span></a>
+ title="{% trans %}see your followed questions{% endtrans %}"
+ ><span>{% trans %}followed{% endtrans %}</span></a>
{% endif %}
</div>
<div id="sort_tabs" class="tabsA">
diff --git a/askbot/skins/default/templates/user_profile/user_info.html b/askbot/skins/default/templates/user_profile/user_info.html
index 63c1b163..5aa5c094 100644
--- a/askbot/skins/default/templates/user_profile/user_info.html
+++ b/askbot/skins/default/templates/user_profile/user_info.html
@@ -23,7 +23,9 @@
</div>
<div class="scoreNumber">{{view_user.reputation|intcomma}}</div>
<p><b style="color:#777;">{% trans %}reputation{% endtrans %}</b></p>
- {{ macros.follow_user_toggle(visitor = request.user, subject = view_user) }}
+ {% if user_follow_feature_on %}
+ {{ macros.follow_user_toggle(visitor = request.user, subject = view_user) }}
+ {% endif %}
</td>
<td width="360" style="padding-left:5px;vertical-align: top;">
<table class="user-details">
diff --git a/askbot/skins/default/templates/user_profile/user_network.html b/askbot/skins/default/templates/user_profile/user_network.html
new file mode 100644
index 00000000..cc741bb1
--- /dev/null
+++ b/askbot/skins/default/templates/user_profile/user_network.html
@@ -0,0 +1,25 @@
+{% extends "user_profile/user.html" %}
+{% import "macros.html" as macros %}
+<!-- user_network.html -->
+{% block profileseciton %}
+ {% trans %}network{% endtrans %}
+{% endblock %}
+{% block usercontent %}
+ {% if followed_users or followers %}
+ {% if followers %}
+ <h2>{% trans count=followers|length %}Followed by {{count}} person{% pluralize count %}Followed by {{count}} people{% endtrans %}</h2>
+ {{ macros.user_list(followers, profile_section = 'network') }}
+ {% endif %}
+ {% if followed_users %}
+ <h2>{% trans count=followed_users|length %}Following {{count}} person{% pluralize count %}Followed by {{count}} people{% endtrans %}</h2>
+ {{ macros.user_list(followed_users, profile_section = 'network') }}
+ {% endif %}
+ {% else %}
+ {% if request.user == view_user %}
+ <p>{% trans %}Your network is empty. Would you like to follow someone? - Just visit their profiles and click "follow"{% endtrans %}</p>
+ {% else %}
+ <p>{% trans username = view_user.username %}{{user}}'s network is empty{% endtrans %}</p>
+ {% endif %}
+ {% endif %}
+{% endblock %}
+<!-- end user_network.html -->
diff --git a/askbot/skins/default/templates/user_profile/user_tabs.html b/askbot/skins/default/templates/user_profile/user_tabs.html
index 276590c9..92c42ea8 100644
--- a/askbot/skins/default/templates/user_profile/user_tabs.html
+++ b/askbot/skins/default/templates/user_profile/user_tabs.html
@@ -11,6 +11,12 @@
href="{% url user_profile view_user.id, view_user.username|slugify %}?sort=inbox"
><span>{% trans %}inbox{% endtrans %}</span></a>
{% endif %}
+ {% if user_follow_feature_on %}
+ <a id="network" {% if tab_name=="network" %}class="on"{% endif %}
+ title="{% trans %}followers and followed users{% endtrans %}"
+ href="{% url user_profile view_user.id, view_user.username|slugify %}?sort=network"
+ ><span>{% trans %}network{% endtrans %}</span></a>
+ {% endif %}
<a id="reputation" {% if tab_name=="reputation" %}class="on"{% endif %}
title="{% trans %}graph of user reputation{% endtrans %}"
href="{% url user_profile view_user.id, view_user.username|slugify %}?sort=reputation"
diff --git a/askbot/skins/default/templates/users.html b/askbot/skins/default/templates/users.html
index 74c171c8..750b3abb 100644
--- a/askbot/skins/default/templates/users.html
+++ b/askbot/skins/default/templates/users.html
@@ -40,28 +40,7 @@
<span>{% trans %}Nothing found.{% endtrans %}</span>
{% endif %}
</p>
-<div class="userList">
- <table class="list-table">
- <tr>
- <td class="list-td">
- {% for user in users.object_list %}
- <div class="user">
- <ul>
- <li class="thumb">{{ macros.gravatar(user, 32) }}</li>
- <li><a href="{% url user_profile user.id, user.username|slugify %}">{{user.username}}</a>{{ macros.user_country_flag(user) }}</li>
- <li>{{ macros.user_score_and_badge_summary(user) }}</li>
- </ul>
- </div>
-
- {% if loop.index is divisibleby 7 %}
- </td>
- <td>
- {% endif %}
- {% endfor %}
- </td>
- </tr>
- </table>
-</div>
+{{ macros.user_list(users.object_list) }}
<div class="pager">
{{ macros.paginator(paginator_context) }}
</div>
diff --git a/askbot/tests/email_alert_tests.py b/askbot/tests/email_alert_tests.py
index 0c239be1..7f086152 100644
--- a/askbot/tests/email_alert_tests.py
+++ b/askbot/tests/email_alert_tests.py
@@ -258,7 +258,7 @@ class EmailAlertTests(TestCase):
timestamp = self.setup_timestamp
if follow is None:
- if author.is_following(question):
+ if author.is_following_question(question):
follow = True
else:
follow = False
diff --git a/askbot/tests/follow_tests.py b/askbot/tests/follow_tests.py
index 86df684b..f583c836 100644
--- a/askbot/tests/follow_tests.py
+++ b/askbot/tests/follow_tests.py
@@ -1,6 +1,12 @@
+from django.conf import settings as django_settings
from askbot.tests.utils import AskbotTestCase
-class FollowUserTests(AskbotTestCase):
+if 'followit' in django_settings.INSTALLED_APPS:
+ TEST_PROTOTYPE = AskbotTestCase
+else:
+ TEST_PROTOTYPE = object
+
+class FollowUserTests(TEST_PROTOTYPE):
def setUp(self):
self.u1 = self.create_user('user1')
diff --git a/askbot/views/users.py b/askbot/views/users.py
index d96ceece..cd3aa421 100644
--- a/askbot/views/users.py
+++ b/askbot/views/users.py
@@ -54,14 +54,14 @@ repute_type_id = repute_type.id
def owner_or_moderator_required(f):
@functools.wraps(f)
- def wrapped_func(request, profile_owner):
+ def wrapped_func(request, profile_owner, context):
if profile_owner == request.user:
pass
elif request.user.is_authenticated() and request.user.can_moderate_user(profile_owner):
pass
else:
raise Http404 #todo: change to access forbidden?
- return f(request, profile_owner)
+ return f(request, profile_owner, context)
return wrapped_func
def users(request):
@@ -131,7 +131,7 @@ def users(request):
return render_into_skin('users.html', data, request)
@csrf.csrf_protect
-def user_moderate(request, subject):
+def user_moderate(request, subject, context):
"""user subview for moderation
"""
moderator = request.user
@@ -213,7 +213,6 @@ def user_moderate(request, subject):
'tab_name': 'moderation',
'tab_description': _('moderate this user'),
'page_title': _('moderate user'),
- 'view_user': subject,
'change_user_status_form': user_status_form,
'change_user_reputation_form': user_rep_form,
'send_message_form': send_message_form,
@@ -222,7 +221,8 @@ def user_moderate(request, subject):
'user_rep_changed': user_rep_changed,
'user_status_changed': user_status_changed
}
- return render_into_skin('user_profile/user_moderate.html', data, request)
+ context.update(data)
+ return render_into_skin('user_profile/user_moderate.html', context, request)
#non-view function
def set_new_email(user, new_email, nomessage=False):
@@ -278,7 +278,7 @@ def edit_user(request, id):
}
return render_into_skin('user_profile/user_edit.html', data, request)
-def user_stats(request, user):
+def user_stats(request, user, context):
question_filter = {'author': user}
if request.user != user:
@@ -379,7 +379,6 @@ def user_stats(request, user):
'tab_name' : 'stats',
'tab_description' : _('user profile'),
'page_title' : _('user profile overview'),
- 'view_user' : user,
'user_status_for_display': user.get_status_display(soft = True),
'questions' : questions,
'favorited_myself': favorited_myself,
@@ -394,9 +393,10 @@ def user_stats(request, user):
'awarded_badge_counts': dict(awarded_badge_counts),
'total_awards' : total_awards,
}
- return render_into_skin('user_profile/user_stats.html', data, request)
+ context.update(data)
+ return render_into_skin('user_profile/user_stats.html', context, request)
-def user_recent(request, user):
+def user_recent(request, user, context):
def get_type_name(type_id):
for item in const.TYPE_ACTIVITY:
@@ -663,13 +663,13 @@ def user_recent(request, user):
'tab_name' : 'recent',
'tab_description' : _('recent user activity'),
'page_title' : _('profile - recent activity'),
- 'view_user' : user,
'activities' : activities[:const.USER_VIEW_DATA_SIZE]
}
- return render_into_skin('user_profile/user_recent.html', data, request)
+ context.update(data)
+ return render_into_skin('user_profile/user_recent.html', context, request)
@owner_or_moderator_required
-def user_responses(request, user):
+def user_responses(request, user, context):
"""
We list answers for question, comments, and
answer accepted by others for this user.
@@ -730,13 +730,24 @@ def user_responses(request, user):
'inbox_section':section,
'tab_description' : _('comments and answers to others questions'),
'page_title' : _('profile - responses'),
- 'view_user' : user,
'responses' : response_list,
}
- return render_into_skin('user_profile/user_inbox.html', data, request)
+ context.update(data)
+ return render_into_skin('user_profile/user_inbox.html', context, request)
+
+def user_network(request, user, context):
+ if 'followit' not in django_settings.INSTALLED_APPS:
+ raise Http404
+ data = {
+ 'tab_name': 'network',
+ 'followed_users': user.get_followed_users(),
+ 'followers': user.get_followers(),
+ }
+ context.update(data)
+ return render_into_skin('user_profile/user_network.html', context, request)
@owner_or_moderator_required
-def user_votes(request, user):
+def user_votes(request, user, context):
votes = []
question_votes = models.Vote.objects.extra(
@@ -794,12 +805,12 @@ def user_votes(request, user):
'tab_name' : 'votes',
'tab_description' : _('user vote record'),
'page_title' : _('profile - votes'),
- 'view_user' : user,
'votes' : votes[:const.USER_VIEW_DATA_SIZE]
}
- return render_into_skin('user_profile/user_votes.html', data, request)
+ context.update(data)
+ return render_into_skin('user_profile/user_votes.html', context, request)
-def user_reputation(request, user):
+def user_reputation(request, user, context):
reputes = models.Repute.objects.filter(user=user).order_by('-reputed_at')
#select_related() adds stuff needed for the query
reputes = reputes.select_related(
@@ -834,9 +845,10 @@ def user_reputation(request, user):
'reputation': reputes,
'reps': reps
}
- return render_into_skin('user_profile/user_reputation.html', data, request)
+ context.update(data)
+ return render_into_skin('user_profile/user_reputation.html', context, request)
-def user_favorites(request, user):
+def user_favorites(request, user, context):
favorited_q_id_list= models.FavoriteQuestion.objects.filter(
user = user
).values_list('question__id', flat=True)
@@ -860,13 +872,13 @@ def user_favorites(request, user):
'page_title' : _('profile - favorite questions'),
'questions' : questions,
'favorited_myself': favorited_q_id_list,
- 'view_user' : user
}
- return render_into_skin('user_profile/user_favorites.html', data, request)
+ context.update(data)
+ return render_into_skin('user_profile/user_favorites.html', context, request)
@owner_or_moderator_required
@csrf.csrf_protect
-def user_email_subscriptions(request, user):
+def user_email_subscriptions(request, user, context):
logging.debug(get_request_info(request))
if request.method == 'POST':
@@ -900,14 +912,14 @@ def user_email_subscriptions(request, user):
'tab_name': 'email_subscriptions',
'tab_description': _('email subscription settings'),
'page_title': _('profile - email subscriptions'),
- 'view_user': user,
'email_feeds_form': email_feeds_form,
'tag_filter_selection_form': tag_filter_form,
'action_status': action_status,
}
+ context.update(data)
return render_into_skin(
'user_profile/user_email_subscriptions.html',
- data,
+ context,
request
)
@@ -915,6 +927,7 @@ user_view_call_table = {
'stats': user_stats,
'recent': user_recent,
'inbox': user_responses,
+ 'network': user_network,
'reputation': user_reputation,
'favorites': user_favorites,
'votes': user_votes,
@@ -943,4 +956,9 @@ def user(request, id, slug=None):
else:
user_view_func = user_stats
- return user_view_func(request, profile_owner)
+ context = {
+ 'view_user': profile_owner,
+ 'user_follow_feature_on': ('followit' in django_settings.INSTALLED_APPS),
+ }
+
+ return user_view_func(request, profile_owner, context)