summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdolfo Fitoria <adolfo.fitoria@gmail.com>2013-07-08 08:45:17 -0600
committerAdolfo Fitoria <adolfo.fitoria@gmail.com>2013-07-08 08:45:17 -0600
commitd81569afcf8aeb393360330f8ffe12d3274c276c (patch)
tree2be05df2d7b718c2642a3880c076710a1681db11
parent5417773b84594b81a45bdda2a22f4a1ee5a9fb67 (diff)
parent23b9e63be8ed7bad82f89e44d5c26613276cf4d8 (diff)
downloadaskbot-d81569afcf8aeb393360330f8ffe12d3274c276c.tar.gz
askbot-d81569afcf8aeb393360330f8ffe12d3274c276c.tar.bz2
askbot-d81569afcf8aeb393360330f8ffe12d3274c276c.zip
Merge remote-tracking branch 'mainstream/master'
-rw-r--r--askbot/mail/__init__.py23
-rw-r--r--askbot/mail/lamson_handlers.py2
-rw-r--r--askbot/mail/parsing.py7
-rw-r--r--askbot/media/style/style.css64
-rw-r--r--askbot/media/style/style.less92
-rw-r--r--askbot/models/user.py34
-rw-r--r--askbot/templates/user_profile/user_recent.html23
-rw-r--r--askbot/views/users.py141
8 files changed, 125 insertions, 261 deletions
diff --git a/askbot/mail/__init__.py b/askbot/mail/__init__.py
index 8b999534..26bc7efd 100644
--- a/askbot/mail/__init__.py
+++ b/askbot/mail/__init__.py
@@ -322,14 +322,14 @@ def extract_user_signature(text, reply_code):
return signature or 'empty signature'
-def process_parts(parts, reply_code=None):
+def process_parts(parts, reply_code=None, from_address=None):
"""Uploads the attachments and parses out the
body, if body is multipart.
Links to attachments will be added to the body of the question.
Returns ready to post body of the message and the list
of uploaded files.
"""
- body_markdown = ''
+ body_text = ''
stored_files = list()
attachments_markdown = ''
for (part_type, content) in parts:
@@ -338,23 +338,30 @@ def process_parts(parts, reply_code=None):
stored_files.append(stored_file)
attachments_markdown += '\n\n' + markdown
elif part_type == 'body':
- body_markdown += '\n\n' + content.strip('\n\t ')
+ body_text += '\n\n' + content.strip('\n\t ')
elif part_type == 'inline':
markdown, stored_file = process_attachment(content)
stored_files.append(stored_file)
- body_markdown += markdown
+ body_text += markdown
#if the response separator is present -
#split the body with it, and discard the "so and so wrote:" part
if reply_code:
#todo: maybe move this part out
- signature = extract_user_signature(body_markdown, reply_code)
- body_markdown = extract_reply(body_markdown)
+ signature = extract_user_signature(body_text, reply_code)
+ body_text = extract_reply(body_text)
else:
signature = None
- body_markdown += attachments_markdown
- return body_markdown.strip(), stored_files, signature
+ body_text += attachments_markdown
+
+ if from_address:
+ body_text = parsing.strip_trailing_sender_references(
+ body_text,
+ from_address
+ )
+
+ return body_text.strip(), stored_files, signature
def process_emailed_question(
diff --git a/askbot/mail/lamson_handlers.py b/askbot/mail/lamson_handlers.py
index 0aa51629..e6414b7d 100644
--- a/askbot/mail/lamson_handlers.py
+++ b/askbot/mail/lamson_handlers.py
@@ -276,7 +276,7 @@ def PROCESS(
#1) get actual email content
# todo: factor this out into the process_reply decorator
reply_code = reply_address_object.address
- body_text, stored_files, signature = mail.process_parts(parts, reply_code)
+ body_text, stored_files, signature = mail.process_parts(parts, reply_code, from_address)
#2) process body text and email signature
user = reply_address_object.user
diff --git a/askbot/mail/parsing.py b/askbot/mail/parsing.py
index 31be6abb..91111f3b 100644
--- a/askbot/mail/parsing.py
+++ b/askbot/mail/parsing.py
@@ -3,6 +3,7 @@ this file is a candidate for publishing as an independent module
"""
import re
import sys
+from askbot.conf import settings as askbot_settings
#Regexes for quote separators
#add more via variables ending with _QUOTE_RE
@@ -45,6 +46,12 @@ def strip_trailing_empties_and_quotes(text):
def strip_leading_empties(text):
return re.sub(r'\A[\n\s\xa0]*', '', text)
+def strip_trailing_sender_references(text, email_address):
+ server_email = 'ask@' + askbot_settings.REPLY_BY_EMAIL_HOSTNAME
+ email_pattern = '(%s|%s)' % (email_address, server_email)
+ pattern = r'\n[^\n]*%s[^\n]*$' % email_pattern
+ return re.sub(pattern, '', text, re.IGNORECASE)
+
def strip_email_client_quote_separator(text):
"""strips email client quote separator from the responses,
e.g. (on such date XYZ wrote)
diff --git a/askbot/media/style/style.css b/askbot/media/style/style.css
index b58884a9..1c89065d 100644
--- a/askbot/media/style/style.css
+++ b/askbot/media/style/style.css
@@ -3476,39 +3476,23 @@ label.retag-error {
.narrow .tags {
float: left;
}
-/* todo: make these more semantic */
-.user-action-1 {
- font-weight: bold;
- color: #333;
-}
-.user-action-2 {
- font-weight: bold;
- color: #CCC;
-}
-.user-action-3 {
- color: #333;
-}
-.user-action-4 {
- color: #333;
-}
-.user-action-5 {
- color: darkred;
+.user-activity {
+ margin-top: 12px;
+ width: 100%;
}
-.user-action-6 {
- color: darkred;
+.user-activity td {
+ padding: 3px 11px;
+ vertical-align: top;
}
-.user-action-7 {
- color: #333;
+.user-activity .timestamp {
+ width: 56px;
}
-.user-action-8 {
- padding: 3px;
- font-weight: bold;
- background-color: #CCC;
- color: #763333;
+.user-activity .action-type {
+ width: 150px;
+ font-size: 13px;
}
-.revision-summary {
- background-color: #FFFE9B;
- padding: 2px;
+.user-activity .description {
+ width: auto;
}
.question-title-link a {
font-weight: bold;
@@ -3517,28 +3501,6 @@ label.retag-error {
.answer-title-link a {
color: #333;
}
-/* todo: make these more semantic */
-.post-type-1 a {
- font-weight: bold;
-}
-.post-type-3 a {
- font-weight: bold;
-}
-.post-type-5 a {
- font-weight: bold;
-}
-.post-type-2 a {
- color: #333;
-}
-.post-type-4 a {
- color: #333;
-}
-.post-type-6 a {
- color: #333;
-}
-.post-type-8 a {
- color: #333;
-}
.hilite {
background-color: #ff0;
}
diff --git a/askbot/media/style/style.less b/askbot/media/style/style.less
index 3061b287..51864aca 100644
--- a/askbot/media/style/style.less
+++ b/askbot/media/style/style.less
@@ -3639,50 +3639,23 @@ label.retag-error {
float: left;
}
-
-
-
-/* todo: make these more semantic */
-.user-action-1 {
- font-weight: bold;
- color: #333;
-}
-
-.user-action-2 {
- font-weight: bold;
- color: #CCC;
-}
-
-.user-action-3 {
- color: #333;
-}
-
-.user-action-4 {
- color: #333;
-}
-
-.user-action-5 {
- color: darkred;
-}
-
-.user-action-6 {
- color: darkred;
-}
-
-.user-action-7 {
- color: #333;
-}
-
-.user-action-8 {
- padding: 3px;
- font-weight: bold;
- background-color: #CCC;
- color: #763333;
-}
-
-.revision-summary {
- background-color: #FFFE9B;
- padding: 2px;
+.user-activity {
+ margin-top: 12px;
+ width: 100%;
+ td {
+ padding: 3px 11px;
+ vertical-align: top;
+ }
+ .timestamp {
+ width: 56px;
+ }
+ .action-type {
+ width: 150px;
+ font-size: 13px;
+ }
+ .description {
+ width: auto;
+ }
}
.question-title-link a {
@@ -3694,37 +3667,6 @@ label.retag-error {
color: #333;
}
-/* todo: make these more semantic */
-.post-type-1 a {
- font-weight: bold;
-
-}
-
-.post-type-3 a {
- font-weight: bold;
-
-}
-
-.post-type-5 a {
- font-weight: bold;
-}
-
-.post-type-2 a {
- color: #333;
-}
-
-.post-type-4 a {
- color: #333;
-}
-
-.post-type-6 a {
- color: #333;
-}
-
-.post-type-8 a {
- color: #333;
-}
-
.hilite {
background-color: #ff0;
}
diff --git a/askbot/models/user.py b/askbot/models/user.py
index af70b8be..3eb77d80 100644
--- a/askbot/models/user.py
+++ b/askbot/models/user.py
@@ -21,6 +21,7 @@ from askbot.models.tag import clean_group_name#todo - delete this
from askbot.models.tag import get_tags_by_names
from askbot.forms import DomainNameField
from askbot.utils.forms import email_is_allowed
+from collections import defaultdict
PERSONAL_GROUP_NAME_PREFIX = '_personal_'
@@ -35,7 +36,8 @@ class ResponseAndMentionActivityManager(models.Manager):
activity_type__in = response_types
)
-class ActivityManager(models.Manager):
+class ActivityQuerySet(models.query.QuerySet):
+ """query set for the `Activity` model"""
def get_all_origin_posts(self):
#todo: redo this with query sets
origin_posts = set()
@@ -50,6 +52,36 @@ class ActivityManager(models.Manager):
)
return list(origin_posts)
+ def fetch_content_objects_dict(self):
+ """return a dictionary where keys are activity ids
+ and values - content objects"""
+ content_object_ids = defaultdict(list)# lists of c.object ids by c.types
+ activity_type_ids = dict()#links c.objects back to activity objects
+ for act in self:
+ content_type_id = act.content_type_id
+ object_id = act.object_id
+ content_object_ids[content_type_id].append(object_id)
+ activity_type_ids[(content_type_id, object_id)] = act.id
+
+ #3) get links from activity objects to content objects
+ objects_by_activity = dict()
+ for content_type_id, object_id_list in content_object_ids.items():
+ content_type = ContentType.objects.get_for_id(content_type_id)
+ model_class = content_type.model_class()
+ content_objects = model_class.objects.filter(id__in=object_id_list)
+ for content_object in content_objects:
+ key = (content_type_id, content_object.id)
+ activity_id = activity_type_ids[key]
+ objects_by_activity[activity_id] = content_object
+
+ return objects_by_activity
+
+
+class ActivityManager(BaseQuerySetManager):
+ """manager class for the `Activity` model"""
+ def get_query_set(self):
+ return ActivityQuerySet(self.model)
+
def create_new_mention(
self,
mentioned_by = None,
diff --git a/askbot/templates/user_profile/user_recent.html b/askbot/templates/user_profile/user_recent.html
index deac051b..67dd95aa 100644
--- a/askbot/templates/user_profile/user_recent.html
+++ b/askbot/templates/user_profile/user_recent.html
@@ -5,14 +5,12 @@
{% trans %}activity{% endtrans %}
{% endblock %}
{% block usercontent %}
- <div style="padding-top:5px;font-size:13px;">
+ <table class="user-activity">
{% for act in activities %}
- <div style="clear:both;line-height:20px" >
- <div style="width:180px;float:left">{{ timeago(act.time) }}</div>
- <div style="width:150px;float:left">
- <span class="user-action-{{ act.type_id }}">{{ act.type }}</span>
- </div>
- <div style="float:left;overflow:hidden;">
+ <tr class="user-action-{{ act.type_id }}">
+ <td class="timestamp">{{ timeago(act.time) }}</td>
+ <td class="action-type">{{ act.type }}</td>
+ <td class="description">
{% if act.is_badge %}
<a href="{{act.badge.get_absolute_url()}}"
title="{{ act.badge.get_type_display() }} : {% trans description=act.badge.get_description() %}{{description}}{% endtrans %}"
@@ -29,13 +27,12 @@
href="{% url question answer.thread._question_post().id %}{{answer.thread.title|slugify}}#{{answer.id}}">{% trans %}source{% endtrans %}</a>)
{% endif %}
{% else %}
- <span class="post-type-{{ act.type_id }}"><a href="{{ act.title_link }}">{{ act.title|escape }}</a></span>
- {% if act.summary %}<span class="revision-summary">{{ act.summary|escape }}</span>{% endif %}
+ <a class="question-title" href="{{ act.title_link }}">{{ act.title|escape }}</a>
+ {% if act.summary %}<div class="revision-summary">{{ act.summary }}</div>{% endif %}
{% endif %}
- <div style="height:5px"></div>
- </div>
- </div>
+ </td>
+ </tr>
{% endfor %}
- </div>
+ </table>
{% endblock %}
<!-- end user_recent.html -->
diff --git a/askbot/views/users.py b/askbot/views/users.py
index 6f8f101c..ce4a6852 100644
--- a/askbot/views/users.py
+++ b/askbot/views/users.py
@@ -570,19 +570,14 @@ def user_recent(request, user, context):
class Event(object):
is_badge = False
- def __init__(self, time, type, title, summary, answer_id, question_id):
+ def __init__(self, time, type, title, summary, url):
self.time = time
self.type = get_type_name(type)
self.type_id = type
self.title = title
self.summary = summary
slug_title = slugify(title)
- self.title_link = reverse(
- 'question',
- kwargs={'id':question_id}
- ) + u'%s' % slug_title
- if int(answer_id) > 0:
- self.title_link += '#%s' % answer_id
+ self.title_link = url
class AwardEvent(object):
is_badge = True
@@ -604,7 +599,7 @@ def user_recent(request, user, context):
const.TYPE_ACTIVITY_PRIZE
)
- #source of information about activities
+ #1) get source of information about activities
activity_objects = models.Activity.objects.filter(
user=user,
activity_type__in=activity_types
@@ -612,117 +607,39 @@ def user_recent(request, user, context):
'-active_at'
)[:const.USER_VIEW_DATA_SIZE]
+ #2) load content objects ("c.objects) for each activity
+ # the return value is dictionary where activity id's are keys
+ content_objects_by_activity = activity_objects.fetch_content_objects_dict()
+
+
#a list of digest objects, suitable for display
#the number of activities to show is not guaranteed to be
#const.USER_VIEW_DATA_TYPE, because we don't show activity
#for deleted content
activities = []
for activity in activity_objects:
+ content = content_objects_by_activity.get(activity.id)
+
+ if content is None:
+ continue
+
+ if activity.activity_type == const.TYPE_ACTIVITY_PRIZE:
+ event = AwardEvent(
+ time=content.awarded_at,
+ type=activity.activity_type,
+ content_object=content.content_object,
+ badge=content.badge,
+ )
+ else:
+ event = Event(
+ time=activity.active_at,
+ type=activity.activity_type,
+ title=content.thread.title,
+ summary=content.summary,
+ url=content.get_absolute_url()
+ )
- # TODO: multi-if means that we have here a construct for which a design pattern should be used
-
- # ask questions
- if activity.activity_type == const.TYPE_ACTIVITY_ASK_QUESTION:
- question = activity.content_object
- if not question.deleted:
- activities.append(Event(
- time=activity.active_at,
- type=activity.activity_type,
- title=question.thread.title,
- summary='', #q.summary, # TODO: was set to '' before, but that was probably wrong
- answer_id=0,
- question_id=question.id
- ))
-
- elif activity.activity_type == const.TYPE_ACTIVITY_ANSWER:
- ans = activity.content_object
- question = ans.thread._question_post()
- if not ans.deleted and not question.deleted:
- activities.append(Event(
- time=activity.active_at,
- type=activity.activity_type,
- title=ans.thread.title,
- summary=question.summary,
- answer_id=ans.id,
- question_id=question.id
- ))
-
- elif activity.activity_type == const.TYPE_ACTIVITY_COMMENT_QUESTION:
- cm = activity.content_object
- q = cm.parent
- #assert q.is_question(): todo the activity types may be wrong
- if not q.deleted:
- activities.append(Event(
- time=cm.added_at,
- type=activity.activity_type,
- title=q.thread.title,
- summary='',
- answer_id=0,
- question_id=q.id
- ))
-
- elif activity.activity_type == const.TYPE_ACTIVITY_COMMENT_ANSWER:
- cm = activity.content_object
- ans = cm.parent
- #assert ans.is_answer()
- question = ans.thread._question_post()
- if not ans.deleted and not question.deleted:
- activities.append(Event(
- time=cm.added_at,
- type=activity.activity_type,
- title=ans.thread.title,
- summary='',
- answer_id=ans.id,
- question_id=question.id
- ))
-
- elif activity.activity_type == const.TYPE_ACTIVITY_UPDATE_QUESTION:
- q = activity.content_object
- if not q.deleted:
- activities.append(Event(
- time=activity.active_at,
- type=activity.activity_type,
- title=q.thread.title,
- summary=q.summary,
- answer_id=0,
- question_id=q.id
- ))
-
- elif activity.activity_type == const.TYPE_ACTIVITY_UPDATE_ANSWER:
- ans = activity.content_object
- question = ans.thread._question_post()
- if not ans.deleted and not question.deleted:
- activities.append(Event(
- time=activity.active_at,
- type=activity.activity_type,
- title=ans.thread.title,
- summary=ans.summary,
- answer_id=ans.id,
- question_id=question.id
- ))
-
- elif activity.activity_type == const.TYPE_ACTIVITY_MARK_ANSWER:
- ans = activity.content_object
- question = ans.thread._question_post()
- if not ans.deleted and not question.deleted:
- activities.append(Event(
- time=activity.active_at,
- type=activity.activity_type,
- title=ans.thread.title,
- summary='',
- answer_id=0,
- question_id=question.id
- ))
-
- elif activity.activity_type == const.TYPE_ACTIVITY_PRIZE:
- award = activity.content_object
- if award is not None:#todo: work around halfa$$ comment deletion
- activities.append(AwardEvent(
- time=award.awarded_at,
- type=activity.activity_type,
- content_object=award.content_object,
- badge=award.badge,
- ))
+ activities.append(event)
activities.sort(key=operator.attrgetter('time'), reverse=True)