From f7521efee66e5297528f52290b670df6c2ddb2aa Mon Sep 17 00:00:00 2001 From: Aurélien Bompard Date: Wed, 31 Jul 2013 09:12:12 +0200 Subject: Add User profiles --- hyperkitty/models.py | 7 -- .../static/hyperkitty/css/hyperkitty-message.css | 9 ++- .../static/hyperkitty/css/hyperkitty-user.css | 5 ++ hyperkitty/templates/messages/message.html | 9 ++- hyperkitty/templates/user_public_profile.html | 80 ++++++++++++++++++++++ hyperkitty/urls.py | 2 + hyperkitty/views/accounts.py | 75 +++++++++++++++++++- 7 files changed, 176 insertions(+), 11 deletions(-) create mode 100644 hyperkitty/templates/user_public_profile.html diff --git a/hyperkitty/models.py b/hyperkitty/models.py index ac7e091..f734952 100644 --- a/hyperkitty/models.py +++ b/hyperkitty/models.py @@ -29,14 +29,9 @@ from paintstore.fields import ColorPickerField class Rating(models.Model): - # @TODO: instead of list_address, user list model from kittystore? list_address = models.CharField(max_length=255, db_index=True) - - # @TODO: instead of messsageid, use message model from kittystore? messageid = models.CharField(max_length=100, db_index=True) - user = models.ForeignKey(User) - vote = models.SmallIntegerField() def __unicode__(self): @@ -65,9 +60,7 @@ class UserProfile(models.Model): class Tag(models.Model): - # @TODO: instead of list_address, user list model from kittystore? list_address = models.CharField(max_length=255, db_index=True) - # @TODO: instead of threadid, use thread model from kittystore? threadid = models.CharField(max_length=100, db_index=True) user = models.ForeignKey(User) tag = models.CharField(max_length=255) diff --git a/hyperkitty/static/hyperkitty/css/hyperkitty-message.css b/hyperkitty/static/hyperkitty/css/hyperkitty-message.css index 2f76e4e..4efd882 100644 --- a/hyperkitty/static/hyperkitty/css/hyperkitty-message.css +++ b/hyperkitty/static/hyperkitty/css/hyperkitty-message.css @@ -199,7 +199,6 @@ .email-header .gravatar { margin-right: 10px; display: inline-block; - vertical-align: middle; } .email-author .name{ @@ -211,6 +210,14 @@ font-size: 80%; font-weight: bold; } +.email-author .messagelink { + font-style: italic; + font-size: 90%; + color: rgb(167, 169, 172); +} +.email-author .messagelink a { + color: rgb(167, 169, 172); +} .email-first .email-date { text-align: right; diff --git a/hyperkitty/static/hyperkitty/css/hyperkitty-user.css b/hyperkitty/static/hyperkitty/css/hyperkitty-user.css index 17b1fa9..23a8c2c 100644 --- a/hyperkitty/static/hyperkitty/css/hyperkitty-user.css +++ b/hyperkitty/static/hyperkitty/css/hyperkitty-user.css @@ -91,3 +91,8 @@ table.user-data input { margin-left: 0; position: absolute; } + + +#user-profile table.subscriptions { + width: auto; +} diff --git a/hyperkitty/templates/messages/message.html b/hyperkitty/templates/messages/message.html index 453e248..7140d75 100644 --- a/hyperkitty/templates/messages/message.html +++ b/hyperkitty/templates/messages/message.html @@ -28,9 +28,14 @@
{{email.sender_name|escapeemail}} +
+ + (permalink) + {% if use_mockups %}
diff --git a/hyperkitty/templates/user_public_profile.html b/hyperkitty/templates/user_public_profile.html new file mode 100644 index 0000000..b62002d --- /dev/null +++ b/hyperkitty/templates/user_public_profile.html @@ -0,0 +1,80 @@ +{% extends "base.html" %} +{% load url from future %} +{% load i18n %} +{% load hk_generic %} +{% load gravatar %} + + +{% block title %} +{% trans 'User Profile' %} for {{ email }} - {{ app_name|title }} +{% endblock %} + +{% block content %} + +
+ +

User profile for {{ email }}

+ + + + + + + + + + + + {% if creation %} + + + + + {% endif %} + + + + + +
{% trans 'Name:' %}{{ fullname }}
{% trans 'Email:' %}{{ email }}
{% trans 'Creation:' %}{{ creation|date:"l, j F Y H:i:s" }} ({{ creation|timesince }})
{% trans 'Votes for this user:' %}+{{ likes }}/-{{ dislikes }}
+ +

Subscriptions

