summaryrefslogtreecommitdiffstats
path: root/askbot/models/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'askbot/models/__init__.py')
-rw-r--r--askbot/models/__init__.py201
1 files changed, 171 insertions, 30 deletions
diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py
index c1beb594..adf7fb90 100644
--- a/askbot/models/__init__.py
+++ b/askbot/models/__init__.py
@@ -1,6 +1,14 @@
from askbot import startup_procedures
startup_procedures.run()
+from django.contrib.auth.models import User
+#set up a possibility for the users to follow others
+try:
+ import followit
+ followit.register(User)
+except ImportError:
+ pass
+
import collections
import datetime
import hashlib
@@ -11,7 +19,6 @@ from django.db.models import signals as django_signals
from django.template import Context
from django.utils.translation import ugettext as _
from django.utils.translation import ungettext
-from django.contrib.auth.models import User
from django.utils.safestring import mark_safe
from django.utils.html import escape
from django.db import models
@@ -26,16 +33,24 @@ from askbot.const.message_keys import get_i18n_message
from askbot.conf import settings as askbot_settings
from askbot.models.question import Thread
from askbot.skins import utils as skin_utils
+from askbot.mail import messages
from askbot.models.question import QuestionView, AnonymousQuestion
+from askbot.models.question import DraftQuestion
from askbot.models.question import FavoriteQuestion
from askbot.models.tag import Tag, MarkedTag
+from askbot.models.tag import format_personal_group_name
+from askbot.models.group import get_groups, get_global_group
from askbot.models.user import EmailFeedSetting, ActivityAuditStatus, Activity
from askbot.models.user import GroupMembership, GroupProfile
-from askbot.models.post import Post, PostRevision, PostFlagReason, AnonymousAnswer
+from askbot.models.post import Post, PostRevision
+from askbot.models.post import PostFlagReason, AnonymousAnswer
+from askbot.models.post import PostToGroup
+from askbot.models.post import DraftAnswer
from askbot.models.reply_by_email import ReplyAddress
from askbot.models import signals
from askbot.models.badges import award_badges_signal, get_badge, BadgeData
from askbot.models.repute import Award, Repute, Vote
+from askbot.models.widgets import AskWidget, QuestionWidget
from askbot import auth
from askbot.utils.decorators import auto_now_timestamp
from askbot.utils.slug import slugify
@@ -55,16 +70,38 @@ def get_admins_and_moderators():
models.Q(is_superuser=True) | models.Q(status='m')
)
-def get_users_by_text_query(search_query):
+def get_admin():
+ """returns admin with the lowest user ID
+ if there are no users at all - creates one
+ with name "admin" and unusable password
+ otherwise raises User.DoesNotExist
+ """
+ try:
+ return User.objects.filter(
+ is_superuser=True
+ ).order_by('id')[0]
+ except IndexError:
+ if User.objects.filter(username='_admin_').count() == 0:
+ admin = User.objects.create_user('_admin_', '')
+ admin.set_unusable_password()
+ admin.set_admin_status()
+ admin.save()
+ return admin
+ else:
+ raise User.DoesNotExist
+
+def get_users_by_text_query(search_query, users_query_set = None):
"""Runs text search in user names and profile.
For postgres, search also runs against user group names.
"""
import askbot
+ if users_query_set is None:
+ users_query_set = User.objects.all()
if 'postgresql_psycopg2' in askbot.get_database_engine_name():
from askbot.search import postgresql
- return postgresql.run_full_text_search(User.objects.all(), search_query)
+ return postgresql.run_full_text_search(users_query_set, search_query)
else:
- return User.objects.filter(
+ return users_query_set.filter(
models.Q(username__icontains=search_query) |
models.Q(about__icontains=search_query)
)
@@ -275,7 +312,7 @@ def user_get_marked_tag_names(self, reason):
attr_name = MARKED_TAG_PROPERTY_MAP[reason]
wildcard_tags = getattr(self, attr_name).split()
tag_names.extend(wildcard_tags)
-
+
return tag_names
def user_has_affinity_to_question(self, question = None, affinity_type = None):
@@ -333,13 +370,20 @@ def user_has_interesting_wildcard_tags(self):
and self.interesting_tags != ''
)
+def user_can_create_tags(self):
+ """true if user can create tags"""
+ if askbot_settings.ENABLE_TAG_MODERATION:
+ return self.is_administrator_or_moderator()
+ else:
+ return True
+
def user_can_have_strong_url(self):
"""True if user's homepage url can be
followed by the search engine crawlers"""
return (self.reputation >= askbot_settings.MIN_REP_TO_HAVE_STRONG_URL)
def user_can_post_by_email(self):
- """True, if reply by email is enabled
+ """True, if reply by email is enabled
and user has sufficient reputatiton"""
return askbot_settings.REPLY_BY_EMAIL and \
self.reputation > askbot_settings.MIN_REP_TO_POST_BY_EMAIL
@@ -1129,6 +1173,8 @@ def user_post_comment(
added_at = timestamp,
by_email = by_email
)
+ comment.add_to_groups([self.get_personal_group()])
+
parent_post.thread.invalidate_cached_data()
award_badges_signal.send(
None,
@@ -1478,6 +1524,8 @@ def user_post_question(
tags = None,
wiki = False,
is_anonymous = False,
+ is_private = False,
+ group_id = None,
timestamp = None,
by_email = False,
email_address = None
@@ -1507,6 +1555,8 @@ def user_post_question(
added_at = timestamp,
wiki = wiki,
is_anonymous = is_anonymous,
+ is_private = is_private,
+ group_id = group_id,
by_email = by_email,
email_address = email_address
)
@@ -1544,7 +1594,8 @@ def user_edit_post(self,
body_text = None,
revision_comment = None,
timestamp = None,
- by_email = False
+ by_email = False,
+ is_private = False
):
"""a simple method that edits post body
todo: unify it in the style of just a generic post
@@ -1571,7 +1622,8 @@ def user_edit_post(self,
body_text = body_text,
timestamp = timestamp,
revision_comment = revision_comment,
- by_email = by_email
+ by_email = by_email,
+ is_private = is_private
)
elif post.post_type == 'tag_wiki':
post.apply_edit(
@@ -1596,6 +1648,7 @@ def user_edit_question(
tags = None,
wiki = False,
edit_anonymously = False,
+ is_private = False,
timestamp = None,
force = False,#if True - bypass the assert
by_email = False
@@ -1613,6 +1666,7 @@ def user_edit_question(
tags = tags,
wiki = wiki,
edit_anonymously = edit_anonymously,
+ is_private = is_private,
by_email = by_email
)
@@ -1632,6 +1686,7 @@ def user_edit_answer(
body_text = None,
revision_comment = None,
wiki = False,
+ is_private = False,
timestamp = None,
force = False,#if True - bypass the assert
by_email = False
@@ -1644,8 +1699,10 @@ def user_edit_answer(
text = body_text,
comment = revision_comment,
wiki = wiki,
+ is_private = is_private,
by_email = by_email
)
+
answer.thread.invalidate_cached_data()
award_badges_signal.send(None,
event = 'edit_answer',
@@ -1702,6 +1759,7 @@ def user_post_answer(
body_text = None,
follow = False,
wiki = False,
+ is_private = False,
timestamp = None,
by_email = False
):
@@ -1767,8 +1825,11 @@ def user_post_answer(
added_at = timestamp,
email_notify = follow,
wiki = wiki,
+ is_private = is_private,
by_email = by_email
)
+ answer_post.add_to_groups([self.get_personal_group()])
+
answer_post.thread.invalidate_cached_data()
award_badges_signal.send(None,
event = 'post_answer',
@@ -2140,6 +2201,37 @@ def get_profile_link(self):
return mark_safe(profile_link)
+def user_get_groups(self, private=False):
+ """returns a query set of groups to which user belongs"""
+ #todo: maybe cache this query
+ return Tag.group_tags.get_for_user(self, private=private)
+
+def user_get_personal_group(self):
+ group_name = format_personal_group_name(self)
+ return Tag.group_tags.get(name=group_name)
+
+def user_get_foreign_groups(self):
+ """returns a query set of groups to which user does not belong"""
+ #todo: maybe cache this query
+ user_group_ids = self.get_groups().values_list('id', flat = True)
+ return get_groups().exclude(id__in = user_group_ids)
+
+def user_get_primary_group(self):
+ """a temporary function - returns ether None or
+ first non-personal non-everyone group
+ works only for one real private group per-person
+ """
+ groups = self.get_groups(private=True)
+ for group in groups:
+ if group.name.startswith('_internal_'):
+ continue
+ return group
+ return None
+
+def user_can_make_group_private_posts(self):
+ """simplest implementation: user belongs to at least one group"""
+ return self.get_groups(private=True).count() > 0
+
def user_get_groups_membership_info(self, groups):
"""returts a defaultdict with values that are
dictionaries with the following keys and values:
@@ -2166,7 +2258,7 @@ def user_get_groups_membership_info(self, groups):
info[group.id]['can_join'] = group.group_profile.can_accept_user(self)
return info
-
+
def user_get_karma_summary(self):
@@ -2311,7 +2403,7 @@ def _process_vote(user, post, timestamp=None, cancel=False, vote_type=None):
auth.onDownVotedCanceled(vote, post, user, timestamp)
else:
auth.onDownVoted(vote, post, user, timestamp)
-
+
post.thread.invalidate_cached_data()
if post.post_type == 'question':
@@ -2403,12 +2495,12 @@ def flag_post(user, post, timestamp=None, cancel=False, cancel_all = False, forc
content_type = post_content_type, object_id=post.id
)
for flag in all_flags:
- auth.onUnFlaggedItem(post, flag.user, timestamp=timestamp)
+ auth.onUnFlaggedItem(post, flag.user, timestamp=timestamp)
elif cancel:#todo: can't unflag?
if force == False:
user.assert_can_remove_flag_offensive(post = post)
- auth.onUnFlaggedItem(post, user, timestamp=timestamp)
+ auth.onUnFlaggedItem(post, user, timestamp=timestamp)
else:
if force == False:
@@ -2533,6 +2625,12 @@ def user_edit_group_membership(self, user = None, group = None, action = None):
else:
raise ValueError('invalid action')
+def user_join_group(self, group):
+ self.edit_group_membership(group=group, user=self, action='add')
+
+def user_leave_group(self, group):
+ self.edit_group_membership(group=group, user=self, action='remove')
+
def user_is_group_member(self, group = None):
return self.group_memberships.filter(group = group).count() == 1
@@ -2559,6 +2657,10 @@ User.add_to_class('get_gravatar_url', user_get_gravatar_url)
User.add_to_class('get_or_create_fake_user', user_get_or_create_fake_user)
User.add_to_class('get_marked_tags', user_get_marked_tags)
User.add_to_class('get_marked_tag_names', user_get_marked_tag_names)
+User.add_to_class('get_groups', user_get_groups)
+User.add_to_class('get_foreign_groups', user_get_foreign_groups)
+User.add_to_class('get_personal_group', user_get_personal_group)
+User.add_to_class('get_primary_group', user_get_primary_group)
User.add_to_class('strip_email_signature', user_strip_email_signature)
User.add_to_class('get_groups_membership_info', user_get_groups_membership_info)
User.add_to_class('get_anonymous_name', user_get_anonymous_name)
@@ -2601,13 +2703,17 @@ 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('update_response_counts', user_update_response_counts)
+User.add_to_class('can_create_tags', user_can_create_tags)
User.add_to_class('can_have_strong_url', user_can_have_strong_url)
User.add_to_class('can_post_by_email', user_can_post_by_email)
User.add_to_class('can_post_comment', user_can_post_comment)
+User.add_to_class('can_make_group_private_posts', user_can_make_group_private_posts)
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('join_group', user_join_group)
+User.add_to_class('leave_group', user_leave_group)
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)
@@ -2719,6 +2825,8 @@ def format_instant_notification_email(
assert(isinstance(post, Post) and post.is_question())
elif update_type == 'new_question':
assert(isinstance(post, Post) and post.is_question())
+ elif update_type == 'post_shared':
+ pass
else:
raise ValueError('unexpected update_type %s' % update_type)
@@ -2743,23 +2851,25 @@ def format_instant_notification_email(
content_preview += '<p>======= Full thread summary =======</p>'
- content_preview += post.thread.format_for_email()
+ content_preview += post.thread.format_for_email(user=to_user)
- if post.is_comment():
+ if update_type == 'post_shared':
+ user_action = _('%(user)s shared a %(post_link)s.')
+ elif post.is_comment():
if update_type.endswith('update'):
user_action = _('%(user)s edited a %(post_link)s.')
else:
- user_action = _('%(user)s posted a %(post_link)s')
+ user_action = _('%(user)s posted a %(post_link)s')
elif post.is_answer():
if update_type.endswith('update'):
user_action = _('%(user)s edited an %(post_link)s.')
else:
- user_action = _('%(user)s posted an %(post_link)s.')
+ user_action = _('%(user)s posted an %(post_link)s.')
elif post.is_question():
if update_type.endswith('update'):
user_action = _('%(user)s edited a %(post_link)s.')
else:
- user_action = _('%(user)s posted a %(post_link)s.')
+ user_action = _('%(user)s posted a %(post_link)s.')
else:
raise ValueError('unrecognized post type')
@@ -2787,7 +2897,7 @@ def format_instant_notification_email(
reply_separator += '</p>'
else:
reply_separator = user_action
-
+
update_data = {
'update_author_name': from_user.username,
'receiving_user_name': to_user.username,
@@ -2816,7 +2926,7 @@ def get_reply_to_addresses(user, post):
the first address - always a real email address,
the second address is not ``None`` only for "question" posts.
- When the user is notified of a new question -
+ When the user is notified of a new question -
i.e. `post` is a "quesiton", he/she
will need to choose - whether to give a question or a comment,
thus we return the second address - for the comment reply.
@@ -2902,7 +3012,7 @@ def send_instant_notifications_about_activity_in_post(
update_type = update_type,
template = get_template('instant_notification.html')
)
-
+
headers['Reply-To'] = reply_address
mail.send_mail(
subject_line = subject_line,
@@ -2923,7 +3033,7 @@ def notify_author_of_published_revision(
if revision.should_notify_author_about_publishing(was_approved):
from askbot.tasks import notify_author_of_published_revision_celery_task
notify_author_of_published_revision_celery_task.delay(revision)
-
+
#todo: move to utils
def calculate_gravatar_hash(instance, **kwargs):
@@ -3235,6 +3345,36 @@ def send_respondable_email_validation_message(
)
+def add_user_to_global_group(sender, instance, created, **kwargs):
+ """auto-joins user to the global group
+ ``instance`` is an instance of ``User`` class
+ """
+ if created:
+ instance.edit_group_membership(
+ group=get_global_group(),
+ user=instance,
+ action='add'
+ )
+
+
+def add_user_to_personal_group(sender, instance, created, **kwargs):
+ """auto-joins user to his/her personal group
+ ``instance`` is an instance of ``User`` class
+ """
+ if created:
+ #todo: groups will indeed need to be separated from tags
+ #so that we can use less complicated naming scheme
+ #in theore here we may have two users that will have
+ #identical group names!!!
+ group_name = format_personal_group_name(instance)
+ group = Tag.group_tags.get_or_create(
+ group_name=group_name, user=instance
+ )
+ instance.edit_group_membership(
+ group=group, user=instance, action='add'
+ )
+
+
def greet_new_user(user, **kwargs):
"""sends welcome email to the newly created user
@@ -3307,7 +3447,7 @@ def update_user_avatar_type_flag(instance, **kwargs):
def make_admin_if_first_user(instance, **kwargs):
"""first user automatically becomes an administrator
the function is run only once in the interpreter session
- """
+ """
import sys
#have to check sys.argv to satisfy the test runner
#which fails with the cache-based skipping
@@ -3326,6 +3466,8 @@ def make_admin_if_first_user(instance, **kwargs):
django_signals.pre_save.connect(make_admin_if_first_user, sender=User)
django_signals.pre_save.connect(calculate_gravatar_hash, sender=User)
django_signals.post_save.connect(add_missing_subscriptions, sender=User)
+django_signals.post_save.connect(add_user_to_global_group, sender=User)
+django_signals.post_save.connect(add_user_to_personal_group, sender=User)
django_signals.post_save.connect(record_award_event, sender=Award)
django_signals.post_save.connect(notify_award_message, sender=Award)
django_signals.post_save.connect(record_answer_accepted, sender=Post)
@@ -3355,12 +3497,6 @@ signals.post_updated.connect(record_post_update_activity)
signals.post_revision_published.connect(notify_author_of_published_revision)
signals.site_visited.connect(record_user_visit)
-#set up a possibility for the users to follow others
-try:
- import followit
- followit.register(User)
-except ImportError:
- pass
__all__ = [
'signals',
@@ -3370,11 +3506,14 @@ __all__ = [
'QuestionView',
'FavoriteQuestion',
'AnonymousQuestion',
+ 'DraftQuestion',
'AnonymousAnswer',
+ 'DraftAnswer',
'Post',
'PostRevision',
+ 'PostToGroup',
'Tag',
'Vote',
@@ -3396,5 +3535,7 @@ __all__ = [
'ReplyAddress',
'get_model',
- 'get_admins_and_moderators'
+ 'get_admins_and_moderators',
+ 'get_groups',
+ 'get_global_group',
]