+ {% if subscriptions %} + + + + + + + + + + + + {% for sub in subscriptions %} + + + {% if sub.first_post %} + + + + + {% else %} + + {% endif %} + + {% endfor %} + +
ListTime of first activityFirst postPosts to this listVotes
+ {{ sub.list_name }} + + {{ sub.first_post|get_date|date:"l, j F Y H:i:s" }} + {{ sub.first_post|get_date|timesince }} + {{ sub.first_post.subject }}{{ sub.posts_count }} post{{ sub.posts_count|pluralize }}+{{ sub.likes }}/-{{ sub.dislikes }}no post yet
+ {% else %} +

no subscriptions

+ {% endif %} + +
+ +{% endblock %} diff --git a/hyperkitty/urls.py b/hyperkitty/urls.py index 9dfe63c..3cc24e5 100644 --- a/hyperkitty/urls.py +++ b/hyperkitty/urls.py @@ -50,6 +50,8 @@ urlpatterns = patterns('hyperkitty.views', url(r'^accounts/profile/votes$', 'accounts.votes', name='user_votes'), url(r'^accounts/register/$', 'accounts.user_registration', {'SSL': True}, name='user_registration'), + # Users + url(r'^user/(?P[^/@]+@[^/@]+)/$', 'accounts.public_profile', name='public_user_profile'), # List archives and overview url(r'^list/(?P[^/@]+@[^/@]+)/(?P\d{4})/(?P\d\d?)/(?P\d\d?)/$', diff --git a/hyperkitty/views/accounts.py b/hyperkitty/views/accounts.py index 373390b..5d7a9fc 100644 --- a/hyperkitty/views/accounts.py +++ b/hyperkitty/views/accounts.py @@ -20,6 +20,7 @@ # import logging +from urllib2 import HTTPError from django.conf import settings from django.core.urlresolvers import reverse @@ -29,10 +30,12 @@ from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User from django.contrib.auth.views import login as django_login_view from django.shortcuts import render, redirect -from django.utils.http import is_safe_url +from django.utils.http import is_safe_url, urlquote from django.utils.timezone import utc, get_current_timezone +from django.http import Http404 #from django.utils.translation import gettext as _ from social_auth.backends import SocialAuthBackend +import dateutil.parser from hyperkitty.models import UserProfile, Rating, Favorite, LastView from hyperkitty.views.forms import RegistrationForm, UserProfileForm @@ -203,3 +206,73 @@ def votes(request): return render(request, 'ajax/votes.html', { "votes": votes, }) + + +def public_profile(request, email): + from mailmanclient import Client, MailmanConnectionError + try: + client = Client('%s/3.0' % settings.MAILMAN_REST_SERVER, + settings.MAILMAN_API_USER, settings.MAILMAN_API_PASS) + mm_user = client.get_user(email) + except HTTPError: + raise Http404("No user with this email: %s" % email) + except MailmanConnectionError: + class EmptyMailmanUser: + created_on = None + subscription_list_ids = [] + mm_user = EmptyMailmanUser() + subscriptions = [] + store = get_store(request) + # Subscriptions + for mlist_id in mm_user.subscription_list_ids: + mlist = client.get_list(mlist_id).fqdn_listname + # de-duplicate subscriptions + if mlist in [ s["list_name"] for s in subscriptions ]: + continue + email_hashes = store.get_message_hashes_by_sender(email, mlist) + try: # Compute the average vote value + votes = Rating.objects.filter(list_address=mlist, + messageid__in=email_hashes) + except Rating.DoesNotExist: + votes = [] + likes = dislikes = 0 + for v in votes: + if v.vote == 1: + likes += 1 + elif v.vote == -1: + dislikes += 1 + all_posts_url = "%s?list=%s&query=sender:%s" % \ + (reverse("search"), mlist, urlquote(email)) + likestatus = "neutral" + if likes - dislikes >= 10: + likestatus = "likealot" + elif likes - dislikes > 0: + likestatus = "like" + subscriptions.append({ + "list_name": mlist, + "first_post": store.get_first_post(mlist, email), + "likes": likes, + "dislikes": dislikes, + "likestatus": likestatus, + "all_posts_url": all_posts_url, + "posts_count": len(email_hashes), + }) + likes = sum([s["likes"] for s in subscriptions]) + dislikes = sum([s["dislikes"] for s in subscriptions]) + likestatus = "neutral" + if likes - dislikes >= 10: + likestatus = "likealot" + elif likes - dislikes > 0: + likestatus = "like" + context = { + "email": email, + "fullname": store.get_sender_name(email), + "mm_user": mm_user, + "creation": dateutil.parser.parse(mm_user.created_on), + "subscriptions": subscriptions, + "posts_count": sum([s["posts_count"] for s in subscriptions]), + "likes": likes, + "dislikes": dislikes, + "likestatus": likestatus, + } + return render(request, "user_public_profile.html", context) -- cgit